Repository: alibaba/nacos Branch: develop Commit: 5ac5c39971e7 Files: 4319 Total size: 30.4 MB Directory structure: gitextract_u6qmkl26/ ├── .gitattributes ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug-report.md │ │ ├── config.yml │ │ └── feature_request.md │ ├── ISSUE_TEMPLATE.md │ ├── PULL_REQUEST_TEMPLATE.md │ └── workflows/ │ ├── anti-spam.yml │ ├── ci.yml │ ├── it.yml │ ├── pr-ci.yml │ ├── pr-comment.yml │ ├── pr-e2e-test.yml │ ├── push-ci.yaml │ └── stale.yml ├── .gitignore ├── .mvn/ │ └── wrapper/ │ └── maven-wrapper.properties ├── .travis.yml ├── BUILDING ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── CONTRIBUTING_zh.md ├── LICENSE ├── NOTICE ├── README.md ├── REPORTING-BUGS.md ├── address/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── address/ │ │ │ ├── AddressServer.java │ │ │ ├── component/ │ │ │ │ ├── AddressServerGeneratorManager.java │ │ │ │ └── AddressServerManager.java │ │ │ ├── config/ │ │ │ │ └── AddressServerSecurityConfiguration.java │ │ │ ├── constant/ │ │ │ │ └── AddressServerConstants.java │ │ │ ├── controller/ │ │ │ │ ├── AddressServerClusterController.java │ │ │ │ └── ServerListController.java │ │ │ └── misc/ │ │ │ └── Loggers.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── logback/ │ │ │ │ └── nacos-included.xml │ │ │ └── nacos-default.properties │ │ └── application.properties │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── address/ │ ├── component/ │ │ ├── AddressServerGeneratorManagerTest.java │ │ └── AddressServerManagerTests.java │ └── controller/ │ ├── AddressServerClusterControllerTest.java │ └── ServerListControllerTest.java ├── ai/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── ai/ │ │ │ ├── config/ │ │ │ │ ├── AiEnabledFilter.java │ │ │ │ ├── McpCacheIndexProperties.java │ │ │ │ ├── McpConfiguration.java │ │ │ │ └── McpServerIndexConfiguration.java │ │ │ ├── constant/ │ │ │ │ ├── Constants.java │ │ │ │ └── McpServerValidationConstants.java │ │ │ ├── controller/ │ │ │ │ ├── A2aAdminController.java │ │ │ │ ├── McpAdminController.java │ │ │ │ ├── PromptAdminController.java │ │ │ │ ├── PromptClientController.java │ │ │ │ └── SkillAdminController.java │ │ │ ├── enums/ │ │ │ │ ├── ExternalDataTypeEnum.java │ │ │ │ └── McpImportResultStatusEnum.java │ │ │ ├── form/ │ │ │ │ ├── a2a/ │ │ │ │ │ └── admin/ │ │ │ │ │ ├── AgentCardForm.java │ │ │ │ │ ├── AgentCardUpdateForm.java │ │ │ │ │ ├── AgentForm.java │ │ │ │ │ └── AgentListForm.java │ │ │ │ ├── mcp/ │ │ │ │ │ └── admin/ │ │ │ │ │ ├── McpDetailForm.java │ │ │ │ │ ├── McpForm.java │ │ │ │ │ ├── McpImportForm.java │ │ │ │ │ ├── McpListForm.java │ │ │ │ │ └── McpUpdateForm.java │ │ │ │ ├── prompt/ │ │ │ │ │ ├── PromptForm.java │ │ │ │ │ ├── PromptHistoryForm.java │ │ │ │ │ ├── PromptLabelBindForm.java │ │ │ │ │ ├── PromptLabelForm.java │ │ │ │ │ ├── PromptListForm.java │ │ │ │ │ ├── PromptMetadataForm.java │ │ │ │ │ ├── PromptPublishForm.java │ │ │ │ │ └── PromptQueryForm.java │ │ │ │ └── skills/ │ │ │ │ └── admin/ │ │ │ │ ├── SkillDetailForm.java │ │ │ │ ├── SkillForm.java │ │ │ │ ├── SkillListForm.java │ │ │ │ └── SkillUpdateForm.java │ │ │ ├── index/ │ │ │ │ ├── AbstractMcpServerIndex.java │ │ │ │ ├── CachedMcpServerIndex.java │ │ │ │ ├── McpCacheIndex.java │ │ │ │ ├── McpServerIndex.java │ │ │ │ ├── MemoryMcpCacheIndex.java │ │ │ │ └── PlainMcpServerIndex.java │ │ │ ├── model/ │ │ │ │ └── mcp/ │ │ │ │ ├── McpServerIndexData.java │ │ │ │ ├── McpServerStorageInfo.java │ │ │ │ └── UrlPageResult.java │ │ │ ├── param/ │ │ │ │ ├── AgentHttpParamExtractor.java │ │ │ │ ├── McpHttpParamExtractor.java │ │ │ │ ├── PromptHttpParamExtractor.java │ │ │ │ └── SkillHttpParamExtractor.java │ │ │ ├── remote/ │ │ │ │ ├── handler/ │ │ │ │ │ ├── McpServerEndpointRequestHandler.java │ │ │ │ │ ├── QueryMcpServerRequestHandler.java │ │ │ │ │ ├── QueryPromptRequestHandler.java │ │ │ │ │ ├── ReleaseMcpServerRequestHandler.java │ │ │ │ │ └── a2a/ │ │ │ │ │ ├── AgentEndpointRequestHandler.java │ │ │ │ │ ├── BatchAgentEndpointRequestHandler.java │ │ │ │ │ ├── QueryAgentCardRequestHandler.java │ │ │ │ │ └── ReleaseAgentCardRequestHandler.java │ │ │ │ └── manager/ │ │ │ │ └── AiConnectionBasedClientManager.java │ │ │ ├── service/ │ │ │ │ ├── McpEndpointOperationService.java │ │ │ │ ├── McpExternalDataAdaptor.java │ │ │ │ ├── McpServerCacheInvalidateService.java │ │ │ │ ├── McpServerImportService.java │ │ │ │ ├── McpServerOperationService.java │ │ │ │ ├── McpServerValidationService.java │ │ │ │ ├── McpToolOperationService.java │ │ │ │ ├── SimpleSyncEffectService.java │ │ │ │ ├── SyncEffectService.java │ │ │ │ ├── a2a/ │ │ │ │ │ ├── A2aServerOperationService.java │ │ │ │ │ └── identity/ │ │ │ │ │ ├── AgentIdCodec.java │ │ │ │ │ ├── AgentIdCodecHolder.java │ │ │ │ │ └── AsciiAgentIdCodec.java │ │ │ │ ├── prompt/ │ │ │ │ │ ├── PromptAdminOperationService.java │ │ │ │ │ ├── PromptAdminOperationServiceImpl.java │ │ │ │ │ ├── PromptClientOperationService.java │ │ │ │ │ ├── PromptClientOperationServiceImpl.java │ │ │ │ │ ├── PromptLabelVersionMappingSnapshot.java │ │ │ │ │ ├── PromptMetaCacheInvalidateService.java │ │ │ │ │ ├── PromptMetaSnapshot.java │ │ │ │ │ └── PromptMetaUtils.java │ │ │ │ └── skills/ │ │ │ │ ├── SkillOperationService.java │ │ │ │ └── SkillOperationServiceImpl.java │ │ │ └── utils/ │ │ │ ├── AgentCardUtil.java │ │ │ ├── AgentEndpointUtil.java │ │ │ ├── AgentRequestUtil.java │ │ │ ├── McpConfigUtils.java │ │ │ ├── McpRequestUtil.java │ │ │ ├── PromptDataIdUtils.java │ │ │ ├── PromptVersionUtils.java │ │ │ ├── SkillRequestUtil.java │ │ │ └── SkillZipParser.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ ├── com.alibaba.nacos.ai.service.McpServerOperationService │ │ ├── com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor │ │ └── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── ai/ │ ├── config/ │ │ ├── AiEnabledFilterTest.java │ │ ├── McpCacheIndexPropertiesTest.java │ │ ├── McpConfigurationTest.java │ │ └── McpServerIndexConfigurationTest.java │ ├── controller/ │ │ ├── A2aAdminControllerTest.java │ │ ├── McpAdminControllerTest.java │ │ └── PromptClientControllerTest.java │ ├── form/ │ │ ├── a2a/ │ │ │ └── admin/ │ │ │ ├── AgentCardFormTest.java │ │ │ ├── AgentFormTest.java │ │ │ └── AgentListFormTest.java │ │ ├── mcp/ │ │ │ └── admin/ │ │ │ └── McpImportFormTest.java │ │ └── prompt/ │ │ ├── PromptFormTest.java │ │ ├── PromptHistoryFormTest.java │ │ ├── PromptLabelBindFormTest.java │ │ ├── PromptLabelFormTest.java │ │ ├── PromptListFormTest.java │ │ └── PromptPublishFormTest.java │ ├── index/ │ │ ├── AbstractMcpServerIndexTest.java │ │ ├── CachedMcpServerIndexTest.java │ │ ├── McpCachePerformanceTest.java │ │ ├── MemoryMcpCacheIndexTest.java │ │ └── PlainMcpServerIndexTest.java │ ├── param/ │ │ ├── AgentHttpParamExtractorTest.java │ │ └── McpHttpParamExtractorTest.java │ ├── remote/ │ │ ├── handler/ │ │ │ ├── McpServerEndpointRequestHandlerTest.java │ │ │ ├── QueryMcpServerRequestHandlerTest.java │ │ │ ├── QueryPromptRequestHandlerTest.java │ │ │ ├── ReleaseMcpServerRequestHandlerTest.java │ │ │ └── a2a/ │ │ │ ├── AgentEndpointRequestHandlerTest.java │ │ │ ├── BatchAgentEndpointRequestHandlerTest.java │ │ │ ├── QueryAgentCardRequestHandlerTest.java │ │ │ └── ReleaseAgentCardRequestHandlerTest.java │ │ └── manager/ │ │ └── AiConnectionBasedClientManagerTest.java │ ├── service/ │ │ ├── McpEndpointOperationServiceTest.java │ │ ├── McpExternalDataAdaptorTest.java │ │ ├── McpServerCacheInvalidateServiceTest.java │ │ ├── McpServerImportServiceTest.java │ │ ├── McpServerOperationServiceTest.java │ │ ├── McpServerValidationServiceTest.java │ │ ├── McpToolOperationServiceTest.java │ │ ├── SimpleSyncEffectServiceTest.java │ │ ├── SyncEffectServiceTest.java │ │ ├── a2a/ │ │ │ ├── A2aServerOperationServiceTest.java │ │ │ └── identity/ │ │ │ ├── AgentIdCodecHolderTest.java │ │ │ └── AsciiAgentIdCodecTest.java │ │ ├── prompt/ │ │ │ ├── PromptAdminOperationServiceImplTest.java │ │ │ ├── PromptClientOperationServiceImplTest.java │ │ │ ├── PromptMetaCacheInvalidateServiceTest.java │ │ │ └── PromptMetaUtilsTest.java │ │ └── skills/ │ │ └── SkillOperationServiceImplTest.java │ └── utils/ │ ├── AgentCardUtilTest.java │ ├── AgentEndpointUtilTest.java │ ├── AgentRequestUtilTest.java │ ├── McpConfigUtilsTest.java │ ├── McpRequestUtilTest.java │ ├── McpRequestUtilsTest.java │ ├── PromptDataIdUtilsTest.java │ └── SkillZipParserTest.java ├── api/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── api/ │ │ │ ├── NacosFactory.java │ │ │ ├── PropertyKeyConst.java │ │ │ ├── SystemPropertyKeyConst.java │ │ │ ├── ability/ │ │ │ │ ├── ClientAbilities.java │ │ │ │ ├── ServerAbilities.java │ │ │ │ ├── constant/ │ │ │ │ │ ├── AbilityKey.java │ │ │ │ │ ├── AbilityMode.java │ │ │ │ │ └── AbilityStatus.java │ │ │ │ ├── initializer/ │ │ │ │ │ ├── AbilityInitializer.java │ │ │ │ │ └── AbilityPostProcessor.java │ │ │ │ └── register/ │ │ │ │ ├── AbstractAbilityRegistry.java │ │ │ │ └── impl/ │ │ │ │ ├── ClusterClientAbilities.java │ │ │ │ ├── SdkClientAbilities.java │ │ │ │ └── ServerAbilities.java │ │ │ ├── ai/ │ │ │ │ ├── A2aService.java │ │ │ │ ├── AiFactory.java │ │ │ │ ├── AiService.java │ │ │ │ ├── constant/ │ │ │ │ │ └── AiConstants.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── AbstractNacosAgentCardListener.java │ │ │ │ │ ├── AbstractNacosMcpServerListener.java │ │ │ │ │ ├── AbstractNacosPromptListener.java │ │ │ │ │ ├── AbstractNacosSkillListener.java │ │ │ │ │ ├── NacosAgentCardEvent.java │ │ │ │ │ ├── NacosAiEvent.java │ │ │ │ │ ├── NacosAiListener.java │ │ │ │ │ ├── NacosMcpServerEvent.java │ │ │ │ │ ├── NacosPromptEvent.java │ │ │ │ │ └── NacosSkillEvent.java │ │ │ │ ├── model/ │ │ │ │ │ ├── a2a/ │ │ │ │ │ │ ├── AgentAuthentication.java │ │ │ │ │ │ ├── AgentCapabilities.java │ │ │ │ │ │ ├── AgentCard.java │ │ │ │ │ │ ├── AgentCardBasicInfo.java │ │ │ │ │ │ ├── AgentCardDetailInfo.java │ │ │ │ │ │ ├── AgentCardVersionInfo.java │ │ │ │ │ │ ├── AgentEndpoint.java │ │ │ │ │ │ ├── AgentExtension.java │ │ │ │ │ │ ├── AgentInterface.java │ │ │ │ │ │ ├── AgentProvider.java │ │ │ │ │ │ ├── AgentSkill.java │ │ │ │ │ │ ├── AgentVersionDetail.java │ │ │ │ │ │ └── SecurityScheme.java │ │ │ │ │ ├── mcp/ │ │ │ │ │ │ ├── EncryptObject.java │ │ │ │ │ │ ├── FrontEndpointConfig.java │ │ │ │ │ │ ├── McpCapability.java │ │ │ │ │ │ ├── McpEndpointInfo.java │ │ │ │ │ │ ├── McpEndpointSpec.java │ │ │ │ │ │ ├── McpServerBasicInfo.java │ │ │ │ │ │ ├── McpServerDetailInfo.java │ │ │ │ │ │ ├── McpServerImportRequest.java │ │ │ │ │ │ ├── McpServerImportResponse.java │ │ │ │ │ │ ├── McpServerImportResult.java │ │ │ │ │ │ ├── McpServerImportValidationResult.java │ │ │ │ │ │ ├── McpServerRemoteServiceConfig.java │ │ │ │ │ │ ├── McpServerValidationItem.java │ │ │ │ │ │ ├── McpServerVersionInfo.java │ │ │ │ │ │ ├── McpServiceRef.java │ │ │ │ │ │ ├── McpTool.java │ │ │ │ │ │ ├── McpToolAnnotations.java │ │ │ │ │ │ ├── McpToolMeta.java │ │ │ │ │ │ ├── McpToolSpecification.java │ │ │ │ │ │ ├── SecurityScheme.java │ │ │ │ │ │ └── registry/ │ │ │ │ │ │ ├── Argument.java │ │ │ │ │ │ ├── Icon.java │ │ │ │ │ │ ├── Input.java │ │ │ │ │ │ ├── InputWithVariables.java │ │ │ │ │ │ ├── KeyValueInput.java │ │ │ │ │ │ ├── McpErrorResponse.java │ │ │ │ │ │ ├── McpRegistryServerDetail.java │ │ │ │ │ │ ├── McpRegistryServerList.java │ │ │ │ │ │ ├── McpServerStatusEnum.java │ │ │ │ │ │ ├── NamedArgument.java │ │ │ │ │ │ ├── OfficialMeta.java │ │ │ │ │ │ ├── Package.java │ │ │ │ │ │ ├── PositionalArgument.java │ │ │ │ │ │ ├── Remote.java │ │ │ │ │ │ ├── Repository.java │ │ │ │ │ │ ├── ServerResponse.java │ │ │ │ │ │ ├── ServerVersionDetail.java │ │ │ │ │ │ ├── SseTransport.java │ │ │ │ │ │ ├── StdioTransport.java │ │ │ │ │ │ └── StreamableHttpTransport.java │ │ │ │ │ ├── prompt/ │ │ │ │ │ │ ├── Prompt.java │ │ │ │ │ │ ├── PromptDescriptor.java │ │ │ │ │ │ ├── PromptLabelVersionMapping.java │ │ │ │ │ │ ├── PromptMetaInfo.java │ │ │ │ │ │ ├── PromptMetaSummary.java │ │ │ │ │ │ ├── PromptVariable.java │ │ │ │ │ │ ├── PromptVersionInfo.java │ │ │ │ │ │ └── PromptVersionSummary.java │ │ │ │ │ └── skills/ │ │ │ │ │ ├── Skill.java │ │ │ │ │ ├── SkillBasicInfo.java │ │ │ │ │ ├── SkillResource.java │ │ │ │ │ └── SkillUtils.java │ │ │ │ └── remote/ │ │ │ │ ├── AiRemoteConstants.java │ │ │ │ ├── request/ │ │ │ │ │ ├── AbstractAgentRequest.java │ │ │ │ │ ├── AbstractMcpRequest.java │ │ │ │ │ ├── AbstractPromptRequest.java │ │ │ │ │ ├── AgentEndpointRequest.java │ │ │ │ │ ├── BatchAgentEndpointRequest.java │ │ │ │ │ ├── McpServerEndpointRequest.java │ │ │ │ │ ├── QueryAgentCardRequest.java │ │ │ │ │ ├── QueryMcpServerRequest.java │ │ │ │ │ ├── QueryPromptRequest.java │ │ │ │ │ ├── ReleaseAgentCardRequest.java │ │ │ │ │ └── ReleaseMcpServerRequest.java │ │ │ │ └── response/ │ │ │ │ ├── AgentEndpointResponse.java │ │ │ │ ├── McpServerEndpointResponse.java │ │ │ │ ├── QueryAgentCardResponse.java │ │ │ │ ├── QueryMcpServerResponse.java │ │ │ │ ├── QueryPromptResponse.java │ │ │ │ ├── ReleaseAgentCardResponse.java │ │ │ │ └── ReleaseMcpServerResponse.java │ │ │ ├── annotation/ │ │ │ │ ├── NacosApi.java │ │ │ │ ├── NacosInjected.java │ │ │ │ └── NacosProperties.java │ │ │ ├── cmdb/ │ │ │ │ ├── pojo/ │ │ │ │ │ ├── Entity.java │ │ │ │ │ ├── EntityEvent.java │ │ │ │ │ ├── EntityEventType.java │ │ │ │ │ ├── Label.java │ │ │ │ │ └── PreservedEntityTypes.java │ │ │ │ └── spi/ │ │ │ │ └── CmdbService.java │ │ │ ├── common/ │ │ │ │ ├── Constants.java │ │ │ │ ├── NodeState.java │ │ │ │ └── ResponseCode.java │ │ │ ├── config/ │ │ │ │ ├── ConfigChangeEvent.java │ │ │ │ ├── ConfigChangeItem.java │ │ │ │ ├── ConfigFactory.java │ │ │ │ ├── ConfigQueryResult.java │ │ │ │ ├── ConfigService.java │ │ │ │ ├── ConfigType.java │ │ │ │ ├── PropertyChangeType.java │ │ │ │ ├── ability/ │ │ │ │ │ ├── ClientConfigAbility.java │ │ │ │ │ └── ServerConfigAbility.java │ │ │ │ ├── annotation/ │ │ │ │ │ ├── NacosConfigListener.java │ │ │ │ │ ├── NacosConfigurationProperties.java │ │ │ │ │ ├── NacosIgnore.java │ │ │ │ │ ├── NacosProperty.java │ │ │ │ │ └── NacosValue.java │ │ │ │ ├── convert/ │ │ │ │ │ └── NacosConfigConverter.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── AbstractConfigFilter.java │ │ │ │ │ ├── IConfigContext.java │ │ │ │ │ ├── IConfigFilter.java │ │ │ │ │ ├── IConfigFilterChain.java │ │ │ │ │ ├── IConfigRequest.java │ │ │ │ │ └── IConfigResponse.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── AbstractFuzzyWatchEventWatcher.java │ │ │ │ │ ├── AbstractListener.java │ │ │ │ │ ├── AbstractSharedListener.java │ │ │ │ │ ├── ConfigChangeParser.java │ │ │ │ │ ├── ConfigFuzzyWatchChangeEvent.java │ │ │ │ │ ├── FuzzyWatchEventWatcher.java │ │ │ │ │ ├── FuzzyWatchLoadWatcher.java │ │ │ │ │ └── Listener.java │ │ │ │ ├── model/ │ │ │ │ │ ├── ConfigBasicInfo.java │ │ │ │ │ ├── ConfigCloneInfo.java │ │ │ │ │ ├── ConfigDetailInfo.java │ │ │ │ │ ├── ConfigGrayInfo.java │ │ │ │ │ ├── ConfigHistoryBasicInfo.java │ │ │ │ │ ├── ConfigHistoryDetailInfo.java │ │ │ │ │ ├── ConfigListenerInfo.java │ │ │ │ │ └── SameConfigPolicy.java │ │ │ │ └── remote/ │ │ │ │ ├── request/ │ │ │ │ │ ├── AbstractConfigRequest.java │ │ │ │ │ ├── AbstractFuzzyWatchNotifyRequest.java │ │ │ │ │ ├── ClientConfigMetricRequest.java │ │ │ │ │ ├── ConfigBatchListenRequest.java │ │ │ │ │ ├── ConfigChangeNotifyRequest.java │ │ │ │ │ ├── ConfigFuzzyWatchChangeNotifyRequest.java │ │ │ │ │ ├── ConfigFuzzyWatchRequest.java │ │ │ │ │ ├── ConfigFuzzyWatchSyncRequest.java │ │ │ │ │ ├── ConfigPublishRequest.java │ │ │ │ │ ├── ConfigQueryRequest.java │ │ │ │ │ ├── ConfigRemoveRequest.java │ │ │ │ │ └── cluster/ │ │ │ │ │ └── ConfigChangeClusterSyncRequest.java │ │ │ │ └── response/ │ │ │ │ ├── ClientConfigMetricResponse.java │ │ │ │ ├── ConfigChangeBatchListenResponse.java │ │ │ │ ├── ConfigChangeNotifyResponse.java │ │ │ │ ├── ConfigFuzzyWatchChangeNotifyResponse.java │ │ │ │ ├── ConfigFuzzyWatchResponse.java │ │ │ │ ├── ConfigFuzzyWatchSyncResponse.java │ │ │ │ ├── ConfigPublishResponse.java │ │ │ │ ├── ConfigQueryResponse.java │ │ │ │ ├── ConfigRemoveResponse.java │ │ │ │ └── cluster/ │ │ │ │ └── ConfigChangeClusterSyncResponse.java │ │ │ ├── exception/ │ │ │ │ ├── NacosException.java │ │ │ │ ├── api/ │ │ │ │ │ └── NacosApiException.java │ │ │ │ └── runtime/ │ │ │ │ ├── NacosDeserializationException.java │ │ │ │ ├── NacosLoadException.java │ │ │ │ ├── NacosRuntimeException.java │ │ │ │ └── NacosSerializationException.java │ │ │ ├── grpc/ │ │ │ │ └── auto/ │ │ │ │ ├── BiRequestStreamGrpc.java │ │ │ │ ├── Metadata.java │ │ │ │ ├── MetadataOrBuilder.java │ │ │ │ ├── NacosGrpcService.java │ │ │ │ ├── Payload.java │ │ │ │ ├── PayloadOrBuilder.java │ │ │ │ └── RequestGrpc.java │ │ │ ├── lock/ │ │ │ │ ├── LockService.java │ │ │ │ ├── NacosLockFactory.java │ │ │ │ ├── common/ │ │ │ │ │ └── LockConstants.java │ │ │ │ ├── constant/ │ │ │ │ │ └── PropertyConstants.java │ │ │ │ ├── model/ │ │ │ │ │ └── LockInstance.java │ │ │ │ └── remote/ │ │ │ │ ├── AbstractLockRequest.java │ │ │ │ ├── LockOperationEnum.java │ │ │ │ ├── request/ │ │ │ │ │ └── LockOperationRequest.java │ │ │ │ └── response/ │ │ │ │ └── LockOperationResponse.java │ │ │ ├── model/ │ │ │ │ ├── NacosForm.java │ │ │ │ ├── Page.java │ │ │ │ ├── response/ │ │ │ │ │ ├── ConnectionInfo.java │ │ │ │ │ ├── ConnectionMetaInfo.java │ │ │ │ │ ├── IdGeneratorInfo.java │ │ │ │ │ ├── NacosMember.java │ │ │ │ │ ├── Namespace.java │ │ │ │ │ ├── ServerLoaderMetric.java │ │ │ │ │ └── ServerLoaderMetrics.java │ │ │ │ └── v2/ │ │ │ │ ├── ErrorCode.java │ │ │ │ ├── Result.java │ │ │ │ └── SupportedLanguage.java │ │ │ ├── naming/ │ │ │ │ ├── CommonParams.java │ │ │ │ ├── NamingFactory.java │ │ │ │ ├── NamingMaintainFactory.java │ │ │ │ ├── NamingMaintainService.java │ │ │ │ ├── NamingResponseCode.java │ │ │ │ ├── NamingService.java │ │ │ │ ├── PreservedMetadataKeys.java │ │ │ │ ├── ability/ │ │ │ │ │ ├── ClientNamingAbility.java │ │ │ │ │ └── ServerNamingAbility.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── AbstractEventListener.java │ │ │ │ │ ├── AbstractFuzzyWatchEventWatcher.java │ │ │ │ │ ├── Event.java │ │ │ │ │ ├── EventListener.java │ │ │ │ │ ├── FuzzyWatchChangeEvent.java │ │ │ │ │ ├── FuzzyWatchEventWatcher.java │ │ │ │ │ ├── FuzzyWatchLoadWatcher.java │ │ │ │ │ └── NamingEvent.java │ │ │ │ ├── pojo/ │ │ │ │ │ ├── Cluster.java │ │ │ │ │ ├── Instance.java │ │ │ │ │ ├── ListView.java │ │ │ │ │ ├── Service.java │ │ │ │ │ ├── ServiceInfo.java │ │ │ │ │ ├── builder/ │ │ │ │ │ │ └── InstanceBuilder.java │ │ │ │ │ ├── healthcheck/ │ │ │ │ │ │ ├── AbstractHealthChecker.java │ │ │ │ │ │ ├── HealthCheckType.java │ │ │ │ │ │ ├── HealthCheckerFactory.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── Http.java │ │ │ │ │ │ ├── Mysql.java │ │ │ │ │ │ └── Tcp.java │ │ │ │ │ └── maintainer/ │ │ │ │ │ ├── ClientPublisherInfo.java │ │ │ │ │ ├── ClientServiceInfo.java │ │ │ │ │ ├── ClientSubscriberInfo.java │ │ │ │ │ ├── ClientSummaryInfo.java │ │ │ │ │ ├── ClusterInfo.java │ │ │ │ │ ├── InstanceMetadataBatchResult.java │ │ │ │ │ ├── MetricsInfo.java │ │ │ │ │ ├── ServiceDetailInfo.java │ │ │ │ │ ├── ServiceView.java │ │ │ │ │ └── SubscriberInfo.java │ │ │ │ ├── remote/ │ │ │ │ │ ├── NamingRemoteConstants.java │ │ │ │ │ ├── request/ │ │ │ │ │ │ ├── AbstractFuzzyWatchNotifyRequest.java │ │ │ │ │ │ ├── AbstractNamingRequest.java │ │ │ │ │ │ ├── BatchInstanceRequest.java │ │ │ │ │ │ ├── InstanceRequest.java │ │ │ │ │ │ ├── NamingFuzzyWatchChangeNotifyRequest.java │ │ │ │ │ │ ├── NamingFuzzyWatchRequest.java │ │ │ │ │ │ ├── NamingFuzzyWatchSyncRequest.java │ │ │ │ │ │ ├── NotifySubscriberRequest.java │ │ │ │ │ │ ├── PersistentInstanceRequest.java │ │ │ │ │ │ ├── ServiceListRequest.java │ │ │ │ │ │ ├── ServiceQueryRequest.java │ │ │ │ │ │ └── SubscribeServiceRequest.java │ │ │ │ │ └── response/ │ │ │ │ │ ├── BatchInstanceResponse.java │ │ │ │ │ ├── InstanceResponse.java │ │ │ │ │ ├── NamingFuzzyWatchChangeNotifyResponse.java │ │ │ │ │ ├── NamingFuzzyWatchResponse.java │ │ │ │ │ ├── NamingFuzzyWatchSyncResponse.java │ │ │ │ │ ├── NotifySubscriberResponse.java │ │ │ │ │ ├── QueryServiceResponse.java │ │ │ │ │ ├── ServiceListResponse.java │ │ │ │ │ └── SubscribeServiceResponse.java │ │ │ │ ├── selector/ │ │ │ │ │ ├── NamingContext.java │ │ │ │ │ ├── NamingResult.java │ │ │ │ │ └── NamingSelector.java │ │ │ │ ├── spi/ │ │ │ │ │ └── generator/ │ │ │ │ │ └── InstanceIdGenerator.java │ │ │ │ └── utils/ │ │ │ │ └── NamingUtils.java │ │ │ ├── plugin/ │ │ │ │ ├── ConfigItemDefinition.java │ │ │ │ ├── ConfigItemType.java │ │ │ │ ├── PluginConfigSpec.java │ │ │ │ ├── PluginProvider.java │ │ │ │ ├── PluginStateChecker.java │ │ │ │ ├── PluginStateCheckerHolder.java │ │ │ │ └── PluginType.java │ │ │ ├── remote/ │ │ │ │ ├── AbstractPushCallBack.java │ │ │ │ ├── AbstractRequestCallBack.java │ │ │ │ ├── DefaultRequestFuture.java │ │ │ │ ├── Payload.java │ │ │ │ ├── PushCallBack.java │ │ │ │ ├── RemoteConstants.java │ │ │ │ ├── RequestCallBack.java │ │ │ │ ├── RequestFuture.java │ │ │ │ ├── Requester.java │ │ │ │ ├── RpcScheduledExecutor.java │ │ │ │ ├── ability/ │ │ │ │ │ ├── ClientRemoteAbility.java │ │ │ │ │ └── ServerRemoteAbility.java │ │ │ │ ├── request/ │ │ │ │ │ ├── ClientDetectionRequest.java │ │ │ │ │ ├── ConnectResetRequest.java │ │ │ │ │ ├── ConnectionSetupRequest.java │ │ │ │ │ ├── HealthCheckRequest.java │ │ │ │ │ ├── InternalRequest.java │ │ │ │ │ ├── PushAckRequest.java │ │ │ │ │ ├── Request.java │ │ │ │ │ ├── RequestMeta.java │ │ │ │ │ ├── ServerCheckRequest.java │ │ │ │ │ ├── ServerLoaderInfoRequest.java │ │ │ │ │ ├── ServerReloadRequest.java │ │ │ │ │ ├── ServerRequest.java │ │ │ │ │ └── SetupAckRequest.java │ │ │ │ └── response/ │ │ │ │ ├── ClientDetectionResponse.java │ │ │ │ ├── ConnectResetResponse.java │ │ │ │ ├── ErrorResponse.java │ │ │ │ ├── HealthCheckResponse.java │ │ │ │ ├── Response.java │ │ │ │ ├── ResponseCode.java │ │ │ │ ├── ServerCheckResponse.java │ │ │ │ ├── ServerLoaderInfoResponse.java │ │ │ │ ├── ServerReloadResponse.java │ │ │ │ └── SetupAckResponse.java │ │ │ ├── selector/ │ │ │ │ ├── AbstractCmdbSelector.java │ │ │ │ ├── AbstractSelector.java │ │ │ │ ├── ExpressionSelector.java │ │ │ │ ├── NoneSelector.java │ │ │ │ ├── Selector.java │ │ │ │ ├── SelectorType.java │ │ │ │ ├── client/ │ │ │ │ │ ├── SelectResult.java │ │ │ │ │ └── Selector.java │ │ │ │ └── context/ │ │ │ │ ├── CmdbContext.java │ │ │ │ └── SelectorContextBuilder.java │ │ │ └── utils/ │ │ │ ├── NetUtils.java │ │ │ └── StringUtils.java │ │ ├── proto/ │ │ │ └── nacos_grpc_service.proto │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.api.remote.Payload │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ ├── api/ │ │ ├── NacosFactoryTest.java │ │ ├── ability/ │ │ │ ├── ClientAbilitiesTest.java │ │ │ ├── ServerAbilitiesTest.java │ │ │ └── register/ │ │ │ └── impl/ │ │ │ ├── ClusterClientAbilitiesTest.java │ │ │ ├── SdkClientAbilitiesTest.java │ │ │ └── ServerAbilitiesTest.java │ │ ├── ai/ │ │ │ ├── A2aServiceDefaultMethodTest.java │ │ │ ├── AiFactoryTest.java │ │ │ ├── constant/ │ │ │ │ └── AiConstantsStatusMappingTest.java │ │ │ ├── listener/ │ │ │ │ ├── NacosAgentCardEventTest.java │ │ │ │ ├── NacosAiListenerDefaultMethodTest.java │ │ │ │ └── NacosMcpServerEventTest.java │ │ │ ├── model/ │ │ │ │ ├── a2a/ │ │ │ │ │ ├── AgentAuthenticationTest.java │ │ │ │ │ ├── AgentCapabilitiesTest.java │ │ │ │ │ ├── AgentCardBasicInfoTest.java │ │ │ │ │ ├── AgentCardDetailInfoTest.java │ │ │ │ │ ├── AgentCardTest.java │ │ │ │ │ ├── AgentCardVersionInfoTest.java │ │ │ │ │ ├── AgentEndpointTest.java │ │ │ │ │ ├── AgentExtensionTest.java │ │ │ │ │ ├── AgentInterfaceTest.java │ │ │ │ │ ├── AgentProviderTest.java │ │ │ │ │ ├── AgentSkillTest.java │ │ │ │ │ ├── AgentVersionDetailTest.java │ │ │ │ │ └── SecuritySchemeTest.java │ │ │ │ ├── mcp/ │ │ │ │ │ ├── EncryptObjectTest.java │ │ │ │ │ ├── FrontEndpointConfigTest.java │ │ │ │ │ ├── McpCapabilityTest.java │ │ │ │ │ ├── McpEndpointInfoTest.java │ │ │ │ │ ├── McpEndpointSpecTest.java │ │ │ │ │ ├── McpServerBasicInfoEnhancedFieldsTest.java │ │ │ │ │ ├── McpServerBasicInfoTest.java │ │ │ │ │ ├── McpServerDetailInfoTest.java │ │ │ │ │ ├── McpServerImportRequestTest.java │ │ │ │ │ ├── McpServerImportResponseTest.java │ │ │ │ │ ├── McpServerImportResultTest.java │ │ │ │ │ ├── McpServerImportValidationResultTest.java │ │ │ │ │ ├── McpServerRemoteServiceConfigTest.java │ │ │ │ │ ├── McpServerValidationItemTest.java │ │ │ │ │ ├── McpServerVersionInfoTest.java │ │ │ │ │ ├── McpServiceRefTest.java │ │ │ │ │ ├── McpToolMetaTest.java │ │ │ │ │ ├── McpToolSpecificationTest.java │ │ │ │ │ ├── McpToolTest.java │ │ │ │ │ ├── SecuritySchemeTest.java │ │ │ │ │ └── registry/ │ │ │ │ │ ├── IconTest.java │ │ │ │ │ ├── InputTest.java │ │ │ │ │ ├── InputWithVariablesTest.java │ │ │ │ │ ├── KeyValueInputTest.java │ │ │ │ │ ├── McpErrorResponseTest.java │ │ │ │ │ ├── McpRegistryServerListTest.java │ │ │ │ │ ├── McpServerStatusEnumTest.java │ │ │ │ │ ├── MetaTest.java │ │ │ │ │ ├── NacosMcpRegistryServerDetailTest.java │ │ │ │ │ ├── NamedArgumentTest.java │ │ │ │ │ ├── OfficialMetaTest.java │ │ │ │ │ ├── PackageTest.java │ │ │ │ │ ├── PackageTransportTest.java │ │ │ │ │ ├── PositionalArgumentTest.java │ │ │ │ │ ├── RemoteTest.java │ │ │ │ │ ├── RepositoryTest.java │ │ │ │ │ ├── ServerResponseTest.java │ │ │ │ │ ├── ServerVersionDetailTest.java │ │ │ │ │ └── TransportTest.java │ │ │ │ └── skills/ │ │ │ │ └── SkillUtilsTest.java │ │ │ └── remote/ │ │ │ ├── request/ │ │ │ │ ├── AbstractAgentRequestTest.java │ │ │ │ ├── AgentEndpointRequestTest.java │ │ │ │ ├── BatchAgentEndpointRequestTest.java │ │ │ │ ├── McpServerEndpointRequestTest.java │ │ │ │ ├── QueryAgentCardRequestTest.java │ │ │ │ ├── QueryMcpServerRequestTest.java │ │ │ │ ├── ReleaseAgentCardRequestTest.java │ │ │ │ └── ReleaseMcpServerRequestTest.java │ │ │ └── response/ │ │ │ ├── AgentEndpointResponseTest.java │ │ │ ├── McpServerEndpointResponseTest.java │ │ │ ├── QueryAgentCardResponseTest.java │ │ │ ├── QueryMcpServerResponseTest.java │ │ │ ├── ReleaseAgentCardResponseTest.java │ │ │ └── ReleaseMcpServerResponseTest.java │ │ ├── annotation/ │ │ │ └── NacosPropertiesTest.java │ │ ├── cmdb/ │ │ │ └── pojo/ │ │ │ ├── EntityEventTest.java │ │ │ ├── EntityTest.java │ │ │ └── LabelTest.java │ │ ├── config/ │ │ │ ├── ConfigChangeEventTest.java │ │ │ ├── ConfigChangeItemTest.java │ │ │ ├── ConfigFactoryTest.java │ │ │ ├── ConfigTypeTest.java │ │ │ ├── ability/ │ │ │ │ ├── ClientRemoteAbilityTest.java │ │ │ │ ├── ServerConfigAbilityTest.java │ │ │ │ └── ServerRemoteAbilityTest.java │ │ │ ├── listener/ │ │ │ │ ├── AbstractFuzzyWatchEventWatcherTest.java │ │ │ │ ├── AbstractListenerTest.java │ │ │ │ ├── AbstractSharedListenerTest.java │ │ │ │ └── ConfigFuzzyWatchChangeEventTest.java │ │ │ ├── model/ │ │ │ │ ├── ConfigCloneInfoTest.java │ │ │ │ ├── ConfigHistoryInfoTest.java │ │ │ │ ├── ConfigInfoTest.java │ │ │ │ ├── ConfigListenerInfoTest.java │ │ │ │ └── SameConfigPolicyTest.java │ │ │ └── remote/ │ │ │ ├── request/ │ │ │ │ ├── BasedConfigRequestTest.java │ │ │ │ ├── ClientConfigMetricRequestTest.java │ │ │ │ ├── ConfigBatchListenRequestTest.java │ │ │ │ ├── ConfigChangeNotifyRequestTest.java │ │ │ │ ├── ConfigFuzzyWatchChangeNotifyRequestTest.java │ │ │ │ ├── ConfigFuzzyWatchRequestTest.java │ │ │ │ ├── ConfigFuzzyWatchSyncRequestTest.java │ │ │ │ ├── ConfigPublishRequestTest.java │ │ │ │ ├── ConfigQueryRequestTest.java │ │ │ │ ├── ConfigRemoveRequestTest.java │ │ │ │ └── cluster/ │ │ │ │ └── ConfigChangeClusterSyncRequestTest.java │ │ │ └── response/ │ │ │ ├── BasedConfigResponseTest.java │ │ │ ├── ClientConfigMetricResponseTest.java │ │ │ ├── ConfigChangeBatchListenResponseTest.java │ │ │ ├── ConfigChangeNotifyResponseTest.java │ │ │ ├── ConfigPublishResponseTest.java │ │ │ ├── ConfigQueryResponseTest.java │ │ │ ├── ConfigRemoveResponseTest.java │ │ │ └── cluster/ │ │ │ └── ConfigChangeClusterSyncResponseTest.java │ │ ├── exception/ │ │ │ ├── NacosExceptionTest.java │ │ │ ├── api/ │ │ │ │ └── NacosApiExceptionTest.java │ │ │ └── runtime/ │ │ │ ├── NacosDeserializationExceptionTest.java │ │ │ ├── NacosLoadExceptionTest.java │ │ │ ├── NacosRuntimeExceptionTest.java │ │ │ └── NacosSerializationExceptionTest.java │ │ ├── lock/ │ │ │ ├── NacosLockFactoryTest.java │ │ │ ├── model/ │ │ │ │ └── LockInstanceTest.java │ │ │ └── remote/ │ │ │ ├── request/ │ │ │ │ └── LockOperationRequestTest.java │ │ │ └── response/ │ │ │ └── LockOperationResponseTest.java │ │ ├── model/ │ │ │ ├── PageTest.java │ │ │ ├── response/ │ │ │ │ ├── ConnectionInfoTest.java │ │ │ │ ├── InstanceIdGeneratorInfoTest.java │ │ │ │ ├── NacosMemberTest.java │ │ │ │ ├── NamespaceTest.java │ │ │ │ └── ServerLoaderMetricsTest.java │ │ │ └── v2/ │ │ │ ├── ErrorCodeTest.java │ │ │ ├── ResultTest.java │ │ │ └── SupportedLanguageTest.java │ │ ├── naming/ │ │ │ ├── NamingFactoryTest.java │ │ │ ├── ability/ │ │ │ │ ├── ClientNamingAbilityTest.java │ │ │ │ └── ServerNamingAbilityTest.java │ │ │ ├── listener/ │ │ │ │ ├── AbstractFuzzyWatchEventWatcherTest.java │ │ │ │ ├── FuzzyWatchChangeEventTest.java │ │ │ │ └── NamingEventTest.java │ │ │ ├── pojo/ │ │ │ │ ├── ClusterTest.java │ │ │ │ ├── InstanceTest.java │ │ │ │ ├── ListViewTest.java │ │ │ │ ├── ServiceInfoTest.java │ │ │ │ ├── ServiceTest.java │ │ │ │ ├── builder/ │ │ │ │ │ └── InstanceBuilderTest.java │ │ │ │ ├── healthcheck/ │ │ │ │ │ ├── AbstractHealthCheckerTest.java │ │ │ │ │ ├── HealthCheckTypeTest.java │ │ │ │ │ ├── HealthCheckerFactoryTest.java │ │ │ │ │ ├── TestChecker.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── HttpTest.java │ │ │ │ │ ├── MysqlTest.java │ │ │ │ │ └── TcpTest.java │ │ │ │ └── maintainer/ │ │ │ │ ├── ClientPublisherInfoTest.java │ │ │ │ ├── ClientServiceInfoTest.java │ │ │ │ ├── ClientSubscriberInfoTest.java │ │ │ │ ├── ClientSummaryInfoTest.java │ │ │ │ ├── ClusterInfoTest.java │ │ │ │ ├── InstanceMetadataBatchResultTest.java │ │ │ │ ├── MetricsInfoTest.java │ │ │ │ ├── ServiceDetailInfoTest.java │ │ │ │ ├── ServiceViewTest.java │ │ │ │ └── SubscriberInfoTest.java │ │ │ ├── remote/ │ │ │ │ ├── request/ │ │ │ │ │ ├── BasedNamingRequestTest.java │ │ │ │ │ ├── BatchInstanceRequestTest.java │ │ │ │ │ ├── InstanceRequestTest.java │ │ │ │ │ ├── NamingFuzzyWatchChangeNotifyRequestTest.java │ │ │ │ │ ├── NamingFuzzyWatchRequestTest.java │ │ │ │ │ ├── NamingFuzzyWatchSyncRequestTest.java │ │ │ │ │ ├── NotifySubscriberRequestTest.java │ │ │ │ │ ├── PersistentInstanceRequestTest.java │ │ │ │ │ ├── ServiceListRequestTest.java │ │ │ │ │ ├── ServiceQueryRequestTest.java │ │ │ │ │ └── SubscribeServiceRequestTest.java │ │ │ │ └── response/ │ │ │ │ ├── BatchInstanceResponseTest.java │ │ │ │ ├── InstanceResponseTest.java │ │ │ │ ├── NamingFuzzyWatchChangeNotifyResponseTest.java │ │ │ │ ├── NamingFuzzyWatchResponseTest.java │ │ │ │ ├── NamingFuzzyWatchSyncResponseTest.java │ │ │ │ ├── NotifySubscriberResponseTest.java │ │ │ │ ├── QueryServiceResponseTest.java │ │ │ │ ├── ServiceListResponseTest.java │ │ │ │ └── SubscribeServiceResponseTest.java │ │ │ └── utils/ │ │ │ └── NamingUtilsTest.java │ │ ├── remote/ │ │ │ ├── AbstractPushCallBackTest.java │ │ │ ├── AbstractRequestCallBackTest.java │ │ │ ├── DefaultRequestFutureTest.java │ │ │ ├── RpcScheduledExecutorTest.java │ │ │ ├── ability/ │ │ │ │ ├── ClientRemoteAbilityTest.java │ │ │ │ └── ServerRemoteAbilityTest.java │ │ │ ├── request/ │ │ │ │ ├── BasicRequestTest.java │ │ │ │ ├── ConnectResetRequestTest.java │ │ │ │ ├── ConnectionSetupRequestTest.java │ │ │ │ ├── EmptyContentRequestTest.java │ │ │ │ ├── PushAckRequestTest.java │ │ │ │ ├── RequestMetaTest.java │ │ │ │ ├── RequestTest.java │ │ │ │ ├── ServerReloadRequestTest.java │ │ │ │ └── SetupAckRequestTest.java │ │ │ └── response/ │ │ │ ├── EmptyContentResponseTest.java │ │ │ ├── ErrorResponseTest.java │ │ │ ├── ServerCheckResponseTest.java │ │ │ └── ServerLoaderInfoResponseTest.java │ │ ├── selector/ │ │ │ ├── AbstractCmdbSelectorTest.java │ │ │ ├── ExpressionSelectorTest.java │ │ │ ├── NoneSelectorTest.java │ │ │ └── context/ │ │ │ └── CmdbContextTest.java │ │ └── utils/ │ │ ├── AbilityKeyTest.java │ │ ├── NetUtilsTest.java │ │ └── StringUtilsTest.java │ └── client/ │ ├── ai/ │ │ └── NacosAiService.java │ ├── config/ │ │ └── NacosConfigService.java │ ├── lock/ │ │ └── NacosLockService.java │ └── naming/ │ └── NacosNamingService.java ├── auth/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── auth/ │ │ ├── AbstractProtocolAuthService.java │ │ ├── GrpcProtocolAuthService.java │ │ ├── HttpProtocolAuthService.java │ │ ├── ProtocolAuthService.java │ │ ├── annotation/ │ │ │ └── Secured.java │ │ ├── config/ │ │ │ ├── AuthErrorCode.java │ │ │ ├── NacosAuthConfig.java │ │ │ └── NacosAuthConfigHolder.java │ │ ├── context/ │ │ │ ├── GrpcIdentityContextBuilder.java │ │ │ ├── HttpIdentityContextBuilder.java │ │ │ └── IdentityContextBuilder.java │ │ ├── parser/ │ │ │ ├── AbstractResourceParser.java │ │ │ ├── DefaultResourceParser.java │ │ │ ├── ResourceParser.java │ │ │ ├── grpc/ │ │ │ │ ├── AbstractGrpcResourceParser.java │ │ │ │ ├── AiGrpcResourceParser.java │ │ │ │ ├── ConfigGrpcResourceParser.java │ │ │ │ └── NamingGrpcResourceParser.java │ │ │ └── http/ │ │ │ ├── AbstractHttpResourceParser.java │ │ │ ├── AiHttpResourceParser.java │ │ │ ├── ConfigHttpResourceParser.java │ │ │ └── NamingHttpResourceParser.java │ │ ├── serveridentity/ │ │ │ ├── DefaultChecker.java │ │ │ ├── ServerIdentity.java │ │ │ ├── ServerIdentityChecker.java │ │ │ ├── ServerIdentityCheckerHolder.java │ │ │ └── ServerIdentityResult.java │ │ └── util/ │ │ ├── AuthHeaderUtil.java │ │ └── Loggers.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── auth/ │ │ ├── GrpcProtocolAuthServiceTest.java │ │ ├── HttpProtocolAuthServiceTest.java │ │ ├── config/ │ │ │ ├── AuthErrorCodeTest.java │ │ │ └── NacosAuthConfigHolderTest.java │ │ ├── context/ │ │ │ ├── GrpcIdentityContextBuilderTest.java │ │ │ └── HtppIdentityContextBuilderTest.java │ │ ├── mock/ │ │ │ ├── MockAuthPluginService.java │ │ │ └── MockResourceParser.java │ │ ├── parser/ │ │ │ ├── grpc/ │ │ │ │ ├── AiGrpcResourceParserTest.java │ │ │ │ ├── ConfigGrpcResourceParserTest.java │ │ │ │ └── NamingGrpcResourceParserTest.java │ │ │ └── http/ │ │ │ ├── AiHttpResourceParserTest.java │ │ │ ├── ConfigHttpResourceParserTest.java │ │ │ └── NamingHttpResourceParserTest.java │ │ ├── serveridentity/ │ │ │ ├── ServerIdentityCheckerHolderTest.java │ │ │ └── ServerIdentityTest.java │ │ └── util/ │ │ ├── AuthHeaderUtilTest.java │ │ └── LoggersTest.java │ └── resources/ │ └── META-INF/ │ └── services/ │ └── com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService ├── bootstrap/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── bootstrap/ │ │ └── NacosBootstrap.java │ └── resources/ │ └── application.properties ├── client/ │ ├── filter-config.json │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── client/ │ │ │ ├── ability/ │ │ │ │ └── ClientAbilityControlManager.java │ │ │ ├── ai/ │ │ │ │ ├── NacosAiService.java │ │ │ │ ├── cache/ │ │ │ │ │ ├── NacosAgentCardCacheHolder.java │ │ │ │ │ ├── NacosMcpServerCacheHolder.java │ │ │ │ │ ├── NacosPromptCacheHolder.java │ │ │ │ │ └── NacosSkillCacheHolder.java │ │ │ │ ├── event/ │ │ │ │ │ ├── AbstractAiListenerInvoker.java │ │ │ │ │ ├── AgentCardChangedEvent.java │ │ │ │ │ ├── AgentCardListenerInvoker.java │ │ │ │ │ ├── AiChangeNotifier.java │ │ │ │ │ ├── McpServerChangedEvent.java │ │ │ │ │ ├── McpServerListenerInvoker.java │ │ │ │ │ ├── PromptChangedEvent.java │ │ │ │ │ ├── PromptListenerInvoker.java │ │ │ │ │ ├── SkillChangedEvent.java │ │ │ │ │ └── SkillListenerInvoker.java │ │ │ │ ├── remote/ │ │ │ │ │ ├── AiClientProxy.java │ │ │ │ │ ├── AiGrpcClient.java │ │ │ │ │ ├── AiHttpClientProxy.java │ │ │ │ │ └── redo/ │ │ │ │ │ ├── AgentEndpointRedoData.java │ │ │ │ │ ├── AgentEndpointWrapper.java │ │ │ │ │ ├── AiGrpcRedoService.java │ │ │ │ │ ├── AiRedoScheduledTask.java │ │ │ │ │ ├── McpServerEndpoint.java │ │ │ │ │ └── McpServerEndpointRedoData.java │ │ │ │ └── utils/ │ │ │ │ └── CacheKeyUtils.java │ │ │ ├── config/ │ │ │ │ ├── NacosConfigService.java │ │ │ │ ├── common/ │ │ │ │ │ ├── ConfigConstants.java │ │ │ │ │ └── GroupKey.java │ │ │ │ ├── filter/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── ConfigContext.java │ │ │ │ │ ├── ConfigEncryptionFilter.java │ │ │ │ │ ├── ConfigFilterChainManager.java │ │ │ │ │ ├── ConfigRequest.java │ │ │ │ │ └── ConfigResponse.java │ │ │ │ ├── http/ │ │ │ │ │ ├── HttpAgent.java │ │ │ │ │ ├── MetricsHttpAgent.java │ │ │ │ │ └── ServerHttpAgent.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── AbstractConfigChangeParser.java │ │ │ │ │ ├── CacheData.java │ │ │ │ │ ├── ChangeNotifyBlockEvent.java │ │ │ │ │ ├── ClientFuzzyWatchNotifyRequestHandler.java │ │ │ │ │ ├── ClientWorker.java │ │ │ │ │ ├── ConfigChangeHandler.java │ │ │ │ │ ├── ConfigFuzzyWatchContext.java │ │ │ │ │ ├── ConfigFuzzyWatchGroupKeyHolder.java │ │ │ │ │ ├── ConfigFuzzyWatchLoadEvent.java │ │ │ │ │ ├── ConfigFuzzyWatchNotifyEvent.java │ │ │ │ │ ├── ConfigFuzzyWatcherWrapper.java │ │ │ │ │ ├── ConfigHttpClientManager.java │ │ │ │ │ ├── ConfigServerListManager.java │ │ │ │ │ ├── ConfigTransportClient.java │ │ │ │ │ ├── Limiter.java │ │ │ │ │ ├── LocalConfigInfoProcessor.java │ │ │ │ │ ├── LocalEncryptedDataKeyProcessor.java │ │ │ │ │ ├── PropertiesChangeParser.java │ │ │ │ │ └── YmlChangeParser.java │ │ │ │ ├── listener/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── AbstractConfigChangeListener.java │ │ │ │ │ └── PropertiesListener.java │ │ │ │ └── utils/ │ │ │ │ ├── ContentUtils.java │ │ │ │ ├── JvmUtil.java │ │ │ │ ├── ParamUtils.java │ │ │ │ └── SnapShotSwitch.java │ │ │ ├── lock/ │ │ │ │ ├── NacosLockService.java │ │ │ │ ├── core/ │ │ │ │ │ ├── NLock.java │ │ │ │ │ └── NLockFactory.java │ │ │ │ └── remote/ │ │ │ │ ├── AbstractLockClient.java │ │ │ │ ├── LockClient.java │ │ │ │ └── grpc/ │ │ │ │ └── LockGrpcClient.java │ │ │ ├── logging/ │ │ │ │ └── NacosLogging.java │ │ │ ├── monitor/ │ │ │ │ └── MetricsMonitor.java │ │ │ ├── naming/ │ │ │ │ ├── NacosNamingMaintainService.java │ │ │ │ ├── NacosNamingService.java │ │ │ │ ├── backups/ │ │ │ │ │ ├── FailoverData.java │ │ │ │ │ ├── FailoverDataSource.java │ │ │ │ │ ├── FailoverReactor.java │ │ │ │ │ ├── FailoverSwitch.java │ │ │ │ │ ├── NamingFailoverData.java │ │ │ │ │ └── datasource/ │ │ │ │ │ └── DiskFailoverDataSource.java │ │ │ │ ├── cache/ │ │ │ │ │ ├── DiskCache.java │ │ │ │ │ ├── FuzzyWatchEventWatcherWrapper.java │ │ │ │ │ ├── InstancesDiffer.java │ │ │ │ │ ├── NamingFuzzyWatchContext.java │ │ │ │ │ ├── NamingFuzzyWatchServiceListHolder.java │ │ │ │ │ └── ServiceInfoHolder.java │ │ │ │ ├── core/ │ │ │ │ │ ├── Balancer.java │ │ │ │ │ ├── NamingServerListManager.java │ │ │ │ │ ├── ProtectMode.java │ │ │ │ │ └── ServiceInfoUpdateService.java │ │ │ │ ├── event/ │ │ │ │ │ ├── InstancesChangeEvent.java │ │ │ │ │ ├── InstancesChangeNotifier.java │ │ │ │ │ ├── InstancesDiff.java │ │ │ │ │ ├── NamingFuzzyWatchLoadEvent.java │ │ │ │ │ └── NamingFuzzyWatchNotifyEvent.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── AbstractNamingChangeListener.java │ │ │ │ │ └── NamingChangeEvent.java │ │ │ │ ├── remote/ │ │ │ │ │ ├── AbstractNamingClientProxy.java │ │ │ │ │ ├── NamingClientProxy.java │ │ │ │ │ ├── NamingClientProxyDelegate.java │ │ │ │ │ ├── gprc/ │ │ │ │ │ │ ├── NamingFuzzyWatchNotifyRequestHandler.java │ │ │ │ │ │ ├── NamingGrpcClientProxy.java │ │ │ │ │ │ ├── NamingPushRequestHandler.java │ │ │ │ │ │ └── redo/ │ │ │ │ │ │ ├── NamingGrpcRedoService.java │ │ │ │ │ │ ├── RedoScheduledTask.java │ │ │ │ │ │ └── data/ │ │ │ │ │ │ ├── BatchInstanceRedoData.java │ │ │ │ │ │ ├── InstanceRedoData.java │ │ │ │ │ │ ├── NamingRedoData.java │ │ │ │ │ │ └── SubscriberRedoData.java │ │ │ │ │ └── http/ │ │ │ │ │ ├── NamingHttpClientManager.java │ │ │ │ │ └── NamingHttpClientProxy.java │ │ │ │ ├── selector/ │ │ │ │ │ ├── DefaultNamingSelector.java │ │ │ │ │ ├── NamingListenerInvoker.java │ │ │ │ │ ├── NamingSelectorFactory.java │ │ │ │ │ ├── NamingSelectorWrapper.java │ │ │ │ │ └── ServiceInfoContext.java │ │ │ │ └── utils/ │ │ │ │ ├── CacheDirUtil.java │ │ │ │ ├── Chooser.java │ │ │ │ ├── GenericPoller.java │ │ │ │ ├── InitUtils.java │ │ │ │ ├── Pair.java │ │ │ │ ├── Poller.java │ │ │ │ └── UtilAndComs.java │ │ │ ├── redo/ │ │ │ │ ├── data/ │ │ │ │ │ └── RedoData.java │ │ │ │ └── service/ │ │ │ │ ├── AbstractRedoService.java │ │ │ │ └── AbstractRedoTask.java │ │ │ ├── security/ │ │ │ │ └── SecurityProxy.java │ │ │ ├── selector/ │ │ │ │ ├── AbstractSelectorWrapper.java │ │ │ │ ├── ListenerInvoker.java │ │ │ │ └── SelectorManager.java │ │ │ └── utils/ │ │ │ ├── ConcurrentDiskUtil.java │ │ │ ├── EnvUtil.java │ │ │ ├── LogUtils.java │ │ │ ├── ParamUtil.java │ │ │ ├── PreInitUtils.java │ │ │ └── ValidatorUtils.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── native-image/ │ │ │ └── com.alibaba.nacos/ │ │ │ └── nacos-client/ │ │ │ ├── jni-config.json │ │ │ ├── predefined-classes-config.json │ │ │ ├── proxy-config.json │ │ │ ├── reflect-config.json │ │ │ ├── resource-config.json │ │ │ └── serialization-config.json │ │ └── services/ │ │ ├── com.alibaba.nacos.api.config.filter.IConfigFilter │ │ ├── com.alibaba.nacos.client.naming.backups.FailoverDataSource │ │ └── com.alibaba.nacos.common.ability.AbstractAbilityControlManager │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── client/ │ │ ├── ability/ │ │ │ ├── AbilityTest.java │ │ │ └── ClientAbilityControlManagerTest.java │ │ ├── ai/ │ │ │ ├── NacosAiServiceTest.java │ │ │ ├── cache/ │ │ │ │ ├── NacosMcpServerCacheHolderTest.java │ │ │ │ └── NacosPromptCacheHolderTest.java │ │ │ ├── event/ │ │ │ │ ├── AiChangeNotifierPromptTest.java │ │ │ │ ├── AiChangeNotifierTest.java │ │ │ │ └── McpServerListenerInvokerTest.java │ │ │ ├── remote/ │ │ │ │ ├── AiGrpcClientTest.java │ │ │ │ ├── AiHttpClientProxyTest.java │ │ │ │ └── redo/ │ │ │ │ ├── AiGrpcRedoServiceTest.java │ │ │ │ ├── AiRedoScheduledTaskTest.java │ │ │ │ ├── McpServerEndpointRedoDataTest.java │ │ │ │ └── McpServerEndpointTest.java │ │ │ └── utils/ │ │ │ └── CacheKeyUtilsTest.java │ │ ├── config/ │ │ │ ├── NacosConfigServiceTest.java │ │ │ ├── common/ │ │ │ │ └── GroupKeyTest.java │ │ │ ├── filter/ │ │ │ │ └── impl/ │ │ │ │ ├── ConfigContextTest.java │ │ │ │ ├── ConfigEncryptionFilterTest.java │ │ │ │ ├── ConfigEncryptionFilterTest1.java │ │ │ │ ├── ConfigFilterChainManagerTest.java │ │ │ │ ├── ConfigFilterChainTest.java │ │ │ │ ├── ConfigRequestTest.java │ │ │ │ ├── ConfigResponseTest.java │ │ │ │ ├── DemoFilter1.java │ │ │ │ └── DemoFilter2.java │ │ │ ├── http/ │ │ │ │ ├── MetricsHttpAgentTest.java │ │ │ │ └── ServerHttpAgentTest.java │ │ │ ├── impl/ │ │ │ │ ├── CacheDataTest.java │ │ │ │ ├── ClientWorkerTest.java │ │ │ │ ├── ConfigChangeHandlerTest.java │ │ │ │ ├── ConfigFuzzyWatchGroupKeyHolderTest.java │ │ │ │ ├── ConfigHttpClientManagerTest.java │ │ │ │ ├── ConfigServerListManagerTest.java │ │ │ │ ├── LimiterTest.java │ │ │ │ ├── PropertiesChangeParserTest.java │ │ │ │ └── YmlChangeParserTest.java │ │ │ ├── listener/ │ │ │ │ └── impl/ │ │ │ │ ├── AbstractConfigChangeListenerTest.java │ │ │ │ └── PropertiesListenerTest.java │ │ │ └── utils/ │ │ │ ├── ContentUtilsTest.java │ │ │ ├── JvmUtilTest.java │ │ │ ├── ParamUtilsTest.java │ │ │ └── SnapShotSwitchTest.java │ │ ├── lock/ │ │ │ ├── NacosLockServiceTest.java │ │ │ └── remote/ │ │ │ └── grpc/ │ │ │ └── LockGrpcClientTest.java │ │ ├── logging/ │ │ │ └── NacosLoggingTest.java │ │ ├── naming/ │ │ │ ├── NacosNamingMaintainServiceTest.java │ │ │ ├── NacosNamingServiceTest.java │ │ │ ├── backups/ │ │ │ │ ├── FailoverReactorTest.java │ │ │ │ └── datasource/ │ │ │ │ └── DiskFailoverDataSourceTest.java │ │ │ ├── cache/ │ │ │ │ ├── DiskCacheTest.java │ │ │ │ ├── NamingFuzzyWatchServiceListHolderTest.java │ │ │ │ └── ServiceInfoHolderTest.java │ │ │ ├── core/ │ │ │ │ ├── BalancerTest.java │ │ │ │ ├── NamingServerListManagerTest.java │ │ │ │ ├── ProtectModeTest.java │ │ │ │ └── ServiceInfoUpdateServiceTest.java │ │ │ ├── event/ │ │ │ │ ├── InstancesChangeEventTest.java │ │ │ │ ├── InstancesChangeNotifierTest.java │ │ │ │ └── InstancesDiffTest.java │ │ │ ├── listener/ │ │ │ │ └── NamingChangeEventTest.java │ │ │ ├── remote/ │ │ │ │ ├── AbstractNamingClientProxyTest.java │ │ │ │ ├── NamingClientProxyDelegateTest.java │ │ │ │ ├── TestConnection.java │ │ │ │ ├── gprc/ │ │ │ │ │ ├── NamingGrpcClientProxyTest.java │ │ │ │ │ ├── NamingPushRequestHandlerTest.java │ │ │ │ │ └── redo/ │ │ │ │ │ ├── NamingGrpcRedoServiceTest.java │ │ │ │ │ ├── RedoScheduledTaskTest.java │ │ │ │ │ └── data/ │ │ │ │ │ ├── BatchInstanceRedoDataTest.java │ │ │ │ │ └── InstanceRedoDataTest.java │ │ │ │ └── http/ │ │ │ │ ├── NamingHttpClientManagerTest.java │ │ │ │ └── NamingHttpClientProxyTest.java │ │ │ ├── selector/ │ │ │ │ ├── DefaultNamingSelectorTest.java │ │ │ │ ├── NamingListenerInvokerTest.java │ │ │ │ ├── NamingSelectorFactoryTest.java │ │ │ │ ├── NamingSelectorWrapperTest.java │ │ │ │ └── ServiceInfoContextTest.java │ │ │ └── utils/ │ │ │ ├── CacheDirUtilTest.java │ │ │ ├── ChooserTest.java │ │ │ ├── ConcurrentDiskUtilTest.java │ │ │ ├── GenericPollerTest.java │ │ │ ├── InitUtilsTest.java │ │ │ └── PairTest.java │ │ ├── redo/ │ │ │ ├── data/ │ │ │ │ └── RedoDataTest.java │ │ │ └── service/ │ │ │ ├── AbstractRedoServiceTest.java │ │ │ └── AbstractRedoTaskTest.java │ │ ├── security/ │ │ │ └── SecurityProxyTest.java │ │ ├── selector/ │ │ │ ├── AbstractSelectorWrapperTest.java │ │ │ └── SelectorManagerTest.java │ │ └── utils/ │ │ ├── EnvUtilTest.java │ │ ├── LogUtilsTest.java │ │ ├── ParamUtilTest.java │ │ ├── PreInitUtilsTest.java │ │ ├── StringUtilsTest.java │ │ └── ValidatorUtilsTest.java │ └── resources/ │ ├── disk_cache_test/ │ │ ├── error%40%40json%40%40file │ │ ├── ignored%40%40not_json%40%40file │ │ ├── invalid_dir/ │ │ │ └── invalid_file │ │ ├── legal%40%40no_name%40%40file │ │ └── legal%40%40with_name%40%40file │ └── failover_test/ │ ├── disabled/ │ │ └── 00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 │ └── enabled/ │ ├── 00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 │ ├── invalid_dir/ │ │ └── invalid_file │ └── legal%40%40with_name%40%40file ├── client-basic/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── client/ │ │ │ ├── address/ │ │ │ │ ├── AbstractServerListManager.java │ │ │ │ ├── AbstractServerListProvider.java │ │ │ │ ├── EndpointServerListProvider.java │ │ │ │ ├── PropertiesListProvider.java │ │ │ │ ├── ServerListChangeEvent.java │ │ │ │ └── ServerListProvider.java │ │ │ ├── auth/ │ │ │ │ ├── impl/ │ │ │ │ │ ├── NacosAuthLoginConstant.java │ │ │ │ │ ├── NacosClientAuthServiceImpl.java │ │ │ │ │ └── process/ │ │ │ │ │ ├── HttpLoginProcessor.java │ │ │ │ │ └── LoginProcessor.java │ │ │ │ └── ram/ │ │ │ │ ├── RamClientAuthServiceImpl.java │ │ │ │ ├── RamConstants.java │ │ │ │ ├── RamContext.java │ │ │ │ ├── identify/ │ │ │ │ │ ├── CredentialListener.java │ │ │ │ │ ├── CredentialService.java │ │ │ │ │ ├── CredentialWatcher.java │ │ │ │ │ ├── Credentials.java │ │ │ │ │ ├── IdentifyConstants.java │ │ │ │ │ ├── SpasCredential.java │ │ │ │ │ ├── SpasCredentialLoader.java │ │ │ │ │ ├── StsConfig.java │ │ │ │ │ ├── StsCredential.java │ │ │ │ │ └── StsCredentialHolder.java │ │ │ │ ├── injector/ │ │ │ │ │ ├── AbstractResourceInjector.java │ │ │ │ │ ├── AiResourceInjector.java │ │ │ │ │ ├── ConfigResourceInjector.java │ │ │ │ │ ├── LockResourceInjector.java │ │ │ │ │ └── NamingResourceInjector.java │ │ │ │ └── utils/ │ │ │ │ ├── CalculateV4SigningKeyUtil.java │ │ │ │ ├── RamUtil.java │ │ │ │ ├── SignUtil.java │ │ │ │ └── SpasAdapter.java │ │ │ ├── constant/ │ │ │ │ └── Constants.java │ │ │ ├── env/ │ │ │ │ ├── AbstractPropertySource.java │ │ │ │ ├── JvmArgsPropertySource.java │ │ │ │ ├── NacosClientProperties.java │ │ │ │ ├── PropertiesPropertySource.java │ │ │ │ ├── SearchableProperties.java │ │ │ │ ├── SourceType.java │ │ │ │ ├── SystemEnvPropertySource.java │ │ │ │ └── convert/ │ │ │ │ ├── AbstractPropertyConverter.java │ │ │ │ ├── BooleanConverter.java │ │ │ │ ├── CompositeConverter.java │ │ │ │ ├── IntegerConverter.java │ │ │ │ └── LongConverter.java │ │ │ ├── remote/ │ │ │ │ └── HttpClientManager.java │ │ │ └── utils/ │ │ │ ├── AppNameUtils.java │ │ │ ├── ClientBasicParamUtil.java │ │ │ ├── ContextPathUtil.java │ │ │ ├── TemplateUtils.java │ │ │ └── TenantUtil.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ ├── com.alibaba.nacos.client.address.ServerListProvider │ │ └── com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── client/ │ │ ├── address/ │ │ │ ├── AbstractServerListManagerTest.java │ │ │ ├── EndpointServerListProviderTest.java │ │ │ ├── PropertiesListProviderTest.java │ │ │ └── mock/ │ │ │ └── MockServerListProvider.java │ │ ├── auth/ │ │ │ ├── impl/ │ │ │ │ ├── NacosClientAuthServiceImplTest.java │ │ │ │ └── process/ │ │ │ │ └── HttpLoginProcessorTest.java │ │ │ └── ram/ │ │ │ ├── RamClientAuthServiceImplTest.java │ │ │ ├── identify/ │ │ │ │ ├── CredentialServiceTest.java │ │ │ │ ├── CredentialWatcherTest.java │ │ │ │ ├── CredentialsTest.java │ │ │ │ ├── StsConfigTest.java │ │ │ │ └── StsCredentialHolderTest.java │ │ │ ├── injector/ │ │ │ │ ├── AbstractResourceInjectorTest.java │ │ │ │ ├── ConfigResourceInjectorTest.java │ │ │ │ └── NamingResourceInjectorTest.java │ │ │ └── utils/ │ │ │ ├── CalculateV4SigningKeyUtilTest.java │ │ │ ├── RamUtilTest.java │ │ │ ├── SignUtilTest.java │ │ │ └── SpasAdapterTest.java │ │ ├── env/ │ │ │ ├── NacosClientPropertiesTest.java │ │ │ ├── SearchablePropertiesTest.java │ │ │ ├── SystemEnvPropertySourceTest.java │ │ │ └── convert/ │ │ │ └── CompositeConverterTest.java │ │ └── utils/ │ │ ├── AppNameUtilsTest.java │ │ ├── ClientBasicParamUtilTest.java │ │ ├── ContextPathUtilTest.java │ │ ├── TemplateUtilsTest.java │ │ └── TenantUtilTest.java │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.client.address.ServerListProvider │ ├── spas.identity │ ├── spas_docker.identity │ ├── spas_invalid.identity │ └── spas_modified.identity ├── cmdb/ │ ├── pom.xml │ └── src/ │ └── main/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── cmdb/ │ ├── CmdbApp.java │ ├── controllers/ │ │ └── OperationController.java │ ├── core/ │ │ └── SwitchAndOptions.java │ ├── memory/ │ │ └── CmdbProvider.java │ ├── service/ │ │ ├── CmdbReader.java │ │ └── CmdbWriter.java │ └── utils/ │ ├── CmdbExecutor.java │ ├── Loggers.java │ └── UtilsAndCommons.java ├── codecov.yml ├── common/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── common/ │ │ │ ├── Beta.java │ │ │ ├── JustForTest.java │ │ │ ├── NotThreadSafe.java │ │ │ ├── ability/ │ │ │ │ ├── AbstractAbilityControlManager.java │ │ │ │ └── discover/ │ │ │ │ └── NacosAbilityManagerHolder.java │ │ │ ├── codec/ │ │ │ │ └── Base64.java │ │ │ ├── constant/ │ │ │ │ ├── HttpHeaderConsts.java │ │ │ │ ├── RequestUrlConstants.java │ │ │ │ ├── ResponseHandlerType.java │ │ │ │ └── Symbols.java │ │ │ ├── event/ │ │ │ │ └── ServerConfigChangeEvent.java │ │ │ ├── executor/ │ │ │ │ ├── ExecutorFactory.java │ │ │ │ ├── NameThreadFactory.java │ │ │ │ └── ThreadPoolManager.java │ │ │ ├── http/ │ │ │ │ ├── AbstractApacheHttpClientFactory.java │ │ │ │ ├── AbstractHttpClientFactory.java │ │ │ │ ├── BaseHttpMethod.java │ │ │ │ ├── Callback.java │ │ │ │ ├── DefaultHttpClientFactory.java │ │ │ │ ├── HttpClientBeanHolder.java │ │ │ │ ├── HttpClientConfig.java │ │ │ │ ├── HttpClientFactory.java │ │ │ │ ├── HttpRestResult.java │ │ │ │ ├── HttpUtils.java │ │ │ │ ├── client/ │ │ │ │ │ ├── AbstractNacosRestTemplate.java │ │ │ │ │ ├── HttpClientRequestInterceptor.java │ │ │ │ │ ├── InterceptingHttpClientRequest.java │ │ │ │ │ ├── NacosAsyncRestTemplate.java │ │ │ │ │ ├── NacosRestTemplate.java │ │ │ │ │ ├── handler/ │ │ │ │ │ │ ├── AbstractResponseHandler.java │ │ │ │ │ │ ├── BeanResponseHandler.java │ │ │ │ │ │ ├── ResponseHandler.java │ │ │ │ │ │ ├── RestResultResponseHandler.java │ │ │ │ │ │ └── StringResponseHandler.java │ │ │ │ │ ├── request/ │ │ │ │ │ │ ├── AsyncHttpClientRequest.java │ │ │ │ │ │ ├── DefaultAsyncHttpClientRequest.java │ │ │ │ │ │ ├── DefaultHttpClientRequest.java │ │ │ │ │ │ ├── HttpClientRequest.java │ │ │ │ │ │ └── JdkHttpClientRequest.java │ │ │ │ │ └── response/ │ │ │ │ │ ├── DefaultClientHttpResponse.java │ │ │ │ │ ├── HttpClientResponse.java │ │ │ │ │ └── JdkHttpClientResponse.java │ │ │ │ └── param/ │ │ │ │ ├── Header.java │ │ │ │ ├── MediaType.java │ │ │ │ └── Query.java │ │ │ ├── labels/ │ │ │ │ ├── LabelsCollector.java │ │ │ │ ├── LabelsCollectorManager.java │ │ │ │ └── impl/ │ │ │ │ ├── DefaultLabelsCollector.java │ │ │ │ └── DefaultLabelsCollectorManager.java │ │ │ ├── lifecycle/ │ │ │ │ └── Closeable.java │ │ │ ├── logging/ │ │ │ │ ├── NacosLoggingAdapter.java │ │ │ │ ├── NacosLoggingAdapterBuilder.java │ │ │ │ └── NacosLoggingProperties.java │ │ │ ├── model/ │ │ │ │ ├── RequestHttpEntity.java │ │ │ │ ├── RestResult.java │ │ │ │ ├── RestResultUtils.java │ │ │ │ └── core/ │ │ │ │ └── IResultCode.java │ │ │ ├── notify/ │ │ │ │ ├── DefaultPublisher.java │ │ │ │ ├── DefaultSharePublisher.java │ │ │ │ ├── Event.java │ │ │ │ ├── EventPublisher.java │ │ │ │ ├── EventPublisherFactory.java │ │ │ │ ├── NotifyCenter.java │ │ │ │ ├── ShardedEventPublisher.java │ │ │ │ ├── SlowEvent.java │ │ │ │ └── listener/ │ │ │ │ ├── SmartSubscriber.java │ │ │ │ └── Subscriber.java │ │ │ ├── package-info.java │ │ │ ├── packagescan/ │ │ │ │ ├── DefaultPackageScan.java │ │ │ │ ├── PackageScan.java │ │ │ │ ├── classreading/ │ │ │ │ │ ├── ClassReader.java │ │ │ │ │ └── Symbol.java │ │ │ │ ├── resource/ │ │ │ │ │ ├── AbstractFileResolvingResource.java │ │ │ │ │ ├── AbstractResource.java │ │ │ │ │ ├── AntPathMatcher.java │ │ │ │ │ ├── ByteArrayResource.java │ │ │ │ │ ├── ClassPathResource.java │ │ │ │ │ ├── ContextResource.java │ │ │ │ │ ├── DefaultResourceLoader.java │ │ │ │ │ ├── FileSystemResource.java │ │ │ │ │ ├── FileUrlResource.java │ │ │ │ │ ├── InputStreamResource.java │ │ │ │ │ ├── InputStreamSource.java │ │ │ │ │ ├── PathMatchingResourcePatternResolver.java │ │ │ │ │ ├── PathResource.java │ │ │ │ │ ├── ProtocolResolver.java │ │ │ │ │ ├── Resource.java │ │ │ │ │ ├── ResourceLoader.java │ │ │ │ │ ├── ResourcePatternResolver.java │ │ │ │ │ ├── UrlResource.java │ │ │ │ │ ├── VfsPatternUtils.java │ │ │ │ │ ├── VfsResource.java │ │ │ │ │ ├── VfsUtils.java │ │ │ │ │ └── WritableResource.java │ │ │ │ └── util/ │ │ │ │ ├── AbstractAssert.java │ │ │ │ ├── AbstractObjectUtils.java │ │ │ │ ├── NestedExceptionUtils.java │ │ │ │ ├── NestedIoException.java │ │ │ │ ├── PathMatcher.java │ │ │ │ └── ResourceUtils.java │ │ │ ├── paramcheck/ │ │ │ │ ├── AbstractParamChecker.java │ │ │ │ ├── DefaultParamChecker.java │ │ │ │ ├── ParamCheckResponse.java │ │ │ │ ├── ParamCheckRule.java │ │ │ │ ├── ParamCheckerManager.java │ │ │ │ └── ParamInfo.java │ │ │ ├── pathencoder/ │ │ │ │ ├── PathEncoder.java │ │ │ │ ├── PathEncoderManager.java │ │ │ │ └── impl/ │ │ │ │ └── WindowsEncoder.java │ │ │ ├── remote/ │ │ │ │ ├── ConnectionType.java │ │ │ │ ├── PayloadRegistry.java │ │ │ │ ├── TlsConfig.java │ │ │ │ ├── client/ │ │ │ │ │ ├── Connection.java │ │ │ │ │ ├── ConnectionEventListener.java │ │ │ │ │ ├── RpcClient.java │ │ │ │ │ ├── RpcClientConfig.java │ │ │ │ │ ├── RpcClientConfigFactory.java │ │ │ │ │ ├── RpcClientFactory.java │ │ │ │ │ ├── RpcClientStatus.java │ │ │ │ │ ├── RpcClientTlsConfig.java │ │ │ │ │ ├── RpcClientTlsConfigFactory.java │ │ │ │ │ ├── RpcConfigFactory.java │ │ │ │ │ ├── RpcConstants.java │ │ │ │ │ ├── RpcTlsConfigFactory.java │ │ │ │ │ ├── ServerListFactory.java │ │ │ │ │ ├── ServerRequestHandler.java │ │ │ │ │ └── grpc/ │ │ │ │ │ ├── DefaultGrpcClientConfig.java │ │ │ │ │ ├── GrpcClient.java │ │ │ │ │ ├── GrpcClientConfig.java │ │ │ │ │ ├── GrpcClusterClient.java │ │ │ │ │ ├── GrpcConnection.java │ │ │ │ │ ├── GrpcConstants.java │ │ │ │ │ ├── GrpcSdkClient.java │ │ │ │ │ └── GrpcUtils.java │ │ │ │ └── exception/ │ │ │ │ ├── ConnectionAlreadyClosedException.java │ │ │ │ ├── ConnectionBusyException.java │ │ │ │ └── RemoteException.java │ │ │ ├── spi/ │ │ │ │ ├── NacosServiceLoader.java │ │ │ │ └── ServiceLoaderException.java │ │ │ ├── task/ │ │ │ │ ├── AbstractDelayTask.java │ │ │ │ ├── AbstractExecuteTask.java │ │ │ │ ├── BatchTaskCounter.java │ │ │ │ ├── NacosTask.java │ │ │ │ ├── NacosTaskProcessor.java │ │ │ │ └── engine/ │ │ │ │ ├── AbstractNacosTaskExecuteEngine.java │ │ │ │ ├── NacosDelayTaskExecuteEngine.java │ │ │ │ ├── NacosExecuteTaskExecuteEngine.java │ │ │ │ ├── NacosTaskExecuteEngine.java │ │ │ │ └── TaskExecuteWorker.java │ │ │ ├── tls/ │ │ │ │ ├── SelfHostnameVerifier.java │ │ │ │ ├── SelfTrustManager.java │ │ │ │ ├── TlsFileWatcher.java │ │ │ │ ├── TlsHelper.java │ │ │ │ └── TlsSystemConfig.java │ │ │ ├── trace/ │ │ │ │ ├── DeregisterInstanceReason.java │ │ │ │ ├── HealthCheckType.java │ │ │ │ ├── event/ │ │ │ │ │ ├── TraceEvent.java │ │ │ │ │ └── naming/ │ │ │ │ │ ├── BatchRegisterInstanceTraceEvent.java │ │ │ │ │ ├── DeregisterInstanceTraceEvent.java │ │ │ │ │ ├── DeregisterServiceTraceEvent.java │ │ │ │ │ ├── HealthStateChangeTraceEvent.java │ │ │ │ │ ├── NamingTraceEvent.java │ │ │ │ │ ├── PushServiceTraceEvent.java │ │ │ │ │ ├── RegisterInstanceTraceEvent.java │ │ │ │ │ ├── RegisterServiceTraceEvent.java │ │ │ │ │ ├── SubscribeServiceTraceEvent.java │ │ │ │ │ ├── UnsubscribeServiceTraceEvent.java │ │ │ │ │ ├── UpdateInstanceTraceEvent.java │ │ │ │ │ └── UpdateServiceTraceEvent.java │ │ │ │ └── publisher/ │ │ │ │ ├── TraceEventPublisher.java │ │ │ │ └── TraceEventPublisherFactory.java │ │ │ └── utils/ │ │ │ ├── ArrayUtils.java │ │ │ ├── ByteUtils.java │ │ │ ├── ClassUtils.java │ │ │ ├── CollectionUtils.java │ │ │ ├── ConcurrentHashSet.java │ │ │ ├── ConnLabelsUtils.java │ │ │ ├── ConvertUtils.java │ │ │ ├── DateFormatUtils.java │ │ │ ├── ExceptionUtil.java │ │ │ ├── FuzzyGroupKeyPattern.java │ │ │ ├── HttpMethod.java │ │ │ ├── InetAddressValidator.java │ │ │ ├── InternetAddressUtil.java │ │ │ ├── IoUtils.java │ │ │ ├── JacksonUtils.java │ │ │ ├── LoggerUtils.java │ │ │ ├── MD5Utils.java │ │ │ ├── MapUtil.java │ │ │ ├── NamespaceUtil.java │ │ │ ├── NumberUtils.java │ │ │ ├── Observable.java │ │ │ ├── Observer.java │ │ │ ├── Pair.java │ │ │ ├── Preconditions.java │ │ │ ├── PropertyUtils.java │ │ │ ├── RandomUtils.java │ │ │ ├── ReflectUtils.java │ │ │ ├── ResourceUtils.java │ │ │ ├── StringUtils.java │ │ │ ├── ThreadFactoryBuilder.java │ │ │ ├── ThreadUtils.java │ │ │ ├── TlsTypeResolve.java │ │ │ ├── TypeUtils.java │ │ │ ├── UuidUtils.java │ │ │ └── VersionUtils.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ ├── com.alibaba.nacos.common.labels.LabelsCollector │ │ │ ├── com.alibaba.nacos.common.paramcheck.AbstractParamChecker │ │ │ └── com.alibaba.nacos.common.pathencoder.PathEncoder │ │ └── nacos-version.txt │ └── test/ │ ├── java/ │ │ ├── ClassUtilsTestMockClass.java │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── common/ │ │ ├── AppTest.java │ │ ├── ability/ │ │ │ ├── AbstractAbilityControlManagerTest.java │ │ │ ├── MockAbilityPostProcessor.java │ │ │ └── discover/ │ │ │ ├── HigherMockAbilityManager.java │ │ │ ├── LowerMockAbilityManager.java │ │ │ └── NacosAbilityManagerHolderTest.java │ │ ├── codec/ │ │ │ └── Base64Test.java │ │ ├── event/ │ │ │ └── ServerConfigChangeEventTest.java │ │ ├── executor/ │ │ │ ├── ExecutorFactoryTest.java │ │ │ ├── NameThreadFactoryTest.java │ │ │ └── ThreadPoolManagerTest.java │ │ ├── http/ │ │ │ ├── AbstractApacheHttpClientFactoryTest.java │ │ │ ├── AbstractHttpClientFactoryTest.java │ │ │ ├── BaseHttpMethodTest.java │ │ │ ├── HttpClientBeanHolderTest.java │ │ │ ├── HttpClientConfigTest.java │ │ │ ├── HttpRestResultTest.java │ │ │ ├── HttpUtilsTest.java │ │ │ ├── client/ │ │ │ │ ├── AbstractNacosRestTemplateTest.java │ │ │ │ ├── InterceptingHttpClientRequestTest.java │ │ │ │ ├── NacosAsyncRestTemplateTest.java │ │ │ │ ├── NacosRestTemplateTest.java │ │ │ │ ├── handler/ │ │ │ │ │ ├── BeanResponseHandlerTest.java │ │ │ │ │ └── RestResultResponseHandlerTest.java │ │ │ │ ├── request/ │ │ │ │ │ ├── DefaultAsyncHttpClientRequestTest.java │ │ │ │ │ ├── DefaultHttpClientRequestTest.java │ │ │ │ │ └── JdkHttpClientRequestTest.java │ │ │ │ └── response/ │ │ │ │ ├── DefaultClientHttpResponseTest.java │ │ │ │ └── JdkClientHttpResponseTest.java │ │ │ └── param/ │ │ │ ├── HeaderTest.java │ │ │ ├── MediaTypeTest.java │ │ │ └── QueryTest.java │ │ ├── labels/ │ │ │ └── impl/ │ │ │ ├── DefaultLabelsCollectorManagerTest.java │ │ │ ├── Test1LabelsCollector.java │ │ │ └── Test2LabelsCollector.java │ │ ├── logging/ │ │ │ └── NacosLoggingPropertiesTest.java │ │ ├── model/ │ │ │ ├── RequestHttpEntityTest.java │ │ │ ├── RestResultTest.java │ │ │ └── RestResultUtilsTest.java │ │ ├── notify/ │ │ │ ├── DefaultPublisherTest.java │ │ │ ├── DefaultSharePublisherTest.java │ │ │ └── NotifyCenterTest.java │ │ ├── packagescan/ │ │ │ ├── DefaultPackageScanTest.java │ │ │ └── mock/ │ │ │ ├── AnnotationClass.java │ │ │ ├── MockClass.java │ │ │ ├── NoAnnotationClass.java │ │ │ └── TestScan.java │ │ ├── paramcheck/ │ │ │ ├── DefaultParamCheckerTest.java │ │ │ ├── MockParamChecker.java │ │ │ └── ParamCheckerManagerTest.java │ │ ├── pathencoder/ │ │ │ ├── PathEncoderManagerTest.java │ │ │ └── WindowsEncoderTest.java │ │ ├── remote/ │ │ │ ├── ConnectionTypeTest.java │ │ │ ├── PayloadRegistryTest.java │ │ │ ├── TlsConfigTest.java │ │ │ ├── client/ │ │ │ │ ├── ConnectionTest.java │ │ │ │ ├── RpcClientConfigFactoryTest.java │ │ │ │ ├── RpcClientFactoryTest.java │ │ │ │ ├── RpcClientTest.java │ │ │ │ ├── RpcClientTlsConfigTest.java │ │ │ │ ├── RpcClusterClientTlsConfigTest.java │ │ │ │ ├── RpcConstantsTest.java │ │ │ │ └── grpc/ │ │ │ │ ├── DefaultGrpcClientConfigTest.java │ │ │ │ ├── GrpcClientTest.java │ │ │ │ ├── GrpcClientTlsTest.java │ │ │ │ ├── GrpcClusterClientTest.java │ │ │ │ ├── GrpcConnectionTest.java │ │ │ │ ├── GrpcConstantsTest.java │ │ │ │ ├── GrpcSdkClientTest.java │ │ │ │ └── GrpcUtilsTest.java │ │ │ └── exception/ │ │ │ └── RemoteExceptionTest.java │ │ ├── spi/ │ │ │ ├── NacosServiceLoaderTest.java │ │ │ ├── SpiTestImpl.java │ │ │ └── SpiTestInterface.java │ │ ├── task/ │ │ │ └── engine/ │ │ │ ├── NacosDelayTaskExecuteEngineTest.java │ │ │ └── NacosExecuteTaskExecuteEngineTest.java │ │ ├── tls/ │ │ │ ├── SelfHostnameVerifierTest.java │ │ │ ├── SelfTrustManagerTest.java │ │ │ ├── TlsFileWatcherTest.java │ │ │ └── TlsHelperTest.java │ │ ├── trace/ │ │ │ ├── event/ │ │ │ │ └── naming/ │ │ │ │ ├── HealthStateChangeTraceEventTest.java │ │ │ │ ├── InstanceTraceEventTest.java │ │ │ │ ├── NamingTraceEventTest.java │ │ │ │ ├── ServiceTraceEventTest.java │ │ │ │ └── SubscribeTraceEventTest.java │ │ │ └── publisher/ │ │ │ ├── TraceEventPublisherFactoryTest.java │ │ │ ├── TraceEventPublisherTest.java │ │ │ └── TraceTestEvent.java │ │ └── utils/ │ │ ├── ArrayUtilsTest.java │ │ ├── ByteUtilsTest.java │ │ ├── ClassUtilsTest.java │ │ ├── CollectionUtilsTest.java │ │ ├── ConcurrentHashSetTest.java │ │ ├── ConnLabelsUtilsTest.java │ │ ├── ConvertUtilsTest.java │ │ ├── DateFormatUtilsTest.java │ │ ├── ExceptionUtilTest.java │ │ ├── FuzzyGroupKeyPatternTest.java │ │ ├── InetAddressValidatorTest.java │ │ ├── InternetAddressUtilTest.java │ │ ├── IoUtilsTest.java │ │ ├── JacksonUtilsTest.java │ │ ├── LoggerUtilsTest.java │ │ ├── MD5UtilsTest.java │ │ ├── MapUtilTest.java │ │ ├── NamespaceUtilTest.java │ │ ├── NumberUtilsTest.java │ │ ├── ObservableTest.java │ │ ├── PairTest.java │ │ ├── PreconditionsTest.java │ │ ├── PropertyUtilsTest.java │ │ ├── RandomUtilsTest.java │ │ ├── ReflectUtilsTest.java │ │ ├── ResourceUtilsTest.java │ │ ├── StringUtilsTest.java │ │ ├── ThreadFactoryBuilderTest.java │ │ ├── ThreadUtilsTest.java │ │ ├── TlsTypeResolveTest.java │ │ ├── TypeUtilsTest.java │ │ ├── UuidUtilsTest.java │ │ ├── VersionUtilsTest.java │ │ └── to/ │ │ └── User.java │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ ├── com.alibaba.nacos.api.ability.initializer.AbilityPostProcessor │ │ ├── com.alibaba.nacos.common.ability.AbstractAbilityControlManager │ │ ├── com.alibaba.nacos.common.labels.LabelsCollector │ │ ├── com.alibaba.nacos.common.paramcheck.AbstractParamChecker │ │ └── com.alibaba.nacos.common.spi.SpiTestInterface │ ├── resource_utils_test.properties │ └── test-tls-cert.pem ├── config/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── config/ │ │ │ └── server/ │ │ │ ├── Config.java │ │ │ ├── aspect/ │ │ │ │ ├── CapacityManagementAspect.java │ │ │ │ ├── ConfigChangeAspect.java │ │ │ │ ├── ConfigOpFailureAspect.java │ │ │ │ └── RequestLogAspect.java │ │ │ ├── configuration/ │ │ │ │ ├── ConfigChangeConfigs.java │ │ │ │ ├── ConfigCommonConfig.java │ │ │ │ ├── ConfigCompatibleConfig.java │ │ │ │ └── NacosConfigConfiguration.java │ │ │ ├── constant/ │ │ │ │ ├── ConfigModuleStateBuilder.java │ │ │ │ ├── Constants.java │ │ │ │ ├── CounterMode.java │ │ │ │ ├── ParametersField.java │ │ │ │ └── PropertiesConstant.java │ │ │ ├── controller/ │ │ │ │ ├── ConfigServletInner.java │ │ │ │ ├── parameters/ │ │ │ │ │ └── SameNamespaceCloneConfigBean.java │ │ │ │ └── v3/ │ │ │ │ ├── CapacityControllerV3.java │ │ │ │ ├── ConfigControllerV3.java │ │ │ │ ├── ConfigOpenApiController.java │ │ │ │ ├── ConfigOpsControllerV3.java │ │ │ │ ├── HistoryControllerV3.java │ │ │ │ ├── ListenerControllerV3.java │ │ │ │ └── MetricsControllerV3.java │ │ │ ├── enums/ │ │ │ │ ├── ApiVersionEnum.java │ │ │ │ ├── FileTypeEnum.java │ │ │ │ └── OperationType.java │ │ │ ├── exception/ │ │ │ │ ├── ConfigAlreadyExistsException.java │ │ │ │ ├── GlobalExceptionHandler.java │ │ │ │ └── NacosConfigException.java │ │ │ ├── filter/ │ │ │ │ ├── CircuitFilter.java │ │ │ │ ├── ConfigEnabledFilter.java │ │ │ │ └── NacosWebFilter.java │ │ │ ├── manager/ │ │ │ │ ├── TaskManager.java │ │ │ │ └── TaskManagerMBean.java │ │ │ ├── model/ │ │ │ │ ├── AclInfo.java │ │ │ │ ├── CacheItem.java │ │ │ │ ├── ConfigAdvanceInfo.java │ │ │ │ ├── ConfigAllInfo.java │ │ │ │ ├── ConfigCache.java │ │ │ │ ├── ConfigCacheFactory.java │ │ │ │ ├── ConfigCacheFactoryDelegate.java │ │ │ │ ├── ConfigCacheGray.java │ │ │ │ ├── ConfigCachePostProcessor.java │ │ │ │ ├── ConfigCachePostProcessorDelegate.java │ │ │ │ ├── ConfigHistoryInfo.java │ │ │ │ ├── ConfigHistoryInfoDetail.java │ │ │ │ ├── ConfigInfo.java │ │ │ │ ├── ConfigInfo4Beta.java │ │ │ │ ├── ConfigInfo4Tag.java │ │ │ │ ├── ConfigInfoBase.java │ │ │ │ ├── ConfigInfoBaseEx.java │ │ │ │ ├── ConfigInfoBetaWrapper.java │ │ │ │ ├── ConfigInfoChanged.java │ │ │ │ ├── ConfigInfoEx.java │ │ │ │ ├── ConfigInfoGrayWrapper.java │ │ │ │ ├── ConfigInfoStateWrapper.java │ │ │ │ ├── ConfigInfoTagWrapper.java │ │ │ │ ├── ConfigInfoWrapper.java │ │ │ │ ├── ConfigKey.java │ │ │ │ ├── ConfigListenState.java │ │ │ │ ├── ConfigMetadata.java │ │ │ │ ├── ConfigOperateResult.java │ │ │ │ ├── ConfigRequestInfo.java │ │ │ │ ├── GroupkeyListenserStatus.java │ │ │ │ ├── ListenerCheckResult.java │ │ │ │ ├── NacosConfigCacheFactory.java │ │ │ │ ├── NacosConfigCachePostProcessor.java │ │ │ │ ├── SampleResult.java │ │ │ │ ├── SubscriberStatus.java │ │ │ │ ├── capacity/ │ │ │ │ │ ├── Capacity.java │ │ │ │ │ ├── GroupCapacity.java │ │ │ │ │ └── NamespaceCapacity.java │ │ │ │ ├── event/ │ │ │ │ │ ├── ConfigDataChangeEvent.java │ │ │ │ │ ├── ConfigDumpEvent.java │ │ │ │ │ ├── ConfigFuzzyWatchEvent.java │ │ │ │ │ ├── IstioConfigChangeEvent.java │ │ │ │ │ ├── LocalDataChangeEvent.java │ │ │ │ │ └── RaftDbErrorRecoverEvent.java │ │ │ │ ├── form/ │ │ │ │ │ ├── ConfigForm.java │ │ │ │ │ ├── ConfigFormV3.java │ │ │ │ │ └── UpdateCapacityForm.java │ │ │ │ └── gray/ │ │ │ │ ├── AbstractGrayRule.java │ │ │ │ ├── BetaGrayRule.java │ │ │ │ ├── ConfigGrayPersistInfo.java │ │ │ │ ├── GrayRule.java │ │ │ │ ├── GrayRuleManager.java │ │ │ │ └── TagGrayRule.java │ │ │ ├── monitor/ │ │ │ │ ├── ConfigDynamicMeterRefreshService.java │ │ │ │ ├── MemoryMonitor.java │ │ │ │ ├── MetricsMonitor.java │ │ │ │ ├── PrintGetConfigResponeTask.java │ │ │ │ ├── PrintMemoryTask.java │ │ │ │ ├── ResponseMonitor.java │ │ │ │ ├── ThreadTaskQueueMonitorTask.java │ │ │ │ └── collector/ │ │ │ │ └── ConfigSubscriberMetricsCollector.java │ │ │ ├── paramcheck/ │ │ │ │ ├── ConfigBlurSearchHttpParamExtractor.java │ │ │ │ ├── ConfigDefaultHttpParamExtractor.java │ │ │ │ └── ConfigListenerHttpParamExtractor.java │ │ │ ├── remote/ │ │ │ │ ├── ConfigChangeBatchListenRequestHandler.java │ │ │ │ ├── ConfigChangeClusterSyncRequestHandler.java │ │ │ │ ├── ConfigChangeListenContext.java │ │ │ │ ├── ConfigClusterRpcClientProxy.java │ │ │ │ ├── ConfigConnectionEventListener.java │ │ │ │ ├── ConfigFuzzyWatchChangeNotifier.java │ │ │ │ ├── ConfigFuzzyWatchRequestHandler.java │ │ │ │ ├── ConfigFuzzyWatchSyncNotifier.java │ │ │ │ ├── ConfigPublishRequestHandler.java │ │ │ │ ├── ConfigQueryRequestHandler.java │ │ │ │ ├── ConfigRemoveRequestHandler.java │ │ │ │ ├── FuzzyWatchChangeNotifyTask.java │ │ │ │ ├── FuzzyWatchSyncNotifyCallback.java │ │ │ │ ├── FuzzyWatchSyncNotifyTask.java │ │ │ │ └── RpcConfigChangeNotifier.java │ │ │ ├── result/ │ │ │ │ └── code/ │ │ │ │ └── ResultCodeEnum.java │ │ │ ├── service/ │ │ │ │ ├── ClientIpWhiteList.java │ │ │ │ ├── ClientRecord.java │ │ │ │ ├── ClientTrackService.java │ │ │ │ ├── ConfigCacheService.java │ │ │ │ ├── ConfigChangePublisher.java │ │ │ │ ├── ConfigDetailService.java │ │ │ │ ├── ConfigFuzzyWatchContextService.java │ │ │ │ ├── ConfigMigrateService.java │ │ │ │ ├── ConfigOperationService.java │ │ │ │ ├── ConfigReadinessCheckService.java │ │ │ │ ├── ConfigSubService.java │ │ │ │ ├── HistoryService.java │ │ │ │ ├── LongPollingConnectionMetricsCollector.java │ │ │ │ ├── LongPollingService.java │ │ │ │ ├── NamespaceConfigInfoService.java │ │ │ │ ├── SwitchService.java │ │ │ │ ├── capacity/ │ │ │ │ │ ├── CapacityService.java │ │ │ │ │ ├── GroupCapacityPersistService.java │ │ │ │ │ └── TenantCapacityPersistService.java │ │ │ │ ├── dump/ │ │ │ │ │ ├── DefaultHistoryConfigCleaner.java │ │ │ │ │ ├── DumpChangeConfigWorker.java │ │ │ │ │ ├── DumpChangeGrayConfigWorker.java │ │ │ │ │ ├── DumpConfigHandler.java │ │ │ │ │ ├── DumpRequest.java │ │ │ │ │ ├── DumpService.java │ │ │ │ │ ├── EmbeddedDumpService.java │ │ │ │ │ ├── ExternalDumpService.java │ │ │ │ │ ├── HistoryConfigCleaner.java │ │ │ │ │ ├── HistoryConfigCleanerConfig.java │ │ │ │ │ ├── HistoryConfigCleanerManager.java │ │ │ │ │ ├── disk/ │ │ │ │ │ │ ├── ConfigDiskService.java │ │ │ │ │ │ ├── ConfigDiskServiceFactory.java │ │ │ │ │ │ ├── ConfigRawDiskService.java │ │ │ │ │ │ └── ConfigRocksDbDiskService.java │ │ │ │ │ ├── processor/ │ │ │ │ │ │ ├── DumpAllGrayProcessor.java │ │ │ │ │ │ ├── DumpAllProcessor.java │ │ │ │ │ │ └── DumpProcessor.java │ │ │ │ │ └── task/ │ │ │ │ │ ├── DumpAllBetaTask.java │ │ │ │ │ ├── DumpAllGrayTask.java │ │ │ │ │ ├── DumpAllTagTask.java │ │ │ │ │ ├── DumpAllTask.java │ │ │ │ │ └── DumpTask.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── ConfigListenerStateDelegate.java │ │ │ │ │ ├── ConfigListenerStateService.java │ │ │ │ │ ├── LocalConfigListenerStateServiceImpl.java │ │ │ │ │ └── RemoteConfigListenerStateServiceImpl.java │ │ │ │ ├── notify/ │ │ │ │ │ ├── AsyncNotifyService.java │ │ │ │ │ └── HttpClientManager.java │ │ │ │ ├── query/ │ │ │ │ │ ├── ConfigChainRequestExtractorService.java │ │ │ │ │ ├── ConfigQueryChainRequestExtractor.java │ │ │ │ │ ├── ConfigQueryChainService.java │ │ │ │ │ ├── ConfigQueryHandlerChain.java │ │ │ │ │ ├── ConfigQueryHandlerChainBuilder.java │ │ │ │ │ ├── DefaultChainRequestExtractor.java │ │ │ │ │ ├── DefaultConfigQueryHandlerChainBuilder.java │ │ │ │ │ ├── enums/ │ │ │ │ │ │ └── ResponseCode.java │ │ │ │ │ ├── handler/ │ │ │ │ │ │ ├── AbstractConfigQueryHandler.java │ │ │ │ │ │ ├── ConfigChainEntryHandler.java │ │ │ │ │ │ ├── ConfigContentTypeHandler.java │ │ │ │ │ │ ├── ConfigQueryHandler.java │ │ │ │ │ │ ├── FormalHandler.java │ │ │ │ │ │ ├── GrayRuleMatchHandler.java │ │ │ │ │ │ └── SpecialTagNotFoundHandler.java │ │ │ │ │ └── model/ │ │ │ │ │ ├── ConfigQueryChainRequest.java │ │ │ │ │ └── ConfigQueryChainResponse.java │ │ │ │ ├── repository/ │ │ │ │ │ ├── ConfigInfoBetaPersistService.java │ │ │ │ │ ├── ConfigInfoGrayPersistService.java │ │ │ │ │ ├── ConfigInfoPersistService.java │ │ │ │ │ ├── ConfigInfoTagPersistService.java │ │ │ │ │ ├── ConfigMigratePersistService.java │ │ │ │ │ ├── ConfigRowMapperInjector.java │ │ │ │ │ ├── HistoryConfigInfoPersistService.java │ │ │ │ │ ├── embedded/ │ │ │ │ │ │ ├── EmbeddedConfigDumpApplyHook.java │ │ │ │ │ │ ├── EmbeddedConfigInfoBetaPersistServiceImpl.java │ │ │ │ │ │ ├── EmbeddedConfigInfoGrayPersistServiceImpl.java │ │ │ │ │ │ ├── EmbeddedConfigInfoPersistServiceImpl.java │ │ │ │ │ │ ├── EmbeddedConfigInfoTagPersistServiceImpl.java │ │ │ │ │ │ ├── EmbeddedConfigMigratePersistServiceImpl.java │ │ │ │ │ │ └── EmbeddedHistoryConfigInfoPersistServiceImpl.java │ │ │ │ │ └── extrnal/ │ │ │ │ │ ├── ExternalConfigInfoBetaPersistServiceImpl.java │ │ │ │ │ ├── ExternalConfigInfoGrayPersistServiceImpl.java │ │ │ │ │ ├── ExternalConfigInfoPersistServiceImpl.java │ │ │ │ │ ├── ExternalConfigInfoTagPersistServiceImpl.java │ │ │ │ │ ├── ExternalConfigMigratePersistServiceImpl.java │ │ │ │ │ └── ExternalHistoryConfigInfoPersistServiceImpl.java │ │ │ │ ├── sql/ │ │ │ │ │ ├── EmbeddedStorageContextUtils.java │ │ │ │ │ └── ExternalStorageUtils.java │ │ │ │ └── trace/ │ │ │ │ └── ConfigTraceService.java │ │ │ └── utils/ │ │ │ ├── AccumulateStatCount.java │ │ │ ├── AppNameUtils.java │ │ │ ├── ConfigExecutor.java │ │ │ ├── ConfigExtInfoUtil.java │ │ │ ├── ConfigTagUtil.java │ │ │ ├── ContentUtils.java │ │ │ ├── GroupKey.java │ │ │ ├── GroupKey2.java │ │ │ ├── LogUtil.java │ │ │ ├── MD5Util.java │ │ │ ├── Md5Comparator.java │ │ │ ├── Md5ComparatorDelegate.java │ │ │ ├── NacosMd5Comparator.java │ │ │ ├── ParamUtils.java │ │ │ ├── PropertyUtil.java │ │ │ ├── Protocol.java │ │ │ ├── RegexParser.java │ │ │ ├── RequestUtil.java │ │ │ ├── ResponseUtil.java │ │ │ ├── SimpleCache.java │ │ │ ├── SimpleFlowData.java │ │ │ ├── SimpleIpFlowData.java │ │ │ ├── SimpleReadWriteLock.java │ │ │ ├── StatConstants.java │ │ │ ├── SystemConfig.java │ │ │ ├── TimeUtils.java │ │ │ ├── TimeoutUtils.java │ │ │ ├── TraceLogUtil.java │ │ │ ├── UrlAnalysisUtils.java │ │ │ ├── YamlParserUtil.java │ │ │ └── ZipUtils.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── logback/ │ │ │ │ └── config-included.xml │ │ │ ├── services/ │ │ │ │ ├── com.alibaba.nacos.config.server.model.gray.GrayRule │ │ │ │ ├── com.alibaba.nacos.config.server.service.query.ConfigQueryChainRequestExtractor │ │ │ │ ├── com.alibaba.nacos.config.server.service.query.ConfigQueryHandlerChainBuilder │ │ │ │ ├── com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor │ │ │ │ ├── com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector │ │ │ │ ├── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ │ │ │ └── com.alibaba.nacos.sys.module.ModuleStateBuilder │ │ │ └── spring.factories │ │ └── version/ │ │ └── version.txt │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── config/ │ │ └── server/ │ │ ├── aspect/ │ │ │ ├── CapacityManagementAspectTest.java │ │ │ ├── ConfigChangeAspectTest.java │ │ │ └── RequestLogAspectTest.java │ │ ├── configuration/ │ │ │ ├── ConfigChangeConfigsTest.java │ │ │ └── ConfigCommonConfigTest.java │ │ ├── constant/ │ │ │ ├── ConfigModuleStateBuilderTest.java │ │ │ ├── ConstantsTest.java │ │ │ └── CounterModeTest.java │ │ ├── controller/ │ │ │ ├── ConfigServletInnerTest.java │ │ │ └── v3/ │ │ │ ├── CapacityControllerV3Test.java │ │ │ ├── ConfigControllerV3Test.java │ │ │ ├── ConfigOpenApiControllerTest.java │ │ │ ├── ConfigOpsControllerV3Test.java │ │ │ ├── HistoryControllerV3Test.java │ │ │ ├── ListenerControllerV3Test.java │ │ │ └── MetricControllerV3Test.java │ │ ├── exception/ │ │ │ └── GlobalExceptionHandlerTest.java │ │ ├── manager/ │ │ │ └── TaskManagerTest.java │ │ ├── model/ │ │ │ ├── ConfigCacheFactoryDelegateTest.java │ │ │ ├── ConfigCachePostProcessorDelegateTest.java │ │ │ ├── ConfigInfoTest.java │ │ │ ├── NacosConfigCacheFactoryTest.java │ │ │ ├── NacosConfigCachePostProcessorTest.java │ │ │ └── form/ │ │ │ └── ConfigFormTest.java │ │ ├── paramcheck/ │ │ │ └── ConfigListenerHttpParamExtractorTest.java │ │ ├── remote/ │ │ │ ├── ConfigChangeBatchListenRequestHandlerTest.java │ │ │ ├── ConfigChangeClusterSyncRequestHandlerTest.java │ │ │ ├── ConfigChangeListenContextTest.java │ │ │ ├── ConfigFuzzyWatchChangeNotifierTest.java │ │ │ ├── ConfigFuzzyWatchSyncNotifierTest.java │ │ │ ├── ConfigPublishRequestHandlerTest.java │ │ │ ├── ConfigQueryRequestHandlerTest.java │ │ │ ├── ConfigRemoveRequestHandlerTest.java │ │ │ ├── FuzzyWatchSyncNotifyCallbackTest.java │ │ │ └── RpcConfigChangeNotifierTest.java │ │ ├── service/ │ │ │ ├── ClientTrackServiceTest.java │ │ │ ├── ConfigCacheServiceTest.java │ │ │ ├── ConfigChangePublisherTest.java │ │ │ ├── ConfigFuzzyWatchContextServiceTest.java │ │ │ ├── ConfigOperationServiceTest.java │ │ │ ├── ConfigSubServiceTest.java │ │ │ ├── HistoryServiceTest.java │ │ │ ├── LongPollingServiceTest.java │ │ │ ├── NamespaceConfigInfoServiceTest.java │ │ │ ├── capacity/ │ │ │ │ ├── CapacityServiceTest.java │ │ │ │ ├── GroupCapacityPersistServiceTest.java │ │ │ │ └── TenantCapacityPersistServiceTest.java │ │ │ ├── dump/ │ │ │ │ ├── DefaultHistoryConfigCleanerTest.java │ │ │ │ ├── DumpChangeConfigWorkerTest.java │ │ │ │ ├── DumpChangeGrayConfigWorkerTest.java │ │ │ │ ├── DumpProcessorTest.java │ │ │ │ ├── DumpProcessorUserRwaDiskTest.java │ │ │ │ ├── DumpServiceTest.java │ │ │ │ ├── HistoryConfigCleanerConfigTest.java │ │ │ │ ├── HistoryConfigCleanerManagerTest.java │ │ │ │ ├── disk/ │ │ │ │ │ ├── ConfigDiskServiceFactoryTest.java │ │ │ │ │ └── ConfigRawDiskServiceTest.java │ │ │ │ └── processor/ │ │ │ │ ├── DumpAllGrayProcessorTest.java │ │ │ │ └── DumpAllProcessorTest.java │ │ │ ├── notify/ │ │ │ │ └── AsyncNotifyServiceTest.java │ │ │ ├── query/ │ │ │ │ ├── DefaultChainRequestExtractorTest.java │ │ │ │ └── handler/ │ │ │ │ ├── ConfigChainEntryHandlerTest.java │ │ │ │ ├── ConfigContentTypeHandlerTest.java │ │ │ │ ├── FormalHandlerTest.java │ │ │ │ ├── GrayRuleMatchHandlerTest.java │ │ │ │ └── SpecialTagNotFoundHandlerTest.java │ │ │ └── repository/ │ │ │ ├── ConfigRowMapperInjectorTest.java │ │ │ ├── embedded/ │ │ │ │ ├── EmbeddedConfigInfoBetaPersistServiceImplTest.java │ │ │ │ ├── EmbeddedConfigInfoGrayPersistServiceImplTest.java │ │ │ │ ├── EmbeddedConfigInfoPersistServiceImplTest.java │ │ │ │ ├── EmbeddedConfigInfoTagPersistServiceImplTest.java │ │ │ │ └── EmbeddedHistoryConfigInfoPersistServiceImplTest.java │ │ │ └── extrnal/ │ │ │ ├── ExternalConfigInfoBetaPersistServiceImplTest.java │ │ │ ├── ExternalConfigInfoGrayPersistServiceImplTest.java │ │ │ ├── ExternalConfigInfoPersistServiceImplTest.java │ │ │ ├── ExternalConfigInfoTagPersistServiceImplTest.java │ │ │ └── ExternalHistoryConfigInfoPersistServiceImplTest.java │ │ └── utils/ │ │ ├── AccumulateStatCountTest.java │ │ ├── AppNameUtilsTest.java │ │ ├── ConfigExecutorTest.java │ │ ├── ConfigExtInfoUtilTest.java │ │ ├── ContentUtilsTest.java │ │ ├── GroupKey2Test.java │ │ ├── GroupKeyTest.java │ │ ├── LogUtilTest.java │ │ ├── MD5UtilTest.java │ │ ├── Md5ComparatorDelegateTest.java │ │ ├── NacosMd5ComparatorTest.java │ │ ├── ParamUtilsTest.java │ │ ├── PropertyUtilTest.java │ │ ├── ProtocolTest.java │ │ ├── RegexParserTest.java │ │ ├── RequestUtilTest.java │ │ ├── ResponseUtilTest.java │ │ ├── SimpleCacheTest.java │ │ ├── SimpleFlowDataTest.java │ │ ├── SimpleIpFlowDataTest.java │ │ ├── SimpleReadWriteLockTest.java │ │ ├── SystemConfigTest.java │ │ ├── TestCaseUtils.java │ │ ├── TimeUtilsTest.java │ │ ├── TimeoutUtilsTest.java │ │ ├── TraceLogUtilTest.java │ │ ├── UrlAnalysisUtilsTest.java │ │ ├── YamlParserUtilTest.java │ │ └── ZipUtilsTest.java │ └── resources/ │ ├── application.properties │ ├── log4j.properties │ ├── logback-test.xml │ └── user.properties ├── consistency/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── consistency/ │ │ │ ├── CommandOperations.java │ │ │ ├── Config.java │ │ │ ├── ConsistencyProtocol.java │ │ │ ├── DataOperation.java │ │ │ ├── IdGenerator.java │ │ │ ├── ProtoMessageUtil.java │ │ │ ├── ProtocolMetaData.java │ │ │ ├── RequestProcessor.java │ │ │ ├── SerializeFactory.java │ │ │ ├── Serializer.java │ │ │ ├── ap/ │ │ │ │ ├── APProtocol.java │ │ │ │ └── RequestProcessor4AP.java │ │ │ ├── cp/ │ │ │ │ ├── CPProtocol.java │ │ │ │ ├── MetadataKey.java │ │ │ │ └── RequestProcessor4CP.java │ │ │ ├── exception/ │ │ │ │ └── ConsistencyException.java │ │ │ ├── serialize/ │ │ │ │ ├── HessianSerializer.java │ │ │ │ ├── JacksonSerializer.java │ │ │ │ └── NacosHessianSerializerFactory.java │ │ │ └── snapshot/ │ │ │ ├── LocalFileMeta.java │ │ │ ├── Reader.java │ │ │ ├── SnapshotOperation.java │ │ │ └── Writer.java │ │ ├── proto/ │ │ │ ├── Data.proto │ │ │ └── consistency.proto │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.consistency.Serializer │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── consistency/ │ ├── CommandOperationsTest.java │ ├── DataOperationTest.java │ ├── ProtoMessageUtilTest.java │ ├── ProtocolMetaDataTest.java │ ├── RequestProcessorTest.java │ ├── SerializeFactoryTest.java │ ├── ap/ │ │ └── RequestProcessor4APTest.java │ ├── cp/ │ │ ├── MetadataKeyTest.java │ │ └── RequestProcessor4CPTest.java │ ├── exception/ │ │ └── ConsistencyExceptionTest.java │ ├── serialize/ │ │ ├── HessianSerializerTest.java │ │ └── JacksonSerializerTest.java │ └── snapshot/ │ ├── LocalFileMetaTest.java │ ├── ReaderTest.java │ └── WriterTest.java ├── console/ │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ ├── Nacos.java │ │ │ └── console/ │ │ │ ├── NacosConsole.java │ │ │ ├── NacosConsoleStartUp.java │ │ │ ├── aot/ │ │ │ │ ├── AotConfiguration.java │ │ │ │ └── NacosRuntimeHints.java │ │ │ ├── cluster/ │ │ │ │ └── RemoteServerMemberManager.java │ │ │ ├── config/ │ │ │ │ ├── ConsoleAuthModuleStateBuilder.java │ │ │ │ ├── ConsoleCorsConfig.java │ │ │ │ ├── ConsoleDeploymentConfig.java │ │ │ │ ├── ConsoleFunctionEnabledConfig.java │ │ │ │ ├── ConsoleModuleStateBuilder.java │ │ │ │ ├── ConsolePackageExcludeFilter.java │ │ │ │ ├── ConsoleWebConfig.java │ │ │ │ ├── NacosConsoleAuthConfig.java │ │ │ │ └── NacosConsoleBeanPostProcessorConfiguration.java │ │ │ ├── controller/ │ │ │ │ └── v3/ │ │ │ │ ├── ConsoleHealthController.java │ │ │ │ ├── ConsoleServerStateController.java │ │ │ │ ├── ai/ │ │ │ │ │ ├── ConsoleA2aController.java │ │ │ │ │ ├── ConsoleCopilotConfigController.java │ │ │ │ │ ├── ConsoleCopilotController.java │ │ │ │ │ ├── ConsoleMcpController.java │ │ │ │ │ ├── ConsolePromptController.java │ │ │ │ │ ├── ConsoleSkillController.java │ │ │ │ │ ├── CopilotHttpParamExtractor.java │ │ │ │ │ └── CopilotSseExceptionHandler.java │ │ │ │ ├── config/ │ │ │ │ │ ├── ConsoleConfigController.java │ │ │ │ │ └── ConsoleHistoryController.java │ │ │ │ ├── core/ │ │ │ │ │ ├── ConsoleClusterController.java │ │ │ │ │ ├── ConsoleNamespaceController.java │ │ │ │ │ └── ConsolePluginController.java │ │ │ │ └── naming/ │ │ │ │ ├── ConsoleInstanceController.java │ │ │ │ └── ConsoleServiceController.java │ │ │ ├── exception/ │ │ │ │ └── ConsoleExceptionHandler.java │ │ │ ├── filter/ │ │ │ │ ├── NacosConsoleAuthFilter.java │ │ │ │ └── XssFilter.java │ │ │ ├── handler/ │ │ │ │ ├── HealthHandler.java │ │ │ │ ├── ServerStateHandler.java │ │ │ │ ├── ai/ │ │ │ │ │ ├── A2aHandler.java │ │ │ │ │ ├── EnabledAiHandler.java │ │ │ │ │ ├── McpHandler.java │ │ │ │ │ ├── PromptHandler.java │ │ │ │ │ └── SkillHandler.java │ │ │ │ ├── config/ │ │ │ │ │ ├── ConfigHandler.java │ │ │ │ │ └── HistoryHandler.java │ │ │ │ ├── core/ │ │ │ │ │ ├── ClusterHandler.java │ │ │ │ │ ├── NamespaceHandler.java │ │ │ │ │ └── PluginHandler.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── AbstractServerStateHandler.java │ │ │ │ │ ├── ConditionFunctionEnabled.java │ │ │ │ │ ├── inner/ │ │ │ │ │ │ ├── EnabledInnerHandler.java │ │ │ │ │ │ ├── HealthInnerHandler.java │ │ │ │ │ │ ├── ServerStateInnerHandler.java │ │ │ │ │ │ ├── ai/ │ │ │ │ │ │ │ ├── A2aInnerHandler.java │ │ │ │ │ │ │ ├── McpInnerHandler.java │ │ │ │ │ │ │ ├── PromptInnerHandler.java │ │ │ │ │ │ │ └── SkillInnerHandler.java │ │ │ │ │ │ ├── config/ │ │ │ │ │ │ │ ├── ConfigInnerHandler.java │ │ │ │ │ │ │ └── HistoryInnerHandler.java │ │ │ │ │ │ ├── core/ │ │ │ │ │ │ │ ├── ClusterInnerHandler.java │ │ │ │ │ │ │ ├── NamespaceInnerHandler.java │ │ │ │ │ │ │ └── PluginInnerHandler.java │ │ │ │ │ │ └── naming/ │ │ │ │ │ │ ├── InstanceInnerHandler.java │ │ │ │ │ │ └── ServiceInnerHandler.java │ │ │ │ │ ├── noop/ │ │ │ │ │ │ ├── ai/ │ │ │ │ │ │ │ ├── A2aNoopHandler.java │ │ │ │ │ │ │ ├── McpNoopHandler.java │ │ │ │ │ │ │ ├── PromptNoopHandler.java │ │ │ │ │ │ │ └── SkillNoopHandler.java │ │ │ │ │ │ ├── config/ │ │ │ │ │ │ │ ├── ConfigNoopHandler.java │ │ │ │ │ │ │ └── HistoryNoopHandler.java │ │ │ │ │ │ └── naming/ │ │ │ │ │ │ ├── InstanceNoopHandler.java │ │ │ │ │ │ └── ServiceNoopHandler.java │ │ │ │ │ └── remote/ │ │ │ │ │ ├── ConsoleMaintainerClientAuthPlugin.java │ │ │ │ │ ├── EnabledRemoteHandler.java │ │ │ │ │ ├── HealthRemoteHandler.java │ │ │ │ │ ├── NacosMaintainerClientHolder.java │ │ │ │ │ ├── RemoteServerConnector.java │ │ │ │ │ ├── ServerStateRemoteHandler.java │ │ │ │ │ ├── ai/ │ │ │ │ │ │ ├── A2aRemoteHandler.java │ │ │ │ │ │ ├── McpRemoteHandler.java │ │ │ │ │ │ ├── PromptRemoteHandler.java │ │ │ │ │ │ └── SkillRemoteHandler.java │ │ │ │ │ ├── config/ │ │ │ │ │ │ ├── ConfigImportAndExportService.java │ │ │ │ │ │ ├── ConfigRemoteHandler.java │ │ │ │ │ │ └── HistoryRemoteHandler.java │ │ │ │ │ ├── core/ │ │ │ │ │ │ ├── ClusterRemoteHandler.java │ │ │ │ │ │ ├── NamespaceRemoteHandler.java │ │ │ │ │ │ └── PluginRemoteHandler.java │ │ │ │ │ └── naming/ │ │ │ │ │ ├── InstanceRemoteHandler.java │ │ │ │ │ └── ServiceRemoteHandler.java │ │ │ │ └── naming/ │ │ │ │ ├── InstanceHandler.java │ │ │ │ └── ServiceHandler.java │ │ │ ├── paramcheck/ │ │ │ │ └── ConsoleDefaultHttpParamExtractor.java │ │ │ └── proxy/ │ │ │ ├── HealthProxy.java │ │ │ ├── ServerStateProxy.java │ │ │ ├── ai/ │ │ │ │ ├── A2aProxy.java │ │ │ │ ├── McpProxy.java │ │ │ │ ├── PromptProxy.java │ │ │ │ └── SkillProxy.java │ │ │ ├── config/ │ │ │ │ ├── ConfigProxy.java │ │ │ │ └── HistoryProxy.java │ │ │ ├── core/ │ │ │ │ ├── ClusterProxy.java │ │ │ │ ├── NamespaceProxy.java │ │ │ │ └── PluginProxy.java │ │ │ └── naming/ │ │ │ ├── InstanceProxy.java │ │ │ └── ServiceProxy.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── nacos-default.properties │ │ │ ├── native-image/ │ │ │ │ └── com.alibaba.nacos/ │ │ │ │ └── nacos-console/ │ │ │ │ ├── jni-config.json │ │ │ │ ├── predefined-classes-config.json │ │ │ │ ├── proxy-config.json │ │ │ │ ├── reflect-config.json │ │ │ │ ├── resource-config.json │ │ │ │ └── serialization-config.json │ │ │ └── services/ │ │ │ ├── com.alibaba.nacos.auth.config.NacosAuthConfig │ │ │ ├── com.alibaba.nacos.core.listener.startup.NacosStartUp │ │ │ ├── com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor │ │ │ ├── com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService │ │ │ ├── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ │ │ └── com.alibaba.nacos.sys.module.ModuleStateBuilder │ │ ├── nacos-console-banner.txt │ │ ├── nacos-console.properties │ │ └── static/ │ │ ├── console-ui/ │ │ │ └── public/ │ │ │ ├── css/ │ │ │ │ ├── bootstrap.css │ │ │ │ ├── codemirror.css │ │ │ │ ├── console1412.css │ │ │ │ ├── font-awesome.css │ │ │ │ ├── icon.css │ │ │ │ └── merge.css │ │ │ └── js/ │ │ │ ├── codemirror.addone.fullscreen.js │ │ │ ├── codemirror.addone.json-lint.js │ │ │ ├── codemirror.addone.lint.js │ │ │ ├── codemirror.js │ │ │ ├── codemirror.lib.clike-lint.js │ │ │ ├── codemirror.lib.json-lint.js │ │ │ ├── diff_match_patch.js │ │ │ ├── javascript.js │ │ │ ├── jquery.js │ │ │ ├── loader.js │ │ │ ├── merge.js │ │ │ ├── vs/ │ │ │ │ ├── base/ │ │ │ │ │ └── worker/ │ │ │ │ │ └── workerMain.js │ │ │ │ ├── basic-languages/ │ │ │ │ │ └── src/ │ │ │ │ │ ├── bat.js │ │ │ │ │ ├── coffee.js │ │ │ │ │ ├── cpp.js │ │ │ │ │ ├── csharp.js │ │ │ │ │ ├── css.js │ │ │ │ │ ├── dockerfile.js │ │ │ │ │ ├── fsharp.js │ │ │ │ │ ├── go.js │ │ │ │ │ ├── handlebars.js │ │ │ │ │ ├── html.js │ │ │ │ │ ├── ini.js │ │ │ │ │ ├── java.js │ │ │ │ │ ├── less.js │ │ │ │ │ ├── lua.js │ │ │ │ │ ├── markdown.js │ │ │ │ │ ├── msdax.js │ │ │ │ │ ├── objective-c.js │ │ │ │ │ ├── php.js │ │ │ │ │ ├── postiats.js │ │ │ │ │ ├── powershell.js │ │ │ │ │ ├── pug.js │ │ │ │ │ ├── python.js │ │ │ │ │ ├── r.js │ │ │ │ │ ├── razor.js │ │ │ │ │ ├── ruby.js │ │ │ │ │ ├── sb.js │ │ │ │ │ ├── scss.js │ │ │ │ │ ├── solidity.js │ │ │ │ │ ├── sql.js │ │ │ │ │ ├── swift.js │ │ │ │ │ ├── vb.js │ │ │ │ │ ├── xml.js │ │ │ │ │ └── yaml.js │ │ │ │ ├── editor/ │ │ │ │ │ ├── editor.main.css │ │ │ │ │ ├── editor.main.js │ │ │ │ │ ├── editor.main.nls.de.js │ │ │ │ │ ├── editor.main.nls.es.js │ │ │ │ │ ├── editor.main.nls.fr.js │ │ │ │ │ ├── editor.main.nls.hu.js │ │ │ │ │ ├── editor.main.nls.it.js │ │ │ │ │ ├── editor.main.nls.ja.js │ │ │ │ │ ├── editor.main.nls.js │ │ │ │ │ ├── editor.main.nls.ko.js │ │ │ │ │ ├── editor.main.nls.pt-br.js │ │ │ │ │ ├── editor.main.nls.ru.js │ │ │ │ │ ├── editor.main.nls.tr.js │ │ │ │ │ ├── editor.main.nls.zh-cn.js │ │ │ │ │ └── editor.main.nls.zh-tw.js │ │ │ │ ├── language/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ ├── cssMode.js │ │ │ │ │ │ └── cssWorker.js │ │ │ │ │ ├── html/ │ │ │ │ │ │ ├── htmlMode.js │ │ │ │ │ │ └── htmlWorker.js │ │ │ │ │ ├── json/ │ │ │ │ │ │ ├── jsonMode.js │ │ │ │ │ │ └── jsonWorker.js │ │ │ │ │ └── typescript/ │ │ │ │ │ ├── lib/ │ │ │ │ │ │ └── typescriptServices.js │ │ │ │ │ └── src/ │ │ │ │ │ ├── mode.js │ │ │ │ │ └── worker.js │ │ │ │ └── loader.js │ │ │ └── xml.js │ │ ├── css/ │ │ │ └── main.css │ │ ├── index.html │ │ ├── js/ │ │ │ └── main.js │ │ └── login.html │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── console/ │ │ ├── NacosConsoleStartUpTest.java │ │ ├── aot/ │ │ │ └── NacosRuntimeHintsTest.java │ │ ├── cluster/ │ │ │ └── RemoteServerMemberManagerTest.java │ │ ├── config/ │ │ │ ├── ConsoleAuthModuleStateBuilderTest.java │ │ │ ├── ConsoleCorsConfigTest.java │ │ │ ├── ConsoleDeploymentConfigTest.java │ │ │ ├── ConsoleFunctionEnabledConfigTest.java │ │ │ ├── ConsoleModuleStateBuilderTest.java │ │ │ ├── ConsoleWebConfigTest.java │ │ │ ├── NacosConsoleAuthConfigTest.java │ │ │ └── NacosConsoleBeanPostProcessorConfigurationTest.java │ │ ├── controller/ │ │ │ └── v3/ │ │ │ ├── ConsoleHealthControllerTest.java │ │ │ ├── ConsoleServerStateControllerTest.java │ │ │ ├── ai/ │ │ │ │ ├── ConsoleA2aControllerTest.java │ │ │ │ └── ConsoleMcpControllerTest.java │ │ │ ├── config/ │ │ │ │ ├── ConsoleConfigControllerTest.java │ │ │ │ └── ConsoleHistoryControllerTest.java │ │ │ ├── core/ │ │ │ │ ├── ConsoleClusterControllerTest.java │ │ │ │ └── ConsoleNamespaceControllerTest.java │ │ │ └── naming/ │ │ │ ├── ConsoleInstanceControllerTest.java │ │ │ └── ConsoleServiceControllerTest.java │ │ ├── exception/ │ │ │ └── ConsoleExceptionHandlerTest.java │ │ ├── filter/ │ │ │ ├── NacosConsoleAuthFilterTest.java │ │ │ └── XssFilterTest.java │ │ ├── handler/ │ │ │ └── impl/ │ │ │ ├── AbstractServerStateHandlerTest.java │ │ │ ├── ConditionFunctionEnabledTest.java │ │ │ ├── inner/ │ │ │ │ ├── HealthInnerHandlerTest.java │ │ │ │ ├── ServerStateInnerHandlerTest.java │ │ │ │ ├── ai/ │ │ │ │ │ ├── A2aInnerHandlerTest.java │ │ │ │ │ └── McpInnerHandlerTest.java │ │ │ │ ├── config/ │ │ │ │ │ ├── ConfigInnerHandlerTest.java │ │ │ │ │ └── HistoryInnerHandlerTest.java │ │ │ │ ├── core/ │ │ │ │ │ ├── ClusterInnerHandlerTest.java │ │ │ │ │ ├── NamespaceInnerHandlerTest.java │ │ │ │ │ └── PluginInnerHandlerTest.java │ │ │ │ └── naming/ │ │ │ │ ├── InstanceInnerHandlerTest.java │ │ │ │ └── ServiceInnerHandlerTest.java │ │ │ ├── noop/ │ │ │ │ ├── ai/ │ │ │ │ │ ├── A2aNoopHandlerTest.java │ │ │ │ │ └── McpNoopHandlerTest.java │ │ │ │ ├── config/ │ │ │ │ │ ├── ConfigNoopHandlerTest.java │ │ │ │ │ └── HistoryNoopHandlerTest.java │ │ │ │ └── naming/ │ │ │ │ ├── InstanceNoopHandlerTest.java │ │ │ │ └── ServiceNoopHandlerTest.java │ │ │ └── remote/ │ │ │ ├── AbstractRemoteHandlerTest.java │ │ │ ├── ConsoleMaintainerClientAuthPluginTest.java │ │ │ ├── HealthRemoteHandlerTest.java │ │ │ ├── NacosMaintainerClientHolderTest.java │ │ │ ├── RemoteServerConnectorTest.java │ │ │ ├── ServerStateRemoteHandlerTest.java │ │ │ ├── ai/ │ │ │ │ ├── A2aRemoteHandlerTest.java │ │ │ │ └── McpRemoteHandlerTest.java │ │ │ ├── config/ │ │ │ │ ├── ConfigImportAndExportServiceTest.java │ │ │ │ ├── ConfigRemoteHandlerTest.java │ │ │ │ └── HistoryRemoteHandlerTest.java │ │ │ ├── core/ │ │ │ │ ├── ClusterRemoteHandlerTest.java │ │ │ │ ├── NamespaceRemoteHandlerTest.java │ │ │ │ └── PluginRemoteHandlerTest.java │ │ │ └── naming/ │ │ │ ├── InstanceRemoteHandlerTest.java │ │ │ └── ServiceRemoteHandlerTest.java │ │ ├── paramcheck/ │ │ │ └── ConsoleDefaultHttpParamExtractorTest.java │ │ └── proxy/ │ │ ├── HealthProxyTest.java │ │ ├── ServerStateProxyTest.java │ │ ├── ai/ │ │ │ ├── A2aProxyTest.java │ │ │ └── McpProxyTest.java │ │ ├── config/ │ │ │ ├── ConfigProxyTest.java │ │ │ └── HistoryProxyTest.java │ │ ├── core/ │ │ │ ├── ClusterProxyTest.java │ │ │ └── NamespaceProxyTest.java │ │ └── naming/ │ │ ├── InstanceProxyTest.java │ │ └── ServiceProxyTest.java │ └── resources/ │ ├── mock/ │ │ ├── application.properties │ │ └── existconf/ │ │ ├── announcement_zh_CN.conf │ │ └── console-guide.conf │ └── nacos-console.properties ├── console-ui/ │ ├── .babelrc │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── README.md │ ├── build/ │ │ ├── copy-dist.js │ │ ├── copyFile.js │ │ ├── webpack.base.conf.js │ │ ├── webpack.dev.conf.js │ │ └── webpack.prod.conf.js │ ├── package.json │ ├── public/ │ │ └── index.ejs │ ├── src/ │ │ ├── components/ │ │ │ ├── BatchHandle/ │ │ │ │ ├── BatchHandle.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── CloneDialog/ │ │ │ │ ├── CloneDialog.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Copy/ │ │ │ │ └── index.jsx │ │ │ ├── DeleteDialog/ │ │ │ │ ├── DeleteDialog.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── DiffEditorDialog/ │ │ │ │ ├── DiffEditorDialog.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── EditorNameSpace/ │ │ │ │ ├── EditorNameSpace.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── ExportDialog/ │ │ │ │ ├── ExportDialog.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── ImportDialog/ │ │ │ │ ├── ImportDialog.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── MagicWandIcon/ │ │ │ │ └── MagicWandIcon.js │ │ │ ├── MarkdownRenderer/ │ │ │ │ └── MarkdownRenderer.js │ │ │ ├── MonacoEditor/ │ │ │ │ ├── MonacoEditor.tsx │ │ │ │ ├── constant.ts │ │ │ │ ├── index.scss │ │ │ │ └── index.tsx │ │ │ ├── NameSpaceList/ │ │ │ │ ├── NameSpaceList.js │ │ │ │ ├── index.js │ │ │ │ ├── index.scss │ │ │ │ └── show.js │ │ │ ├── NewNameSpace/ │ │ │ │ ├── NewNameSpace.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Page/ │ │ │ │ └── TotalRender.js │ │ │ ├── PageTitle/ │ │ │ │ └── index.js │ │ │ ├── QueryResult/ │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── RegionGroup/ │ │ │ │ ├── RegionGroup.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── ShowCodeing/ │ │ │ │ ├── ShowCodeing.js │ │ │ │ ├── ShowServiceCodeing.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ └── SuccessDialog/ │ │ │ ├── SuccessDialog.js │ │ │ ├── index.js │ │ │ └── index.scss │ │ ├── config.js │ │ ├── constants.js │ │ ├── globalLib.js │ │ ├── index.js │ │ ├── index.scss │ │ ├── layouts/ │ │ │ ├── Header.js │ │ │ ├── MainLayout.js │ │ │ ├── index.scss │ │ │ └── menu.js │ │ ├── lib.js │ │ ├── locales/ │ │ │ ├── en-US.js │ │ │ ├── index.js │ │ │ └── zh-CN.js │ │ ├── pages/ │ │ │ ├── AI/ │ │ │ │ ├── AgentDetail/ │ │ │ │ │ ├── AgentDetail.js │ │ │ │ │ └── index.js │ │ │ │ ├── AgentManagement/ │ │ │ │ │ ├── AgentManagement.js │ │ │ │ │ ├── AgentManagement.scss │ │ │ │ │ └── index.js │ │ │ │ ├── McpDetail/ │ │ │ │ │ ├── CreateTools/ │ │ │ │ │ │ ├── AdvancedConfig.js │ │ │ │ │ │ ├── AnnotationsEditor.js │ │ │ │ │ │ ├── BasicInfo.js │ │ │ │ │ │ ├── CreateTools.css │ │ │ │ │ │ ├── DeleteTool.js │ │ │ │ │ │ ├── InputSchema.js │ │ │ │ │ │ ├── MetaEditor.js │ │ │ │ │ │ ├── OutputSchema.js │ │ │ │ │ │ ├── SchemaEditor.js │ │ │ │ │ │ ├── components.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── utils.js │ │ │ │ │ ├── McpDetail.css │ │ │ │ │ ├── McpDetail.js │ │ │ │ │ ├── ShowTools.css │ │ │ │ │ ├── ShowTools.css.bak │ │ │ │ │ ├── ShowTools.js │ │ │ │ │ ├── Swagger2Tools.js │ │ │ │ │ └── index.js │ │ │ │ ├── McpManagement/ │ │ │ │ │ ├── DashboardCard.js │ │ │ │ │ ├── ImportMcpDialog.jsx │ │ │ │ │ ├── McpManagement.js │ │ │ │ │ └── index.js │ │ │ │ ├── NewAgent/ │ │ │ │ │ ├── NewAgent.js │ │ │ │ │ ├── NewAgent.scss │ │ │ │ │ └── index.js │ │ │ │ ├── NewMcpServer/ │ │ │ │ │ ├── NewMcpServer.css │ │ │ │ │ ├── NewMcpServer.js │ │ │ │ │ └── index.js │ │ │ │ ├── NewPrompt/ │ │ │ │ │ ├── NewPrompt.js │ │ │ │ │ ├── NewPrompt.scss │ │ │ │ │ └── index.js │ │ │ │ ├── NewSkill/ │ │ │ │ │ ├── NewSkill.js │ │ │ │ │ ├── NewSkill.scss │ │ │ │ │ └── index.js │ │ │ │ ├── PromptDetail/ │ │ │ │ │ ├── PromptDetail.js │ │ │ │ │ ├── PromptDetail.scss │ │ │ │ │ └── index.js │ │ │ │ ├── PromptManagement/ │ │ │ │ │ ├── PromptManagement.js │ │ │ │ │ ├── PromptManagement.scss │ │ │ │ │ └── index.js │ │ │ │ ├── PromptOptimizeDialog/ │ │ │ │ │ ├── PromptOptimizeDialog.js │ │ │ │ │ ├── PromptOptimizeDialog.scss │ │ │ │ │ └── index.js │ │ │ │ ├── PublishPromptVersion/ │ │ │ │ │ ├── PublishPromptVersion.js │ │ │ │ │ ├── PublishPromptVersion.scss │ │ │ │ │ └── index.js │ │ │ │ ├── README.md │ │ │ │ ├── SkillDetail/ │ │ │ │ │ ├── SkillDetail.js │ │ │ │ │ ├── SkillDetail.scss │ │ │ │ │ └── index.js │ │ │ │ ├── SkillManagement/ │ │ │ │ │ ├── SkillManagement.js │ │ │ │ │ ├── SkillManagement.scss │ │ │ │ │ ├── SkillOptimizeDialog.js │ │ │ │ │ ├── SkillOptimizeDialog.scss │ │ │ │ │ └── index.js │ │ │ │ └── services/ │ │ │ │ └── OpenApiService.js │ │ │ ├── AuthorityControl/ │ │ │ │ ├── PermissionsManagement/ │ │ │ │ │ ├── NewPermissions.js │ │ │ │ │ ├── PermissionsManagement.js │ │ │ │ │ ├── PermissionsManagement.scss │ │ │ │ │ └── index.js │ │ │ │ ├── README.md │ │ │ │ ├── RolesManagement/ │ │ │ │ │ ├── NewRole.js │ │ │ │ │ ├── RolesManagement.js │ │ │ │ │ ├── RolesManagement.scss │ │ │ │ │ └── index.js │ │ │ │ ├── UserManagement/ │ │ │ │ │ ├── NewUser.js │ │ │ │ │ ├── PasswordReset.js │ │ │ │ │ ├── UserManagement.js │ │ │ │ │ ├── UserManagement.scss │ │ │ │ │ └── index.js │ │ │ │ └── authority.scss │ │ │ ├── ClusterManagement/ │ │ │ │ └── ClusterNodeList/ │ │ │ │ ├── ClusterNodeList.js │ │ │ │ ├── ClusterNodeList.scss │ │ │ │ └── index.js │ │ │ ├── ConfigurationManagement/ │ │ │ │ ├── ConfigDetail/ │ │ │ │ │ ├── ConfigCompared.js │ │ │ │ │ ├── ConfigDetail.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── ConfigEditor/ │ │ │ │ │ ├── ConfigEditor.js │ │ │ │ │ ├── NewConfigEditor.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── ConfigRollback/ │ │ │ │ │ ├── ConfigRollback.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── ConfigSync/ │ │ │ │ │ ├── ConfigSync.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── ConfigurationManagement/ │ │ │ │ │ ├── ConfigurationManagement.js │ │ │ │ │ ├── DashboardCard.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── HistoryDetail/ │ │ │ │ │ ├── HistoryDetail.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── HistoryRollback/ │ │ │ │ │ ├── HistoryRollback.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ ├── ListeningToQuery/ │ │ │ │ │ ├── ListeningToQuery.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── index.scss │ │ │ │ └── NewConfig/ │ │ │ │ ├── NewConfig.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── Login/ │ │ │ │ ├── Login.jsx │ │ │ │ ├── index.jsx │ │ │ │ └── index.scss │ │ │ ├── NameSpace/ │ │ │ │ ├── NameSpace.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ ├── PluginManagement/ │ │ │ │ └── PluginList/ │ │ │ │ ├── PluginDetail.js │ │ │ │ ├── PluginList.js │ │ │ │ ├── PluginList.scss │ │ │ │ └── index.js │ │ │ ├── Register/ │ │ │ │ ├── Register.jsx │ │ │ │ ├── index.jsx │ │ │ │ └── index.scss │ │ │ ├── ServiceManagement/ │ │ │ │ ├── ServiceDetail/ │ │ │ │ │ ├── EditClusterDialog.js │ │ │ │ │ ├── EditInstanceDialog.js │ │ │ │ │ ├── EditServiceDialog.js │ │ │ │ │ ├── InstanceFilter.js │ │ │ │ │ ├── InstanceTable.js │ │ │ │ │ ├── ServiceDetail.js │ │ │ │ │ ├── ServiceDetail.scss │ │ │ │ │ ├── constant.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── util.js │ │ │ │ ├── ServiceList/ │ │ │ │ │ ├── ServiceList.js │ │ │ │ │ ├── ServiceList.scss │ │ │ │ │ └── index.js │ │ │ │ └── SubscriberList/ │ │ │ │ ├── SubscriberList.js │ │ │ │ ├── SubscriberList.scss │ │ │ │ └── index.js │ │ │ ├── SettingCenter/ │ │ │ │ ├── CopilotConfig.js │ │ │ │ ├── SettingCenter.js │ │ │ │ ├── index.js │ │ │ │ └── index.scss │ │ │ └── Welcome/ │ │ │ ├── Welcome.js │ │ │ └── index.js │ │ ├── reducers/ │ │ │ ├── authority.js │ │ │ ├── base.js │ │ │ ├── configuration.js │ │ │ ├── index.js │ │ │ ├── locale.js │ │ │ ├── namespace.js │ │ │ └── subscribers.js │ │ ├── theme/ │ │ │ ├── index.js │ │ │ └── index.scss │ │ └── utils/ │ │ ├── languageDetector.js │ │ ├── message.js │ │ ├── nacosutil.js │ │ ├── request.js │ │ └── validateContent.js │ ├── test/ │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── README.md │ │ ├── commons/ │ │ │ └── commons.md │ │ ├── config.json │ │ ├── hosts │ │ ├── install.sh │ │ ├── package.json │ │ ├── run.bat │ │ ├── run.sh │ │ ├── sample/ │ │ │ ├── configDetail.spec.js │ │ │ ├── configurationManagement.spec.js │ │ │ └── instanceFilter.spec.js │ │ └── uploadfiles/ │ │ └── uploadfiles.md │ └── tsconfig.json ├── copilot/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── copilot/ │ │ │ ├── adapter/ │ │ │ │ └── StreamResponseCallback.java │ │ │ ├── capability/ │ │ │ │ └── prompt/ │ │ │ │ ├── PromptOptimizationPrompt.java │ │ │ │ ├── SkillGenerationPrompt.java │ │ │ │ └── SkillOptimizationPrompt.java │ │ │ ├── config/ │ │ │ │ ├── CopilotAgentManager.java │ │ │ │ ├── CopilotConfigStorage.java │ │ │ │ ├── CopilotConfiguration.java │ │ │ │ └── CopilotProperties.java │ │ │ ├── constant/ │ │ │ │ └── CopilotConstants.java │ │ │ ├── form/ │ │ │ │ ├── PromptDebugForm.java │ │ │ │ ├── PromptOptimizationForm.java │ │ │ │ ├── SkillGenerationForm.java │ │ │ │ └── SkillOptimizationForm.java │ │ │ ├── model/ │ │ │ │ ├── ChatMessage.java │ │ │ │ ├── ChatRequest.java │ │ │ │ ├── ChatResponse.java │ │ │ │ ├── ConversationHistory.java │ │ │ │ ├── ConversationMessage.java │ │ │ │ ├── OptimizationChange.java │ │ │ │ ├── PromptDebugRequest.java │ │ │ │ ├── PromptDebugResponse.java │ │ │ │ ├── PromptOptimizationRequest.java │ │ │ │ ├── PromptOptimizationResponse.java │ │ │ │ ├── SkillGenerationRequest.java │ │ │ │ ├── SkillGenerationResponse.java │ │ │ │ ├── SkillOptimizationRequest.java │ │ │ │ ├── SkillOptimizationResponse.java │ │ │ │ └── StreamResponseType.java │ │ │ └── service/ │ │ │ ├── PromptDebugService.java │ │ │ ├── PromptDebugServiceImpl.java │ │ │ ├── PromptOptimizationService.java │ │ │ ├── PromptOptimizationServiceImpl.java │ │ │ ├── SkillGenerationService.java │ │ │ ├── SkillGenerationServiceImpl.java │ │ │ ├── SkillOptimizationService.java │ │ │ ├── SkillOptimizationServiceImpl.java │ │ │ └── StreamEventProcessor.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── copilot/ │ ├── config/ │ │ ├── CopilotAgentManagerTest.java │ │ └── CopilotPropertiesTest.java │ ├── form/ │ │ ├── SkillGenerationFormTest.java │ │ └── SkillOptimizationFormTest.java │ ├── model/ │ │ ├── ChatMessageTest.java │ │ ├── ChatRequestTest.java │ │ ├── ChatResponseTest.java │ │ ├── OptimizationChangeTest.java │ │ ├── SkillOptimizationResponseTest.java │ │ └── StreamResponseTypeTest.java │ └── service/ │ ├── SkillGenerationServiceImplTest.java │ ├── SkillOptimizationServiceImplTest.java │ └── StreamEventProcessorTest.java ├── core/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ ├── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── core/ │ │ │ │ ├── ability/ │ │ │ │ │ ├── RemoteAbilityInitializer.java │ │ │ │ │ ├── ServerAbilityInitializer.java │ │ │ │ │ ├── ServerAbilityInitializerHolder.java │ │ │ │ │ ├── config/ │ │ │ │ │ │ └── AbilityConfigs.java │ │ │ │ │ └── control/ │ │ │ │ │ └── ServerAbilityControlManager.java │ │ │ │ ├── auth/ │ │ │ │ │ ├── AbstractWebAuthFilter.java │ │ │ │ │ ├── AuthAdminFilter.java │ │ │ │ │ ├── AuthConfig.java │ │ │ │ │ ├── AuthFilter.java │ │ │ │ │ ├── AuthModuleStateBuilder.java │ │ │ │ │ ├── InnerApiAuthEnabled.java │ │ │ │ │ ├── NacosServerAdminAuthConfig.java │ │ │ │ │ ├── NacosServerAuthConfig.java │ │ │ │ │ └── RemoteRequestAuthFilter.java │ │ │ │ ├── cluster/ │ │ │ │ │ ├── Member.java │ │ │ │ │ ├── MemberChangeListener.java │ │ │ │ │ ├── MemberLookup.java │ │ │ │ │ ├── MemberMetaDataConstants.java │ │ │ │ │ ├── MemberUtil.java │ │ │ │ │ ├── MembersChangeEvent.java │ │ │ │ │ ├── NacosMemberManager.java │ │ │ │ │ ├── ServerMemberManager.java │ │ │ │ │ ├── Task.java │ │ │ │ │ ├── health/ │ │ │ │ │ │ ├── AbstractModuleHealthChecker.java │ │ │ │ │ │ ├── ModuleHealthCheckerHolder.java │ │ │ │ │ │ └── ReadinessResult.java │ │ │ │ │ ├── lookup/ │ │ │ │ │ │ ├── AbstractMemberLookup.java │ │ │ │ │ │ ├── AddressServerMemberLookup.java │ │ │ │ │ │ ├── FileConfigMemberLookup.java │ │ │ │ │ │ ├── LookupFactory.java │ │ │ │ │ │ └── StandaloneMemberLookup.java │ │ │ │ │ └── remote/ │ │ │ │ │ ├── ClusterRpcClientProxy.java │ │ │ │ │ ├── MemberReportHandler.java │ │ │ │ │ ├── request/ │ │ │ │ │ │ ├── AbstractClusterRequest.java │ │ │ │ │ │ ├── MemberReportRequest.java │ │ │ │ │ │ ├── PluginAvailabilityRequest.java │ │ │ │ │ │ └── PluginAvailabilityRequestHandler.java │ │ │ │ │ └── response/ │ │ │ │ │ ├── MemberReportResponse.java │ │ │ │ │ └── PluginAvailabilityResponse.java │ │ │ │ ├── code/ │ │ │ │ │ ├── ControllerMethodsCache.java │ │ │ │ │ ├── RequestMappingInfo.java │ │ │ │ │ ├── SpringApplicationRunListener.java │ │ │ │ │ ├── StandaloneProfileApplicationListener.java │ │ │ │ │ └── condition/ │ │ │ │ │ ├── ParamRequestCondition.java │ │ │ │ │ └── PathRequestCondition.java │ │ │ │ ├── config/ │ │ │ │ │ ├── AbstractDynamicConfig.java │ │ │ │ │ ├── DistroModuleStateBuilder.java │ │ │ │ │ └── RaftModuleStateBuilder.java │ │ │ │ ├── console/ │ │ │ │ │ ├── ConsolePathTipConfig.java │ │ │ │ │ └── NacosConsolePathTipFilter.java │ │ │ │ ├── context/ │ │ │ │ │ ├── RequestContext.java │ │ │ │ │ ├── RequestContextHolder.java │ │ │ │ │ ├── addition/ │ │ │ │ │ │ ├── AddressContext.java │ │ │ │ │ │ ├── AuthContext.java │ │ │ │ │ │ ├── BasicContext.java │ │ │ │ │ │ └── EngineContext.java │ │ │ │ │ └── remote/ │ │ │ │ │ ├── HttpRequestContextConfig.java │ │ │ │ │ └── HttpRequestContextFilter.java │ │ │ │ ├── control/ │ │ │ │ │ ├── SpringValueConfigsInitializer.java │ │ │ │ │ ├── TpsControl.java │ │ │ │ │ ├── TpsControlConfig.java │ │ │ │ │ ├── http/ │ │ │ │ │ │ ├── HttpTpsCheckRequestParser.java │ │ │ │ │ │ ├── HttpTpsCheckRequestParserRegistry.java │ │ │ │ │ │ ├── HttpTpsPointRegistry.java │ │ │ │ │ │ ├── NacosHttpTpsControlRegistration.java │ │ │ │ │ │ └── NacosHttpTpsFilter.java │ │ │ │ │ └── remote/ │ │ │ │ │ ├── RemoteTpsCheckRequestParser.java │ │ │ │ │ ├── RemoteTpsCheckRequestParserRegistry.java │ │ │ │ │ └── TpsControlRequestFilter.java │ │ │ │ ├── controller/ │ │ │ │ │ ├── compatibility/ │ │ │ │ │ │ └── Compatibility.java │ │ │ │ │ └── v3/ │ │ │ │ │ ├── CoreOpsControllerV3.java │ │ │ │ │ ├── NacosClusterControllerV3.java │ │ │ │ │ ├── NamespaceControllerV3.java │ │ │ │ │ ├── PluginControllerV3.java │ │ │ │ │ ├── ServerLoaderControllerV3.java │ │ │ │ │ └── ServerStateController.java │ │ │ │ ├── distributed/ │ │ │ │ │ ├── AbstractConsistencyProtocol.java │ │ │ │ │ ├── ConsistencyConfiguration.java │ │ │ │ │ ├── ProtocolExecutor.java │ │ │ │ │ ├── ProtocolManager.java │ │ │ │ │ ├── distro/ │ │ │ │ │ │ ├── DistroConfig.java │ │ │ │ │ │ ├── DistroConstants.java │ │ │ │ │ │ ├── DistroProtocol.java │ │ │ │ │ │ ├── component/ │ │ │ │ │ │ │ ├── DistroCallback.java │ │ │ │ │ │ │ ├── DistroComponentHolder.java │ │ │ │ │ │ │ ├── DistroDataProcessor.java │ │ │ │ │ │ │ ├── DistroDataStorage.java │ │ │ │ │ │ │ ├── DistroFailedTaskHandler.java │ │ │ │ │ │ │ └── DistroTransportAgent.java │ │ │ │ │ │ ├── entity/ │ │ │ │ │ │ │ ├── DistroData.java │ │ │ │ │ │ │ └── DistroKey.java │ │ │ │ │ │ ├── exception/ │ │ │ │ │ │ │ └── DistroException.java │ │ │ │ │ │ ├── monitor/ │ │ │ │ │ │ │ ├── DistroRecord.java │ │ │ │ │ │ │ └── DistroRecordsHolder.java │ │ │ │ │ │ └── task/ │ │ │ │ │ │ ├── DistroTaskEngineHolder.java │ │ │ │ │ │ ├── delay/ │ │ │ │ │ │ │ ├── DistroDelayTask.java │ │ │ │ │ │ │ ├── DistroDelayTaskExecuteEngine.java │ │ │ │ │ │ │ └── DistroDelayTaskProcessor.java │ │ │ │ │ │ ├── execute/ │ │ │ │ │ │ │ ├── AbstractDistroExecuteTask.java │ │ │ │ │ │ │ ├── DistroExecuteTaskExecuteEngine.java │ │ │ │ │ │ │ ├── DistroSyncChangeTask.java │ │ │ │ │ │ │ └── DistroSyncDeleteTask.java │ │ │ │ │ │ ├── load/ │ │ │ │ │ │ │ └── DistroLoadDataTask.java │ │ │ │ │ │ └── verify/ │ │ │ │ │ │ ├── DistroVerifyExecuteTask.java │ │ │ │ │ │ └── DistroVerifyTimedTask.java │ │ │ │ │ ├── id/ │ │ │ │ │ │ ├── IdGeneratorManager.java │ │ │ │ │ │ └── SnowFlowerIdGenerator.java │ │ │ │ │ └── raft/ │ │ │ │ │ ├── JRaftMaintainService.java │ │ │ │ │ ├── JRaftProtocol.java │ │ │ │ │ ├── JRaftServer.java │ │ │ │ │ ├── JSnapshotOperation.java │ │ │ │ │ ├── NacosClosure.java │ │ │ │ │ ├── NacosStateMachine.java │ │ │ │ │ ├── RaftConfig.java │ │ │ │ │ ├── RaftErrorEvent.java │ │ │ │ │ ├── RaftEvent.java │ │ │ │ │ ├── RaftSysConstants.java │ │ │ │ │ ├── exception/ │ │ │ │ │ │ ├── DuplicateRaftGroupException.java │ │ │ │ │ │ ├── JRaftException.java │ │ │ │ │ │ ├── NoLeaderException.java │ │ │ │ │ │ └── NoSuchRaftGroupException.java │ │ │ │ │ ├── processor/ │ │ │ │ │ │ ├── AbstractProcessor.java │ │ │ │ │ │ ├── NacosReadRequestProcessor.java │ │ │ │ │ │ └── NacosWriteRequestProcessor.java │ │ │ │ │ └── utils/ │ │ │ │ │ ├── FailoverClosure.java │ │ │ │ │ ├── FailoverClosureImpl.java │ │ │ │ │ ├── JRaftConstants.java │ │ │ │ │ ├── JRaftLogOperation.java │ │ │ │ │ ├── JRaftOps.java │ │ │ │ │ ├── JRaftUtils.java │ │ │ │ │ ├── RaftExecutor.java │ │ │ │ │ ├── RaftOptionsBuilder.java │ │ │ │ │ └── RetryRunner.java │ │ │ │ ├── exception/ │ │ │ │ │ ├── ErrorCode.java │ │ │ │ │ ├── KvStorageException.java │ │ │ │ │ └── NacosApiExceptionHandler.java │ │ │ │ ├── listener/ │ │ │ │ │ ├── LoggingApplicationListener.java │ │ │ │ │ ├── NacosApplicationListener.java │ │ │ │ │ ├── StartingApplicationListener.java │ │ │ │ │ └── startup/ │ │ │ │ │ ├── AbstractNacosStartUp.java │ │ │ │ │ ├── NacosCoreStartUp.java │ │ │ │ │ ├── NacosStartUp.java │ │ │ │ │ ├── NacosStartUpManager.java │ │ │ │ │ └── NacosWebStartUp.java │ │ │ │ ├── model/ │ │ │ │ │ ├── form/ │ │ │ │ │ │ ├── AggregationForm.java │ │ │ │ │ │ ├── PageForm.java │ │ │ │ │ │ └── v3/ │ │ │ │ │ │ └── RaftCommandForm.java │ │ │ │ │ └── request/ │ │ │ │ │ ├── LogUpdateRequest.java │ │ │ │ │ └── LookupUpdateRequest.java │ │ │ │ ├── monitor/ │ │ │ │ │ ├── GrpcServerThreadPoolMonitor.java │ │ │ │ │ ├── MetricsMonitor.java │ │ │ │ │ ├── NacosMeterRegistryCenter.java │ │ │ │ │ └── topn/ │ │ │ │ │ ├── BaseTopNCounter.java │ │ │ │ │ ├── FixedSizePriorityQueue.java │ │ │ │ │ ├── StringTopNCounter.java │ │ │ │ │ └── TopNConfig.java │ │ │ │ ├── namespace/ │ │ │ │ │ ├── filter/ │ │ │ │ │ │ ├── NamespaceValidation.java │ │ │ │ │ │ ├── NamespaceValidationConfig.java │ │ │ │ │ │ └── NamespaceValidationRequestFilter.java │ │ │ │ │ ├── injector/ │ │ │ │ │ │ ├── AbstractNamespaceDetailInjector.java │ │ │ │ │ │ └── NamespaceDetailInjectorHolder.java │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── NamespaceTypeEnum.java │ │ │ │ │ │ ├── TenantInfo.java │ │ │ │ │ │ └── form/ │ │ │ │ │ │ ├── CreateNamespaceForm.java │ │ │ │ │ │ └── NamespaceForm.java │ │ │ │ │ └── repository/ │ │ │ │ │ ├── EmbeddedNamespacePersistServiceImpl.java │ │ │ │ │ ├── ExternalNamespacePersistServiceImpl.java │ │ │ │ │ ├── NamespacePersistService.java │ │ │ │ │ └── NamespaceRowMapperInjector.java │ │ │ │ ├── paramcheck/ │ │ │ │ │ ├── AbstractHttpParamExtractor.java │ │ │ │ │ ├── AbstractRpcParamExtractor.java │ │ │ │ │ ├── CheckConfiguration.java │ │ │ │ │ ├── ExtractorManager.java │ │ │ │ │ ├── ParamCheckerFilter.java │ │ │ │ │ ├── ParamExtractor.java │ │ │ │ │ ├── ServerParamCheckConfig.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── AgentRequestParamExtractor.java │ │ │ │ │ ├── BatchInstanceRequestParamExtractor.java │ │ │ │ │ ├── ConfigBatchListenRequestParamExtractor.java │ │ │ │ │ ├── ConfigFuzzyWatchRequestParamsExtractor.java │ │ │ │ │ ├── ConfigRequestParamExtractor.java │ │ │ │ │ ├── InstanceRequestParamExtractor.java │ │ │ │ │ ├── McpServerRequestParamExtractor.java │ │ │ │ │ ├── PersistentInstanceRequestParamExtractor.java │ │ │ │ │ ├── PromptRequestParamExtractor.java │ │ │ │ │ ├── ServiceListRequestParamExtractor.java │ │ │ │ │ ├── ServiceQueryRequestParamExtractor.java │ │ │ │ │ └── SubscribeServiceRequestParamExtractor.java │ │ │ │ ├── persistence/ │ │ │ │ │ ├── DerbySnapshotOperation.java │ │ │ │ │ └── DistributedDatabaseOperateImpl.java │ │ │ │ ├── plugin/ │ │ │ │ │ ├── CriticalPluginConfig.java │ │ │ │ │ ├── PluginManager.java │ │ │ │ │ ├── PluginStateProcessor.java │ │ │ │ │ ├── PluginStateSnapshotOperation.java │ │ │ │ │ ├── condition/ │ │ │ │ │ │ ├── ConditionOnClusterMode.java │ │ │ │ │ │ └── ConditionOnStandaloneMode.java │ │ │ │ │ ├── model/ │ │ │ │ │ │ ├── PluginInfo.java │ │ │ │ │ │ ├── PluginStateOperation.java │ │ │ │ │ │ ├── PluginStateSnapshot.java │ │ │ │ │ │ ├── form/ │ │ │ │ │ │ │ ├── PluginConfigForm.java │ │ │ │ │ │ │ └── PluginStatusForm.java │ │ │ │ │ │ └── vo/ │ │ │ │ │ │ ├── PluginDetailVO.java │ │ │ │ │ │ └── PluginInfoVO.java │ │ │ │ │ ├── storage/ │ │ │ │ │ │ ├── FilePluginStatePersistenceImpl.java │ │ │ │ │ │ ├── PluginPersistenceException.java │ │ │ │ │ │ └── PluginStatePersistenceService.java │ │ │ │ │ └── sync/ │ │ │ │ │ ├── PluginStateApplier.java │ │ │ │ │ ├── PluginStateSynchronizer.java │ │ │ │ │ ├── RaftPluginStateSynchronizer.java │ │ │ │ │ └── StandalonePluginStateSynchronizer.java │ │ │ │ ├── remote/ │ │ │ │ │ ├── AbstractRequestFilter.java │ │ │ │ │ ├── BaseRpcServer.java │ │ │ │ │ ├── ClientConnectionEventListener.java │ │ │ │ │ ├── ClientConnectionEventListenerRegistry.java │ │ │ │ │ ├── Connection.java │ │ │ │ │ ├── ConnectionManager.java │ │ │ │ │ ├── ConnectionMeta.java │ │ │ │ │ ├── HealthCheckRequestHandler.java │ │ │ │ │ ├── LongConnectionMetricsCollector.java │ │ │ │ │ ├── NacosRuntimeConnectionEjector.java │ │ │ │ │ ├── RequestFilters.java │ │ │ │ │ ├── RequestHandler.java │ │ │ │ │ ├── RequestHandlerRegistry.java │ │ │ │ │ ├── RpcAckCallbackSynchronizer.java │ │ │ │ │ ├── RpcPushService.java │ │ │ │ │ ├── RuntimeConnectionEjector.java │ │ │ │ │ ├── core/ │ │ │ │ │ │ ├── RpcAckCallbackInitorOrCleaner.java │ │ │ │ │ │ ├── ServerLoaderInfoRequestHandler.java │ │ │ │ │ │ └── ServerReloaderRequestHandler.java │ │ │ │ │ ├── event/ │ │ │ │ │ │ └── RemotingHeartBeatEvent.java │ │ │ │ │ ├── grpc/ │ │ │ │ │ │ ├── AddressTransportFilter.java │ │ │ │ │ │ ├── BaseGrpcServer.java │ │ │ │ │ │ ├── ConnectionGeneratorService.java │ │ │ │ │ │ ├── ConnectionGeneratorServiceDelegate.java │ │ │ │ │ │ ├── ConnectionGeneratorServiceImpl.java │ │ │ │ │ │ ├── GrpcBiStreamRequestAcceptor.java │ │ │ │ │ │ ├── GrpcClusterServer.java │ │ │ │ │ │ ├── GrpcConnection.java │ │ │ │ │ │ ├── GrpcConnectionInterceptor.java │ │ │ │ │ │ ├── GrpcRequestAcceptor.java │ │ │ │ │ │ ├── GrpcSdkServer.java │ │ │ │ │ │ ├── GrpcServerConstants.java │ │ │ │ │ │ ├── InvokeSource.java │ │ │ │ │ │ ├── PushAckIdGenerator.java │ │ │ │ │ │ ├── RemoteParamCheckFilter.java │ │ │ │ │ │ ├── filter/ │ │ │ │ │ │ │ ├── NacosGrpcServerTransportFilter.java │ │ │ │ │ │ │ └── NacosGrpcServerTransportFilterServiceLoader.java │ │ │ │ │ │ ├── interceptor/ │ │ │ │ │ │ │ ├── NacosGrpcServerInterceptor.java │ │ │ │ │ │ │ └── NacosGrpcServerInterceptorServiceLoader.java │ │ │ │ │ │ └── negotiator/ │ │ │ │ │ │ ├── AbstractProtocolNegotiatorBuilderSingleton.java │ │ │ │ │ │ ├── ClusterProtocolNegotiatorBuilderSingleton.java │ │ │ │ │ │ ├── NacosGrpcProtocolNegotiator.java │ │ │ │ │ │ ├── ProtocolNegotiatorBuilder.java │ │ │ │ │ │ ├── SdkProtocolNegotiatorBuilderSingleton.java │ │ │ │ │ │ └── tls/ │ │ │ │ │ │ ├── ClusterDefaultTlsProtocolNegotiatorBuilder.java │ │ │ │ │ │ ├── DefaultTlsContextBuilder.java │ │ │ │ │ │ ├── OptionalTlsProtocolNegotiator.java │ │ │ │ │ │ └── SdkDefaultTlsProtocolNegotiatorBuilder.java │ │ │ │ │ └── tls/ │ │ │ │ │ ├── RpcServerSslContextRefresher.java │ │ │ │ │ ├── RpcServerSslContextRefresherHolder.java │ │ │ │ │ ├── RpcServerTlsConfig.java │ │ │ │ │ ├── RpcServerTlsConfigFactory.java │ │ │ │ │ └── SslContextChangeAware.java │ │ │ │ ├── service/ │ │ │ │ │ ├── NacosClusterOperationService.java │ │ │ │ │ ├── NacosServerLoaderService.java │ │ │ │ │ ├── NacosServerStateService.java │ │ │ │ │ └── NamespaceOperationService.java │ │ │ │ ├── trace/ │ │ │ │ │ └── NacosCombinedTraceSubscriber.java │ │ │ │ ├── utils/ │ │ │ │ │ ├── ClassUtils.java │ │ │ │ │ ├── Commons.java │ │ │ │ │ ├── GenericType.java │ │ │ │ │ ├── GlobalExecutor.java │ │ │ │ │ ├── Loggers.java │ │ │ │ │ ├── OverrideParameterRequestWrapper.java │ │ │ │ │ ├── PageUtil.java │ │ │ │ │ ├── RemoteUtils.java │ │ │ │ │ ├── ReuseHttpRequest.java │ │ │ │ │ ├── ReuseHttpServletRequest.java │ │ │ │ │ ├── ReuseUploadFileHttpServletRequest.java │ │ │ │ │ ├── StringPool.java │ │ │ │ │ └── WebUtils.java │ │ │ │ └── web/ │ │ │ │ ├── FormSizeFilter.java │ │ │ │ ├── NacosCoreWebConfiguration.java │ │ │ │ ├── NacosWebBean.java │ │ │ │ └── NacosWebServerListener.java │ │ │ └── io/ │ │ │ └── grpc/ │ │ │ └── netty/ │ │ │ └── shaded/ │ │ │ └── io/ │ │ │ └── grpc/ │ │ │ └── netty/ │ │ │ └── NettyChannelHelper.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ ├── logback/ │ │ │ │ └── nacos.xml │ │ │ ├── services/ │ │ │ │ ├── com.alibaba.nacos.api.remote.Payload │ │ │ │ ├── com.alibaba.nacos.auth.config.NacosAuthConfig │ │ │ │ ├── com.alibaba.nacos.common.ability.AbstractAbilityControlManager │ │ │ │ ├── com.alibaba.nacos.core.ability.ServerAbilityInitializer │ │ │ │ ├── com.alibaba.nacos.core.listener.NacosApplicationListener │ │ │ │ ├── com.alibaba.nacos.core.listener.startup.NacosStartUp │ │ │ │ ├── com.alibaba.nacos.core.paramcheck.AbstractRpcParamExtractor │ │ │ │ ├── com.alibaba.nacos.core.remote.grpc.ConnectionGeneratorService │ │ │ │ ├── com.alibaba.nacos.core.remote.grpc.negotiator.ProtocolNegotiatorBuilder │ │ │ │ ├── com.alibaba.nacos.plugin.control.configs.ControlConfigsInitializer │ │ │ │ ├── com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector │ │ │ │ └── com.alibaba.nacos.sys.module.ModuleStateBuilder │ │ │ └── spring.factories │ │ └── core-banner.txt │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── core/ │ │ ├── CoreUtApplication.java │ │ ├── ability/ │ │ │ ├── AbilityControlManagerTest.java │ │ │ ├── RemoteAbilityInitializerTest.java │ │ │ ├── ServerAbilityInitializerHolderTest.java │ │ │ ├── TestServerAbilityControlManager.java │ │ │ └── config/ │ │ │ ├── AbilityConfigsTest.java │ │ │ └── TestAbilityConfig.java │ │ ├── auth/ │ │ │ ├── AuthAdminFilterTest.java │ │ │ ├── AuthConfigTest.java │ │ │ ├── AuthFilterTest.java │ │ │ ├── AuthModuleStateBuilderTest.java │ │ │ ├── InnerApiAuthEnabledTest.java │ │ │ ├── NacosServerAdminAuthConfigTest.java │ │ │ ├── NacosServerAuthConfigTest.java │ │ │ └── RemoteRequestAuthFilterTest.java │ │ ├── cluster/ │ │ │ ├── MemberChangeListenerTest.java │ │ │ ├── MemberLookupTest.java │ │ │ ├── MemberMetaDataConstantsTest.java │ │ │ ├── MemberTest.java │ │ │ ├── MemberUtilTest.java │ │ │ ├── MembersChangeEventTest.java │ │ │ ├── ServerMemberManagerTest.java │ │ │ ├── TaskTest.java │ │ │ ├── health/ │ │ │ │ ├── ModuleHealthCheckerHolderTest.java │ │ │ │ └── ReadinessResultTest.java │ │ │ ├── lookup/ │ │ │ │ ├── AddressServerMemberLookupTest.java │ │ │ │ ├── FileConfigMemberLookupTest.java │ │ │ │ └── LookupFactoryTest.java │ │ │ └── remote/ │ │ │ ├── ClusterRpcClientProxyTest.java │ │ │ ├── MemberReportHandlerTest.java │ │ │ ├── request/ │ │ │ │ ├── AbstractClusterRequestTest.java │ │ │ │ ├── MemberReportRequestTest.java │ │ │ │ ├── PluginAvailabilityRequestHandlerTest.java │ │ │ │ └── PluginAvailabilityRequestTest.java │ │ │ └── response/ │ │ │ ├── MemberReportResponseTest.java │ │ │ └── PluginAvailabilityResponseTest.java │ │ ├── code/ │ │ │ ├── ControllerMethodsCacheTest.java │ │ │ ├── RequestMappingInfoTest.java │ │ │ ├── SpringApplicationRunListenerTest.java │ │ │ └── condition/ │ │ │ ├── ParamRequestConditionTest.java │ │ │ └── PathRequestConditionTest.java │ │ ├── config/ │ │ │ ├── DistroModuleStateBuilderTest.java │ │ │ ├── ModuleStateClusterTest.java │ │ │ ├── ModuleStateStandaloneTest.java │ │ │ └── RaftModuleStateBuilderTest.java │ │ ├── console/ │ │ │ ├── ConsolePathTipConfigTest.java │ │ │ └── NacosConsolePathTipFilterTest.java │ │ ├── context/ │ │ │ ├── RequestContextHolderTest.java │ │ │ ├── RequestContextTest.java │ │ │ ├── addition/ │ │ │ │ ├── AddressContextTest.java │ │ │ │ ├── AuthContextTest.java │ │ │ │ ├── BasicContextTest.java │ │ │ │ └── EngineContextTest.java │ │ │ └── remote/ │ │ │ ├── HttpRequestContextConfigTest.java │ │ │ └── HttpRequestContextFilterTest.java │ │ ├── control/ │ │ │ ├── SpringValueConfigsInitializerTest.java │ │ │ ├── TpsControlConfigTest.java │ │ │ ├── http/ │ │ │ │ ├── HttpTpsCheckRequestParserRegistryTest.java │ │ │ │ ├── HttpTpsPointRegistryTest.java │ │ │ │ ├── NacosHttpTpsControlRegistrationTest.java │ │ │ │ └── NacosHttpTpsFilterTest.java │ │ │ └── remote/ │ │ │ ├── RemoteTpsCheckRequestParserRegistryTest.java │ │ │ └── TpsControlRequestFilterTest.java │ │ ├── controller/ │ │ │ └── v3/ │ │ │ ├── CoreOpsControllerV3Test.java │ │ │ ├── NacosClusterControllerV3Test.java │ │ │ ├── NamespaceControllerV3Test.java │ │ │ ├── PluginControllerV3Test.java │ │ │ ├── ServerLoaderControllerV3Test.java │ │ │ └── ServerStateControllerTest.java │ │ ├── distributed/ │ │ │ ├── AbstractConsistencyProtocolTest.java │ │ │ ├── ConsistencyConfigurationTest.java │ │ │ ├── ProtocolExecutorTest.java │ │ │ ├── ProtocolManagerTest.java │ │ │ ├── distro/ │ │ │ │ ├── DistroConfigTest.java │ │ │ │ ├── DistroConstantsTest.java │ │ │ │ ├── DistroProtocolTest.java │ │ │ │ ├── component/ │ │ │ │ │ └── DistroComponentHolderTest.java │ │ │ │ ├── entity/ │ │ │ │ │ ├── DistroDataTest.java │ │ │ │ │ └── DistroKeyTest.java │ │ │ │ ├── exception/ │ │ │ │ │ └── DistroExceptionTest.java │ │ │ │ ├── monitor/ │ │ │ │ │ ├── DistroRecordTest.java │ │ │ │ │ └── DistroRecordsHolderTest.java │ │ │ │ └── task/ │ │ │ │ ├── DistroTaskEngineHolderTest.java │ │ │ │ ├── delay/ │ │ │ │ │ ├── DistroDelayTaskExecuteEngineTest.java │ │ │ │ │ ├── DistroDelayTaskProcessorTest.java │ │ │ │ │ └── DistroDelayTaskTest.java │ │ │ │ ├── execute/ │ │ │ │ │ ├── DistroSyncChangeTaskTest.java │ │ │ │ │ └── DistroSyncDeleteTaskTest.java │ │ │ │ ├── load/ │ │ │ │ │ └── DistroLoadDataTaskTest.java │ │ │ │ └── verify/ │ │ │ │ ├── DistroVerifyExecuteTaskTest.java │ │ │ │ └── DistroVerifyTimedTaskTest.java │ │ │ ├── id/ │ │ │ │ ├── IdGeneratorManagerTest.java │ │ │ │ ├── SnowFlowerInstanceIdGeneratorTest.java │ │ │ │ └── TestIdGenerator.java │ │ │ └── raft/ │ │ │ ├── JRaftMaintainServiceTest.java │ │ │ ├── JRaftProtocolTest.java │ │ │ ├── JRaftServerTest.java │ │ │ ├── JSnapshotOperationTest.java │ │ │ ├── NacosClosureTest.java │ │ │ ├── NacosStateMachineTest.java │ │ │ ├── RaftConfigTest.java │ │ │ ├── RaftErrorEventTest.java │ │ │ ├── RaftEventTest.java │ │ │ ├── RaftSysConstantsTest.java │ │ │ ├── exception/ │ │ │ │ ├── DuplicateRaftGroupExceptionTest.java │ │ │ │ ├── JRaftExceptionTest.java │ │ │ │ ├── NoLeaderExceptionTest.java │ │ │ │ └── NoSuchRaftGroupExceptionTest.java │ │ │ ├── processor/ │ │ │ │ ├── AbstractProcessorTest.java │ │ │ │ └── NacosReadRequestProcessorTest.java │ │ │ └── utils/ │ │ │ ├── FailoverClosureImplTest.java │ │ │ ├── JRaftConstantsTest.java │ │ │ ├── JRaftLogOperationTest.java │ │ │ ├── JRaftOpsExecuteTest.java │ │ │ ├── JRaftOpsTest.java │ │ │ ├── JRaftUtilsTest.java │ │ │ ├── RaftExecutorTest.java │ │ │ ├── RaftOptionsBuilderTest.java │ │ │ └── RetryRunnerTest.java │ │ ├── exception/ │ │ │ ├── ErrorCodeTest.java │ │ │ ├── KvStorageExceptionTest.java │ │ │ └── NacosApiExceptionHandlerTest.java │ │ ├── listener/ │ │ │ ├── LoggingApplicationListenerTest.java │ │ │ ├── NacosApplicationListenerTest.java │ │ │ ├── StandaloneProfileApplicationListenerTest.java │ │ │ ├── StartingApplicationListenerTest.java │ │ │ └── startup/ │ │ │ ├── AbstractNacosStartUpTest.java │ │ │ ├── NacosCoreStartUpTest.java │ │ │ ├── NacosStartUpManagerTest.java │ │ │ └── NacosWebStartUpTest.java │ │ ├── mock/ │ │ │ └── MockAuthPluginServiceB.java │ │ ├── model/ │ │ │ ├── form/ │ │ │ │ ├── AggregationFormTest.java │ │ │ │ ├── PageFormTest.java │ │ │ │ └── v3/ │ │ │ │ └── RaftCommandFormTest.java │ │ │ └── request/ │ │ │ ├── LogUpdateRequestTest.java │ │ │ └── LookupUpdateRequestTest.java │ │ ├── monitor/ │ │ │ ├── GrpcServerThreadPoolMonitorTest.java │ │ │ ├── MetricsMonitorTest.java │ │ │ ├── NacosMeterRegistryCenterTest.java │ │ │ └── topn/ │ │ │ ├── FixedSizePriorityQueueTest.java │ │ │ ├── StringTopNCounterTest.java │ │ │ └── TopNConfigTest.java │ │ ├── namespace/ │ │ │ ├── filter/ │ │ │ │ ├── NamespaceValidationConfigTest.java │ │ │ │ └── NamespaceValidationRequestFilterTest.java │ │ │ ├── model/ │ │ │ │ ├── NamespaceTypeEnumTest.java │ │ │ │ └── form/ │ │ │ │ ├── CreateNamespaceFormTest.java │ │ │ │ └── NamespaceFormTest.java │ │ │ └── repository/ │ │ │ ├── EmbeddedNamespacePersistServiceTest.java │ │ │ ├── ExternalNamespacePersistServiceTest.java │ │ │ └── NamespaceRowMapperInjectorTest.java │ │ ├── paramcheck/ │ │ │ ├── CheckConfigurationTest.java │ │ │ ├── ExtractorManagerTest.java │ │ │ ├── ParamCheckerFilterTest.java │ │ │ ├── ParamExtractorTest.java │ │ │ ├── ServerParamCheckConfigTest.java │ │ │ └── impl/ │ │ │ ├── AgentRequestParamExtractorTest.java │ │ │ ├── BatchInstanceRequestParamExtractorTest.java │ │ │ ├── ConfigBatchListenRequestParamExtractorTest.java │ │ │ ├── ConfigFuzzyWatchRequestParamsExtractorTest.java │ │ │ ├── ConfigRequestParamExtractorTest.java │ │ │ ├── InstanceRequestParamExtractorTest.java │ │ │ ├── McpServerRequestParamExtractorTest.java │ │ │ ├── PersistentInstanceRequestParamExtractorTest.java │ │ │ ├── PromptRequestParamExtractorTest.java │ │ │ ├── ServiceListRequestParamExtractorTest.java │ │ │ ├── ServiceQueryRequestParamExtractorTest.java │ │ │ └── SubscribeServiceRequestParamExtractorTest.java │ │ ├── persistence/ │ │ │ ├── DerbySnapshotOperationTest.java │ │ │ └── DistributedDatabaseOperateImplTest.java │ │ ├── plugin/ │ │ │ ├── CriticalPluginConfigTest.java │ │ │ ├── PluginClusterSyncIntegrationTest.java │ │ │ ├── PluginManagerTest.java │ │ │ ├── PluginStateProcessorTest.java │ │ │ ├── PluginStateSnapshotOperationTest.java │ │ │ ├── condition/ │ │ │ │ ├── ConditionOnClusterModeTest.java │ │ │ │ └── ConditionOnStandaloneModeTest.java │ │ │ ├── model/ │ │ │ │ ├── PluginInfoTest.java │ │ │ │ ├── PluginStateOperationTest.java │ │ │ │ ├── PluginStateSnapshotTest.java │ │ │ │ ├── form/ │ │ │ │ │ ├── PluginConfigFormTest.java │ │ │ │ │ └── PluginStatusFormTest.java │ │ │ │ └── vo/ │ │ │ │ ├── PluginDetailVOTest.java │ │ │ │ └── PluginInfoVOTest.java │ │ │ ├── storage/ │ │ │ │ ├── FilePluginStatePersistenceImplTest.java │ │ │ │ ├── PluginPersistenceExceptionTest.java │ │ │ │ └── PluginStatePersistenceTest.java │ │ │ └── sync/ │ │ │ ├── RaftPluginStateSynchronizerTest.java │ │ │ └── StandalonePluginStateSynchronizerTest.java │ │ ├── remote/ │ │ │ ├── BaseRpcServerTest.java │ │ │ ├── ClientConnectionEventListenerRegistryTest.java │ │ │ ├── ClientConnectionEventListenerTest.java │ │ │ ├── ConnectionManagerTest.java │ │ │ ├── ConnectionMetaTest.java │ │ │ ├── ConnectionTest.java │ │ │ ├── HealthCheckRequestHandlerTest.java │ │ │ ├── LongConnectionMetricsCollectorTest.java │ │ │ ├── NacosRuntimeConnectionEjectorTest.java │ │ │ ├── RequestFiltersTest.java │ │ │ ├── RequestHandlerRegistryTest.java │ │ │ ├── RpcAckCallbackSynchronizerTest.java │ │ │ ├── RpcPushServiceTest.java │ │ │ ├── RuntimeConnectionEjectorTest.java │ │ │ ├── core/ │ │ │ │ ├── RpcAckCallbackInitorOrCleanerTest.java │ │ │ │ ├── ServerLoaderInfoRequestHandlerTest.java │ │ │ │ └── ServerReloaderRequestHandlerTest.java │ │ │ ├── event/ │ │ │ │ └── RemotingHeartBeatEventTest.java │ │ │ ├── grpc/ │ │ │ │ ├── AddressTransportFilterTest.java │ │ │ │ ├── ConnectionGeneratorServiceDelegateTest.java │ │ │ │ ├── ConnectionGeneratorServiceImplTest.java │ │ │ │ ├── GrpcBiStreamRequestAcceptorTest.java │ │ │ │ ├── GrpcConnectionInterceptorTest.java │ │ │ │ ├── GrpcConnectionTest.java │ │ │ │ ├── GrpcRequestAcceptorTest.java │ │ │ │ ├── GrpcServerConstantsTest.java │ │ │ │ ├── GrpcServerTest.java │ │ │ │ ├── PushAckIdGeneratorTest.java │ │ │ │ ├── RemoteParamCheckFilterTest.java │ │ │ │ ├── filter/ │ │ │ │ │ ├── NacosGrpcServerTransportFilterServiceLoaderTest.java │ │ │ │ │ └── NacosGrpcServerTransportFilterTest.java │ │ │ │ ├── interceptor/ │ │ │ │ │ ├── NacosGrpcServerInterceptorServiceLoaderTest.java │ │ │ │ │ └── NacosGrpcServerInterceptorTest.java │ │ │ │ └── negotiator/ │ │ │ │ ├── ClusterProtocolNegotiatorBuilderSingletonTest.java │ │ │ │ ├── SdkProtocolNegotiatorBuilderSingletonTest.java │ │ │ │ └── tls/ │ │ │ │ ├── ClusterDefaultTlsProtocolNegotiatorBuilderTest.java │ │ │ │ ├── OptionalTlsProtocolNegotiatorTest.java │ │ │ │ ├── RpcServerSslContextRefresherHolderTest.java │ │ │ │ ├── SdkDefaultTlsContextBuilderTest.java │ │ │ │ └── SdkDefaultTlsProtocolNegotiatorBuilderTest.java │ │ │ └── tls/ │ │ │ ├── RpcClusterServerSslContextRefresherTest.java │ │ │ ├── RpcSdkServerSslContextRefresherTest.java │ │ │ └── RpcServerSslContextRefresherHolderTest.java │ │ ├── service/ │ │ │ ├── NacosClusterOperationServiceTest.java │ │ │ ├── NacosServerLoaderServiceTest.java │ │ │ ├── NacosServerStateServiceTest.java │ │ │ └── NamespaceOperationServiceTest.java │ │ ├── trace/ │ │ │ └── NacosCombinedTraceSubscriberTest.java │ │ ├── utils/ │ │ │ ├── ClassUtilsTest.java │ │ │ ├── CommonsTest.java │ │ │ ├── GenericTypeTest.java │ │ │ ├── GlobalExecutorTest.java │ │ │ ├── LoggersTest.java │ │ │ ├── OverrideParameterRequestWrapperTest.java │ │ │ ├── PageUtilTest.java │ │ │ ├── RemoteUtilsTest.java │ │ │ ├── ReuseHttpServletRequestTest.java │ │ │ ├── ReuseUploadFileHttpServletRequestTest.java │ │ │ ├── StringPoolTest.java │ │ │ ├── SystemUtilsTest.java │ │ │ └── WebUtilsTest.java │ │ └── web/ │ │ ├── FormSizeFilterTest.java │ │ ├── NacosCoreWebConfigurationTest.java │ │ └── NacosWebServerListenerTest.java │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ ├── com.alibaba.nacos.consistency.IdGenerator │ │ ├── com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor │ │ ├── com.alibaba.nacos.core.remote.tls.RpcServerSslContextRefresher │ │ └── com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService │ ├── application.properties │ ├── logback-test.xml │ ├── test-ca-cert.pem │ ├── test-server-cert.pem │ └── test-server-key.pem ├── distribution/ │ ├── LICENSE-BIN │ ├── NOTICE-BIN │ ├── bin/ │ │ ├── shutdown.cmd │ │ ├── shutdown.sh │ │ ├── startup-native.sh │ │ ├── startup.cmd │ │ └── startup.sh │ ├── conf/ │ │ ├── announcement_en-US.conf │ │ ├── announcement_zh-CN.conf │ │ ├── application.properties │ │ ├── cluster.conf.example │ │ ├── console-guide.conf │ │ └── nacos-logback.xml │ ├── pom.xml │ ├── release-address.xml │ ├── release-client.xml │ ├── release-nacos.xml │ └── release-native.xml ├── example/ │ ├── README.md │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── example/ │ │ ├── App.java │ │ ├── ConfigExample.java │ │ ├── ConfigFuzzyWatchExample.java │ │ ├── NamingExample.java │ │ └── NamingFuzzyWatchExample.java │ └── resources/ │ └── logback.xml ├── istio/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── istio/ │ │ │ ├── IstioApp.java │ │ │ ├── api/ │ │ │ │ ├── ApiConstants.java │ │ │ │ ├── ApiGenerator.java │ │ │ │ └── ApiGeneratorFactory.java │ │ │ ├── common/ │ │ │ │ ├── AbstractConnection.java │ │ │ │ ├── Debounce.java │ │ │ │ ├── EventProcessor.java │ │ │ │ ├── IstioConfigProcessor.java │ │ │ │ ├── NacosResourceManager.java │ │ │ │ ├── NacosServiceInfoResourceWatcher.java │ │ │ │ ├── ResourceSnapshot.java │ │ │ │ └── WatchedStatus.java │ │ │ ├── config/ │ │ │ │ └── IstioEnabledFilter.java │ │ │ ├── mcp/ │ │ │ │ ├── EmptyMcpGenerator.java │ │ │ │ ├── McpConnection.java │ │ │ │ ├── NacosMcpService.java │ │ │ │ └── ServiceEntryMcpGenerator.java │ │ │ ├── misc/ │ │ │ │ ├── IstioConfig.java │ │ │ │ └── Loggers.java │ │ │ ├── model/ │ │ │ │ ├── DestinationRule.java │ │ │ │ ├── IstioEndpoint.java │ │ │ │ ├── IstioResources.java │ │ │ │ ├── IstioService.java │ │ │ │ ├── PushRequest.java │ │ │ │ ├── ServiceEntryWrapper.java │ │ │ │ └── VirtualService.java │ │ │ ├── server/ │ │ │ │ ├── IstioServer.java │ │ │ │ └── ServerInterceptor.java │ │ │ ├── util/ │ │ │ │ ├── IstioCrdUtil.java │ │ │ │ ├── IstioExecutor.java │ │ │ │ └── NonceGenerator.java │ │ │ └── xds/ │ │ │ ├── CdsGenerator.java │ │ │ ├── DeltaConnection.java │ │ │ ├── EdsGenerator.java │ │ │ ├── EmptyXdsGenerator.java │ │ │ ├── LdsGenerator.java │ │ │ ├── NacosXdsService.java │ │ │ ├── RdsGenerator.java │ │ │ ├── ServiceEntryXdsGenerator.java │ │ │ └── XdsConnection.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ │ └── proto/ │ │ ├── gogoproto/ │ │ │ └── gogo.proto │ │ ├── google/ │ │ │ └── protobuf/ │ │ │ └── any.proto │ │ ├── mcp/ │ │ │ ├── Readme.md │ │ │ └── v1alpha1/ │ │ │ ├── mcp.proto │ │ │ ├── metadata.proto │ │ │ └── resource.proto │ │ └── networking/ │ │ └── v1alpha3/ │ │ ├── destination_rule.proto │ │ ├── envoy_filter.proto │ │ ├── gateway.proto │ │ ├── service_entry.proto │ │ ├── sidecar.proto │ │ ├── virtual_service.proto │ │ ├── workload_entry.proto │ │ └── workload_group.proto │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── istio/ │ └── config/ │ └── IstioEnabledFilterTest.java ├── k8s-sync/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── k8s/ │ │ │ └── sync/ │ │ │ ├── K8sSyncConfig.java │ │ │ ├── K8sSyncEnabledFilter.java │ │ │ ├── K8sSyncServer.java │ │ │ └── Loggers.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── k8s/ │ └── sync/ │ └── K8sSyncEnabledFilterTest.java ├── lock/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── lock/ │ │ │ ├── LockManager.java │ │ │ ├── NacosLockManager.java │ │ │ ├── aspect/ │ │ │ │ └── RequestLockAspect.java │ │ │ ├── constant/ │ │ │ │ ├── Constants.java │ │ │ │ └── PropertiesConstant.java │ │ │ ├── core/ │ │ │ │ └── reentrant/ │ │ │ │ ├── AbstractAtomicLock.java │ │ │ │ ├── AtomicLockService.java │ │ │ │ └── mutex/ │ │ │ │ └── MutexAtomicLock.java │ │ │ ├── exception/ │ │ │ │ └── NacosLockException.java │ │ │ ├── factory/ │ │ │ │ ├── LockFactory.java │ │ │ │ └── SimpleLockFactory.java │ │ │ ├── model/ │ │ │ │ ├── LockInfo.java │ │ │ │ └── LockKey.java │ │ │ ├── monitor/ │ │ │ │ ├── LockMemoryMonitor.java │ │ │ │ └── LockMetricsMonitor.java │ │ │ ├── persistence/ │ │ │ │ └── NacosLockSnapshotOperation.java │ │ │ ├── raft/ │ │ │ │ └── request/ │ │ │ │ └── MutexLockRequest.java │ │ │ ├── remote/ │ │ │ │ └── rpc/ │ │ │ │ └── handler/ │ │ │ │ └── LockRequestHandler.java │ │ │ └── service/ │ │ │ ├── LockOperationService.java │ │ │ └── impl/ │ │ │ └── LockOperationServiceImpl.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.lock.factory.LockFactory │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── lock/ │ │ ├── LockManagerTest.java │ │ ├── core/ │ │ │ └── reentrant/ │ │ │ └── mutex/ │ │ │ ├── ClientAtomicLock.java │ │ │ └── MutexAtomicLockTest.java │ │ ├── factory/ │ │ │ └── ClientLockFactory.java │ │ ├── remote/ │ │ │ └── rpc/ │ │ │ └── handler/ │ │ │ └── LockRequestHandlerTest.java │ │ └── service/ │ │ └── impl/ │ │ └── LockOperationServiceImplTest.java │ └── resources/ │ └── META-INF/ │ └── services/ │ └── com.alibaba.nacos.lock.factory.LockFactory ├── logger-adapter-impl/ │ ├── log4j2-adapter/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── logger/ │ │ │ │ └── adapter/ │ │ │ │ └── log4j2/ │ │ │ │ ├── Log4J2NacosLoggingAdapter.java │ │ │ │ ├── Log4j2NacosLoggingAdapterBuilder.java │ │ │ │ ├── Log4j2NacosLoggingPropertiesHolder.java │ │ │ │ ├── NacosClientPropertiesLookup.java │ │ │ │ └── NacosLog4j2Configurator.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── services/ │ │ │ │ └── com.alibaba.nacos.common.logging.NacosLoggingAdapterBuilder │ │ │ └── nacos-log4j2.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── logger/ │ │ └── adapter/ │ │ └── log4j2/ │ │ ├── Log4J2NacosLoggingAdapterTest.java │ │ ├── Log4j2NacosLoggingAdapterBuilderTest.java │ │ └── NacosClientPropertiesLookupTest.java │ ├── logback-adapter-12/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── logger/ │ │ │ │ └── adapter/ │ │ │ │ └── logback12/ │ │ │ │ ├── LogbackNacosLoggingAdapter.java │ │ │ │ ├── LogbackNacosLoggingAdapterBuilder.java │ │ │ │ ├── NacosClientPropertyAction.java │ │ │ │ └── NacosLogbackConfiguratorAdapterV1.java │ │ │ └── resources/ │ │ │ ├── META-INF/ │ │ │ │ └── services/ │ │ │ │ ├── com.alibaba.nacos.common.logging.NacosLoggingAdapter │ │ │ │ └── com.alibaba.nacos.common.logging.NacosLoggingAdapterBuilder │ │ │ └── nacos-logback12.xml │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── logger/ │ │ └── adapter/ │ │ └── logback12/ │ │ ├── LogbackNacosLoggingAdapterBuilderTest.java │ │ ├── LogbackNacosLoggingAdapterTest.java │ │ ├── NacosClientPropertyActionTest.java │ │ └── NacosLogbackConfiguratorAdapterV1Test.java │ └── pom.xml ├── maintainer-client/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── maintainer/ │ │ └── client/ │ │ ├── NacosMaintainerFactory.java │ │ ├── address/ │ │ │ └── DefaultServerListManager.java │ │ ├── ai/ │ │ │ ├── A2aMaintainerService.java │ │ │ ├── AiMaintainerFactory.java │ │ │ ├── AiMaintainerService.java │ │ │ ├── McpMaintainerService.java │ │ │ ├── NacosAiMaintainerServiceImpl.java │ │ │ ├── PromptMaintainerService.java │ │ │ └── SkillMaintainerService.java │ │ ├── config/ │ │ │ ├── BetaConfigMaintainerService.java │ │ │ ├── ConfigHistoryMaintainerService.java │ │ │ ├── ConfigMaintainerFactory.java │ │ │ ├── ConfigMaintainerService.java │ │ │ ├── ConfigOpsMaintainerService.java │ │ │ └── NacosConfigMaintainerServiceImpl.java │ │ ├── constants/ │ │ │ └── Constants.java │ │ ├── core/ │ │ │ ├── AbstractCoreMaintainerService.java │ │ │ └── CoreMaintainerService.java │ │ ├── model/ │ │ │ └── HttpRequest.java │ │ ├── naming/ │ │ │ ├── InstanceMaintainerService.java │ │ │ ├── NacosNamingMaintainerServiceImpl.java │ │ │ ├── NamingClientMaintainerService.java │ │ │ ├── NamingMaintainerFactory.java │ │ │ ├── NamingMaintainerService.java │ │ │ └── ServiceMaintainerService.java │ │ ├── remote/ │ │ │ ├── ClientHttpProxy.java │ │ │ └── HttpClientManager.java │ │ └── utils/ │ │ ├── ParamUtil.java │ │ └── RequestUtil.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── maintainer/ │ │ └── client/ │ │ ├── NacosMaintainerFactoryTest.java │ │ ├── address/ │ │ │ └── DefaultServerListManagerTest.java │ │ ├── ai/ │ │ │ ├── AiMaintainerFactoryTest.java │ │ │ └── NacosAiMaintainerServiceImplTest.java │ │ ├── config/ │ │ │ ├── ConfigMaintainerFactoryTest.java │ │ │ └── NacosConfigMaintainerServiceImplTest.java │ │ ├── core/ │ │ │ └── AbstractCoreMaintainerServiceTest.java │ │ ├── model/ │ │ │ └── HttpRequestTest.java │ │ ├── naming/ │ │ │ ├── NacosNamingMaintainerServiceImplTest.java │ │ │ └── NamingMaintainerFactoryTest.java │ │ ├── remote/ │ │ │ ├── ClientHttpProxyTest.java │ │ │ ├── HttpClientManagerTest.java │ │ │ └── mock/ │ │ │ └── MockServerListProvider.java │ │ └── utils/ │ │ └── ParamUtilTest.java │ └── resources/ │ └── META-INF/ │ └── services/ │ └── com.alibaba.nacos.client.address.ServerListProvider ├── mcp-registry-adaptor/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── mcpregistry/ │ │ │ ├── NacosMcpRegistry.java │ │ │ ├── NacosMcpRegistryStartUp.java │ │ │ ├── config/ │ │ │ │ ├── HttpPathConfiguration.java │ │ │ │ └── McpRegistryPackageExcludeFilter.java │ │ │ ├── controller/ │ │ │ │ └── McpRegistryController.java │ │ │ ├── form/ │ │ │ │ ├── GetServerForm.java │ │ │ │ ├── ListServerForm.java │ │ │ │ ├── ListServersNacosForm.java │ │ │ │ └── ListServersOfficialForm.java │ │ │ └── service/ │ │ │ └── NacosMcpRegistryService.java │ │ └── resources/ │ │ ├── META-INF/ │ │ │ └── services/ │ │ │ ├── com.alibaba.nacos.core.listener.startup.NacosStartUp │ │ │ └── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ │ ├── nacos-mcp-registry-banner.txt │ │ └── nacos-mcp-registry.properties │ └── test/ │ └── java/ │ └── com/ │ └── alibaba/ │ └── nacos/ │ └── mcpregistry/ │ ├── NacosMcpRegistryStartUpTest.java │ ├── controller/ │ │ └── McpRegistryControllerTest.java │ └── service/ │ └── NacosMcpRegistryServiceTest.java ├── mvnw ├── mvnw.cmd ├── naming/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── naming/ │ │ │ ├── NamingApp.java │ │ │ ├── ability/ │ │ │ │ └── NamingAbilityInitializer.java │ │ │ ├── cluster/ │ │ │ │ ├── NamingReadinessCheckService.java │ │ │ │ ├── ServerStatus.java │ │ │ │ ├── ServerStatusManager.java │ │ │ │ ├── remote/ │ │ │ │ │ ├── request/ │ │ │ │ │ │ ├── DistroDataRequest.java │ │ │ │ │ │ └── RequestRegistry.java │ │ │ │ │ └── response/ │ │ │ │ │ ├── DistroDataResponse.java │ │ │ │ │ └── ResponseRegistry.java │ │ │ │ └── transport/ │ │ │ │ ├── JacksonSerializer.java │ │ │ │ └── Serializer.java │ │ │ ├── config/ │ │ │ │ └── NamingEnabledFilter.java │ │ │ ├── consistency/ │ │ │ │ ├── Datum.java │ │ │ │ ├── KeyBuilder.java │ │ │ │ ├── ephemeral/ │ │ │ │ │ └── distro/ │ │ │ │ │ └── v2/ │ │ │ │ │ ├── DistroClientComponentRegistry.java │ │ │ │ │ ├── DistroClientDataProcessor.java │ │ │ │ │ ├── DistroClientTaskFailedHandler.java │ │ │ │ │ ├── DistroClientTransportAgent.java │ │ │ │ │ └── DistroClientVerifyInfo.java │ │ │ │ └── persistent/ │ │ │ │ └── impl/ │ │ │ │ ├── AbstractSnapshotOperation.java │ │ │ │ ├── BatchReadResponse.java │ │ │ │ ├── BatchWriteRequest.java │ │ │ │ └── OldDataOperation.java │ │ │ ├── constants/ │ │ │ │ ├── ClientConstants.java │ │ │ │ ├── Constants.java │ │ │ │ ├── FieldsConstants.java │ │ │ │ ├── PushConstants.java │ │ │ │ └── RequestConstant.java │ │ │ ├── controllers/ │ │ │ │ ├── OperatorMetricsV1Controller.java │ │ │ │ └── v3/ │ │ │ │ ├── ClientControllerV3.java │ │ │ │ ├── ClusterControllerV3.java │ │ │ │ ├── HealthControllerV3.java │ │ │ │ ├── InstanceControllerV3.java │ │ │ │ ├── InstanceOpenApiController.java │ │ │ │ ├── OperatorControllerV3.java │ │ │ │ └── ServiceControllerV3.java │ │ │ ├── core/ │ │ │ │ ├── CatalogService.java │ │ │ │ ├── CatalogServiceV2Impl.java │ │ │ │ ├── ClientService.java │ │ │ │ ├── ClientServiceImpl.java │ │ │ │ ├── ClusterOperator.java │ │ │ │ ├── ClusterOperatorV2Impl.java │ │ │ │ ├── DistroMapper.java │ │ │ │ ├── HealthOperator.java │ │ │ │ ├── HealthOperatorV2Impl.java │ │ │ │ ├── InstanceOperator.java │ │ │ │ ├── InstanceOperatorClientImpl.java │ │ │ │ ├── InstancePatchObject.java │ │ │ │ ├── Operator.java │ │ │ │ ├── OperatorV2Impl.java │ │ │ │ ├── ServiceOperator.java │ │ │ │ ├── ServiceOperatorV2Impl.java │ │ │ │ ├── SubscribeManager.java │ │ │ │ └── v2/ │ │ │ │ ├── ServiceManager.java │ │ │ │ ├── cleaner/ │ │ │ │ │ ├── AbstractNamingCleaner.java │ │ │ │ │ ├── EmptyServiceAutoCleanerV2.java │ │ │ │ │ ├── ExpiredMetadataCleaner.java │ │ │ │ │ └── NamingCleaner.java │ │ │ │ ├── client/ │ │ │ │ │ ├── AbstractClient.java │ │ │ │ │ ├── Client.java │ │ │ │ │ ├── ClientAttributes.java │ │ │ │ │ ├── ClientSyncData.java │ │ │ │ │ ├── ClientSyncDatumSnapshot.java │ │ │ │ │ ├── factory/ │ │ │ │ │ │ ├── ClientFactory.java │ │ │ │ │ │ ├── ClientFactoryHolder.java │ │ │ │ │ │ └── impl/ │ │ │ │ │ │ ├── ConnectionBasedClientFactory.java │ │ │ │ │ │ ├── EphemeralIpPortClientFactory.java │ │ │ │ │ │ └── PersistentIpPortClientFactory.java │ │ │ │ │ ├── impl/ │ │ │ │ │ │ ├── ConnectionBasedClient.java │ │ │ │ │ │ └── IpPortBasedClient.java │ │ │ │ │ └── manager/ │ │ │ │ │ ├── ClientManager.java │ │ │ │ │ ├── ClientManagerDelegate.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── ConnectionBasedClientManager.java │ │ │ │ │ ├── EphemeralIpPortClientManager.java │ │ │ │ │ └── PersistentIpPortClientManager.java │ │ │ │ ├── event/ │ │ │ │ │ ├── client/ │ │ │ │ │ │ ├── ClientEvent.java │ │ │ │ │ │ └── ClientOperationEvent.java │ │ │ │ │ ├── metadata/ │ │ │ │ │ │ ├── InfoChangeEvent.java │ │ │ │ │ │ └── MetadataEvent.java │ │ │ │ │ ├── publisher/ │ │ │ │ │ │ ├── NamingEventPublisher.java │ │ │ │ │ │ └── NamingEventPublisherFactory.java │ │ │ │ │ └── service/ │ │ │ │ │ └── ServiceEvent.java │ │ │ │ ├── index/ │ │ │ │ │ ├── ClientServiceIndexesManager.java │ │ │ │ │ ├── NamingFuzzyWatchContextService.java │ │ │ │ │ └── ServiceStorage.java │ │ │ │ ├── metadata/ │ │ │ │ │ ├── AbstractMetadataSnapshotOperation.java │ │ │ │ │ ├── ClusterMetadata.java │ │ │ │ │ ├── ExpiredMetadataInfo.java │ │ │ │ │ ├── InstanceMetadata.java │ │ │ │ │ ├── InstanceMetadataProcessor.java │ │ │ │ │ ├── InstanceMetadataSnapshotOperation.java │ │ │ │ │ ├── MetadataOperation.java │ │ │ │ │ ├── NamingMetadataManager.java │ │ │ │ │ ├── NamingMetadataOperateService.java │ │ │ │ │ ├── ServiceMetadata.java │ │ │ │ │ ├── ServiceMetadataProcessor.java │ │ │ │ │ └── ServiceMetadataSnapshotOperation.java │ │ │ │ ├── pojo/ │ │ │ │ │ ├── BatchInstanceData.java │ │ │ │ │ ├── BatchInstancePublishInfo.java │ │ │ │ │ ├── HealthCheckInstancePublishInfo.java │ │ │ │ │ ├── InstancePublishInfo.java │ │ │ │ │ └── Service.java │ │ │ │ └── service/ │ │ │ │ ├── ClientOperationService.java │ │ │ │ ├── ClientOperationServiceProxy.java │ │ │ │ └── impl/ │ │ │ │ ├── EphemeralClientOperationServiceImpl.java │ │ │ │ └── PersistentClientOperationServiceImpl.java │ │ │ ├── exception/ │ │ │ │ └── ResponseExceptionHandler.java │ │ │ ├── healthcheck/ │ │ │ │ ├── HealthCheckReactor.java │ │ │ │ ├── HealthCheckStatus.java │ │ │ │ ├── NacosHealthCheckTask.java │ │ │ │ ├── RsInfo.java │ │ │ │ ├── extend/ │ │ │ │ │ ├── AbstractHealthCheckProcessorExtend.java │ │ │ │ │ ├── HealthCheckExtendProvider.java │ │ │ │ │ └── HealthCheckProcessorExtendV2.java │ │ │ │ ├── heartbeat/ │ │ │ │ │ ├── AbstractBeatCheckInterceptor.java │ │ │ │ │ ├── BeatCheckTask.java │ │ │ │ │ ├── BeatProcessor.java │ │ │ │ │ ├── ClientBeatCheckTaskV2.java │ │ │ │ │ ├── ClientBeatProcessorV2.java │ │ │ │ │ ├── ClientBeatUpdateTask.java │ │ │ │ │ ├── ExpiredInstanceChecker.java │ │ │ │ │ ├── InstanceBeatCheckResponsibleInterceptor.java │ │ │ │ │ ├── InstanceBeatCheckTask.java │ │ │ │ │ ├── InstanceBeatCheckTaskInterceptorChain.java │ │ │ │ │ ├── InstanceBeatChecker.java │ │ │ │ │ ├── InstanceEnableBeatCheckInterceptor.java │ │ │ │ │ ├── ServiceEnableBeatCheckInterceptor.java │ │ │ │ │ └── UnhealthyInstanceChecker.java │ │ │ │ ├── interceptor/ │ │ │ │ │ ├── AbstractHealthCheckInterceptor.java │ │ │ │ │ ├── HealthCheckEnableInterceptor.java │ │ │ │ │ ├── HealthCheckInterceptorChain.java │ │ │ │ │ ├── HealthCheckResponsibleInterceptor.java │ │ │ │ │ └── HealthCheckTaskInterceptWrapper.java │ │ │ │ └── v2/ │ │ │ │ ├── HealthCheckTaskV2.java │ │ │ │ ├── HealthStatusSynchronizer.java │ │ │ │ ├── PersistentHealthStatusSynchronizer.java │ │ │ │ └── processor/ │ │ │ │ ├── HealthCheckCommonV2.java │ │ │ │ ├── HealthCheckProcessorV2.java │ │ │ │ ├── HealthCheckProcessorV2Delegate.java │ │ │ │ ├── HttpHealthCheckProcessor.java │ │ │ │ ├── MysqlHealthCheckProcessor.java │ │ │ │ ├── NoneHealthCheckProcessor.java │ │ │ │ └── TcpHealthCheckProcessor.java │ │ │ ├── interceptor/ │ │ │ │ ├── AbstractNamingInterceptorChain.java │ │ │ │ ├── Interceptable.java │ │ │ │ ├── NacosNamingInterceptor.java │ │ │ │ └── NacosNamingInterceptorChain.java │ │ │ ├── misc/ │ │ │ │ ├── ClientConfig.java │ │ │ │ ├── GlobalConfig.java │ │ │ │ ├── GlobalExecutor.java │ │ │ │ ├── GracefulShutdownListener.java │ │ │ │ ├── HttpClient.java │ │ │ │ ├── HttpClientManager.java │ │ │ │ ├── Loggers.java │ │ │ │ ├── NamingExecuteTaskDispatcher.java │ │ │ │ ├── NamingTraceEventInitializer.java │ │ │ │ ├── SwitchDomain.java │ │ │ │ ├── SwitchDomainSnapshotOperation.java │ │ │ │ ├── SwitchEntry.java │ │ │ │ ├── SwitchManager.java │ │ │ │ └── UtilsAndCommons.java │ │ │ ├── model/ │ │ │ │ ├── form/ │ │ │ │ │ ├── ClientServiceForm.java │ │ │ │ │ ├── InstanceForm.java │ │ │ │ │ ├── InstanceListForm.java │ │ │ │ │ ├── InstanceMetadataBatchOperationForm.java │ │ │ │ │ ├── ServiceForm.java │ │ │ │ │ ├── ServiceListForm.java │ │ │ │ │ ├── UpdateClusterForm.java │ │ │ │ │ ├── UpdateHealthForm.java │ │ │ │ │ └── UpdateSwitchForm.java │ │ │ │ └── vo/ │ │ │ │ ├── InstanceDetailInfoVo.java │ │ │ │ └── MetricsInfoVo.java │ │ │ ├── monitor/ │ │ │ │ ├── MetricsMonitor.java │ │ │ │ ├── NamingDynamicMeterRefreshService.java │ │ │ │ ├── NamingTpsMonitor.java │ │ │ │ ├── PerformanceLoggerThread.java │ │ │ │ ├── ServiceTopNCounter.java │ │ │ │ ├── TpsMonitorItem.java │ │ │ │ └── collector/ │ │ │ │ ├── NamingSubAndPubMetricsCollector.java │ │ │ │ ├── PushPendingTaskCountMetricsCollector.java │ │ │ │ └── ServiceEventQueueSizeMetricsCollector.java │ │ │ ├── paramcheck/ │ │ │ │ ├── NamingDefaultHttpParamExtractor.java │ │ │ │ ├── NamingInstanceBeatHttpParamExtractor.java │ │ │ │ ├── NamingInstanceListHttpParamExtractor.java │ │ │ │ └── NamingInstanceMetadataBatchHttpParamExtractor.java │ │ │ ├── pojo/ │ │ │ │ ├── ClusterInfo.java │ │ │ │ ├── InstanceOperationInfo.java │ │ │ │ ├── IpAddressInfo.java │ │ │ │ ├── Record.java │ │ │ │ ├── ServiceDetailInfo.java │ │ │ │ ├── ServiceNameView.java │ │ │ │ ├── Subscriber.java │ │ │ │ ├── Subscribers.java │ │ │ │ └── instance/ │ │ │ │ ├── BeatInfoInstanceBuilder.java │ │ │ │ ├── DefaultInstanceIdGenerator.java │ │ │ │ ├── HttpRequestInstanceBuilder.java │ │ │ │ ├── InstanceExtensionHandler.java │ │ │ │ ├── InstanceIdGeneratorManager.java │ │ │ │ └── SnowFlakeInstanceIdGenerator.java │ │ │ ├── push/ │ │ │ │ ├── ClientInfo.java │ │ │ │ ├── NamingFuzzyWatchChangeNotifier.java │ │ │ │ ├── NamingFuzzyWatchSyncNotifier.java │ │ │ │ ├── NamingSubscriberService.java │ │ │ │ ├── NamingSubscriberServiceAggregationImpl.java │ │ │ │ ├── NamingSubscriberServiceLocalImpl.java │ │ │ │ └── v2/ │ │ │ │ ├── NamingSubscriberServiceV2Impl.java │ │ │ │ ├── NoRequiredRetryException.java │ │ │ │ ├── PushConfig.java │ │ │ │ ├── PushDataWrapper.java │ │ │ │ ├── executor/ │ │ │ │ │ ├── PushExecutor.java │ │ │ │ │ ├── PushExecutorDelegate.java │ │ │ │ │ ├── PushExecutorRpcImpl.java │ │ │ │ │ ├── SpiImplPushExecutorHolder.java │ │ │ │ │ └── SpiPushExecutor.java │ │ │ │ ├── hook/ │ │ │ │ │ ├── NacosMonitorPushResultHook.java │ │ │ │ │ ├── PushResult.java │ │ │ │ │ ├── PushResultHook.java │ │ │ │ │ └── PushResultHookHolder.java │ │ │ │ └── task/ │ │ │ │ ├── FuzzyWatchChangeNotifyExecuteTask.java │ │ │ │ ├── FuzzyWatchChangeNotifyTask.java │ │ │ │ ├── FuzzyWatchPushDelayTaskEngine.java │ │ │ │ ├── FuzzyWatchSyncNotifyCallback.java │ │ │ │ ├── FuzzyWatchSyncNotifyExecuteTask.java │ │ │ │ ├── FuzzyWatchSyncNotifyTask.java │ │ │ │ ├── NamingPushCallback.java │ │ │ │ ├── PushDelayTask.java │ │ │ │ ├── PushDelayTaskExecuteEngine.java │ │ │ │ └── PushExecuteTask.java │ │ │ ├── remote/ │ │ │ │ └── rpc/ │ │ │ │ └── handler/ │ │ │ │ ├── BatchInstanceRequestHandler.java │ │ │ │ ├── DistroDataRequestHandler.java │ │ │ │ ├── InstanceRequestHandler.java │ │ │ │ ├── NamingFuzzyWatchRequestHandler.java │ │ │ │ ├── PersistentInstanceRequestHandler.java │ │ │ │ ├── ServiceListRequestHandler.java │ │ │ │ ├── ServiceQueryRequestHandler.java │ │ │ │ └── SubscribeServiceRequestHandler.java │ │ │ ├── selector/ │ │ │ │ ├── LabelSelector.java │ │ │ │ ├── NoneSelector.java │ │ │ │ ├── SelectorManager.java │ │ │ │ ├── context/ │ │ │ │ │ ├── CmdbSelectorContextBuilder.java │ │ │ │ │ └── NoneSelectorContextBuilder.java │ │ │ │ └── interpreter/ │ │ │ │ └── ExpressionInterpreter.java │ │ │ ├── utils/ │ │ │ │ ├── DistroUtils.java │ │ │ │ ├── InstanceUtil.java │ │ │ │ ├── NamingRequestUtil.java │ │ │ │ └── ServiceUtil.java │ │ │ └── web/ │ │ │ ├── CanDistro.java │ │ │ ├── ClientAttributesFilter.java │ │ │ ├── DistroFilter.java │ │ │ ├── DistroIpPortTagGenerator.java │ │ │ ├── DistroTagGenerator.java │ │ │ ├── DistroTagGeneratorImpl.java │ │ │ ├── NamingConfig.java │ │ │ ├── ServiceNameFilter.java │ │ │ └── TrafficReviseFilter.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── logback/ │ │ │ └── naming-included.xml │ │ └── services/ │ │ ├── com.alibaba.nacos.api.naming.spi.generator.InstanceIdGenerator │ │ ├── com.alibaba.nacos.api.remote.Payload │ │ ├── com.alibaba.nacos.api.selector.Selector │ │ ├── com.alibaba.nacos.api.selector.context.SelectorContextBuilder │ │ ├── com.alibaba.nacos.core.ability.ServerAbilityInitializer │ │ ├── com.alibaba.nacos.core.listener.NacosApplicationListener │ │ ├── com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor │ │ ├── com.alibaba.nacos.naming.core.v2.client.factory.ClientFactory │ │ ├── com.alibaba.nacos.naming.healthcheck.heartbeat.AbstractBeatCheckInterceptor │ │ ├── com.alibaba.nacos.naming.healthcheck.interceptor.AbstractHealthCheckInterceptor │ │ ├── com.alibaba.nacos.naming.push.v2.hook.PushResultHook │ │ └── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── naming/ │ │ ├── BaseTest.java │ │ ├── ability/ │ │ │ └── NamingAbilityInitializerTest.java │ │ ├── cluster/ │ │ │ ├── ServerStatusManagerTest.java │ │ │ ├── remote/ │ │ │ │ ├── request/ │ │ │ │ │ ├── AbstractClusterRequestTest.java │ │ │ │ │ └── DistroDataRequestTest.java │ │ │ │ └── response/ │ │ │ │ └── DistroDataResponseTest.java │ │ │ └── transport/ │ │ │ └── JacksonSerializerTest.java │ │ ├── consistency/ │ │ │ └── ephemeral/ │ │ │ └── distro/ │ │ │ └── v2/ │ │ │ ├── DistroClientComponentRegistryTest.java │ │ │ ├── DistroClientDataProcessorTest.java │ │ │ └── DistroClientTransportAgentTest.java │ │ ├── controllers/ │ │ │ └── v3/ │ │ │ ├── ClientControllerV3Test.java │ │ │ ├── ClusterControllerV3Test.java │ │ │ ├── HealthControllerV3Test.java │ │ │ ├── InstanceControllerV3Test.java │ │ │ ├── InstanceOpenApiControllerTest.java │ │ │ ├── OperatorControllerV3Test.java │ │ │ └── ServiceControllerV3Test.java │ │ ├── core/ │ │ │ ├── CatalogServiceV2ImplTest.java │ │ │ ├── ClusterOperatorV2ImplTest.java │ │ │ ├── DistroMapperTest.java │ │ │ ├── HealthOperatorV2ImplTest.java │ │ │ ├── InstanceOperatorClientImplTest.java │ │ │ ├── ServiceOperatorV2ImplTest.java │ │ │ ├── SubscribeManagerTest.java │ │ │ └── v2/ │ │ │ ├── cleaner/ │ │ │ │ ├── EmptyServiceAutoCleanerV2Test.java │ │ │ │ └── ExpiredMetadataCleanerTest.java │ │ │ ├── client/ │ │ │ │ ├── AbstractClientTest.java │ │ │ │ ├── MockAbstractClient.java │ │ │ │ ├── impl/ │ │ │ │ │ ├── ConnectionBasedClientTest.java │ │ │ │ │ └── IpPortBasedClientTest.java │ │ │ │ └── manager/ │ │ │ │ ├── ClientManagerDelegateTest.java │ │ │ │ └── impl/ │ │ │ │ ├── ConnectionBasedClientManagerTest.java │ │ │ │ ├── EphemeralIpPortClientManagerTest.java │ │ │ │ └── PersistentIpPortClientManagerTest.java │ │ │ ├── event/ │ │ │ │ └── publisher/ │ │ │ │ ├── NamingEventPublisherFactoryTest.java │ │ │ │ ├── NamingEventPublisherTest.java │ │ │ │ └── TestEvent.java │ │ │ ├── index/ │ │ │ │ ├── ClientServiceIndexesManagerTest.java │ │ │ │ ├── NamingFuzzyWatchContextServiceTest.java │ │ │ │ └── ServiceStorageTest.java │ │ │ ├── metadata/ │ │ │ │ ├── MetadataOperationTest.java │ │ │ │ ├── NamingMetadataManagerTest.java │ │ │ │ ├── NamingMetadataOperateServiceTest.java │ │ │ │ ├── ServiceMetadataProcessorTest.java │ │ │ │ ├── ServiceMetadataSnapshotOperationTest.java │ │ │ │ └── ServiceMetadataTest.java │ │ │ └── service/ │ │ │ ├── ClientOperationServiceProxyTest.java │ │ │ └── impl/ │ │ │ ├── EphemeralClientOperationServiceImplTest.java │ │ │ └── PersistentClientOperationServiceImplTest.java │ │ ├── healthcheck/ │ │ │ ├── extend/ │ │ │ │ ├── HealthCheckExtendProviderTest.java │ │ │ │ ├── HealthCheckProcessorExtendV2Test.java │ │ │ │ ├── TestChecker.java │ │ │ │ └── TestHealthCheckProcessor.java │ │ │ ├── heartbeat/ │ │ │ │ └── ClientBeatCheckTaskV2Test.java │ │ │ ├── interceptor/ │ │ │ │ └── HealthCheckTaskInterceptWrapperTest.java │ │ │ └── v2/ │ │ │ ├── HealthCheckTaskV2Test.java │ │ │ ├── PersistentHealthStatusSynchronizerTest.java │ │ │ └── processor/ │ │ │ ├── HealthCheckCommonV2Test.java │ │ │ ├── HealthCheckProcessorV2DelegateTest.java │ │ │ └── HttpHealthCheckProcessorTest.java │ │ ├── misc/ │ │ │ ├── ClientConfigTest.java │ │ │ └── UtilsAndCommonsTest.java │ │ ├── monitor/ │ │ │ └── MetricsMonitorTest.java │ │ ├── paramcheck/ │ │ │ └── RpcParamCheckTest.java │ │ ├── pojo/ │ │ │ ├── SubscriberTest.java │ │ │ └── instance/ │ │ │ ├── BeatInfoInstanceBuilderTest.java │ │ │ ├── DefaultInstanceInstanceIdGeneratorTest.java │ │ │ ├── HttpRequestInstanceBuilderTest.java │ │ │ ├── InstanceIdGeneratorManagerTest.java │ │ │ ├── MockInstanceExtensionHandler.java │ │ │ └── SnowFlakeInstanceIdGeneratorTest.java │ │ ├── push/ │ │ │ ├── ClientInfoTest.java │ │ │ ├── NamingFuzzyWatchChangeNotifierTest.java │ │ │ ├── NamingFuzzyWatchSyncNotifierTest.java │ │ │ ├── NamingSubscriberServiceAggregationImplTest.java │ │ │ └── v2/ │ │ │ ├── NamingSubscriberServiceV2ImplTest.java │ │ │ ├── PushConfigTest.java │ │ │ ├── executor/ │ │ │ │ ├── PushExecutorDelegateTest.java │ │ │ │ ├── PushExecutorRpcImplTest.java │ │ │ │ └── SpiImplPushExecutorHolderTest.java │ │ │ ├── hook/ │ │ │ │ └── NacosMonitorPushResultHookTest.java │ │ │ └── task/ │ │ │ ├── FixturePushExecutor.java │ │ │ ├── FuzzyWatchSyncNotifyExecuteTaskTest.java │ │ │ ├── PushDelayTaskExecuteEngineTest.java │ │ │ ├── PushDelayTaskTest.java │ │ │ └── PushExecuteTaskTest.java │ │ ├── remote/ │ │ │ └── rpc/ │ │ │ └── handler/ │ │ │ ├── BatchInstanceRequestHandlerTest.java │ │ │ ├── DistroDataRequestHandlerTest.java │ │ │ ├── InstanceRequestHandlerTest.java │ │ │ ├── PersistentInstanceRequestHandlerTest.java │ │ │ ├── ServiceListRequestHandlerTest.java │ │ │ ├── ServiceQueryRequestHandlerTest.java │ │ │ └── SubscribeServiceRequestHandlerTest.java │ │ ├── selector/ │ │ │ ├── LabelSelectorTest.java │ │ │ ├── MockCmdbContextBuilder.java │ │ │ ├── MockSelector.java │ │ │ ├── NoneSelectorTest.java │ │ │ ├── SelectorManagerTest.java │ │ │ └── context/ │ │ │ └── NoneSelectorContextBuilderTest.java │ │ ├── utils/ │ │ │ ├── DistroUtilsTest.java │ │ │ ├── InstanceUtilTest.java │ │ │ ├── NamingRequestUtilTest.java │ │ │ └── ServiceUtilTest.java │ │ └── web/ │ │ └── ClientAttributesFilterTest.java │ └── resources/ │ └── META-INF/ │ └── services/ │ ├── com.alibaba.nacos.api.naming.spi.generator.InstanceIdGenerator │ ├── com.alibaba.nacos.api.selector.Selector │ ├── com.alibaba.nacos.api.selector.context.SelectorContextBuilder │ └── com.alibaba.nacos.naming.pojo.instance.InstanceExtensionHandler ├── persistence/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── persistence/ │ │ │ ├── configuration/ │ │ │ │ ├── DatasourceConfiguration.java │ │ │ │ └── condition/ │ │ │ │ ├── ConditionDistributedEmbedStorage.java │ │ │ │ ├── ConditionOnEmbeddedStorage.java │ │ │ │ ├── ConditionOnExternalStorage.java │ │ │ │ └── ConditionStandaloneEmbedStorage.java │ │ │ ├── constants/ │ │ │ │ └── PersistenceConstant.java │ │ │ ├── datasource/ │ │ │ │ ├── DataSourcePoolProperties.java │ │ │ │ ├── DataSourceService.java │ │ │ │ ├── DynamicDataSource.java │ │ │ │ ├── ExternalDataSourceProperties.java │ │ │ │ ├── ExternalDataSourceServiceImpl.java │ │ │ │ └── LocalDataSourceServiceImpl.java │ │ │ ├── exception/ │ │ │ │ └── NJdbcException.java │ │ │ ├── model/ │ │ │ │ └── event/ │ │ │ │ ├── DerbyImportEvent.java │ │ │ │ ├── DerbyLoadEvent.java │ │ │ │ └── RaftDbErrorEvent.java │ │ │ ├── monitor/ │ │ │ │ └── DatasourceMetrics.java │ │ │ ├── repository/ │ │ │ │ ├── PaginationHelper.java │ │ │ │ ├── RowMapperManager.java │ │ │ │ ├── embedded/ │ │ │ │ │ ├── EmbeddedPaginationHelperImpl.java │ │ │ │ │ ├── EmbeddedStorageContextHolder.java │ │ │ │ │ ├── hook/ │ │ │ │ │ │ ├── EmbeddedApplyHook.java │ │ │ │ │ │ └── EmbeddedApplyHookHolder.java │ │ │ │ │ ├── operate/ │ │ │ │ │ │ ├── BaseDatabaseOperate.java │ │ │ │ │ │ ├── DatabaseOperate.java │ │ │ │ │ │ └── StandaloneDatabaseOperateImpl.java │ │ │ │ │ └── sql/ │ │ │ │ │ ├── ModifyRequest.java │ │ │ │ │ ├── QueryType.java │ │ │ │ │ ├── SelectRequest.java │ │ │ │ │ └── limiter/ │ │ │ │ │ ├── SqlLimiter.java │ │ │ │ │ └── SqlTypeLimiter.java │ │ │ │ └── extrnal/ │ │ │ │ └── ExternalStoragePaginationHelperImpl.java │ │ │ └── utils/ │ │ │ ├── ConnectionCheckUtil.java │ │ │ ├── DatasourcePlatformUtil.java │ │ │ ├── DerbyUtils.java │ │ │ └── PersistenceExecutor.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── spring.factories │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── persistence/ │ │ ├── configuration/ │ │ │ ├── DatasourceConfigurationTest.java │ │ │ └── condition/ │ │ │ ├── ConditionDistributedEmbedStorageTest.java │ │ │ ├── ConditionOnEmbeddedStorageTest.java │ │ │ ├── ConditionOnExternalStorageTest.java │ │ │ └── ConditionStandaloneEmbedStorageTest.java │ │ ├── constants/ │ │ │ └── PersistenceConstantTest.java │ │ ├── datasource/ │ │ │ ├── ClusterExternalStorageTest.java │ │ │ ├── DataSourcePoolPropertiesTest.java │ │ │ ├── DynamicDataSourceTest.java │ │ │ ├── ExternalDataSourcePropertiesTest.java │ │ │ ├── ExternalDataSourceServiceImplTest.java │ │ │ ├── LocalDataSourceServiceImplTest.java │ │ │ ├── StandaloneExternalStorageTest.java │ │ │ └── mock/ │ │ │ ├── MockConnection.java │ │ │ ├── MockDriver.java │ │ │ ├── MockPreparedStatement.java │ │ │ ├── MockResultSet.java │ │ │ └── MockStatement.java │ │ ├── exception/ │ │ │ └── NJdbcExceptionTest.java │ │ ├── model/ │ │ │ └── event/ │ │ │ └── EventTest.java │ │ ├── monitor/ │ │ │ └── DatasourceMetricsTest.java │ │ ├── repository/ │ │ │ ├── RowMapperManagerTest.java │ │ │ ├── embedded/ │ │ │ │ ├── EmbeddedPaginationHelperImplTest.java │ │ │ │ ├── EmbeddedStorageContextHolderTest.java │ │ │ │ ├── hook/ │ │ │ │ │ └── EmbeddedApplyHookHolderTest.java │ │ │ │ ├── operate/ │ │ │ │ │ ├── BaseDatabaseOperateTest.java │ │ │ │ │ ├── DatabaseOperateTest.java │ │ │ │ │ ├── MockConfigInfo.java │ │ │ │ │ └── StandaloneDatabaseOperateImplTest.java │ │ │ │ └── sql/ │ │ │ │ ├── ModifyRequestTest.java │ │ │ │ ├── QueryTypeTest.java │ │ │ │ ├── SelectRequestTest.java │ │ │ │ └── limiter/ │ │ │ │ └── SqlTypeLimiterTest.java │ │ │ └── extrnal/ │ │ │ └── ExternalStoragePaginationHelperImplTest.java │ │ └── utils/ │ │ ├── ConnectionCheckUtilTest.java │ │ ├── DerbyUtilsTest.java │ │ └── PersistenceExecutorTest.java │ └── resources/ │ ├── META-INF/ │ │ ├── derby-schema.sql │ │ └── test-derby-import.sql │ ├── application.properties │ └── logback-test.xml ├── plugin/ │ ├── auth/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── auth/ │ │ │ │ ├── api/ │ │ │ │ │ ├── AuthResult.java │ │ │ │ │ ├── IdentityContext.java │ │ │ │ │ ├── LoginIdentityContext.java │ │ │ │ │ ├── Permission.java │ │ │ │ │ ├── RequestResource.java │ │ │ │ │ └── Resource.java │ │ │ │ ├── constant/ │ │ │ │ │ ├── ActionTypes.java │ │ │ │ │ ├── ApiType.java │ │ │ │ │ ├── Constants.java │ │ │ │ │ └── SignType.java │ │ │ │ ├── exception/ │ │ │ │ │ └── AccessException.java │ │ │ │ └── spi/ │ │ │ │ ├── client/ │ │ │ │ │ ├── AbstractClientAuthService.java │ │ │ │ │ ├── ClientAuthPluginManager.java │ │ │ │ │ └── ClientAuthService.java │ │ │ │ └── server/ │ │ │ │ ├── AuthPluginManager.java │ │ │ │ ├── AuthPluginProvider.java │ │ │ │ └── AuthPluginService.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.api.plugin.PluginProvider │ │ └── test/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── auth/ │ │ │ ├── api/ │ │ │ │ ├── IdentityContextTest.java │ │ │ │ ├── LoginIdentityContextTest.java │ │ │ │ ├── PermissionTest.java │ │ │ │ └── RequestResourceTest.java │ │ │ ├── constant/ │ │ │ │ ├── ActionTypesTest.java │ │ │ │ └── ConstantsTest.java │ │ │ ├── exception/ │ │ │ │ └── AccessExceptionTest.java │ │ │ └── spi/ │ │ │ ├── client/ │ │ │ │ └── ClientAuthPluginManagerTest.java │ │ │ ├── mock/ │ │ │ │ ├── MockAuthPluginService.java │ │ │ │ ├── MockClientAuthService.java │ │ │ │ └── MockEmptyNameAuthPluginService.java │ │ │ └── server/ │ │ │ ├── AuthPluginManagerTest.java │ │ │ └── AuthPluginProviderTest.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ ├── com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService │ │ └── com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService │ ├── config/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── config/ │ │ │ │ ├── ConfigChangePluginManager.java │ │ │ │ ├── ConfigChangePluginProvider.java │ │ │ │ ├── constants/ │ │ │ │ │ ├── ConfigChangeConstants.java │ │ │ │ │ ├── ConfigChangeExecuteTypes.java │ │ │ │ │ └── ConfigChangePointCutTypes.java │ │ │ │ ├── model/ │ │ │ │ │ ├── ConfigChangeRequest.java │ │ │ │ │ └── ConfigChangeResponse.java │ │ │ │ └── spi/ │ │ │ │ └── ConfigChangePluginService.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.api.plugin.PluginProvider │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── plugin/ │ │ └── config/ │ │ ├── ConfigChangePluginManagerTests.java │ │ └── ConfigChangePluginProviderTest.java │ ├── control/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── control/ │ │ │ │ ├── ControlManagerCenter.java │ │ │ │ ├── Loggers.java │ │ │ │ ├── configs/ │ │ │ │ │ ├── ControlConfigs.java │ │ │ │ │ └── ControlConfigsInitializer.java │ │ │ │ ├── connection/ │ │ │ │ │ ├── ConnectionControlManager.java │ │ │ │ │ ├── ConnectionMetricsCollector.java │ │ │ │ │ ├── DefaultConnectionControlManager.java │ │ │ │ │ ├── request/ │ │ │ │ │ │ └── ConnectionCheckRequest.java │ │ │ │ │ ├── response/ │ │ │ │ │ │ ├── ConnectionCheckCode.java │ │ │ │ │ │ └── ConnectionCheckResponse.java │ │ │ │ │ └── rule/ │ │ │ │ │ └── ConnectionControlRule.java │ │ │ │ ├── event/ │ │ │ │ │ ├── ConnectionLimitRuleChangeEvent.java │ │ │ │ │ └── TpsControlRuleChangeEvent.java │ │ │ │ ├── rule/ │ │ │ │ │ ├── ControlRuleChangeActivator.java │ │ │ │ │ ├── parser/ │ │ │ │ │ │ ├── ConnectionControlRuleParser.java │ │ │ │ │ │ ├── NacosConnectionControlRuleParser.java │ │ │ │ │ │ ├── NacosTpsControlRuleParser.java │ │ │ │ │ │ ├── RuleParser.java │ │ │ │ │ │ └── TpsControlRuleParser.java │ │ │ │ │ └── storage/ │ │ │ │ │ ├── ExternalRuleStorage.java │ │ │ │ │ ├── LocalDiskRuleStorage.java │ │ │ │ │ ├── RuleStorage.java │ │ │ │ │ └── RuleStorageProxy.java │ │ │ │ ├── spi/ │ │ │ │ │ ├── ControlManagerBuilder.java │ │ │ │ │ ├── ControlPluginProvider.java │ │ │ │ │ └── ExternalRuleStorageBuilder.java │ │ │ │ ├── tps/ │ │ │ │ │ ├── DefaultTpsControlManager.java │ │ │ │ │ ├── MonitorType.java │ │ │ │ │ ├── TpsControlManager.java │ │ │ │ │ ├── TpsMetrics.java │ │ │ │ │ ├── barrier/ │ │ │ │ │ │ ├── DefaultNacosTpsBarrier.java │ │ │ │ │ │ ├── LocalSimpleCountRateCounter.java │ │ │ │ │ │ ├── LocalSimpleCountRuleBarrier.java │ │ │ │ │ │ ├── RateCounter.java │ │ │ │ │ │ ├── RuleBarrier.java │ │ │ │ │ │ ├── SimpleCountRuleBarrier.java │ │ │ │ │ │ ├── TpsBarrier.java │ │ │ │ │ │ └── creator/ │ │ │ │ │ │ ├── DefaultNacosTpsBarrierCreator.java │ │ │ │ │ │ ├── LocalSimpleCountBarrierCreator.java │ │ │ │ │ │ ├── RuleBarrierCreator.java │ │ │ │ │ │ └── TpsBarrierCreator.java │ │ │ │ │ ├── request/ │ │ │ │ │ │ ├── BarrierCheckRequest.java │ │ │ │ │ │ └── TpsCheckRequest.java │ │ │ │ │ ├── response/ │ │ │ │ │ │ ├── TpsCheckResponse.java │ │ │ │ │ │ └── TpsResultCode.java │ │ │ │ │ └── rule/ │ │ │ │ │ ├── RuleDetail.java │ │ │ │ │ ├── RuleModel.java │ │ │ │ │ └── TpsControlRule.java │ │ │ │ └── utils/ │ │ │ │ ├── DiskUtils.java │ │ │ │ └── EnvUtils.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.api.plugin.PluginProvider │ │ └── test/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── control/ │ │ │ ├── ControlManagerBuilderTest.java │ │ │ ├── ControlManagerCenterTest.java │ │ │ ├── connection/ │ │ │ │ ├── DefaultConnectionControlManagerTest.java │ │ │ │ ├── TestAConnectionMetricsCollector.java │ │ │ │ └── TestBConnectionMetricsCollector.java │ │ │ ├── spi/ │ │ │ │ ├── ExternalRuleStorageBuilderTest.java │ │ │ │ └── ExternalRuleStorageTest.java │ │ │ ├── tps/ │ │ │ │ └── DefaultNacosTpsBarrierTest.java │ │ │ └── utils/ │ │ │ ├── DiskUtilsTest.java │ │ │ └── EnvUtilsTest.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ ├── com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector │ │ ├── com.alibaba.nacos.plugin.control.spi.ControlManagerBuilder │ │ ├── com.alibaba.nacos.plugin.control.spi.ExternalRuleStorageBuilder │ │ └── com.alibaba.nacos.plugin.control.tps.barrier.creator.RuleBarrierCreator │ ├── datasource/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── datasource/ │ │ │ │ ├── MapperManager.java │ │ │ │ ├── constants/ │ │ │ │ │ ├── CommonConstant.java │ │ │ │ │ ├── ContextConstant.java │ │ │ │ │ ├── DataSourceConstant.java │ │ │ │ │ ├── DatabaseTypeConstant.java │ │ │ │ │ ├── FieldConstant.java │ │ │ │ │ ├── PrimaryKeyConstant.java │ │ │ │ │ └── TableConstant.java │ │ │ │ ├── dialect/ │ │ │ │ │ └── DatabaseDialect.java │ │ │ │ ├── manager/ │ │ │ │ │ ├── DatabaseDialectManager.java │ │ │ │ │ └── DatasourceDialectPluginProvider.java │ │ │ │ ├── mapper/ │ │ │ │ │ ├── AbstractMapper.java │ │ │ │ │ ├── ConfigInfoAggrMapper.java │ │ │ │ │ ├── ConfigInfoBetaMapper.java │ │ │ │ │ ├── ConfigInfoGrayMapper.java │ │ │ │ │ ├── ConfigInfoMapper.java │ │ │ │ │ ├── ConfigInfoTagMapper.java │ │ │ │ │ ├── ConfigMigrateMapper.java │ │ │ │ │ ├── ConfigTagsRelationMapper.java │ │ │ │ │ ├── GroupCapacityMapper.java │ │ │ │ │ ├── HistoryConfigInfoMapper.java │ │ │ │ │ ├── Mapper.java │ │ │ │ │ ├── TenantCapacityMapper.java │ │ │ │ │ ├── TenantInfoMapper.java │ │ │ │ │ └── ext/ │ │ │ │ │ └── WhereBuilder.java │ │ │ │ ├── model/ │ │ │ │ │ ├── MapperContext.java │ │ │ │ │ └── MapperResult.java │ │ │ │ └── proxy/ │ │ │ │ └── MapperProxy.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.api.plugin.PluginProvider │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── plugin/ │ │ └── datasource/ │ │ ├── manager/ │ │ │ └── DatasourceDialectPluginProviderTest.java │ │ ├── mapper/ │ │ │ └── AbstractMapperTest.java │ │ └── proxy/ │ │ └── MapperProxyTest.java │ ├── encryption/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── encryption/ │ │ │ │ ├── EncryptionPluginManager.java │ │ │ │ ├── EncryptionPluginProvider.java │ │ │ │ ├── handler/ │ │ │ │ │ └── EncryptionHandler.java │ │ │ │ └── spi/ │ │ │ │ └── EncryptionPluginService.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.api.plugin.PluginProvider │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── plugin/ │ │ └── encryption/ │ │ ├── EncryptionPluginManagerTest.java │ │ ├── EncryptionPluginProviderTest.java │ │ └── handler/ │ │ ├── EncryptionAesHandlerTest.java │ │ └── EncryptionHandlerTest.java │ ├── environment/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── environment/ │ │ │ │ ├── CustomEnvironmentPluginManager.java │ │ │ │ └── spi/ │ │ │ │ ├── CustomEnvironmentPluginService.java │ │ │ │ └── EnvironmentPluginProvider.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.api.plugin.PluginProvider │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── plugin/ │ │ └── environment/ │ │ └── CustomEnvironmentPluginManagerTest.java │ ├── pom.xml │ └── trace/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── trace/ │ │ │ ├── NacosTracePluginManager.java │ │ │ ├── TracePluginProvider.java │ │ │ └── spi/ │ │ │ └── NacosTraceSubscriber.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.api.plugin.PluginProvider │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── plugin/ │ │ └── trace/ │ │ ├── NacosTracePluginManagerTest.java │ │ ├── TracePluginProviderTest.java │ │ └── mock/ │ │ └── MockNacosTraceSubscriber.java │ └── resources/ │ └── META-INF/ │ └── services/ │ └── com.alibaba.nacos.plugin.trace.spi.NacosTraceSubscriber ├── plugin-default-impl/ │ ├── nacos-default-auth-plugin/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── auth/ │ │ │ │ └── impl/ │ │ │ │ ├── JwtAuthenticationEntryPoint.java │ │ │ │ ├── LdapAuthPluginService.java │ │ │ │ ├── NacosAuthPluginService.java │ │ │ │ ├── SafeBcryptPasswordEncoder.java │ │ │ │ ├── authenticate/ │ │ │ │ │ ├── AbstractAuthenticationManager.java │ │ │ │ │ ├── DefaultAuthenticationManager.java │ │ │ │ │ ├── IAuthenticationManager.java │ │ │ │ │ └── LdapAuthenticationManager.java │ │ │ │ ├── condition/ │ │ │ │ │ ├── ConditionOnInnerDatasource.java │ │ │ │ │ ├── ConditionOnLdapAuth.java │ │ │ │ │ ├── ConditionOnNacosAuth.java │ │ │ │ │ └── ConditionOnRemoteDatasource.java │ │ │ │ ├── config/ │ │ │ │ │ └── AuthImplPackageExcludeFilter.java │ │ │ │ ├── configuration/ │ │ │ │ │ ├── AuthConfigs.java │ │ │ │ │ ├── autoconfiguration/ │ │ │ │ │ │ ├── NacosAuthPluginInnerAutoConfig.java │ │ │ │ │ │ └── NacosAuthPluginRemoteAutoConfig.java │ │ │ │ │ ├── core/ │ │ │ │ │ │ ├── NacosAuthPluginCoreConfig.java │ │ │ │ │ │ ├── NacosAuthPluginInnerServiceConfig.java │ │ │ │ │ │ └── NacosAuthPluginRemoteServiceConfig.java │ │ │ │ │ ├── persistence/ │ │ │ │ │ │ ├── NacosAuthPluginEmbeddedStorageConfig.java │ │ │ │ │ │ ├── NacosAuthPluginExternalStorageConfig.java │ │ │ │ │ │ └── NacosAuthPluginPersistenceConfig.java │ │ │ │ │ └── web/ │ │ │ │ │ ├── NacosAuthPluginControllerConfig.java │ │ │ │ │ ├── NacosAuthPluginOldControllerConfig.java │ │ │ │ │ └── NacosAuthPluginWebConfig.java │ │ │ │ ├── constant/ │ │ │ │ │ ├── AuthConstants.java │ │ │ │ │ ├── AuthPageConstant.java │ │ │ │ │ └── AuthSystemTypes.java │ │ │ │ ├── controller/ │ │ │ │ │ ├── UserController.java │ │ │ │ │ └── v3/ │ │ │ │ │ ├── PermissionControllerV3.java │ │ │ │ │ ├── RoleControllerV3.java │ │ │ │ │ └── UserControllerV3.java │ │ │ │ ├── jwt/ │ │ │ │ │ ├── NacosJwtParser.java │ │ │ │ │ ├── NacosJwtPayload.java │ │ │ │ │ └── NacosSignatureAlgorithm.java │ │ │ │ ├── ldap/ │ │ │ │ │ ├── LdapAuthPluginConfig.java │ │ │ │ │ ├── LdapAuthenticationProvider.java │ │ │ │ │ └── NacosLdapContextSource.java │ │ │ │ ├── model/ │ │ │ │ │ └── OffsetFetchResult.java │ │ │ │ ├── persistence/ │ │ │ │ │ ├── AuthPaginationHelper.java │ │ │ │ │ ├── AuthRowMapperManager.java │ │ │ │ │ ├── EmbeddedPermissionPersistServiceImpl.java │ │ │ │ │ ├── EmbeddedRolePersistServiceImpl.java │ │ │ │ │ ├── EmbeddedUserPersistServiceImpl.java │ │ │ │ │ ├── ExternalPermissionPersistServiceImpl.java │ │ │ │ │ ├── ExternalRolePersistServiceImpl.java │ │ │ │ │ ├── ExternalUserPersistServiceImpl.java │ │ │ │ │ ├── PermissionInfo.java │ │ │ │ │ ├── PermissionPersistService.java │ │ │ │ │ ├── RoleInfo.java │ │ │ │ │ ├── RolePersistService.java │ │ │ │ │ ├── User.java │ │ │ │ │ ├── UserPersistService.java │ │ │ │ │ ├── embedded/ │ │ │ │ │ │ └── AuthEmbeddedPaginationHelperImpl.java │ │ │ │ │ ├── extrnal/ │ │ │ │ │ │ └── AuthExternalPaginationHelperImpl.java │ │ │ │ │ └── handler/ │ │ │ │ │ ├── PageHandlerAdapter.java │ │ │ │ │ ├── PageHandlerAdapterFactory.java │ │ │ │ │ └── support/ │ │ │ │ │ ├── DefaultPageHandlerAdapter.java │ │ │ │ │ ├── DerbyPageHandlerAdapter.java │ │ │ │ │ └── MysqlPageHandlerAdapter.java │ │ │ │ ├── roles/ │ │ │ │ │ ├── AbstractCachedRoleService.java │ │ │ │ │ ├── AbstractCheckedRoleService.java │ │ │ │ │ ├── NacosRoleService.java │ │ │ │ │ ├── NacosRoleServiceDirectImpl.java │ │ │ │ │ └── NacosRoleServiceRemoteImpl.java │ │ │ │ ├── token/ │ │ │ │ │ ├── TokenManager.java │ │ │ │ │ ├── TokenManagerDelegate.java │ │ │ │ │ └── impl/ │ │ │ │ │ ├── CachedJwtTokenManager.java │ │ │ │ │ └── JwtTokenManager.java │ │ │ │ ├── users/ │ │ │ │ │ ├── AbstractCachedUserService.java │ │ │ │ │ ├── NacosUser.java │ │ │ │ │ ├── NacosUserDetails.java │ │ │ │ │ ├── NacosUserService.java │ │ │ │ │ ├── NacosUserServiceDirectImpl.java │ │ │ │ │ ├── NacosUserServiceRemoteImpl.java │ │ │ │ │ └── User.java │ │ │ │ └── utils/ │ │ │ │ ├── Base64Decode.java │ │ │ │ ├── PasswordEncoderUtil.java │ │ │ │ ├── PasswordGeneratorUtil.java │ │ │ │ └── RemoteServerUtil.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ ├── services/ │ │ │ │ ├── com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService │ │ │ │ └── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ │ │ └── spring/ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ └── test/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── plugin/ │ │ └── auth/ │ │ └── impl/ │ │ ├── SafeBcryptPasswordEncoderTest.java │ │ ├── authenticate/ │ │ │ ├── AbstractAuthenticationManagerTest.java │ │ │ └── LdapAuthenticationManagerTest.java │ │ ├── configuration/ │ │ │ ├── AuthConfigsTest.java │ │ │ └── ConditionOnLdapAuthTest.java │ │ ├── controller/ │ │ │ ├── UserControllerTest.java │ │ │ └── v3/ │ │ │ ├── PermissionControllerV3Test.java │ │ │ ├── RoleControllerV3Test.java │ │ │ └── UserControllerV3Test.java │ │ ├── jwt/ │ │ │ └── NacosJwtParserTest.java │ │ ├── ldap/ │ │ │ └── LdapAuthenticationProviderTest.java │ │ ├── persistence/ │ │ │ ├── EmbeddedPermissionPersistServiceImplTest.java │ │ │ ├── EmbeddedRolePersistServiceImplTest.java │ │ │ ├── EmbeddedUserPersistServiceImplTest.java │ │ │ ├── ExternalPermissionPersistServiceImplTest.java │ │ │ ├── ExternalRolePersistServiceImplTest.java │ │ │ └── ExternalUserPersistServiceImplTest.java │ │ ├── roles/ │ │ │ └── NacosRoleServiceDirectImplTest.java │ │ ├── token/ │ │ │ ├── TokenManagerDelegateTest.java │ │ │ └── impl/ │ │ │ ├── CachedJwtTokenManagerTest.java │ │ │ └── JwtTokenManagerTest.java │ │ ├── users/ │ │ │ └── NacosUserServiceDirectImplTest.java │ │ └── utils/ │ │ ├── Base64DecodeTest.java │ │ ├── PasswordEncoderUtilTest.java │ │ └── PasswordGeneratorUtilTest.java │ ├── nacos-default-control-plugin/ │ │ ├── pom.xml │ │ └── src/ │ │ ├── main/ │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── control/ │ │ │ │ └── impl/ │ │ │ │ ├── NacosConnectionControlManager.java │ │ │ │ ├── NacosControlManagerBuilder.java │ │ │ │ └── NacosTpsControlManager.java │ │ │ └── resources/ │ │ │ └── META-INF/ │ │ │ └── services/ │ │ │ └── com.alibaba.nacos.plugin.control.spi.ControlManagerBuilder │ │ └── test/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── control/ │ │ │ └── impl/ │ │ │ ├── NacosConnectionControlManagerTest.java │ │ │ ├── NacosControlManagerBuilderTest.java │ │ │ ├── NacosTpsControlManagerTest.java │ │ │ └── TestConnectionMetricsCollector.java │ │ └── resources/ │ │ └── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector │ ├── nacos-default-datasource-plugin/ │ │ ├── nacos-datasource-plugin-base/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── alibaba/ │ │ │ │ └── nacos/ │ │ │ │ └── plugin/ │ │ │ │ └── datasource/ │ │ │ │ └── impl/ │ │ │ │ ├── base/ │ │ │ │ │ ├── BaseConfigInfoAggrMapper.java │ │ │ │ │ ├── BaseConfigInfoBetaMapper.java │ │ │ │ │ ├── BaseConfigInfoMapper.java │ │ │ │ │ ├── BaseConfigInfoTagMapper.java │ │ │ │ │ ├── BaseConfigTagsRelationMapper.java │ │ │ │ │ ├── BaseGroupCapacityMapper.java │ │ │ │ │ ├── BaseTenantCapacityMapper.java │ │ │ │ │ └── BaseTenantInfoMapper.java │ │ │ │ ├── dialect/ │ │ │ │ │ └── AbstractDatabaseDialect.java │ │ │ │ └── enums/ │ │ │ │ └── TrustedPostgresqFunctionEnum.java │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── datasource/ │ │ │ ├── MapperManagerTest.java │ │ │ └── mapper/ │ │ │ └── TestMapper.java │ │ ├── nacos-datasource-plugin-derby/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── alibaba/ │ │ │ │ │ └── nacos/ │ │ │ │ │ └── plugin/ │ │ │ │ │ └── datasource/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── derby/ │ │ │ │ │ │ ├── AbstractMapperByDerby.java │ │ │ │ │ │ ├── ConfigInfoBetaMapperByDerby.java │ │ │ │ │ │ ├── ConfigInfoGrayMapperByDerby.java │ │ │ │ │ │ ├── ConfigInfoMapperByDerby.java │ │ │ │ │ │ ├── ConfigInfoTagMapperByDerby.java │ │ │ │ │ │ ├── ConfigInfoTagsRelationMapperByDerby.java │ │ │ │ │ │ ├── ConfigMigrateMapperByDerby.java │ │ │ │ │ │ ├── GroupCapacityMapperByDerby.java │ │ │ │ │ │ ├── HistoryConfigInfoMapperByDerby.java │ │ │ │ │ │ ├── TenantCapacityMapperByDerby.java │ │ │ │ │ │ └── TenantInfoMapperByDerby.java │ │ │ │ │ ├── dialect/ │ │ │ │ │ │ └── DerbyDatabaseDialect.java │ │ │ │ │ └── enums/ │ │ │ │ │ └── derby/ │ │ │ │ │ ├── TrustedDerbyFunctionEnum.java │ │ │ │ │ └── TrustedDerbylFunctionEnum.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ ├── derby-schema.sql │ │ │ │ └── services/ │ │ │ │ ├── com.alibaba.nacos.plugin.datasource.dialect.DatabaseDialect │ │ │ │ └── com.alibaba.nacos.plugin.datasource.mapper.Mapper │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── datasource/ │ │ │ └── impl/ │ │ │ ├── derby/ │ │ │ │ ├── ConfigInfoBetaMapperByDerbyTest.java │ │ │ │ ├── ConfigInfoMapperByDerbyTest.java │ │ │ │ ├── ConfigInfoTagMapperByDerbyTest.java │ │ │ │ ├── ConfigInfoTagsRelationMapperByDerbyTest.java │ │ │ │ ├── GroupCapacityMapperByDerbyTest.java │ │ │ │ ├── HistoryConfigInfoMapperByDerbyTest.java │ │ │ │ ├── TenantCapacityMapperByDerbyTest.java │ │ │ │ └── TenantInfoMapperByDerbyTest.java │ │ │ └── enums/ │ │ │ └── derby/ │ │ │ └── TrustedDerbyFunctionEnumTest.java │ │ ├── nacos-datasource-plugin-mysql/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── alibaba/ │ │ │ │ │ └── nacos/ │ │ │ │ │ └── plugin/ │ │ │ │ │ └── datasource/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── dialect/ │ │ │ │ │ │ ├── DefaultDatabaseDialect.java │ │ │ │ │ │ └── MysqlDatabaseDialect.java │ │ │ │ │ ├── enums/ │ │ │ │ │ │ └── mysql/ │ │ │ │ │ │ └── TrustedMysqlFunctionEnum.java │ │ │ │ │ └── mysql/ │ │ │ │ │ ├── AbstractMapperByMysql.java │ │ │ │ │ ├── ConfigInfoAggrMapperByMySql.java │ │ │ │ │ ├── ConfigInfoBetaMapperByMySql.java │ │ │ │ │ ├── ConfigInfoGrayMapperByMySql.java │ │ │ │ │ ├── ConfigInfoMapperByMySql.java │ │ │ │ │ ├── ConfigInfoTagMapperByMySql.java │ │ │ │ │ ├── ConfigMigrateMapperByMysql.java │ │ │ │ │ ├── ConfigTagsRelationMapperByMySql.java │ │ │ │ │ ├── GroupCapacityMapperByMysql.java │ │ │ │ │ ├── HistoryConfigInfoMapperByMySql.java │ │ │ │ │ ├── TenantCapacityMapperByMySql.java │ │ │ │ │ └── TenantInfoMapperByMySql.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ ├── mysql-schema.sql │ │ │ │ └── services/ │ │ │ │ ├── com.alibaba.nacos.plugin.datasource.dialect.DatabaseDialect │ │ │ │ └── com.alibaba.nacos.plugin.datasource.mapper.Mapper │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── datasource/ │ │ │ └── impl/ │ │ │ ├── enums/ │ │ │ │ └── mysql/ │ │ │ │ └── TrustedMysqlFunctionEnumTest.java │ │ │ └── mysql/ │ │ │ ├── ConfigInfoBetaMapperByMySqlTest.java │ │ │ ├── ConfigInfoMapperByMySqlTest.java │ │ │ ├── ConfigInfoTagMapperByMySqlTest.java │ │ │ ├── ConfigTagsRelationMapperByMySqlTest.java │ │ │ ├── GroupCapacityMapperByMysqlTest.java │ │ │ ├── HistoryConfigInfoMapperByMySqlTest.java │ │ │ ├── TenantCapacityMapperByMySqlTest.java │ │ │ └── TenantInfoMapperByMySqlTest.java │ │ ├── nacos-datasource-plugin-oracle/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── alibaba/ │ │ │ │ │ └── nacos/ │ │ │ │ │ └── plugin/ │ │ │ │ │ └── datasource/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── dialect/ │ │ │ │ │ │ └── OracleDatabaseDialect.java │ │ │ │ │ ├── enums/ │ │ │ │ │ │ └── oracle/ │ │ │ │ │ │ └── TrustedOracleFunctionEnum.java │ │ │ │ │ └── oracle/ │ │ │ │ │ ├── AbstractMapperByOracle.java │ │ │ │ │ ├── ConfigInfoAggrMapperByOracle.java │ │ │ │ │ ├── ConfigInfoBetaMapperByOracle.java │ │ │ │ │ ├── ConfigInfoGrayMapperByOracle.java │ │ │ │ │ ├── ConfigInfoMapperByOracle.java │ │ │ │ │ ├── ConfigInfoTagMapperByOracle.java │ │ │ │ │ ├── ConfigMigrateMapperByOracle.java │ │ │ │ │ ├── ConfigTagsRelationMapperByOracle.java │ │ │ │ │ ├── GroupCapacityMapperByOracle.java │ │ │ │ │ ├── HistoryConfigInfoMapperByOracle.java │ │ │ │ │ ├── TenantCapacityMapperByOracle.java │ │ │ │ │ └── TenantInfoMapperByOracle.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ ├── oracle-schema.sql │ │ │ │ └── services/ │ │ │ │ ├── com.alibaba.nacos.plugin.datasource.dialect.DatabaseDialect │ │ │ │ └── com.alibaba.nacos.plugin.datasource.mapper.Mapper │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── datasource/ │ │ │ └── impl/ │ │ │ ├── enums/ │ │ │ │ └── oracle/ │ │ │ │ └── TrustedOracleFunctionEnumTest.java │ │ │ └── oracle/ │ │ │ ├── ConfigInfoAggrMapperByOracleTest.java │ │ │ ├── ConfigInfoBetaMapperByOracleTest.java │ │ │ ├── ConfigInfoGrayMapperByOracleTest.java │ │ │ ├── ConfigInfoMapperByOracleTest.java │ │ │ ├── ConfigInfoTagMapperByOracleTest.java │ │ │ ├── ConfigMigrateMapperByOracleTest.java │ │ │ ├── ConfigTagsRelationMapperByOracleTest.java │ │ │ ├── GroupCapacityMapperByOracleTest.java │ │ │ ├── HistoryConfigInfoMapperByOracleTest.java │ │ │ ├── TenantCapacityMapperByOracleTest.java │ │ │ └── TenantInfoMapperByOracleTest.java │ │ ├── nacos-datasource-plugin-postgresql/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── alibaba/ │ │ │ │ │ └── nacos/ │ │ │ │ │ └── plugin/ │ │ │ │ │ └── datasource/ │ │ │ │ │ └── impl/ │ │ │ │ │ ├── dialect/ │ │ │ │ │ │ └── PostgresqlDatabaseDialect.java │ │ │ │ │ ├── enums/ │ │ │ │ │ │ └── postgresql/ │ │ │ │ │ │ └── TrustedPostgresqlFunctionEnum.java │ │ │ │ │ └── postgresql/ │ │ │ │ │ ├── ConfigInfoAggrMapperByPostgresql.java │ │ │ │ │ ├── ConfigInfoBetaMapperByPostgresql.java │ │ │ │ │ ├── ConfigInfoGrayMapperByPostgresql.java │ │ │ │ │ ├── ConfigInfoMapperByPostgresql.java │ │ │ │ │ ├── ConfigInfoTagMapperByPostgresql.java │ │ │ │ │ ├── ConfigMigrateMapperByPostgresql.java │ │ │ │ │ ├── ConfigTagsRelationMapperByPostgresql.java │ │ │ │ │ ├── GroupCapacityMapperByPostgresql.java │ │ │ │ │ ├── HistoryConfigInfoMapperByPostgresql.java │ │ │ │ │ ├── TenantCapacityMapperByPostgresql.java │ │ │ │ │ └── TenantInfoMapperByPostgresql.java │ │ │ │ └── resources/ │ │ │ │ └── META-INF/ │ │ │ │ ├── pg-schema.sql │ │ │ │ └── services/ │ │ │ │ ├── com.alibaba.nacos.plugin.datasource.dialect.DatabaseDialect │ │ │ │ └── com.alibaba.nacos.plugin.datasource.mapper.Mapper │ │ │ └── test/ │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── datasource/ │ │ │ └── impl/ │ │ │ ├── enums/ │ │ │ │ └── postgresql/ │ │ │ │ └── TrustedPostgresqlFunctionEnumTest.java │ │ │ └── postgresql/ │ │ │ ├── ConfigInfoMapperByPostgresqlTest.java │ │ │ └── ConfigTagsRelationMapperByPostgresqlTest.java │ │ └── pom.xml │ ├── nacos-default-plugin-all/ │ │ └── pom.xml │ ├── nacos-oidc-auth-plugin/ │ │ ├── dependency-reduced-pom.xml │ │ ├── pom.xml │ │ └── src/ │ │ └── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── plugin/ │ │ │ └── auth/ │ │ │ └── impl/ │ │ │ └── oidc/ │ │ │ ├── AuthorityProvider.java │ │ │ ├── IdentityProvider.java │ │ │ ├── OidcAuthPluginService.java │ │ │ ├── OidcAuthorityProvider.java │ │ │ ├── OidcIdentityProvider.java │ │ │ ├── authenticate/ │ │ │ │ ├── AuthorizationCodeHandler.java │ │ │ │ ├── OidcAuthenticationManager.java │ │ │ │ └── OidcSessionManager.java │ │ │ ├── authorization/ │ │ │ │ ├── AuthorizationClient.java │ │ │ │ ├── AuthorizationRequest.java │ │ │ │ └── AuthorizationResponse.java │ │ │ ├── config/ │ │ │ │ ├── OidcAuthConfig.java │ │ │ │ ├── OidcPluginAutoConfiguration.java │ │ │ │ └── OidcWebSecurityConfig.java │ │ │ ├── constant/ │ │ │ │ └── OidcConstants.java │ │ │ ├── controller/ │ │ │ │ └── OidcLoginController.java │ │ │ ├── identity/ │ │ │ │ └── OidcUserMapper.java │ │ │ └── token/ │ │ │ ├── JwksProvider.java │ │ │ └── JwtTokenValidator.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── services/ │ │ │ └── com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService │ │ └── spring/ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ └── pom.xml ├── pom.xml ├── prometheus/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── prometheus/ │ │ ├── PrometheusApp.java │ │ ├── api/ │ │ │ └── ApiConstants.java │ │ ├── conf/ │ │ │ └── PrometheusSecurityConfiguration.java │ │ ├── controller/ │ │ │ └── PrometheusController.java │ │ ├── exception/ │ │ │ └── PrometheusApiExceptionHandler.java │ │ ├── filter/ │ │ │ └── PrometheusAuthFilter.java │ │ └── utils/ │ │ └── PrometheusUtils.java │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── prometheus/ │ │ └── controller/ │ │ ├── PrometheusControllerTest.java │ │ └── exception/ │ │ └── PrometheusApiExceptionHandlerTest.java │ └── resources/ │ └── application.properties ├── resources/ │ └── copyright ├── server/ │ ├── pom.xml │ └── src/ │ └── main/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ ├── NacosServerBasicApplication.java │ │ ├── NacosServerWebApplication.java │ │ └── server/ │ │ ├── AbstractNacosWebBeanTypeFilter.java │ │ ├── NacosNormalBeanTypeFilter.java │ │ ├── NacosWebBeanPostProcessorConfiguration.java │ │ └── NacosWebBeanTypeFilter.java │ └── resources/ │ ├── nacos-server-web-banner.txt │ └── nacos-server.properties ├── skills/ │ └── nacos-skill-registry/ │ └── SKILL.md ├── style/ │ ├── NacosCheckStyle.xml │ ├── codeStyle.md │ ├── nacos-code-style-for-idea.xml │ └── spotbugs-exclude.xml ├── sys/ │ ├── pom.xml │ └── src/ │ ├── main/ │ │ ├── java/ │ │ │ └── com/ │ │ │ └── alibaba/ │ │ │ └── nacos/ │ │ │ └── sys/ │ │ │ ├── env/ │ │ │ │ ├── AbstractNacosDuplicateBeanPostProcessor.java │ │ │ │ ├── Constants.java │ │ │ │ ├── DeploymentType.java │ │ │ │ ├── EnvModuleStateBuilder.java │ │ │ │ ├── EnvUtil.java │ │ │ │ ├── NacosDuplicateConfigurationBeanPostProcessor.java │ │ │ │ ├── NacosDuplicateSpringBeanPostProcessor.java │ │ │ │ ├── OperatingSystemBeanManager.java │ │ │ │ └── OriginTrackedPropertiesLoader.java │ │ │ ├── file/ │ │ │ │ ├── FileChangeEvent.java │ │ │ │ ├── FileWatcher.java │ │ │ │ └── WatchFileCenter.java │ │ │ ├── filter/ │ │ │ │ ├── NacosPackageExcludeFilter.java │ │ │ │ └── NacosTypeExcludeFilter.java │ │ │ ├── module/ │ │ │ │ ├── AbstractConsoleModuleStateBuilder.java │ │ │ │ ├── AbstractServerModuleStateBuilder.java │ │ │ │ ├── ModuleState.java │ │ │ │ ├── ModuleStateBuilder.java │ │ │ │ └── ModuleStateHolder.java │ │ │ └── utils/ │ │ │ ├── ApplicationUtils.java │ │ │ ├── DiskUtils.java │ │ │ ├── InetUtils.java │ │ │ ├── MethodUtil.java │ │ │ ├── PropertiesUtil.java │ │ │ └── TimerContext.java │ │ └── resources/ │ │ └── META-INF/ │ │ ├── nacos-default.properties │ │ ├── services/ │ │ │ └── com.alibaba.nacos.sys.module.ModuleStateBuilder │ │ └── spring.factories │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── sys/ │ │ ├── env/ │ │ │ ├── EnvModuleStateBuilderTest.java │ │ │ ├── EnvUtilTest.java │ │ │ ├── EnvUtilWithConfigTest.java │ │ │ ├── EnvUtilWithoutConfigTest.java │ │ │ ├── NacosDuplicateConfigurationBeanPostProcessorTest.java │ │ │ ├── NacosDuplicateSpringBeanPostProcessorTest.java │ │ │ ├── OperatingSystemBeanManagerTest.java │ │ │ ├── OriginTrackedPropertiesLoaderTest.java │ │ │ └── mock/ │ │ │ └── MockAutoConfiguration.java │ │ ├── file/ │ │ │ └── WatchFileCenterTest.java │ │ ├── filter/ │ │ │ ├── NacosTypeExcludeFilterTest.java │ │ │ └── mock/ │ │ │ └── MockNacosPackageExcludeFilter.java │ │ ├── module/ │ │ │ ├── ModuleStateHolderTest.java │ │ │ └── mock/ │ │ │ ├── ExceptionMockModuleStateBuilder.java │ │ │ ├── MockModuleStateBuilder.java │ │ │ └── MockRebuildModuleStateBuilder.java │ │ └── utils/ │ │ ├── ApplicationUtilsTest.java │ │ ├── DiskUtilsTest.java │ │ ├── DiskUtilsZipTest.java │ │ ├── InetUtilsTest.java │ │ ├── MethodUtilTest.java │ │ ├── PropertiesUtilTest.java │ │ └── TimerContextTest.java │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ ├── com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter │ │ └── com.alibaba.nacos.sys.module.ModuleStateBuilder │ ├── application-empty.properties │ ├── application-prefix.properties │ ├── application-test.properties │ ├── application.properties │ ├── conf/ │ │ └── cluster.conf │ ├── existing-non-multi-document.properties │ ├── line_iterator_test.txt │ ├── logback-test.xml │ ├── test-file-watcher-overflow/ │ │ └── test.properties │ ├── test-properties-malformed-unicode.properties │ └── test-properties.properties └── test/ ├── config-test/ │ ├── pom.xml │ └── src/ │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── test/ │ │ ├── base/ │ │ │ ├── BaseClusterTest.java │ │ │ ├── ConfigCleanUtils.java │ │ │ ├── HttpClient4Test.java │ │ │ └── Params.java │ │ └── config/ │ │ ├── AbstractConfigAPIConfigITCase.java │ │ ├── ConfigAPIConfigITCase.java │ │ ├── ConfigAPIV2ConfigITCase.java │ │ ├── ConfigAPIWithRootContextPathConfigITCase.java │ │ ├── ConfigBetaConfigITCase.java │ │ ├── ConfigDerbyImportConfigITCase.java │ │ ├── ConfigDerbyRaftConfigITCase.java │ │ ├── ConfigExportAndImportAPIConfigITCase.java │ │ ├── ConfigLongPollConfigITCase.java │ │ ├── ConfigLongPollReturnChangesConfigITCase.java │ │ ├── EmbeddedStorageContextUtilsConfigITCase.java │ │ ├── NacosConfigServiceComTlsGrpcClientConfigITCase.java │ │ ├── NacosConfigServiceNoComTlsGrpcClientConfigITCase.java │ │ ├── NacosConfigV2MutualAuthConfigITCase.java │ │ └── TextChangeParser.java │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.api.config.listener.ConfigChangeParser │ ├── application.properties │ ├── derby-schema.sql │ ├── logback-test.xml │ ├── test-ca-cert.pem │ ├── test-ca-key.pem │ ├── test-client-cert.pem │ ├── test-client-key.pem │ ├── test-server-cert.pem │ └── test-server-key.pem ├── core-test/ │ ├── pom.xml │ └── src/ │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── test/ │ │ ├── ConfigCleanUtils.java │ │ ├── ability/ │ │ │ ├── AbilityDiscovery.java │ │ │ └── component/ │ │ │ └── TestServerAbilityControlManager.java │ │ ├── base/ │ │ │ ├── HttpClient4Test.java │ │ │ ├── Params.java │ │ │ └── TextChangeParser.java │ │ ├── client/ │ │ │ ├── ConfigIntegrationV1ServerNonCompatibilityCoreITCase.java │ │ │ ├── ConfigIntegrationV2MutualAuthCoreITCase.java │ │ │ └── ConfigIntegrationV3CoreITCase.java │ │ ├── common/ │ │ │ ├── FileTypeEnumCoreITCase.java │ │ │ ├── NacosAsyncRestTemplateCoreITCase.java │ │ │ ├── NacosRestTemplateCoreITCase.java │ │ │ ├── NacosRestTemplateInterceptorsCoreITCase.java │ │ │ └── WatchFileCenterCoreITCase.java │ │ ├── core/ │ │ │ ├── SnowFlowerIdGeneratorCoreITCase.java │ │ │ ├── auth/ │ │ │ │ ├── AuthBase.java │ │ │ │ ├── ConfigAuthCoreITCase.java │ │ │ │ ├── LdapAuthCoreITCase.java │ │ │ │ ├── NamingAuthCoreITCase.java │ │ │ │ ├── PermissionCoreITCase.java │ │ │ │ ├── RoleCoreITCase.java │ │ │ │ └── UserCoreITCase.java │ │ │ ├── cluster/ │ │ │ │ ├── MemberLookupCoreITCase.java │ │ │ │ └── ServerMemberManagerCoreITCase.java │ │ │ └── code/ │ │ │ └── ControllerMethodsCacheCoreITCase.java │ │ └── smoke/ │ │ └── NacosSmokeCoreITCase.java │ └── resources/ │ ├── META-INF/ │ │ └── services/ │ │ └── com.alibaba.nacos.api.config.listener.ConfigChangeParser │ ├── application.properties │ ├── logback-test.xml │ ├── test-ca-cert.pem │ ├── test-ca-key.pem │ ├── test-client-cert.pem │ ├── test-client-key.pem │ ├── test-server-cert.pem │ └── test-server-key.pem ├── naming-test/ │ ├── pom.xml │ └── src/ │ └── test/ │ ├── java/ │ │ └── com/ │ │ └── alibaba/ │ │ └── nacos/ │ │ └── test/ │ │ ├── base/ │ │ │ ├── BaseClusterTest.java │ │ │ ├── ConfigCleanUtils.java │ │ │ ├── HttpClient4Test.java │ │ │ └── Params.java │ │ └── naming/ │ │ ├── AbstractInstanceOperateNamingITCase.java │ │ ├── CPInstancesAPINamingITCase.java │ │ ├── ClientBeatNamingITCase.java │ │ ├── CompatibilityServiceTlsNamingITCase.java │ │ ├── InstanceOperateNamingITCase.java │ │ ├── MaintainServiceNamingITCase.java │ │ ├── MultiTenantInstanceAPINamingITCase.java │ │ ├── MultiTenantNamingITCase.java │ │ ├── NamingBase.java │ │ ├── RaftNamingITCase.java │ │ ├── RandomUtils.java │ │ ├── RestAPINamingITCase.java │ │ ├── SelectInstancesNamingITCase.java │ │ ├── SelectOneHealthyInstanceNamingITCase.java │ │ ├── ServiceListTestNamingITCase.java │ │ ├── SubscribeClusterNamingITCase.java │ │ ├── SubscribeNamingITCase.java │ │ ├── SubscribeSelectorNamingITCase.java │ │ ├── TlsServiceAndMutualAuthNamingITCase.java │ │ ├── TlsServiceTlsNamingITCase.java │ │ └── UnsubscribeNamingITCase.java │ └── resources/ │ ├── application.properties │ ├── logback-test.xml │ ├── test-ca-cert.pem │ ├── test-ca-key.pem │ ├── test-client-cert.pem │ ├── test-client-key.pem │ ├── test-server-cert.pem │ └── test-server-key.pem └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitattributes ================================================ *.js linguist-language=java *.css linguist-language=java *.html linguist-language=java ================================================ FILE: .github/ISSUE_TEMPLATE/bug-report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **Expected behavior** A clear and concise description of what you expected to happen. **Actually behavior** A clear and concise description of what you actually to happen. **How to Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See errors **Desktop (please complete the following information):** - OS: [e.g. Centos] - Version [e.g. nacos-server 1.3.1, nacos-client 1.3.1] - Module [e.g. naming/config] - SDK [e.g. original, spring-cloud-alibaba-nacos, dubbo] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Questions url: https://github.com/alibaba/nacos/discussions about: For questions and discussions. Bug reports and feature requests should use the templates above. - name: Security Vulnerabilities url: https://security.alibaba.com about: Report security vulnerabilities to ASRC (Alibaba Security Response Center). ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE.md ================================================ ## Issue Description Type: *bug report* or *feature request* ### Describe what happened (or what feature you want) ### Describe what you expected to happen ### How to reproduce it (as minimally and precisely as possible) 1. 2. 3. ### Tell us your environment ### Anything else we need to know? ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ Please do not create a Pull Request without creating an issue first. ## What is the purpose of the change XXXXX ## Brief changelog XX ## Verifying this change XXXX Follow this checklist to help us incorporate your contribution quickly and easily: * [ ] Make sure there is a Github issue filed for the change (usually before you start working on it). Trivial changes like typos do not require a Github issue. Your pull request should address just this issue, without pulling in other changes - one PR resolves one issue. * [ ] Format the pull request title like `[ISSUE #123] Fix UnknownException when host config not exist`. Each commit in the pull request should have a meaningful subject line and body. * [ ] Write a pull request description that is detailed enough to understand what the pull request does, how, and why. * [ ] Write necessary unit-test to verify your logic correction, more mock a little better when cross module dependency exist. If the new feature or significant change is committed, please remember to add integration-test in [test module](https://github.com/alibaba/nacos/tree/master/test). * [ ] Run `mvn -B clean package apache-rat:check spotbugs:check -DskipTests` to make sure basic checks pass. Run `mvn clean install -DskipITs` to make sure unit-test pass. Run `mvn clean test-compile failsafe:integration-test` to make sure integration-test pass. ================================================ FILE: .github/workflows/anti-spam.yml ================================================ name: Anti-Spam Protection on: issues: types: [opened, edited] permissions: issues: write jobs: spam-detection: runs-on: ubuntu-latest steps: - name: Check for spam uses: actions/github-script@v7 with: script: | const issue = context.payload.issue; const title = issue.title.toLowerCase(); const body = (issue.body || '').toLowerCase(); const author = issue.user.login; const authorAssociation = issue.author_association; // Skip if author is a member, collaborator, or owner if (['MEMBER', 'COLLABORATOR', 'OWNER'].includes(authorAssociation)) { console.log(`Skipping spam check for ${authorAssociation}: ${author}`); return; } // Get user details via API to calculate account age let accountAgeDays = 365; // Default to old account if API fails try { const { data: userData } = await github.rest.users.getByUsername({ username: author }); const authorCreatedAt = new Date(userData.created_at); const now = new Date(); accountAgeDays = (now - authorCreatedAt) / (1000 * 60 * 60 * 24); console.log(`Account ${author} created at ${userData.created_at}, age: ${accountAgeDays.toFixed(1)} days`); } catch (error) { console.log(`Failed to get user info for ${author}: ${error.message}`); } // Spam keywords - airlines and travel-related spam const spamKeywords = [ // Airlines 'lufthansa', 'emirates', 'klm', 'turkish airlines', 'singapore airlines', 'aer lingus', 'sas airlines', 'qatar airways', 'british airways', 'american airlines', 'united airlines', 'delta airlines', 'air france', 'swiss air', 'austrian airlines', 'tap portugal', 'air canada', 'air europa', 'ita airways', // Italian terms 'telefono', 'rimborso', 'volo', 'biglietto', 'prenotazione', 'annullare', 'cancellare', 'modifica', 'gestire', 'chiamare', 'numero di telefono', 'contattare', 'assistenza clienti', // German terms 'kontakt', 'buchen', 'kundenservice', 'hotline', 'kundendienst', 'buchung', 'stornieren', 'umbuchung', 'erreichen', 'telefonnummer', // French terms 'billet', 'réservation', 'annuler', 'rembours', 'vol', 'contacter', 'numéro', 'téléphone', 'modifier', 'payer', // Common spam patterns 'customer service number', 'booking number', 'flight cancel', 'refund process', 'how to contact', 'toll free', 'helpline', '1-800', '1-888', '1-877', '1-866' ]; // Check content for spam keywords const contentToCheck = title + ' ' + body; const matchedKeywords = spamKeywords.filter(kw => contentToCheck.includes(kw)); // Check for phone number patterns (3+ phone numbers is suspicious) const phonePatterns = [ /\+\d{1,3}[\s-]?\d{2,4}[\s-]?\d{3,4}[\s-]?\d{3,4}/g, // International format /\d{3}[\s.-]?\d{3}[\s.-]?\d{4}/g // US/common format ]; let phoneCount = 0; for (const pattern of phonePatterns) { const matches = contentToCheck.match(pattern) || []; phoneCount += matches.length; } const hasExcessivePhones = phoneCount >= 2; // Spam detection rules: // 1. Match 2+ spam keywords // 2. New account (< 7 days) + 1 spam keyword // 3. 2+ phone numbers in content const isSpam = matchedKeywords.length >= 2 || (accountAgeDays < 7 && matchedKeywords.length >= 1) || hasExcessivePhones; if (isSpam) { console.log(`Spam detected in issue #${issue.number}`); console.log(`Author: ${author}, Account age: ${accountAgeDays.toFixed(1)} days`); console.log(`Matched keywords: ${matchedKeywords.join(', ')}`); console.log(`Phone numbers found: ${phoneCount}`); // Add spam label await github.rest.issues.addLabels({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, labels: ['spam'] }); // Add comment await github.rest.issues.createComment({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, body: `This issue has been automatically detected as spam and will be closed.\n\n` + `If this is a legitimate issue, please create a new issue using the [issue template](https://github.com/${context.repo.owner}/${context.repo.repo}/issues/new/choose).\n\n` + `---\n` + `此 Issue 被自动检测为垃圾信息,将被关闭。如果这是一个合法的问题,请使用 [Issue 模板](https://github.com/${context.repo.owner}/${context.repo.repo}/issues/new/choose) 重新创建。` }); // Close issue await github.rest.issues.update({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, state: 'closed', state_reason: 'not_planned' }); // Lock issue await github.rest.issues.lock({ owner: context.repo.owner, repo: context.repo.repo, issue_number: issue.number, lock_reason: 'spam' }); console.log(`Issue #${issue.number} closed and locked as spam`); } else { console.log(`No spam detected in issue #${issue.number}`); console.log(`Matched keywords: ${matchedKeywords.length}, Phone numbers: ${phoneCount}, Account age: ${accountAgeDays.toFixed(1)} days`); } ================================================ FILE: .github/workflows/ci.yml ================================================ # This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven name: "Continuous Integration" on: push: branches: [ develop, v2.x-develop ] pull_request: branches: [ develop, v2.x-develop ] permissions: read-all jobs: ci: runs-on: ubuntu-latest timeout-minutes: 60 steps: - name: "Cache Maven Repos" uses: actions/cache@v4 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: "Checkout" uses: actions/checkout@v4 - name: "Set up JDK 17" uses: actions/setup-java@v4 with: java-version: 17 distribution: 'zulu' - name: "Print maven version" run: mvn -version - name: "Check with Maven" run: mvn -B clean compile apache-rat:check spotbugs:check -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - name: "Build and Test with Maven" run: mvn -B '-Prelease-nacos,!dev' clean install -Drat.skip=true -Dspotbugs.skip=true -DtrimStackTrace=false -U -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - name: "Upload coverage to Codecov" uses: codecov/codecov-action@v5 with: token: ${{ secrets.CODECOV_TOKEN }} fail_ci_if_error: false verbose: true files: ./core/target/site/jacoco/jacoco.xml,./plugin-default-impl/nacos-default-auth-plugin/target/site/jacoco/jacoco.xml,./plugin-default-impl/nacos-default-control-plugin/target/site/jacoco/jacoco.xml,./plugin-default-impl/nacos-default-datasource-plugin/nacos-datasource-plugin-base/target/site/jacoco/jacoco.xml,./plugin-default-impl/nacos-default-datasource-plugin/nacos-datasource-plugin-mysql/target/site/jacoco/jacoco.xml,./plugin-default-impl/nacos-default-datasource-plugin/nacos-datasource-plugin-derby/target/site/jacoco/jacoco.xml,./plugin-default-impl/nacos-default-datasource-plugin/nacos-datasource-plugin-postgresql/target/site/jacoco/jacoco.xml,./plugin-default-impl/nacos-default-datasource-plugin/nacos-datasource-plugin-oracle/target/site/jacoco/jacoco.xml,./config/target/site/jacoco/jacoco.xml,./auth/target/site/jacoco/jacoco.xml,./plugin/encryption/target/site/jacoco/jacoco.xml,./plugin/datasource/target/site/jacoco/jacoco.xml,./plugin/trace/target/site/jacoco/jacoco.xml,./plugin/config/target/site/jacoco/jacoco.xml,./plugin/auth/target/site/jacoco/jacoco.xml,./plugin/environment/target/site/jacoco/jacoco.xml,./plugin/control/target/site/jacoco/jacoco.xml,./lock/target/site/jacoco/jacoco.xml,./logger-adapter-impl/log4j2-adapter/target/site/jacoco/jacoco.xml,./logger-adapter-impl/logback-adapter-12/target/site/jacoco/jacoco.xml,./consistency/target/site/jacoco/jacoco.xml,./common/target/site/jacoco/jacoco.xml,./sys/target/site/jacoco/jacoco.xml,./ai/target/site/jacoco/jacoco.xml,./naming/target/site/jacoco/jacoco.xml,./client-basic/target/site/jacoco/jacoco.xml,./address/target/site/jacoco/jacoco.xml,./persistence/target/site/jacoco/jacoco.xml,./api/target/site/jacoco/jacoco.xml,./maintainer-client/target/site/jacoco/jacoco.xml,./prometheus/target/site/jacoco/jacoco.xml,./client/target/site/jacoco/jacoco.xml,./console/target/site/jacoco/jacoco.xml ================================================ FILE: .github/workflows/it.yml ================================================ # This workflow will build a Java project with Maven # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven name: "Integration Test For 2.X" on: push: branches: [ v2.x-develop ] pull_request: branches: [ v2.x-develop ] permissions: contents: read # to fetch code (actions/checkout) jobs: test: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] java: [8, 8.0.192, 11, 11.0.3] steps: - name: "Cache Maven Repos" uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - name: "Checkout" uses: actions/checkout@v3 - name: "Set up JDK ${{ matrix.java }}" uses: actions/setup-java@v3 with: java-version: ${{ matrix.java }} distribution: 'zulu' architecture: x64 - name: "Print maven version" run: mvn -version - name: "Test Config" run: mvn clean package -Pcit-test -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - name: "Clean Env" run: mvn clean -Premove-test-data -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - name: "Test Naming" run: mvn clean package -Pnit-test -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - name: "Clean Env" run: mvn clean -Premove-test-data -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn ================================================ FILE: .github/workflows/pr-ci.yml ================================================ name: PR-CI on: pull_request: branches: [ develop, v2.x-develop ] jobs: dist-tar: name: Build distribution tar runs-on: ubuntu-latest timeout-minutes: 120 steps: - uses: actions/checkout@v3 with: submodules: true - uses: actions/setup-java@v3 with: distribution: "temurin" java-version: "17" cache: "maven" - name: Build distribution tar run: | mvn '-Prelease-nacos,!dev' -DskipTests clean install -U -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - uses: actions/upload-artifact@v4 name: Upload distribution tar with: name: nacos path: distribution/target/nacos-server-*.tar.gz - name: Save PR number run: | mkdir -p ./pr echo ${{ github.event.pull_request.number }} > ./pr/pr.txt - uses: actions/upload-artifact@v4 name: Upload PR number with: name: pr path: pr/pr.txt ================================================ FILE: .github/workflows/pr-comment.yml ================================================ name: "Comment on PR" on: pull_request_target: types: [opened, reopened] jobs: thanks-and-hint-to-document: runs-on: ubuntu-latest permissions: pull-requests: write name: Say thanks for the PR and hint to document steps: - name: comment on the pull request uses: hasura/comment-progress@v2.3.0 with: github-token: ${{ secrets.GITHUB_TOKEN }} repository: ${{ github.repository }} number: ${{ github.event.number }} id: thanks-and-hint-to-document recreate: true message: | Thanks for your this PR. :pray: Please check again for your PR changes whether contains any usage/api/configuration change such as `Add new API `, `Add new configuration`, `Change default value of configuration`. If so, please add or update documents(markdown type) in `docs/next/` for repository [nacos-group/nacos-group.github.io](https://github.com/nacos-group/nacos-group.github.io/tree/develop-astro-nacos/src/content/docs/next) --- 感谢您提交的PR。 :pray: 请再次查看您的PR内容,确认是否包含任何使用方式/API/配置参数的变更,如:`新增API`、`新增配置参数`、`修改默认配置`等操作。 如果是,请确保在提交之前,在仓库[nacos-group/nacos-group.github.io](https://github.com/nacos-group/nacos-group.github.io/tree/develop-astro-nacos/src/content/docs/next)中的`docs/next/`目录下添加或更新文档(markdown格式)。 ================================================ FILE: .github/workflows/pr-e2e-test.yml ================================================ name: E2E test for pull request on: workflow_run: workflows: ["PR-CI"] types: - completed env: DOCKER_REPO: wuyfeedocker/nacos-ci DOCKER_REPO_B: wuyfeehub/nacos-ci TEST_REPO_NAME: nacos-group/nacos-e2e HEADER_PARAMS: "-H \"Accept: application/vnd.github+json\" -H \"Authorization: Bearer ${{ secrets.ACTION_TOKEN }}\" -H \"X-GitHub-Api-Version: 2022-11-28\"" HEADER_PARAMS_WRITE_COMMENT: "-H \"Accept: application/vnd.github+json\" -H \"Authorization: Bearer ${{ secrets.ACTION_TOKEN }}\" -H \"X-GitHub-Api-Version: 2022-11-28\"" BASE_URL_NACOS: https://api.github.com/repos/${GITHUB_REPOSITORY} BASE_URL_ROBOT: https://api.github.com/repos/wuyfee/nacos jobs: docker: runs-on: ubuntu-latest if: > github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' timeout-minutes: 30 strategy: matrix: base-image: ["ubuntu"] java-version: ["17"] outputs: version-json: ${{ steps.show_versions.outputs.version-json }} steps: - name: 'Download artifact' uses: actions/github-script@v6 with: script: | let artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{ github.event.workflow_run.id }}, }); let matchArtifactNacos = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "nacos" })[0]; let download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifactNacos.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/nacos.zip', Buffer.from(download.data)); - run: | unzip nacos.zip mkdir nacos cp -r nacos-* nacos/ - uses: actions/checkout@v3 with: repository: nacos-group/nacos-e2e.git ref: main path: nacos-e2e - name: Generate image tag id: build-images run: | mv nacos-server-*.tar.gz nacos-e2e/cicd/build cd nacos-e2e/cicd/build version=${{ github.event.pull_request.number || github.ref_name }}-$(uuidgen) mkdir versionlist touch versionlist/"${version}-`echo ${{ matrix.java-version }} | sed -e "s/:/-/g"`" ls versionlist/ echo TAG=${version}-$(echo ${{ matrix.java-version }} | sed -e "s/:/-/g") >> $GITHUB_ENV - name: docker-login-1 uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: save docker_1 images run: | mkdir build_backup cp -rf nacos-e2e/cicd/build/* ./build_backup/ cd nacos-e2e/cicd/build docker build --no-cache -f Dockerfile -t ${DOCKER_REPO}:${{ env.TAG }} . docker push ${DOCKER_REPO}:${{ env.TAG }} - name: docker-login-2 uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKERHUB_USER_B }} password: ${{ secrets.DOCKERHUB_TOKEN_B }} - name: save docker_2 images run: | rm -rf nacos-e2e/cicd/build/* mv ./build_backup/* nacos-e2e/cicd/build/ cd nacos-e2e/cicd/build docker build --no-cache -f Dockerfile -t ${DOCKER_REPO_B}:${{ env.TAG }} . docker push ${DOCKER_REPO_B}:${{ env.TAG }} - name: Show versions id: show_versions run: | a=(`ls nacos-e2e/cicd/build/versionlist`) printf '%s\n' "${a[@]}" | jq -R . | jq -s . echo version-json=`printf '%s\n' "${a[@]}" | jq -R . | jq -s .` >> $GITHUB_OUTPUT deploy: if: ${{ success() }} name: Deploy nacos needs: [docker] runs-on: ubuntu-latest timeout-minutes: 60 env: REPLICA_COUNT: 3 DATABASE: mysql NODE_PORT: 30000 AUTH_ENABLED: false ACTUAL_MODE: cluster strategy: fail-fast: false matrix: mode: ["cluster","standalone","standalone_auth"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - name: set nodeport run: | echo "NODE_PORT=$(expr $(expr $(expr $(expr ${{ strategy.job-index }} + 1) * ${{ github.run_number }}) % 30000) + 30000)" >> $GITHUB_ENV - name: set params values run: | if [[ ${{ matrix.mode }} == "standalone"* ]];then if [[ ${{ matrix.mode }} = "standalone_auth" ]]; then echo "AUTH_ENABLED=true" >> $GITHUB_ENV fi echo "ACTUAL_MODE=standalone" >> $GITHUB_ENV echo "REPLICA_COUNT=1" >> $GITHUB_ENV echo "DATABASE=embedded" >> $GITHUB_ENV echo ${{ matrix.mode }}-nacos-${{ github.run_id }}-${{ strategy.job-index }} fi - name: allocate docker repo run: | if [[ $(expr $(expr ${{ github.run_id }} + ${{ strategy.job-index }} ) % 2 ) -eq 1 ]]; then echo "DOCKER_REPO_ACTUAL=${{ env.DOCKER_REPO }}" >> $GITHUB_ENV else echo "DOCKER_REPO_ACTUAL=${{ env.DOCKER_REPO_B }}" >> $GITHUB_ENV fi - uses: apache/rocketmq-test-tool@java-dev name: Deploy nacos with: yamlString: | action: deploy namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} waitTimes: 2000 velaAppDescription: nacos-${{ env.GITHUB_WORKFLOW }}-${{ github.run_id }}@${{ matrix.version }} repoName: nacos helm: chart: ./cicd/helm git: branch: main repoType: git retries: 3 url: https://github.com/nacos-group/nacos-e2e.git values: namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} global: mode: ${{ env.ACTUAL_MODE }} nacos: replicaCount: ${{ env.REPLICA_COUNT }} image: repository: ${{ env.DOCKER_REPO_ACTUAL }} tag: ${{ matrix.version }} auth: enabled: ${{ env.AUTH_ENABLED }} storage: type: ${{ env.DATABASE }} db: port: 3306 username: nacos password: nacos param: characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false service: nodePort: ${{ env.NODE_PORT }} type: ClusterIP e2e-java-test: if: ${{ success() }} name: Java e2e Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 env: CODE_PATH: java/nacos-2X strategy: fail-fast: false matrix: mode: ["cluster","standalone","standalone_auth"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - name: set code path run: | if [[ ${{ matrix.mode }} = "standalone_auth" ]]; then echo "CODE_PATH=java/auth" >> $GITHUB_ENV echo ${{ matrix.mode }}-nacos-${{ github.run_id }}-${{ strategy.job-index }} fi - uses: apache/rocketmq-test-tool@java-dev name: java e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: ${{ env.CODE_PATH }} CMD: mvn clean test -B ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-java.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-go-test: if: ${{ success() }} name: GO E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: go e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: golang CMD: | cd /root/code/golang && go mod init nacos_go_test && go mod tidy gotestsum --junitfile ./target/surefire-reports/TEST-report.xml ./nacosgotest ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-go.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-cpp-test: if: ${{ success() }} name: Cpp E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: cpp e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: cpp CMD: | yum-config-manager remove centos-sclo-rh cd /root/code/cpp && make install echo "export LD_LIBRARY_PATH=/usr/local/lib" >> ~/.bashrc && source ~/.bashrc cd /root/code/cpp/nacoscpptest g++ nacos_test.cpp -o nacos_test -lgtest -lpthread -I/usr/local/include/nacos/ -L/usr/local/lib/ -lnacos-cli chmod 777 nacos_test && ./nacos_test --gtest_output="xml:../target/surefire-reports/TEST-gtestresults.xml" ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-cpp.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-csharp-test: if: ${{ success() }} name: Csharp E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: csharp e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: csharp CMD: | rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm yum -y install dotnet-sdk-3.1 && yum -y install aspnetcore-runtime-7.0 cd /root/code/csharp/nacos-csharp-sdk-test && dotnet restore dotnet test --logger:"junit;LogFilePath=../target/surefire-reports/TEST-result.xml" ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-csharp.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-nodejs-test: if: ${{ success() }} name: Nodejs E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: nodejs e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: nodejs CMD: | cd /root/code/nodejs/nacosnodejstest && npm install mocha test --reporter mocha-junit-reporter --reporter-options mochaFile=../target/surefire-reports/TEST-report.xml ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-nodejs.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-python-test: if: ${{ success() }} name: Python E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: python e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: python CMD: | cd /root/code/python pip3 install -r requirements.txt source ~/.bashrc cd nacospythontest && pytest --junitxml ../target/surefire-reports/TEST-report.xml test/*_test.py --log-cli-level=DEBUG ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-python.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY clean: if: ${{ always() }} name: Clean needs: [docker, e2e-java-test, e2e-go-test, e2e-cpp-test, e2e-csharp-test, e2e-nodejs-test, e2e-python-test] runs-on: ubuntu-latest timeout-minutes: 5 strategy: fail-fast: false matrix: mode: ["cluster","standalone","standalone_auth"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: clean with: yamlString: | action: clean namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} write-comment: if: ${{ always() }} name: write comment to pr needs: [docker, deploy, e2e-java-test, e2e-go-test, e2e-cpp-test, e2e-csharp-test, e2e-nodejs-test, e2e-python-test, clean] runs-on: ubuntu-latest timeout-minutes: 5 steps: - name: 'Download artifact' uses: actions/github-script@v6 with: script: | let artifacts = await github.rest.actions.listWorkflowRunArtifacts({ owner: context.repo.owner, repo: context.repo.repo, run_id: ${{ github.event.workflow_run.id }}, }); let matchArtifactPR = artifacts.data.artifacts.filter((artifact) => { return artifact.name == "pr" })[0]; let download = await github.rest.actions.downloadArtifact({ owner: context.repo.owner, repo: context.repo.repo, artifact_id: matchArtifactPR.id, archive_format: 'zip', }); var fs = require('fs'); fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data)); - name: unzip pr run: | unzip pr.zip cat pr.txt pr_number=`cat pr.txt` echo "PR_NUMBER=${pr_number}" >> $GITHUB_ENV - name: write issue comment run: | echo ${{ env.PR_NUMBER }} job_status="" if [ ${{ needs.docker.result }} = 'success' ] && [ ${{ needs.deploy.result }} = 'success' ] && [ ${{ needs.e2e-java-test.result }} = 'success' ] && [ ${{ needs.e2e-go-test.result }} = 'success' ] && [ ${{ needs.e2e-cpp-test.result }} = 'success' ] && [ ${{ needs.e2e-csharp-test.result }} = 'success' ] && [ ${{ needs.e2e-nodejs-test.result }} = 'success' ] && [ ${{ needs.e2e-python-test.result }} = 'success' ]; then jobs_status='$\\color{green}{SUCCESS}$' jobs_status+="\n[DETAILS](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${{ github.run_id }})" else jobs_status='$\\color{red}{FAILURE}$' jobs_status+="\n[DETAILS](https://github.com/${GITHUB_REPOSITORY}/actions/runs/${{ github.run_id }})" fi echo ${jobs_status} markdown_content="${jobs_status}" generate_content() { if [ $1 = "success" ];then echo "\n ✅ $2 $1 " else echo "\n ❌ $2 $1 " fi } markdown_content+=$(generate_content ${{ needs.docker.result }} "- docker: ") markdown_content+=$(generate_content ${{ needs.deploy.result }} "- deploy (standalone & cluster & standalone_auth): ") markdown_content+=$(generate_content ${{ needs.e2e-java-test.result }} "- e2e-java-test (standalone & cluster & standalone_auth): ") markdown_content+=$(generate_content ${{ needs.e2e-go-test.result }} "- e2e-go-test (standalone & cluster): ") markdown_content+=$(generate_content ${{ needs.e2e-cpp-test.result }} "- e2e-cpp-test (standalone & cluster): ") markdown_content+=$(generate_content ${{ needs.e2e-csharp-test.result }} "- e2e-csharp-test (standalone & cluster): ") markdown_content+=$(generate_content ${{ needs.e2e-nodejs-test.result }} "- e2e-nodejs-test (standalone & cluster): ") markdown_content+=$(generate_content ${{ needs.e2e-python-test.result }} "- e2e-python-test (standalone & cluster): ") markdown_content+=$(generate_content ${{ needs.clean.result }} "- clean (standalone & cluster & standalone_auth): ") echo "markdown_content: \n ${markdown_content}" payload=`echo "{\"body\": \"${markdown_content}\"}" | jq .` curl -L -X POST ${{ env.HEADER_PARAMS_WRITE_COMMENT }} ${{ env.BASE_URL_NACOS }}/issues/${{ env.PR_NUMBER }}/comments -d "${payload}" ================================================ FILE: .github/workflows/push-ci.yaml ================================================ name: PUSH-CI on: push: branches: [master, develop, v2.x-develop] permissions: contents: read concurrency: group: nacos-${{ github.ref }} env: DOCKER_REPO: wuyfeedocker/nacos-ci DOCKER_REPO_B: wuyfeehub/nacos-ci TEST_REPO_NAME: nacos-group/nacos-e2e jobs: dist-tar: name: Build dist tar runs-on: ubuntu-latest timeout-minutes: 30 steps: - uses: actions/cache@v3 with: path: ~/.m2/repository key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} restore-keys: | ${{ runner.os }}-maven- - uses: actions/checkout@v3 with: submodules: true - uses: actions/setup-java@v3 with: distribution: "temurin" java-version: "17" cache: "maven" - name: Build distribution tar run: | mvn '-Prelease-nacos,!dev' -DskipTests clean install -U -e -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn - uses: actions/upload-artifact@v4 name: Upload distribution tar with: name: nacos path: distribution/target/nacos-server-*.tar.gz docker: if: ${{ success() }} name: Docker images needs: [dist-tar] runs-on: ubuntu-latest timeout-minutes: 30 env: DOCKERHUB_USER_ACTUAL: ${{ secrets.DOCKERHUB_USER }} DOCKERHUB_TOKEN_ACTUAL: ${{ secrets.DOCKERHUB_TOKEN }} strategy: matrix: base-image: ["centos"] java-version: ["17"] outputs: version-json: ${{ steps.show_versions.outputs.version-json }} steps: - uses: actions/checkout@v3 with: repository: nacos-group/nacos-e2e.git ref: main path: nacos-e2e - uses: actions/download-artifact@v4.1.7 name: Download distribution tar with: name: nacos path: ./ - name: Generate image tag id: build-images run: | mv nacos-server-*.tar.gz nacos-e2e/cicd/build/ cd nacos-e2e/cicd/build version=${{ github.event.pull_request.number || github.ref_name }}-$(uuidgen) mkdir versionlist touch versionlist/"${version}-`echo ${{ matrix.java-version }} | sed -e "s/:/-/g"`" ls versionlist/ echo TAG=${version}-$(echo ${{ matrix.java-version }} | sed -e "s/:/-/g") >> $GITHUB_ENV - name: docker-login-1 uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKERHUB_USER }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: save docker_1 images run: | mkdir build_backup cp -rf nacos-e2e/cicd/build/* ./build_backup/ cd nacos-e2e/cicd/build docker build --no-cache -f Dockerfile -t ${DOCKER_REPO}:${{ env.TAG }} . docker push ${DOCKER_REPO}:${{ env.TAG }} - name: docker-login-2 uses: docker/login-action@v2 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.DOCKERHUB_USER_B }} password: ${{ secrets.DOCKERHUB_TOKEN_B }} - name: save docker_2 images run: | rm -rf nacos-e2e/cicd/build/* mv ./build_backup/* nacos-e2e/cicd/build/ cd nacos-e2e/cicd/build docker build --no-cache -f Dockerfile -t ${DOCKER_REPO_B}:${{ env.TAG }} . docker push ${DOCKER_REPO_B}:${{ env.TAG }} - name: Show versions id: show_versions run: | a=(`ls nacos-e2e/cicd/build/versionlist`) printf '%s\n' "${a[@]}" | jq -R . | jq -s . echo version-json=`printf '%s\n' "${a[@]}" | jq -R . | jq -s .` >> $GITHUB_OUTPUT deploy: if: ${{ success() }} name: Deploy nacos needs: [docker] runs-on: ubuntu-latest timeout-minutes: 60 env: REPLICA_COUNT: 3 DATABASE: mysql NODE_PORT: 30000 AUTH_ENABLED: false ACTUAL_MODE: cluster strategy: fail-fast: false matrix: mode: ["cluster","standalone","standalone_auth"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - name: set nodeport run: | echo "NODE_PORT=$(expr $(expr $(expr $(expr ${{ strategy.job-index }} + 1) * ${{ github.run_number }}) % 30000) + 30000)" >> $GITHUB_ENV - name: set params values run: | if [[ ${{ matrix.mode }} == "standalone"* ]];then if [[ ${{ matrix.mode }} = "standalone_auth" ]]; then echo "AUTH_ENABLED=true" >> $GITHUB_ENV fi echo "ACTUAL_MODE=standalone" >> $GITHUB_ENV echo "REPLICA_COUNT=1" >> $GITHUB_ENV echo "DATABASE=embedded" >> $GITHUB_ENV echo ${{ matrix.mode }}-nacos-${{ github.run_id }}-${{ strategy.job-index }} fi - name: allocate docker repo run: | if [[ $(expr $(expr ${{ github.run_id }} + ${{ strategy.job-index }} ) % 2 ) -eq 1 ]]; then echo "DOCKER_REPO_ACTUAL=${{ env.DOCKER_REPO }}" >> $GITHUB_ENV else echo "DOCKER_REPO_ACTUAL=${{ env.DOCKER_REPO_B }}" >> $GITHUB_ENV fi - uses: apache/rocketmq-test-tool@java-dev name: Deploy nacos with: yamlString: | action: deploy namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} waitTimes: 2000 velaAppDescription: nacos-${{ env.GITHUB_WORKFLOW }}-${{ github.run_id }}@${{ matrix.version }} repoName: nacos helm: chart: ./cicd/helm git: branch: main repoType: git retries: 3 url: https://github.com/nacos-group/nacos-e2e.git values: namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} global: mode: ${{ env.ACTUAL_MODE }} nacos: replicaCount: ${{ env.REPLICA_COUNT }} image: repository: ${{ env.DOCKER_REPO_ACTUAL }} tag: ${{ matrix.version }} auth: enabled: ${{ env.AUTH_ENABLED }} storage: type: ${{ env.DATABASE }} db: port: 3306 username: nacos password: nacos param: characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useSSL=false service: nodePort: ${{ env.NODE_PORT }} type: ClusterIP e2e-java-test: if: ${{ success() }} name: Java e2e Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 env: CODE_PATH: java/nacos-2X strategy: fail-fast: false matrix: mode: ["cluster","standalone","standalone_auth"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - name: set code path run: | if [[ ${{ matrix.mode }} = "standalone_auth" ]]; then echo "CODE_PATH=java/auth" >> $GITHUB_ENV echo ${{ matrix.mode }}-nacos-${{ github.run_id }}-${{ strategy.job-index }} fi - uses: apache/rocketmq-test-tool@java-dev name: java e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: ${{ env.CODE_PATH }} CMD: mvn clean test -B ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-java.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-go-test: if: ${{ success() }} name: GO E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: go e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: golang CMD: | cd /root/code/golang && go mod init nacos_go_test && go mod tidy gotestsum --junitfile ./target/surefire-reports/TEST-report.xml ./nacosgotest ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-go.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-cpp-test: if: ${{ success() }} name: Cpp E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: cpp e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: cpp CMD: | yum-config-manager remove centos-sclo-rh cd /root/code/cpp && make install echo "export LD_LIBRARY_PATH=/usr/local/lib" >> ~/.bashrc && source ~/.bashrc cd /root/code/cpp/nacoscpptest g++ nacos_test.cpp -o nacos_test -lgtest -lpthread -I/usr/local/include/nacos/ -L/usr/local/lib/ -lnacos-cli chmod 777 nacos_test && ./nacos_test --gtest_output="xml:../target/surefire-reports/TEST-gtestresults.xml" ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-cpp.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-csharp-test: if: ${{ success() }} name: Csharp E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: csharp e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: csharp CMD: | rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-microsoft-prod.rpm yum -y install dotnet-sdk-3.1 && yum -y install aspnetcore-runtime-7.0 cd /root/code/csharp/nacos-csharp-sdk-test && dotnet restore dotnet test --logger:"junit;LogFilePath=../target/surefire-reports/TEST-result.xml" ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-csharp.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-nodejs-test: if: ${{ success() }} name: Nodejs E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: nodejs e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: nodejs CMD: | cd /root/code/nodejs/nacosnodejstest && npm install mocha test --reporter mocha-junit-reporter --reporter-options mochaFile=../target/surefire-reports/TEST-report.xml ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-nodejs.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY e2e-python-test: if: ${{ success() }} name: Python E2E Test needs: [docker, deploy] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: python e2e test with: yamlString: | action: test namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} API_VERSION: v1 KIND: Pod RESTART_POLICY: Never ENV: WAIT_TIME: 900 REPO_NAME: ${{ env.TEST_REPO_NAME }} CODE: https://github.com/${{ env.TEST_REPO_NAME }} BRANCH: main CODE_PATH: python CMD: | cd /root/code/python && pip3 install -r requirements.txt && source ~/.bashrc cd nacospythontest && pytest --junitxml ../target/surefire-reports/TEST-report.xml test/*_test.py --log-cli-level=DEBUG ALL_IP: null CONTAINER: IMAGE: cloudnativeofalibabacloud/test-runner:v0.0.4 RESOURCE_LIMITS: cpu: 2 memory: 2Gi RESOURCE_REQUIRE: cpu: 2 memory: 2Gi - uses: actions/upload-artifact@v4 if: always() name: Upload test log with: name: testlog-${{ matrix.mode }}-python.txt path: testlog.txt - name: add markdown if: always() run: | cat result.md >> $GITHUB_STEP_SUMMARY clean: if: always() name: Clean needs: [docker, e2e-java-test, e2e-go-test, e2e-cpp-test, e2e-csharp-test, e2e-nodejs-test, e2e-python-test] runs-on: ubuntu-latest timeout-minutes: 60 strategy: fail-fast: false matrix: mode: ["cluster","standalone","standalone_auth"] version: ${{ fromJSON(needs.docker.outputs.version-json) }} steps: - uses: apache/rocketmq-test-tool@java-dev name: clean with: yamlString: | action: clean namespace: nacos-${{ github.run_id }}-${{ strategy.job-index }} askConfig: ${{ secrets.ASK_CONFIG_VIRGINA }} ================================================ FILE: .github/workflows/stale.yml ================================================ name: Close inactive issues on: schedule: - cron: "0 17 * * *" # UTC+8 01:00(UTC 17:00) jobs: close-issues: runs-on: ubuntu-latest permissions: issues: write pull-requests: write steps: - uses: actions/stale@v10 with: operations-per-run: 50 days-before-issue-stale: 7 days-before-issue-close: 7 stale-issue-label: "expired" stale-issue-message: "This issue has been open 7 days with no activity. This will be closed in 7 days." close-issue-message: "This issue was closed because it has been inactive for 7 days since being marked as expired." days-before-pr-stale: -1 days-before-pr-close: -1 exempt-all-milestones: true exempt-issue-labels: "kind/bug,kind/code quality,kind/discussion,kind/enhancement,kind/feature,kind/notice,kind/performance,kind/proposal,kind/question,kind/refactor,kind/user experience,good first issue,contribution welcome,dependencies" repo-token: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ # Except this file !.gitignore .classpath .project .settings target .idea .vscode .DS_Store .factorypath /logs *.iml *.log node_modules test/derby.log derby.log work test/logs derby.log yarn.lock .flattened-pom.xml lefthook.yml distribution/conf/*.sql distribution/plugins/*.jar ================================================ 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 # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. wrapperVersion=3.3.2 distributionType=only-script distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip ================================================ FILE: .travis.yml ================================================ notifications: email: recipients: - dev-nacos@googlegroups.com - mw_configcenter@list.alibaba-inc.com on_success: change on_failure: always language: java matrix: include: # On OSX, run with default JDK only. # - os: osx # On Linux, run with specific JDKs only. - os: linux env: CUSTOM_JDK="oraclejdk8" - name: Linux aarch64 dist: focal arch: arm64-graviton2 group: edge virt: vm jdk: - openjdk11 - openjdk8 before_install: - echo 'MAVEN_OPTS="$MAVEN_OPTS -Xmx1024m -XX:MaxPermSize=512m -XX:+BytecodeVerificationLocal"' >> ~/.mavenrc - cat ~/.mavenrc # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export JAVA_HOME=$(/usr/libexec/java_home); fi # - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then jdk_switcher use "$CUSTOM_JDK"; fi script: - mvn -B clean package apache-rat:check findbugs:findbugs -Dmaven.test.skip=true - mvn clean -Premove-test-data - mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U - mvn clean -Premove-test-data - mvn clean package -Pcit-test - mvn clean -Premove-test-data - mvn clean package -Pnit-test - mvn clean -Premove-test-data after_success: - mvn clean package -Pit-test - mvn sonar:sonar -Psonar-apache ================================================ FILE: BUILDING ================================================ Build Instructions for NACOS ==================================================== (1) Prerequisites JDK 17+ is required in order to compile and run Nacos. nacos utilizes Maven as a distribution management and packaging tool. Version 3.6.3 or later is required. The plugin org.apache.maven.plugins:maven-enforcer-plugin:3.5.0 requires Maven version 3.6.3 . Maven installation and configuration instructions can be found here: http://maven.apache.org/run-maven/index.html (2) Run test cases Execute the following command in order to compile and run test cases of each components: $ mvn test (3) Import projects to Eclipse IDE First, generate eclipse project files: $ mvn -U eclipse:eclipse Then, import to eclipse by specifying the root directory of the project via: [File] > [Import] > [Existing Projects into Workspace]. (4) Build distribution packages Execute the following command in order to build the tar.gz packages and install JAR into local repository: #build nacos $ mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U $ ./mvnw -Prelease-nacos -Dmaven.test.skip=true clean install -U ================================================ FILE: CHANGELOG.md ================================================ ## 1.0.0-RC4(Mar 22, 2019) * [#923] Nacos 1.0.0 compatible with nacos-client 0.6.2 * [#938] Client beat processor task lost * [#946] Change default server mode to AP ## 1.0.0-RC1(Mar 15, 2019) * [#870] About Nacos's namespace and tenant design * [#869] Client exception message is confusing * [#866] BeatInfo scheduled property may have the memory visibility issue * [#865] checksum value is not correct * [#839] Refactor API URLs * [#811] ApiCommands.updateIpPublish countDownLatch timeout issue * [#809] Instance field 'valid' should be deprecated and replaced by 'healthy' * [#803] Nacos front-end function regression plan and landing * [#801] Nacos uses nignx as a best practice article for current limiting. * [#757] The word 'domain' should be replaced by 'service' * [#745] Support server running mode in CP, AP or mixed * [#744] The exact status of server should be stored and controlled * [#725] Will the nacos registry be how to do multi-environment deployment? * [#677] Support ephemeral instances and persistent instances * [#651] Remove old API entry 'APICommands' * [#650] Refactor server list management to make it irrelevant to consistency protocol * [#634] Add global push enable switch and data query enable switch * [#629] Server data needs warm up before open traffic * [#502] Registering ephemeral instance as well as persistent instances * [#501] Health check mode confict when building muilt clusters whit nacos sync + nacos * [#479] Metadata should be displayed and edited using standard property syntax * [#327] Inform the ACM SDK of the RAM role name and access the configuration ? ? * [#269] need to support service group in naming module ## 0.9.0(Feb 28, 2019) * [#840] Nacos server adds startup mode to distinguish between config and naming. * [#762] Register instance returns failed when the health check mode is 'server' in standalone mode. * [#473] Nacos Cluster Mode kubernate Startup nacos.log error Log. * [#240] Log strong dependence problem. * [#824] getServicesOfServer throws exception if service list is empty. * [#802] Nacos server multi-boot mode support. * [#800] Nacos's client-to-server addressing mode document introduction. * [#768] The interval at which the heartbeat is sent in BeatReactor is not controlled by the server return value. * [#759] why instance can't auto-delete. * [#756] Format of instance and service should be validated. * [#720] Memory leak in PushService. * [#653] IoUtils under nacos-common-0.2.1-RC1.jar lacks "" judgment on encoding. * [#588] Client compatible to jdk1.6. ## 0.8.0(Jan 22, 2019) PRE-GA * [#162] Support open metrics and prometheus * [#268] Health check is performed in the Nacos startup script * [#320] Nacos supports multiple configuration files, configuration template abstraction and inheritance * [#333] Use nacos in k8s to get hostname exception * [#335] update nacos.io docker img priority/low * [#339] Project language problem identified in github * [#381] Discuss:How to support Login * [#397] Some questions for Nacos * [#402] When the configuration is added or edited, the edit box will not come out * [#462] Nacos monitor discuss (0.8 version) * [#496] Warning log printing when quering a nonexistent service * [#497] Make subscription of service triggered by getInstance method optional * [#498] Support namespace for service discovery * [#499] When the configuration is newly created (if data-id and group already exist), the original configuration will be overwritten * [#512] nacos-logs start.out always print 8848 (but port can be changed) * [#514] Nacos 0.7 not support namespace * [#523] Add a switch to control server detection and client reporting heartbeat switching * [#526] Possible data loss in server side health check mode * [#527] Many repeat client beat tasks can be generated * [#558] Enable access log recording by default * [#560] Nacos server startup issues * [#579] New API support - “update health in none health check mode through api” * [#587] Client sends request concurrently * [#592] Service restful interface put/post is reversed * [#599] getSubscribeServices method gets services that were deregistered * [#603] Format log of naming module * [#609] Always print a NPE log at start * [#663] Nacos update instance info NPE * [#668] 0.8.0-SNAPSHOT naming heartbeat not compatible with lower version client * [#672] Startup.cmd bug ## 0.7.0(Dec, 2018) * [ #461 ] Registration failed when instance port is set to 0 * [ #455 ] The console can't change the change code * [ #447 ] 集群模式server挂掉一台后,提供方注册失败 * [ #445 ] 0.6.1控制台创建配置发布提交时,提示信息有问题 * [ #442 ] Typos in class names and variables. * [ #413 ] The console has some uncaught exceptions * [ #395 ] nacos surport mysql in the case of stand-alone mode * [ #393 ] Support operation of selector on console * [ #365 ] NodeJs SDK support * [ #362 ] The metadata will lost when online or offline instance through web ui * [ #187 ] Provide Label ability for Naming Service into NACOS for complex multi-DC scenario. ## 0.6.1(Dec, 2018) * [#421] NamingService's serivce name can't use colon(:) in Windows * [#432] When packing nacos-core, ${user.home} is replaced in the logback configuration file (nacos.xml) ## 0.6.0(Dec, 2018) * [#388] Cluster name should be provided in the Instance * [#377] Clean up messy code in Naming module * [#369] Support instance list persisted on disk * [#366] findbugs-maven-plugin version * [#362] The metadata will lost when online or offline instance through web ui * [#352] Refactoring internationalization Nacos console * [#278] Nacos docker img * [#243] optimize the efficiency of integration testing, it’s taking too long now ## 0.5.0(Nov, 2018) * [#148] Naming write performace. * [#175] Support deregistering instance automatically. * [#176] Naming client query instance method should bypass local cache at client start. * [#177] Console supports registering new empty service and delete empty service. * [#181] NPE when adding an instance if no leader in the raft cluster. * [#193] Configure host domain name cause nacos server cluster is unavailable. * [#209] Disable service and cluster level customization in client registerInstance method. * [#214] Please support Java 11. * [#222] print more nacos server start status info in start.log. * [#231] Refactoring: Parsing the Nacos home directory and the cluster.conf file. * [#246] "mvn -B clean apache-rat:check findbugs:findbugs" did not work as expected. * [#251] Console Editor Optimization. * [#254] DataId and group are required in historical version and listener query. * [#256] Whether the service discovery data needs to add a newline link symbol. * [#257] Listening query switching query dimension data is not refreshed. * [#258] Remove the Balloon of DataId/Group. * [#259] Listening query paging size problem. * [#272] "#it is ip" is also parsed into an instance IP. * [#275] nacos coredns plugin to support DNS. * [#281] We should lint the console code. * [#302] Maven build project supports java 11. * [#316] In stand alone mode, Nacos still checks the cluster.conf. ## 0.4.0(Nov 7, 2018) * [#216] Fix tenant dir problem * [#197] Service update ignored some properties * [#190] Client beat lose weight info and metadata info * [#188] Console delete data cannot be updated in time * [#179] Listening query fail when namespace is not blank * [#157] Lack information in readme.md to describe the related project repositories for Nacos echosystem * [#144] There have a error and something are not clear * [#106] Snapshot file create error * [#92] Eliminate warnings, refactor code, show start.log detail ## 0.3.0(Oct 26, 2018) * [#171] UI debug errors * [#156] Web UI 404 problem * [#155] use local resource * [#145] nacos-example not found :org.apache.logging.log4j.core.Logger * [#142] UI console show Group * [#149] Fix naming client beat process failed bug. * [#150] Fix naming service registration hangs bug. ## 0.3.0-RC1(Oct 19, 2018) * [#33] Support console for config management. * [#51] Support console for naming service. * [#121] Fix get instance method hanging bug. * [#138] Add a flag to indicate if instance is offline. * [#130] Fix health check disabled if machine has one CPU core bug. * [#139] Fix still get instance with zero weight bug. * [#128] Fix console layout bug. ## 0.2.1-release(Sept 28, 2018) * Fix deregister last instance failed error. * Fix url pattern error. * Fully integrate with and seamlessly support Spring framework, Spring Boot and Spring Cloud * Separate nacos-api from nacos client implementation * Support high available cluster mode * Fix cluster node health check abnormality * Fix stand-alone mode gets the change history list exception * Fix Pulling does not exist configuration print io exception * Optimized log framework * Service Discovery: Client support getting server status. * Service Discovery: Client support get all service names of server. * Service Discovery: Client support get all subscribed services. ## 0.2.0 (Sept 17, 2018) #### FEATURES: * separate nacos-api from nacos client implementation * Cluster node health check abnormality * Stand-alone mode gets the change history list exception * Pulling does not exist configuration print io exception * Optimized log framework * Service Discovery: Client support getting server status. * Service Discovery: Client support get all service names of server. * Service Discovery: Client support get all subscribed services. #### IMPROVEMENTS: #### BUG FIXES: #### BREAKING CHANGES: ## 0.1.0 (July 18, 2018) #### FEATURES: * Creating, deleting, modifying, and querying configurations: the core functionalities. * Multiple languages support: supports Java/Shell/HTTP OpenAPI. * Service Discovery: Basic service registry and discovery. * Service Discovery: Service load balancing using instance weights, protect threshold and instance health statuses. * Service Discovery: Supports four ways for health check: http, tcp, mysql and client heartbeat. * Service Discovery: CRUD operations on service instances through Java client and open API. * Service Discovery: Service subscribtion and push through Java client. * Nacos official website is coming. https://nacos.io/ #### IMPROVEMENTS: #### BUG FIXES: #### BREAKING CHANGES: ================================================ 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, education, socio-economic status, 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 users-nacos@googlegroups.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and 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 https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to Nacos [中文版](./CONTRIBUTING_zh.md) Welcome to Nacos! This document is a guideline about how to contribute to Nacos. If you find something incorrect or missing, please leave comments / suggestions. ## Before you get started ### Code of Conduct Please make sure to read and observe our [Code of Conduct](./CODE_OF_CONDUCT.md). ## Contributing Nacos welcome new participants of any role, including user, contributor, committer and PMC. ![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/contributor_definition.png) We encourage newcomers actively joining in Nacos projects and involving from user roles to committer roles, and even PMC roles. In order to accomplish this, new comers needs to actively contribute in Nacos project. The following paragraph introduce how to contribute in Nacos way. #### Open / pickup an issue for preparation If you find a typo in a document, find a bug in code or want new features, or want to give suggestions, you can [open an issue on GitHub](https://github.com/alibaba/Nacos/issues/new) to report it. If you just want to contribute directly you can choose the issue below. - [Contribution Welcome](https://github.com/alibaba/nacos/labels/contribution%20welcome): Heavily needed issue, but currently short of hand. - [good first issue](https://github.com/alibaba/nacos/labels/good%20first%20issue): Good for newcomers, newcomers can pick up one for warm-up. We strongly value documentation and integration with other projects such as Spring Cloud, Kubernetes, Dubbo, etc. We are very glad to work on any issue for these aspects. Please note that any PR must be associated with a valid issue. Otherwise, the PR will be rejected. #### Begin your contribution Now if you want to contribute, please create a new pull request. We use the `develop` branch as the development branch, which indicates that this is an unstable branch. Furthermore, our branching model complies with [https://nvie.com/posts/a-successful-git-branching-model/](https://nvie.com/posts/a-successful-git-branching-model/). We strongly suggest new comers walk through the above article before creating PR. Now, if you are ready to create PR, here is the workflow for contributors: 1. Fork to your own 2. Clone fork to a local repository 3. Create a new branch and work on it 4. Keep your branch in sync 5. Commit your changes (make sure your commit message is concise) 6. Run pre-submission checks locally (see [Pre-submission Checks](#pre-submission-checks) below) 7. Push your commits to your forked repository 8. Create a pull request to **develop** branch. When creating pull request: 1. Please follow [the pull request template](./.github/PULL_REQUEST_TEMPLATE.md). 2. Please create the request to **develop** branch. 3. Please make sure the PR has a corresponding issue. 4. If your PR contains large changes, e.g. component refactor or new components, please write detailed documents about its design and usage. 5. Note that a single PR should not be too large. If heavy changes are required, it's better to separate the changes to a few individual PRs. 6. After creating a PR, one or more reviewers will be assigned to the pull request. 7. Before merging a PR, squash any fix review feedback, typo, merged and rebased sorts of commits. The final commit message should be clear and concise. If your PR contains large changes, e.g. component refactor or new components, please write detailed documents about its design and usage. ### License header Every new source file (`.java`, `.xml`, etc.) **must** include the Apache License 2.0 header. CI enforces this via `apache-rat:check` and your PR will fail without it. Copy the header below into every new file (adjust the comment style for non-Java files): ```java /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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. */ ``` ### Pre-submission checks Before pushing your commits, run the following command locally to catch issues early: ```bash mvn -B clean compile apache-rat:check checkstyle:check spotbugs:check -DskipTests ``` | Check | What it verifies | |-------|-----------------| | `compile` | Code compiles without errors | | `apache-rat:check` | All source files have the required Apache License header | | `checkstyle:check` | Code style complies with [Alibaba Java Coding Guidelines](style/NacosCheckStyle.xml) | | `spotbugs:check` | No high-priority bug patterns detected by [SpotBugs](https://spotbugs.github.io/) | To run unit tests: ```bash mvn clean test ``` ### Code review guidance Committers will rotate reviewing the code to make sure all the PR will be reviewed timely and by at least one committer before merge. If we aren't doing our job (sometimes we drop things). And as always, we welcome volunteers for code review. Some principles: - Readability - Important code should be well-documented. API should have Javadoc. Code style should be complied with the existing one. - Elegance: New functions, classes or components should be well-designed. - Testability - 80% of the new code should be covered by unit test cases. - Maintainability - Comply with our [PMD spec](style/codeStyle.md), and 3-month-frequency update should be maintained at least. ### Now how about try become a committer? Generally speaking, contribute 8 non-trivial patches and get at least three different people to review them (you'll need three people to support you). Then ask someone to nominate you. You're demonstrating your: - at least 8 PR and the associated issues to the project, - ability to collaborate with the team, - understanding of the projects' code base and coding style, and - ability to write good code (last but certainly not least) A current committer nominates you by slacking the team on the Nacos issue with the label "nomination" - your first and last name - a link to your Git profile - an explanation of why you should be a committer, - Elaborate on the top 3 PR and the associated issues the nominator has worked with you that can demonstrate your ability. Two other committers need to second your nomination. If no one objects in 5 working days (China), you're a committer. If anyone objects or wants more information, the committers discuss and usually come to a consensus (within the 5 working days). If issues cannot be resolved, there's a vote among current committers. ![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/nomination_process.png) In the worst case, this can drag out for two weeks. Keep contributing! Even in the rare cases where a nomination fails, the objection is usually something easy to address like "more patches" or "not enough people are familiar with this person's work." ================================================ FILE: CONTRIBUTING_zh.md ================================================ # 贡献指南 [English](./CONTRIBUTING.md) 欢迎来到 Nacos!本文档是关于如何为 Nacos 做贡献的指南。 如果您发现任何不正确或遗漏的内容,请留下意见或建议。 ## 开始之前 ### 行为准则 请务必阅读并遵守我们的[行为准则](./CODE_OF_CONDUCT.md)。 ## 参与贡献 Nacos 欢迎任何角色的新参与者,包括用户、贡献者、Committer 和 PMC。 ![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/contributor_definition.png) 我们鼓励新人积极参与 Nacos 项目,从用户角色发展到贡献者、Committer,甚至 PMC。为了实现这一目标,新人需要积极地为 Nacos 项目做贡献。以下内容介绍了如何以 Nacos 的方式进行贡献。 #### 创建或认领 Issue 如果您发现文档中的拼写错误、代码中的 bug,或者想要新功能、提出建议,可以在 [GitHub 上创建 Issue](https://github.com/alibaba/Nacos/issues/new) 进行反馈。 如果您想直接参与贡献,可以选择以下标签的 Issue: - [Contribution Welcome](https://github.com/alibaba/nacos/labels/contribution%20welcome):急需解决但人手不足的 Issue。 - [good first issue](https://github.com/alibaba/nacos/labels/good%20first%20issue):适合新手的 Issue,可以作为入门热身。 我们非常重视文档编写以及与其他项目(如 Spring Cloud、Kubernetes、Dubbo 等)的集成。我们很乐意处理这些方面的任何 Issue。 请注意,每个 PR 必须关联一个有效的 Issue,否则 PR 将被拒绝。 #### 开始贡献 如果您准备开始贡献,请创建一个新的 Pull Request。 我们使用 `develop` 分支作为开发分支,这是一个不稳定的分支。 此外,我们的分支模型遵循 [Git Flow](https://nvie.com/posts/a-successful-git-branching-model/)。我们强烈建议新人在创建 PR 之前先阅读上述文章。 以下是贡献者的工作流程: 1. Fork 仓库到自己的账号下 2. 将 Fork 克隆到本地 3. 创建新分支并在上面开发 4. 保持分支与上游同步 5. 提交更改(确保 commit message 简洁明了) 6. 在本地运行提交前检查(参见下方[提交前检查](#提交前检查)) 7. 将提交推送到您的 Fork 仓库 8. 向 **develop** 分支创建 Pull Request 创建 Pull Request 时: 1. 请遵循 [PR 模板](./.github/PULL_REQUEST_TEMPLATE.md)。 2. 请将 PR 提交到 **develop** 分支。 3. 请确保 PR 关联了对应的 Issue。 4. 如果 PR 包含较大的改动(如组件重构或新组件),请编写详细的设计和使用文档。 5. 注意单个 PR 不要过大。如果需要大量改动,最好将其拆分为多个独立的 PR。 6. 创建 PR 后,一名或多名审核者会被分配到该 PR。 7. 合并前,请将修复审查意见、拼写错误、合并和变基等提交压缩为有意义的提交。最终的 commit message 应当清晰简洁。 如果 PR 包含较大的改动(如组件重构或新组件),请编写详细的设计和使用文档。 ### License 头 每个新的源文件(`.java`、`.xml` 等)**必须**包含 Apache License 2.0 头。CI 会通过 `apache-rat:check` 自动检查,缺少 License 头的 PR 将无法通过。 请将以下头信息复制到每个新文件中(非 Java 文件请调整注释风格): ```java /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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. */ ``` ### 提交前检查 在推送代码之前,请在本地运行以下命令以尽早发现问题: ```bash mvn -B clean compile apache-rat:check checkstyle:check spotbugs:check -DskipTests ``` | 检查项 | 说明 | |-------|------| | `compile` | 代码是否能正常编译 | | `apache-rat:check` | 所有源文件是否包含 Apache License 头 | | `checkstyle:check` | 代码风格是否符合[阿里巴巴 Java 开发规约](style/NacosCheckStyle.xml) | | `spotbugs:check` | 是否存在 [SpotBugs](https://spotbugs.github.io/) 检测到的高优先级 bug | 运行单元测试: ```bash mvn clean test ``` ### 代码审查指南 Committer 会轮流审查代码,确保所有 PR 在合并前至少经过一名 Committer 的及时审核。如果我们有所疏漏,欢迎随时提醒。同时,我们也欢迎志愿者参与代码审查。 一些原则: - 可读性 - 重要的代码应有完善的文档。API 应有 Javadoc。代码风格应与现有代码保持一致。 - 优雅性 - 新的函数、类或组件应当设计良好。 - 可测试性 - 新代码应有 80% 的单元测试覆盖率。 - 可维护性 - 遵守我们的[代码规范](style/codeStyle.md)。 ### 如何成为 Committer? 一般来说,需要贡献 8 个非琐碎的补丁,并获得至少三个不同的人来审核(您需要三个人的支持)。然后请人提名您。您需要展示: - 至少为项目贡献了 8 个 PR 和对应的 Issue - 能够与团队协作 - 了解项目代码库和编码风格 - 能够编写高质量的代码 Committer 通过在带有 "nomination" 标签的 Nacos Issue 中通知团队来提名您,需要包含: - 您的姓名 - 您的 Git 主页链接 - 解释您为何应该成为 Committer - 详细说明提名者与您合作过的前 3 个 PR 和对应 Issue,以证明您的能力 需要另外两名 Committer 附议您的提名。如果 5 个工作日(中国时间)内没有人反对,您就是 Committer 了。如果有人反对或需要更多信息,Committer 们会进行讨论并通常在 5 个工作日内达成共识。如果问题无法解决,将在现有 Committer 中进行投票。 ![](http://acm-public.oss-cn-hangzhou.aliyuncs.com/nomination_process.png) 在最坏的情况下,这个过程可能会持续两周。请继续贡献!即使在提名失败的罕见情况下,反对意见通常也是容易解决的,比如"需要更多补丁"或"没有足够的人熟悉此人的工作"。 ================================================ 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. ================================================ FILE: NOTICE ================================================ Nacos Copyright 2018-2020 This product includes software developed at The Alibaba MiddleWare Group. ------ This product has a bundle Spring Boot: The Spring Boot Project ================= Please visit the Spring Boot web site for more information: * https://spring.io/projects/spring-boot Copyright 2014 The Spring Boot Project The Spring Boot 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. Also, please refer to each LICENSE..txt file, which is located in the 'license' directory of the distribution file, for the license terms of the components that this product depends on. ------ This product has a bundle Spring Framework: Spring Framework ================= Please visit the git for more information: * https://github.com/spring-projects/spring-framework.git Copyright 2002-2020 the original author or authors. The Spring Framework licenses this file to you under the Apache License, version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Also, please refer to each LICENSE..txt file, which is located in the 'license' directory of the distribution file, for the license terms of the components that this product depends on. ------ com.alibaba.nacos.common.utils.InetAddressValidator.java in this product is copied from com.dynatrace.openkit.core.util.InetAddressValidator.java of openkit-java project. https://github.com/Dynatrace/openkit-java openkit-java ====================== Copyright 2018-2021 Dynatrace LLC 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. ------ This product has a bundle Protocol Buffers�� Protocol Buffers ======================= Protocol Buffers for Go with Gadgets Copyright (c) 2013, The GoGo Authors. All rights reserved. http://github.com/gogo/protobuf ------ This product has a bundle Istio�� Istio ======================= Copyright 2018 Istio 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. ================================================ FILE: README.md ================================================ # Nacos: Dynamic *Na*ming and *Co*nfiguration *S*ervice [![Gitter](https://badges.gitter.im/alibaba/nacos.svg)](https://gitter.im/alibaba/nacos?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) [![Gitter](https://travis-ci.org/alibaba/nacos.svg?branch=master)](https://travis-ci.org/alibaba/nacos) [![](https://img.shields.io/badge/Nacos-Check%20Your%20Contribution-orange)](https://opensource.alibaba.com/contribution_leaderboard/details?projectValue=nacos) [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/alibaba/nacos) ------- ## What does it do Nacos (official site: [nacos.io](https://nacos.io)) is an easy-to-use platform designed for dynamic service discovery and configuration and service management. It helps you to build cloud native applications and microservices platform easily. Service is a first-class citizen in Nacos. Nacos supports almost all type of services,for example,[Dubbo/gRPC service](https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/), [Spring Cloud RESTFul service](https://nacos.io/docs/latest/ecology/use-nacos-with-spring-cloud/) or [Kubernetes service](https://nacos.io/docs/latest/quickstart/quick-start-kubernetes/). Nacos provides four major functions. * **Service Discovery and Service Health Check** Nacos makes it simple for services to register themselves and to discover other services via a DNS or HTTP interface. Nacos also provides real-time health checks of services to prevent sending requests to unhealthy hosts or service instances. * **Dynamic Configuration Management** Dynamic Configuration Service allows you to manage configurations of all services in a centralized and dynamic manner across all environments. Nacos eliminates the need to redeploy applications and services when configurations are updated, which makes configuration changes more efficient and agile. * **Dynamic DNS Service** Nacos supports weighted routing, making it easier for you to implement mid-tier load balancing, flexible routing policies, flow control, and simple DNS resolution services in the production environment within your data center. It helps you to implement DNS-based service discovery easily and prevent applications from coupling to vendor-specific service discovery APIs. * **Service and MetaData Management** Nacos provides an easy-to-use service dashboard to help you manage your services metadata, configuration, kubernetes DNS, service health and metrics statistics. ## Quick Start It is super easy to get started with your first project. ### Deploying Nacos on cloud You can deploy Nacos on cloud, which is the easiest and most convenient way to start Nacos. Use the following [Nacos deployment guide](https://cn.aliyun.com/product/aliware/mse?spm=nacos-website.topbar.0.0.0) to see more information and deploy a stable and out-of-the-box Nacos server. ### Start by the provided startup package #### Step 1: Download the binary package You can download the package from the [latest stable release](https://github.com/alibaba/nacos/releases). Take release `nacos-server-1.0.0.zip` for example: ```sh unzip nacos-server-1.0.0.zip cd nacos/bin ``` #### Step 2: Start Server On the **Linux/Unix/Mac** platform, run the following command to start server with standalone mode: ```sh sh startup.sh -m standalone ``` On the **Windows** platform, run the following command to start server with standalone mode. Alternatively, you can also double-click the `startup.cmd` to run NacosServer. ``` startup.cmd -m standalone ``` For more details, see [quick-start.](https://nacos.io/docs/latest/quickstart/quick-start/) ## Quick start for other open-source projects: * [Quick start with Nacos command and console](https://nacos.io/docs/latest/quickstart/quick-start/) * [Quick start with dubbo](https://nacos.io/docs/latest/ecology/use-nacos-with-dubbo/) * [Quick start with spring cloud](https://nacos.io/docs/latest/ecology/use-nacos-with-spring-cloud/) * [Quick start with kubernetes](https://nacos.io/docs/latest/quickstart/quick-start-kubernetes/) ## Documentation You can view the full documentation from the [Nacos website](https://nacos.io/docs/latest/overview/). You can also read this online eBook from the [NACOS ARCHITECTURE & PRINCIPLES](https://nacos.io/docs/ebook/kbyo6n/). All the latest and long-term notice can also be found here from [GitHub notice issue](https://github.com/alibaba/nacos/labels/notice). ## Contributing Contributors are welcomed to join Nacos project. Please check [CONTRIBUTING](./CONTRIBUTING.md) about how to contribute to this project. ### How can I contribute? * Take a look at issues with tags marked [`good first issue`](https://github.com/alibaba/nacos/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) or [`contribution welcome`](https://github.com/alibaba/nacos/issues?q=is%3Aopen+is%3Aissue+label%3A%22contribution+welcome%22). * Answer questions on [issues](https://github.com/alibaba/nacos/issues). * Fix bugs reported on [issues](https://github.com/alibaba/nacos/issues), and send us a pull request. * Review the existing [pull request](https://github.com/alibaba/nacos/pulls). * Improve the [website](https://github.com/nacos-group/nacos-group.github.io), typically we need * blog post * translation on documentation * use cases around the integration of Nacos in enterprise systems. ## Other Related Project Repositories * [nacos-spring-project](https://github.com/nacos-group/nacos-spring-project) provides the integration functionality for Spring. * [nacos-group](https://github.com/nacos-group) is the repository that hosts the eco tools for Nacos, such as SDK, synchronization tool, etc. * [spring-cloud-alibaba](https://github.com/spring-cloud-incubator/spring-cloud-alibaba) provides the one-stop solution for application development over Alibaba middleware which includes Nacos. ## Contact * [Gitter](https://gitter.im/alibaba/nacos): Nacos's IM tool for community messaging, collaboration and discovery. * [Twitter](https://twitter.com/nacos2): Follow along for latest nacos news on Twitter. * [Weibo](https://weibo.com/u/6574374908): Follow along for latest nacos news on Weibo (Twitter of China version). * [Nacos Segmentfault](https://segmentfault.com/t/nacos): Get latest notice and prompt help from Segmentfault. * Email Group: * users-nacos@googlegroups.com: Nacos usage general discussion. * dev-nacos@googlegroups.com: Nacos developer discussion (APIs, feature design, etc). * commits-nacos@googlegroups.com: Commits notice, very high frequency. * Join us from DingDing(Group 1: 21708933(full), Group 2: 30438813(full), Group 3: 31222241(full), Group 4: 12810027056). ### DingDing Group QR Code ![](https://cdn.nlark.com/yuque/0/2025/png/1577777/1750054497446-f834cba6-fa83-4421-b202-a0dc1d5cc28b.png) ### DingDing MCP Group QR Code ![](https://cdn.nlark.com/yuque/0/2025/png/1577777/1750054500395-e271cbe4-2dd8-4723-8cd0-bd8a731b812a.png) ### WeChat Group QR Code ![](https://cdn.nlark.com/yuque/0/2025/png/1577777/1750054421702-a7d1421a-ab8e-42da-bc59-01b5d287b290.png) ## Enterprise Service If you need Nacos enterprise service support, or purchase cloud product services, you can join the discussion by scanning the following DingTalk group. It can also be directly activated and used through the microservice engine (MSE) provided by Alibaba Cloud. https://cn.aliyun.com/product/aliware/mse?spm=nacos-website.topbar.0.0.0 ## Download - [Nacos Official Website](https://nacos.io/download/nacos-server) - [GitHub Release](https://github.com/alibaba/nacos/releases) ## Who is using These are only part of the companies using Nacos, for reference only. If you are using Nacos, please [add your company here](https://github.com/alibaba/nacos/issues/273) to tell us your scenario to make Nacos better.
Alibaba Group 虎牙直播 ICBC 爱奇艺
平安科技 华夏信财 优客工场 贝壳找房
瑞安农村商业银行 司法大数据 搜易贷 平行云
甘肃紫光 海云天 Acmedcare+ 北京天合互联信息有限公司
上海密尔克卫化工 大连新唯 立思辰 东家
上海克垚 联采科技 南京28研究所 凤凰网-汽车
中化信息 一点车 明传无线 妙优车
蜂巢 华存数据 数云 广通软件
菜菜 科蓝公司 浩鲸 未名天日语
金联创 同窗链 顺能 百世快递
汽车之家 鲸打卡 时代光华 康美
环球易购 Nepxion chigua 宅无限
天阙 联合永道 明源云 DaoCloud
美菜 松格科技 集萃智能 吾享
拓深科技 长亮科技 深圳易停车库 武汉日创科技
易管智能 云帐房 三诺生物
郑州山水 知氏教育
================================================ FILE: REPORTING-BUGS.md ================================================ # How to report bugs If any part of the Nacos project has bugs or documentation mistakes, please let us know by [opening an issue][Nacos-issue]. We treat bugs and mistakes very seriously and believe no issue is too small, anyone is implement. Before creating a bug report, please check that an issue reporting the same problem does not already exist. To make the bug report accurate and easy to understand, please try to create bug reports that are: - Specific. Include as many details as possible: which version, what environment, what configuration, etc. If the bug is related to running the Nacos server, please attach the Nacos log (the starting log with Nacos configuration is especially important). - Reproducible. Include the steps to reproduce the problem. We understand some issues might be hard to reproduce, please includes the steps that might lead to the problem. If possible, please attach the affected Nacos data dir and stack strace to the bug report. - Unique. Do not duplicate the existing bug report. It may be worthwhile to read [Elika Etemad’s article on filing good bug reports][filing-good-bugs] before creating a bug report. We might ask for further information to locate a bug. A duplicated bug report will be closed. [etcd-issue]: https://github.com/etcd-io/etcd/issues/new [filing-good-bugs]: http://fantasai.inkedblade.net/style/talks/filing-good-bugs/ # 如何提交错误报告 如果Nacos项目的任何部分存在问题或文档问题,请通过[opening an issue][Nacos-issue]告诉我们。我们非常认真地对待错误和缺陷,在产品面前没有不重要的问题。不过在创建错误报告之前,请检查是否存在报告相同问题的issues。 为了使错误报告准确且易于理解,请尝试创建以下错误报告: - 具体到细节。包括尽可能多的细节:哪个版本,什么环境,什么配置等。如果错误与运行Nacos服务器有关,请附加Nacos日志(具有Nacos配置的起始日志尤为重要)。 - 可复现。包括重现问题的步骤。我们理解某些问题可能难以重现,请包括可能导致问题的步骤。如果可能,请将受影响的Nacos数据目录和堆栈strace附加到错误报告中。 - 不重复。不要复制现有的错误报告。 在创建错误报告之前,最好阅读下[Elika Etemad关于提交好错误报告的文章] [归档好错误],相信 会给你启发。 我们可能会要求您提供更多信息以查找错误。将关闭重复的错误报告。 [etcd-issue]:https://github.com/etcd-io/etcd/issues/new [filing-good-bugs]:http://fantasai.inkedblade.net/style/talks/filing-good-bugs/ ================================================ FILE: address/pom.xml ================================================ nacos-all com.alibaba.nacos ${revision} 4.0.0 nacos-address jar nacos-address ${project.version} https://nacos.io ${project.groupId} nacos-naming ${project.groupId} nacos-cmdb org.springframework spring-test test src/main/resources com.github.spotbugs spotbugs-maven-plugin release-address nacos-address maven-jar-plugin true true org.springframework.boot spring-boot-maven-plugin com.alibaba.nacos.address.AddressServer repackage ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/AddressServer.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * support address server. * * @author nacos * @since 1.1.0 */ @SpringBootApplication(scanBasePackages = "com.alibaba.nacos") public class AddressServer { public static void main(String[] args) { SpringApplication.run(AddressServer.class, args); } } ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/component/AddressServerGeneratorManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.component; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * will generator some result by the input parameter. * * @author pbting * @date 2019-07-01 8:53 PM * @since 1.1.0 */ @Component public class AddressServerGeneratorManager { /** * Generate product name. * * @param name name * @return product */ public String generateProductName(String name) { if (StringUtils.isBlank(name) || AddressServerConstants.DEFAULT_PRODUCT.equals(name)) { return AddressServerConstants.ALIWARE_NACOS_DEFAULT_PRODUCT_NAME; } return String.format(AddressServerConstants.ALIWARE_NACOS_PRODUCT_DOM_TEMPLATE, name); } /** * Note: if the parameter inputted is empty then will return the empty list. * * @param serviceName service name * @param clusterName cluster name * @param ipArray array of ips * @return instance list */ public List generateInstancesByIps(String serviceName, String rawProductName, String clusterName, String[] ipArray) { if (StringUtils.isEmpty(serviceName) || StringUtils.isEmpty(clusterName) || ipArray == null || ipArray.length == 0) { return Collections.emptyList(); } List instanceList = new ArrayList<>(ipArray.length); for (String ip : ipArray) { String[] ipAndPort = generateIpAndPort(ip); Instance instance = new Instance(); instance.setIp(ipAndPort[0]); instance.setPort(Integer.parseInt(ipAndPort[1])); instance.setClusterName(clusterName); instance.setServiceName(serviceName); instance.setEphemeral(false); instance.getMetadata().put("app", rawProductName); instance.getMetadata().put("tenant", Constants.DEFAULT_NAMESPACE_ID); instanceList.add(instance); } return instanceList; } private String[] generateIpAndPort(String ip) { String[] result = InternetAddressUtil.splitIpPortStr(ip); if (result.length != InternetAddressUtil.SPLIT_IP_PORT_RESULT_LENGTH) { return new String[] {result[0], String.valueOf(AddressServerConstants.DEFAULT_SERVER_PORT)}; } return result; } /** * Generate response ips. * * @param instanceList an instance set will generate string response to client. * @return the result of response to client */ public String generateResponseIps(List instanceList) { StringBuilder ips = new StringBuilder(); instanceList.forEach(instance -> { ips.append(instance.getIp()).append(':').append(instance.getPort()); ips.append('\n'); }); return ips.toString(); } /** * Generate nacos service name. * * @param rawServiceName the raw service name will not contain the {@link Constants#DEFAULT_GROUP}. * @return the nacos service name */ public String generateNacosServiceName(String rawServiceName) { if (rawServiceName.contains(Constants.DEFAULT_GROUP)) { return rawServiceName; } return Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + rawServiceName; } } ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/component/AddressServerManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.component; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.stereotype.Component; /** * This class holds the IP list of the CAI's address service. * * @author deshao * @date 2016/4/28 20:58 * @since 1.1.0 */ @Component public class AddressServerManager { public String getRawProductName(String name) { if (StringUtils.isBlank(name)) { return AddressServerConstants.DEFAULT_PRODUCT; } return name; } /** * If the name is empty then return the default {@link UtilsAndCommons#DEFAULT_CLUSTER_NAME}, or return the source * name by input. * * @param name name * @return default cluster name */ public String getDefaultClusterNameIfEmpty(String name) { if (StringUtils.isEmpty(name)) { return AddressServerConstants.DEFAULT_GET_CLUSTER; } return name; } public String getRawClusterName(String name) { return getDefaultClusterNameIfEmpty(name); } /** * Split ips. * * @param ips multi ip will separator by the ',' * @return array of ip */ public String[] splitIps(String ips) { if (StringUtils.isBlank(ips)) { return new String[0]; } return ips.split(AddressServerConstants.MULTI_IPS_SEPARATOR); } } ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/config/AddressServerSecurityConfiguration.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.Customizer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; /** * nacos web security configuration. * * @author onewe */ @Configuration public class AddressServerSecurityConfiguration { @Bean @Order(99) public SecurityFilterChain addressServerSecurityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests( requestMatcherRegistry -> requestMatcherRegistry .requestMatchers("/nacos/v1/as/**").authenticated()) .csrf(AbstractHttpConfigurer::disable) .httpBasic(Customizer.withDefaults()); return http.build(); } } ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/constant/AddressServerConstants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.constant; import com.alibaba.nacos.naming.misc.UtilsAndCommons; /** * Uniform constant parameter naming for address servers and default values ​​for related parameters. * * @author pbting * @date 2019-06-17 7:23 PM * @since 1.1.0 */ public interface AddressServerConstants { /** * the default server port when create the Instance object. */ int DEFAULT_SERVER_PORT = 8848; /** * when post ips is not given the product,then use the default. */ String DEFAULT_PRODUCT = "nacos"; /** * the separator for service name between raw service name and group. */ String GROUP_SERVICE_NAME_SEP = "@@"; /** * when post ips is not given the cluster,then use the default. */ String DEFAULT_GET_CLUSTER = "serverlist"; /** * post multi ip will use the "," to separator. */ String MULTI_IPS_SEPARATOR = ","; /** * the default product name when deploy nacos with naming and config. */ String ALIWARE_NACOS_DEFAULT_PRODUCT_NAME = "nacos.as.default"; /** * when the config and naming will separate deploy,then must specify product name by the client. */ String ALIWARE_NACOS_PRODUCT_DOM_TEMPLATE = "nacos.as.%s"; /** * the url for address server prefix. */ String ADDRESS_SERVER_REQUEST_URL = UtilsAndCommons.NACOS_SERVER_CONTEXT + UtilsAndCommons.NACOS_SERVER_VERSION + "/as"; } ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/controller/AddressServerClusterController.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.controller; import com.alibaba.nacos.address.component.AddressServerGeneratorManager; import com.alibaba.nacos.address.component.AddressServerManager; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.address.misc.Loggers; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.naming.core.ClusterOperator; import com.alibaba.nacos.naming.core.InstanceOperator; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Optional; /** * Address server cluster controller. * * @author pbting * @since 1.1.0 */ @RestController @RequestMapping({AddressServerConstants.ADDRESS_SERVER_REQUEST_URL + "/nodes"}) public class AddressServerClusterController { private final InstanceOperator instanceOperator; private final NamingMetadataManager metadataManager; private final ClusterOperator clusterOperator; private final AddressServerManager addressServerManager; private final AddressServerGeneratorManager addressServerGeneratorManager; public AddressServerClusterController(InstanceOperator instanceOperator, NamingMetadataManager metadataManager, ClusterOperator clusterOperator, AddressServerManager addressServerManager, AddressServerGeneratorManager addressServerGeneratorManager) { this.instanceOperator = instanceOperator; this.metadataManager = metadataManager; this.clusterOperator = clusterOperator; this.addressServerManager = addressServerManager; this.addressServerGeneratorManager = addressServerGeneratorManager; } /** * Create new cluster. * * @param product Ip list of products to be associated * @param cluster Ip list of product cluster to be associated * @param ips will post ip list. * @return result of create new cluster */ @RequestMapping(value = "", method = RequestMethod.POST) public ResponseEntity postCluster(@RequestParam(required = false) String product, @RequestParam(required = false) String cluster, @RequestParam(name = "ips") String ips) { //1. prepare the storage name for product and cluster String productName = addressServerGeneratorManager.generateProductName(product); String clusterName = addressServerManager.getDefaultClusterNameIfEmpty(cluster); //2. prepare the response name for product and cluster to client String rawProductName = addressServerManager.getRawProductName(product); String rawClusterName = addressServerManager.getRawClusterName(cluster); Loggers.ADDRESS_LOGGER.info("put cluster node,the cluster name is " + cluster + "; the product name=" + product + "; the ip list=" + ips); ResponseEntity responseEntity; try { String serviceName = addressServerGeneratorManager.generateNacosServiceName(productName); Result result = registerCluster(serviceName, rawProductName, clusterName, ips); if (InternetAddressUtil.checkOk(result.getCheckResult())) { responseEntity = ResponseEntity .ok("product=" + rawProductName + ",cluster=" + rawClusterName + "; put success with size=" + result.getSize()); } else { responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(result.getCheckResult()); } } catch (Exception e) { responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getMessage()); } return responseEntity; } private Result registerCluster(String serviceName, String productName, String clusterName, String ips) throws NacosException { String serviceWithoutGroup = NamingUtils.getServiceName(serviceName); String groupName = NamingUtils.getGroupName(serviceName); Service service = Service.newService(Constants.DEFAULT_NAMESPACE_ID, groupName, serviceWithoutGroup, false); service = ServiceManager.getInstance().getSingleton(service); if (service.isEphemeral()) { return new Result( String.format("Service %s is ephemeral service, can't use as address server", serviceName), 0); } ServiceMetadata serviceMetadata = metadataManager.getServiceMetadata(service).orElse(new ServiceMetadata()); if (!serviceMetadata.getClusters().containsKey(clusterName)) { ClusterMetadata metadata = new ClusterMetadata(); metadata.setHealthyCheckType(AbstractHealthChecker.None.TYPE); metadata.setHealthChecker(new AbstractHealthChecker.None()); clusterOperator.updateClusterMetadata(Constants.DEFAULT_NAMESPACE_ID, serviceName, clusterName, metadata); } String[] ipArray = addressServerManager.splitIps(ips); String checkResult = InternetAddressUtil.checkIps(ipArray); if (InternetAddressUtil.checkOk(checkResult)) { List instanceList = addressServerGeneratorManager .generateInstancesByIps(serviceName, productName, clusterName, ipArray); for (Instance instance : instanceList) { instanceOperator.registerInstance(Constants.DEFAULT_NAMESPACE_ID, serviceName, instance); } } return new Result(checkResult, ipArray.length); } /** * Delete cluster. * * @param product Ip list of products to be associated * @param cluster Ip list of product cluster to be associated * @param ips will delete ips. * @return delete result (the cluster information is return if success, exception information is return if fail) */ @RequestMapping(value = "", method = RequestMethod.DELETE) public ResponseEntity deleteCluster(@RequestParam(required = false) String product, @RequestParam(required = false) String cluster, @RequestParam String ips) { //1. prepare the storage name for product and cluster String productName = addressServerGeneratorManager.generateProductName(product); String clusterName = addressServerManager.getDefaultClusterNameIfEmpty(cluster); //2. prepare the response name for product and cluster to client String rawProductName = addressServerManager.getRawProductName(product); String rawClusterName = addressServerManager.getRawClusterName(cluster); ResponseEntity responseEntity = ResponseEntity.status(HttpStatus.OK) .body("product=" + rawProductName + ", cluster=" + rawClusterName + " delete success."); try { String serviceName = addressServerGeneratorManager.generateNacosServiceName(productName); String serviceWithoutGroup = NamingUtils.getServiceName(serviceName); String groupName = NamingUtils.getGroupName(serviceName); Optional service = com.alibaba.nacos.naming.core.v2.ServiceManager .getInstance().getSingletonIfExist(Constants.DEFAULT_NAMESPACE_ID, groupName, serviceWithoutGroup); if (!service.isPresent()) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("product=" + rawProductName + " not found."); } if (StringUtils.isBlank(ips)) { // delete all ips from the cluster return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("ips must not be empty."); } // delete specified ip list String[] ipArray = addressServerManager.splitIps(ips); String checkResult = InternetAddressUtil.checkIps(ipArray); if (InternetAddressUtil.checkOk(checkResult)) { List instanceList = addressServerGeneratorManager .generateInstancesByIps(serviceName, rawProductName, clusterName, ipArray); for (Instance each : instanceList) { instanceOperator.removeInstance(Constants.DEFAULT_NAMESPACE_ID, serviceName, each); } } else { responseEntity = ResponseEntity.status(HttpStatus.BAD_REQUEST).body(checkResult); } } catch (Exception e) { responseEntity = ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(e.getCause()); } return responseEntity; } private class Result { private final String checkResult; private final int size; public Result(String checkResult, int size) { this.checkResult = checkResult; this.size = size; } public String getCheckResult() { return checkResult; } public int getSize() { return size; } } } ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/controller/ServerListController.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.controller; import com.alibaba.nacos.address.component.AddressServerGeneratorManager; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.utils.ServiceUtil; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.Optional; /** * Server list controller. * * @author pbting * @since 1.1.0 */ @RestController public class ServerListController { private final AddressServerGeneratorManager addressServerBuilderManager; private final NamingMetadataManager metadataManager; private final ServiceStorage serviceStorage; public ServerListController(AddressServerGeneratorManager addressServerBuilderManager, NamingMetadataManager metadataManager, ServiceStorage serviceStorage) { this.addressServerBuilderManager = addressServerBuilderManager; this.metadataManager = metadataManager; this.serviceStorage = serviceStorage; } /** * Get cluster. * * @param product will get Ip list of that products to be associated * @param cluster will get Ip list of that product cluster to be associated * @return result of get */ @RequestMapping(value = "/{product}/{cluster}", method = RequestMethod.GET) public ResponseEntity getCluster(@PathVariable String product, @PathVariable String cluster) { String productName = addressServerBuilderManager.generateProductName(product); String serviceName = addressServerBuilderManager.generateNacosServiceName(productName); String serviceWithoutGroup = NamingUtils.getServiceName(serviceName); String groupName = NamingUtils.getGroupName(serviceName); Optional service = ServiceManager.getInstance() .getSingletonIfExist(Constants.DEFAULT_NAMESPACE_ID, groupName, serviceWithoutGroup); if (!service.isPresent()) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("product=" + product + " not found."); } ClusterMetadata metadata = metadataManager.getServiceMetadata(service.get()).orElse(new ServiceMetadata()) .getClusters().get(cluster); if (null == metadata) { return ResponseEntity.status(HttpStatus.NOT_FOUND) .body("product=" + product + ",cluster=" + cluster + " not found."); } ServiceInfo serviceInfo = serviceStorage.getData(service.get()); serviceInfo = ServiceUtil.selectInstances(serviceInfo, cluster, false); return ResponseEntity.status(HttpStatus.OK) .body(addressServerBuilderManager.generateResponseIps(serviceInfo.getHosts())); } } ================================================ FILE: address/src/main/java/com/alibaba/nacos/address/misc/Loggers.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.misc; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Loggers holder. * * @author pbting * @date 2019-07-04 4:34 PM */ public class Loggers { public static final Logger ADDRESS_LOGGER = LoggerFactory.getLogger("com.alibaba.nacos.address.main"); } ================================================ FILE: address/src/main/resources/META-INF/logback/nacos-included.xml ================================================ ${LOG_HOME}/nacos-address.log true ${LOG_HOME}/nacos-address.log.%d{yyyy-MM-dd}.%i 2GB 15 7GB true %date %level %msg%n%n UTF-8 ================================================ FILE: address/src/main/resources/META-INF/nacos-default.properties ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # ================================================ FILE: address/src/main/resources/application.properties ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # server.port=8080 server.servlet.context-path=/ ================================================ FILE: address/src/test/java/com/alibaba/nacos/address/component/AddressServerGeneratorManagerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.component; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class AddressServerGeneratorManagerTest { @Test void testGenerateProductName() { AddressServerGeneratorManager manager = new AddressServerGeneratorManager(); final String blankName = manager.generateProductName(""); assertEquals(AddressServerConstants.ALIWARE_NACOS_DEFAULT_PRODUCT_NAME, blankName); final String defaultName = manager.generateProductName(AddressServerConstants.DEFAULT_PRODUCT); assertEquals(AddressServerConstants.ALIWARE_NACOS_DEFAULT_PRODUCT_NAME, defaultName); final String testName = manager.generateProductName("test"); assertEquals("nacos.as.test", testName); } @Test void testGenerateInstancesByIps() { AddressServerGeneratorManager manager = new AddressServerGeneratorManager(); final List empty = manager.generateInstancesByIps(null, null, null, null); assertNotNull(empty); assertTrue(empty.isEmpty()); String[] ipArray = new String[]{"192.168.3.1:8848", "192.168.3.2:8848", "192.168.3.3:8848"}; final List instanceList = manager.generateInstancesByIps("DEFAULT_GROUP@@nacos.as.test", "test", "test", ipArray); assertNotNull(instanceList); assertFalse(instanceList.isEmpty()); assertEquals(3, instanceList.size()); final Instance instance1 = instanceList.get(0); assertEquals("192.168.3.1", instance1.getIp()); final Instance instance2 = instanceList.get(1); assertEquals("192.168.3.2", instance2.getIp()); final Instance instance3 = instanceList.get(2); assertEquals("192.168.3.3", instance3.getIp()); } @Test void testGenerateResponseIps() { final List instanceList = new ArrayList<>(); Instance instance1 = new Instance(); instance1.setIp("192.168.3.1"); instance1.setPort(8848); Instance instance2 = new Instance(); instance2.setIp("192.168.3.2"); instance2.setPort(8848); Instance instance3 = new Instance(); instance3.setIp("192.168.3.3"); instance3.setPort(8848); instanceList.add(instance1); instanceList.add(instance2); instanceList.add(instance3); AddressServerGeneratorManager manager = new AddressServerGeneratorManager(); final String ipListStr = manager.generateResponseIps(instanceList); StringBuilder expectStr = new StringBuilder(); final StringBuilder ret = expectStr .append("192.168.3.1:8848").append('\n') .append("192.168.3.2:8848").append('\n') .append("192.168.3.3:8848").append('\n'); assertEquals(ret.toString(), ipListStr); } @Test void testGenerateNacosServiceName() { AddressServerGeneratorManager manager = new AddressServerGeneratorManager(); final String containDefault = manager.generateNacosServiceName("DEFAULT_GROUP@@test"); assertEquals("DEFAULT_GROUP@@test", containDefault); final String product = manager.generateNacosServiceName("product"); assertEquals("DEFAULT_GROUP@@product", product); } } ================================================ FILE: address/src/test/java/com/alibaba/nacos/address/component/AddressServerManagerTests.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.component; import com.alibaba.nacos.address.constant.AddressServerConstants; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class AddressServerManagerTests { private static final AddressServerManager ADDRESS_SERVER_MANAGER = new AddressServerManager(); @Test void getRawProductName() { assertEquals(AddressServerConstants.DEFAULT_PRODUCT, ADDRESS_SERVER_MANAGER.getRawProductName("")); assertEquals(AddressServerConstants.DEFAULT_PRODUCT, ADDRESS_SERVER_MANAGER.getRawProductName(AddressServerConstants.DEFAULT_PRODUCT)); assertEquals("otherProduct", ADDRESS_SERVER_MANAGER.getRawProductName("otherProduct")); } @Test void getDefaultClusterNameIfEmpty() { assertEquals(AddressServerConstants.DEFAULT_GET_CLUSTER, ADDRESS_SERVER_MANAGER.getDefaultClusterNameIfEmpty("")); assertEquals(AddressServerConstants.DEFAULT_GET_CLUSTER, ADDRESS_SERVER_MANAGER.getDefaultClusterNameIfEmpty(AddressServerConstants.DEFAULT_GET_CLUSTER)); assertEquals("otherServerList", ADDRESS_SERVER_MANAGER.getDefaultClusterNameIfEmpty("otherServerList")); } @Test void testGetRawClusterName() { assertEquals("serverList", ADDRESS_SERVER_MANAGER.getRawClusterName("serverList")); assertEquals(AddressServerConstants.DEFAULT_GET_CLUSTER, ADDRESS_SERVER_MANAGER.getRawClusterName("")); } @Test void testSplitIps() { final String[] emptyArr = ADDRESS_SERVER_MANAGER.splitIps(""); assertEquals(0, emptyArr.length); final String[] one = ADDRESS_SERVER_MANAGER.splitIps("192.168.1.12:8848"); assertEquals(1, one.length); assertEquals("192.168.1.12:8848", one[0]); final String[] two = ADDRESS_SERVER_MANAGER.splitIps("192.168.1.12:8848,192.268.3.33:8848"); assertEquals(2, two.length); assertEquals("192.168.1.12:8848", two[0]); assertEquals("192.268.3.33:8848", two[1]); } } ================================================ FILE: address/src/test/java/com/alibaba/nacos/address/controller/AddressServerClusterControllerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.controller; import com.alibaba.nacos.address.component.AddressServerGeneratorManager; import com.alibaba.nacos.address.component.AddressServerManager; import com.alibaba.nacos.address.constant.AddressServerConstants; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.naming.core.ClusterOperator; import com.alibaba.nacos.naming.core.InstanceOperator; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.pojo.Service; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(MockitoExtension.class) class AddressServerClusterControllerTest { @Mock private InstanceOperator instanceOperator; @Mock private NamingMetadataManager metadataManager; @Mock private ClusterOperator clusterOperator; private MockMvc mockMvc; @BeforeEach void before() { mockMvc = MockMvcBuilders.standaloneSetup( new AddressServerClusterController(instanceOperator, metadataManager, clusterOperator, new AddressServerManager(), new AddressServerGeneratorManager())).build(); Service service = Service .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); ServiceManager.getInstance().getSingleton(service); } @AfterEach void tearDown() { Service service = Service .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); ServiceManager.getInstance().removeSingleton(service); } @Test void testPostCluster() throws Exception { mockMvc.perform(post("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isOk()); } @Test void testPostClusterWithErrorIps() throws Exception { mockMvc.perform(post("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "192.168.1")).andExpect(status().isBadRequest()); } @Test void testPostClusterThrowException() throws Exception { Mockito.doThrow(new NacosException(500, "create service error")).when(clusterOperator) .updateClusterMetadata(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), Mockito.eq( Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"), Mockito.eq("serverList"), Mockito.any()); mockMvc.perform(post("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "192.168.1")).andExpect(status().isInternalServerError()); } @Test void testDeleteCluster() throws Exception { mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isOk()); } @Test void testDeleteClusterCannotFindService() throws Exception { tearDown(); mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isNotFound()); } @Test void testDeleteClusterEmptyIps() throws Exception { mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "")).andExpect(status().isBadRequest()); } @Test void testDeleteClusterErrorIps() throws Exception { mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "192.168.1")).andExpect(status().isBadRequest()); } @Test void testDeleteClusterThrowException() throws Exception { Mockito.doThrow(new NacosException(500, "remove service error")).when(instanceOperator) .removeInstance(Mockito.eq(Constants.DEFAULT_NAMESPACE_ID), Mockito.eq( Constants.DEFAULT_GROUP + AddressServerConstants.GROUP_SERVICE_NAME_SEP + "nacos.as.default"), Mockito.any()); mockMvc.perform(delete("/nacos/v1/as/nodes").param("product", "default").param("cluster", "serverList") .param("ips", "192.168.3.1,192.168.3.2")).andExpect(status().isInternalServerError()); } } ================================================ FILE: address/src/test/java/com/alibaba/nacos/address/controller/ServerListControllerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.address.controller; import com.alibaba.nacos.address.component.AddressServerGeneratorManager; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; 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.junit.jupiter.MockitoExtension; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.ArrayList; import java.util.List; import java.util.Optional; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @ExtendWith(MockitoExtension.class) class ServerListControllerTest { @Mock private NamingMetadataManager metadataManager; @Mock private ServiceStorage serviceStorage; private Service service; private MockMvc mockMvc; @BeforeEach void before() { this.mockMvc = MockMvcBuilders.standaloneSetup( new ServerListController(new AddressServerGeneratorManager(), metadataManager, serviceStorage)).build(); service = Service .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); ServiceManager.getInstance().getSingleton(service); } @AfterEach void tearDown() { ServiceManager.getInstance().removeSingleton(service); } @Test void testGetCluster() throws Exception { final Service service = Service .newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, "nacos.as.default", false); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.getClusters().put("serverList", new ClusterMetadata()); when(metadataManager.getServiceMetadata(service)).thenReturn(Optional.of(serviceMetadata)); List list = new ArrayList<>(2); list.add(new Instance()); list.add(new Instance()); ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setHosts(list); when(serviceStorage.getData(service)).thenReturn(serviceInfo); mockMvc.perform(get("/nacos/serverList")).andExpect(status().isOk()); } @Test void testGetClusterCannotFindService() throws Exception { tearDown(); mockMvc.perform(get("/default/serverList")).andExpect(status().isNotFound()); } @Test void testGetClusterCannotFindCluster() throws Exception { mockMvc.perform(get("/nacos/serverList")).andExpect(status().isNotFound()); } } ================================================ FILE: ai/pom.xml ================================================ 4.0.0 com.alibaba.nacos nacos-all ${revision} nacos-ai nacos-ai ${project.version} https://nacos.io com.alibaba.nacos nacos-common com.alibaba.nacos nacos-naming com.alibaba.nacos nacos-config org.apache.commons commons-compress 1.26.0 org.springframework.boot spring-boot-test-autoconfigure test ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/config/AiEnabledFilter.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Set; /** * Nacos AI Component enabled filter. * * @author xiweng.yy */ public class AiEnabledFilter implements NacosPackageExcludeFilter { private static final Logger LOGGER = LoggerFactory.getLogger(AiEnabledFilter.class); public static final String AI_ENABLED_KEY = "nacos.extension.ai.enabled"; @Override public String getResponsiblePackagePrefix() { return "com.alibaba.nacos.ai"; } @Override public boolean isExcluded(String className, Set annotationNames) { String functionMode = EnvUtil.getFunctionMode(); // When not specified naming mode or config mode, if (StringUtils.isNotEmpty(functionMode)) { LOGGER.warn("AI module disabled because function mode is {}, and AI depend naming module and config module both", functionMode); return true; } boolean aiDisabled = !EnvUtil.getProperty(AI_ENABLED_KEY, Boolean.class, true); if (aiDisabled) { LOGGER.warn("AI module disabled because set {} as false", AI_ENABLED_KEY); } return aiDisabled; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/config/McpCacheIndexProperties.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import org.springframework.boot.context.properties.ConfigurationProperties; /** * MCP Cache Index configuration properties. Centralized configuration management for MCP cache index related settings. * * @author misselvexu */ @ConfigurationProperties(prefix = "nacos.mcp.cache") public class McpCacheIndexProperties { /** * Whether MCP cache is enabled. */ private boolean enabled = true; /** * Maximum size of the cache. */ private int maxSize = 10000; /** * Cache entry expiration time in seconds. */ private long expireTimeSeconds = 3600; /** * Cache cleanup interval in seconds. */ private long cleanupIntervalSeconds = 300; /** * Cache synchronization interval in seconds. */ private long syncIntervalSeconds = 300; public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public int getMaxSize() { return maxSize; } public void setMaxSize(int maxSize) { this.maxSize = maxSize; } public long getExpireTimeSeconds() { return expireTimeSeconds; } public void setExpireTimeSeconds(long expireTimeSeconds) { this.expireTimeSeconds = expireTimeSeconds; } public long getCleanupIntervalSeconds() { return cleanupIntervalSeconds; } public void setCleanupIntervalSeconds(long cleanupIntervalSeconds) { this.cleanupIntervalSeconds = cleanupIntervalSeconds; } public long getSyncIntervalSeconds() { return syncIntervalSeconds; } public void setSyncIntervalSeconds(long syncIntervalSeconds) { this.syncIntervalSeconds = syncIntervalSeconds; } @Override public String toString() { return "McpCacheIndexProperties{" + "enabled=" + enabled + ", maxSize=" + maxSize + ", expireTimeSeconds=" + expireTimeSeconds + ", cleanupIntervalSeconds=" + cleanupIntervalSeconds + ", syncIntervalSeconds=" + syncIntervalSeconds + '}'; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/config/McpConfiguration.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import com.alibaba.nacos.core.code.ControllerMethodsCache; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; /** * AI MCP spring configuration. * * @author xiweng.yy */ @Configuration public class McpConfiguration { private final ControllerMethodsCache methodsCache; public McpConfiguration(ControllerMethodsCache methodsCache) { this.methodsCache = methodsCache; } @PostConstruct public void init() { methodsCache.initClassMethod("com.alibaba.nacos.ai.controller"); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/config/McpServerIndexConfiguration.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import com.alibaba.nacos.ai.index.CachedMcpServerIndex; import com.alibaba.nacos.ai.index.McpCacheIndex; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.index.MemoryMcpCacheIndex; import com.alibaba.nacos.ai.index.PlainMcpServerIndex; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.core.service.NamespaceOperationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; /** * MCP server index configuration class. * * @author misselvexu */ @Configuration @EnableConfigurationProperties(McpCacheIndexProperties.class) public class McpServerIndexConfiguration { private static final Logger LOGGER = LoggerFactory.getLogger(McpServerIndexConfiguration.class); private final McpCacheIndexProperties cacheProperties; public McpServerIndexConfiguration(McpCacheIndexProperties cacheProperties) { this.cacheProperties = cacheProperties; } /** * Create memory cache index Bean. */ @Bean @ConditionalOnProperty(name = "nacos.mcp.cache.enabled", havingValue = "true", matchIfMissing = true) public McpCacheIndex mcpCacheIndex() { LOGGER.info("Creating McpCacheIndex bean with maxSize={}, expireTime={}s, cleanupInterval={}s", cacheProperties.getMaxSize(), cacheProperties.getExpireTimeSeconds(), cacheProperties.getCleanupIntervalSeconds()); return new MemoryMcpCacheIndex(cacheProperties); } /** * Create scheduled task executor Bean. */ @Bean @ConditionalOnProperty(name = "nacos.mcp.cache.enabled", havingValue = "true", matchIfMissing = true) public ScheduledExecutorService mcpCacheScheduledExecutor() { LOGGER.info("Creating ScheduledExecutorService for MCP cache with syncInterval={}s", cacheProperties.getSyncIntervalSeconds()); // Manually create thread pool, following Alibaba coding standards return new ScheduledThreadPoolExecutor(1, r -> { Thread t = new Thread(r, "mcp-cache-sync"); t.setDaemon(true); return t; }, new ThreadPoolExecutor.CallerRunsPolicy()); } /** * Create the primary MCP server index Bean when cache is enabled. */ @Bean @Primary @ConditionalOnProperty(name = "nacos.mcp.cache.enabled", havingValue = "true", matchIfMissing = true) public McpServerIndex cachedMcpServerIndex(ConfigDetailService configDetailService, NamespaceOperationService namespaceOperationService, ConfigQueryChainService configQueryChainService, McpCacheIndex mcpCacheIndex, ScheduledExecutorService mcpCacheScheduledExecutor) { LOGGER.info("Creating CachedMcpServerIndex bean with cache enabled"); return new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, mcpCacheIndex, mcpCacheScheduledExecutor, cacheProperties.isEnabled(), cacheProperties.getSyncIntervalSeconds()); } /** * Create the primary MCP server index Bean when cache is disabled. */ @Bean @Primary @ConditionalOnProperty(name = "nacos.mcp.cache.enabled", havingValue = "false") public McpServerIndex plainMcpServerIndex(ConfigDetailService configDetailService, NamespaceOperationService namespaceOperationService, ConfigQueryChainService configQueryChainService) { LOGGER.info("Creating PlainMcpServerIndex bean as cache is disabled"); return new PlainMcpServerIndex(namespaceOperationService, configDetailService, configQueryChainService); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/constant/Constants.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.constant; /** * Nacos AI Server Constants. * * @author xiweng.yy */ public class Constants { public static final String MCP_PATH = "/ai/mcp"; public static final String MCP_ADMIN_PATH = "/v3/admin" + MCP_PATH; public static final String MCP_CONSOLE_PATH = "/v3/console" + MCP_PATH; public static final String MCP_LIST_SEARCH_ACCURATE = "accurate"; public static final String MCP_LIST_SEARCH_BLUR = "blur"; public static final String ALL_PATTERN = com.alibaba.nacos.api.common.Constants.ALL_PATTERN; public static final String MCP_SERVER_VERSIONS_GROUP = "mcp-server-versions"; public static final String MCP_SERVER_GROUP = "mcp-server"; public static final String MCP_SERVER_TOOL_GROUP = "mcp-tools"; public static final String MCP_SERVER_SPEC_DATA_ID_SUFFIX = "-mcp-server.json"; public static final String MCP_SERVER_VERSION_DATA_ID_SUFFIX = "-mcp-versions.json"; public static final String MCP_SERVER_TOOL_DATA_ID_SUFFIX = "-mcp-tools.json"; public static final String MCP_SERVER_ENDPOINT_GROUP = "mcp-endpoints"; public static final String MCP_SERVER_ENDPOINT_CLUSTER = com.alibaba.nacos.api.common.Constants.DEFAULT_CLUSTER_NAME; public static final String MCP_BACKEND_INSTANCE_PROTOCOL_KEY = "transportProtocol"; public static final String MCP_SERVER_ENDPOINT_ADDRESS = "address"; public static final String MCP_SERVER_ENDPOINT_PORT = "port"; public static final String MCP_SERVER_ENDPOINT_METADATA_MARK = "__nacos.ai.mcp.service__"; public static final String MCP_SERVER_CONFIG_MARK = "nacos.internal.config=mcp"; public static final String PROTOCOL_TYPE_HTTP = "http"; public static final String PROTOCOL_TYPE_HTTPS = "https"; public static final String SERVER_EXPORT_PATH_KEY = "exportPath"; public static final String MCP_SERVER_NAME_TAG_KEY_PREFIX = "mcpServerName="; public static final int MAX_LIST_SIZE = 100; public static final String RELEASE_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'"; public static final String CONFIG_TAGS_NAME = "config_tags"; public static final String META_PATH = "path"; public static final String SERVER_VERSION_CONFIG_DATA_ID_TEMPLATE = "%s" + MCP_SERVER_VERSION_DATA_ID_SUFFIX; public static final String SERVER_SPECIFICATION_CONFIG_DATA_ID_TEMPLATE = "%s-%s" + MCP_SERVER_SPEC_DATA_ID_SUFFIX; public static final String SERVER_TOOLS_SPEC_CONFIG_DATA_ID_TEMPLATE = "%s-%s" + MCP_SERVER_TOOL_DATA_ID_SUFFIX; public static class A2A { public static final String CONSOLE_PATH = "/v3/console/ai/a2a"; public static final String ADMIN_PATH = "/v3/admin/ai/a2a"; public static final String AGENT_GROUP = "agent"; public static final String AGENT_VERSION_GROUP = "agent-version"; public static final String SEARCH_BLUR = "blur"; public static final String SEARCH_ACCURATE = "accurate"; public static final String AGENT_ENDPOINT_GROUP = "agent-endpoints"; public static final String AGENT_ENDPOINT_PATH_KEY = "__nacos.agent.endpoint.path__"; public static final String AGENT_ENDPOINT_TRANSPORT_KEY = "__nacos.agent.endpoint.transport__"; public static final String NACOS_AGENT_ENDPOINT_SUPPORT_TLS = "__nacos.agent.endpoint.supportTls__"; public static final String NACOS_AGENT_ENDPOINT_PROTOCOL_KEY = "__nacos.agent.endpoint.protocol__"; public static final String NACOS_AGENT_ENDPOINT_QUERY_KEY = "__nacos.agent.endpoint.query__"; } public static class Skills { public static final String CONSOLE_PATH = "/v3/console/ai/skills"; public static final String ADMIN_PATH = "/v3/admin/ai/skills"; public static final String SKILL_GROUP = "skill"; public static final String SKILL_VERSION_GROUP = "skill-version"; public static final String SEARCH_BLUR = "blur"; public static final String SEARCH_ACCURATE = "accurate"; public static final String SKILL_DEFAULT_NAMESPACE = "public"; /** * Max allowed size for skill zip upload (10MB). Exceeding this will result in a clear error. */ public static final long MAX_UPLOAD_ZIP_BYTES = 10L * 1024 * 1024; } public static class Prompt { public static final String CONSOLE_PATH = "/v3/console/ai/prompt"; public static final String ADMIN_PATH = "/v3/admin/ai/prompt"; public static final String CLIENT_PATH = "/v3/client/ai/prompt"; /** * Fixed group for all prompt configurations. */ public static final String PROMPT_GROUP = "nacos-ai-prompt"; /** * DataId suffix for prompt configurations. */ public static final String PROMPT_DATA_ID_SUFFIX = ".json"; /** * DataId suffix for descriptor side prompt metadata. */ public static final String DESCRIPTOR_DATA_ID_SUFFIX = ".descriptor" + PROMPT_DATA_ID_SUFFIX; /** * DataId suffix for runtime label/version mapping. */ public static final String LABEL_VERSION_MAPPING_DATA_ID_SUFFIX = ".label-version-mapping" + PROMPT_DATA_ID_SUFFIX; /** * Key for prompt version in extInfo. */ public static final String EXT_PROMPT_VERSION = "prompt_version"; /** * Key for prompt commit message in extInfo. */ public static final String EXT_PROMPT_COMMIT_MSG = "prompt_commit_msg"; /** * Search mode: blur search. */ public static final String SEARCH_BLUR = "blur"; /** * Search mode: accurate search. */ public static final String SEARCH_ACCURATE = "accurate"; /** * Default namespace for prompt. */ public static final String PROMPT_DEFAULT_NAMESPACE = "public"; /** * Config type for prompt. */ public static final String PROMPT_CONFIG_TYPE = "json"; /** * JSON field: promptKey. */ public static final String FIELD_PROMPT_KEY = "promptKey"; /** * JSON field: version. */ public static final String FIELD_VERSION = "version"; /** * JSON field: template. */ public static final String FIELD_TEMPLATE = "template"; /** * JSON field: commitMsg. */ public static final String FIELD_COMMIT_MSG = "commitMsg"; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/constant/McpServerValidationConstants.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.constant; /** * Constants for MCP server validation. * * @author nacos */ public final class McpServerValidationConstants { /** * Validation status: valid. */ public static final String STATUS_VALID = "valid"; /** * Validation status: invalid. */ public static final String STATUS_INVALID = "invalid"; /** * Validation status: duplicate. */ public static final String STATUS_DUPLICATE = "duplicate"; private McpServerValidationConstants() { // Private constructor to prevent instantiation } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/controller/A2aAdminController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.ai.param.AgentHttpParamExtractor; import com.alibaba.nacos.ai.service.a2a.A2aServerOperationService; import com.alibaba.nacos.ai.utils.AgentRequestUtil; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * Nacos A2A Admin controller. * * @author nacos */ @NacosApi @RestController @RequestMapping(Constants.A2A.ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = AgentHttpParamExtractor.class) public class A2aAdminController { private final A2aServerOperationService a2aServerOperationService; public A2aAdminController(A2aServerOperationService a2aServerOperationService) { this.a2aServerOperationService = a2aServerOperationService; } /** * Register agent. * * @param form the agent detail form to register * @return result of the registration operation * @throws NacosException if the agent registration fails due to invalid input or internal error */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result registerAgent(AgentCardForm form) throws NacosException { form.validate(); AgentCard agentCard = AgentRequestUtil.parseAgentCard(form); a2aServerOperationService.registerAgent(agentCard, form.getNamespaceId(), form.getRegistrationType()); return Result.success("ok"); } /** * Get agent card. * * @param form the agent form to get * @return result of the get operation * @throws NacosApiException if the agent get fails due to invalid input or internal error */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result getAgentCard(AgentForm form) throws NacosApiException { form.validate(); return Result.success( a2aServerOperationService.getAgentCard(form.getNamespaceId(), form.getAgentName(), form.getVersion(), form.getRegistrationType())); } /** * Update agent. * * @param form the agent update form to update * @return result of the update operation * @throws NacosException if the agent update fails due to invalid input or internal error */ @PutMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result updateAgentCard(AgentCardUpdateForm form) throws NacosException { form.validate(); AgentCard agentCard = AgentRequestUtil.parseAgentCard(form); a2aServerOperationService.updateAgentCard(agentCard, form.getNamespaceId(), form.getRegistrationType(), form.getSetAsLatest()); return Result.success("ok"); } /** * Delete agent. * * @param form the agent form to delete * @return result of the deletion operation * @throws NacosException if the agent deletion fails due to invalid input or internal error */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result deleteAgent(AgentForm form) throws NacosException { form.validate(); a2aServerOperationService.deleteAgent(form.getNamespaceId(), form.getAgentName(), form.getVersion()); return Result.success("ok"); } /** * List agents. * * @param agentListForm the agent list form to list * @param pageForm the page form to list * @return result of the list operation * @throws NacosException if the agent list fails due to invalid input or internal error */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result> listAgents(AgentListForm agentListForm, PageForm pageForm) throws NacosException { agentListForm.validate(); pageForm.validate(); return Result.success( a2aServerOperationService.listAgents(agentListForm.getNamespaceId(), agentListForm.getAgentName(), agentListForm.getSearch(), pageForm.getPageNo(), pageForm.getPageSize())); } /** * List all versions for target Agent. * * @param agentForm agent form * @return all version for target agent. * @throws NacosException nacos exception */ @GetMapping("/version/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result> listAgentVersions(AgentForm agentForm) throws NacosException { agentForm.validate(); return Result.success( a2aServerOperationService.listAgentVersions(agentForm.getNamespaceId(), agentForm.getAgentName())); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/controller/McpAdminController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.mcp.admin.McpDetailForm; import com.alibaba.nacos.ai.form.mcp.admin.McpForm; import com.alibaba.nacos.ai.form.mcp.admin.McpListForm; import com.alibaba.nacos.ai.form.mcp.admin.McpUpdateForm; import com.alibaba.nacos.ai.param.McpHttpParamExtractor; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.ai.utils.McpRequestUtil; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Nacos AI MCP controller. * * @author xiweng.yy */ @NacosApi @RestController @RequestMapping(Constants.MCP_ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = McpHttpParamExtractor.class) public class McpAdminController { private final McpServerOperationService mcpServerOperationService; public McpAdminController(McpServerOperationService mcpServerOperationService) { this.mcpServerOperationService = mcpServerOperationService; } /** * List mcp server. * * @param mcpListForm list mcp servers request form. * @param pageForm page info about the request. * @return mcp server list wrapper with {@link Result} * @throws NacosApiException if request parameter is invalid or handle error */ @GetMapping(value = "/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result> listMcpServers(McpListForm mcpListForm, PageForm pageForm) throws NacosException { mcpListForm.validate(); pageForm.validate(); return Result.success( mcpServerOperationService.listMcpServerWithPage(mcpListForm.getNamespaceId(), mcpListForm.getMcpName(), mcpListForm.getSearch(), pageForm.getPageNo(), pageForm.getPageSize())); } /** * Get specified mcp server detail info. * * @param mcpForm get mcp server request form * @return detail info with {@link McpServerDetailInfo} * @throws NacosException any exception during handling */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result getMcpServer(McpForm mcpForm) throws NacosException { mcpForm.validate(); return Result.success(mcpServerOperationService.getMcpServerDetail(mcpForm.getNamespaceId(), mcpForm.getMcpId(), mcpForm.getMcpName(), mcpForm.getVersion())); } /** * Create new mcp server. * * @param mcpForm create mcp server request form * @throws NacosException any exception during handling */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result createMcpServer(McpDetailForm mcpForm) throws NacosException { mcpForm.validate(); McpServerBasicInfo basicInfo = McpRequestUtil.parseMcpServerBasicInfo(mcpForm); McpToolSpecification mcpTools = McpRequestUtil.parseMcpTools(mcpForm); McpEndpointSpec endpointSpec = McpRequestUtil.parseMcpEndpointSpec(basicInfo, mcpForm); String mcpId = mcpServerOperationService.createMcpServer(mcpForm.getNamespaceId(), basicInfo, mcpTools, endpointSpec); return Result.success(mcpId); } /** * Update existed mcp server. * *

* `namespaceId` and `mcpName` can't be changed. *

* * @param mcpForm update mcp servers request form * @throws NacosException any exception during handling */ @PutMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result updateMcpServer(McpUpdateForm mcpForm) throws NacosException { mcpForm.validate(); McpServerBasicInfo basicInfo = McpRequestUtil.parseMcpServerBasicInfo(mcpForm); McpToolSpecification mcpTools = McpRequestUtil.parseMcpTools(mcpForm); McpEndpointSpec endpointSpec = McpRequestUtil.parseMcpEndpointSpec(basicInfo, mcpForm); mcpServerOperationService.updateMcpServer(mcpForm.getNamespaceId(), mcpForm.getLatest(), basicInfo, mcpTools, endpointSpec, mcpForm.isOverrideExisting()); return Result.success("ok"); } /** * Delete existed mcp server. * * @param mcpForm delete mcp server request form * @throws NacosException any exception during handling */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result deleteMcpServer(McpForm mcpForm) throws NacosException { mcpForm.validate(); mcpServerOperationService.deleteMcpServer(mcpForm.getNamespaceId(), mcpForm.getMcpName(), mcpForm.getMcpId(), mcpForm.getVersion()); return Result.success("ok"); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/controller/PromptAdminController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.prompt.PromptForm; import com.alibaba.nacos.ai.form.prompt.PromptHistoryForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelBindForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelForm; import com.alibaba.nacos.ai.form.prompt.PromptListForm; import com.alibaba.nacos.ai.form.prompt.PromptMetadataForm; import com.alibaba.nacos.ai.form.prompt.PromptPublishForm; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVariable; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.ai.param.PromptHttpParamExtractor; import com.alibaba.nacos.ai.service.prompt.PromptAdminOperationService; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.fasterxml.jackson.core.type.TypeReference; import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; /** * Prompt admin controller. * *

Provides REST APIs for prompt management operations.

* * @author nacos */ @NacosApi @RestController @RequestMapping(Constants.Prompt.ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = PromptHttpParamExtractor.class) public class PromptAdminController { private final PromptAdminOperationService promptOperationService; public PromptAdminController(PromptAdminOperationService promptOperationService) { this.promptOperationService = promptOperationService; } /** * Publish a new version of prompt. * * @param form the prompt publish form * @param request HTTP request for getting client info * @return result of the publish operation * @throws NacosException if the prompt publish fails */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result publishPrompt(PromptPublishForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptOperationService.publishPromptVersion( form.getNamespaceId(), form.getPromptKey(), form.getVersion(), form.getTemplate(), form.getCommitMsg(), form.getDescription(), parseBizTags(form.getBizTags()), parseVariables(form.getVariables()), srcUser, srcIp ); return Result.success(success); } /** * Get prompt metadata. */ @GetMapping("/metadata") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result getPromptMetadata(PromptForm form) throws NacosException { form.validate(); PromptMetaInfo detail = promptOperationService.getPromptMeta(form.getNamespaceId(), form.getPromptKey()); return Result.success(detail); } /** * Delete prompt. * * @param form the prompt form * @param request HTTP request for getting client info * @return result of the deletion operation * @throws NacosException if the prompt deletion fails */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result deletePrompt(PromptForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptOperationService.deletePrompt( form.getNamespaceId(), form.getPromptKey(), srcUser, srcIp ); return Result.success(success); } /** * List prompts with pagination. * * @param form the prompt list form * @return result of the list operation * @throws NacosException if the prompt list fails */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result> listPrompts(PromptListForm form) throws NacosException { form.validate(); Page result = promptOperationService.listPrompts( form.getNamespaceId(), form.getPromptKey(), form.getSearch(), form.getBizTags(), form.getPageNo(), form.getPageSize() ); return Result.success(result); } /** * List prompt versions. */ @GetMapping("/versions") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result> listPromptVersions(PromptHistoryForm form) throws NacosException { form.validate(); Page result = promptOperationService.listPromptVersions( form.getNamespaceId(), form.getPromptKey(), form.getPageNo(), form.getPageSize() ); return Result.success(result); } /** * Get prompt detail by specified version, null for latest. */ @GetMapping("/detail") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result queryPromptDetail(PromptQueryForm form) throws NacosException { form.validate(); PromptVersionInfo detail = promptOperationService.queryPromptDetail( form.getNamespaceId(), form.getPromptKey(), form.getVersion(), form.getLabel() ); return Result.success(detail); } /** * Bind label to a specified prompt version. */ @PutMapping("/label") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result bindLabel(PromptLabelBindForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptOperationService.bindLabel( form.getNamespaceId(), form.getPromptKey(), form.getLabel(), form.getVersion(), srcUser, srcIp ); return Result.success(success); } /** * Unbind label from prompt. */ @DeleteMapping("/label") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result unbindLabel(PromptLabelForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptOperationService.unbindLabel( form.getNamespaceId(), form.getPromptKey(), form.getLabel(), srcUser, srcIp ); return Result.success(success); } /** * Update prompt metadata (description only). * * @param form the prompt metadata form * @param request HTTP request for getting client info * @return result of the update operation * @throws NacosException if the update fails */ @PutMapping("/metadata") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result updatePromptMetadata(PromptMetadataForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptOperationService.updatePromptMetadata( form.getNamespaceId(), form.getPromptKey(), form.getDescription(), parseBizTags(form.getBizTags()), srcUser, srcIp ); return Result.success(success); } private List parseBizTags(String bizTags) { if (bizTags == null) { return null; } if (bizTags.trim().isEmpty()) { return new ArrayList<>(0); } String[] split = bizTags.split(","); List result = new ArrayList<>(split.length); for (String each : split) { if (each != null && !each.trim().isEmpty()) { result.add(each.trim()); } } return result; } private List parseVariables(String variables) { if (StringUtils.isBlank(variables)) { return null; } return JacksonUtils.toObj(variables, new TypeReference>() { }); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/controller/PromptClientController.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.ai.param.PromptHttpParamExtractor; import com.alibaba.nacos.ai.service.prompt.PromptClientOperationService; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import jakarta.servlet.http.HttpServletResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Prompt client controller for prompt runtime read query. * * @author nacos */ @NacosApi @RestController @RequestMapping(Constants.Prompt.CLIENT_PATH) @ExtractorManager.Extractor(httpExtractor = PromptHttpParamExtractor.class) public class PromptClientController { private final PromptClientOperationService promptOperationService; public PromptClientController(PromptClientOperationService promptOperationService) { this.promptOperationService = promptOperationService; } /** * Query prompt by label/version/latest with priority label > version > latest. */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.OPEN_API) public Result queryPrompt(PromptQueryForm form, HttpServletResponse response) throws NacosException { form.validate(); try { PromptVersionInfo result = promptOperationService.queryPrompt( form.getNamespaceId(), form.getPromptKey(), form.getVersion(), form.getLabel(), form.getMd5()); return Result.success(convertToClientPrompt(result)); } catch (NacosException ex) { if (ex.getErrCode() == NacosException.NOT_MODIFIED) { response.setStatus(NacosException.NOT_MODIFIED); return Result.success(null); } throw ex; } } private Prompt convertToClientPrompt(PromptVersionInfo versionInfo) { Prompt prompt = new Prompt(); prompt.setPromptKey(versionInfo.getPromptKey()); prompt.setVersion(versionInfo.getVersion()); prompt.setTemplate(versionInfo.getTemplate()); prompt.setMd5(versionInfo.getMd5()); return prompt; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/controller/SkillAdminController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.ai.form.skills.admin.SkillForm; import com.alibaba.nacos.ai.form.skills.admin.SkillListForm; import com.alibaba.nacos.ai.form.skills.admin.SkillUpdateForm; import com.alibaba.nacos.ai.param.SkillHttpParamExtractor; import com.alibaba.nacos.ai.service.skills.SkillOperationService; import com.alibaba.nacos.ai.utils.SkillRequestUtil; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import jakarta.servlet.http.HttpServletRequest; /** * Skill admin controller. * * @author nacos */ @NacosApi @RestController @RequestMapping(Constants.Skills.ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = SkillHttpParamExtractor.class) public class SkillAdminController { private final SkillOperationService skillOperationService; public SkillAdminController(SkillOperationService skillOperationService) { this.skillOperationService = skillOperationService; } /** * Register skill. * * @param form the skill detail form to register * @return result of the registration operation * @throws NacosException if the skill registration fails */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result registerSkill(SkillDetailForm form) throws NacosException { form.validate(); Skill skill = SkillRequestUtil.parseSkill(form); String skillName = skillOperationService.registerSkill(skill, form.getNamespaceId()); return Result.success(skillName); } /** * Get skill. * * @param form the skill form to get * @return result of the get operation * @throws NacosException if the skill get fails */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result getSkill(SkillForm form) throws NacosException { form.validate(); return Result.success( skillOperationService.getSkillDetail(form.getNamespaceId(), form.getSkillName())); } /** * Update skill. * * @param form the skill update form to update * @return result of the update operation * @throws NacosException if the skill update fails */ @PutMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result updateSkill(SkillUpdateForm form) throws NacosException { form.validate(); Skill skill = SkillRequestUtil.parseSkill(form); skillOperationService.updateSkill(skill, form.getNamespaceId()); return Result.success("ok"); } /** * Delete skill. * * @param form the skill form to delete * @return result of the deletion operation * @throws NacosException if the skill deletion fails */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result deleteSkill(SkillForm form) throws NacosException { form.validate(); skillOperationService.deleteSkill(form.getNamespaceId(), form.getSkillName()); return Result.success("ok"); } /** * List skills. * * @param skillListForm the skill list form to list * @param pageForm the page form to list * @return result of the list operation * @throws NacosException if the skill list fails */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result> listSkills(SkillListForm skillListForm, PageForm pageForm) throws NacosException { skillListForm.validate(); pageForm.validate(); return Result.success( skillOperationService.listSkills(skillListForm.getNamespaceId(), skillListForm.getSkillName(), skillListForm.getSearch(), pageForm.getPageNo(), pageForm.getPageSize())); } /** * Upload skill from zip file. * * @param request HTTP servlet request * @param namespaceId namespace ID * @param file zip file containing skill * @return result of the upload operation * @throws NacosException if the upload fails */ @PostMapping(value = "/upload", consumes = "multipart/form-data") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.ADMIN_API) @ExtractorManager.Extractor(httpExtractor = ExtractorManager.DefaultHttpExtractor.class) public Result uploadSkill(HttpServletRequest request, @RequestParam(value = "namespaceId", required = false) String namespaceId, @RequestParam("file") MultipartFile file) throws NacosException { namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); byte[] zipBytes = SkillRequestUtil.validateAndExtractZipBytes(file); String skillName = skillOperationService.uploadSkillFromZip(namespaceId, zipBytes); return Result.success(skillName); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/enums/ExternalDataTypeEnum.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.enums; import com.alibaba.nacos.api.utils.StringUtils; /** * External data type enum. User can import external * mcp server data to nacos, the data type is defined * by this enum. * @author xinluo */ public enum ExternalDataTypeEnum { /** * MCP Server json text, the json text should follow the * MCP Server json format as defined in * MCP Server. */ JSON("json"), /** * MCP registry url, the url should be a valid MCP registry url * and the api should follow the MCP registry api as defined in * openapi.yaml. */ URL("url"), /** * MCP registry seed file * seed.json. */ FILE("file"); /** * The name of the external data type. */ private final String name; ExternalDataTypeEnum(String name) { this.name = name; } public String getName() { return name; } /** * Parse the external data type from the given value. * @param value the value to parse. * @return the external data type. */ public static ExternalDataTypeEnum parseType(String value) { if (StringUtils.isBlank(value)) { return null; } for (ExternalDataTypeEnum type : ExternalDataTypeEnum.values()) { if (type.getName().equals(value)) { return type; } } return null; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/enums/McpImportResultStatusEnum.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.enums; /** * McpImportResultStatusEnum. * @author xinluo */ public enum McpImportResultStatusEnum { /** * SKIPPED. */ SKIPPED("skipped"), /** * FAILED. */ FAILED("failed"), /** * SUCCESS. */ SUCCESS("success"); private final String name; McpImportResultStatusEnum(String name) { this.name = name; } public String getName() { return name; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/a2a/admin/AgentCardForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.a2a.admin; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; import static com.alibaba.nacos.api.ai.constant.AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE; import static com.alibaba.nacos.api.ai.constant.AiConstants.A2a.A2A_ENDPOINT_TYPE_URL; /** * Agent Card Form request. * * @author xiweng.yy */ public class AgentCardForm extends AgentForm { @Serial private static final long serialVersionUID = 8361628138801381818L; private String agentCard; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); fillDefaultRegistrationType(); if (StringUtils.isEmpty(agentCard)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Request parameter `agentCard` should not be `null` or empty."); } validateRegistrationType(); } protected void validateRegistrationType() throws NacosApiException { if (!A2A_ENDPOINT_TYPE_URL.equals(getRegistrationType()) && !A2A_ENDPOINT_TYPE_SERVICE.equals( getRegistrationType())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, String.format("Required parameter 'registrationType' value should be `%s` or `%s` but was `%s`", A2A_ENDPOINT_TYPE_URL, A2A_ENDPOINT_TYPE_SERVICE, getRegistrationType())); } } protected void fillDefaultRegistrationType() { if (StringUtils.isEmpty(getRegistrationType())) { setRegistrationType(A2A_ENDPOINT_TYPE_URL); } } public String getAgentCard() { return agentCard; } public void setAgentCard(String agentCard) { this.agentCard = agentCard; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/a2a/admin/AgentCardUpdateForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.a2a.admin; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Agent Card Update Form. * * @author xiweng.yy */ public class AgentCardUpdateForm extends AgentCardForm { @Serial private static final long serialVersionUID = 353698557363707304L; private boolean setAsLatest; public boolean getSetAsLatest() { return setAsLatest; } public void setSetAsLatest(boolean setAsLatest) { this.setAsLatest = setAsLatest; } @Override protected void fillDefaultRegistrationType() { // Update does not need to fill registration type } @Override protected void validateRegistrationType() throws NacosApiException { // Update request if no set registration type, means not change the registration type if (StringUtils.isEmpty(getRegistrationType())) { return; } super.validateRegistrationType(); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/a2a/admin/AgentForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.a2a.admin; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; import static com.alibaba.nacos.api.ai.constant.AiConstants.A2a.A2A_DEFAULT_NAMESPACE; /** * Agent form. * * @author KiteSoar **/ public class AgentForm implements NacosForm { @Serial private static final long serialVersionUID = -73912927386186928L; private String namespaceId; private String agentName; private String version; private String registrationType; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); if (StringUtils.isEmpty(agentName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'name' type String is not present"); } } protected void fillDefaultNamespaceId() { if (StringUtils.isEmpty(namespaceId)) { namespaceId = A2A_DEFAULT_NAMESPACE; } } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getAgentName() { return agentName; } public void setAgentName(String agentName) { this.agentName = agentName; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getRegistrationType() { return registrationType; } public void setRegistrationType(String registrationType) { this.registrationType = registrationType; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/a2a/admin/AgentListForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.a2a.admin; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import java.io.Serial; /** * Agent list form. * * @author KiteSoar */ public class AgentListForm extends AgentForm { @Serial private static final long serialVersionUID = 4706219418699928980L; private String search; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); if (!Constants.MCP_LIST_SEARCH_ACCURATE.equalsIgnoreCase(search) && !Constants.MCP_LIST_SEARCH_BLUR.equalsIgnoreCase(search)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Request parameter `search` should be `accurate` or `blur`."); } } public String getSearch() { return search; } public void setSearch(String search) { this.search = search; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/mcp/admin/McpDetailForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.mcp.admin; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Nacos AI Mcp Server request detail form, used in create or update. * * @author xiweng.yy */ public class McpDetailForm extends McpForm { @Serial private static final long serialVersionUID = 8016131725604983670L; private String serverSpecification; private String toolSpecification; private String endpointSpecification; @Override public void validate() throws NacosApiException { fillDefaultValue(); if (StringUtils.isEmpty(serverSpecification)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'serverSpecification' type McpServerBasicInfo is not present"); } } public String getServerSpecification() { return serverSpecification; } public void setServerSpecification(String serverSpecification) { this.serverSpecification = serverSpecification; } public String getToolSpecification() { return toolSpecification; } public void setToolSpecification(String toolSpecification) { this.toolSpecification = toolSpecification; } public String getEndpointSpecification() { return endpointSpecification; } public void setEndpointSpecification(String endpointSpecification) { this.endpointSpecification = endpointSpecification; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/mcp/admin/McpForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.mcp.admin; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Nacos AI Mcp Server request form. * * @author xiweng.yy */ public class McpForm implements NacosForm { @Serial private static final long serialVersionUID = 1314659974972866397L; private String mcpId; private String namespaceId; private String mcpName; private String version; @Override public void validate() throws NacosApiException { fillDefaultValue(); if (StringUtils.isEmpty(mcpId) && StringUtils.isEmpty(mcpName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'mcpId' or 'mcpName' type String at lease one is not present"); } } protected void fillDefaultValue() { if (StringUtils.isEmpty(namespaceId)) { namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; } } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getMcpId() { return mcpId; } public void setMcpId(String id) { this.mcpId = id; } public String getMcpName() { return mcpName; } public void setMcpName(String name) { this.mcpName = name; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/mcp/admin/McpImportForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.mcp.admin; import com.alibaba.nacos.ai.enums.ExternalDataTypeEnum; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Nacos AI MCP Server import request form. * * @author WangzJi */ public class McpImportForm extends McpForm { @Serial private static final long serialVersionUID = 8016131725604983671L; private String importType; private String data; private boolean overrideExisting = false; private boolean validateOnly = false; /** * Whether to skip invalid servers when executing import. */ private boolean skipInvalid = false; private String[] selectedServers; /** * Optional start cursor for URL-based import pagination. */ private String cursor; /** * Optional page size for URL-based import (items per page). */ private Integer limit; /** * Optional fuzzy search keyword for registry import listing. * Only used when importType is 'url'. */ private String search; @Override public void validate() throws NacosApiException { fillDefaultValue(); if (StringUtils.isEmpty(importType)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'importType' is not present"); } if (StringUtils.isEmpty(data)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'data' is not present"); } if (ExternalDataTypeEnum.parseType(importType) == null) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "importType must be one of: json, url, file"); } } public String getImportType() { return importType; } public void setImportType(String importType) { this.importType = importType; } public String getData() { return data; } public void setData(String data) { this.data = data; } public boolean isOverrideExisting() { return overrideExisting; } public void setOverrideExisting(boolean overrideExisting) { this.overrideExisting = overrideExisting; } public boolean isValidateOnly() { return validateOnly; } public void setValidateOnly(boolean validateOnly) { this.validateOnly = validateOnly; } public boolean isSkipInvalid() { return skipInvalid; } public void setSkipInvalid(boolean skipInvalid) { this.skipInvalid = skipInvalid; } public String[] getSelectedServers() { return selectedServers; } public void setSelectedServers(String[] selectedServers) { this.selectedServers = selectedServers; } public String getCursor() { return cursor; } public void setCursor(String cursor) { this.cursor = cursor; } public Integer getLimit() { return limit; } public void setLimit(Integer limit) { this.limit = limit; } public String getSearch() { return search; } public void setSearch(String search) { this.search = search; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/mcp/admin/McpListForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.mcp.admin; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Nacos AI Mcp Server request form. * * @author xiweng.yy */ public class McpListForm extends McpForm { /** * blur or accurate. */ private String search; @Serial private static final long serialVersionUID = 9017621414114266178L; @Override public void validate() throws NacosApiException { fillDefaultValue(); if (!Constants.MCP_LIST_SEARCH_ACCURATE.equalsIgnoreCase(search) && !Constants.MCP_LIST_SEARCH_BLUR.equalsIgnoreCase(search)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Request parameter `search` should be `accurate` or `blur`."); } } @Override protected void fillDefaultValue() { super.fillDefaultValue(); if (StringUtils.isEmpty(search)) { search = Constants.MCP_LIST_SEARCH_ACCURATE; } } public String getSearch() { return search; } public void setSearch(String search) { this.search = search; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/mcp/admin/McpUpdateForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.mcp.admin; import java.io.Serial; /** * Mcp server update form. * * @author xinluo */ public class McpUpdateForm extends McpDetailForm { @Serial private static final long serialVersionUID = 4144251088520249913L; private boolean latest = true; private boolean overrideExisting = false; public Boolean getLatest() { return latest; } public void setLatest(Boolean publish) { this.latest = publish; } public boolean isOverrideExisting() { return overrideExisting; } public void setOverrideExisting(boolean overrideExisting) { this.overrideExisting = overrideExisting; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.NamespaceUtil; import java.io.Serial; /** * Prompt form base class. * * @author nacos */ public class PromptForm implements NacosForm { @Serial private static final long serialVersionUID = 1L; private String namespaceId; private String promptKey; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); if (StringUtils.isEmpty(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'promptKey' type String is not present"); } } protected void fillDefaultNamespaceId() { namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptHistoryForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.api.NacosApiException; import java.io.Serial; /** * Prompt history query form. * * @author nacos */ public class PromptHistoryForm extends PromptForm { @Serial private static final long serialVersionUID = 1L; private static final int MAX_PAGE_SIZE = 50; /** * Page number (1-based). */ private int pageNo = 1; /** * Page size. */ private int pageSize = 10; @Override public void validate() throws NacosApiException { super.validate(); if (pageNo < 1) { pageNo = 1; } if (pageSize < 1) { pageSize = 10; } if (pageSize > MAX_PAGE_SIZE) { pageSize = MAX_PAGE_SIZE; } } public int getPageNo() { return pageNo; } public void setPageNo(int pageNo) { this.pageNo = pageNo; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptLabelBindForm.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; /** * Prompt label bind form. * * @author nacos */ public class PromptLabelBindForm extends PromptForm { private String label; private String version; @Override public void validate() throws NacosApiException { super.validate(); if (StringUtils.isBlank(label)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'label' type String is not present"); } if (StringUtils.isBlank(version)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'version' type String is not present"); } } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptLabelForm.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; /** * Prompt label form. * * @author nacos */ public class PromptLabelForm extends PromptForm { private String label; @Override public void validate() throws NacosApiException { super.validate(); if (StringUtils.isBlank(label)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'label' type String is not present"); } } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptListForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Prompt list query form. * * @author nacos */ public class PromptListForm implements NacosForm { @Serial private static final long serialVersionUID = 1L; private static final int MAX_PAGE_SIZE = 50; private String namespaceId; /** * Optional promptKey filter. */ private String promptKey; /** * Search mode: "accurate" or "blur". */ private String search; /** * Optional biz tags filter (comma-separated). */ private String bizTags; /** * Page number (1-based). */ private int pageNo = 1; /** * Page size. */ private int pageSize = 10; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); if (StringUtils.isNotBlank(search) && !Constants.Prompt.SEARCH_ACCURATE.equalsIgnoreCase(search) && !Constants.Prompt.SEARCH_BLUR.equalsIgnoreCase(search)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Request parameter 'search' should be 'accurate' or 'blur'."); } if (pageNo < 1) { pageNo = 1; } if (pageSize < 1) { pageSize = 10; } if (pageSize > MAX_PAGE_SIZE) { pageSize = MAX_PAGE_SIZE; } } private void fillDefaultNamespaceId() { if (StringUtils.isEmpty(namespaceId)) { namespaceId = Constants.Prompt.PROMPT_DEFAULT_NAMESPACE; } } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } public String getSearch() { return search; } public void setSearch(String search) { this.search = search; } public String getBizTags() { return bizTags; } public void setBizTags(String bizTags) { this.bizTags = bizTags; } public int getPageNo() { return pageNo; } public void setPageNo(int pageNo) { this.pageNo = pageNo; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptMetadataForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import java.io.Serial; /** * Prompt metadata update form. * *

Used for updating prompt description without changing version.

* * @author nacos */ public class PromptMetadataForm extends PromptForm { @Serial private static final long serialVersionUID = 1L; /** * New description for the prompt. */ private String description; /** * Prompt biz tags (comma-separated). */ private String bizTags; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getBizTags() { return bizTags; } public void setBizTags(String bizTags) { this.bizTags = bizTags; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptPublishForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.ai.utils.PromptVersionUtils; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Prompt publish form. * * @author nacos */ public class PromptPublishForm extends PromptForm { @Serial private static final long serialVersionUID = 1L; /** * Version in format "major.minor.patch" (e.g., "1.0.0"). */ private String version; /** * Prompt template content. */ private String template; /** * Commit message for this version. */ private String commitMsg; /** * Description for the prompt (optional, stored in config metadata). */ private String description; /** * Prompt biz tags (comma-separated, optional). */ private String bizTags; /** * Variable definitions with default values (JSON array string, optional). * *

Example: [{"name":"question","defaultValue":"Hello","description":"User question"}]

*/ private String variables; @Override public void validate() throws NacosApiException { super.validate(); if (StringUtils.isEmpty(version)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'version' type String is not present"); } if (!PromptVersionUtils.isValidVersion(version)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Parameter 'version' must be in format 'major.minor.patch' (e.g., '1.0.0')"); } } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getTemplate() { return template; } public void setTemplate(String template) { this.template = template; } public String getCommitMsg() { return commitMsg; } public void setCommitMsg(String commitMsg) { this.commitMsg = commitMsg; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getBizTags() { return bizTags; } public void setBizTags(String bizTags) { this.bizTags = bizTags; } public String getVariables() { return variables; } public void setVariables(String variables) { this.variables = variables; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/prompt/PromptQueryForm.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; /** * Prompt query form for client read API. * * @author nacos */ public class PromptQueryForm extends PromptForm { private String version; private String label; private String md5; public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/skills/admin/SkillDetailForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.skills.admin; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Skill detail form (for create and detail). * * @author nacos */ public class SkillDetailForm extends SkillForm { @Serial private static final long serialVersionUID = 1L; /** * Skill card JSON string, contains complete Skill information. */ private String skillCard; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); // For create/detail, skillName is optional (can be in skillCard) // Only skillCard is required if (StringUtils.isEmpty(skillCard)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Request parameter `skillCard` should not be `null` or empty."); } } public String getSkillCard() { return skillCard; } public void setSkillCard(String skillCard) { this.skillCard = skillCard; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/skills/admin/SkillForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.skills.admin; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Skill form base class. * * @author nacos */ public class SkillForm implements NacosForm { @Serial private static final long serialVersionUID = 1L; private String namespaceId; private String skillName; private String version; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); if (StringUtils.isEmpty(skillName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'skillName' type String is not present"); } } protected void fillDefaultNamespaceId() { if (StringUtils.isEmpty(namespaceId)) { namespaceId = Constants.Skills.SKILL_DEFAULT_NAMESPACE; } } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getSkillName() { return skillName; } public void setSkillName(String skillName) { this.skillName = skillName; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/skills/admin/SkillListForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.skills.admin; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.io.Serial; /** * Skill list form. * * @author nacos */ public class SkillListForm extends SkillForm { @Serial private static final long serialVersionUID = 1L; private String search; @Override public void validate() throws NacosApiException { fillDefaultNamespaceId(); // For list query, skillName is optional if (StringUtils.isNotBlank(search) && !Constants.Skills.SEARCH_ACCURATE.equalsIgnoreCase(search) && !Constants.Skills.SEARCH_BLUR.equalsIgnoreCase(search)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Request parameter `search` should be `accurate` or `blur`."); } } public String getSearch() { return search; } public void setSearch(String search) { this.search = search; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/form/skills/admin/SkillUpdateForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.skills.admin; import java.io.Serial; /** * Skill update form. * * @author nacos */ public class SkillUpdateForm extends SkillDetailForm { @Serial private static final long serialVersionUID = 1L; /** * Whether to set as latest version. */ private Boolean setAsLatest; public Boolean getSetAsLatest() { return setAsLatest; } public void setSetAsLatest(Boolean setAsLatest) { this.setAsLatest = setAsLatest; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/index/AbstractMcpServerIndex.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Objects; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.utils.McpConfigUtils; import com.alibaba.nacos.api.ai.model.mcp.McpServerVersionInfo; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.core.service.NamespaceOperationService; /** * Abstract base class for MCP server index implementations. * * @author xinluo */ public abstract class AbstractMcpServerIndex implements McpServerIndex { private final NamespaceOperationService namespaceOperationService; protected final ConfigDetailService configDetailService; public AbstractMcpServerIndex(NamespaceOperationService namespaceOperationService, ConfigDetailService configDetailService) { this.namespaceOperationService = namespaceOperationService; this.configDetailService = configDetailService; } protected List fetchOrderedNamespaceList() { return namespaceOperationService.getNamespaceList().stream() .sorted(Comparator.comparing(Namespace::getNamespace)).map(Namespace::getNamespace).toList(); } protected McpServerIndexData getFirstMcpServerByName(String name) { return fetchOrderedNamespaceList() .stream() .filter(namespaceId -> !StringUtils.isEmpty(namespaceId)) .map(namespaceId -> getMcpServerByName(namespaceId, name)) .filter(index -> Objects.nonNull(index)) .findFirst() .orElse(null); } @Override public Page searchMcpServerByNameWithPage(String namespaceId, String name, String search, int pageNo, int limit) { Page serverInfos = searchMcpServers(namespaceId, name, search, pageNo, limit); List indexDataList = serverInfos.getPageItems().stream() .map((configInfo) -> { configInfo.setTenant(namespaceId); return configInfo; }) .map(this::mapToMcpServerVersionInfo) .map(this::mcpToIndexAndUpdateToCache) .toList(); Page result = new Page<>(); result.setPageItems(indexDataList); result.setTotalCount(serverInfos.getTotalCount()); result.setPagesAvailable((int) Math.ceil((double) serverInfos.getTotalCount() / (double) limit)); result.setPageNumber(pageNo); return result; } /** * Callback after search operation. Subclasses can implement this to perform additional operations. * * @param searchResult the search results * @param name the search name */ protected abstract void afterSearch(McpServerIndexData searchResult, String name); /** * Search MCP servers. */ protected Page searchMcpServers(String namespace, String serverName, String search, int pageNo, int limit) { HashMap advanceInfo = new HashMap<>(1); if (Objects.isNull(serverName)) { serverName = ""; } String dataId = Constants.ALL_PATTERN; if (Constants.MCP_LIST_SEARCH_BLUR.equals(search) || serverName.isEmpty()) { String nameTag = McpConfigUtils.formatServerNameTagBlurSearchValue(serverName); advanceInfo.put(Constants.CONFIG_TAGS_NAME, nameTag); search = Constants.MCP_LIST_SEARCH_BLUR; } else { advanceInfo.put(Constants.CONFIG_TAGS_NAME, McpConfigUtils.formatServerNameTagAccurateSearchValue(serverName)); dataId = null; } return configDetailService.findConfigInfoPage(search, pageNo, limit, dataId, Constants.MCP_SERVER_VERSIONS_GROUP, namespace, advanceInfo); } protected McpServerVersionInfo mapToMcpServerVersionInfo(ConfigInfo configInfo) { McpServerVersionInfo obj = JacksonUtils.toObj(configInfo.getContent(), McpServerVersionInfo.class); obj.setNamespaceId(configInfo.getTenant()); return obj; } protected McpServerIndexData mcpToIndexAndUpdateToCache(McpServerVersionInfo versionInfo) { McpServerIndexData data = new McpServerIndexData(); data.setId(versionInfo.getId()); data.setNamespaceId(versionInfo.getNamespaceId()); afterSearch(data, versionInfo.getName()); return data; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/index/CachedMcpServerIndex.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.annotation.PreDestroy; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.core.service.NamespaceOperationService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Enhanced MCP cache index implementation combining memory cache and database queries. * * @author misselvexu */ public class CachedMcpServerIndex extends AbstractMcpServerIndex { private static final Logger LOGGER = LoggerFactory.getLogger(CachedMcpServerIndex.class); private final McpCacheIndex cacheIndex; private final ConfigQueryChainService configQueryChainService; private final ScheduledExecutorService scheduledExecutor; private ScheduledFuture syncTask; private final boolean cacheEnabled; private final long syncInterval; /** * Constructor. */ public CachedMcpServerIndex(ConfigDetailService configDetailService, NamespaceOperationService namespaceOperationService, ConfigQueryChainService configQueryChainService, McpCacheIndex cacheIndex, ScheduledExecutorService scheduledExecutor, boolean cacheEnabled, long syncInterval) { super(namespaceOperationService, configDetailService); this.configQueryChainService = configQueryChainService; this.cacheIndex = cacheIndex; this.scheduledExecutor = scheduledExecutor; this.cacheEnabled = cacheEnabled; this.syncInterval = syncInterval; if (cacheEnabled) { startSyncTask(); } LOGGER.info("CachedMcpServerIndex initialized with cacheEnabled={}, syncInterval={}s", cacheEnabled, syncInterval); } /** * Get MCP server information by ID. */ @Override public McpServerIndexData getMcpServerById(String id) { if (!cacheEnabled) { LOGGER.debug("Cache disabled, querying directly from database for mcpId: {}", id); return getMcpServerByIdFromDatabase(id); } // Priority query cache McpServerIndexData cachedData = cacheIndex.getMcpServerById(id); if (cachedData != null) { LOGGER.debug("Cache hit for mcpId: {}", id); return cachedData; } // Cache miss, query database LOGGER.debug("Cache miss for mcpId: {}, querying database", id); McpServerIndexData dbData = getMcpServerByIdFromDatabase(id); if (dbData != null) { cacheIndex.updateIndex(dbData.getNamespaceId(), dbData.getId(), dbData.getId()); LOGGER.debug("Updated cache for mcpId: {}", id); } return dbData; } /** * Get MCP server information by name. */ @Override public McpServerIndexData getMcpServerByName(String namespaceId, String name) { if (StringUtils.isEmpty(namespaceId) && StringUtils.isEmpty(name)) { LOGGER.warn("Invalid parameters for getMcpServerByName: namespaceId={}, name={}", namespaceId, name); return null; } if (StringUtils.isEmpty(namespaceId)) { return getFirstMcpServerByName(name); } if (!cacheEnabled) { LOGGER.debug("Cache disabled, querying directly from database for name: {}:{}", namespaceId, name); return getMcpServerByNameFromDatabase(namespaceId, name); } // Priority query cache McpServerIndexData cachedData = cacheIndex.getMcpServerByName(namespaceId, name); if (cachedData != null) { LOGGER.debug("Cache hit for name: {}:{}", namespaceId, name); return cachedData; } // Cache miss, query database LOGGER.debug("Cache miss for name: {}:{}, querying database", namespaceId, name); McpServerIndexData dbData = getMcpServerByNameFromDatabase(namespaceId, name); if (dbData != null) { cacheIndex.updateIndex(namespaceId, name, dbData.getId()); LOGGER.debug("Updated cache for name: {}:{}", namespaceId, name); } return dbData; } @Override protected void afterSearch(McpServerIndexData indexData, String name) { // Update cache if (cacheEnabled) { cacheIndex.updateIndex(indexData.getNamespaceId(), name, indexData.getId()); } } /** * Get MCP server from database by ID. */ private McpServerIndexData getMcpServerByIdFromDatabase(String id) { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId(id + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); request.setGroup(Constants.MCP_SERVER_VERSIONS_GROUP); List namespaceList = fetchOrderedNamespaceList(); for (String namespaceId : namespaceList) { request.setTenant(namespaceId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL) { McpServerIndexData result = new McpServerIndexData(); result.setId(id); result.setNamespaceId(namespaceId); LOGGER.debug("Found MCP server in database: mcpId={}, namespaceId={}", id, namespaceId); return result; } } LOGGER.debug("MCP server not found in database: mcpId={}", id); return null; } /** * Get MCP server from database by name. */ private McpServerIndexData getMcpServerByNameFromDatabase(String namespaceId, String name) { // 直接查询数据库,避免调用searchMcpServerByName导致重复更新缓存 Page serverInfos = searchMcpServers(namespaceId, name, Constants.MCP_LIST_SEARCH_ACCURATE, 1, 1); if (CollectionUtils.isNotEmpty(serverInfos.getPageItems())) { ConfigInfo configInfo = serverInfos.getPageItems().get(0); McpServerIndexData result = new McpServerIndexData(); result.setId(configInfo.getDataId().replace(Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX, "")); result.setNamespaceId(configInfo.getTenant()); LOGGER.debug("Found MCP server in database: name={}:{}, mcpId={}", namespaceId, name, result.getId()); return result; } LOGGER.debug("MCP server not found in database: name={}:{}", namespaceId, name); return null; } /** * Start scheduled sync task. */ private void startSyncTask() { syncTask = scheduledExecutor.scheduleWithFixedDelay(() -> { try { LOGGER.debug("Starting cache sync task"); syncCacheFromDatabase(); LOGGER.debug("Cache sync task completed"); } catch (Exception e) { LOGGER.error("Error during cache sync task", e); } }, syncInterval, syncInterval, TimeUnit.SECONDS); LOGGER.info("Cache sync task started with interval: {}s", syncInterval); } /** * Shutdown the cache sync task and cleanup resources. */ @PreDestroy public void destroy() { try { if (syncTask != null) { syncTask.cancel(true); } scheduledExecutor.shutdown(); } catch (Exception e) { LOGGER.warn("shutting down sync task schedule executor failed", e); } } /** * Sync cache from database. */ private void syncCacheFromDatabase() { LOGGER.debug("Syncing cache from database"); List namespaceList = fetchOrderedNamespaceList(); for (String namespaceId : namespaceList) { try { searchMcpServerByNameWithPage(namespaceId, null, Constants.MCP_LIST_SEARCH_BLUR, 1, 1000); } catch (Exception e) { LOGGER.error("Error syncing cache for namespace: {}", namespaceId, e); } } } /** * Get cache statistics. */ public McpCacheIndex.CacheStats getCacheStats() { McpCacheIndex.CacheStats stats = cacheIndex.getStats(); LOGGER.debug("Cache stats: hitCount={}, missCount={}, evictionCount={}, size={}, hitRate=%.2f%%", stats.getHitCount(), stats.getMissCount(), stats.getEvictionCount(), stats.getSize(), stats.getHitRate() * 100); return stats; } /** * Clear cache. */ public void clearCache() { cacheIndex.clear(); LOGGER.info("Cache cleared"); } /** * Manually trigger cache synchronization. */ public void triggerCacheSync() { if (cacheEnabled) { LOGGER.info("Manual cache sync triggered"); syncCacheFromDatabase(); } else { LOGGER.warn("Cache is disabled, manual sync ignored"); } } /** * Remove cache entry by namespace ID and MCP server name. * * @param namespaceId namespace ID * @param mcpName MCP server name */ @Override public void removeMcpServerByName(String namespaceId, String mcpName) { if (cacheEnabled) { LOGGER.debug("Removing cache entry by name: namespaceId={}, mcpName={}", namespaceId, mcpName); cacheIndex.removeIndex(namespaceId, mcpName); } else { LOGGER.debug("Cache is disabled, ignoring cache removal by name: namespaceId={}, mcpName={}", namespaceId, mcpName); } } /** * Remove cache entry by MCP server ID. * * @param mcpId MCP server ID */ @Override public void removeMcpServerById(String mcpId) { if (cacheEnabled) { LOGGER.debug("Removing cache entry by ID: mcpId={}", mcpId); cacheIndex.removeIndex(mcpId); } else { LOGGER.debug("Cache is disabled, ignoring cache removal by ID: mcpId={}", mcpId); } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/index/McpCacheIndex.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; /** * MCP cache index interface providing fast mapping between MCP Name and MCP ID. * * @author misselvexu */ public interface McpCacheIndex { /** * Get MCP ID by namespace ID and MCP name. * * @param namespaceId namespace ID * @param mcpName MCP name * @return MCP ID, returns null if not found */ String getMcpId(String namespaceId, String mcpName); /** * Get MCP server information by namespace ID and MCP name. * * @param namespaceId namespace ID * @param mcpName MCP name * @return MCP server information, returns null if not found */ McpServerIndexData getMcpServerByName(String namespaceId, String mcpName); /** * Get MCP server information by MCP ID. * * @param mcpId MCP ID * @return MCP server information, returns null if not found */ McpServerIndexData getMcpServerById(String mcpId); /** * Update index. * * @param namespaceId namespace ID * @param mcpName MCP name * @param mcpId MCP ID */ void updateIndex(String namespaceId, String mcpName, String mcpId); /** * Remove index by name. * * @param namespaceId namespace ID * @param mcpName MCP name */ void removeIndex(String namespaceId, String mcpName); /** * Remove index by ID. * * @param mcpId MCP ID */ void removeIndex(String mcpId); /** * Clear cache. */ void clear(); /** * Get cache size. * * @return number of cache entries */ int getSize(); /** * Get cache statistics. * * @return cache statistics */ CacheStats getStats(); /** * Cache statistics. */ class CacheStats { private final long hitCount; private final long missCount; private final long evictionCount; private final long size; public CacheStats(long hitCount, long missCount, long evictionCount, long size) { this.hitCount = hitCount; this.missCount = missCount; this.evictionCount = evictionCount; this.size = size; } public long getHitCount() { return hitCount; } public long getMissCount() { return missCount; } public long getEvictionCount() { return evictionCount; } public long getSize() { return size; } public double getHitRate() { long total = hitCount + missCount; return total == 0 ? 0.0 : (double) hitCount / total; } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/index/McpServerIndex.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.api.model.Page; /** * Server info index interface. We should know the relation between the mcp server id and namespaceId + mcpServerName. * * @author xinluo */ public interface McpServerIndex { /** * Search Mcp server by name and namespaceId with pagination. * * @param namespaceId namespace ID * @param name mcp server name * @param search search mode * @param pageNo page number * @param limit page size limit * @return MCP Server Index Data page */ Page searchMcpServerByNameWithPage(String namespaceId, String name, String search, int pageNo, int limit); /** * Get mcp server by id. * * @param id mcp server id * @return {@link McpServerIndexData} */ McpServerIndexData getMcpServerById(String id); /** * Get mcp server by namespaceId and servername. * * @param namespaceId namespaceId * @param name servername * @return {@link McpServerIndexData} */ McpServerIndexData getMcpServerByName(String namespaceId, String name); /** * Remove cache entry by namespace ID and MCP server name. * * @param namespaceId namespace ID * @param mcpName MCP server name */ void removeMcpServerByName(String namespaceId, String mcpName); /** * Remove cache entry by MCP server ID. * * @param mcpId MCP server ID */ void removeMcpServerById(String mcpId); } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/index/MemoryMcpCacheIndex.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import com.alibaba.nacos.ai.config.McpCacheIndexProperties; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Memory-based MCP cache index implementation with optimized locking. * *

* TODO This Memory cache might include some design issues: *

    *
  • * 1. The read method in cache include LRU operation(write), which means read lock can't intercept write operation * in multiple threads reading and cause thread-safe problem. *
  • *
  • * 2. For solve problem 1. Use {@code synchronized} wrapper {@link #removeFromLru} and {@link #moveToHead} method, * which may cause the read operation performance will be affected in high qps. *
  • *
  • * 3. The next consider it whether keep the LRU behavior in next versions when qps improved. If keep it, the LRU cache should * be re-designed or use stabled high performance LRU cache such as guava. *
  • *
*

* * @author misselvexu */ public class MemoryMcpCacheIndex implements McpCacheIndex { private static final Logger LOGGER = LoggerFactory.getLogger(MemoryMcpCacheIndex.class); private static final int DEFAULT_SHUTDOWN_TIMEOUT_SECONDS = 5; private final McpCacheIndexProperties properties; private final ConcurrentHashMap idToEntry; private final ConcurrentHashMap nameKeyToId; private final CacheNode head; private final CacheNode tail; private final ReentrantReadWriteLock lock; private final ReentrantReadWriteLock.ReadLock readLock; private final ReentrantReadWriteLock.WriteLock writeLock; private final AtomicLong hitCount; private final AtomicLong missCount; private final AtomicLong evictionCount; private final ScheduledExecutorService cleanupScheduler; private volatile boolean shutdown = false; public MemoryMcpCacheIndex(McpCacheIndexProperties properties) { this.properties = properties; // Initialize cache storage this.idToEntry = new ConcurrentHashMap<>(properties.getMaxSize()); this.nameKeyToId = new ConcurrentHashMap<>(); // Initialize LRU linked list this.head = new CacheNode("", null, 0); this.tail = new CacheNode("", null, 0); this.head.next = this.tail; this.tail.prev = this.head; // Initialize lock this.lock = new ReentrantReadWriteLock(); this.readLock = lock.readLock(); this.writeLock = lock.writeLock(); // Initialize statistics this.hitCount = new AtomicLong(0); this.missCount = new AtomicLong(0); this.evictionCount = new AtomicLong(0); // Start cleanup scheduler this.cleanupScheduler = new ScheduledThreadPoolExecutor(1, r -> { Thread t = new Thread(r, "mcp-cache-cleanup"); t.setDaemon(true); return t; }, new ThreadPoolExecutor.CallerRunsPolicy()); // Schedule periodic cleanup this.cleanupScheduler.scheduleWithFixedDelay(this::cleanupExpiredEntries, properties.getCleanupIntervalSeconds(), properties.getCleanupIntervalSeconds(), TimeUnit.SECONDS); } @Override public String getMcpId(String namespaceId, String mcpName) { if (StringUtils.isBlank(namespaceId) || StringUtils.isBlank(mcpName)) { return null; } String key = buildNameKey(namespaceId, mcpName); readLock.lock(); try { String id = nameKeyToId.get(key); if (id == null) { missCount.incrementAndGet(); return null; } CacheNode node = idToEntry.get(id); if (node == null || node.isExpired(properties.getExpireTimeSeconds())) { // Clean up invalid mapping nameKeyToId.remove(key, id); if (node != null) { removeFromLru(node); idToEntry.remove(id, node); } missCount.incrementAndGet(); return null; } // Update LRU position moveToHead(node); hitCount.incrementAndGet(); return id; } finally { readLock.unlock(); } } @Override public McpServerIndexData getMcpServerByName(String namespaceId, String mcpName) { String id = getMcpId(namespaceId, mcpName); if (id == null) { return null; } return getMcpServerById(id); } @Override public McpServerIndexData getMcpServerById(String mcpId) { if (StringUtils.isBlank(mcpId)) { return null; } readLock.lock(); try { CacheNode node = idToEntry.get(mcpId); if (node == null || node.isExpired(properties.getExpireTimeSeconds())) { if (node != null) { removeFromLru(node); idToEntry.remove(mcpId, node); cleanupInvalidMappings(mcpId); } missCount.incrementAndGet(); return null; } // Update LRU position moveToHead(node); hitCount.incrementAndGet(); return node.data; } finally { readLock.unlock(); } } @Override public void updateIndex(String namespaceId, String mcpName, String mcpId) { if (StringUtils.isBlank(namespaceId) || StringUtils.isBlank(mcpName) || StringUtils.isBlank(mcpId)) { return; } McpServerIndexData data = McpServerIndexData.newIndexData(mcpId, namespaceId); CacheNode newNode = new CacheNode(mcpId, data, System.currentTimeMillis() / 1000); writeLock.lock(); try { CacheNode oldNode = idToEntry.put(mcpId, newNode); if (oldNode != null) { // Remove old node from LRU list removeFromLru(oldNode); } // Add to head of LRU list addToHead(newNode); // Check if eviction is needed and evict until size is correct while (idToEntry.size() > properties.getMaxSize()) { evictLeastRecentlyUsed(); } // Update name mapping String key = buildNameKey(namespaceId, mcpName); nameKeyToId.put(key, mcpId); } finally { writeLock.unlock(); } } @Override public void removeIndex(String namespaceId, String mcpName) { if (StringUtils.isBlank(namespaceId) || StringUtils.isBlank(mcpName)) { return; } String key = buildNameKey(namespaceId, mcpName); String id = nameKeyToId.remove(key); if (id != null) { CacheNode node = idToEntry.remove(id); if (node != null) { removeFromLru(node); } } } @Override public void removeIndex(String mcpId) { if (StringUtils.isBlank(mcpId)) { return; } CacheNode node = idToEntry.remove(mcpId); if (node != null) { removeFromLru(node); } cleanupInvalidMappings(mcpId); } @Override public void clear() { writeLock.lock(); try { idToEntry.clear(); nameKeyToId.clear(); head.next = tail; tail.prev = head; } finally { writeLock.unlock(); } hitCount.set(0); missCount.set(0); evictionCount.set(0); } @Override public int getSize() { return idToEntry.size(); } @Override public CacheStats getStats() { return new CacheStats(hitCount.get(), missCount.get(), evictionCount.get(), getSize()); } /** * Shuts down the cache and cleans up resources. */ public void shutdown() { if (!shutdown) { shutdown = true; cleanupScheduler.shutdown(); try { if (!cleanupScheduler.awaitTermination(DEFAULT_SHUTDOWN_TIMEOUT_SECONDS, TimeUnit.SECONDS)) { cleanupScheduler.shutdownNow(); } } catch (InterruptedException e) { cleanupScheduler.shutdownNow(); Thread.currentThread().interrupt(); } clear(); } } private String buildNameKey(String namespaceId, String mcpName) { return namespaceId + "::" + mcpName; } private void cleanupInvalidMappings(String mcpId) { nameKeyToId.entrySet().removeIf(entry -> mcpId.equals(entry.getValue())); } private void cleanupExpiredEntries() { if (shutdown) { return; } try { Iterator> iterator = idToEntry.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); CacheNode node = entry.getValue(); if (node.isExpired(properties.getExpireTimeSeconds())) { iterator.remove(); removeFromLru(node); cleanupInvalidMappings(entry.getKey()); evictionCount.incrementAndGet(); } } } catch (Exception e) { LOGGER.error("Clean up expired mcp id and name cache failed.", e); } } private void evictLeastRecentlyUsed() { CacheNode last = tail.prev; if (last != head) { CacheNode removed = idToEntry.remove(last.key); if (removed != null) { removeFromLru(last); cleanupInvalidMappings(last.key); evictionCount.incrementAndGet(); } } } private void addToHead(CacheNode node) { node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; } private synchronized void removeFromLru(CacheNode node) { if (node.prev != null && node.next != null) { node.prev.next = node.next; node.next.prev = node.prev; } } private synchronized void moveToHead(CacheNode node) { // Remove from current position if (node.prev != null && node.next != null) { node.prev.next = node.next; node.next.prev = node.prev; } // Add to head node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; } // Inner classes private static class CacheNode { final String key; final McpServerIndexData data; final long createTimeSeconds; volatile CacheNode prev; volatile CacheNode next; CacheNode(String key, McpServerIndexData data, long createTimeSeconds) { this.key = key; this.data = data; this.createTimeSeconds = createTimeSeconds; } boolean isExpired(long expireTimeSeconds) { if (expireTimeSeconds <= 0) { return false; } long currentTimeSeconds = System.currentTimeMillis() / 1000; return (currentTimeSeconds - createTimeSeconds) >= expireTimeSeconds; } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/index/PlainMcpServerIndex.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import java.util.List; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.utils.McpConfigUtils; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.core.service.NamespaceOperationService; /** * Plain Mcp server index implementation. This is empty index implementation so the performance is not well. this should * be implemented by memory index or db index. * * @author xinluo */ public class PlainMcpServerIndex extends AbstractMcpServerIndex { private final ConfigQueryChainService configQueryChainService; public PlainMcpServerIndex(NamespaceOperationService namespaceOperationService, ConfigDetailService configDetailService, ConfigQueryChainService configQueryChainService) { super(namespaceOperationService, configDetailService); this.configQueryChainService = configQueryChainService; } /** * Get mcp server by id. * * @param id mcp server id * @return {@link McpServerIndexData} return null if server not found */ @Override public McpServerIndexData getMcpServerById(String id) { if (StringUtils.isEmpty(id)) { return null; } List namespaceList = fetchOrderedNamespaceList(); for (String namespaceId : namespaceList) { McpServerIndexData result = getMcpServerById(namespaceId, id); if (result != null) { return result; } } return null; } public McpServerIndexData getMcpServerById(String namespaceId, String id) { ConfigQueryChainRequest request = buildConfigQueryChainRequest(namespaceId, id); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (McpConfigUtils.isConfigFound(response.getStatus())) { return McpServerIndexData.newIndexData(id, namespaceId); } return null; } private ConfigQueryChainRequest buildConfigQueryChainRequest(String namespaceId, String serverId) { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setTenant(namespaceId); request.setDataId(McpConfigUtils.formatServerVersionInfoDataId(serverId)); request.setGroup(Constants.MCP_SERVER_VERSIONS_GROUP); return request; } /** * Get mcp server by namespaceId and servername. * If namespaceId is empty, we search all namespaces and return the first found server. * @param namespaceId namespaceId * @param name servername * @return {@link McpServerIndexData} */ @Override public McpServerIndexData getMcpServerByName(String namespaceId, String name) { if (StringUtils.isEmpty(namespaceId)) { return getFirstMcpServerByName(name); } Page indexDataPage = searchMcpServerByNameWithPage(namespaceId, name, Constants.MCP_LIST_SEARCH_ACCURATE, 1, 1); if (CollectionUtils.isNotEmpty(indexDataPage.getPageItems())) { return indexDataPage.getPageItems().get(0); } return null; } /** * Remove cache entry by namespace ID and MCP server name. This is a no-op implementation since PlainMcpServerIndex * doesn't use cache. * * @param namespaceId namespace ID * @param mcpName MCP server name */ @Override public void removeMcpServerByName(String namespaceId, String mcpName) { // No-op implementation since PlainMcpServerIndex doesn't use cache } /** * Remove cache entry by MCP server ID. This is a no-op implementation since PlainMcpServerIndex doesn't use cache. * * @param mcpId MCP server ID */ @Override public void removeMcpServerById(String mcpId) { // No-op implementation since PlainMcpServerIndex doesn't use cache } @Override protected void afterSearch(McpServerIndexData searchResult, String name) { } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/model/mcp/McpServerIndexData.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.model.mcp; /** * McpServerIndexData. * * @author xinluo */ public class McpServerIndexData { private String id; private String namespaceId; /** * Factory method for index data. * @param id server id * @param namespaceId namespaceId * @return index */ public static McpServerIndexData newIndexData(String id, String namespaceId) { McpServerIndexData data = new McpServerIndexData(); data.setNamespaceId(namespaceId); data.setId(id); return data; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/model/mcp/McpServerStorageInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; /** * Storage model for AI MCP server. * * @author xiweng.yy */ public class McpServerStorageInfo extends McpServerBasicInfo { private String toolsDescriptionRef; private String promptDescriptionRef; private String resourceDescriptionRef; public String getToolsDescriptionRef() { return toolsDescriptionRef; } public void setToolsDescriptionRef(String toolsDescriptionRef) { this.toolsDescriptionRef = toolsDescriptionRef; } public String getPromptDescriptionRef() { return promptDescriptionRef; } public void setPromptDescriptionRef(String promptDescriptionRef) { this.promptDescriptionRef = promptDescriptionRef; } public String getResourceDescriptionRef() { return resourceDescriptionRef; } public void setResourceDescriptionRef(String resourceDescriptionRef) { this.resourceDescriptionRef = resourceDescriptionRef; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/model/mcp/UrlPageResult.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import java.util.List; /** * Url page result. * @author xinluo */ public class UrlPageResult { private List servers; private String nextCursor; public UrlPageResult(List servers, String nextCursor) { this.servers = servers; this.nextCursor = nextCursor; } public List getServers() { return servers; } public String getNextCursor() { return nextCursor; } public void setNextCursor(String nextCursor) { this.nextCursor = nextCursor; } public void setServers(List servers) { this.servers = servers; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/param/AgentHttpParamExtractor.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.param; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; /** * Nacos AI Agent or AgentCard param extractor. * * @author xiweng.yy */ public class AgentHttpParamExtractor extends AbstractHttpParamExtractor { private static final String AGENT_CARD_PARAM = "agentCard"; @Override public List extractParam(HttpServletRequest request) throws NacosException { ParamInfo paramInfo = new ParamInfo(); paramInfo.setNamespaceId(request.getParameter("namespaceId")); paramInfo.setAgentName(request.getParameter("agentName")); if (request.getParameterMap().containsKey(AGENT_CARD_PARAM)) { paramInfo.setAgentName(deserializeAndGetAgentName(request.getParameter(AGENT_CARD_PARAM))); } return Collections.singletonList(paramInfo); } private String deserializeAndGetAgentName(String agentCardJson) { try { AgentCard agentCard = JacksonUtils.toObj(agentCardJson, AgentCard.class); return agentCard.getName(); } catch (Exception ignored) { return StringUtils.EMPTY; } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/param/McpHttpParamExtractor.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.param; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; /** * Nacos AI MCP server param extractor. * * @author xiweng.yy */ public class McpHttpParamExtractor extends AbstractHttpParamExtractor { @Override public List extractParam(HttpServletRequest request) throws NacosException { ParamInfo paramInfo = new ParamInfo(); paramInfo.setNamespaceId(request.getParameter("namespaceId")); paramInfo.setMcpName(request.getParameter("mcpName")); paramInfo.setMcpId(request.getParameter("mcpId")); return Collections.singletonList(paramInfo); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/param/PromptHttpParamExtractor.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.param; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; /** * Nacos AI Prompt param extractor. * * @author nacos */ public class PromptHttpParamExtractor extends AbstractHttpParamExtractor { private static final String PROMPT_DATA_ID_SUFFIX = ".json"; @Override public List extractParam(HttpServletRequest request) throws NacosException { ParamInfo paramInfo = new ParamInfo(); paramInfo.setNamespaceId(request.getParameter("namespaceId")); String promptKey = request.getParameter("promptKey"); if (StringUtils.isNotBlank(promptKey)) { paramInfo.setDataId(promptKey + PROMPT_DATA_ID_SUFFIX); } return Collections.singletonList(paramInfo); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/param/SkillHttpParamExtractor.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.param; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.util.Collections; import java.util.List; /** * Nacos AI Skill param extractor. * * @author nacos */ public class SkillHttpParamExtractor extends AbstractHttpParamExtractor { private static final String SKILL_CARD_PARAM = "skillCard"; @Override public List extractParam(HttpServletRequest request) throws NacosException { ParamInfo paramInfo = new ParamInfo(); paramInfo.setNamespaceId(request.getParameter("namespaceId")); paramInfo.setAgentName(request.getParameter("skillName")); if (request.getParameterMap().containsKey(SKILL_CARD_PARAM)) { paramInfo.setAgentName(deserializeAndGetSkillName(request.getParameter(SKILL_CARD_PARAM))); } return Collections.singletonList(paramInfo); } private String deserializeAndGetSkillName(String skillCardJson) { try { Skill skill = JacksonUtils.toObj(skillCardJson, Skill.class); return skill.getName(); } catch (Exception ignored) { return StringUtils.EMPTY; } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/McpServerEndpointRequestHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.ai.utils.McpRequestUtil; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.McpServerEndpointRequest; import com.alibaba.nacos.api.ai.remote.response.McpServerEndpointResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.DeregisterInstanceReason; import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.RegisterInstanceTraceEvent; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.McpServerRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; import com.alibaba.nacos.naming.utils.NamingRequestUtil; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Register or Deregister endpoint for mcp server to nacos AI module request handler. * * @author xiweng.yy */ @Component public class McpServerEndpointRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(McpServerEndpointRequestHandler.class); private static final String VERSION_TAG = "_mcp_server_version"; private final EphemeralClientOperationServiceImpl clientOperationService; private final McpServerOperationService mcpServerOperationService; private final McpServerIndex mcpServerIndex; public McpServerEndpointRequestHandler(EphemeralClientOperationServiceImpl clientOperationService, McpServerOperationService mcpServerOperationService, McpServerIndex mcpServerIndex) { this.clientOperationService = clientOperationService; this.mcpServerOperationService = mcpServerOperationService; this.mcpServerIndex = mcpServerIndex; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = McpServerRequestParamExtractor.class) @Secured(action = ActionTypes.WRITE, signType = SignType.AI) public McpServerEndpointResponse handle(McpServerEndpointRequest request, RequestMeta meta) throws NacosException { McpRequestUtil.fillNamespaceId(request); try { checkParameters(request); Instance instance = buildInstance(request); return doHandler(request, instance, meta); } catch (NacosException e) { McpServerEndpointResponse response = new McpServerEndpointResponse(); response.setErrorInfo(e.getErrCode(), e.getErrMsg()); return response; } } private void checkParameters(McpServerEndpointRequest request) throws NacosApiException { if (StringUtils.isBlank(request.getMcpName())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `mcpName` can't be empty or null"); } } private McpServerEndpointResponse doHandler(McpServerEndpointRequest request, Instance instance, RequestMeta meta) throws NacosException { McpServerIndexData indexData = mcpServerIndex.getMcpServerByName(request.getNamespaceId(), request.getMcpName()); if (null == indexData) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SERVER_NOT_FOUND, String.format("MCP server `%s` not found in namespaceId: `%s`", request.getMcpName(), request.getNamespaceId())); } McpServerDetailInfo mcpServer = mcpServerOperationService.getMcpServerDetail(request.getNamespaceId(), indexData.getId(), null, request.getVersion()); McpServiceRef serviceRef = buildServiceRef(mcpServer); if (null == serviceRef) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SERVER_REF_ENDPOINT_SERVICE_NOT_FOUND, "The Mcp Server Ref endpoint service not found."); } Service service = Service.newService(request.getNamespaceId(), serviceRef.getGroupName(), serviceRef.getServiceName(), true); switch (request.getType()) { case AiRemoteConstants.REGISTER_ENDPOINT: LOGGER.info("[{}] register endpoint {}:{} version {} for mcp server: {}", meta.getConnectionId(), request.getAddress(), request.getPort(), request.getVersion(), request.getMcpName()); doRegister(service, instance, meta); break; case AiRemoteConstants.DE_REGISTER_ENDPOINT: LOGGER.info("[{}] de-register endpoint {}:{} version {} for mcp server: {}", meta.getConnectionId(), request.getAddress(), request.getPort(), request.getVersion(), request.getMcpName()); doDeregister(service, instance, meta); break; default: throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, String.format("parameter `type` should be %s or %s, but was %s", AiRemoteConstants.REGISTER_ENDPOINT, AiRemoteConstants.DE_REGISTER_ENDPOINT, request.getType())); } McpServerEndpointResponse response = new McpServerEndpointResponse(); response.setType(request.getType()); return response; } private Instance buildInstance(McpServerEndpointRequest request) throws NacosApiException { Instance instance = new Instance(); instance.setIp(request.getAddress()); instance.setPort(request.getPort()); instance.validate(); if (StringUtils.isNotBlank(request.getVersion())) { instance.getMetadata().put(VERSION_TAG, request.getVersion()); } return instance; } private McpServiceRef buildServiceRef(McpServerDetailInfo mcpServer) { boolean isRegisterToFrontend = AiConstants.Mcp.MCP_PROTOCOL_HTTP.equals(mcpServer.getProtocol()); McpServiceRef result = null; if (isRegisterToFrontend) { for (FrontEndpointConfig each : mcpServer.getRemoteServerConfig().getFrontEndpointConfigList()) { if (AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF.equals(each.getEndpointType())) { result = McpRequestUtil.transferToMcpServiceRef(each.getEndpointData()); break; } } } else { result = mcpServer.getRemoteServerConfig().getServiceRef(); } return result; } private void doRegister(Service service, Instance instance, RequestMeta meta) throws NacosException { clientOperationService.registerInstance(service, instance, meta.getConnectionId()); NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort())); } private void doDeregister(Service service, Instance instance, RequestMeta meta) { clientOperationService.deregisterInstance(service, instance, meta.getConnectionId()); NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, DeregisterInstanceReason.REQUEST, service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort())); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/QueryMcpServerRequestHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.ai.utils.McpRequestUtil; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.remote.request.QueryMcpServerRequest; import com.alibaba.nacos.api.ai.remote.response.QueryMcpServerResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.McpServerRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.stereotype.Component; /** * Nacos AI module query mcp request handler. * * @author xiweng.yy */ @Component public class QueryMcpServerRequestHandler extends RequestHandler { private final McpServerOperationService mcpServerOperationService; private final McpServerIndex mcpServerIndex; public QueryMcpServerRequestHandler(McpServerOperationService mcpServerOperationService, McpServerIndex mcpServerIndex) { this.mcpServerOperationService = mcpServerOperationService; this.mcpServerIndex = mcpServerIndex; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = McpServerRequestParamExtractor.class) @Secured(action = ActionTypes.READ, signType = SignType.AI) public QueryMcpServerResponse handle(QueryMcpServerRequest request, RequestMeta meta) throws NacosException { McpRequestUtil.fillNamespaceId(request); if (StringUtils.isBlank(request.getMcpName())) { QueryMcpServerResponse errorResponse = new QueryMcpServerResponse(); errorResponse.setErrorInfo(NacosException.INVALID_PARAM, "parameters `mcpName` can't be empty or null"); return errorResponse; } return doHandler(request, meta); } private QueryMcpServerResponse doHandler(QueryMcpServerRequest request, RequestMeta meta) throws NacosException { McpServerIndexData indexData = mcpServerIndex.getMcpServerByName(request.getNamespaceId(), request.getMcpName()); QueryMcpServerResponse response = new QueryMcpServerResponse(); if (null == indexData) { response.setErrorInfo(NacosException.NOT_FOUND, String.format("MCP server `%s` not found in namespaceId: `%s`", request.getMcpName(), request.getNamespaceId())); return response; } McpServerDetailInfo detailInfo = mcpServerOperationService.getMcpServerDetail(request.getNamespaceId(), indexData.getId(), null, request.getVersion()); response.setMcpServerDetailInfo(detailInfo); return response; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/QueryPromptRequestHandler.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.service.prompt.PromptClientOperationService; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.remote.request.QueryPromptRequest; import com.alibaba.nacos.api.ai.remote.response.QueryPromptResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.PromptRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Nacos AI module query prompt request handler. * * @author nacos */ @Component public class QueryPromptRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(QueryPromptRequestHandler.class); private final PromptClientOperationService promptOperationService; public QueryPromptRequestHandler(PromptClientOperationService promptOperationService) { this.promptOperationService = promptOperationService; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = PromptRequestParamExtractor.class) @Secured(action = ActionTypes.READ, signType = SignType.AI) public QueryPromptResponse handle(QueryPromptRequest request, RequestMeta meta) { request.setNamespaceId(NamespaceUtil.processNamespaceParameter(request.getNamespaceId())); if (StringUtils.isBlank(request.getPromptKey())) { QueryPromptResponse errorResponse = new QueryPromptResponse(); errorResponse.setErrorInfo(NacosException.INVALID_PARAM, "parameters `promptKey` can't be empty or null"); return errorResponse; } QueryPromptResponse response = new QueryPromptResponse(); try { PromptVersionInfo result = promptOperationService.queryPrompt( request.getNamespaceId(), request.getPromptKey(), request.getVersion(), request.getLabel(), request.getMd5()); response.setPromptInfo(convertToClientPrompt(result)); } catch (NacosException e) { if (e.getErrCode() == NacosException.NOT_MODIFIED) { response.setErrorInfo(NacosException.NOT_MODIFIED, "prompt data is up to date"); return response; } LOGGER.error("Query prompt {} error: {}", request.getPromptKey(), e.getErrMsg()); response.setErrorInfo(e.getErrCode(), e.getErrMsg()); } return response; } private Prompt convertToClientPrompt(PromptVersionInfo versionInfo) { Prompt prompt = new Prompt(); prompt.setPromptKey(versionInfo.getPromptKey()); prompt.setVersion(versionInfo.getVersion()); prompt.setTemplate(versionInfo.getTemplate()); prompt.setMd5(versionInfo.getMd5()); prompt.setVariables(versionInfo.getVariables()); return prompt; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/ReleaseMcpServerRequestHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.service.McpEndpointOperationService; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.ai.utils.McpRequestUtil; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.remote.request.ReleaseMcpServerRequest; import com.alibaba.nacos.api.ai.remote.response.ReleaseMcpServerResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.McpServerRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Nacos AI module release new mcp server or new version of exist mcp server request handler. * * @author xiweng.yy */ @Component public class ReleaseMcpServerRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseMcpServerRequest.class); private final McpServerOperationService mcpServerOperationService; private final McpEndpointOperationService endpointOperationService; private final McpServerIndex mcpServerIndex; public ReleaseMcpServerRequestHandler(McpServerOperationService mcpServerOperationService, McpEndpointOperationService endpointOperationService, McpServerIndex mcpServerIndex) { this.mcpServerOperationService = mcpServerOperationService; this.endpointOperationService = endpointOperationService; this.mcpServerIndex = mcpServerIndex; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = McpServerRequestParamExtractor.class) @Secured(action = ActionTypes.WRITE, signType = SignType.AI) public ReleaseMcpServerResponse handle(ReleaseMcpServerRequest request, RequestMeta meta) throws NacosException { McpRequestUtil.fillNamespaceId(request); try { checkParameters(request); return doHandler(request, meta); } catch (NacosException e) { ReleaseMcpServerResponse response = new ReleaseMcpServerResponse(); response.setErrorInfo(e.getErrCode(), e.getErrMsg()); return response; } } private void checkParameters(ReleaseMcpServerRequest request) throws NacosException { McpServerBasicInfo serverSpecification = request.getServerSpecification(); if (null == serverSpecification) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'serverSpecification' type McpServerBasicInfo is not present"); } String mcpName = serverSpecification.getName(); if (StringUtils.isEmpty(mcpName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'serverSpecification.name' type String is not present"); } if (null == serverSpecification.getVersionDetail() || StringUtils.isBlank( serverSpecification.getVersionDetail().getVersion())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `serverSpecification.versionDetail.version` not present"); } } private ReleaseMcpServerResponse doHandler(ReleaseMcpServerRequest request, RequestMeta meta) throws NacosException { String namespaceId = request.getNamespaceId(); McpServerBasicInfo serverSpecification = request.getServerSpecification(); LOGGER.info("Release new mcp server {}, version {} into namespaceId {} from connectionId {}.", serverSpecification.getName(), serverSpecification.getVersionDetail().getVersion(), namespaceId, meta.getConnectionId()); ReleaseMcpServerResponse response = new ReleaseMcpServerResponse(); try { // mcp server and version found, means this version of mcp server has been release, throw exception. McpServerBasicInfo existMcpServer = mcpServerOperationService.getMcpServerDetail(namespaceId, serverSpecification.getId(), serverSpecification.getName(), serverSpecification.getVersionDetail().getVersion()); String version = existMcpServer.getVersionDetail().getVersion(); LOGGER.info("Mcp Server {} and target version {} already exist.", existMcpServer.getName(), version); throw new NacosApiException(NacosException.CONFLICT, ErrorCode.MCP_SERVER_VERSION_EXIST, String.format("Mcp Server %s and target version %s already exist, do not do release", existMcpServer.getName(), version)); } catch (NacosApiException e) { if (ErrorCode.MCP_SERVER_NOT_FOUND.getCode() == e.getDetailErrCode()) { // mcp server not found, create new mcp server. String mcpId = createNewMcpServer(namespaceId, request); response.setMcpId(mcpId); LOGGER.info("Mcp Server {} released, Mcp Server id: {}", serverSpecification.getName(), mcpId); } else if (ErrorCode.MCP_SEVER_VERSION_NOT_FOUND.getCode() == e.getDetailErrCode()) { // mcp server found but version not found, update mcp server. createNewVersionMcpServer(namespaceId, request); McpServerIndexData mcpServerIndexData = mcpServerIndex.getMcpServerByName(namespaceId, serverSpecification.getName()); response.setMcpId(mcpServerIndexData.getId()); LOGGER.info("Mcp Server {} new version {} released, Mcp Server id: {}", serverSpecification.getName(), serverSpecification.getVersionDetail().getVersion(), mcpServerIndexData.getId()); } else { LOGGER.error("Mcp Server {} released failed.", serverSpecification.getName(), e); throw e; } } return response; } private String createNewMcpServer(String namespaceId, ReleaseMcpServerRequest request) throws NacosException { McpServerBasicInfo mcpServerBasicInfo = request.getServerSpecification(); McpToolSpecification toolSpecification = request.getToolSpecification(); McpEndpointSpec endpointSpecification = null == request.getEndpointSpecification() ? autoBuildMcpEndpointSpecification(namespaceId, mcpServerBasicInfo) : request.getEndpointSpecification(); return mcpServerOperationService.createMcpServer(namespaceId, mcpServerBasicInfo, toolSpecification, endpointSpecification); } private void createNewVersionMcpServer(String namespaceId, ReleaseMcpServerRequest request) throws NacosException { McpServerBasicInfo mcpServerBasicInfo = request.getServerSpecification(); McpToolSpecification toolSpecification = request.getToolSpecification(); McpEndpointSpec endpointSpecification = null == request.getEndpointSpecification() ? autoBuildMcpEndpointSpecification(namespaceId, mcpServerBasicInfo) : request.getEndpointSpecification(); Boolean isLatest = mcpServerBasicInfo.getVersionDetail().getIs_latest(); boolean isPublish = isLatest != null && isLatest; mcpServerOperationService.updateMcpServer(namespaceId, isPublish, mcpServerBasicInfo, toolSpecification, endpointSpecification, Boolean.FALSE); } private McpEndpointSpec autoBuildMcpEndpointSpecification(String namespaceId, McpServerBasicInfo mcpServerBasicInfo) { if (AiConstants.Mcp.MCP_PROTOCOL_STDIO.equals(mcpServerBasicInfo.getProtocol())) { return null; } // Not stdio protocol need to create endpoint service. return autoBuildMcpEndpointSpecification(namespaceId, mcpServerBasicInfo.getName(), mcpServerBasicInfo.getVersionDetail().getVersion()); } private McpEndpointSpec autoBuildMcpEndpointSpecification(String namespaceId, String mcpName, String version) { String versionMcpName = mcpName + "::" + version; Service service = endpointOperationService.generateService(namespaceId, versionMcpName); McpEndpointSpec endpointSpecification = new McpEndpointSpec(); endpointSpecification.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF); endpointSpecification.getData().put(CommonParams.NAMESPACE_ID, service.getNamespace()); endpointSpecification.getData().put(CommonParams.GROUP_NAME, service.getGroup()); endpointSpecification.getData().put(CommonParams.SERVICE_NAME, service.getName()); return endpointSpecification; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/a2a/AgentEndpointRequestHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.a2a.identity.AgentIdCodecHolder; import com.alibaba.nacos.ai.utils.AgentEndpointUtil; import com.alibaba.nacos.ai.utils.AgentRequestUtil; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.AgentEndpointRequest; import com.alibaba.nacos.api.ai.remote.response.AgentEndpointResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.DeregisterInstanceReason; import com.alibaba.nacos.common.trace.event.naming.DeregisterInstanceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.RegisterInstanceTraceEvent; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.AgentRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; import com.alibaba.nacos.naming.utils.NamingRequestUtil; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Register or Deregister endpoint for agent to nacos AI module request handler. * * @author xiweng.yy */ @Component public class AgentEndpointRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(AgentEndpointRequestHandler.class); private final EphemeralClientOperationServiceImpl clientOperationService; private final AgentIdCodecHolder agentIdCodecHolder; public AgentEndpointRequestHandler(EphemeralClientOperationServiceImpl clientOperationService, AgentIdCodecHolder agentIdCodecHolder) { this.clientOperationService = clientOperationService; this.agentIdCodecHolder = agentIdCodecHolder; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = AgentRequestParamExtractor.class) @Secured(action = ActionTypes.WRITE, signType = SignType.AI) public AgentEndpointResponse handle(AgentEndpointRequest request, RequestMeta meta) throws NacosException { AgentEndpointResponse response = new AgentEndpointResponse(); response.setType(request.getType()); AgentRequestUtil.fillNamespaceId(request); try { validateRequest(request); Instance instance = transferInstance(request); String serviceName = agentIdCodecHolder.encode(request.getAgentName()) + "::" + request.getEndpoint().getVersion(); Service service = Service.newService(request.getNamespaceId(), Constants.A2A.AGENT_ENDPOINT_GROUP, serviceName); switch (request.getType()) { case AiRemoteConstants.REGISTER_ENDPOINT: doRegisterEndpoint(service, instance, meta); break; case AiRemoteConstants.DE_REGISTER_ENDPOINT: doDeregisterEndpoint(service, instance, meta); break; default: throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, String.format("parameter `type` should be %s or %s, but was %s", AiRemoteConstants.REGISTER_ENDPOINT, AiRemoteConstants.DE_REGISTER_ENDPOINT, request.getType())); } } catch (NacosApiException e) { response.setErrorInfo(e.getErrCode(), e.getErrMsg()); LOGGER.error("[{}] Register agent endpoint to agent {} error: {}", meta.getConnectionId(), request.getAgentName(), e.getErrMsg()); } return response; } private Instance transferInstance(AgentEndpointRequest request) throws NacosApiException { return AgentEndpointUtil.transferToInstance(request.getEndpoint()); } private void validateRequest(AgentEndpointRequest request) throws NacosApiException { if (StringUtils.isBlank(request.getAgentName())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `agentName` can't be empty or null"); } if (null == request.getEndpoint()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `endpoint` can't be null"); } if (StringUtils.isBlank(request.getEndpoint().getVersion())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `endpoint.version` can't be empty or null"); } } private void doRegisterEndpoint(Service service, Instance instance, RequestMeta meta) throws NacosException { clientOperationService.registerInstance(service, instance, meta.getConnectionId()); NotifyCenter.publishEvent(new RegisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort())); } private void doDeregisterEndpoint(Service service, Instance instance, RequestMeta meta) { clientOperationService.deregisterInstance(service, instance, meta.getConnectionId()); NotifyCenter.publishEvent(new DeregisterInstanceTraceEvent(System.currentTimeMillis(), NamingRequestUtil.getSourceIpForGrpcRequest(meta), true, DeregisterInstanceReason.REQUEST, service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort())); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/a2a/BatchAgentEndpointRequestHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.a2a.identity.AgentIdCodecHolder; import com.alibaba.nacos.ai.utils.AgentEndpointUtil; import com.alibaba.nacos.ai.utils.AgentRequestUtil; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.BatchAgentEndpointRequest; import com.alibaba.nacos.api.ai.remote.response.AgentEndpointResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.event.naming.BatchRegisterInstanceTraceEvent; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.AgentRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; import com.alibaba.nacos.naming.utils.NamingRequestUtil; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; /** * Batch Register endpoints for agent to nacos AI module request handler. * * @author xiweng.yy */ @Component public class BatchAgentEndpointRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(BatchAgentEndpointRequestHandler.class); private final EphemeralClientOperationServiceImpl clientOperationService; private final AgentIdCodecHolder agentIdCodecHolder; public BatchAgentEndpointRequestHandler(EphemeralClientOperationServiceImpl clientOperationService, AgentIdCodecHolder agentIdCodecHolder) { this.clientOperationService = clientOperationService; this.agentIdCodecHolder = agentIdCodecHolder; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = AgentRequestParamExtractor.class) @Secured(action = ActionTypes.WRITE, signType = SignType.AI) public AgentEndpointResponse handle(BatchAgentEndpointRequest request, RequestMeta meta) throws NacosException { AgentEndpointResponse response = new AgentEndpointResponse(); response.setType(AiRemoteConstants.BATCH_REGISTER_ENDPOINT); AgentRequestUtil.fillNamespaceId(request); try { validateRequest(request); List instances = AgentEndpointUtil.transferToInstances(request.getEndpoints()); String version = request.getEndpoints().stream().findFirst().get().getVersion(); String serviceName = agentIdCodecHolder.encode(request.getAgentName()) + "::" + version; Service service = Service.newService(request.getNamespaceId(), Constants.A2A.AGENT_ENDPOINT_GROUP, serviceName); clientOperationService.batchRegisterInstance(service, instances, meta.getConnectionId()); publishBatchRegisterInstanceTraceEvent(service, instances, meta); } catch (NacosApiException e) { response.setErrorInfo(e.getErrCode(), e.getErrMsg()); LOGGER.error("[{}] Batch Register agent endpoints to agent {} error: {}", meta.getConnectionId(), request.getAgentName(), e.getErrMsg()); } return response; } private void validateRequest(BatchAgentEndpointRequest request) throws NacosApiException { if (StringUtils.isBlank(request.getAgentName())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `agentName` can't be empty or null"); } if (null == request.getEndpoints() || request.getEndpoints().isEmpty()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `endpoints` can't be empty or null, if want to deregister, please use deregister API."); } Collection endpoints = request.getEndpoints(); Set versions = new HashSet<>(); for (AgentEndpoint each : endpoints) { if (StringUtils.isBlank(each.getVersion())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `endpoint.version` can't be empty or null."); } versions.add(each.getVersion()); } if (versions.size() > 1) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, String.format("Required parameter `endpoint.version` can't be different, current includes: %s.", String.join(",", versions))); } } private void publishBatchRegisterInstanceTraceEvent(Service service, List instances, RequestMeta meta) { long eventTime = System.currentTimeMillis(); String clientIp = NamingRequestUtil.getSourceIpForGrpcRequest(meta); instances.forEach(instance -> NotifyCenter.publishEvent( new BatchRegisterInstanceTraceEvent(eventTime, clientIp, true, service.getNamespace(), service.getGroup(), service.getName(), instance.getIp(), instance.getPort()))); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/a2a/QueryAgentCardRequestHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.service.a2a.A2aServerOperationService; import com.alibaba.nacos.ai.utils.AgentRequestUtil; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.remote.request.QueryAgentCardRequest; import com.alibaba.nacos.api.ai.remote.response.QueryAgentCardResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.AgentRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Nacos AI module query agent card request handler. * * @author xiweng.yy */ @Component public class QueryAgentCardRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(QueryAgentCardRequestHandler.class); private final A2aServerOperationService a2aServerOperationService; public QueryAgentCardRequestHandler(A2aServerOperationService a2aServerOperationService) { this.a2aServerOperationService = a2aServerOperationService; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = AgentRequestParamExtractor.class) @Secured(action = ActionTypes.READ, signType = SignType.AI) public QueryAgentCardResponse handle(QueryAgentCardRequest request, RequestMeta meta) throws NacosException { AgentRequestUtil.fillNamespaceId(request); if (StringUtils.isBlank(request.getAgentName())) { QueryAgentCardResponse errorResponse = new QueryAgentCardResponse(); errorResponse.setErrorInfo(NacosException.INVALID_PARAM, "parameters `agentName` can't be empty or null"); return errorResponse; } return doHandler(request); } private QueryAgentCardResponse doHandler(QueryAgentCardRequest request) { QueryAgentCardResponse response = new QueryAgentCardResponse(); try { AgentCardDetailInfo result = a2aServerOperationService.getAgentCard(request.getNamespaceId(), request.getAgentName(), request.getVersion(), request.getRegistrationType()); response.setAgentCardDetailInfo(result); } catch (NacosException e) { LOGGER.error("Query agent card for agent {} error: {}", request.getAgentName(), e.getErrMsg()); response.setErrorInfo(e.getErrCode(), e.getErrMsg()); } return response; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/handler/a2a/ReleaseAgentCardRequestHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.service.a2a.A2aServerOperationService; import com.alibaba.nacos.ai.utils.AgentRequestUtil; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.remote.request.ReleaseAgentCardRequest; import com.alibaba.nacos.api.ai.remote.response.ReleaseAgentCardResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.AgentRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; /** * Nacos AI module release agent card request handler. * * @author xiweng.yy */ @Component public class ReleaseAgentCardRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ReleaseAgentCardRequestHandler.class); private final A2aServerOperationService a2aServerOperationService; public ReleaseAgentCardRequestHandler(A2aServerOperationService a2aServerOperationService) { this.a2aServerOperationService = a2aServerOperationService; } @Override @NamespaceValidation @ExtractorManager.Extractor(rpcExtractor = AgentRequestParamExtractor.class) @Secured(action = ActionTypes.WRITE, signType = SignType.AI) public ReleaseAgentCardResponse handle(ReleaseAgentCardRequest request, RequestMeta meta) throws NacosException { AgentRequestUtil.fillNamespaceId(request); ReleaseAgentCardResponse response = new ReleaseAgentCardResponse(); try { validateRequest(request); doHandler(request, meta); return response; } catch (NacosException e) { response.setErrorInfo(e.getErrCode(), e.getErrMsg()); LOGGER.error("[{}] Release agent card {} error: {}", meta.getConnectionId(), null == request.getAgentCard() ? null : JacksonUtils.toJson(request.getAgentCard()), e.getErrMsg()); } return response; } private void validateRequest(ReleaseAgentCardRequest request) throws NacosApiException { if (null == request.getAgentCard()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentCard` can't be null"); } AgentRequestUtil.validateAgentCard(request.getAgentCard()); } private void doHandler(ReleaseAgentCardRequest request, RequestMeta meta) throws NacosException { String namespaceId = request.getNamespaceId(); AgentCard agentCard = request.getAgentCard(); LOGGER.info("Release new agent {}, version {} into namespaceId {} from connectionId {}.", agentCard.getName(), agentCard.getVersion(), namespaceId, meta.getConnectionId()); try { AgentCardDetailInfo existAgentCard = a2aServerOperationService.getAgentCard(namespaceId, agentCard.getName(), agentCard.getVersion(), StringUtils.EMPTY); LOGGER.info("AgentCard {} and target version {} already exist.", existAgentCard.getName(), existAgentCard.getVersion()); } catch (NacosApiException e) { if (ErrorCode.AGENT_NOT_FOUND.getCode() == e.getDetailErrCode()) { // agent card not found, create new agent card. createAgentCard(namespaceId, agentCard, request.getRegistrationType()); LOGGER.info("AgentCard {} released.", agentCard.getName()); } else if (ErrorCode.AGENT_VERSION_NOT_FOUND.getCode() == e.getDetailErrCode()) { // agent card found but version not found, update agent card. createNewVersionAgentCard(namespaceId, agentCard, request.getRegistrationType(), request.isSetAsLatest()); LOGGER.info("AgentCard {} new version {} released.", agentCard.getName(), agentCard.getVersion()); } else { LOGGER.error("AgentCard {} released failed.", agentCard.getName(), e); throw e; } } } private void createAgentCard(String namespaceId, AgentCard agentCard, String registrationType) throws NacosException { a2aServerOperationService.registerAgent(agentCard, namespaceId, registrationType); } private void createNewVersionAgentCard(String namespaceId, AgentCard agentCard, String registrationType, boolean setAsLatest) throws NacosException { a2aServerOperationService.updateAgentCard(agentCard, namespaceId, registrationType, setAsLatest); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/remote/manager/AiConnectionBasedClientManager.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.manager; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.core.remote.ClientConnectionEventListener; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.constants.ClientConstants; import com.alibaba.nacos.naming.core.v2.client.Client; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; import com.alibaba.nacos.naming.core.v2.client.manager.ClientManager; import com.alibaba.nacos.naming.core.v2.client.manager.impl.ConnectionBasedClientManager; import org.springframework.stereotype.Service; import java.util.Collection; /** * The manager of {@code ConnectionBasedClient} For AI module. * *

* proxy for {@link ConnectionBasedClientManager}, only listen connection that module `ai`. * The actual implementation is {@link ConnectionBasedClientManager}, *

* * @author xiweng.yy */ @Service public class AiConnectionBasedClientManager extends ClientConnectionEventListener implements ClientManager { private final ConnectionBasedClientManager delegate; public AiConnectionBasedClientManager(ConnectionBasedClientManager connectionBasedClientManager) { this.delegate = connectionBasedClientManager; } @Override public void clientConnected(Connection connect) { // ignore `naming`, `config` and `lock` module connection if (!RemoteConstants.LABEL_MODULE_AI.equals(connect.getMetaInfo().getLabel(RemoteConstants.LABEL_MODULE))) { return; } ClientAttributes attributes = new ClientAttributes(); attributes.addClientAttribute(ClientConstants.CONNECTION_TYPE, connect.getMetaInfo().getConnectType()); attributes.addClientAttribute(ClientConstants.CONNECTION_METADATA, connect.getMetaInfo()); clientConnected(connect.getMetaInfo().getConnectionId(), attributes); } @Override public boolean clientConnected(String clientId, ClientAttributes attributes) { return delegate.clientConnected(clientId, attributes); } @Override public boolean clientConnected(Client client) { return delegate.clientConnected(client); } @Override public boolean syncClientConnected(String clientId, ClientAttributes attributes) { return delegate.syncClientConnected(clientId, attributes); } @Override public void clientDisConnected(Connection connect) { // ignore `naming`, `config` and `lock` module connection if (!RemoteConstants.LABEL_MODULE_AI.equals(connect.getMetaInfo().getLabel(RemoteConstants.LABEL_MODULE))) { return; } clientDisconnected(connect.getMetaInfo().getConnectionId()); } @Override public boolean clientDisconnected(String clientId) { return delegate.clientDisconnected(clientId); } @Override public Client getClient(String clientId) { return delegate.getClient(clientId); } @Override public boolean contains(String clientId) { return delegate.contains(clientId); } @Override public Collection allClientId() { return delegate.allClientId(); } @Override public boolean isResponsibleClient(Client client) { return delegate.isResponsibleClient(client); } @Override public boolean verifyClient(DistroClientVerifyInfo verifyData) { return delegate.verifyClient(verifyData); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/McpEndpointOperationService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.naming.core.InstanceOperator; import com.alibaba.nacos.naming.core.ServiceOperator; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; import java.util.List; import java.util.Map; /** * Nacos AI MCP Endpoint operation service. * * @author xiweng.yy */ @org.springframework.stereotype.Service public class McpEndpointOperationService { private final ServiceOperator serviceOperator; private final InstanceOperator instanceOperator; private final NamingMetadataManager metadataManager; public McpEndpointOperationService(ServiceOperator serviceOperator, InstanceOperator instanceOperator, NamingMetadataManager metadataManager) { this.serviceOperator = serviceOperator; this.instanceOperator = instanceOperator; this.metadataManager = metadataManager; } /** * Create Mcp Server Endpoint Service if necessary. * *

If type is REF, directly return service

*

If service not exist, do create new service and register instance, then return service

*

If service exist, only do register instance, then return service

* * @param namespaceId namespace id of mcp server * @param mcpName name of mcp server * @param endpointSpecification mcp server endpoint specification, see {@link McpEndpointSpec} * @param overrideExisting if replace all the instances when update the mcp server * @return {@link Service} * @throws NacosException any exception during handling */ public Service createMcpServerEndpointServiceIfNecessary(String namespaceId, String mcpName, String version, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException { if (AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF.equalsIgnoreCase(endpointSpecification.getType())) { Map endpointServiceData = endpointSpecification.getData(); if (!endpointServiceData.containsKey(CommonParams.NAMESPACE_ID) || !endpointServiceData.containsKey( CommonParams.GROUP_NAME) || !endpointServiceData.containsKey(CommonParams.SERVICE_NAME)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "`namespaceId`, `groupName`, `serviceName` should be in remoteServerConfig data if type is `REF`"); } String refGroupName = endpointSpecification.getData().get(CommonParams.GROUP_NAME); String refServiceName = endpointSpecification.getData().get(CommonParams.SERVICE_NAME); return Service.newService(namespaceId, refGroupName, refServiceName); } String versionMcpName = mcpName + "::" + version; Service service = generateService(namespaceId, versionMcpName); if (isNotExist(service)) { doCreateNewService(service); doUpdateInstanceInfo(service, endpointSpecification, namespaceId, mcpName, overrideExisting, versionMcpName); return service; } doUpdateInstanceInfo(service, endpointSpecification, namespaceId, mcpName, overrideExisting, versionMcpName); return service; } public Service generateService(String namespaceId, String mcpName) { return Service.newService(namespaceId, Constants.MCP_SERVER_ENDPOINT_GROUP, mcpName); } public List getMcpServerEndpointInstances(McpServiceRef serviceRef) throws NacosException { return instanceOperator.listInstance(serviceRef.getNamespaceId(), serviceRef.getGroupName(), serviceRef.getServiceName(), null, "", true).getHosts(); } /** * Delete Mcp Server Endpoint Service. * *

If service not exist, return directly

*

If service exist and service is ref, return directly

*

If service exist and service is direct, do deregister instance and remove service

* * @param namespaceId namespace id of mcp server * @param mcpServerName name of mcp server * @throws NacosException any exception during handling */ public void deleteMcpServerEndpointService(String namespaceId, String mcpServerName) throws NacosException { Service service = Service.newService(namespaceId, Constants.MCP_SERVER_ENDPOINT_GROUP, mcpServerName); if (isNotExist(service) || !isMcpDirectService(service)) { return; } List deletingInstance = instanceOperator.listInstance(namespaceId, Constants.MCP_SERVER_ENDPOINT_GROUP, mcpServerName, null, "", false).getHosts(); for (Instance each : deletingInstance) { instanceOperator.removeInstance(namespaceId, Constants.MCP_SERVER_ENDPOINT_GROUP, mcpServerName, each); } serviceOperator.delete(service.getNamespace(), service.getGroupedServiceName()); } private boolean isNotExist(Service service) throws NacosException { return !ServiceManager.getInstance().containSingleton(service); } private boolean isMcpDirectService(Service service) { ServiceMetadata metadata = metadataManager.getServiceMetadata(service).orElse(new ServiceMetadata()); return metadata.getExtendData().containsKey(Constants.MCP_SERVER_ENDPOINT_METADATA_MARK); } private void doCreateNewService(Service service) throws NacosException { ClusterMetadata clusterMetadata = new ClusterMetadata(); clusterMetadata.setHealthyCheckType(AbstractHealthChecker.None.TYPE); clusterMetadata.setHealthChecker(new AbstractHealthChecker.None()); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.getClusters().put(Constants.MCP_SERVER_ENDPOINT_CLUSTER, clusterMetadata); serviceMetadata.setEphemeral(false); // Mark service as direct service serviceMetadata.getExtendData().put(Constants.MCP_SERVER_ENDPOINT_METADATA_MARK, "true"); serviceOperator.create(service.getNamespace(), service.getGroupedServiceName(), serviceMetadata); } private void doUpdateInstanceInfo(Service service, McpEndpointSpec endpointSpecification, String namespaceId, String mcpServerName, boolean overrideExisting, String versionMcpName) throws NacosException { Instance instance = new Instance(); instance.setIp(endpointSpecification.getData().get(Constants.MCP_SERVER_ENDPOINT_ADDRESS)); instance.setPort(Integer.parseInt(endpointSpecification.getData().get(Constants.MCP_SERVER_ENDPOINT_PORT))); instance.setClusterName(Constants.MCP_SERVER_ENDPOINT_CLUSTER); instance.setEphemeral(false); if (overrideExisting) { List oldInstances = instanceOperator.listInstance(namespaceId, Constants.MCP_SERVER_ENDPOINT_GROUP, versionMcpName, null, "", false).getHosts(); for (Instance each : oldInstances) { instanceOperator.removeInstance(namespaceId, Constants.MCP_SERVER_ENDPOINT_GROUP, versionMcpName, each); } } instanceOperator.registerInstance(service.getNamespace(), service.getGroup(), service.getName(), instance); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/McpExternalDataAdaptor.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.enums.ExternalDataTypeEnum; import com.alibaba.nacos.ai.model.mcp.UrlPageResult; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerRemoteServiceConfig; import com.alibaba.nacos.api.ai.model.mcp.registry.McpRegistryServerDetail; import com.alibaba.nacos.api.ai.model.mcp.registry.McpRegistryServerList; import com.alibaba.nacos.api.ai.model.mcp.registry.Remote; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerResponse; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.ai.model.mcp.registry.OfficialMeta; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.fasterxml.jackson.core.type.TypeReference; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.UUID; import java.net.URI; import java.net.URLEncoder; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.stream.Collectors; /** *

Adapt the External data(mcp server json file, mcp registry api data) to Nacos MCP server format * {@link McpServerDetailInfo}. MCP official formats docs.

* *

1. MCP Server format is defined in * * server.schema.json.

* *

2. MCP Registry Api is defined in * * openapi.yaml.

* * @author nacos */ @Service public class McpExternalDataAdaptor { private HttpClient httpClient; private static final String CURSOR_QUERY_NAME = "cursor"; private static final String LIMIT_QUERY_NAME = "limit"; private static final String SEARCH_QUERY_NAME = "search"; private static final String HEADER_ACCEPT = "Accept"; private static final String HEADER_ACCEPT_JSON = "application/json"; private static final String QUERY_MARK = "?"; private static final String AMPERSAND = "&"; private static final int HTTP_STATUS_SUCCESS_MIN = 200; private static final int HTTP_STATUS_SUCCESS_MAX = 299; private static final int CONNECT_TIMEOUT_SECONDS = 10; private static final int READ_TIMEOUT_SECONDS = 20; private static final int FETCH_ALL_LIMIT_MARK = -1; /** * Safety guard to avoid infinite loops when server keeps returning cursors. * Limits the maximum number of pages iterated when fetching from URL. */ private static final int MAX_PAGES_GUARD = 200; /** * Adapt the external data to Nacos MCP server format. * * @param request import request * @return Nacos MCP server format * @throws Exception if adapt failed */ public List adaptExternalDataToNacosMcpServerFormat(McpServerImportRequest request) throws Exception { ExternalDataTypeEnum externalDataTypeEnum = ExternalDataTypeEnum.parseType(request.getImportType()); if (ExternalDataTypeEnum.FILE.equals(externalDataTypeEnum)) { return adaptOfficialSeedFile(request.getData()); } else if (ExternalDataTypeEnum.JSON.equals(externalDataTypeEnum)) { return adaptOfficialMcpServerJsonText(request.getData()); } else if (ExternalDataTypeEnum.URL.equals(externalDataTypeEnum)) { return adaptOfficialRegistryUrl(request.getData(), request.getCursor(), request.getLimit(), request.getSearch()); } else { throw new IllegalArgumentException("Unsupported import type: " + externalDataTypeEnum); } } private UrlPageResult fetchUrlPage(String urlData, String cursor, Integer limit, String search) throws Exception { String base = urlData.trim(); HttpClient client = getHttpClient(); String pageUrl = buildPageUrl(base, cursor, limit, search); HttpRequest request = buildGetRequest(pageUrl); HttpResponse resp = client.send(request, HttpResponse.BodyHandlers.ofString()); int code = resp.statusCode(); if (!isSuccessStatus(code)) { throw new IllegalStateException("HTTP " + code + " when fetching " + pageUrl); } List servers = null; String next = null; try { McpRegistryServerList listPage = JacksonUtils.toObj(resp.body(), McpRegistryServerList.class); if (listPage != null && listPage.getServers() != null) { servers = listPage.getServers().stream() .map(this::adaptOfficialMcpServerFromResponse) .collect(Collectors.toList()); } if (listPage != null && listPage.getMetadata() != null) { next = listPage.getMetadata().getNextCursor(); } } catch (Exception e) { throw new IllegalStateException("Failed to parse response body", e); } return new UrlPageResult(servers, next); } private List fetchUrlServersAll(String urlData, String search) throws Exception { List collected = new ArrayList<>(); String cursor = null; int pages = 0; while (pages < MAX_PAGES_GUARD) { pages++; UrlPageResult page = fetchUrlPage(urlData, cursor, 30, search); if (CollectionUtils.isNotEmpty(page.getServers())) { collected.addAll(page.getServers()); } String next = page.getNextCursor(); if (next == null) { break; } cursor = next; } return collected; } private McpServerDetailInfo adaptOfficialMcpServer(McpRegistryServerDetail registryServer) { if (registryServer == null) { return null; } McpServerDetailInfo server = new McpServerDetailInfo(); applyBasicInfo(registryServer, server); applyVersionInfo(registryServer, server); applyProtocolInfo(registryServer, server); applyLocalAndRemoteConfig(registryServer, server); return server; } /** * Adapt official mcp server from server response. * Just append version meta info to the result of adaptOfficialMcpServer. * * @param response the server response object * @return adapted mcp server detail info */ private McpServerDetailInfo adaptOfficialMcpServerFromResponse(ServerResponse response) { McpServerDetailInfo adaptOfficialMcpServer = adaptOfficialMcpServer(response.getServer()); OfficialMeta official = response.getMeta().getOfficial(); ServerVersionDetail versionDetail = adaptOfficialMcpServer.getVersionDetail(); if (versionDetail != null) { versionDetail.setRelease_date(official.getPublishedAt()); versionDetail.setIs_latest(true); String status = official.getStatus(); if (StringUtils.isNotBlank(status)) { adaptOfficialMcpServer.setStatus(status); } } return adaptOfficialMcpServer; } private void applyBasicInfo(McpRegistryServerDetail registryServer, McpServerDetailInfo out) { String id = generateMcpServerId(registryServer.getName()); out.setId(id); out.setName(registryServer.getName()); out.setDescription(registryServer.getDescription()); out.setRepository(registryServer.getRepository()); } private void applyVersionInfo(McpRegistryServerDetail registryServer, McpServerDetailInfo out) { ServerVersionDetail v = null; if (StringUtils.isNotBlank(registryServer.getVersion())) { v = new ServerVersionDetail(); v.setVersion(registryServer.getVersion()); } out.setVersionDetail(v); } private void applyProtocolInfo(McpRegistryServerDetail registryServer, McpServerDetailInfo out) { String protocol = resolveServerProtocol(registryServer); if (StringUtils.isNotBlank(protocol)) { out.setProtocol(protocol); out.setFrontProtocol(protocol); } } private void applyLocalAndRemoteConfig(McpRegistryServerDetail registryServer, McpServerDetailInfo server) { if (registryServer != null) { server.setPackages(registryServer.getPackages()); server.setRemoteServerConfig(generateRemoteServiceConfig(registryServer.getRemotes())); } } private String resolveServerProtocol(McpRegistryServerDetail detail) { if (CollectionUtils.isNotEmpty(detail.getPackages())) { return AiConstants.Mcp.MCP_PROTOCOL_STDIO; } if (CollectionUtils.isNotEmpty(detail.getRemotes())) { Remote first = detail.getRemotes().get(0); String tt = first != null ? first.getType() : null; if (tt != null) { String lower = tt.trim().toLowerCase(); if (AiConstants.Mcp.OFFICIAL_TRANSPORT_SSE.equals(lower)) { return AiConstants.Mcp.MCP_PROTOCOL_SSE; } if (AiConstants.Mcp.OFFICIAL_TRANSPORT_STREAMABLE.equals(lower)) { return AiConstants.Mcp.MCP_PROTOCOL_STREAMABLE; } } } return null; } private McpServerRemoteServiceConfig generateRemoteServiceConfig(List remotes) { if (CollectionUtils.isEmpty(remotes)) { return null; } McpServerRemoteServiceConfig remoteConfig = new McpServerRemoteServiceConfig(); List endpoints = new ArrayList<>(); for (Remote remote : remotes) { String url = remote.getUrl().trim(); try { UrlComponents components = parseUrlComponents(url); boolean isHttps = "https".equalsIgnoreCase(components.getScheme()); int effectivePort = (components.getPort() > 0) ? components.getPort() : (isHttps ? 443 : 80); String endpointData = components.getHost() + ":" + effectivePort; FrontEndpointConfig cfg = new FrontEndpointConfig(); cfg.setEndpointData(endpointData); cfg.setPath(StringUtils.isNotBlank(components.getPath()) ? components.getPath() : "/"); cfg.setType(remote.getType()); cfg.setProtocol(components.getScheme()); cfg.setEndpointType(AiConstants.Mcp.MCP_FRONT_ENDPOINT_TYPE_TO_BACK); cfg.setHeaders(remote.getHeaders()); endpoints.add(cfg); // Use first remote's path as export path if (remoteConfig.getExportPath() == null) { remoteConfig.setExportPath(components.getPath() != null ? components.getPath() : "/"); } } catch (Exception e) { throw new IllegalStateException("Invalid URL: " + url, e); } } remoteConfig.setFrontEndpointConfigList(endpoints); return remoteConfig; } /** * Parse URL into components (scheme, host, port, path). * Manual parsing without using URI class. * * @param url the URL string to parse * @return UrlComponents containing scheme, host, port, and path */ private UrlComponents parseUrlComponents(String url) { String scheme = null; String host = null; int port = -1; String path = null; // Parse scheme int schemeEnd = url.indexOf("://"); if (schemeEnd > 0) { scheme = url.substring(0, schemeEnd); url = url.substring(schemeEnd + 3); } // Parse host, port, and path int pathStart = url.indexOf('/'); String hostPart; if (pathStart > 0) { hostPart = url.substring(0, pathStart); path = url.substring(pathStart); } else { hostPart = url; path = null; } // Parse host and port int portStart = hostPart.lastIndexOf(':'); if (portStart > 0) { host = hostPart.substring(0, portStart); try { port = Integer.parseInt(hostPart.substring(portStart + 1)); } catch (NumberFormatException e) { // Invalid port, treat the whole thing as host host = hostPart; port = -1; } } else { host = hostPart; } return new UrlComponents(scheme, host, port, path); } /** * Inner class to hold URL components parsed from a URI. */ private static class UrlComponents { private final String scheme; private final String host; private final int port; private final String path; public UrlComponents(String scheme, String host, int port, String path) { this.scheme = scheme; this.host = host; this.port = port; this.path = path; } public String getScheme() { return scheme; } public String getHost() { return host; } public int getPort() { return port; } public String getPath() { return path; } } /** * URL import wrapper: fetch contents from specified URL and adapt to Nacos mcp servers. * Fetch specified contents from specified URL and adapt to Nacos mcp servers. * * @param urlData URL data to parse. Only support official mcp registry api. * @param cursor Cursor for pagination * @param limit Limit for pagination. Fetch all pages when limit = -1 * @param search fuzzy search keyword * @return list of adapted mcp servers * @throws Exception if adaptation failed */ private List adaptOfficialRegistryUrl(String urlData, String cursor, Integer limit, String search) throws Exception { if (StringUtils.isBlank(urlData)) { throw new IllegalArgumentException("URL is blank"); } // If limit = -1, fetch all pages if (limit != null && limit == FETCH_ALL_LIMIT_MARK) { return fetchUrlServersAll(urlData.trim(), search); } // Otherwise, fetch a single page using fetchUrlPage UrlPageResult page = fetchUrlPage(urlData.trim(), cursor, limit, search); return page.getServers(); } /** * File import wrapper: parse into a list of RegistryDetails and convert to * Nacos servers. */ private List adaptOfficialSeedFile(String data) { return unmarshaledSeedToServerList(data).stream() .map(this::adaptOfficialMcpServer) .filter(Objects::nonNull) .collect(Collectors.toList()); } private List adaptOfficialMcpServerJsonText(String data) { McpRegistryServerDetail detail = JacksonUtils.toObj(data, McpRegistryServerDetail.class); return Collections.singletonList(adaptOfficialMcpServer(detail)); } private List unmarshaledSeedToServerList(String data) { return JacksonUtils.toObj(data, new TypeReference<>() { }); } private HttpClient getHttpClient() { if (httpClient == null) { httpClient = HttpClient.newBuilder() .followRedirects(HttpClient.Redirect.NORMAL) .connectTimeout(Duration.ofSeconds(CONNECT_TIMEOUT_SECONDS)) .build(); } return httpClient; } public void setHttpClient(HttpClient client) { this.httpClient = client; } private String buildPageUrl(String base, String cursor, Integer limit, String search) { StringBuilder url = new StringBuilder(base); boolean hasQuery = base.contains(QUERY_MARK); if (StringUtils.isNotBlank(cursor)) { String enc = URLEncoder.encode(cursor, StandardCharsets.UTF_8); url.append(hasQuery ? AMPERSAND : QUERY_MARK).append(CURSOR_QUERY_NAME).append("=").append(enc); hasQuery = true; } if (limit != null && limit > 0) { url.append(hasQuery ? AMPERSAND : QUERY_MARK).append(LIMIT_QUERY_NAME).append("=").append(limit); hasQuery = true; } if (StringUtils.isNotBlank(search)) { String encSearch = URLEncoder.encode(search, StandardCharsets.UTF_8); url.append(hasQuery ? AMPERSAND : QUERY_MARK).append(SEARCH_QUERY_NAME).append("=").append(encSearch); } return url.toString(); } private HttpRequest buildGetRequest(String url) { return HttpRequest.newBuilder(URI.create(url)) .timeout(Duration.ofSeconds(READ_TIMEOUT_SECONDS)) .GET() .header(HEADER_ACCEPT, HEADER_ACCEPT_JSON).build(); } private boolean isSuccessStatus(int code) { return code >= HTTP_STATUS_SUCCESS_MIN && code <= HTTP_STATUS_SUCCESS_MAX; } private String generateMcpServerId(String name) { return UUID.nameUUIDFromBytes(name.getBytes(StandardCharsets.UTF_8)).toString(); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/McpServerCacheInvalidateService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.utils.GroupKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * MCP Server cache invalidation service. * *

This service listens to ConfigDataChangeEvent and invalidates MCP server cache * when MCP-related configurations are deleted or modified. The implementation follows * the same pattern as AsyncNotifyService for configuration synchronization.

* * @author xinluo */ @Service public class McpServerCacheInvalidateService extends Subscriber { private static final Logger LOGGER = LoggerFactory.getLogger(McpServerCacheInvalidateService.class); private final McpServerIndex mcpServerIndex; @Autowired public McpServerCacheInvalidateService(McpServerIndex mcpServerIndex) { this.mcpServerIndex = mcpServerIndex; NotifyCenter.registerSubscriber(this); } /** * Handle ConfigDataChangeEvent to invalidate MCP server cache. * * @param event configuration change event */ void handleConfigDataChangeEvent(LocalDataChangeEvent event) { // Check if the configuration is MCP server related String groupKey = event.groupKey; String[] strings = GroupKey.parseKey(groupKey); String dataId = strings[0]; String group = strings[1]; String tenant = strings.length > 2 ? strings[2] : ""; if (!isMcpServerConfig(group)) { return; } // Extract server ID from dataId String serverId = extractServerIdFromDataId(group, dataId); if (StringUtils.isEmpty(serverId)) { LOGGER.warn("Failed to extract server ID from dataId: {}, group: {}", dataId, group); return; } // Invalidate cache invalidateCache(tenant, serverId); LOGGER.info("Handled MCP server config change event: namespaceId={}, group={}, dataId={}, serverId={}", tenant, group, dataId, serverId); } /** * Check if the configuration group is MCP server related. * * @param group configuration group * @return true if the group is MCP server related */ private boolean isMcpServerConfig(String group) { return Constants.MCP_SERVER_VERSIONS_GROUP.equals(group); } /** * Extract server ID from dataId based on the configuration group. * *

MCP server configurations follow these naming patterns:

*
    *
  • Version info: {serverId}-mcp-versions.json (group: mcp-server-versions)
  • *
  • Server spec: {serverId}-{version}-mcp-server.json (group: mcp-server)
  • *
  • Tool spec: {serverId}-{version}-mcp-tools.json (group: mcp-tools)
  • *
* * @param group configuration group * @param dataId configuration dataId * @return extracted server ID, or null if extraction fails */ private String extractServerIdFromDataId(String group, String dataId) { if (Constants.MCP_SERVER_VERSIONS_GROUP.equals(group)) { // Version info: remove "-mcp-versions.json" suffix if (StringUtils.isNotEmpty(dataId) && dataId.endsWith(Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX)) { return dataId.substring(0, dataId.length() - Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX.length()); } } return null; } /** * Invalidate MCP server cache by server ID. * *

This method is idempotent - calling it multiple times with the same * serverId will not cause any side effects.

* * @param namespaceId namespace ID * @param serverId MCP server ID */ private void invalidateCache(String namespaceId, String serverId) { try { // Clear cache by server ID mcpServerIndex.removeMcpServerById(serverId); LOGGER.info("MCP Server cache invalidated successfully: namespaceId={}, serverId={}", namespaceId, serverId); } catch (Exception e) { // Cache invalidation failure should not affect configuration deletion LOGGER.error("Failed to invalidate MCP Server cache: namespaceId={}, serverId={}, error={}", namespaceId, serverId, e.getMessage(), e); } } @Override public void onEvent(LocalDataChangeEvent event) { handleConfigDataChangeEvent(event); } @Override public Class subscribeType() { return LocalDataChangeEvent.class; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/McpServerImportService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.McpServerValidationConstants; import com.alibaba.nacos.ai.enums.ExternalDataTypeEnum; import com.alibaba.nacos.ai.enums.McpImportResultStatusEnum; import com.alibaba.nacos.ai.index.McpCacheIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.utils.McpConfigUtils; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResult; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpServerValidationItem; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.CollectionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; /** * MCP Server Import Service. * Handles the import logic for MCP servers from various sources. * * @author WangzJi */ @Service public class McpServerImportService { private final McpCacheIndex mcpCacheIndex; private static final Logger LOG = LoggerFactory.getLogger(McpServerImportService.class); private final McpExternalDataAdaptor transformService; private final McpServerValidationService validationService; private final McpServerOperationService operationService; public McpServerImportService(McpExternalDataAdaptor transformService, McpServerValidationService validationService, McpServerOperationService operationService, McpCacheIndex mcpCacheIndex) { this.transformService = transformService; this.validationService = validationService; this.operationService = operationService; this.mcpCacheIndex = mcpCacheIndex; } /** * Validate servers for import. * * @param namespaceId namespace ID * @param request import request * @return validation result * @throws NacosException if validation fails */ public McpServerImportValidationResult validateImport(String namespaceId, McpServerImportRequest request) throws NacosException { ExternalDataTypeEnum externalDataTypeEnum = ExternalDataTypeEnum.parseType(request.getImportType()); if (Objects.isNull(externalDataTypeEnum)) { throw new NacosException(NacosException.INVALID_PARAM, "Invalid import type: " + request.getImportType()); } try { List servers; servers = transformService.adaptExternalDataToNacosMcpServerFormat(request); return validationService.validateServers(namespaceId, servers); } catch (Exception e) { McpServerImportValidationResult result = new McpServerImportValidationResult(); result.setValid(false); List errors = new ArrayList<>(); errors.add("Import validation failed: " + e.getMessage()); result.setErrors(errors); return result; } } /** * Execute import of MCP servers. * * @param namespaceId namespace ID * @param request import request * @return import response */ public McpServerImportResponse executeImport(String namespaceId, McpServerImportRequest request) { try { McpServerImportValidationResult validationResult = validateImport(namespaceId, request); boolean valid = validationResult.isValid(); boolean overrideExisting = request.isOverrideExisting(); if (valid || request.isSkipInvalid()) { List validSelectedServers = filterValidSelectedServers(validationResult.getServers(), request.getSelectedServers()); return applyResultOnRequestPolicy(namespaceId, validSelectedServers, overrideExisting); } else { return responseError("Import validation failed: " + String.join(", ", validationResult.getErrors())); } } catch (Exception e) { LOG.error("Import execution failed", e); return responseError("Import execution failed: " + e.getMessage()); } } private static McpServerImportResponse responseError(String msg) { McpServerImportResponse response = new McpServerImportResponse(); response.setSuccess(false); response.setErrorMessage(msg); return response; } private McpServerImportResponse applyResultOnRequestPolicy(String namespaceId, List serversToImport, boolean overwrite) { McpServerImportResponse response = new McpServerImportResponse(); List results = new ArrayList<>(); int successCount = 0; int failedCount = 0; int skippedCount = 0; for (McpServerValidationItem item : serversToImport) { McpServerImportResult result = importSingleServer(namespaceId, item, overwrite); if ("success".equals(result.getStatus())) { successCount++; } else if ("failed".equals(result.getStatus())) { failedCount++; } else if ("skipped".equals(result.getStatus())) { skippedCount++; } results.add(result); } response.setSuccess(failedCount == 0); response.setTotalCount(serversToImport.size()); response.setSuccessCount(successCount); response.setFailedCount(failedCount); response.setSkippedCount(skippedCount); response.setResults(results); return response; } /** * Filter valid selected servers for import. * * @param validationItems validation items * @param selectedServers selected server IDs * @return filtered servers */ private List filterValidSelectedServers(List validationItems, String[] selectedServers) { if (CollectionUtils.isEmpty(validationItems)) { return Collections.emptyList(); } if (selectedServers == null || selectedServers.length == 0) { return validationItems; } Set selectServers = new HashSet<>(Arrays.asList(selectedServers)); return validationItems.stream() .filter(item -> McpServerValidationConstants.STATUS_VALID.equals(item.getStatus())) .filter(item -> selectServers.isEmpty() || selectServers.contains(item.getServerId())) .toList(); } /** * Import single MCP server. * * @param namespaceId namespace ID * @param item validation item * @param overrideExisting whether to override existing servers * @return import result */ private McpServerImportResult importSingleServer(String namespaceId, McpServerValidationItem item, boolean overrideExisting) { McpServerImportResult result = new McpServerImportResult(); result.setServerName(item.getServerName()); result.setServerId(item.getServerId()); try { if (item.isExists() && !overrideExisting) { result.setStatus(McpImportResultStatusEnum.SKIPPED.getName()); result.setConflictType("existing"); return result; } McpServerDetailInfo server = item.getServer(); McpToolSpecification toolSpec = server.getToolSpec(); McpServerBasicInfo basicInfo = generateMcpBasicInfo(server); McpEndpointSpec endpointSpec = generateEndpointSpec(server); McpServerIndexData exist = mcpCacheIndex.getMcpServerByName(namespaceId, item.getServerName()); if (exist != null && overrideExisting) { operationService.updateMcpServer(namespaceId, true, basicInfo, toolSpec, endpointSpec, true); } else { operationService.createMcpServer(namespaceId, basicInfo, toolSpec, endpointSpec); } result.setStatus(McpImportResultStatusEnum.SUCCESS.getName()); } catch (Exception e) { result.setStatus(McpImportResultStatusEnum.FAILED.getName()); result.setErrorMessage("Failed to import server: " + e.getMessage()); } return result; } private static McpServerBasicInfo generateMcpBasicInfo(McpServerDetailInfo server) { McpServerBasicInfo basicInfo = new McpServerBasicInfo(); basicInfo.setId(server.getId()); basicInfo.setName(server.getName()); basicInfo.setProtocol(server.getProtocol()); basicInfo.setFrontProtocol(server.getFrontProtocol()); basicInfo.setDescription(server.getDescription()); basicInfo.setStatus(server.getStatus()); basicInfo.setRepository(server.getRepository()); basicInfo.setVersionDetail(server.getVersionDetail()); basicInfo.setRemoteServerConfig(server.getRemoteServerConfig()); basicInfo.setPackages(server.getPackages()); return basicInfo; } /** * Convert {@link McpServerDetailInfo} info to {@link McpEndpointSpec}. *

Only keep first item in frontEndpointConfigList for endpoint spec generation.

* * @param server server detail info * @return endpoint spec, null if not convert is not supported. */ private McpEndpointSpec generateEndpointSpec(McpServerDetailInfo server) { if (AiConstants.Mcp.MCP_PROTOCOL_STDIO.equals(server.getProtocol())) { return null; } if (server.getRemoteServerConfig() == null || CollectionUtils.isEmpty(server.getRemoteServerConfig().getFrontEndpointConfigList())) { return null; } try { FrontEndpointConfig first = server.getRemoteServerConfig() .getFrontEndpointConfigList() .get(0); return McpConfigUtils.convertFrontEndpointConfig(first); } catch (Exception e) { LOG.error("Failed to convert to endpoint spec", e); throw new RuntimeException("Failed to convert to endpoint spec", e); } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/McpServerOperationService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.model.mcp.McpServerStorageInfo; import com.alibaba.nacos.ai.utils.McpConfigUtils; import com.alibaba.nacos.ai.utils.McpRequestUtil; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpCapability; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointInfo; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerVersionInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.mcp.registry.KeyValueInput; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.naming.core.v2.pojo.Service; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanUtils; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; import static com.alibaba.nacos.ai.constant.Constants.MCP_SERVER_CONFIG_MARK; import static com.alibaba.nacos.ai.utils.McpConfigUtils.buildMcpServerVersionConfigTags; /** * Nacos AI MCP server operation service. Currently, mcp server is present by there configs: 1. mcp server version info * {@link McpServerVersionInfo} 2. mcp server description for specified version {@link McpServerDetailInfo} 3. mcp tools * info {@link McpToolSpecification} when create the mcp server, we will tag the {@link McpServerVersionInfo} with mcp * servername for name fuzzy search. * * @author xiweng.yy */ @org.springframework.stereotype.Service public class McpServerOperationService { private static final Logger LOGGER = LoggerFactory.getLogger(McpServerOperationService.class); private final ConfigQueryChainService configQueryChainService; private final ConfigOperationService configOperationService; private final McpToolOperationService toolOperationService; private final McpEndpointOperationService endpointOperationService; private final McpServerIndex mcpServerIndex; private final SyncEffectService syncEffectService; public McpServerOperationService(ConfigQueryChainService configQueryChainService, ConfigOperationService configOperationService, McpToolOperationService toolOperationService, McpEndpointOperationService endpointOperationService, McpServerIndex mcpServerIndex, SyncEffectService syncEffectService) { this.configQueryChainService = configQueryChainService; this.configOperationService = configOperationService; this.toolOperationService = toolOperationService; this.endpointOperationService = endpointOperationService; this.mcpServerIndex = mcpServerIndex; this.syncEffectService = syncEffectService; } /** * List mcp server. * * @param namespaceId namespace id of mcp servers * @param mcpName mcp name pattern, if null or empty, filter all mcp servers. * @param search search type `blur` or `accurate`, means whether to search by fuzzy or exact match by * `mcpName`. * @param pageNo page number, start from 1 * @param pageSize page size each page * @return list of {@link McpServerBasicInfo} matched input parameters. */ public Page listMcpServerWithPage(String namespaceId, String mcpName, String search, int pageNo, int pageSize) { Page indexData = mcpServerIndex.searchMcpServerByNameWithPage(namespaceId, mcpName, search, pageNo, pageSize); return mapIndexPageToBasicServerPage(indexData); } private List mapIndexListToServerList(List indexData) { List finalResult = Collections.emptyList(); if (CollectionUtils.isNotEmpty(indexData)) { finalResult = indexData.stream() .map((index) -> { ConfigQueryChainRequest request = buildQueryMcpServerVersionInfoRequest(index.getNamespaceId(), index.getId()); ConfigQueryChainResponse response = configQueryChainService.handle(request); McpServerBasicInfo basicInfo = transferToMcpServerVersionInfo(response.getContent()); basicInfo.setNamespaceId(index.getNamespaceId()); return basicInfo; }) .collect(Collectors.toList()); } return finalResult; } private Page mapIndexPageToBasicServerPage(Page indexData) { Page result = new Page<>(); result.setTotalCount(indexData.getTotalCount()); result.setPageNumber(indexData.getPageNumber()); result.setPagesAvailable(indexData.getPagesAvailable()); result.setPageItems(mapIndexListToServerList(indexData.getPageItems())); return result; } /** * Get specified mcp server detail info. mcpServerId or namespaceId + mcpServerName is needed. * * @param namespaceId namespace id of mcp server * @param mcpServerId id of mcp server * @return detail info with {@link McpServerDetailInfo} * @throws NacosException any exception during handling */ public McpServerDetailInfo getMcpServerDetail(String namespaceId, String mcpServerId, String mcpServerName, String version) throws NacosException { mcpServerId = resolveMcpServerId(namespaceId, mcpServerName, mcpServerId); McpServerVersionInfo mcpServerVersionInfo = getMcpServerVersionInfo(namespaceId, mcpServerId); if (StringUtils.isEmpty(version)) { int size = mcpServerVersionInfo.getVersionDetails().size(); ServerVersionDetail last = mcpServerVersionInfo.getVersionDetails().get(size - 1); version = last.getVersion(); } ConfigQueryChainRequest request = buildQueryMcpServerRequest(namespaceId, mcpServerId, version); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (McpConfigUtils.isConfigNotFound(response.getStatus())) { throw new NacosApiException(NacosApiException.NOT_FOUND, ErrorCode.MCP_SEVER_VERSION_NOT_FOUND, String.format("mcp server `%s` for version `%s` not found", mcpServerId, version)); } McpServerStorageInfo serverSpecification = JacksonUtils.toObj(response.getContent(), McpServerStorageInfo.class); McpServerDetailInfo result = new McpServerDetailInfo(); result.setId(mcpServerId); result.setNamespaceId(namespaceId); BeanUtils.copyProperties(serverSpecification, result); List versionDetails = mcpServerVersionInfo.getVersionDetails(); String latestVersion = mcpServerVersionInfo.getLatestPublishedVersion(); for (ServerVersionDetail versionDetail : versionDetails) { versionDetail.setIs_latest(versionDetail.getVersion().equals(latestVersion)); } result.setAllVersions(mcpServerVersionInfo.getVersionDetails()); ServerVersionDetail versionDetail = result.getVersionDetail(); versionDetail.setIs_latest(versionDetail.getVersion().equals(latestVersion)); result.setVersion(versionDetail.getVersion()); if (Objects.nonNull(serverSpecification.getToolsDescriptionRef())) { McpToolSpecification toolSpec = toolOperationService.getMcpTool(namespaceId, serverSpecification.getToolsDescriptionRef()); result.setToolSpec(toolSpec); } if (!AiConstants.Mcp.MCP_PROTOCOL_STDIO.equalsIgnoreCase(serverSpecification.getProtocol())) { injectEndpoint(result); } return result; } private McpServerVersionInfo getMcpServerVersionInfo(String namespaceId, String mcpServerId) throws NacosApiException { ConfigQueryChainRequest request = buildQueryMcpServerVersionInfoRequest(namespaceId, mcpServerId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (McpConfigUtils.isConfigNotFound(response.getStatus())) { throw new NacosApiException(NacosApiException.NOT_FOUND, ErrorCode.MCP_SERVER_NOT_FOUND, String.format("Mcp server [ID: %s] not found in namespace [%s]. Response: %s", mcpServerId, namespaceId, response.getMessage())); } return JacksonUtils.toObj(response.getContent(), McpServerVersionInfo.class); } private void injectEndpoint(McpServerDetailInfo detailInfo) throws NacosException { injectBackendEndpointRef(detailInfo); injectFrontendEndpointRef(detailInfo); } private void injectBackendEndpointRef(McpServerDetailInfo detailInfo) throws NacosException { List instances; instances = endpointOperationService.getMcpServerEndpointInstances( detailInfo.getRemoteServerConfig().getServiceRef()); String protocol = null; McpServiceRef serviceRef = detailInfo.getRemoteServerConfig().getServiceRef(); if (Objects.nonNull(serviceRef)) { protocol = serviceRef.getTransportProtocol(); } List backendEndpoints = transferToMcpEndpointInfo(instances, detailInfo.getRemoteServerConfig().getExportPath(), protocol); detailInfo.setBackendEndpoints(backendEndpoints); } private List transferToMcpEndpointInfoWithHeaders(List instances, String exportPath, String protocol, List headers) { List endpointInfos = new LinkedList<>(); for (Instance each : instances) { McpEndpointInfo mcpEndpointInfo = new McpEndpointInfo(); mcpEndpointInfo.setAddress(each.getIp()); mcpEndpointInfo.setPort(each.getPort()); mcpEndpointInfo.setProtocol(protocol); mcpEndpointInfo.setHeaders(headers); mcpEndpointInfo.setPath(exportPath); endpointInfos.add(mcpEndpointInfo); } return endpointInfos; } private List transferToMcpEndpointInfo(List instances, String exportPath, String protocol) { return transferToMcpEndpointInfoWithHeaders(instances, exportPath, protocol, null); } private void injectFrontendEndpointRef(McpServerDetailInfo detailInfo) throws NacosException { List frontEndpointConfigs = detailInfo.getRemoteServerConfig() .getFrontEndpointConfigList(); if (CollectionUtils.isEmpty(frontEndpointConfigs)) { detailInfo.setFrontendEndpoints(Collections.emptyList()); return; } List frontendEndpoints = new LinkedList<>(); for (FrontEndpointConfig each : frontEndpointConfigs) { if (AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF.equals(each.getEndpointType())) { McpServiceRef mcpServiceRef = McpRequestUtil.transferToMcpServiceRef(each.getEndpointData()); List instances = endpointOperationService.getMcpServerEndpointInstances(mcpServiceRef); List endpointInfos = transferToMcpEndpointInfoWithHeaders(instances, each.getPath(), each.getProtocol(), each.getHeaders()); frontendEndpoints.addAll(endpointInfos); } else if (AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT.equals(each.getEndpointType())) { McpEndpointInfo endpointInfo = new McpEndpointInfo(); endpointInfo.setPath(each.getPath()); endpointInfo.setProtocol(each.getProtocol()); endpointInfo.setHeaders(each.getHeaders()); String address = each.getEndpointData().toString(); if (InternetAddressUtil.containsPort(address)) { String[] info = InternetAddressUtil.splitIpPortStr(address); endpointInfo.setAddress(info[0]); endpointInfo.setPort(Integer.parseInt(info[1])); } else { endpointInfo.setAddress(address); endpointInfo.setPort(Constants.PROTOCOL_TYPE_HTTP.equals(each.getProtocol()) ? 80 : 443); } frontendEndpoints.add(endpointInfo); } else if (AiConstants.Mcp.MCP_FRONT_ENDPOINT_TYPE_TO_BACK.equals(each.getEndpointType())) { detailInfo.getBackendEndpoints().stream() .map((endpoint) -> { McpEndpointInfo frontEndpoint = new McpEndpointInfo(); frontEndpoint.setAddress(endpoint.getAddress()); frontEndpoint.setPort(endpoint.getPort()); frontEndpoint.setProtocol(endpoint.getProtocol()); frontEndpoint.setPath(each.getPath()); frontEndpoint.setHeaders(each.getHeaders()); return frontEndpoint; }) .forEach(frontendEndpoints::add); } detailInfo.setFrontendEndpoints(frontendEndpoints); } } /** * Create new mcp server. * * @param namespaceId namespace id of mcp server * @param serverSpecification mcp server specification, see {@link McpServerBasicInfo} * @param toolSpecification mcp server included tools, see {@link McpToolSpecification}, optional * @param endpointSpecification mcp server endpoint specification, see {@link McpEndpointSpec}, optional * @throws NacosException any exception during handling */ public String createMcpServer(String namespaceId, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { String existId = resolveMcpServerId(namespaceId, serverSpecification.getName(), StringUtils.EMPTY); if (StringUtils.isNotEmpty(existId)) { throw new NacosApiException(NacosApiException.CONFLICT, ErrorCode.RESOURCE_CONFLICT, String.format("mcp server `%s` has existed, please update it rather than create.", serverSpecification.getName())); } ServerVersionDetail versionDetail = serverSpecification.getVersionDetail(); if (null == versionDetail && StringUtils.isNotBlank(serverSpecification.getVersion())) { versionDetail = new ServerVersionDetail(); versionDetail.setVersion(serverSpecification.getVersion()); serverSpecification.setVersionDetail(versionDetail); } if (Objects.isNull(versionDetail) || StringUtils.isEmpty(versionDetail.getVersion())) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Version must be specified in parameter `serverSpecification`"); } String id; String customMcpId = serverSpecification.getId(); if (StringUtils.isEmpty(customMcpId)) { id = UUID.randomUUID().toString(); } else { if (!StringUtils.isUuidString(customMcpId)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "parameter `serverSpecification.id` is not match uuid pattern, must obey uuid pattern"); } if (mcpServerIndex.getMcpServerById(serverSpecification.getId()) != null) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "parameter `serverSpecification.id` conflict with exist mcp server id"); } id = customMcpId; } serverSpecification.setId(id); ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Constants.RELEASE_DATE_FORMAT); String formattedCurrentTime = currentTime.format(formatter); versionDetail.setRelease_date(formattedCurrentTime); McpServerStorageInfo newSpecification = new McpServerStorageInfo(); BeanUtils.copyProperties(serverSpecification, newSpecification); injectToolAndEndpoint(namespaceId, serverSpecification.getId(), newSpecification, toolSpecification, endpointSpecification, Boolean.FALSE); McpServerVersionInfo versionInfo = buildServerVersionInfo(newSpecification, id, versionDetail); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setUpdateForExist(Boolean.FALSE); ConfigFormV3 mcpServerVersionForm = buildMcpServerVersionForm(namespaceId, versionInfo); configOperationService.publishConfig(mcpServerVersionForm, configRequestInfo, null); ConfigForm configForm = buildMcpConfigForm(namespaceId, id, versionDetail.getVersion(), newSpecification); long startOperationTime = System.currentTimeMillis(); configOperationService.publishConfig(configForm, configRequestInfo, null); syncEffectService.toSync(configForm, startOperationTime); // Delete the relevant cache after a successful database operation invalidateCacheAfterDbOperation(namespaceId, serverSpecification.getName(), id); return id; } private static McpServerVersionInfo buildServerVersionInfo(McpServerBasicInfo serverSpecification, String id, ServerVersionDetail versionDetail) { McpServerVersionInfo versionInfo = new McpServerVersionInfo(); versionInfo.setName(serverSpecification.getName()); versionInfo.setId(id); versionInfo.setDescription(serverSpecification.getDescription()); versionInfo.setRepository(serverSpecification.getRepository()); versionInfo.setFrontProtocol(serverSpecification.getFrontProtocol()); versionInfo.setProtocol(serverSpecification.getProtocol()); versionInfo.setCapabilities(serverSpecification.getCapabilities()); versionInfo.setLatestPublishedVersion(serverSpecification.getVersionDetail().getVersion()); versionInfo.setVersions(Collections.singletonList(versionDetail)); return versionInfo; } /** * Update existed mcp server. * *

* `namespaceId` and `mcpServerId` can't be changed. *

* * @param namespaceId namespace id of mcp server, used to mark which mcp server to update * @param serverSpecification mcp server specification, see {@link McpServerBasicInfo} * @param toolSpecification mcp server included tools, see {@link McpToolSpecification}, optional * @param endpointSpecification mcp server endpoint specification, see {@link McpEndpointSpec}, optional * @param overrideExisting if replace all the instances when update the mcp server * @throws NacosException any exception during handling */ public void updateMcpServer(String namespaceId, boolean isPublish, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException { String mcpServerId = serverSpecification.getId(); mcpServerId = resolveMcpServerId(namespaceId, serverSpecification.getName(), mcpServerId); if (StringUtils.isEmpty(serverSpecification.getId())) { serverSpecification.setId(mcpServerId); } ServerVersionDetail versionDetail = serverSpecification.getVersionDetail(); if (null == versionDetail && StringUtils.isNotBlank(serverSpecification.getVersion())) { versionDetail = new ServerVersionDetail(); versionDetail.setVersion(serverSpecification.getVersion()); serverSpecification.setVersionDetail(versionDetail); } if (Objects.isNull(versionDetail) || StringUtils.isEmpty(versionDetail.getVersion())) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Version must be specified in parameter `serverSpecification`"); } final McpServerVersionInfo mcpServerVersionInfo = getMcpServerVersionInfo(namespaceId, mcpServerId); String updateVersion = versionDetail.getVersion(); McpServerStorageInfo newSpecification = new McpServerStorageInfo(); BeanUtils.copyProperties(serverSpecification, newSpecification); injectToolAndEndpoint(namespaceId, mcpServerId, newSpecification, toolSpecification, endpointSpecification, overrideExisting); ConfigForm configForm = buildMcpConfigForm(namespaceId, mcpServerId, updateVersion, newSpecification); configOperationService.publishConfig(configForm, new ConfigRequestInfo(), null); List versionDetails = mcpServerVersionInfo.getVersionDetails(); Set versionSet = versionDetails.stream().map(ServerVersionDetail::getVersion) .collect(Collectors.toSet()); if (!versionSet.contains(updateVersion)) { ServerVersionDetail version = new ServerVersionDetail(); version.setVersion(updateVersion); versionDetails.add(version); mcpServerVersionInfo.setVersions(versionDetails); } if (isPublish) { mcpServerVersionInfo.setName(newSpecification.getName()); mcpServerVersionInfo.setDescription(newSpecification.getDescription()); mcpServerVersionInfo.setRepository(newSpecification.getRepository()); mcpServerVersionInfo.setProtocol(newSpecification.getProtocol()); mcpServerVersionInfo.setFrontProtocol(newSpecification.getFrontProtocol()); mcpServerVersionInfo.setCapabilities(newSpecification.getCapabilities()); mcpServerVersionInfo.setLatestPublishedVersion(updateVersion); for (ServerVersionDetail detail : versionDetails) { if (detail.getVersion().equals(updateVersion)) { ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'"); String formattedCurrentTime = currentTime.format(formatter); detail.setRelease_date(formattedCurrentTime); detail.setIs_latest(true); break; } else { detail.setIs_latest(false); } } mcpServerVersionInfo.setVersions(versionDetails); mcpServerVersionInfo.setEnabled(newSpecification.isEnabled()); } ConfigFormV3 mcpServerVersionForm = buildMcpServerVersionForm(namespaceId, mcpServerVersionInfo); long startOperationTime = System.currentTimeMillis(); configOperationService.publishConfig(mcpServerVersionForm, new ConfigRequestInfo(), null); syncEffectService.toSync(mcpServerVersionForm, startOperationTime); // Delete the relevant cache after a successful database operation invalidateCacheAfterDbUpdateOperation(namespaceId, mcpServerVersionInfo.getName(), serverSpecification.getName(), mcpServerId); } /** * Delete existed mcp server. * * @param namespaceId namespace id of mcp server * @param mcpServerId name of mcp server * @throws NacosException any exception during handling */ public void deleteMcpServer(String namespaceId, String mcpName, String mcpServerId, String version) throws NacosException { mcpServerId = resolveMcpServerId(namespaceId, mcpName, mcpServerId); McpServerVersionInfo mcpServerVersionInfo = getMcpServerVersionInfo(namespaceId, mcpServerId); List versionsNeedDelete = new ArrayList<>(); if (StringUtils.isNotEmpty(version)) { versionsNeedDelete.add(version); } else { versionsNeedDelete = mcpServerVersionInfo.getVersionDetails().stream().map(ServerVersionDetail::getVersion) .collect(Collectors.toList()); } for (String versionNeedDelete : versionsNeedDelete) { toolOperationService.deleteMcpTool(namespaceId, mcpServerId, versionNeedDelete); endpointOperationService.deleteMcpServerEndpointService(namespaceId, mcpServerVersionInfo.getName() + "::" + versionNeedDelete); String serverSpecDataId = McpConfigUtils.formatServerSpecInfoDataId(mcpServerId, versionNeedDelete); configOperationService.deleteConfig(serverSpecDataId, Constants.MCP_SERVER_GROUP, namespaceId, null, null, "nacos", null); String serverVersionDataId = McpConfigUtils.formatServerVersionInfoDataId(mcpServerId); configOperationService.deleteConfig(serverVersionDataId, Constants.MCP_SERVER_VERSIONS_GROUP, namespaceId, null, null, "nacos", null); } // Delete the relevant cache after a successful database operation invalidateCacheAfterDbOperation(namespaceId, mcpName, mcpServerId); } private void injectToolAndEndpoint(String namespaceId, String mcpServerId, McpServerStorageInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException { serverSpecification.setCapabilities(new LinkedList<>()); boolean hasToolSpec = toolSpecification != null; boolean hasTools = hasToolSpec && toolSpecification.getTools() != null; boolean hasSecuritySchemes = hasToolSpec && toolSpecification.getSecuritySchemes() != null; boolean hasEncryptedData = hasToolSpec && toolSpecification.getEncryptData() != null; boolean shouldCreateToolConfig = hasToolSpec && (hasTools || hasSecuritySchemes || hasEncryptedData); if (shouldCreateToolConfig) { toolOperationService.refreshMcpTool(namespaceId, serverSpecification, toolSpecification); serverSpecification.getCapabilities().add(McpCapability.TOOL); String version = serverSpecification.getVersionDetail().getVersion(); String toolSpecDataId = McpConfigUtils.formatServerToolSpecDataId(mcpServerId, version); serverSpecification.setToolsDescriptionRef(toolSpecDataId); } if (null != endpointSpecification) { Service service = endpointOperationService.createMcpServerEndpointServiceIfNecessary(namespaceId, serverSpecification.getName(), serverSpecification.getVersionDetail().getVersion(), endpointSpecification, overrideExisting); String transportProtocol = endpointSpecification.getData().get(Constants.MCP_BACKEND_INSTANCE_PROTOCOL_KEY); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId(service.getNamespace()); serviceRef.setGroupName(service.getGroup()); serviceRef.setServiceName(service.getName()); serviceRef.setTransportProtocol(transportProtocol); serverSpecification.getRemoteServerConfig().setServiceRef(serviceRef); } } private ConfigFormV3 buildMcpServerVersionForm(String namespaceId, McpServerVersionInfo mcpServerVersionInfo) { ConfigFormV3 configFormV3 = new ConfigFormV3(); configFormV3.setGroupName(Constants.MCP_SERVER_VERSIONS_GROUP); configFormV3.setGroup(Constants.MCP_SERVER_VERSIONS_GROUP); configFormV3.setNamespaceId(namespaceId); configFormV3.setDataId(McpConfigUtils.formatServerVersionInfoDataId(mcpServerVersionInfo.getId())); configFormV3.setContent(JacksonUtils.toJson(mcpServerVersionInfo)); configFormV3.setType(ConfigType.JSON.getType()); configFormV3.setAppName(mcpServerVersionInfo.getName()); configFormV3.setSrcUser("nacos"); String configTags = buildMcpServerVersionConfigTags(mcpServerVersionInfo.getName()); configFormV3.setConfigTags(configTags); return configFormV3; } private ConfigFormV3 buildMcpConfigForm(String namespaceId, String mcpServerId, String version, McpServerBasicInfo serverSpecification) { ConfigFormV3 configFormV3 = new ConfigFormV3(); configFormV3.setGroupName(Constants.MCP_SERVER_GROUP); configFormV3.setGroup(Constants.MCP_SERVER_GROUP); configFormV3.setNamespaceId(namespaceId); configFormV3.setDataId(McpConfigUtils.formatServerSpecInfoDataId(mcpServerId, version)); configFormV3.setContent(JacksonUtils.toJson(serverSpecification)); configFormV3.setType(ConfigType.JSON.getType()); configFormV3.setAppName(serverSpecification.getName()); configFormV3.setSrcUser("nacos"); configFormV3.setConfigTags(MCP_SERVER_CONFIG_MARK); return configFormV3; } private ConfigQueryChainRequest buildQueryMcpServerRequest(String namespaceId, String mcpServerId, String version) { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId(McpConfigUtils.formatServerSpecInfoDataId(mcpServerId, version)); request.setGroup(Constants.MCP_SERVER_GROUP); request.setTenant(namespaceId); return request; } private ConfigQueryChainRequest buildQueryMcpServerVersionInfoRequest(String namespaceId, String mcpServerId) { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId(McpConfigUtils.formatServerVersionInfoDataId(mcpServerId)); request.setGroup(Constants.MCP_SERVER_VERSIONS_GROUP); request.setTenant(namespaceId); return request; } private McpServerVersionInfo transferToMcpServerVersionInfo(String content) { McpServerVersionInfo versionInfo = JacksonUtils.toObj(content, McpServerVersionInfo.class); String latestPublishedVersion = versionInfo.getLatestPublishedVersion(); for (ServerVersionDetail versionDetail : versionInfo.getVersionDetails()) { if (versionDetail.getVersion().equals(latestPublishedVersion)) { versionDetail.setIs_latest(true); versionInfo.setVersionDetail(versionDetail); break; } else { versionDetail.setIs_latest(false); } } versionInfo.setVersion(latestPublishedVersion); return versionInfo; } private String resolveMcpServerId(String namespaceId, String serverName, String serverId) { if (StringUtils.isNotEmpty(serverId)) { return serverId; } McpServerIndexData indexData = mcpServerIndex.getMcpServerByName(namespaceId, serverName); if (Objects.nonNull(indexData)) { return indexData.getId(); } return null; } /** * Invalidate cache after update mcp server operation. * * @param namespaceId namespace id of mcp server * @param oldMcpName old mcp server name * @param newMcpName new mcp server name * @param mcpServerId mcp server id */ private void invalidateCacheAfterDbUpdateOperation(String namespaceId, String oldMcpName, String newMcpName, String mcpServerId) { try { if (StringUtils.isNotEmpty(oldMcpName) && !oldMcpName.equals(newMcpName)) { mcpServerIndex.removeMcpServerByName(namespaceId, oldMcpName); } if (StringUtils.isNotEmpty(newMcpName)) { mcpServerIndex.removeMcpServerByName(namespaceId, newMcpName); } if (StringUtils.isNotEmpty(mcpServerId)) { mcpServerIndex.removeMcpServerById(mcpServerId); } LOGGER.debug("Cache invalidated after updateMcpServer: namespaceId={}, oldName={}, newName={}, id={}", namespaceId, oldMcpName, newMcpName, mcpServerId); } catch (Exception e) { LOGGER.warn( "Failed to invalidate cache after updateMcpServer: namespaceId={}, oldName={}, newName={}, id={}, error={}", namespaceId, oldMcpName, newMcpName, mcpServerId, e.getMessage()); } } /** * Unified cache invalidation method. * * @param namespaceId namespace ID * @param mcpName MCP server name * @param mcpServerId MCP server ID */ private void invalidateCacheAfterDbOperation(String namespaceId, String mcpName, String mcpServerId) { try { if (StringUtils.isNotEmpty(mcpName)) { mcpServerIndex.removeMcpServerByName(namespaceId, mcpName); } if (StringUtils.isNotEmpty(mcpServerId)) { mcpServerIndex.removeMcpServerById(mcpServerId); } LOGGER.debug("Cache invalidated after DB operation: namespaceId={}, mcpName={}, mcpId={}", namespaceId, mcpName, mcpServerId); } catch (Exception e) { LOGGER.warn("Failed to invalidate cache after DB operation: namespaceId={}, mcpName={}, mcpId={}, error={}", namespaceId, mcpName, mcpServerId, e.getMessage()); } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/McpServerValidationService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.McpServerValidationConstants; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpServerValidationItem; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; /** * MCP Server Validation Service. * * @author nacos */ @Service public class McpServerValidationService { @Autowired private McpServerOperationService mcpServerOperationService; /** * Validate MCP servers for import. * * @param namespaceId namespace ID * @param servers servers to validate * @return validation result * @throws NacosException if validation fails */ public McpServerImportValidationResult validateServers(String namespaceId, List servers) throws NacosException { McpServerImportValidationResult result = new McpServerImportValidationResult(); List validationItems = new ArrayList<>(); List overallErrors = new ArrayList<>(); Set serverNames = new HashSet<>(); int validCount = 0; int invalidCount = 0; int duplicateCount = 0; try { for (McpServerDetailInfo server : servers) { McpServerValidationItem item = validateSingleServer(namespaceId, server, serverNames); validationItems.add(item); switch (item.getStatus()) { case McpServerValidationConstants.STATUS_VALID: validCount++; break; case McpServerValidationConstants.STATUS_INVALID: invalidCount++; break; case McpServerValidationConstants.STATUS_DUPLICATE: duplicateCount++; break; default: // Handle unknown status break; } } result.setValid(invalidCount == 0); result.setTotalCount(servers.size()); result.setValidCount(validCount); result.setInvalidCount(invalidCount); result.setDuplicateCount(duplicateCount); result.setServers(validationItems); result.setErrors(overallErrors); } catch (Exception e) { overallErrors.add("Validation failed: " + e.getMessage()); result.setValid(false); result.setErrors(overallErrors); } return result; } /** * Validate single MCP server. * * @param namespaceId namespace ID * @param server server to validate * @param existingNames existing server names in current batch * @return validation item */ private McpServerValidationItem validateSingleServer(String namespaceId, McpServerDetailInfo server, Set existingNames) { McpServerValidationItem item = new McpServerValidationItem(); List errors = new ArrayList<>(); String serverName = server.getName(); item.setServerName(serverName); item.setServerId(server.getId()); item.setServer(server); // Check required fields if (StringUtils.isBlank(serverName)) { errors.add("Server name is required"); } if (StringUtils.isBlank(server.getProtocol())) { errors.add("Protocol is required"); } else if (!isValidProtocol(server.getProtocol())) { errors.add("Invalid protocol: " + server.getProtocol()); } if (StringUtils.isBlank(server.getDescription())) { errors.add("Description is required"); } // Check for duplicates in current batch if (existingNames.contains(serverName + server.getVersionDetail().getVersion())) { errors.add("Duplicate server name in import batch: " + serverName); item.setStatus(McpServerValidationConstants.STATUS_DUPLICATE); } else { existingNames.add(serverName + server.getVersionDetail().getVersion()); } try { if (isVersionedServerExist(namespaceId, serverName, server.getVersionDetail().getVersion())) { item.setExists(true); if (!McpServerValidationConstants.STATUS_DUPLICATE.equals(item.getStatus())) { item.setStatus(McpServerValidationConstants.STATUS_DUPLICATE); errors.add("Server already exists: " + serverName); } } } catch (Exception e) { errors.add("Error checking existing server: " + e.getMessage()); } // Validate protocol-specific configurations validateProtocolSpecificConfig(server, errors); // Set validation status if (errors.isEmpty()) { item.setStatus(McpServerValidationConstants.STATUS_VALID); } else if (!McpServerValidationConstants.STATUS_DUPLICATE.equals(item.getStatus())) { item.setStatus(McpServerValidationConstants.STATUS_INVALID); } item.setErrors(errors); return item; } private boolean isVersionedServerExist(String namespaceId, String serverName, String version) throws NacosException { try { McpServerDetailInfo existingServer = mcpServerOperationService.getMcpServerDetail(namespaceId, version, serverName, null); return existingServer != null; } catch (NacosApiException e) { if (e.getDetailErrCode() == ErrorCode.MCP_SERVER_NOT_FOUND.getCode()) { return false; } throw e; } } /** * Check if protocol is valid. * * @param protocol protocol to check * @return true if valid */ private boolean isValidProtocol(String protocol) { return AiConstants.Mcp.MCP_PROTOCOL_STDIO.equals(protocol) || AiConstants.Mcp.MCP_PROTOCOL_SSE.equals(protocol) || AiConstants.Mcp.MCP_PROTOCOL_STREAMABLE.equals(protocol) || AiConstants.Mcp.MCP_PROTOCOL_HTTP.equals(protocol) || AiConstants.Mcp.MCP_PROTOCOL_DUBBO.equals(protocol); } /** * Validate protocol-specific configurations. * * @param server server to validate * @param errors error list to append to */ private void validateProtocolSpecificConfig(McpServerDetailInfo server, List errors) { String protocol = server.getProtocol(); if (AiConstants.Mcp.MCP_PROTOCOL_STDIO.equals(protocol)) { // For stdio protocol, check if command is provided if (server.getLocalServerConfig() == null && CollectionUtils.isEmpty(server.getPackages())) { errors.add("Local server configuration or packages are required for stdio protocol"); } } else { // For non-stdio protocols, basic validation if (server.getRemoteServerConfig() == null) { errors.add("Remote server configuration is required for " + protocol + " protocol"); } } // Validate tools if present if (server.getToolSpec() != null) { if (server.getToolSpec().getTools() == null || server.getToolSpec().getTools().isEmpty()) { errors.add("Tool specification should contain at least one tool"); } } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/McpToolOperationService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.utils.McpConfigUtils; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpTool; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.fasterxml.jackson.core.type.TypeReference; import org.springframework.stereotype.Service; /** * Nacos AI MCP tool operation service. * * @author xiweng.yy */ @Service public class McpToolOperationService { private final ConfigQueryChainService configQueryChainService; private final ConfigOperationService configOperationService; public McpToolOperationService(ConfigQueryChainService configQueryChainService, ConfigOperationService configOperationService) { this.configQueryChainService = configQueryChainService; this.configOperationService = configOperationService; } /** * Create or Update mcp server tool. If mcp server tool already exist, will full replace it. * * @param namespaceId namespace id of mcp server * @param toolSpecification mcp server included tools, see {@link McpTool}, optional * @throws NacosException any exception during handling */ public void refreshMcpTool(String namespaceId, McpServerBasicInfo serverBasicInfo, McpToolSpecification toolSpecification) throws NacosException { ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); ConfigFormV3 toolConfigForm = buildMcpToolConfigForm(namespaceId, serverBasicInfo, toolSpecification); configOperationService.publishConfig(toolConfigForm, configRequestInfo, null); } public McpToolSpecification getMcpTool(String namespaceId, String toolsDescriptionRef) { ConfigQueryChainRequest request = buildQueryMcpToolRequest(namespaceId, toolsDescriptionRef); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND == response.getStatus()) { return null; } return transferToMcpServerTool(response); } public void deleteMcpTool(String namespaceId, String mcpServerId, String version) throws NacosException { configOperationService.deleteConfig(McpConfigUtils.formatServerToolSpecDataId(mcpServerId, version), Constants.MCP_SERVER_TOOL_GROUP, namespaceId, null, null, "nacos", null); } private ConfigFormV3 buildMcpToolConfigForm(String namespaceId, McpServerBasicInfo mcpServerBasicInfo, McpToolSpecification toolSpecification) { ConfigFormV3 configFormV3 = new ConfigFormV3(); configFormV3.setGroupName(Constants.MCP_SERVER_TOOL_GROUP); configFormV3.setGroup(Constants.MCP_SERVER_TOOL_GROUP); configFormV3.setNamespaceId(namespaceId); String toolSpecDataId = McpConfigUtils.formatServerToolSpecDataId(mcpServerBasicInfo.getId(), mcpServerBasicInfo.getVersionDetail().getVersion()); configFormV3.setDataId(toolSpecDataId); configFormV3.setContent(JacksonUtils.toJson(toolSpecification)); configFormV3.setType(ConfigType.JSON.getType()); configFormV3.setAppName(mcpServerBasicInfo.getName()); configFormV3.setSrcUser("nacos"); configFormV3.setConfigTags(Constants.MCP_SERVER_CONFIG_MARK); return configFormV3; } private ConfigQueryChainRequest buildQueryMcpToolRequest(String namespaceId, String toolsDescriptionRef) { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId(toolsDescriptionRef); request.setGroup(Constants.MCP_SERVER_TOOL_GROUP); request.setTenant(namespaceId); return request; } private McpToolSpecification transferToMcpServerTool(ConfigQueryChainResponse response) { return JacksonUtils.toObj(response.getContent(), new TypeReference<>() { }); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/SimpleSyncEffectService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.config.server.model.form.ConfigForm; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * The simple implementation of {@link SyncEffectService}. Implemented by direct waiting time. * *

* This implementation is a very simple one. The best expected one is listen * {@link com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent} and then do async to sync by * {@link java.util.concurrent.CountDownLatch} or {@link java.util.concurrent.Future} *

* * @author xiweng.yy */ @Service public class SimpleSyncEffectService implements SyncEffectService { @Override public void toSync(ConfigForm configForm, long startTimeStamp, long timeout, TimeUnit timeUnit) { try { Thread.sleep(timeout); } catch (InterruptedException ignored) { } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/SyncEffectService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.config.server.model.form.ConfigForm; import java.util.concurrent.TimeUnit; /** * Nacos Mcp server async effect service. * *

* Nacos Mcp server or Agent will be written to Nacos configuration, and the configuration dump to local is async and need time. * This service is used to adapt and transfer async dump to sync dump. *

* * @author xiweng.yy */ public interface SyncEffectService { /** * Transfer Async mcp server operation to sync with 200 milliseconds timeout. * * @param configForm mcp server configuration changed form * @param startTimeStamp start time of operation mcp server. */ default void toSync(ConfigForm configForm, long startTimeStamp) { toSync(configForm, startTimeStamp, 200L, TimeUnit.MILLISECONDS); } /** * Transfer Async mcp server operation to sync. * * @param configForm mcp server configuration changed form * @param startTimeStamp start time of operation mcp server. * @param timeout max time wait for operation mcp server. * @param timeUnit the time unit for timeout */ void toSync(ConfigForm configForm, long startTimeStamp, long timeout, TimeUnit timeUnit); } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/a2a/A2aServerOperationService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.a2a; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.SyncEffectService; import com.alibaba.nacos.ai.service.a2a.identity.AgentIdCodecHolder; import com.alibaba.nacos.ai.utils.AgentCardUtil; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentInterface; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.exception.ConfigAlreadyExistsException; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.pojo.Service; import org.springframework.beans.BeanUtils; import java.util.List; import java.util.concurrent.ThreadLocalRandom; import static com.alibaba.nacos.ai.constant.Constants.A2A.AGENT_GROUP; import static com.alibaba.nacos.ai.constant.Constants.A2A.AGENT_VERSION_GROUP; /** * A2a server operation service. * * @author KiteSoar */ @org.springframework.stereotype.Service public class A2aServerOperationService { private final ConfigQueryChainService configQueryChainService; private final ConfigOperationService configOperationService; private final ConfigDetailService configDetailService; private final SyncEffectService syncEffectService; private final ServiceStorage serviceStorage; private final AgentIdCodecHolder agentIdCodecHolder; public A2aServerOperationService(ConfigQueryChainService configQueryChainService, ConfigOperationService configOperationService, ConfigDetailService configDetailService, SyncEffectService syncEffectService, ServiceStorage serviceStorage, AgentIdCodecHolder agentIdCodecHolder) { this.configQueryChainService = configQueryChainService; this.configOperationService = configOperationService; this.configDetailService = configDetailService; this.syncEffectService = syncEffectService; this.serviceStorage = serviceStorage; this.agentIdCodecHolder = agentIdCodecHolder; } /** * Register agent. * * @param agentCard agent card * @throws NacosException nacos exception */ public void registerAgent(AgentCard agentCard, String namespaceId, String registrationType) throws NacosException { try { // 1. register agent's info AgentCardVersionInfo agentCardVersionInfo = AgentCardUtil.buildAgentCardVersionInfo(agentCard, registrationType, true); ConfigForm configForm = transferVersionInfoToConfigForm(agentCardVersionInfo, namespaceId); ConfigRequestInfo versionConfigRequest = new ConfigRequestInfo(); versionConfigRequest.setUpdateForExist(Boolean.FALSE); configOperationService.publishConfig(configForm, versionConfigRequest, null); // 2. register agent's version info AgentCardDetailInfo agentCardDetailInfo = AgentCardUtil.buildAgentCardDetailInfo(agentCard, registrationType); ConfigForm configFormVersion = transferAgentInfoToConfigForm(agentCardDetailInfo, namespaceId); ConfigRequestInfo agentCardConfigRequest = new ConfigRequestInfo(); agentCardConfigRequest.setUpdateForExist(Boolean.FALSE); long startOperationTime = System.currentTimeMillis(); configOperationService.publishConfig(configFormVersion, agentCardConfigRequest, null); syncEffectService.toSync(configFormVersion, startOperationTime); } catch (ConfigAlreadyExistsException e) { throw new NacosApiException(NacosException.CONFLICT, ErrorCode.RESOURCE_CONFLICT, String.format("AgentCard name %s already exist", agentCard.getName())); } } /** * Delete agent. * * @param namespaceId namespaceId of agent * @param agentName agent name * @param version target version of want to delete, if is null or empty, delete all versions * @throws NacosException nacos exception */ public void deleteAgent(String namespaceId, String agentName, String version) throws NacosException { String encodedName = agentIdCodecHolder.encode(agentName); ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest(encodedName, AGENT_GROUP, namespaceId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) { return; } AgentCardVersionInfo agentCardVersionInfo = JacksonUtils.toObj(response.getContent(), AgentCardVersionInfo.class); List allVersions = agentCardVersionInfo.getVersionDetails().stream().map(AgentVersionDetail::getVersion) .toList(); // 1. If version is specified, only delete the corresponding version of the agent if (StringUtils.isNotEmpty(version)) { String versionDataId = encodedName + "-" + version; configOperationService.deleteConfig(versionDataId, AGENT_VERSION_GROUP, namespaceId, null, null, "nacos", null); List versionDetails = agentCardVersionInfo.getVersionDetails(); boolean isLatestVersion = version.equals(agentCardVersionInfo.getLatestPublishedVersion()); if (versionDetails.size() == 1 && versionDetails.get(0).getVersion().equals(version)) { configOperationService.deleteConfig(encodedName, AGENT_GROUP, namespaceId, null, null, "nacos", null); } else { agentCardVersionInfo.getVersionDetails() .removeIf(versionDetail -> versionDetail.getVersion().equals(version)); if (isLatestVersion) { agentCardVersionInfo.setLatestPublishedVersion(null); agentCardVersionInfo.setVersion(null); } ConfigForm updateForm = transferVersionInfoToConfigForm(agentCardVersionInfo, namespaceId); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setUpdateForExist(Boolean.TRUE); configOperationService.publishConfig(updateForm, configRequestInfo, null); } } else { // 2. If no version specified, delete all versions and agent information for (String each : allVersions) { String versionDataId = encodedName + "-" + each; configOperationService.deleteConfig(versionDataId, AGENT_VERSION_GROUP, namespaceId, null, null, "nacos", null); } configOperationService.deleteConfig(encodedName, AGENT_GROUP, namespaceId, null, null, "nacos", null); } } /** * Update agent card. * * @param agentCard the new agent card information * @param namespaceId namespace id * @param registrationType new registration type * @param setAsLatest whether set as latest version * @throws NacosException nacos exception */ public void updateAgentCard(AgentCard agentCard, String namespaceId, String registrationType, boolean setAsLatest) throws NacosException { final AgentCardVersionInfo existingAgentInfo = queryAgentCardVersionInfo(namespaceId, agentCard.getName()); // Check if the version exists, if not exist, add new version into version info boolean versionExisted = existingAgentInfo.getVersionDetails().stream().anyMatch( agentVersionDetail -> StringUtils.equals(agentVersionDetail.getVersion(), agentCard.getVersion())); if (!versionExisted) { existingAgentInfo.getVersionDetails().add(AgentCardUtil.buildAgentVersionDetail(agentCard, setAsLatest)); } // If input new registrationType is empty, use existed registrationType. if (StringUtils.isEmpty(registrationType)) { registrationType = existingAgentInfo.getRegistrationType(); } AgentCardDetailInfo agentCardDetailInfo = AgentCardUtil.buildAgentCardDetailInfo(agentCard, registrationType); BeanUtils.copyProperties(agentCardDetailInfo, existingAgentInfo, "versionDetails", "latestPublishedVersion"); if (setAsLatest) { existingAgentInfo.setLatestPublishedVersion(agentCard.getVersion()); List updatedVersionDetails = existingAgentInfo.getVersionDetails().stream() .peek(detail -> { if (StringUtils.equals(detail.getVersion(), agentCard.getVersion())) { // Only update the corresponding version detail.setLatest(true); AgentCardUtil.updateUpdateTime(detail); } else { detail.setLatest(false); } }).toList(); existingAgentInfo.setVersionDetails(updatedVersionDetails); } // Update agent version info ConfigForm configForm = transferVersionInfoToConfigForm(existingAgentInfo, namespaceId); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setUpdateForExist(Boolean.TRUE); configOperationService.publishConfig(configForm, configRequestInfo, null); // Update agent info ConfigForm versionConfigForm = transferAgentInfoToConfigForm(agentCardDetailInfo, namespaceId); ConfigRequestInfo versionConfigRequestInfo = new ConfigRequestInfo(); versionConfigRequestInfo.setUpdateForExist(Boolean.TRUE); long startOperationTime = System.currentTimeMillis(); configOperationService.publishConfig(versionConfigForm, versionConfigRequestInfo, null); syncEffectService.toSync(versionConfigForm, startOperationTime); } /** * List agents. * * @param namespaceId namespace id * @param agentName agent name * @param search search type, {@link Constants.A2A#SEARCH_BLUR} or {@link Constants.A2A#SEARCH_ACCURATE} * @param pageNo page number * @param pageSize page size * * @return agent card version info list */ public Page listAgents(String namespaceId, String agentName, String search, int pageNo, int pageSize) throws NacosException { String dataId; if (StringUtils.isEmpty(agentName) || Constants.A2A.SEARCH_BLUR.equalsIgnoreCase(search)) { search = Constants.A2A.SEARCH_BLUR; dataId = Constants.ALL_PATTERN + agentIdCodecHolder.encodeForSearch(agentName) + Constants.ALL_PATTERN; } else { search = Constants.A2A.SEARCH_ACCURATE; dataId = agentIdCodecHolder.encode(agentName); } Page configInfoPage = configDetailService.findConfigInfoPage(search, pageNo, pageSize, dataId, AGENT_GROUP, namespaceId, null); List versionInfos = configInfoPage.getPageItems().stream() .map(configInfo -> JacksonUtils.toObj(configInfo.getContent(), AgentCardVersionInfo.class)).toList(); Page result = new Page<>(); result.setPageItems(versionInfos); result.setTotalCount(configInfoPage.getTotalCount()); result.setPagesAvailable((int) Math.ceil((double) configInfoPage.getTotalCount() / (double) pageSize)); result.setPageNumber(pageNo); return result; } /** * List agent versions. * @param namespaceId namespace id of target agent * @param name name of target agent * @return agent version detail list */ public List listAgentVersions(String namespaceId, String name) throws NacosApiException { AgentCardVersionInfo agentCardVersionInfo = queryAgentCardVersionInfo(namespaceId, name); return agentCardVersionInfo.getVersionDetails(); } /** * Query Agent Card. If not specified version, query the latest version. * * @param namespaceId namespaceId of agent * @param agentName agent name * @param version target version of want to query, if is null or empty, get latest version * @param registrationType registration type * @return agent card detail info * @throws NacosApiException nacos api exception */ public AgentCardDetailInfo getAgentCard(String namespaceId, String agentName, String version, String registrationType) throws NacosApiException { AgentCardVersionInfo agentCardVersionInfo = queryAgentCardVersionInfo(namespaceId, agentName); return StringUtils.isEmpty(version) ? queryLatestVersion(agentCardVersionInfo, namespaceId, registrationType) : queryTargetVersion(agentCardVersionInfo, version, namespaceId, registrationType); } private AgentCardDetailInfo queryLatestVersion(AgentCardVersionInfo agentCardVersionInfo, String namespaceId, String registrationType) throws NacosApiException { String latestVersion = agentCardVersionInfo.getVersionDetails().stream().filter(AgentVersionDetail::isLatest) .findFirst().orElseThrow( () -> new NacosApiException(NacosException.NOT_FOUND, ErrorCode.AGENT_VERSION_NOT_FOUND, String.format("Agent %s latest version not found", agentCardVersionInfo.getName()))) .getVersion(); return queryTargetVersion(agentCardVersionInfo, latestVersion, namespaceId, registrationType); } private AgentCardDetailInfo queryTargetVersion(AgentCardVersionInfo agentCardVersionInfo, String version, String namespaceId, String registrationType) throws NacosApiException { String versionDataId = agentIdCodecHolder.encode(agentCardVersionInfo.getName()) + "-" + version; ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest(versionDataId, AGENT_VERSION_GROUP, namespaceId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.AGENT_VERSION_NOT_FOUND, String.format("Agent %s version %s not found.", agentCardVersionInfo.getName(), version)); } AgentCardDetailInfo result = JacksonUtils.toObj(response.getContent(), AgentCardDetailInfo.class); if (StringUtils.isBlank(registrationType)) { registrationType = result.getRegistrationType(); } if (AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE.equalsIgnoreCase(registrationType)) { injectEndpoint(result, namespaceId); } if (agentCardVersionInfo.getLatestPublishedVersion().equals(result.getVersion())) { result.setLatestVersion(true); } return result; } private void injectEndpoint(AgentCardDetailInfo agentCard, String namespaceId) { String serviceName = agentIdCodecHolder.encode(agentCard.getName()) + "::" + agentCard.getVersion(); Service service = Service.newService(namespaceId, Constants.A2A.AGENT_ENDPOINT_GROUP, serviceName); ServiceInfo serviceInfo = serviceStorage.getData(service); if (serviceInfo.getHosts().isEmpty()) { return; } List allAgentEndpoints = serviceInfo.getHosts().stream().map(AgentCardUtil::buildAgentInterface) .toList(); agentCard.setAdditionalInterfaces(allAgentEndpoints); List matchTransportEndpoints = allAgentEndpoints.stream() .filter(agentInterface -> agentInterface.getTransport() .equalsIgnoreCase(agentCard.getPreferredTransport())).toList(); AgentInterface randomPreferredTransportEndpoint = randomOne( matchTransportEndpoints.isEmpty() ? allAgentEndpoints : matchTransportEndpoints); agentCard.setUrl(randomPreferredTransportEndpoint.getUrl()); agentCard.setPreferredTransport(randomPreferredTransportEndpoint.getTransport()); } /** * TODO abstract a choose policy. */ private AgentInterface randomOne(List agentInterfaces) { return agentInterfaces.get(ThreadLocalRandom.current().nextInt(agentInterfaces.size())); } private ConfigForm transferVersionInfoToConfigForm(AgentCardVersionInfo agentCardVersionInfo, String namespaceId) { ConfigForm configForm = new ConfigForm(); String actualDataId = agentIdCodecHolder.encode(agentCardVersionInfo.getName()); configForm.setDataId(actualDataId); configForm.setGroup(AGENT_GROUP); configForm.setNamespaceId(namespaceId); configForm.setContent(JacksonUtils.toJson(agentCardVersionInfo)); configForm.setConfigTags("nacos.internal.config=agent"); configForm.setAppName(agentCardVersionInfo.getName()); configForm.setSrcUser("nacos"); configForm.setType(ConfigType.JSON.getType()); return configForm; } private ConfigForm transferAgentInfoToConfigForm(AgentCardDetailInfo storageInfo, String namespaceId) { ConfigForm configForm = new ConfigForm(); String actualDataId = agentIdCodecHolder.encode(storageInfo.getName()) + "-" + storageInfo.getVersion(); configForm.setDataId(actualDataId); configForm.setGroup(AGENT_VERSION_GROUP); configForm.setNamespaceId(namespaceId); configForm.setContent(JacksonUtils.toJson(storageInfo)); configForm.setConfigTags("nacos.internal.config=agent-version"); configForm.setAppName(storageInfo.getName()); configForm.setSrcUser("nacos"); configForm.setType(ConfigType.JSON.getType()); return configForm; } private AgentCardVersionInfo queryAgentCardVersionInfo(String namespaceId, String name) throws NacosApiException { // Check if the agent exists String actualDataId = agentIdCodecHolder.encode(name); ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest(actualDataId, AGENT_GROUP, namespaceId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.AGENT_NOT_FOUND, "Agent not found: " + name); } return JacksonUtils.toObj(response.getContent(), AgentCardVersionInfo.class); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/a2a/identity/AgentIdCodec.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.a2a.identity; /** * Nacos AI module A2A(Agent & AgentCard)identity Codec. * *

* Agent and AgentCard allow user custom agent name without limit for now, but no limit means out of control and might cause un-expected behavior. * So when storage in Nacos, it should be match some word limits. * We need to encode and decode agent name as the identity to do storage. *

* * @author xiweng.yy */ public interface AgentIdCodec { /** * Encode agent name to identity. * * @param agentName agent name * @return identity encoded from agent name */ String encode(String agentName); /** * Encode agent name to identity for search, which means only do encode value without any prefix and suffix, used to do blur search. * * @param agentName agent name * @return identity encoded from agent name */ String encodeForSearch(String agentName); /** * Decode agent id to agent name. * * @param agentId agent identity * @return agent name */ String decode(String agentId); } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/a2a/identity/AgentIdCodecHolder.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.a2a.identity; import org.springframework.beans.factory.ObjectProvider; import org.springframework.stereotype.Component; /** * The Holder of {@link AgentIdCodec}. * * @author xiweng.yy */ @Component public class AgentIdCodecHolder { private final AgentIdCodec agentIdCodec; public AgentIdCodecHolder(ObjectProvider agentIdCodecsProvider) { this.agentIdCodec = agentIdCodecsProvider.getIfAvailable(AsciiAgentIdCodec::new); } /** * Encode agent name to identity. * * @param agentName agent name * @return identity encoded from agent name */ public String encode(String agentName) { return agentIdCodec.encode(agentName); } /** * Encode agent name to identity for search, which means only do encode value without any prefix and suffix, used to do blur search. * * @param agentName agent name * @return identity encoded from agent name */ public String encodeForSearch(String agentName) { return agentIdCodec.encodeForSearch(agentName); } /** * Decode agent id to agent name. * * @param agentId agent identity * @return agent name */ public String decode(String agentId) { return agentIdCodec.decode(agentId); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/a2a/identity/AsciiAgentIdCodec.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.a2a.identity; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.utils.ParamUtils; import java.util.Set; /** * Agent Identity Codec implement by ASCII. * *

* Only consider show-able ASCII code from 32(x20) to 126(x7E). *

* * @author xiweng.yy */ public class AsciiAgentIdCodec implements AgentIdCodec { private static final String ENCODE_PREFIX = "____:"; private static final char ENCODE_MARK_CHAR = '_'; /** * Come From {@link ParamUtils#validChars} and remove {@link #ENCODE_MARK_CHAR}. */ private static final Set VALID_CHAR = Set.of('-', '.', ':'); @Override public String encode(String agentName) { if (!isNeedEncoded(agentName)) { return agentName; } StringBuilder sb = new StringBuilder(ENCODE_PREFIX); for (char ch : agentName.toCharArray()) { if (Character.isLetter(ch) || VALID_CHAR.contains(ch)) { // Keep letters, valid characters and non-underscores // number should be encoded, because the encoded result will contain numbers // which will cause search agentName contains unexpected results. sb.append(ch); } else { sb.append(ENCODE_MARK_CHAR).append(String.format("%03d", (int) ch)); } } return sb.toString(); } @Override public String encodeForSearch(String agentName) { String encodedName = encode(agentName); return isEncoded(encodedName) ? encodedName.substring(ENCODE_PREFIX.length()) : encodedName; } @Override public String decode(String agentId) { if (!isEncoded(agentId)) { return agentId; } String body = agentId.substring(ENCODE_PREFIX.length()); StringBuilder sb = new StringBuilder(); for (int i = 0; i < body.length(); ) { char ch = body.charAt(i); if (ch == '_' && i + 4 <= body.length()) { String codePart = body.substring(i + 1, i + 4); if (isDigit(codePart)) { int codePoint = Integer.parseInt(codePart, 10); sb.append((char) codePoint); i += 4; continue; } } sb.append(ch); i++; } return sb.toString(); } private boolean isDigit(String s) { for (char c : s.toCharArray()) { if (!Character.isDigit(c)) { return false; } } return s.length() == 3; } private boolean isEncoded(String name) { return name != null && name.startsWith(ENCODE_PREFIX); } private boolean isNeedEncoded(String name) { if (StringUtils.isEmpty(name)) { return false; } if (name.startsWith(ENCODE_PREFIX)) { return false; } for (char ch : name.toCharArray()) { if (!Character.isLetter(ch) && !VALID_CHAR.contains(ch)) { return true; } } return false; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptAdminOperationService.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVariable; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import java.util.List; /** * Prompt admin operation service. * * @author nacos */ public interface PromptAdminOperationService { /** * Publish prompt version boolean. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param version the version * @param template the template * @param commitMsg the commit msg * @param description the description * @param bizTags the biz tags * @param variables the prompt variable definitions with optional default values * @param srcUser the src user * @param srcIp the src ip * @return the boolean * @throws NacosException the nacos exception */ boolean publishPromptVersion(String namespaceId, String promptKey, String version, String template, String commitMsg, String description, List bizTags, List variables, String srcUser, String srcIp) throws NacosException; /** * Bind label boolean. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param label the label * @param version the version * @param srcUser the src user * @param srcIp the src ip * @return the boolean * @throws NacosException the nacos exception */ boolean bindLabel(String namespaceId, String promptKey, String label, String version, String srcUser, String srcIp) throws NacosException; /** * Unbind label boolean. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param label the label * @param srcUser the src user * @param srcIp the src ip * @return the boolean * @throws NacosException the nacos exception */ boolean unbindLabel(String namespaceId, String promptKey, String label, String srcUser, String srcIp) throws NacosException; /** * Delete prompt boolean. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param srcUser the src user * @param srcIp the src ip * @return the boolean * @throws NacosException the nacos exception */ boolean deletePrompt(String namespaceId, String promptKey, String srcUser, String srcIp) throws NacosException; /** * Update prompt metadata boolean. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param description the description * @param bizTags the biz tags * @param srcUser the src user * @param srcIp the src ip * @return the boolean * @throws NacosException the nacos exception */ boolean updatePromptMetadata(String namespaceId, String promptKey, String description, List bizTags, String srcUser, String srcIp) throws NacosException; /** * List prompts page. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param search the search * @param bizTags the biz tags * @param pageNo the page no * @param pageSize the page size * @return the page * @throws NacosException the nacos exception */ Page listPrompts(String namespaceId, String promptKey, String search, String bizTags, int pageNo, int pageSize) throws NacosException; /** * List prompt versions page. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param pageNo the page no * @param pageSize the page size * @return the page * @throws NacosException the nacos exception */ Page listPromptVersions(String namespaceId, String promptKey, int pageNo, int pageSize) throws NacosException; /** * Gets prompt meta. * * @param namespaceId the namespace id * @param promptKey the prompt key * @return the prompt meta * @throws NacosException the nacos exception */ PromptMetaInfo getPromptMeta(String namespaceId, String promptKey) throws NacosException; /** * Query prompt detail prompt version info. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param version the version * @param label the label * @return the prompt version info * @throws NacosException the nacos exception */ PromptVersionInfo queryPromptDetail(String namespaceId, String promptKey, String version, String label) throws NacosException; } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptAdminOperationServiceImpl.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.utils.PromptDataIdUtils; import com.alibaba.nacos.ai.utils.PromptVersionUtils; import com.alibaba.nacos.api.ai.model.prompt.PromptDescriptor; import com.alibaba.nacos.api.ai.model.prompt.PromptLabelVersionMapping; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVariable; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import static com.alibaba.nacos.ai.constant.Constants.ALL_PATTERN; import static com.alibaba.nacos.ai.constant.Constants.Prompt.PROMPT_CONFIG_TYPE; import static com.alibaba.nacos.ai.constant.Constants.Prompt.PROMPT_GROUP; import static com.alibaba.nacos.ai.constant.Constants.Prompt.SEARCH_BLUR; /** * Prompt admin operation service implementation. * * @author nacos */ @Service public class PromptAdminOperationServiceImpl implements PromptAdminOperationService { private static final Logger LOGGER = LoggerFactory.getLogger(PromptAdminOperationServiceImpl.class); private final ConfigOperationService configOperationService; private final ConfigDetailService configDetailService; private final ConfigInfoPersistService configInfoPersistService; public PromptAdminOperationServiceImpl(ConfigOperationService configOperationService, ConfigDetailService configDetailService, ConfigInfoPersistService configInfoPersistService) { this.configOperationService = configOperationService; this.configDetailService = configDetailService; this.configInfoPersistService = configInfoPersistService; } @Override public boolean publishPromptVersion(String namespaceId, String promptKey, String version, String template, String commitMsg, String description, List bizTags, List variables, String srcUser, String srcIp) throws NacosException { validatePromptKeyAndVersion(promptKey, version); if (!PromptVersionUtils.isValidVersion(version)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Version must be in format major.minor.patch"); } if (StringUtils.isBlank(template)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `template` not present"); } PromptLabelVersionMappingSnapshot snapshot = loadLabelVersionMappingSnapshot(namespaceId, promptKey); boolean newPrompt = snapshot.getMapping() == null; boolean hasMetadataFields = description != null || bizTags != null; if (!newPrompt && hasMetadataFields) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "description and bizTags can only be set on first publish, use updatePromptMetadata afterwards"); } PromptLabelVersionMapping mapping = snapshot.getMapping() == null ? PromptMetaUtils.initEmptyLabelVersionMapping(promptKey) : snapshot.getMapping(); if (mapping.getVersions().contains(version)) { throw new NacosApiException(NacosException.CONFLICT, ErrorCode.RESOURCE_CONFLICT, String.format("prompt version `%s` already exists", version)); } String versionDataId = PromptDataIdUtils.buildVersionDataId(promptKey, version); ConfigInfoWrapper versionConfig = configInfoPersistService.findConfigInfo(versionDataId, PROMPT_GROUP, namespaceId); if (versionConfig != null && StringUtils.isNotBlank(versionConfig.getContent())) { throw new NacosApiException(NacosException.CONFLICT, ErrorCode.RESOURCE_CONFLICT, String.format("prompt version `%s` already exists", version)); } long now = System.currentTimeMillis(); PromptVersionInfo versionInfo = new PromptVersionInfo(); versionInfo.setPromptKey(promptKey); versionInfo.setVersion(version); versionInfo.setTemplate(template); versionInfo.setCommitMsg(commitMsg); versionInfo.setGmtModified(now); versionInfo.setVariables(variables); publishConfig(namespaceId, versionDataId, JacksonUtils.toJson(versionInfo), srcUser, srcIp, null, false, null); mapping.getVersions().add(version); mapping.getVersions().sort(buildVersionComparator()); mapping.setLatestVersion(mapping.getVersions().get(mapping.getVersions().size() - 1)); mapping.setGmtModified(now); publishLabelVersionMapping(namespaceId, promptKey, mapping, snapshot.getMd5(), srcUser, srcIp); if (newPrompt) { PromptDescriptor descriptor = PromptMetaUtils.initEmptyDescriptor(promptKey); if (StringUtils.isNotBlank(description)) { descriptor.setDescription(description); } if (bizTags != null) { descriptor.setBizTags(new ArrayList<>(bizTags)); } publishDescriptor(namespaceId, promptKey, descriptor, null, srcUser, srcIp); } refreshLatestMirror(namespaceId, promptKey, mapping.getLatestVersion(), srcUser, srcIp); return true; } @Override public boolean bindLabel(String namespaceId, String promptKey, String label, String version, String srcUser, String srcIp) throws NacosException { validatePromptKeyAndVersion(promptKey, version); if (StringUtils.isBlank(label)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `label` not present"); } PromptLabelVersionMappingSnapshot snapshot = requireLabelVersionMappingSnapshot(namespaceId, promptKey); PromptLabelVersionMapping mapping = snapshot.getMapping(); if (!mapping.getVersions().contains(version)) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt version `%s` not found", version)); } mapping.getLabels().put(label, version); mapping.setGmtModified(System.currentTimeMillis()); publishLabelVersionMapping(namespaceId, promptKey, mapping, snapshot.getMd5(), srcUser, srcIp); return true; } @Override public boolean unbindLabel(String namespaceId, String promptKey, String label, String srcUser, String srcIp) throws NacosException { if (StringUtils.isBlank(promptKey) || StringUtils.isBlank(label)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `promptKey` and `label` not present"); } PromptLabelVersionMappingSnapshot snapshot = requireLabelVersionMappingSnapshot(namespaceId, promptKey); PromptLabelVersionMapping mapping = snapshot.getMapping(); mapping.getLabels().remove(label); mapping.setGmtModified(System.currentTimeMillis()); publishLabelVersionMapping(namespaceId, promptKey, mapping, snapshot.getMd5(), srcUser, srcIp); return true; } @Override public boolean deletePrompt(String namespaceId, String promptKey, String srcUser, String srcIp) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `promptKey` not present"); } PromptLabelVersionMappingSnapshot snapshot = loadLabelVersionMappingSnapshot(namespaceId, promptKey); configOperationService.deleteConfig(PromptDataIdUtils.buildDescriptorDataId(promptKey), PROMPT_GROUP, namespaceId, null, srcIp, srcUser, null); configOperationService.deleteConfig(PromptDataIdUtils.buildLabelVersionMappingDataId(promptKey), PROMPT_GROUP, namespaceId, null, srcIp, srcUser, null); configOperationService.deleteConfig(PromptDataIdUtils.buildLatestDataId(promptKey), PROMPT_GROUP, namespaceId, null, srcIp, srcUser, null); if (snapshot.getMapping() != null) { for (String version : new ArrayList<>(snapshot.getMapping().getVersions())) { String versionDataId = PromptDataIdUtils.buildVersionDataId(promptKey, version); configOperationService.deleteConfig(versionDataId, PROMPT_GROUP, namespaceId, null, srcIp, srcUser, null); } } return true; } @Override public boolean updatePromptMetadata(String namespaceId, String promptKey, String description, List bizTags, String srcUser, String srcIp) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `promptKey` not present"); } requireLabelVersionMappingSnapshot(namespaceId, promptKey); DescriptorSnapshot descriptorSnapshot = loadDescriptorSnapshot(namespaceId, promptKey); PromptDescriptor descriptor = descriptorSnapshot.getDescriptor() == null ? PromptMetaUtils.initEmptyDescriptor(promptKey) : descriptorSnapshot.getDescriptor(); if (description != null) { descriptor.setDescription(description); } if (bizTags != null) { descriptor.setBizTags(new ArrayList<>(bizTags)); } publishDescriptor(namespaceId, promptKey, descriptor, descriptorSnapshot.getMd5(), srcUser, srcIp); return true; } @Override public Page listPrompts(String namespaceId, String promptKey, String search, String bizTags, int pageNo, int pageSize) throws NacosException { String metaPattern; if (StringUtils.isEmpty(promptKey) || SEARCH_BLUR.equalsIgnoreCase(search)) { String keyPattern = StringUtils.isNotBlank(promptKey) ? promptKey : StringUtils.EMPTY; metaPattern = ALL_PATTERN + keyPattern + ALL_PATTERN + Constants.Prompt.DESCRIPTOR_DATA_ID_SUFFIX; search = SEARCH_BLUR; } else { metaPattern = PromptDataIdUtils.buildDescriptorDataId(promptKey); } Map configAdvanceInfo = null; if (StringUtils.isNotBlank(bizTags)) { configAdvanceInfo = new HashMap<>(2); configAdvanceInfo.put("config_tags", bizTags); } Page configPage = configDetailService.findConfigInfoPage(search, pageNo, pageSize, metaPattern, PROMPT_GROUP, namespaceId, configAdvanceInfo); List items = configPage.getPageItems().stream().map(each -> { try { PromptDescriptor descriptor = PromptMetaUtils.normalizeDescriptor( JacksonUtils.toObj(each.getContent(), PromptDescriptor.class)); if (each.getGmtModified() != null) { descriptor.setGmtModified(each.getGmtModified()); } String itemPromptKey = StringUtils.isBlank(descriptor.getPromptKey()) ? PromptDataIdUtils.extractPromptKeyFromDescriptorDataId(each.getDataId()) : descriptor.getPromptKey(); PromptLabelVersionMapping mapping = getPromptLabelVersionMapping(namespaceId, itemPromptKey); PromptMetaInfo merged = PromptMetaUtils.composeMetaInfo(itemPromptKey, mapping, descriptor); PromptMetaSummary result = new PromptMetaSummary(); result.setSchemaVersion(merged.getSchemaVersion()); result.setPromptKey(merged.getPromptKey()); result.setDescription(merged.getDescription()); result.setLatestVersion(merged.getLatestVersion()); result.setBizTags(merged.getBizTags() == null ? new ArrayList<>(4) : new ArrayList<>(merged.getBizTags())); result.setGmtModified(merged.getGmtModified()); return result; } catch (Exception ex) { return null; } }).filter(Objects::nonNull).collect(Collectors.toList()); Page result = new Page<>(); result.setPageNumber(pageNo); result.setPagesAvailable(configPage.getPagesAvailable()); result.setTotalCount(configPage.getTotalCount()); result.setPageItems(items); return result; } @Override public Page listPromptVersions(String namespaceId, String promptKey, int pageNo, int pageSize) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `promptKey` not present"); } PromptMetaInfo meta = getPromptMeta(namespaceId, promptKey); if (meta == null) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt `%s` not found", promptKey)); } List versions = new ArrayList<>(meta.getVersions()); versions.sort(buildVersionComparator().reversed()); int totalCount = versions.size(); int safePageNo = Math.max(pageNo, 1); int safePageSize = Math.max(pageSize, 1); int startIndex = (safePageNo - 1) * safePageSize; int endIndex = Math.min(startIndex + safePageSize, totalCount); List pagedVersions = startIndex < totalCount ? versions.subList(startIndex, endIndex) : Collections.emptyList(); List items = pagedVersions.stream().map(each -> { PromptVersionSummary result = new PromptVersionSummary(); result.setPromptKey(promptKey); result.setVersion(each); try { PromptVersionInfo detail = queryPromptDetail(namespaceId, promptKey, each, null); result.setCommitMsg(detail.getCommitMsg()); result.setSrcUser(detail.getSrcUser()); result.setGmtModified(detail.getGmtModified()); } catch (NacosException ex) { LOGGER.warn("Query prompt version detail failed for prompt={}, version={}", promptKey, each, ex); } return result; }).collect(Collectors.toList()); Page result = new Page<>(); result.setPageItems(items); result.setTotalCount(totalCount); result.setPagesAvailable((int) Math.ceil((double) totalCount / (double) safePageSize)); result.setPageNumber(safePageNo); return result; } @Override public PromptMetaInfo getPromptMeta(String namespaceId, String promptKey) throws NacosException { if (StringUtils.isBlank(promptKey)) { return null; } PromptLabelVersionMapping mapping = getPromptLabelVersionMapping(namespaceId, promptKey); if (mapping == null) { return null; } DescriptorSnapshot descriptorSnapshot = loadDescriptorSnapshot(namespaceId, promptKey); return PromptMetaUtils.composeMetaInfo(promptKey, mapping, descriptorSnapshot.getDescriptor()); } @Override public PromptVersionInfo queryPromptDetail(String namespaceId, String promptKey, String version, String label) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `promptKey` not present"); } PromptMetaInfo meta = getPromptMeta(namespaceId, promptKey); if (meta == null) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt `%s` not found", promptKey)); } String targetVersion = PromptMetaUtils.resolveTargetVersion(meta, version, label); String versionDataId = PromptDataIdUtils.buildVersionDataId(promptKey, targetVersion); ConfigAllInfo versionConfig = configInfoPersistService.findConfigAllInfo(versionDataId, PROMPT_GROUP, namespaceId); if (versionConfig == null || StringUtils.isBlank(versionConfig.getContent())) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt `%s` version `%s` not found", promptKey, targetVersion)); } PromptVersionInfo result = JacksonUtils.toObj(versionConfig.getContent(), PromptVersionInfo.class); result.setPromptKey(promptKey); result.setVersion(targetVersion); result.setMd5(versionConfig.getMd5()); result.setSrcUser(versionConfig.getCreateUser()); return result; } private PromptLabelVersionMappingSnapshot loadLabelVersionMappingSnapshot(String namespaceId, String promptKey) { ConfigInfoWrapper mappingConfig = configInfoPersistService.findConfigInfo( PromptDataIdUtils.buildLabelVersionMappingDataId(promptKey), PROMPT_GROUP, namespaceId); if (mappingConfig == null || StringUtils.isBlank(mappingConfig.getContent())) { return PromptLabelVersionMappingSnapshot.empty(); } PromptLabelVersionMapping mapping = PromptMetaUtils.normalizeLabelVersionMapping( JacksonUtils.toObj(mappingConfig.getContent(), PromptLabelVersionMapping.class)); return new PromptLabelVersionMappingSnapshot(mapping, mappingConfig.getMd5()); } private PromptLabelVersionMappingSnapshot requireLabelVersionMappingSnapshot(String namespaceId, String promptKey) throws NacosException { PromptLabelVersionMappingSnapshot snapshot = loadLabelVersionMappingSnapshot(namespaceId, promptKey); if (snapshot.getMapping() == null) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt `%s` not found", promptKey)); } return snapshot; } private PromptLabelVersionMapping getPromptLabelVersionMapping(String namespaceId, String promptKey) { return loadLabelVersionMappingSnapshot(namespaceId, promptKey).getMapping(); } private DescriptorSnapshot loadDescriptorSnapshot(String namespaceId, String promptKey) { ConfigInfoWrapper descriptorConfig = configInfoPersistService.findConfigInfo( PromptDataIdUtils.buildDescriptorDataId(promptKey), PROMPT_GROUP, namespaceId); if (descriptorConfig == null || StringUtils.isBlank(descriptorConfig.getContent())) { return DescriptorSnapshot.empty(); } PromptDescriptor descriptor = PromptMetaUtils.normalizeDescriptor( JacksonUtils.toObj(descriptorConfig.getContent(), PromptDescriptor.class)); descriptor.setGmtModified(descriptorConfig.getLastModified()); return new DescriptorSnapshot(descriptor, descriptorConfig.getMd5()); } private void refreshLatestMirror(String namespaceId, String promptKey, String latestVersion, String srcUser, String srcIp) throws NacosException { String latestDataId = PromptDataIdUtils.buildLatestDataId(promptKey); String versionDataId = PromptDataIdUtils.buildVersionDataId(promptKey, latestVersion); ConfigInfoWrapper versionConfig = configInfoPersistService.findConfigInfo(versionDataId, PROMPT_GROUP, namespaceId); if (versionConfig == null || StringUtils.isBlank(versionConfig.getContent())) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("latest version content `%s` not found", latestVersion)); } publishConfig(namespaceId, latestDataId, versionConfig.getContent(), srcUser, srcIp, null, true, null); } private void publishLabelVersionMapping(String namespaceId, String promptKey, PromptLabelVersionMapping mapping, String casMd5, String srcUser, String srcIp) throws NacosException { String mappingDataId = PromptDataIdUtils.buildLabelVersionMappingDataId(promptKey); publishConfig(namespaceId, mappingDataId, JacksonUtils.toJson(mapping), srcUser, srcIp, casMd5, true, null); } private void publishDescriptor(String namespaceId, String promptKey, PromptDescriptor descriptor, String casMd5, String srcUser, String srcIp) throws NacosException { String descriptorDataId = PromptDataIdUtils.buildDescriptorDataId(promptKey); publishConfig(namespaceId, descriptorDataId, JacksonUtils.toJson(descriptor), srcUser, srcIp, casMd5, true, joinBizTags(descriptor.getBizTags())); } private void publishConfig(String namespaceId, String dataId, String content, String srcUser, String srcIp, String casMd5, boolean updateForExist, String configTags) throws NacosException { ConfigForm form = new ConfigForm(); form.setDataId(dataId); form.setGroup(PROMPT_GROUP); form.setNamespaceId(namespaceId); form.setType(PROMPT_CONFIG_TYPE); form.setContent(content); form.setSrcUser(srcUser); if (StringUtils.isNotBlank(configTags)) { form.setConfigTags(configTags); } ConfigRequestInfo requestInfo = new ConfigRequestInfo(); requestInfo.setSrcIp(srcIp); requestInfo.setUpdateForExist(updateForExist); if (StringUtils.isNotBlank(casMd5)) { requestInfo.setCasMd5(casMd5); } configOperationService.publishConfig(form, requestInfo, null); } private void validatePromptKeyAndVersion(String promptKey, String version) throws NacosApiException { if (StringUtils.isBlank(promptKey) || StringUtils.isBlank(version)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `promptKey` and `version` not present"); } } private Comparator buildVersionComparator() { return (left, right) -> { if (Objects.equals(left, right)) { return 0; } if (!PromptVersionUtils.isValidVersion(left) || !PromptVersionUtils.isValidVersion(right)) { return left.compareTo(right); } return PromptVersionUtils.compareVersion(left, right); }; } private String joinBizTags(List bizTags) { if (bizTags == null || bizTags.isEmpty()) { return null; } return bizTags.stream().filter(StringUtils::isNotBlank).map(String::trim).distinct() .collect(Collectors.joining(",")); } private static class DescriptorSnapshot { private final PromptDescriptor descriptor; private final String md5; DescriptorSnapshot(PromptDescriptor descriptor, String md5) { this.descriptor = descriptor; this.md5 = md5; } public PromptDescriptor getDescriptor() { return descriptor; } public String getMd5() { return md5; } static DescriptorSnapshot empty() { return new DescriptorSnapshot(null, null); } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptClientOperationService.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.exception.NacosException; /** * Prompt client operation service. * * @author nacos */ public interface PromptClientOperationService { /** * Query prompt by label/version/latest with priority label > version > latest. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param version the version * @param label the label * @param md5 the client md5 for conditional query * @return the prompt version info * @throws NacosException the nacos exception */ PromptVersionInfo queryPrompt(String namespaceId, String promptKey, String version, String label, String md5) throws NacosException; /** * Query prompt by label/version/latest with priority label > version > latest. * * @param namespaceId the namespace id * @param promptKey the prompt key * @param version the version * @param label the label * @return the prompt version info * @throws NacosException the nacos exception */ default PromptVersionInfo queryPrompt(String namespaceId, String promptKey, String version, String label) throws NacosException { return queryPrompt(namespaceId, promptKey, version, label, null); } /** * Invalidate prompt meta cache entry. * * @param namespaceId the namespace id * @param promptKey the prompt key */ void invalidateMetaCache(String namespaceId, String promptKey); /** * Get prompt meta with cache. * * @param namespaceId the namespace id * @param promptKey the prompt key * @return the prompt meta * @throws NacosException the nacos exception */ PromptMetaInfo getPromptMeta(String namespaceId, String promptKey) throws NacosException; } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptClientOperationServiceImpl.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.ai.utils.PromptDataIdUtils; import com.alibaba.nacos.api.ai.model.prompt.PromptLabelVersionMapping; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.config.server.utils.GroupKey2; import org.springframework.stereotype.Service; import java.util.concurrent.ConcurrentHashMap; import static com.alibaba.nacos.ai.constant.Constants.Prompt.PROMPT_GROUP; /** * Prompt client operation service implementation. * * @author nacos */ @Service public class PromptClientOperationServiceImpl implements PromptClientOperationService { private static final int DEFAULT_META_CACHE_MAX_SIZE = 10000; private static final long DEFAULT_META_CACHE_EXPIRE_SECONDS = 60; private final ConfigQueryChainService configQueryChainService; private final ConcurrentHashMap metaCache = new ConcurrentHashMap<>(); private final int metaCacheMaxSize; private final long metaCacheExpireMs; public PromptClientOperationServiceImpl(ConfigQueryChainService configQueryChainService) { this.configQueryChainService = configQueryChainService; this.metaCacheMaxSize = Integer.parseInt( System.getProperty("nacos.prompt.meta.cache.maxSize", String.valueOf(DEFAULT_META_CACHE_MAX_SIZE))); this.metaCacheExpireMs = 1000L * Long.parseLong( System.getProperty("nacos.prompt.meta.cache.expireSeconds", String.valueOf(DEFAULT_META_CACHE_EXPIRE_SECONDS))); } @Override public PromptVersionInfo queryPrompt(String namespaceId, String promptKey, String version, String label, String md5) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `promptKey` not present"); } PromptLabelVersionMapping mapping = getPromptLabelVersionMapping(namespaceId, promptKey); if (mapping == null) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt `%s` not found", promptKey)); } String targetVersion = PromptMetaUtils.resolveTargetVersion(mapping, version, label); String versionDataId = PromptDataIdUtils.buildVersionDataId(promptKey, targetVersion); if (StringUtils.isNotBlank(md5)) { String groupKey = GroupKey2.getKey(versionDataId, PROMPT_GROUP, namespaceId); if (ConfigCacheService.isUptodate(groupKey, md5)) { throw new NacosException(NacosException.NOT_MODIFIED, "prompt data is up to date"); } } ConfigQueryChainResponse response = queryConfig(namespaceId, versionDataId); if (!isFound(response)) { invalidateMetaCache(namespaceId, promptKey); throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt `%s` version `%s` not found", promptKey, targetVersion)); } PromptVersionInfo versionInfo = JacksonUtils.toObj(response.getContent(), PromptVersionInfo.class); versionInfo.setPromptKey(promptKey); versionInfo.setMd5(response.getMd5()); return versionInfo; } @Override public void invalidateMetaCache(String namespaceId, String promptKey) { if (StringUtils.isBlank(promptKey)) { return; } metaCache.remove(buildMetaCacheKey(namespaceId, promptKey)); } @Override public PromptMetaInfo getPromptMeta(String namespaceId, String promptKey) throws NacosException { PromptLabelVersionMapping mapping = getPromptLabelVersionMapping(namespaceId, promptKey); if (mapping == null) { return null; } return PromptMetaUtils.composeMetaInfo(promptKey, mapping, null); } private PromptLabelVersionMapping getPromptLabelVersionMapping(String namespaceId, String promptKey) { if (StringUtils.isBlank(promptKey)) { return null; } String cacheKey = buildMetaCacheKey(namespaceId, promptKey); MetaCacheEntry cacheEntry = metaCache.get(cacheKey); long now = System.currentTimeMillis(); if (cacheEntry != null && cacheEntry.expireAtMs > now) { return PromptMetaUtils.cloneLabelVersionMapping(cacheEntry.snapshot.getMapping()); } PromptLabelVersionMappingSnapshot loaded = loadLabelVersionMappingSnapshot(namespaceId, promptKey); if (loaded.getMapping() != null) { putMetaCache(cacheKey, loaded, now + metaCacheExpireMs); return PromptMetaUtils.cloneLabelVersionMapping(loaded.getMapping()); } return null; } private PromptLabelVersionMappingSnapshot loadLabelVersionMappingSnapshot(String namespaceId, String promptKey) { String dataId = PromptDataIdUtils.buildLabelVersionMappingDataId(promptKey); ConfigQueryChainResponse response = queryConfig(namespaceId, dataId); if (!isFound(response)) { return PromptLabelVersionMappingSnapshot.empty(); } PromptLabelVersionMapping mapping = PromptMetaUtils.normalizeLabelVersionMapping( JacksonUtils.toObj(response.getContent(), PromptLabelVersionMapping.class)); return new PromptLabelVersionMappingSnapshot(mapping, response.getMd5()); } private ConfigQueryChainResponse queryConfig(String namespaceId, String dataId) { ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest(dataId, PROMPT_GROUP, namespaceId); return configQueryChainService.handle(request); } private boolean isFound(ConfigQueryChainResponse response) { return response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL || response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY; } private String buildMetaCacheKey(String namespaceId, String promptKey) { return namespaceId + "::" + promptKey; } private void putMetaCache(String cacheKey, PromptLabelVersionMappingSnapshot snapshot, long expireAtMs) { if (metaCache.size() >= metaCacheMaxSize) { String removingKey = metaCache.keySet().stream().findFirst().orElse(null); if (removingKey != null) { metaCache.remove(removingKey); } } metaCache.put(cacheKey, new MetaCacheEntry(snapshot, expireAtMs)); } private static class MetaCacheEntry { private final PromptLabelVersionMappingSnapshot snapshot; private final long expireAtMs; MetaCacheEntry(PromptLabelVersionMappingSnapshot snapshot, long expireAtMs) { this.snapshot = snapshot; this.expireAtMs = expireAtMs; } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptLabelVersionMappingSnapshot.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptLabelVersionMapping; /** * Prompt label/version mapping snapshot. * * @author nacos */ public class PromptLabelVersionMappingSnapshot { private final PromptLabelVersionMapping mapping; private final String md5; public PromptLabelVersionMappingSnapshot(PromptLabelVersionMapping mapping, String md5) { this.mapping = mapping; this.md5 = md5; } public PromptLabelVersionMapping getMapping() { return mapping; } public String getMd5() { return md5; } public static PromptLabelVersionMappingSnapshot empty() { return new PromptLabelVersionMappingSnapshot(null, null); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptMetaCacheInvalidateService.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.utils.PromptDataIdUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.utils.GroupKey; import org.springframework.stereotype.Service; /** * Prompt meta cache invalidate service. * * @author nacos */ @Service public class PromptMetaCacheInvalidateService extends Subscriber { private final PromptClientOperationService promptOperationService; public PromptMetaCacheInvalidateService(PromptClientOperationService promptOperationService) { this.promptOperationService = promptOperationService; NotifyCenter.registerSubscriber(this); } @Override public void onEvent(LocalDataChangeEvent event) { String[] keyParts = GroupKey.parseKey(event.groupKey); String dataId = keyParts[0]; String group = keyParts[1]; String tenant = keyParts.length > 2 ? keyParts[2] : ""; if (!Constants.Prompt.PROMPT_GROUP.equals(group)) { return; } if (!PromptDataIdUtils.isLabelVersionMappingDataId(dataId)) { return; } String promptKey = PromptDataIdUtils.extractPromptKeyFromLabelVersionMappingDataId(dataId); promptOperationService.invalidateMetaCache(NamespaceUtil.processNamespaceParameter(tenant), promptKey); } @Override public Class subscribeType() { return LocalDataChangeEvent.class; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptMetaSnapshot.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; /** * Prompt meta snapshot. * * @author nacos */ public class PromptMetaSnapshot { private final PromptMetaInfo meta; private final String md5; public PromptMetaSnapshot(PromptMetaInfo meta, String md5) { this.meta = meta; this.md5 = md5; } public PromptMetaInfo getMeta() { return meta; } public String getMd5() { return md5; } public static PromptMetaSnapshot empty() { return new PromptMetaSnapshot(null, null); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/prompt/PromptMetaUtils.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.ai.utils.PromptVersionUtils; import com.alibaba.nacos.api.ai.model.prompt.PromptDescriptor; import com.alibaba.nacos.api.ai.model.prompt.PromptLabelVersionMapping; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import java.util.ArrayList; import java.util.HashMap; /** * Prompt meta utility methods. * * @author nacos */ public final class PromptMetaUtils { private PromptMetaUtils() { } /** * Ensure mutable collection fields are initialized. * * @param meta prompt meta * @return normalized prompt meta */ public static PromptMetaInfo normalizeMeta(PromptMetaInfo meta) { if (meta == null) { return null; } if (meta.getLabels() == null) { meta.setLabels(new HashMap<>(4)); } if (meta.getVersions() == null) { meta.setVersions(new ArrayList<>(4)); } if (meta.getBizTags() == null) { meta.setBizTags(new ArrayList<>(4)); } return meta; } /** * Ensure mapping collection fields are initialized. * * @param mapping prompt label/version mapping * @return normalized mapping */ public static PromptLabelVersionMapping normalizeLabelVersionMapping(PromptLabelVersionMapping mapping) { if (mapping == null) { return null; } if (mapping.getLabels() == null) { mapping.setLabels(new HashMap<>(4)); } if (mapping.getVersions() == null) { mapping.setVersions(new ArrayList<>(4)); } return mapping; } /** * Ensure descriptor collection fields are initialized. * * @param descriptor descriptor * @return normalized descriptor */ public static PromptDescriptor normalizeDescriptor(PromptDescriptor descriptor) { if (descriptor == null) { return null; } if (descriptor.getBizTags() == null) { descriptor.setBizTags(new ArrayList<>(4)); } return descriptor; } /** * Defensive clone for prompt meta object. * * @param meta prompt meta * @return cloned prompt meta */ public static PromptMetaInfo cloneMeta(PromptMetaInfo meta) { if (meta == null) { return null; } PromptMetaInfo copied = new PromptMetaInfo(); copied.setSchemaVersion(meta.getSchemaVersion()); copied.setPromptKey(meta.getPromptKey()); copied.setDescription(meta.getDescription()); copied.setLatestVersion(meta.getLatestVersion()); copied.setGmtModified(meta.getGmtModified()); copied.setBizTags(meta.getBizTags() == null ? new ArrayList<>(4) : new ArrayList<>(meta.getBizTags())); copied.setVersions(meta.getVersions() == null ? new ArrayList<>(4) : new ArrayList<>(meta.getVersions())); copied.setLabels(meta.getLabels() == null ? new HashMap<>(4) : new HashMap<>(meta.getLabels())); return copied; } /** * Defensive clone for prompt mapping object. * * @param mapping prompt label/version mapping * @return cloned mapping */ public static PromptLabelVersionMapping cloneLabelVersionMapping(PromptLabelVersionMapping mapping) { if (mapping == null) { return null; } PromptLabelVersionMapping copied = new PromptLabelVersionMapping(); copied.setSchemaVersion(mapping.getSchemaVersion()); copied.setPromptKey(mapping.getPromptKey()); copied.setLatestVersion(mapping.getLatestVersion()); copied.setGmtModified(mapping.getGmtModified()); copied.setVersions(mapping.getVersions() == null ? new ArrayList<>(4) : new ArrayList<>(mapping.getVersions())); copied.setLabels(mapping.getLabels() == null ? new HashMap<>(4) : new HashMap<>(mapping.getLabels())); return copied; } /** * Defensive clone for prompt descriptor object. * * @param descriptor descriptor * @return cloned descriptor */ public static PromptDescriptor cloneDescriptor(PromptDescriptor descriptor) { if (descriptor == null) { return null; } PromptDescriptor copied = new PromptDescriptor(); copied.setSchemaVersion(descriptor.getSchemaVersion()); copied.setPromptKey(descriptor.getPromptKey()); copied.setDescription(descriptor.getDescription()); copied.setGmtModified(descriptor.getGmtModified()); copied.setBizTags(descriptor.getBizTags() == null ? new ArrayList<>(4) : new ArrayList<>(descriptor.getBizTags())); return copied; } /** * Build empty prompt meta for first publish flow. * * @param promptKey prompt key * @return initialized prompt meta */ public static PromptMetaInfo initEmptyMeta(String promptKey) { PromptMetaInfo result = new PromptMetaInfo(); result.setPromptKey(promptKey); result.setVersions(new ArrayList<>(4)); result.setLabels(new HashMap<>(4)); result.setBizTags(new ArrayList<>(4)); return result; } /** * Build empty prompt label/version mapping for first publish flow. * * @param promptKey prompt key * @return initialized prompt mapping */ public static PromptLabelVersionMapping initEmptyLabelVersionMapping(String promptKey) { PromptLabelVersionMapping result = new PromptLabelVersionMapping(); result.setPromptKey(promptKey); result.setVersions(new ArrayList<>(4)); result.setLabels(new HashMap<>(4)); return result; } /** * Build empty prompt descriptor for first publish flow. * * @param promptKey prompt key * @return initialized prompt descriptor */ public static PromptDescriptor initEmptyDescriptor(String promptKey) { PromptDescriptor result = new PromptDescriptor(); result.setPromptKey(promptKey); result.setBizTags(new ArrayList<>(4)); return result; } /** * Compose full prompt meta info from mapping and descriptor. * * @param promptKey prompt key * @param mapping runtime mapping * @param descriptor descriptor * @return merged prompt meta info */ public static PromptMetaInfo composeMetaInfo(String promptKey, PromptLabelVersionMapping mapping, PromptDescriptor descriptor) { PromptMetaInfo result = new PromptMetaInfo(); result.setPromptKey(promptKey); if (mapping != null) { result.setSchemaVersion(mapping.getSchemaVersion()); result.setLatestVersion(mapping.getLatestVersion()); result.setVersions(mapping.getVersions() == null ? new ArrayList<>(4) : new ArrayList<>(mapping.getVersions())); result.setLabels(mapping.getLabels() == null ? new HashMap<>(4) : new HashMap<>(mapping.getLabels())); result.setGmtModified(mapping.getGmtModified()); } else { result.setVersions(new ArrayList<>(4)); result.setLabels(new HashMap<>(4)); } if (descriptor != null) { result.setSchemaVersion(descriptor.getSchemaVersion()); result.setDescription(descriptor.getDescription()); result.setBizTags(descriptor.getBizTags() == null ? new ArrayList<>(4) : new ArrayList<>(descriptor.getBizTags())); if (descriptor.getGmtModified() != null) { result.setGmtModified(descriptor.getGmtModified()); } } else { result.setBizTags(new ArrayList<>(4)); } return normalizeMeta(result); } /** * Resolve target version with precedence label > version > latest. * * @param meta prompt meta * @param version requested version * @param label requested label * @return resolved target version * @throws NacosException on invalid parameters or missing resources */ public static String resolveTargetVersion(PromptMetaInfo meta, String version, String label) throws NacosException { if (meta == null) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "prompt latest version not found"); } PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setLatestVersion(meta.getLatestVersion()); mapping.setLabels(meta.getLabels()); mapping.setVersions(meta.getVersions()); return resolveTargetVersion(mapping, version, label); } /** * Resolve target version with precedence label > version > latest. * * @param mapping prompt label/version mapping * @param version requested version * @param label requested label * @return resolved target version * @throws NacosException on invalid parameters or missing resources */ public static String resolveTargetVersion(PromptLabelVersionMapping mapping, String version, String label) throws NacosException { if (mapping == null) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "prompt latest version not found"); } if (StringUtils.isNotBlank(label)) { String target = mapping.getLabels().get(label); if (StringUtils.isBlank(target)) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt label `%s` not found", label)); } return target; } if (StringUtils.isNotBlank(version)) { if (!PromptVersionUtils.isValidVersion(version)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Version must be in format major.minor.patch"); } if (!mapping.getVersions().contains(version)) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, String.format("prompt version `%s` not found", version)); } return version; } if (StringUtils.isBlank(mapping.getLatestVersion())) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "prompt latest version not found"); } return mapping.getLatestVersion(); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/skills/SkillOperationService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.skills; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; /** * Skill operation service. * * @author nacos */ public interface SkillOperationService { /** * Register a new skill. * * @param skill skill object * @param namespaceId namespace ID * @return skill ID * @throws NacosException if registration failed */ String registerSkill(Skill skill, String namespaceId) throws NacosException; /** * Get skill detail. * * @param namespaceId namespace ID * @param skillName skill name * @return skill detail * @throws NacosException if skill not found */ Skill getSkillDetail(String namespaceId, String skillName) throws NacosException; /** * Update skill. * * @param skill skill object * @param namespaceId namespace ID * @throws NacosException if update failed */ void updateSkill(Skill skill, String namespaceId) throws NacosException; /** * Delete skill. * * @param namespaceId namespace ID * @param skillName skill name * @throws NacosException if delete failed */ void deleteSkill(String namespaceId, String skillName) throws NacosException; /** * List skills with pagination. * * @param namespaceId namespace ID * @param skillName skill name (for search) * @param search search keyword * @param pageNo page number * @param pageSize page size * @return skill list page * @throws NacosException if query failed */ Page listSkills(String namespaceId, String skillName, String search, int pageNo, int pageSize) throws NacosException; /** * Upload skill from zip file. * * @param namespaceId namespace ID * @param zipBytes zip file bytes * @return skill name * @throws NacosException if upload failed */ String uploadSkillFromZip(String namespaceId, byte[] zipBytes) throws NacosException; } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/service/skills/SkillOperationServiceImpl.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.skills; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.SyncEffectService; import com.alibaba.nacos.ai.utils.SkillZipParser; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.ai.model.skills.SkillResource; import com.alibaba.nacos.api.ai.model.skills.SkillUtils; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.exception.ConfigAlreadyExistsException; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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 static com.alibaba.nacos.ai.constant.Constants.Skills; /** * Skill operation service implementation. * * @author nacos */ @org.springframework.stereotype.Service public class SkillOperationServiceImpl implements SkillOperationService { private static final Logger LOGGER = LoggerFactory.getLogger(SkillOperationServiceImpl.class); private static final String SKILL_NAME_PATTERN = "^[a-zA-Z_-]+$"; /** * Parse resource ID to get type and resource name. * Format: {type}_{resourcename} or {resourcename} * If resourcename contains __, convert __ back to . */ private String[] parseResourceId(String resourceId) { if (StringUtils.isBlank(resourceId)) { return new String[]{"", ""}; } int underscoreIndex = resourceId.indexOf('_'); if (underscoreIndex > 0) { String type = resourceId.substring(0, underscoreIndex); String resourceName = resourceId.substring(underscoreIndex + 1); // Convert __ back to . resourceName = resourceName.replace("__", "."); return new String[]{type, resourceName}; } else { // No type, just resource name String resourceName = resourceId.replace("__", "."); return new String[]{"", resourceName}; } } private final ConfigQueryChainService configQueryChainService; private final ConfigOperationService configOperationService; private final ConfigInfoPersistService configInfoPersistService; private final SyncEffectService syncEffectService; public SkillOperationServiceImpl(ConfigQueryChainService configQueryChainService, ConfigOperationService configOperationService, ConfigInfoPersistService configInfoPersistService, SyncEffectService syncEffectService) { this.configQueryChainService = configQueryChainService; this.configOperationService = configOperationService; this.configInfoPersistService = configInfoPersistService; this.syncEffectService = syncEffectService; } @Override public String registerSkill(Skill skill, String namespaceId) throws NacosException { try { // 1. Validate skill name (only allow English letters, underscore, hyphen) if (StringUtils.isBlank(skill.getName())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Skill name is required"); } if (!skill.getName().matches(SKILL_NAME_PATTERN)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Skill name can only contain English letters, underscore, and hyphen"); } // 2. Build main config (skill.json) SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skill.getName()); long uniformId = System.currentTimeMillis(); ConfigForm mainConfigForm = buildMainConfigForm(skill, namespaceId, mainConfigInfo.getGroup(), uniformId); ConfigRequestInfo mainConfigRequest = new ConfigRequestInfo(); Boolean mainPublishResult = configOperationService.publishConfig(mainConfigForm, mainConfigRequest, null); if (mainPublishResult == null || !mainPublishResult) { throw new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.SERVER_ERROR, String.format("Failed to publish main config for skill: %s", skill.getName())); } // 3. Build and publish resource configs if (skill.getResource() != null && !skill.getResource().isEmpty()) { for (Map.Entry entry : skill.getResource().entrySet()) { SkillResource resource = entry.getValue(); SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skill.getName(), resource.getType(), resource.getName()); ConfigForm resourceConfigForm = buildResourceConfigForm(resource, namespaceId, resourceConfigInfo.getGroup(), resourceConfigInfo.getDataId(), uniformId); ConfigRequestInfo resourceConfigRequest = new ConfigRequestInfo(); Boolean resourcePublishResult = configOperationService.publishConfig(resourceConfigForm, resourceConfigRequest, null); if (resourcePublishResult == null || !resourcePublishResult) { throw new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.SERVER_ERROR, String.format("Failed to publish resource config for skill: %s, resource: %s", skill.getName(), resource.getName())); } } } long startOperationTime = System.currentTimeMillis(); syncEffectService.toSync(mainConfigForm, startOperationTime); return skill.getName(); } catch (ConfigAlreadyExistsException e) { throw new NacosApiException(NacosException.CONFLICT, ErrorCode.RESOURCE_CONFLICT, String.format("Skill name %s already exists", skill.getName())); } } @Override public Skill getSkillDetail(String namespaceId, String skillName) throws NacosException { // 1. Query main config SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest( mainConfigInfo.getDataId(), mainConfigInfo.getGroup(), namespaceId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "Skill not found: " + skillName); } // 2. Parse main config SkillMainConfig mainConfig = JacksonUtils.toObj(response.getContent(), SkillMainConfig.class); // 3. Build Skill object Skill skill = new Skill(); skill.setNamespaceId(namespaceId); skill.setName(mainConfig.getName()); skill.setDescription(mainConfig.getDescription()); skill.setInstruction(mainConfig.getInstruction()); // 4. Query all resource configs Map resourceMap = new HashMap<>( mainConfig.getResources() != null ? mainConfig.getResources().size() : 16); if (mainConfig.getResources() != null && !mainConfig.getResources().isEmpty()) { for (SkillResourceRef resourceRef : mainConfig.getResources()) { // Generate resourceId from type and name String resourceId = SkillUtils.generateResourceId(resourceRef.getType(), resourceRef.getName()); // Query resource config using resourceRef info SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); ConfigQueryChainRequest resourceRequest = ConfigQueryChainRequest.buildConfigQueryChainRequest( resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), namespaceId); ConfigQueryChainResponse resourceResponse = configQueryChainService.handle(resourceRequest); if (resourceResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL || resourceResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY) { SkillResource resource = JacksonUtils.toObj(resourceResponse.getContent(), SkillResource.class); // Use resourceId as key so multi-level paths and same filename in different folders are unique resourceMap.put(resourceId, resource); } else { LOGGER.warn("Resource configuration not found: dataId={}, group={}", resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup()); } } } skill.setResource(resourceMap); return skill; } @Override public void updateSkill(Skill skill, String namespaceId) throws NacosException { // 1. Check if skill exists and get existing main config SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skill.getName()); ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest( mainConfigInfo.getDataId(), mainConfigInfo.getGroup(), namespaceId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "Skill not found: " + skill.getName()); } // 2. Parse existing main config to get all existing resources SkillMainConfig existingMainConfig = JacksonUtils.toObj(response.getContent(), SkillMainConfig.class); // 3. Generate uniform timestamp for all configs in this update long uniformId = System.currentTimeMillis(); // 4. Update main config with uniformId ConfigForm mainConfigForm = buildMainConfigForm(skill, namespaceId, mainConfigInfo.getGroup(), uniformId); ConfigRequestInfo mainConfigRequest = new ConfigRequestInfo(); mainConfigRequest.setUpdateForExist(Boolean.TRUE); Boolean mainUpdateResult = configOperationService.publishConfig(mainConfigForm, mainConfigRequest, null); if (mainUpdateResult == null || !mainUpdateResult) { throw new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.SERVER_ERROR, String.format("Failed to update main config for skill: %s", skill.getName())); } // 5. Compute existing and new resource key sets Set existingResourceKeys = collectResourceKeys(existingMainConfig); Set newResourceKeys = collectSkillResourceKeys(skill); // 6. Process all resources from the new skill object updateSkillResources(skill, namespaceId, existingResourceKeys, uniformId); // 7. Delete resources that were removed deleteRemovedResources(skill.getName(), namespaceId, existingMainConfig, newResourceKeys); long startOperationTime = System.currentTimeMillis(); syncEffectService.toSync(mainConfigForm, startOperationTime); } private Set collectResourceKeys(SkillMainConfig mainConfig) { Set keys = new HashSet<>(); if (mainConfig.getResources() != null) { for (SkillResourceRef resourceRef : mainConfig.getResources()) { keys.add(SkillUtils.generateResourceId(resourceRef.getType(), resourceRef.getName())); } } return keys; } private Set collectSkillResourceKeys(Skill skill) { Set keys = new HashSet<>(); if (skill.getResource() != null) { for (Map.Entry entry : skill.getResource().entrySet()) { SkillResource resource = entry.getValue(); keys.add(SkillUtils.generateResourceId(resource.getType(), resource.getName())); } } return keys; } private void updateSkillResources(Skill skill, String namespaceId, Set existingResourceKeys, long uniformId) throws NacosException { if (skill.getResource() == null || skill.getResource().isEmpty()) { return; } for (Map.Entry entry : skill.getResource().entrySet()) { SkillResource resource = entry.getValue(); SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skill.getName(), resource.getType(), resource.getName()); String resourceKey = SkillUtils.generateResourceId(resource.getType(), resource.getName()); boolean isNewResource = !existingResourceKeys.contains(resourceKey); ConfigForm resourceConfigForm = buildResourceConfigForm(resource, namespaceId, resourceConfigInfo.getGroup(), resourceConfigInfo.getDataId(), uniformId); ConfigRequestInfo resourceConfigRequest = new ConfigRequestInfo(); if (!isNewResource) { resourceConfigRequest.setUpdateForExist(Boolean.TRUE); } Boolean resourcePublishResult = configOperationService.publishConfig( resourceConfigForm, resourceConfigRequest, null); if (resourcePublishResult == null || !resourcePublishResult) { throw new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.SERVER_ERROR, String.format("Failed to %s resource config for skill: %s, resource: %s", isNewResource ? "create" : "update", skill.getName(), resource.getName())); } } } private void deleteRemovedResources(String skillName, String namespaceId, SkillMainConfig existingMainConfig, Set newResourceKeys) throws NacosException { if (existingMainConfig.getResources() == null || existingMainConfig.getResources().isEmpty()) { return; } for (SkillResourceRef resourceRef : existingMainConfig.getResources()) { String key = SkillUtils.generateResourceId(resourceRef.getType(), resourceRef.getName()); if (!newResourceKeys.contains(key)) { SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); configOperationService.deleteConfig(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), namespaceId, null, null, "nacos", null); } } } @Override public void deleteSkill(String namespaceId, String skillName) throws NacosException { // 1. Query main config to get resource list SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); ConfigQueryChainRequest request = ConfigQueryChainRequest.buildConfigQueryChainRequest( mainConfigInfo.getDataId(), mainConfigInfo.getGroup(), namespaceId); ConfigQueryChainResponse response = configQueryChainService.handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) { return; // Already deleted } // 2. Delete all resource configs SkillMainConfig mainConfig = JacksonUtils.toObj(response.getContent(), SkillMainConfig.class); if (mainConfig.getResources() != null && !mainConfig.getResources().isEmpty()) { for (SkillResourceRef resourceRef : mainConfig.getResources()) { SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); configOperationService.deleteConfig(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), namespaceId, null, null, "nacos", null); } } // 3. Delete main config configOperationService.deleteConfig(mainConfigInfo.getDataId(), mainConfigInfo.getGroup(), namespaceId, null, null, "nacos", null); } @Override public Page listSkills(String namespaceId, String skillName, String search, int pageNo, int pageSize) throws NacosException { // Only query skill.json (main config), not resource_*.json String dataId = SkillUtils.SKILL_MAIN_DATA_ID; String groupPattern; if (StringUtils.isEmpty(skillName)) { // Query all skills: group=skill_* groupPattern = SkillUtils.SKILL_GROUP_PREFIX + Constants.ALL_PATTERN; } else if (Skills.SEARCH_ACCURATE.equalsIgnoreCase(search)) { // Exact match: group=skill_{skillName} SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); groupPattern = mainConfigInfo.getGroup(); } else { // Blur search: group=skill_*{skillName}* groupPattern = SkillUtils.SKILL_GROUP_PREFIX + Constants.ALL_PATTERN + skillName + Constants.ALL_PATTERN; } // Use ConfigInfoPersistService to query config list (now includes gmt_modified) Page configInfoPage = configInfoPersistService.findConfigInfoLike4Page(pageNo, pageSize, dataId, groupPattern, namespaceId, null); List skillBasicInfos = configInfoPage.getPageItems().stream().map(configInfo -> { try { SkillMainConfig mainConfig = JacksonUtils.toObj(configInfo.getContent(), SkillMainConfig.class); SkillBasicInfo basicInfo = new SkillBasicInfo(); basicInfo.setNamespaceId(namespaceId); basicInfo.setName(mainConfig.getName()); basicInfo.setDescription(mainConfig.getDescription()); // Get modify time directly from ConfigInfo basicInfo.setUpdateTime(configInfo.getGmtModified()); return basicInfo; } catch (Exception e) { LOGGER.warn("Failed to parse skill config: dataId={}, group={}", configInfo.getDataId(), configInfo.getGroup(), e); return null; } }).filter(java.util.Objects::nonNull).toList(); Page result = new Page<>(); // For blur search with filtering, we need to adjust total count // But for simplicity, use the original total count (may be slightly inaccurate for blur search) result.setPageItems(skillBasicInfos); result.setTotalCount(configInfoPage.getTotalCount()); result.setPagesAvailable((int) Math.ceil((double) configInfoPage.getTotalCount() / (double) pageSize)); result.setPageNumber(pageNo); return result; } @Override public String uploadSkillFromZip(String namespaceId, byte[] zipBytes) throws NacosException { Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, namespaceId); return registerSkill(skill, namespaceId); } /** * Build main config form. */ private ConfigForm buildMainConfigForm(Skill skill, String namespaceId, String skillGroup, long uniformId) { // Build main config (only references, no content) SkillMainConfig mainConfig = new SkillMainConfig(); mainConfig.setName(skill.getName()); mainConfig.setDescription(skill.getDescription()); mainConfig.setInstruction(skill.getInstruction()); mainConfig.setUniformId(uniformId); // Build resource references (without content) List resourceRefs = new ArrayList<>( skill.getResource() != null ? skill.getResource().size() : 16); if (skill.getResource() != null) { for (Map.Entry entry : skill.getResource().entrySet()) { SkillResource resource = entry.getValue(); SkillResourceRef ref = new SkillResourceRef(); ref.setName(resource.getName()); ref.setType(resource.getType()); resourceRefs.add(ref); } } mainConfig.setResources(resourceRefs); SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skill.getName()); ConfigForm configForm = new ConfigForm(); configForm.setDataId(mainConfigInfo.getDataId()); configForm.setGroup(mainConfigInfo.getGroup()); configForm.setNamespaceId(namespaceId); configForm.setContent(JacksonUtils.toJson(mainConfig)); configForm.setConfigTags("nacos.internal.config=skill"); configForm.setSrcUser("nacos"); configForm.setType(ConfigType.JSON.getType()); return configForm; } /** * Build resource config form. */ private ConfigForm buildResourceConfigForm(SkillResource resource, String namespaceId, String skillGroup, String resourceDataId, long uniformId) { // Add uniformId to resource metadata Map metadata = resource.getMetadata(); if (metadata == null) { metadata = new HashMap<>(4); resource.setMetadata(metadata); } metadata.put("uniformId", uniformId); ConfigForm configForm = new ConfigForm(); configForm.setDataId(resourceDataId); configForm.setGroup(skillGroup); configForm.setNamespaceId(namespaceId); configForm.setContent(JacksonUtils.toJson(resource)); configForm.setConfigTags("nacos.internal.config=skill-resource"); configForm.setSrcUser("nacos"); configForm.setType(ConfigType.JSON.getType()); return configForm; } /** * Skill main config (from skill.json). */ private static class SkillMainConfig { private String name; private String description; private String instruction; private Long uniformId; private List resources; 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 String getInstruction() { return instruction; } public void setInstruction(String instruction) { this.instruction = instruction; } public Long getUniformId() { return uniformId; } public void setUniformId(Long uniformId) { this.uniformId = uniformId; } public List getResources() { return resources; } public void setResources(List resources) { this.resources = resources; } } /** * Skill resource reference (in skill.json). */ private static class SkillResourceRef { private String name; private String type; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/AgentCardUtil.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardBasicInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentInterface; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.utils.StringUtils; import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; import java.util.Collections; /** * Utils for Agent Card of A2A. * * @author xiweng.yy */ public class AgentCardUtil { private static final String AGENT_INTERFACE_URL_PATTERN = "%s://%s:%s"; /** * Build Agent Card Storage Info from Agent Detail form. * * @param agentCard agent card * @return Agent Card Storage Info */ public static AgentCardDetailInfo buildAgentCardDetailInfo(AgentCard agentCard, String registrationType) { AgentCardDetailInfo agentCardDetailInfo = new AgentCardDetailInfo(); copyAgentCardInfo(agentCardDetailInfo, agentCard); agentCardDetailInfo.setRegistrationType(registrationType); return agentCardDetailInfo; } /** * Build Agent Card Storage Info from AgentCard. * * @param agentCard agent detail form * @param registrationType target registrationType * @param isLatest is latest version * @return Agent Card Version Info */ public static AgentCardVersionInfo buildAgentCardVersionInfo(AgentCard agentCard, String registrationType, boolean isLatest) { AgentCardVersionInfo agentCardVersionInfo = new AgentCardVersionInfo(); copyAgentCardBasicInfo(agentCardVersionInfo, agentCard); agentCardVersionInfo.setRegistrationType(registrationType); if (isLatest) { agentCardVersionInfo.setLatestPublishedVersion(agentCard.getVersion()); } agentCardVersionInfo.setVersionDetails(Collections.singletonList(buildAgentVersionDetail(agentCard, isLatest))); return agentCardVersionInfo; } /** * Build Agent version detail from Agent Detail form. * * @param agentCard agent detail form * @return Agent Version Detail */ public static AgentVersionDetail buildAgentVersionDetail(AgentCard agentCard, boolean isLatest) { AgentVersionDetail agentVersionDetail = new AgentVersionDetail(); agentVersionDetail.setCreatedAt(getCurrentTime()); agentVersionDetail.setUpdatedAt(getCurrentTime()); agentVersionDetail.setVersion(agentCard.getVersion()); agentVersionDetail.setLatest(isLatest); return agentVersionDetail; } /** * Update update time of agent version detail. * * @param versionDetail agent version detail */ public static void updateUpdateTime(AgentVersionDetail versionDetail) { versionDetail.setUpdatedAt(getCurrentTime()); } /** * Build {@link AgentInterface} from service {@link Instance}. * * @param instance service instance. * @return agent interface (endpoint) */ public static AgentInterface buildAgentInterface(Instance instance) { AgentInterface agentInterface = new AgentInterface(); String protocol = instance.getMetadata().get(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY); if (StringUtils.isEmpty(protocol)) { protocol = AiConstants.A2a.A2A_ENDPOINT_DEFAULT_PROTOCOL; } boolean isSupportTls = Boolean.parseBoolean( instance.getMetadata().get(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS)); protocol = handlerTlsIfNeeded(protocol, isSupportTls); String url = String.format(AGENT_INTERFACE_URL_PATTERN, protocol, instance.getIp(), instance.getPort()); String path = instance.getMetadata().get(Constants.A2A.AGENT_ENDPOINT_PATH_KEY); if (StringUtils.isNotBlank(path)) { url += path.startsWith("/") ? path : "/" + path; } String query = instance.getMetadata().get(Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY); if (StringUtils.isNotBlank(query)) { url += "?" + query; } agentInterface.setUrl(url); agentInterface.setTransport(instance.getMetadata().get(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY)); return agentInterface; } private static String handlerTlsIfNeeded(String protocol, boolean isSupportTls) { if (AiConstants.A2a.A2A_ENDPOINT_DEFAULT_PROTOCOL.equalsIgnoreCase(protocol)) { return isSupportTls ? Constants.PROTOCOL_TYPE_HTTPS : Constants.PROTOCOL_TYPE_HTTP; } return protocol; } private static String getCurrentTime() { ZonedDateTime currentTime = ZonedDateTime.now(ZoneOffset.UTC); DateTimeFormatter formatter = DateTimeFormatter.ofPattern(Constants.RELEASE_DATE_FORMAT); return currentTime.format(formatter); } private static void copyAgentCardInfo(AgentCard target, AgentCard source) { copyAgentCardBasicInfo(target, source); target.setUrl(source.getUrl()); target.setPreferredTransport(source.getPreferredTransport()); target.setAdditionalInterfaces(source.getAdditionalInterfaces()); target.setProvider(source.getProvider()); target.setSecuritySchemes(source.getSecuritySchemes()); target.setSecurity(source.getSecurity()); target.setDefaultInputModes(source.getDefaultInputModes()); target.setDefaultOutputModes(source.getDefaultOutputModes()); target.setSupportsAuthenticatedExtendedCard(source.getSupportsAuthenticatedExtendedCard()); target.setDocumentationUrl(source.getDocumentationUrl()); } private static void copyAgentCardBasicInfo(AgentCardBasicInfo target, AgentCardBasicInfo source) { target.setProtocolVersion(source.getProtocolVersion()); target.setName(source.getName()); target.setDescription(source.getDescription()); target.setVersion(source.getVersion()); target.setIconUrl(source.getIconUrl()); target.setCapabilities(source.getCapabilities()); target.setSkills(source.getSkills()); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/AgentEndpointUtil.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Utils for Agent Endpoint of A2A. * * @author xiweng.yy */ public class AgentEndpointUtil { /** * Transfer a collection of AgentEndpoint to a list of Instance. * * @param endpoints the collection of AgentEndpoint to transfer * @return the list of Instance transferred from AgentEndpoint * @throws NacosApiException if any validation failed during the transfer process */ public static List transferToInstances(Collection endpoints) throws NacosApiException { List result = new LinkedList<>(); for (AgentEndpoint endpoint : endpoints) { result.add(transferToInstance(endpoint)); } return result; } /** * Transfer a single AgentEndpoint to an Instance. * * @param endpoint the AgentEndpoint to transfer * @return the Instance transferred from AgentEndpoint * @throws NacosApiException if any validation failed during the transfer process */ public static Instance transferToInstance(AgentEndpoint endpoint) throws NacosApiException { Instance instance = new Instance(); instance.setIp(endpoint.getAddress()); instance.setPort(endpoint.getPort()); String path = StringUtils.isBlank(endpoint.getPath()) ? StringUtils.EMPTY : endpoint.getPath(); String protocol = StringUtils.isBlank(endpoint.getProtocol()) ? StringUtils.EMPTY : endpoint.getProtocol(); String query = StringUtils.isBlank(endpoint.getQuery()) ? StringUtils.EMPTY : endpoint.getQuery(); Map metadata = Map.of(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, path, Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, endpoint.getTransport(), Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, String.valueOf(endpoint.isSupportTls()), Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY, protocol, Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY, query); instance.setMetadata(metadata); instance.validate(); return instance; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/AgentRequestUtil.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCapabilities; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.remote.request.AbstractAgentRequest; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.fasterxml.jackson.core.type.TypeReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; /** * Agent and AgentCard request util. * * @author xiweng.yy */ public class AgentRequestUtil { private static final Logger LOGGER = LoggerFactory.getLogger(McpRequestUtil.class); /** * Parse Agent card request form to {@link AgentCard}. * * @param agentCardForm agent card request. * @return agent card * @throws NacosApiException if parse failed or request parameter is conflicted. */ public static AgentCard parseAgentCard(AgentCardForm agentCardForm) throws NacosApiException { try { AgentCard result = JacksonUtils.toObj(agentCardForm.getAgentCard(), new TypeReference<>() { }); validateAgentCard(result); return result; } catch (NacosDeserializationException e) { LOGGER.error(String.format("Deserialize %s from %s failed, ", AgentCard.class.getSimpleName(), agentCardForm.getAgentCard()), e); throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "agentCard is invalid. Can't be parsed."); } } /** * Validate agent card is legal. * * @param agentCard agent card * @throws NacosApiException if agent card is illegal. */ public static void validateAgentCard(AgentCard agentCard) throws NacosApiException { validateAgentCardField("name", agentCard.getName()); validateAgentCardField("version", agentCard.getVersion()); validateAgentCardField("protocolVersion", agentCard.getProtocolVersion()); validateAgentCardField("preferredTransport", agentCard.getPreferredTransport()); validateAgentCardField("url", agentCard.getUrl()); if (null == agentCard.getDescription()) { agentCard.setDescription(StringUtils.EMPTY); } if (null == agentCard.getCapabilities()) { agentCard.setCapabilities(new AgentCapabilities()); } if (null == agentCard.getDefaultInputModes()) { agentCard.setDefaultInputModes(List.of()); } if (null == agentCard.getDefaultOutputModes()) { agentCard.setDefaultOutputModes(List.of()); } if (null == agentCard.getSkills()) { agentCard.setSkills(List.of()); } } /** * If request contains valid namespaceId, do nothing. If not, fill default namespaceId. * * @param request agent request */ public static void fillNamespaceId(AbstractAgentRequest request) { if (StringUtils.isEmpty(request.getNamespaceId())) { request.setNamespaceId(AiConstants.A2a.A2A_DEFAULT_NAMESPACE); } } private static void validateAgentCardField(String fieldName, String fieldValue) throws NacosApiException { if (StringUtils.isEmpty(fieldValue)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `agentCard." + fieldName + "` not present"); } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/McpConfigUtils.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import java.util.Arrays; import static com.alibaba.nacos.ai.constant.Constants.MCP_SERVER_CONFIG_MARK; /** * Mcp config utils. * @author xinluo */ public class McpConfigUtils { private static final String MCP_ENDPOINT_CONFIG_DIRECT_SPLIT = ":"; private static final String MCP_ENDPOINT_SPEC_ADDRESS_KEY = "address"; private static final String MCP_ENDPOINT_SPEC_PORT_KEY = "port"; private static final int MCP_ENDPOINT_SPEC_PORT_KEY_LENGTH = 2; /** * Format the Mcp server version info config data id. * @param id server id * @return mcp server version info config data id */ public static String formatServerVersionInfoDataId(String id) { return String.format(Constants.SERVER_VERSION_CONFIG_DATA_ID_TEMPLATE, id); } public static String formatServerSpecInfoDataId(String id, String version) { return String.format(Constants.SERVER_SPECIFICATION_CONFIG_DATA_ID_TEMPLATE, id, version); } public static String formatServerToolSpecDataId(String id, String version) { return String.format(Constants.SERVER_TOOLS_SPEC_CONFIG_DATA_ID_TEMPLATE, id, version); } public static String formatServerNameTagBlurSearchValue(String serverName) { return Constants.MCP_SERVER_NAME_TAG_KEY_PREFIX + Constants.ALL_PATTERN + serverName + Constants.ALL_PATTERN; } public static String formatServerNameTagAccurateSearchValue(String serverName) { return Constants.MCP_SERVER_NAME_TAG_KEY_PREFIX + serverName; } public static boolean isConfigFound(ConfigQueryChainResponse.ConfigQueryStatus status) { return ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL.equals(status); } public static boolean isConfigNotFound(ConfigQueryChainResponse.ConfigQueryStatus status) { return ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND.equals(status); } public static String buildMcpServerVersionConfigTags(String serverName) { return StringUtils.join(Arrays.asList(MCP_SERVER_CONFIG_MARK, Constants.MCP_SERVER_NAME_TAG_KEY_PREFIX + serverName), ","); } /** * Convert {@link FrontEndpointConfig} to {@link McpEndpointSpec}. *

Only support MCP_ENDPOINT_TYPE_DIRECT type.

* @param frontEndpointConfig front endpoint config * @return mcp endpoint spec * @throws IllegalArgumentException if convert failed */ public static McpEndpointSpec convertFrontEndpointConfig(FrontEndpointConfig frontEndpointConfig) { Object epDataObj = frontEndpointConfig.getEndpointData(); String epData = (String) epDataObj; McpEndpointSpec endpointSpec = new McpEndpointSpec(); String[] hp = epData.split(MCP_ENDPOINT_CONFIG_DIRECT_SPLIT); if (hp.length != MCP_ENDPOINT_SPEC_PORT_KEY_LENGTH) { throw new IllegalArgumentException("Invalid endpoint data: " + epData); } endpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); endpointSpec.getData().put(MCP_ENDPOINT_SPEC_ADDRESS_KEY, hp[0]); endpointSpec.getData().put(MCP_ENDPOINT_SPEC_PORT_KEY, hp[1]); endpointSpec.getData().put(Constants.MCP_BACKEND_INSTANCE_PROTOCOL_KEY, frontEndpointConfig.getProtocol()); return endpointSpec; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/McpRequestUtil.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.form.mcp.admin.McpDetailForm; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.ai.model.mcp.McpTool; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.remote.request.AbstractMcpRequest; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.fasterxml.jackson.core.type.TypeReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; /** * MCP request util. * * @author xiweng.yy */ public class McpRequestUtil { private static final Logger LOGGER = LoggerFactory.getLogger(McpRequestUtil.class); /** * Parse Mcp detail request form to {@link McpServerBasicInfo}. * * @param mcpForm mcp detail request. * @return mcp server basic info. * @throws NacosApiException if parse failed or request parameter is conflicted. */ public static McpServerBasicInfo parseMcpServerBasicInfo(McpDetailForm mcpForm) throws NacosApiException { McpServerBasicInfo result = McpRequestUtil.deserializeSpec(mcpForm.getServerSpecification(), new TypeReference<>() { }); if (StringUtils.isEmpty(result.getName())) { result.setName(mcpForm.getMcpName()); } return result; } /** * Parse Mcp tools request form to {@link McpTool}. * * @param mcpForm mcp detail request. * @return mcp server tool info * @throws NacosApiException if parse failed. */ public static McpToolSpecification parseMcpTools(McpDetailForm mcpForm) throws NacosApiException { if (StringUtils.isBlank(mcpForm.getToolSpecification())) { return null; } return McpRequestUtil.deserializeSpec(mcpForm.getToolSpecification(), new TypeReference<>() { }); } /** * Parse Mcp endpoint request form to {@link McpEndpointSpec}. * * @param basicInfo mcp server basic info * @param mcpForm mcp detail request. * @return mcp server endpoint info * @throws NacosApiException if parse failed or request parameter is conflicted. */ public static McpEndpointSpec parseMcpEndpointSpec(McpServerBasicInfo basicInfo, McpDetailForm mcpForm) throws NacosApiException { if (AiConstants.Mcp.MCP_PROTOCOL_STDIO.equalsIgnoreCase(basicInfo.getProtocol())) { return null; } if (StringUtils.isBlank(mcpForm.getEndpointSpecification())) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "request parameter `endpointSpecification` is required if mcp server type not `local`."); } return McpRequestUtil.deserializeSpec(mcpForm.getEndpointSpecification(), new TypeReference<>() { }); } /** * Deserialize spec from json request. * * @param spec spec json string. * @param typeReference the type of spec. * @param the type of spec. * @return spec object. * @throws NacosApiException if deserialize failed. */ public static T deserializeSpec(String spec, TypeReference typeReference) throws NacosApiException { return deserializeSpec(spec, typeReference, LOGGER); } /** * Deserialize spec from json request. * * @param spec spec json string. * @param typeReference the type of spec. * @param logger the logger to log error. * @param the type of spec. * @return spec object. * @throws NacosApiException if deserialize failed. */ public static T deserializeSpec(String spec, TypeReference typeReference, Logger logger) throws NacosApiException { try { return JacksonUtils.toObj(spec, typeReference); } catch (NacosDeserializationException e) { logger.error(String.format("Deserialize %s from %s failed, ", typeReference.getType().getTypeName(), spec), e); throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "serverSpecification or toolSpecification is invalid. Can't be parsed."); } } /** * Transfer input to McpServiceRef. * * @param input input object, should be McpServiceRef type or Map type. * @return McpServiceRef */ public static McpServiceRef transferToMcpServiceRef(Object input) { if (input instanceof McpServiceRef) { return (McpServiceRef) input; } if (input instanceof Map) { return JacksonUtils.toObj(JacksonUtils.toJson(input), McpServiceRef.class); } throw new IllegalArgumentException("input must be instance of McpServiceRef or Map"); } /** * If request contains valid namespaceId, do nothing. If not, fill default namespaceId. * * @param request mcp request */ public static void fillNamespaceId(AbstractMcpRequest request) { if (StringUtils.isEmpty(request.getNamespaceId())) { request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/PromptDataIdUtils.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.common.utils.StringUtils; /** * Prompt data id utility methods. * * @author nacos */ public final class PromptDataIdUtils { private PromptDataIdUtils() { } public static String buildMetaDataId(String promptKey) { return buildDescriptorDataId(promptKey); } public static String buildDescriptorDataId(String promptKey) { return promptKey + Constants.Prompt.DESCRIPTOR_DATA_ID_SUFFIX; } @Deprecated public static String buildAdminInfoDataId(String promptKey) { return buildDescriptorDataId(promptKey); } public static String buildLabelVersionMappingDataId(String promptKey) { return promptKey + Constants.Prompt.LABEL_VERSION_MAPPING_DATA_ID_SUFFIX; } public static String buildLatestDataId(String promptKey) { return promptKey + Constants.Prompt.PROMPT_DATA_ID_SUFFIX; } public static String buildVersionDataId(String promptKey, String version) { return promptKey + "." + version + Constants.Prompt.PROMPT_DATA_ID_SUFFIX; } /** * Check whether dataId is prompt meta dataId. * * @param dataId config dataId * @return true if meta dataId */ public static boolean isMetaDataId(String dataId) { return isDescriptorDataId(dataId); } public static boolean isDescriptorDataId(String dataId) { return StringUtils.isNotBlank(dataId) && dataId.endsWith(Constants.Prompt.DESCRIPTOR_DATA_ID_SUFFIX); } @Deprecated public static boolean isAdminInfoDataId(String dataId) { return isDescriptorDataId(dataId); } /** * Check whether dataId is prompt label/version mapping dataId. * * @param dataId config dataId * @return true if mapping dataId */ public static boolean isLabelVersionMappingDataId(String dataId) { return StringUtils.isNotBlank(dataId) && dataId.endsWith(Constants.Prompt.LABEL_VERSION_MAPPING_DATA_ID_SUFFIX); } /** * Extract prompt key from prompt meta dataId. * * @param dataId config dataId * @return prompt key if valid, otherwise null */ public static String extractPromptKeyFromMetaDataId(String dataId) { return extractPromptKeyFromDescriptorDataId(dataId); } /** * Extract prompt key from prompt descriptor dataId. * * @param dataId config dataId * @return prompt key if valid, otherwise null */ public static String extractPromptKeyFromDescriptorDataId(String dataId) { if (!isDescriptorDataId(dataId)) { return null; } return dataId.substring(0, dataId.length() - Constants.Prompt.DESCRIPTOR_DATA_ID_SUFFIX.length()); } @Deprecated public static String extractPromptKeyFromAdminInfoDataId(String dataId) { return extractPromptKeyFromDescriptorDataId(dataId); } /** * Extract prompt key from mapping dataId. * * @param dataId config dataId * @return prompt key if valid, otherwise null */ public static String extractPromptKeyFromLabelVersionMappingDataId(String dataId) { if (!isLabelVersionMappingDataId(dataId)) { return null; } return dataId.substring(0, dataId.length() - Constants.Prompt.LABEL_VERSION_MAPPING_DATA_ID_SUFFIX.length()); } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/PromptVersionUtils.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.regex.Pattern; /** * Utility class for prompt version validation and comparison. * *

Version format: major.minor.patch (e.g., "1.0.0", "2.1.3")

* * @author nacos */ public class PromptVersionUtils { /** * Version pattern: a.b.c where a, b, c are non-negative integers. */ private static final Pattern VERSION_PATTERN = Pattern.compile("^\\d+\\.\\d+\\.\\d+$"); /** * Number of parts in semantic version (major.minor.patch). */ private static final int VERSION_PARTS_COUNT = 3; /** * JSON file extension suffix. */ private static final String JSON_SUFFIX = ".json"; private PromptVersionUtils() { } /** * Validate version format. * * @param version version string * @return true if version is valid (a.b.c format) */ public static boolean isValidVersion(String version) { if (StringUtils.isBlank(version)) { return false; } return VERSION_PATTERN.matcher(version).matches(); } /** * Compare two versions. * * @param version1 first version * @param version2 second version * @return positive if version1 > version2, negative if version1 < version2, 0 if equal * @throws IllegalArgumentException if version format is invalid */ public static int compareVersion(String version1, String version2) { if (!isValidVersion(version1)) { throw new IllegalArgumentException("Invalid version format: " + version1); } if (!isValidVersion(version2)) { throw new IllegalArgumentException("Invalid version format: " + version2); } String[] parts1 = version1.split("\\."); String[] parts2 = version2.split("\\."); for (int i = 0; i < VERSION_PARTS_COUNT; i++) { int num1 = Integer.parseInt(parts1[i]); int num2 = Integer.parseInt(parts2[i]); if (num1 != num2) { return num1 - num2; } } return 0; } /** * Check if newVersion is greater than currentVersion. * * @param newVersion new version to publish * @param currentVersion current version (can be null or empty for first publish) * @return true if newVersion > currentVersion, or currentVersion is null/empty */ public static boolean isVersionGreater(String newVersion, String currentVersion) { if (!isValidVersion(newVersion)) { return false; } if (StringUtils.isBlank(currentVersion)) { // First publish, any valid version is allowed return true; } if (!isValidVersion(currentVersion)) { // Current version is invalid, allow override return true; } return compareVersion(newVersion, currentVersion) > 0; } /** * Build dataId from promptKey. * * @param promptKey prompt key * @return dataId (promptKey.json) */ public static String buildDataId(String promptKey) { return promptKey + JSON_SUFFIX; } /** * Extract promptKey from dataId. * * @param dataId dataId * @return promptKey */ public static String extractPromptKey(String dataId) { if (StringUtils.isBlank(dataId)) { return null; } if (dataId.endsWith(JSON_SUFFIX)) { return dataId.substring(0, dataId.length() - JSON_SUFFIX.length()); } return dataId; } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/SkillRequestUtil.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.fasterxml.jackson.core.type.TypeReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; /** * Skill request util. * * @author nacos */ public class SkillRequestUtil { private static final Logger LOGGER = LoggerFactory.getLogger(SkillRequestUtil.class); /** * Parse Skill request form to {@link Skill}. * * @param skillDetailForm skill detail form. * @return skill * @throws NacosApiException if parse failed or request parameter is conflicted. */ public static Skill parseSkill(SkillDetailForm skillDetailForm) throws NacosApiException { try { Skill result = JacksonUtils.toObj(skillDetailForm.getSkillCard(), new TypeReference<>() { }); validateSkill(result); return result; } catch (NacosDeserializationException e) { LOGGER.error(String.format("Deserialize %s from %s failed, ", Skill.class.getSimpleName(), skillDetailForm.getSkillCard()), e); throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "skillCard is invalid. Can't be parsed."); } } /** * Validate skill is legal. * * @param skill skill * @throws NacosApiException if skill is illegal. */ public static void validateSkill(Skill skill) throws NacosApiException { validateSkillField("name", skill.getName()); validateSkillField("description", skill.getDescription()); validateSkillField("instruction", skill.getInstruction()); } private static void validateSkillField(String fieldName, String fieldValue) throws NacosApiException { if (StringUtils.isEmpty(fieldValue)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `skillCard." + fieldName + "` not present"); } } /** * Validate uploaded skill zip file and extract bytes. * *

Validates the file is not null/empty, checks file size against the maximum limit, * and extracts the file bytes. This method is shared by both admin and console upload endpoints.

* * @param file the uploaded multipart file * @return the file bytes * @throws NacosException if validation fails or file reading fails */ public static byte[] validateAndExtractZipBytes(MultipartFile file) throws NacosException { if (file == null || file.isEmpty()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.DATA_EMPTY, "File is required"); } if (file.getSize() > Constants.Skills.MAX_UPLOAD_ZIP_BYTES) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Skill zip size must not exceed " + (Constants.Skills.MAX_UPLOAD_ZIP_BYTES / 1024 / 1024) + "MB, current: " + (file.getSize() / 1024 / 1024) + "MB"); } try { return file.getBytes(); } catch (IOException e) { throw new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.PARSING_DATA_FAILED, "Failed to read file: " + e.getMessage()); } } } ================================================ FILE: ai/src/main/java/com/alibaba/nacos/ai/utils/SkillZipParser.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillResource; import com.alibaba.nacos.api.ai.model.skills.SkillUtils; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream; /** * Skill zip parser utility. Supports both text and binary resources: * text files are stored as UTF-8; binary files (e.g. .ttf, .png) are stored as Base64 with metadata encoding=base64. * * @author nacos */ public class SkillZipParser { private static final Logger LOGGER = LoggerFactory.getLogger(SkillZipParser.class); private static final String SKILL_MD_FILE = "SKILL.md"; /** UTF-8 BOM character that some editors prepend to files. Must be stripped before parsing. */ private static final char UTF8_BOM = '\uFEFF'; /** macOS AppleDouble/resource fork metadata file prefix (e.g. ._LICENSE.txt). Should be excluded from skill zip. */ private static final String MACOS_METADATA_PREFIX = "._"; private static final String INSTRUCTIONS_HEADER_WITH_SPACE = "## Instructions"; private static final String INSTRUCTIONS_HEADER_NO_SPACE = "##Instructions"; private static final String DOUBLE_QUOTE = "\""; private static final String SINGLE_QUOTE = "'"; private static final String DOUBLE_SINGLE_QUOTE = "''"; private static final String BACKSLASH = "\\"; private static final String DOUBLE_BACKSLASH = "\\\\"; private static final String ESCAPED_DOUBLE_QUOTE = "\\\""; private static final String SLASH = "/"; private static final String DOT = "."; /** Metadata key for binary resources: value "base64" means content is Base64-encoded. */ private static final String METADATA_ENCODING = "encoding"; private static final String METADATA_ENCODING_BASE64 = "base64"; /** File extensions treated as binary; content will be stored as Base64. */ private static final Set BINARY_EXTENSIONS = new HashSet<>(); static { BINARY_EXTENSIONS.add("ttf"); BINARY_EXTENSIONS.add("otf"); BINARY_EXTENSIONS.add("woff"); BINARY_EXTENSIONS.add("woff2"); BINARY_EXTENSIONS.add("eot"); BINARY_EXTENSIONS.add("png"); BINARY_EXTENSIONS.add("jpg"); BINARY_EXTENSIONS.add("jpeg"); BINARY_EXTENSIONS.add("gif"); BINARY_EXTENSIONS.add("webp"); BINARY_EXTENSIONS.add("ico"); BINARY_EXTENSIONS.add("cur"); BINARY_EXTENSIONS.add("pdf"); BINARY_EXTENSIONS.add("bin"); } private static final Pattern YAML_FRONT_MATTER = Pattern.compile( "^---\\s*\\n(.*?)\\n---\\s*\\n(.*)$", Pattern.DOTALL); /** * Parse skill from zip file bytes. Zip size must not exceed {@link Constants.Skills#MAX_UPLOAD_ZIP_BYTES}. * Text files are decoded as UTF-8; binary files (by extension) are stored as Base64 with metadata encoding=base64. * * @param zipBytes zip file bytes * @param namespaceId namespace ID * @return parsed skill * @throws NacosApiException if parsing failed or zip exceeds size limit */ public static Skill parseSkillFromZip(byte[] zipBytes, String namespaceId) throws NacosApiException { if (zipBytes == null || zipBytes.length == 0) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Skill zip file is empty"); } if (zipBytes.length > Constants.Skills.MAX_UPLOAD_ZIP_BYTES) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Skill zip size must not exceed " + (Constants.Skills.MAX_UPLOAD_ZIP_BYTES / 1024 / 1024) + "MB, current: " + (zipBytes.length / 1024 / 1024) + "MB"); } try { List entries = unzipToEntries(zipBytes); String skillMdContent = null; for (ZipEntryData entry : entries) { String name = entry.name; if (isMacOsMetadataFile(name)) { continue; } boolean isSkillMdFile = SKILL_MD_FILE.equals(name); boolean isSkillMdInSubdir = name.endsWith(SLASH + SKILL_MD_FILE); boolean endsWithSkillMd = name.endsWith(SKILL_MD_FILE); boolean isSkillMd = isSkillMdFile || isSkillMdInSubdir; if (endsWithSkillMd && isSkillMd) { skillMdContent = stripBom(new String(entry.data, StandardCharsets.UTF_8)); break; } } if (StringUtils.isBlank(skillMdContent)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "SKILL.md file not found in zip"); } Skill skill = parseSkillMarkdown(skillMdContent, namespaceId); Map resources = parseResources(entries, skill.getName()); skill.setResource(resources); return skill; } catch (NacosApiException e) { throw e; } catch (Exception e) { LOGGER.error("Failed to parse skill zip file", e); throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARSING_DATA_FAILED, "Failed to parse zip file: " + e.getMessage()); } } /** * Unzip to list of (name, raw bytes). Does not decode as text so binary files are preserved. * Uses Apache Commons Compress to support zip files with STORED entries that have data descriptor * (e.g. created on macOS or by some tools), which JDK ZipInputStream rejects. */ private static List unzipToEntries(byte[] zipBytes) throws IOException { List result = new ArrayList<>(); try (ZipArchiveInputStream zis = new ZipArchiveInputStream(new ByteArrayInputStream(zipBytes), StandardCharsets.UTF_8.name(), true, true)) { ZipArchiveEntry entry; byte[] buffer = new byte[8192]; while ((entry = zis.getNextEntry()) != null) { if (entry.isDirectory()) { continue; } String name = entry.getName(); boolean isMacOsxEntry = name != null && (name.contains("__MACOSX") || name.contains("/__MACOSX/")); if (isMacOsxEntry) { continue; } ByteArrayOutputStream out = new ByteArrayOutputStream(); int n; while ((n = zis.read(buffer)) != -1) { out.write(buffer, 0, n); } result.add(new ZipEntryData(name, out.toByteArray())); } } return result; } /** * Parse resources from zip entries. Text files use UTF-8 content; binary (by extension) use Base64 content and metadata encoding=base64. */ private static Map parseResources(List entries, String skillName) { Map resources = new HashMap<>(16); for (ZipEntryData entry : entries) { String itemName = entry.name; if (isMacOsMetadataFile(itemName)) { continue; } if (itemName.endsWith(SKILL_MD_FILE) || itemName.endsWith("/")) { continue; } String[] parts = itemName.split("/"); String type; String resourceName; if (parts.length == 2 && parts[0].equals(skillName)) { type = ""; resourceName = parts[1]; } else if (parts.length >= 3 && parts[0].equals(skillName)) { // Preserve full path as type so multi-level folders (e.g. folder1/folder2) are kept StringBuilder typeSb = new StringBuilder(); for (int i = 1; i < parts.length - 1; i++) { if (typeSb.length() > 0) { typeSb.append('/'); } typeSb.append(parts[i]); } type = typeSb.toString(); resourceName = parts[parts.length - 1]; } else if (parts.length >= 2) { StringBuilder typeSb = new StringBuilder(); for (int i = 0; i < parts.length - 1; i++) { if (typeSb.length() > 0) { typeSb.append('/'); } typeSb.append(parts[i]); } type = typeSb.toString(); resourceName = parts[parts.length - 1]; } else { continue; } boolean isBinary = isBinaryResource(resourceName); String content; Map metadata = new HashMap<>(4); if (isBinary) { content = Base64.getEncoder().encodeToString(entry.data); metadata.put(METADATA_ENCODING, METADATA_ENCODING_BASE64); } else { content = new String(entry.data, StandardCharsets.UTF_8); } SkillResource resource = new SkillResource(); resource.setName(resourceName); resource.setType(type); resource.setContent(content); resource.setMetadata(metadata.isEmpty() ? null : metadata); // Use same key as getSkillDetail so resource map is consistent when skill is read back String key = SkillUtils.generateResourceId(type, resourceName); resources.put(key, resource); } return resources; } private static boolean isBinaryResource(String fileName) { if (StringUtils.isBlank(fileName) || !fileName.contains(DOT)) { return false; } String ext = fileName.substring(fileName.lastIndexOf(DOT.charAt(0)) + 1).trim().toLowerCase(); return BINARY_EXTENSIONS.contains(ext); } private static final class ZipEntryData { final String name; final byte[] data; ZipEntryData(String name, byte[] data) { this.name = name; this.data = data; } } /** * Parse skill from SKILL.md markdown content. */ private static Skill parseSkillMarkdown(String markdownContent, String namespaceId) throws NacosApiException { Matcher matcher = YAML_FRONT_MATTER.matcher(markdownContent); if (!matcher.matches()) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "SKILL.md must contain YAML front matter (---)"); } String yamlContent = matcher.group(1); String instructionContent = matcher.group(2); Map yamlMap = parseYamlFrontMatter(yamlContent); String name = yamlMap.get("name"); String description = yamlMap.get("description"); if (StringUtils.isBlank(name)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Skill name is required in YAML front matter"); } if (StringUtils.isBlank(description)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Skill description is required in YAML front matter"); } String instruction = extractInstruction(instructionContent); if (StringUtils.isBlank(instruction)) { throw new NacosApiException(NacosApiException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Skill instruction is required"); } Skill skill = new Skill(); skill.setNamespaceId(namespaceId); skill.setName(name.trim()); skill.setDescription(description.trim()); skill.setInstruction(instruction.trim()); return skill; } private static Map parseYamlFrontMatter(String yamlContent) { Map result = new HashMap<>(4); String[] lines = yamlContent.split("\\n"); for (String line : lines) { line = line.trim(); if (line.isEmpty() || line.startsWith("#")) { continue; } int colonIndex = line.indexOf(':'); if (colonIndex > 0) { String key = line.substring(0, colonIndex).trim(); String value = line.substring(colonIndex + 1).trim(); boolean hasDoubleQuotes = value.startsWith(DOUBLE_QUOTE) && value.endsWith(DOUBLE_QUOTE); boolean hasSingleQuotes = value.startsWith(SINGLE_QUOTE) && value.endsWith(SINGLE_QUOTE); if (hasDoubleQuotes) { value = value.substring(1, value.length() - 1); value = unescapeDoubleQuotedYamlValue(value); } else if (hasSingleQuotes) { value = value.substring(1, value.length() - 1); value = value.replace(DOUBLE_SINGLE_QUOTE, SINGLE_QUOTE); } result.put(key, value); } } return result; } /** * Minimal unescape for double-quoted YAML scalar values. * Only revert the escape sequences that are emitted by SKILL.md exporters: * - \\\\ -> \ * - \\\" -> " */ private static String unescapeDoubleQuotedYamlValue(String value) { if (StringUtils.isBlank(value)) { return value; } return value.replace(DOUBLE_BACKSLASH, BACKSLASH).replace(ESCAPED_DOUBLE_QUOTE, DOUBLE_QUOTE); } private static String extractInstruction(String markdownContent) { String content = markdownContent.trim(); boolean hasHeaderWithSpace = content.startsWith(INSTRUCTIONS_HEADER_WITH_SPACE); boolean hasHeaderNoSpace = content.startsWith(INSTRUCTIONS_HEADER_NO_SPACE); if (hasHeaderWithSpace || hasHeaderNoSpace) { int headerEnd = content.indexOf('\n'); if (headerEnd > 0) { content = content.substring(headerEnd).trim(); } else { content = content.replaceFirst("##\\s*Instructions\\s*", ""); } } return content.trim(); } private static boolean isMacOsMetadataFile(String itemName) { if (StringUtils.isBlank(itemName)) { return false; } int lastSlash = itemName.lastIndexOf('/'); String fileName = lastSlash >= 0 ? itemName.substring(lastSlash + 1) : itemName; return fileName.startsWith(MACOS_METADATA_PREFIX); } /** * Strip UTF-8 BOM character from the beginning of a string if present. * * @param content the string to strip BOM from * @return the string without leading BOM */ private static String stripBom(String content) { if (content != null && !content.isEmpty() && content.charAt(0) == UTF8_BOM) { return content.substring(1); } return content; } } ================================================ FILE: ai/src/main/resources/META-INF/services/com.alibaba.nacos.ai.service.McpServerOperationService ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.ai.service.McpServerOperationService ================================================ FILE: ai/src/main/resources/META-INF/services/com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.ai.param.McpHttpParamExtractor com.alibaba.nacos.ai.param.AgentHttpParamExtractor ================================================ FILE: ai/src/main/resources/META-INF/services/com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter ================================================ # # Copyright 1999-2025 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.ai.config.AiEnabledFilter ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/config/AiEnabledFilterTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.mock.env.MockEnvironment; import org.springframework.test.util.ReflectionTestUtils; 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 AiEnabledFilterTest { AiEnabledFilter aiEnabledFilter; MockEnvironment environment; @BeforeEach void setUp() { environment = new MockEnvironment(); EnvUtil.setEnvironment(environment); aiEnabledFilter = new AiEnabledFilter(); } @AfterEach void tearDown() { EnvUtil.setEnvironment(null); ReflectionTestUtils.setField(EnvUtil.class, "functionModeType", ""); } @Test void getResponsiblePackagePrefix() { assertEquals("com.alibaba.nacos.ai", aiEnabledFilter.getResponsiblePackagePrefix()); } @Test void isExcludedOnlyNamingFunction() { ReflectionTestUtils.setField(EnvUtil.class, "functionModeType", "naming"); assertTrue(aiEnabledFilter.isExcluded("com.alibaba.nacos.ai.config.AiEnabledFilter", null)); } @Test void isExcludedOnlyConfigFunction() { ReflectionTestUtils.setField(EnvUtil.class, "functionModeType", "config"); assertTrue(aiEnabledFilter.isExcluded("com.alibaba.nacos.ai.config.AiEnabledFilter", null)); } @Test void isExcludedDisabled() { environment.setProperty("nacos.extension.ai.enabled", "false"); assertTrue(aiEnabledFilter.isExcluded("com.alibaba.nacos.ai.config.AiEnabledFilter", null)); } @Test void isExcludedEnabled() { environment.setProperty("nacos.extension.ai.enabled", "true"); assertFalse(aiEnabledFilter.isExcluded("com.alibaba.nacos.ai.config.AiEnabledFilter", null)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/config/McpCacheIndexPropertiesTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class McpCacheIndexPropertiesTest { @Test public void testToString() { McpCacheIndexProperties properties = new McpCacheIndexProperties(); // Test default value. assertEquals( "McpCacheIndexProperties{enabled=true, maxSize=10000, expireTimeSeconds=3600, cleanupIntervalSeconds=300, syncIntervalSeconds=300}", properties.toString()); properties.setEnabled(false); properties.setCleanupIntervalSeconds(10); properties.setSyncIntervalSeconds(10); properties.setExpireTimeSeconds(10); properties.setMaxSize(100); assertEquals( "McpCacheIndexProperties{enabled=false, maxSize=100, expireTimeSeconds=10, cleanupIntervalSeconds=10, syncIntervalSeconds=10}", properties.toString()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/config/McpConfigurationTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import com.alibaba.nacos.core.code.ControllerMethodsCache; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class McpConfigurationTest { @Mock ControllerMethodsCache methodsCache; @Test void testInit() { McpConfiguration mcpConfiguration = new McpConfiguration(methodsCache); mcpConfiguration.init(); verify(methodsCache).initClassMethod("com.alibaba.nacos.ai.controller"); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/config/McpServerIndexConfigurationTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.config; import com.alibaba.nacos.ai.index.CachedMcpServerIndex; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.index.PlainMcpServerIndex; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.core.service.NamespaceOperationService; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; 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.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; /** * Unit tests for McpServerIndexConfiguration. */ class McpServerIndexConfigurationTest { @Configuration static class TestConfig { @Bean public ConfigDetailService configDetailService() { return mock(ConfigDetailService.class); } @Bean public NamespaceOperationService namespaceOperationService() { return mock(NamespaceOperationService.class); } @Bean public ConfigQueryChainService configQueryChainService() { return mock(ConfigQueryChainService.class); } } @Nested @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {McpServerIndexConfiguration.class, TestConfig.class}) @TestPropertySource(properties = {"nacos.mcp.cache.enabled=true"}) class CacheEnabled { @org.springframework.beans.factory.annotation.Autowired(required = false) private McpServerIndex mcpServerIndex; @Test void shouldInjectCachedMcpServerIndexWhenCacheEnabled() { assertNotNull(mcpServerIndex, "McpServerIndex should be injected"); assertInstanceOf(CachedMcpServerIndex.class, mcpServerIndex, "Should be CachedMcpServerIndex when cache enabled"); } } @Nested @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {McpServerIndexConfiguration.class, TestConfig.class}) @TestPropertySource(properties = {"nacos.mcp.cache.enabled=false"}) class CacheDisabled { @org.springframework.beans.factory.annotation.Autowired(required = false) private McpServerIndex mcpServerIndex; @Test void shouldInjectPlainMcpServerIndexWhenCacheDisabled() { assertNotNull(mcpServerIndex, "McpServerIndex should be injected"); assertInstanceOf(PlainMcpServerIndex.class, mcpServerIndex, "Should be PlainMcpServerIndex when cache disabled"); } } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/controller/A2aAdminControllerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.ai.service.a2a.A2aServerOperationService; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.core.model.form.PageForm; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.ai.constant.Constants.MCP_LIST_SEARCH_BLUR; 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.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Unit tests for A2aAdminController. * * @author nacos */ @ExtendWith(MockitoExtension.class) class A2aAdminControllerTest { @Mock private A2aServerOperationService a2aServerOperationService; @InjectMocks private A2aAdminController a2aAdminController; private AgentCardForm agentCardForm; private AgentForm agentForm; private AgentCardUpdateForm agentCardUpdateForm; private AgentListForm agentListForm; private PageForm pageForm; @BeforeEach void setUp() { agentCardForm = new AgentCardForm(); agentCardForm.setAgentName("test-agent"); agentCardForm.setNamespaceId("public"); agentCardForm.setVersion("1.0.0"); agentCardForm.setRegistrationType(AiConstants.A2a.A2A_ENDPOINT_TYPE_URL); agentCardForm.setAgentCard( "{\"name\":\"test-agent\",\"version\":\"1.0.0\",\"protocolVersion\":\"1.0\",\"preferredTransport\":\"JSONRPC\",\"description\":\"Test agent description\",\"url\":\"http://test-agent.example.com\"}"); agentForm = new AgentForm(); agentForm.setAgentName("test-agent"); agentForm.setNamespaceId("public"); agentForm.setVersion("1.0.0"); agentForm.setRegistrationType(AiConstants.A2a.A2A_ENDPOINT_TYPE_URL); agentCardUpdateForm = new AgentCardUpdateForm(); agentCardUpdateForm.setAgentName("test-agent"); agentCardUpdateForm.setNamespaceId("public"); agentCardUpdateForm.setVersion("1.0.0"); agentCardUpdateForm.setSetAsLatest(true); agentCardUpdateForm.setRegistrationType(AiConstants.A2a.A2A_ENDPOINT_TYPE_URL); agentCardUpdateForm.setAgentCard( "{\"name\":\"test-agent\",\"version\":\"1.0.0\",\"protocolVersion\":\"1.0\",\"preferredTransport\":\"JSONRPC\",\"description\":\"Updated description\",\"url\":\"http://test-agent.example.com\"}"); agentListForm = new AgentListForm(); agentListForm.setAgentName("test-agent"); agentListForm.setNamespaceId("public"); agentListForm.setSearch(MCP_LIST_SEARCH_BLUR); pageForm = new PageForm(); pageForm.setPageNo(1); pageForm.setPageSize(10); } @Test void testRegisterAgentSuccess() throws NacosException { // Arrange doNothing().when(a2aServerOperationService).registerAgent(any(AgentCard.class), anyString(), anyString()); // Act Result result = a2aAdminController.registerAgent(agentCardForm); // Assert assertNotNull(result); assertEquals("ok", result.getData()); verify(a2aServerOperationService).registerAgent(any(AgentCard.class), anyString(), anyString()); } @Test void testRegisterAgentValidationFailure() throws NacosException { // Arrange AgentCardForm invalidForm = new AgentCardForm(); // Missing required name field // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.registerAgent(invalidForm)); verify(a2aServerOperationService, never()).registerAgent(any(AgentCard.class), anyString(), anyString()); } @Test void testRegisterAgentServiceException() throws NacosException { // Arrange NacosException exception = new NacosException(NacosException.SERVER_ERROR, "Registration failed"); doThrow(exception).when(a2aServerOperationService) .registerAgent(any(AgentCard.class), anyString(), anyString()); // Act & Assert assertThrows(NacosException.class, () -> a2aAdminController.registerAgent(agentCardForm)); verify(a2aServerOperationService).registerAgent(any(AgentCard.class), anyString(), anyString()); } @Test void testGetAgentCardSuccess() throws NacosApiException { // Arrange AgentCardDetailInfo expectedAgentCard = new AgentCardDetailInfo(); expectedAgentCard.setName("test-agent"); expectedAgentCard.setVersion("1.0.0"); expectedAgentCard.setProtocolVersion("1.0"); expectedAgentCard.setPreferredTransport("JSONRPC"); expectedAgentCard.setDescription("Test agent description"); when(a2aServerOperationService.getAgentCard(anyString(), anyString(), anyString(), anyString())).thenReturn( expectedAgentCard); // Act Result result = a2aAdminController.getAgentCard(agentForm); // Assert assertNotNull(result); assertEquals(expectedAgentCard, result.getData()); verify(a2aServerOperationService).getAgentCard(anyString(), anyString(), anyString(), anyString()); } @Test void testGetAgentCardValidationFailure() throws NacosApiException { // Arrange AgentForm invalidForm = new AgentForm(); // Missing required fields // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.getAgentCard(invalidForm)); verify(a2aServerOperationService, never()).getAgentCard(anyString(), anyString(), anyString(), anyString()); } @Test void testGetAgentCardServiceException() throws NacosApiException { // Arrange NacosApiException exception = new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.RESOURCE_NOT_FOUND, "Agent not found"); when(a2aServerOperationService.getAgentCard(anyString(), anyString(), anyString(), anyString())).thenThrow( exception); // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.getAgentCard(agentForm)); verify(a2aServerOperationService).getAgentCard(anyString(), anyString(), anyString(), anyString()); } @Test void testUpdateAgentCardSuccess() throws NacosException { // Arrange doNothing().when(a2aServerOperationService) .updateAgentCard(any(AgentCard.class), anyString(), anyString(), anyBoolean()); // Act Result result = a2aAdminController.updateAgentCard(agentCardUpdateForm); // Assert assertNotNull(result); assertEquals("ok", result.getData()); verify(a2aServerOperationService).updateAgentCard(any(AgentCard.class), anyString(), anyString(), anyBoolean()); } @Test void testUpdateAgentCardValidationFailure() throws NacosException { // Arrange AgentCardUpdateForm invalidForm = new AgentCardUpdateForm(); // Missing required name field // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.updateAgentCard(invalidForm)); verify(a2aServerOperationService, never()).updateAgentCard(any(AgentCard.class), anyString(), anyString(), anyBoolean()); } @Test void testUpdateAgentCardServiceException() throws NacosException { // Arrange NacosException exception = new NacosException(NacosException.SERVER_ERROR, "Update failed"); doThrow(exception).when(a2aServerOperationService) .updateAgentCard(any(AgentCard.class), anyString(), anyString(), anyBoolean()); // Act & Assert assertThrows(NacosException.class, () -> a2aAdminController.updateAgentCard(agentCardUpdateForm)); verify(a2aServerOperationService).updateAgentCard(any(AgentCard.class), anyString(), anyString(), anyBoolean()); } @Test void testDeleteAgentSuccess() throws NacosException { // Arrange doNothing().when(a2aServerOperationService).deleteAgent(anyString(), anyString(), anyString()); // Act Result result = a2aAdminController.deleteAgent(agentForm); // Assert assertNotNull(result); assertEquals("ok", result.getData()); verify(a2aServerOperationService).deleteAgent(anyString(), anyString(), anyString()); } @Test void testDeleteAgentValidationFailure() throws NacosException { // Arrange AgentForm invalidForm = new AgentForm(); // Missing required fields // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.deleteAgent(invalidForm)); verify(a2aServerOperationService, never()).deleteAgent(anyString(), anyString(), anyString()); } @Test void testDeleteAgentServiceException() throws NacosException { // Arrange NacosException exception = new NacosException(NacosException.SERVER_ERROR, "Delete failed"); doThrow(exception).when(a2aServerOperationService).deleteAgent(anyString(), anyString(), anyString()); // Act & Assert assertThrows(NacosException.class, () -> a2aAdminController.deleteAgent(agentForm)); verify(a2aServerOperationService).deleteAgent(anyString(), anyString(), anyString()); } @Test void testListAgentsSuccess() throws NacosException { // Arrange AgentCardVersionInfo agent1 = new AgentCardVersionInfo(); agent1.setName("agent1"); agent1.setLatestPublishedVersion("1.0.0"); AgentCardVersionInfo agent2 = new AgentCardVersionInfo(); agent2.setName("agent2"); agent2.setLatestPublishedVersion("2.0.0"); List agentList = Arrays.asList(agent1, agent2); Page expectedPage = new Page<>(); expectedPage.setPageItems(agentList); expectedPage.setTotalCount(2); expectedPage.setPageNumber(1); expectedPage.setPagesAvailable(1); when(a2aServerOperationService.listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt())).thenReturn(expectedPage); // Act Result> result = a2aAdminController.listAgents(agentListForm, pageForm); // Assert assertNotNull(result); assertEquals(expectedPage, result.getData()); assertEquals(2, result.getData().getTotalCount()); assertEquals(2, result.getData().getPageItems().size()); verify(a2aServerOperationService).listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt()); } @Test void testListAgentsAgentListFormValidationFailure() throws NacosException { // Arrange AgentListForm invalidForm = new AgentListForm(); // Missing required fields PageForm validPageForm = new PageForm(); validPageForm.setPageNo(1); validPageForm.setPageSize(10); // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.listAgents(invalidForm, validPageForm)); verify(a2aServerOperationService, never()).listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt()); } @Test void testListAgentsPageFormValidationFailure() throws NacosException { // Arrange AgentListForm validAgentListForm = new AgentListForm(); validAgentListForm.setAgentName("test-agent"); validAgentListForm.setNamespaceId("public"); validAgentListForm.setSearch(MCP_LIST_SEARCH_BLUR); PageForm invalidPageForm = new PageForm(); invalidPageForm.setPageNo(0); // Invalid page number // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.listAgents(validAgentListForm, invalidPageForm)); verify(a2aServerOperationService, never()).listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt()); } @Test void testListAgentsServiceException() throws NacosException { // Arrange NacosException exception = new NacosException(NacosException.SERVER_ERROR, "List failed"); when(a2aServerOperationService.listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt())).thenThrow( exception); // Act & Assert assertThrows(NacosException.class, () -> a2aAdminController.listAgents(agentListForm, pageForm)); verify(a2aServerOperationService).listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt()); } @Test void testListAgentVersionsSuccess() throws NacosException { // Arrange AgentVersionDetail version1 = new AgentVersionDetail(); version1.setVersion("1.0.0"); version1.setLatest(true); AgentVersionDetail version2 = new AgentVersionDetail(); version2.setVersion("2.0.0"); version2.setLatest(false); List versionList = Arrays.asList(version1, version2); when(a2aServerOperationService.listAgentVersions(anyString(), anyString())).thenReturn(versionList); // Act Result> result = a2aAdminController.listAgentVersions(agentForm); // Assert assertNotNull(result); assertEquals(versionList, result.getData()); assertEquals(2, result.getData().size()); verify(a2aServerOperationService).listAgentVersions(anyString(), anyString()); } @Test void testListAgentVersionsValidationFailure() throws NacosApiException { // Arrange AgentForm invalidForm = new AgentForm(); // Missing required fields // Act & Assert assertThrows(NacosApiException.class, () -> a2aAdminController.listAgentVersions(invalidForm)); verify(a2aServerOperationService, never()).listAgentVersions(anyString(), anyString()); } @Test void testListAgentVersionsEmptyResult() throws NacosException { // Arrange List emptyList = Arrays.asList(); when(a2aServerOperationService.listAgentVersions(anyString(), anyString())).thenReturn(emptyList); // Act Result> result = a2aAdminController.listAgentVersions(agentForm); // Assert assertNotNull(result); assertEquals(emptyList, result.getData()); assertEquals(0, result.getData().size()); verify(a2aServerOperationService).listAgentVersions(anyString(), anyString()); } @Test void testListAgentsEmptyResult() throws NacosException { // Arrange List emptyList = Arrays.asList(); Page emptyPage = new Page<>(); emptyPage.setPageItems(emptyList); emptyPage.setTotalCount(0); emptyPage.setPageNumber(1); emptyPage.setPagesAvailable(1); when(a2aServerOperationService.listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt())).thenReturn(emptyPage); // Act Result> result = a2aAdminController.listAgents(agentListForm, pageForm); // Assert assertNotNull(result); assertEquals(emptyPage, result.getData()); assertEquals(0, result.getData().getTotalCount()); assertEquals(0, result.getData().getPageItems().size()); verify(a2aServerOperationService).listAgents(anyString(), anyString(), anyString(), anyInt(), anyInt()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/controller/McpAdminControllerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.sys.env.EnvUtil; import com.fasterxml.jackson.core.type.TypeReference; import jakarta.servlet.ServletException; 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.junit.jupiter.api.function.Executable; import org.mockito.Mock; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration class McpAdminControllerTest { private static final String MCP_SERVER_SPEC = "{\"protocol\":\"stdio\",\"frontProtocol\":\"stdio\",\"name\":\"nacos-mcp-server\"," + "\"id\":\"\",\"description\":\"nacos local mcp server(test version)\",\"versionDetail\":{\"version\":\"1.0.0\"}," + "\"enabled\":true,\"localServerConfig\":{}}'"; private McpAdminController mcpAdminController; private MockMvc mockMvc; private ConfigurableEnvironment cachedEnvironment; @Mock private McpServerOperationService mcpServerOperationService; @BeforeEach void setUp() { cachedEnvironment = EnvUtil.getEnvironment(); EnvUtil.setEnvironment(new StandardEnvironment()); mcpAdminController = new McpAdminController(mcpServerOperationService); mockMvc = MockMvcBuilders.standaloneSetup(mcpAdminController).build(); } @AfterEach void tearDown() { EnvUtil.setEnvironment(cachedEnvironment); } @Test void listMcpServersWithIllegalSearch() throws Throwable { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH + "/list") .param("search", "illegal"); assertServletException(NacosApiException.class, () -> mockMvc.perform(builder).andReturn(), "ErrCode:400, ErrMsg:Request parameter `search` should be `accurate` or `blur`."); } @Test void listMcpServersWithIllegalPage() throws Throwable { final MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH + "/list") .param("search", "blur").param("pageNo", "-1"); assertServletException(NacosApiException.class, () -> mockMvc.perform(builder).andReturn(), "ErrCode:400, ErrMsg:Required parameter 'pageNo' should be positive integer, current is -1"); final MockHttpServletRequestBuilder builder2 = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH + "/list") .param("search", "blur").param("pageNo", "1").param("pageSize", "0"); assertServletException(NacosApiException.class, () -> mockMvc.perform(builder2).andReturn(), "ErrCode:400, ErrMsg:Required parameter 'pageSize' should be positive integer, current is 0"); } @Test void listMcpServersSuccess() throws Throwable { when(mcpServerOperationService.listMcpServerWithPage(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_ACCURATE, 1, 100)).thenReturn(new Page<>()); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH + "/list") .param("pageNo", "1").param("pageSize", "100"); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result> result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertInstanceOf(Page.class, result.getData()); } @Test void getMcpServerWithoutMcpIdAndMcpName() throws Throwable { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH); assertServletException(NacosApiException.class, () -> mockMvc.perform(builder).andReturn(), "ErrCode:400, ErrMsg:Required parameter 'mcpId' or 'mcpName' type String at lease one is not present"); } @Test void getMcpServerWithMcpName() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH) .param("mcpName", "testName"); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "testName", null)).thenReturn(new McpServerDetailInfo()); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertInstanceOf(McpServerDetailInfo.class, result.getData()); } @Test void getMcpServerWithMcpId() throws Exception { String id = UUID.randomUUID().toString(); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH).param("mcpId", id); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null)).thenReturn(new McpServerDetailInfo()); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertInstanceOf(McpServerDetailInfo.class, result.getData()); } @Test void getMcpServerWithVersion() throws Exception { String id = UUID.randomUUID().toString(); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.MCP_ADMIN_PATH).param("mcpId", id) .param("namespaceId", "testNs").param("version", "1.0.0"); when(mcpServerOperationService.getMcpServerDetail("testNs", id, null, "1.0.0")).thenReturn( new McpServerDetailInfo()); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertInstanceOf(McpServerDetailInfo.class, result.getData()); } @Test void createMcpServerWithoutSpec() throws Throwable { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(Constants.MCP_ADMIN_PATH); assertServletException(NacosApiException.class, () -> mockMvc.perform(builder).andReturn(), "ErrCode:400, ErrMsg:Required parameter 'serverSpecification' type McpServerBasicInfo is not present"); } @Test void createMcpServerWithSpec() throws Exception { String mcpId = UUID.randomUUID().toString(); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(Constants.MCP_ADMIN_PATH) .param("serverSpecification", MCP_SERVER_SPEC); when(mcpServerOperationService.createMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), any(McpServerBasicInfo.class), any(), any())).thenReturn(mcpId); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals(mcpId, result.getData()); verify(mcpServerOperationService).createMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), any(McpServerBasicInfo.class), isNull(), isNull()); } @Test void updateMcpServerWithoutSpec() throws Throwable { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(Constants.MCP_ADMIN_PATH); assertServletException(NacosApiException.class, () -> mockMvc.perform(builder).andReturn(), "ErrCode:400, ErrMsg:Required parameter 'serverSpecification' type McpServerBasicInfo is not present"); } @Test void updateMcpServerWithSpec() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(Constants.MCP_ADMIN_PATH) .param("serverSpecification", MCP_SERVER_SPEC); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals("ok", result.getData()); verify(mcpServerOperationService).updateMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(true), any(McpServerBasicInfo.class), isNull(), isNull(), eq(false)); } @Test void updateMcpServerWithOverrideExisting() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(Constants.MCP_ADMIN_PATH) .param("serverSpecification", MCP_SERVER_SPEC).param("overrideExisting", "true"); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals("ok", result.getData()); verify(mcpServerOperationService).updateMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(true), any(McpServerBasicInfo.class), isNull(), isNull(), eq(true)); } @Test void updateMcpServerWithoutLatest() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(Constants.MCP_ADMIN_PATH) .param("serverSpecification", MCP_SERVER_SPEC).param("latest", "false"); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals("ok", result.getData()); verify(mcpServerOperationService).updateMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(false), any(McpServerBasicInfo.class), isNull(), isNull(), eq(false)); } @Test void deleteMcpServerWithoutMcpIdAndMcpName() throws Throwable { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.MCP_ADMIN_PATH); assertServletException(NacosApiException.class, () -> mockMvc.perform(builder).andReturn(), "ErrCode:400, ErrMsg:Required parameter 'mcpId' or 'mcpName' type String at lease one is not present"); } @Test void deleteMcpServerWithMcpName() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.MCP_ADMIN_PATH) .param("mcpName", "testName"); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals("ok", result.getData()); verify(mcpServerOperationService).deleteMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "testName", null, null); } @Test void deleteMcpServerWithMcpId() throws Exception { String id = UUID.randomUUID().toString(); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.MCP_ADMIN_PATH) .param("mcpId", id); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals("ok", result.getData()); verify(mcpServerOperationService).deleteMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, id, null); } @Test void deleteMcpServerWithVersion() throws Exception { String id = UUID.randomUUID().toString(); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.MCP_ADMIN_PATH) .param("mcpId", id).param("namespaceId", "testNs").param("version", "1.0.0"); MockHttpServletResponse response = mockMvc.perform(builder).andReturn().getResponse(); assertEquals(200, response.getStatus()); Result result = JacksonUtils.toObj(response.getContentAsString(), new TypeReference<>() { }); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals("ok", result.getData()); verify(mcpServerOperationService).deleteMcpServer("testNs", null, id, "1.0.0"); } private static void assertServletException(Class expectedCause, Executable executable, String expectedMsg) throws Throwable { try { executable.execute(); } catch (ServletException e) { Throwable caused = e.getCause(); assertInstanceOf(expectedCause, caused); assertEquals(expectedMsg, caused.toString()); } } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/controller/PromptClientControllerTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.controller; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.ai.service.prompt.PromptClientOperationService; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import jakarta.servlet.http.HttpServletResponse; 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.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockHttpServletResponse; 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.assertThrows; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class PromptClientControllerTest { @Mock private PromptClientOperationService promptClientOperationService; private PromptClientController controller; @BeforeEach void setUp() { controller = new PromptClientController(promptClientOperationService); } @Test void queryPromptShouldReturn304AndNullWhenNotModified() throws NacosException { PromptQueryForm form = new PromptQueryForm(); form.setPromptKey("p1"); HttpServletResponse response = new MockHttpServletResponse(); when(promptClientOperationService.queryPrompt("public", "p1", null, null, null)) .thenThrow(new NacosException(NacosException.NOT_MODIFIED, "up to date")); Result result = controller.queryPrompt(form, response); assertEquals(304, response.getStatus()); assertNull(result.getData()); } @Test void queryPromptShouldReturnPromptWhenSuccess() throws NacosException { PromptQueryForm form = new PromptQueryForm(); form.setPromptKey("p1"); MockHttpServletResponse response = new MockHttpServletResponse(); PromptVersionInfo versionInfo = new PromptVersionInfo(); versionInfo.setPromptKey("p1"); versionInfo.setVersion("1.0.0"); versionInfo.setTemplate("template"); versionInfo.setMd5("md5"); when(promptClientOperationService.queryPrompt("public", "p1", null, null, null)).thenReturn(versionInfo); Result result = controller.queryPrompt(form, response); assertNotNull(result.getData()); assertEquals("p1", result.getData().getPromptKey()); assertEquals("1.0.0", result.getData().getVersion()); assertEquals("template", result.getData().getTemplate()); } @Test void queryPromptShouldRethrowWhenNon304Exception() throws NacosException { PromptQueryForm form = new PromptQueryForm(); form.setPromptKey("p1"); HttpServletResponse response = new MockHttpServletResponse(); when(promptClientOperationService.queryPrompt("public", "p1", null, null, null)) .thenThrow(new NacosException(NacosException.NOT_FOUND, "not found")); assertThrows(NacosException.class, () -> controller.queryPrompt(form, response)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/a2a/admin/AgentCardFormTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.a2a.admin; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class AgentCardFormTest { @Test void testValidateSuccess() throws NacosApiException { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setAgentName("test-agent"); agentCardForm.setAgentCard("{\"name\":\"test-agent\"}"); agentCardForm.validate(); // Should not throw exception } @Test void testValidateWithEmptyAgentCardShouldThrowException() { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setAgentName("test-agent"); agentCardForm.setAgentCard(""); assertThrows(NacosApiException.class, agentCardForm::validate); } @Test void testValidateWithNullAgentCardShouldThrowException() { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setAgentName("test-agent"); agentCardForm.setAgentCard(null); assertThrows(NacosApiException.class, agentCardForm::validate); } @Test void testValidateShouldFillDefaultNamespaceIdAndRegistrationType() throws NacosApiException { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setAgentName("test-agent"); agentCardForm.setAgentCard("{\"name\":\"test-agent\"}"); agentCardForm.validate(); assertEquals("public", agentCardForm.getNamespaceId()); assertEquals("URL", agentCardForm.getRegistrationType()); } @Test void testValidateWithValidRegistrationType() throws NacosApiException { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setAgentName("test-agent"); agentCardForm.setAgentCard("{\"name\":\"test-agent\"}"); agentCardForm.setRegistrationType("URL"); agentCardForm.validate(); // Should not throw exception agentCardForm.setRegistrationType("SERVICE"); agentCardForm.validate(); // Should not throw exception } @Test void testValidateWithInvalidRegistrationTypeShouldThrowException() { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setAgentName("test-agent"); agentCardForm.setAgentCard("{\"name\":\"test-agent\"}"); agentCardForm.setRegistrationType("INVALID"); assertThrows(NacosApiException.class, agentCardForm::validate); } @Test void testFillDefaultRegistrationType() { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.fillDefaultRegistrationType(); assertEquals("URL", agentCardForm.getRegistrationType()); } @Test void testFillDefaultRegistrationTypeWithExistingValue() { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setRegistrationType("SERVICE"); agentCardForm.fillDefaultRegistrationType(); assertEquals("SERVICE", agentCardForm.getRegistrationType()); } @Test void testGetterAndSetter() { AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setNamespaceId("test-namespace"); agentCardForm.setAgentName("test-agent"); agentCardForm.setVersion("1.0.0"); agentCardForm.setRegistrationType("SERVICE"); agentCardForm.setAgentCard("{\"name\":\"test-agent\"}"); assertEquals("test-namespace", agentCardForm.getNamespaceId()); assertEquals("test-agent", agentCardForm.getAgentName()); assertEquals("1.0.0", agentCardForm.getVersion()); assertEquals("SERVICE", agentCardForm.getRegistrationType()); assertEquals("{\"name\":\"test-agent\"}", agentCardForm.getAgentCard()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/a2a/admin/AgentFormTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.a2a.admin; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class AgentFormTest { @Test void testValidateSuccess() throws NacosApiException { AgentForm agentForm = new AgentForm(); agentForm.setAgentName("test-agent"); agentForm.validate(); // Should not throw exception } @Test void testValidateWithEmptyNameShouldThrowException() { AgentForm agentForm = new AgentForm(); assertThrows(NacosApiException.class, agentForm::validate); } @Test void testValidateWithNullNameShouldThrowException() { AgentForm agentForm = new AgentForm(); agentForm.setAgentName(null); assertThrows(NacosApiException.class, agentForm::validate); } @Test void testFillDefaultNamespaceId() { AgentForm agentForm = new AgentForm(); agentForm.fillDefaultNamespaceId(); assertEquals("public", agentForm.getNamespaceId()); } @Test void testFillDefaultNamespaceIdWithExistingValue() { AgentForm agentForm = new AgentForm(); agentForm.setNamespaceId("test-namespace"); agentForm.fillDefaultNamespaceId(); assertEquals("test-namespace", agentForm.getNamespaceId()); } @Test void testValidateShouldFillDefaultNamespaceId() throws NacosApiException { AgentForm agentForm = new AgentForm(); agentForm.setAgentName("test-agent"); agentForm.validate(); assertEquals("public", agentForm.getNamespaceId()); } @Test void testGetterAndSetter() { AgentForm agentForm = new AgentForm(); agentForm.setNamespaceId("test-namespace"); agentForm.setAgentName("test-agent"); agentForm.setVersion("1.0.0"); agentForm.setRegistrationType("URL"); assertEquals("test-namespace", agentForm.getNamespaceId()); assertEquals("test-agent", agentForm.getAgentName()); assertEquals("1.0.0", agentForm.getVersion()); assertEquals("URL", agentForm.getRegistrationType()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/a2a/admin/AgentListFormTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.a2a.admin; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class AgentListFormTest { @Test void testValidateWithAccurateSearch() throws NacosApiException { AgentListForm agentListForm = new AgentListForm(); agentListForm.setAgentName("test-agent"); agentListForm.setSearch("accurate"); agentListForm.validate(); // Should not throw exception } @Test void testValidateWithBlurSearch() throws NacosApiException { AgentListForm agentListForm = new AgentListForm(); agentListForm.setAgentName("test-agent"); agentListForm.setSearch("blur"); agentListForm.validate(); // Should not throw exception } @Test void testValidateWithUpperCaseSearch() throws NacosApiException { AgentListForm agentListForm = new AgentListForm(); agentListForm.setAgentName("test-agent"); agentListForm.setSearch("ACCURATE"); agentListForm.validate(); // Should not throw exception agentListForm.setSearch("BLUR"); agentListForm.validate(); // Should not throw exception } @Test void testValidateWithInvalidSearchShouldThrowException() { AgentListForm agentListForm = new AgentListForm(); agentListForm.setAgentName("test-agent"); agentListForm.setSearch("invalid"); assertThrows(NacosApiException.class, agentListForm::validate); } @Test void testValidateWithNullSearchShouldThrowException() { AgentListForm agentListForm = new AgentListForm(); agentListForm.setAgentName("test-agent"); agentListForm.setSearch(null); assertThrows(NacosApiException.class, agentListForm::validate); } @Test void testValidateShouldFillDefaultNamespaceId() throws NacosApiException { AgentListForm agentListForm = new AgentListForm(); agentListForm.setAgentName("test-agent"); agentListForm.setSearch("accurate"); agentListForm.validate(); assertEquals("public", agentListForm.getNamespaceId()); } @Test void testGetterAndSetter() { AgentListForm agentListForm = new AgentListForm(); agentListForm.setNamespaceId("test-namespace"); agentListForm.setAgentName("test-agent"); agentListForm.setVersion("1.0.0"); agentListForm.setRegistrationType("URL"); agentListForm.setSearch("accurate"); assertEquals("test-namespace", agentListForm.getNamespaceId()); assertEquals("test-agent", agentListForm.getAgentName()); assertEquals("1.0.0", agentListForm.getVersion()); assertEquals("URL", agentListForm.getRegistrationType()); assertEquals("accurate", agentListForm.getSearch()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/mcp/admin/McpImportFormTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.mcp.admin; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.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 McpImportFormTest { @Test void testValidateSuccessWithJsonType() throws NacosApiException { McpImportForm form = new McpImportForm(); form.setImportType("json"); form.setData("{\"test\": \"data\"}"); form.validate(); // Should not throw exception } @Test void testValidateSuccessWithUrlType() throws NacosApiException { McpImportForm form = new McpImportForm(); form.setImportType("url"); form.setData("http://example.com/registry.json"); form.validate(); // Should not throw exception } @Test void testValidateSuccessWithFileType() throws NacosApiException { McpImportForm form = new McpImportForm(); form.setImportType("file"); form.setData("/path/to/registry.json"); form.validate(); // Should not throw exception } @Test void testValidateWithEmptyImportTypeShouldThrowException() { McpImportForm form = new McpImportForm(); form.setImportType(""); form.setData("{\"test\": \"data\"}"); assertThrows(NacosApiException.class, form::validate); } @Test void testValidateWithNullImportTypeShouldThrowException() { McpImportForm form = new McpImportForm(); form.setImportType(null); form.setData("{\"test\": \"data\"}"); assertThrows(NacosApiException.class, form::validate); } @Test void testValidateWithEmptyDataShouldThrowException() { McpImportForm form = new McpImportForm(); form.setImportType("json"); form.setData(""); assertThrows(NacosApiException.class, form::validate); } @Test void testValidateWithNullDataShouldThrowException() { McpImportForm form = new McpImportForm(); form.setImportType("json"); form.setData(null); assertThrows(NacosApiException.class, form::validate); } @Test void testValidateWithInvalidImportTypeShouldThrowException() { McpImportForm form = new McpImportForm(); form.setImportType("invalid"); form.setData("{\"test\": \"data\"}"); assertThrows(NacosApiException.class, form::validate); } @Test void testValidateShouldFillDefaultValue() throws NacosApiException { McpImportForm form = new McpImportForm(); form.setImportType("json"); form.setData("{\"test\": \"data\"}"); form.validate(); assertEquals("public", form.getNamespaceId()); } @Test void testGetterAndSetter() { McpImportForm form = new McpImportForm(); // Test basic fields form.setImportType("json"); form.setData("{\"test\": \"data\"}"); form.setOverrideExisting(true); form.setValidateOnly(true); form.setSkipInvalid(true); form.setCursor("cursor123"); form.setLimit(10); form.setSearch("test"); String[] selectedServers = {"server1", "server2"}; form.setSelectedServers(selectedServers); assertEquals("json", form.getImportType()); assertEquals("{\"test\": \"data\"}", form.getData()); assertTrue(form.isOverrideExisting()); assertTrue(form.isValidateOnly()); assertTrue(form.isSkipInvalid()); assertArrayEquals(selectedServers, form.getSelectedServers()); assertEquals("cursor123", form.getCursor()); assertEquals(10, form.getLimit()); assertEquals("test", form.getSearch()); } @Test void testDefaultValueOfBooleanFields() { McpImportForm form = new McpImportForm(); assertFalse(form.isOverrideExisting()); assertFalse(form.isValidateOnly()); assertFalse(form.isSkipInvalid()); assertNull(form.getSelectedServers()); assertNull(form.getCursor()); assertNull(form.getLimit()); assertNull(form.getSearch()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/prompt/PromptFormTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class PromptFormTest { @Test void validateShouldFillDefaultNamespaceWhenNamespaceBlank() throws NacosApiException { PromptForm form = new PromptForm(); form.setPromptKey("p1"); form.validate(); assertEquals("public", form.getNamespaceId()); } @Test void validateShouldThrowWhenPromptKeyMissing() { PromptForm form = new PromptForm(); assertThrows(NacosApiException.class, form::validate); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/prompt/PromptHistoryFormTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PromptHistoryFormTest { @Test void validateShouldNormalizePageDefaultsWhenPageInvalid() throws NacosApiException { PromptHistoryForm form = new PromptHistoryForm(); form.setPromptKey("p1"); form.setPageNo(-1); form.setPageSize(0); form.validate(); assertEquals(1, form.getPageNo()); assertEquals(10, form.getPageSize()); } @Test void validateShouldCapPageSizeToMax() throws NacosApiException { PromptHistoryForm form = new PromptHistoryForm(); form.setPromptKey("p1"); form.setPageSize(1000); form.validate(); assertEquals(50, form.getPageSize()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/prompt/PromptLabelBindFormTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; class PromptLabelBindFormTest { @Test void validateShouldThrowWhenLabelMissing() { PromptLabelBindForm form = new PromptLabelBindForm(); form.setPromptKey("p1"); form.setVersion("1.0.0"); assertThrows(NacosApiException.class, form::validate); } @Test void validateShouldThrowWhenVersionMissing() { PromptLabelBindForm form = new PromptLabelBindForm(); form.setPromptKey("p1"); form.setLabel("prod"); assertThrows(NacosApiException.class, form::validate); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/prompt/PromptLabelFormTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; class PromptLabelFormTest { @Test void validateShouldThrowWhenLabelMissing() { PromptLabelForm form = new PromptLabelForm(); form.setPromptKey("p1"); assertThrows(NacosApiException.class, form::validate); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/prompt/PromptListFormTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class PromptListFormTest { @Test void validateShouldThrowWhenSearchInvalid() { PromptListForm form = new PromptListForm(); form.setSearch("invalid"); assertThrows(NacosApiException.class, form::validate); } @Test void validateShouldNormalizePageDefaultsWhenPageInvalid() throws NacosApiException { PromptListForm form = new PromptListForm(); form.setPageNo(0); form.setPageSize(0); form.validate(); assertEquals(1, form.getPageNo()); assertEquals(10, form.getPageSize()); assertEquals("public", form.getNamespaceId()); } @Test void validateShouldCapPageSizeToMax() throws NacosApiException { PromptListForm form = new PromptListForm(); form.setPageSize(999); form.validate(); assertEquals(50, form.getPageSize()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/form/prompt/PromptPublishFormTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.form.prompt; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertThrows; class PromptPublishFormTest { @Test void validateShouldThrowWhenVersionMissing() { PromptPublishForm form = new PromptPublishForm(); form.setPromptKey("p1"); assertThrows(NacosApiException.class, form::validate); } @Test void validateShouldThrowWhenVersionInvalid() { PromptPublishForm form = new PromptPublishForm(); form.setPromptKey("p1"); form.setVersion("1.0"); assertThrows(NacosApiException.class, form::validate); } @Test void validateShouldPassWhenVersionValid() { PromptPublishForm form = new PromptPublishForm(); form.setPromptKey("p1"); form.setVersion("1.2.3"); assertDoesNotThrow(form::validate); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/index/AbstractMcpServerIndexTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; /** * Unit tests for AbstractMcpServerIndex.searchMcpServerByNameWithOffset method. * Covers various combinations of offset, limit, and total count scenarios. * * @author xinluo */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class AbstractMcpServerIndexTest { } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/index/CachedMcpServerIndexTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.core.service.NamespaceOperationService; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Unit tests for CachedMcpServerIndex. */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class CachedMcpServerIndexTest { @Mock private ConfigDetailService configDetailService; @Mock private NamespaceOperationService namespaceOperationService; @Mock private ConfigQueryChainService configQueryChainService; @Mock private McpCacheIndex cacheIndex; @Mock private ScheduledExecutorService scheduledExecutor; private CachedMcpServerIndex cachedIndex; @BeforeEach void setUp() { // Set system properties to enable cache System.setProperty("nacos.mcp.cache.enabled", "true"); System.setProperty("nacos.mcp.cache.sync.interval", "300"); cachedIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, true, 300); } @Test void testGetMcpServerByIdWithCacheHit() { final String mcpId = "test-id-123"; final String namespaceId = "test-namespace"; // 模拟缓存命中 McpServerIndexData cachedData = new McpServerIndexData(); cachedData.setId(mcpId); cachedData.setNamespaceId(namespaceId); when(cacheIndex.getMcpServerById(mcpId)).thenReturn(cachedData); // 执行查询 McpServerIndexData result = cachedIndex.getMcpServerById(mcpId); // 验证结果 assertNotNull(result); assertEquals(mcpId, result.getId()); assertEquals(namespaceId, result.getNamespaceId()); // 验证缓存被调用,数据库查询没有被调用 verify(cacheIndex).getMcpServerById(mcpId); verify(configQueryChainService, never()).handle(any()); } @Test void testGetMcpServerByIdWithCacheMiss() { final String mcpId = "test-id-123"; final String namespaceId = "test-namespace"; // 模拟缓存未命中 when(cacheIndex.getMcpServerById(mcpId)).thenReturn(null); // 模拟数据库查询结果 ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class); when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse); // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace(namespaceId); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 执行查询 McpServerIndexData result = cachedIndex.getMcpServerById(mcpId); // 验证结果 assertNotNull(result); assertEquals(mcpId, result.getId()); assertEquals(namespaceId, result.getNamespaceId()); // 验证缓存被调用,数据库查询也被调用 verify(cacheIndex).getMcpServerById(mcpId); verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class)); // 验证缓存被更新 verify(cacheIndex).updateIndex(eq(namespaceId), eq(mcpId), eq(mcpId)); } @Test void testGetMcpServerByNameWithCacheHit() { final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; final String mcpId = "test-id-123"; // 模拟缓存命中 McpServerIndexData cachedData = new McpServerIndexData(); cachedData.setId(mcpId); cachedData.setNamespaceId(namespaceId); when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(cachedData); // 执行查询 McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName); // 验证结果 assertNotNull(result); assertEquals(mcpId, result.getId()); assertEquals(namespaceId, result.getNamespaceId()); // 验证缓存被调用 verify(cacheIndex).getMcpServerByName(namespaceId, mcpName); } @Test void testGetMcpServerByNameWithCacheMiss() { final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; final String mcpId = "test-id-123"; // 模拟缓存未命中 when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(null); // 模拟数据库查询结果 final Page mockPage = new Page<>(); List configList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo.setTenant(namespaceId); configList.add(configInfo); mockPage.setPageItems(configList); mockPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行查询 McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName); // 验证结果 assertNotNull(result); assertEquals(mcpId, result.getId()); assertEquals(namespaceId, result.getNamespaceId()); // 验证缓存被调用,数据库查询也被调用 verify(cacheIndex).getMcpServerByName(namespaceId, mcpName); verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); // 验证缓存被更新 verify(cacheIndex).updateIndex(eq(namespaceId), eq(mcpName), eq(mcpId)); } @Test void testSearchMcpServerByName() { final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; final String mcpId = "test-id-123"; // 模拟数据库查询结果 final Page mockPage = new Page<>(); List configList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo.setTenant(namespaceId); // 设置content为JSON格式的McpServerVersionInfo configInfo.setContent("{\"id\":\"" + mcpId + "\"}"); configList.add(configInfo); mockPage.setPageItems(configList); mockPage.setTotalCount(1); // 使用正确的参数匹配,匹配实际的调用参数 when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行搜索 Page result = cachedIndex.searchMcpServerByNameWithPage(namespaceId, mcpName, Constants.MCP_LIST_SEARCH_ACCURATE, 1, 10); // 验证结果 assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); McpServerIndexData indexData = result.getPageItems().get(0); assertEquals(mcpId, indexData.getId()); assertEquals(namespaceId, indexData.getNamespaceId()); // 验证数据库查询被调用 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); // 验证缓存被更新 verify(cacheIndex).updateIndex(eq(namespaceId), any(), eq(mcpId)); } @Test void testCacheDisabled() { // 设置系统属性以禁用缓存 System.setProperty("nacos.mcp.cache.enabled", "false"); // 重新创建实例 final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0); final String mcpId = "test-id-123"; final String namespaceId = "test-namespace"; // 模拟数据库查询结果 ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class); when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse); // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace(namespaceId); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 执行查询 McpServerIndexData result = disabledIndex.getMcpServerById(mcpId); // 验证结果 assertNotNull(result); assertEquals(mcpId, result.getId()); assertEquals(namespaceId, result.getNamespaceId()); // 验证缓存没有被调用 verify(cacheIndex, never()).getMcpServerById(anyString()); verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString()); // 验证数据库查询被调用 verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class)); } @Test void testGetCacheStats() { // 模拟缓存统计 McpCacheIndex.CacheStats mockStats = new McpCacheIndex.CacheStats(10, 5, 2, 100); when(cacheIndex.getStats()).thenReturn(mockStats); // 获取统计信息 McpCacheIndex.CacheStats result = cachedIndex.getCacheStats(); // 验证结果 assertNotNull(result); assertEquals(10, result.getHitCount()); assertEquals(5, result.getMissCount()); assertEquals(2, result.getEvictionCount()); assertEquals(100, result.getSize()); assertEquals(2.0 / 3.0, result.getHitRate(), 0.001); } @Test void testClearCache() { // 执行清空缓存 cachedIndex.clearCache(); // 验证缓存被清空 verify(cacheIndex).clear(); } @Test void testTriggerCacheSync() { // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace("test-namespace"); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 模拟搜索结果 final Page mockPage = new Page<>(); mockPage.setPageItems(new ArrayList<>()); mockPage.setTotalCount(0); when(configDetailService.findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(), anyString(), any())).thenReturn(mockPage); // 执行手动同步 cachedIndex.triggerCacheSync(); // 验证数据库查询被调用 verify(configDetailService).findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(), anyString(), any()); } // 新增缓存删除功能测试 @Test void testRemoveMcpServerByNameWhenCacheEnabled() { final String namespaceId = "test-namespace"; final String mcpName = "test-mcp-name"; // 执行缓存删除 cachedIndex.removeMcpServerByName(namespaceId, mcpName); // 验证缓存删除方法被调用 verify(cacheIndex).removeIndex(namespaceId, mcpName); } @Test void testRemoveMcpServerByIdWhenCacheEnabled() { final String mcpId = "test-mcp-id-123"; // 执行缓存删除 cachedIndex.removeMcpServerById(mcpId); // 验证缓存删除方法被调用 verify(cacheIndex).removeIndex(mcpId); } @Test void testRemoveMcpServerByNameWhenCacheDisabled() { // 创建禁用缓存的实例 final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0); final String namespaceId = "test-namespace"; final String mcpName = "test-mcp-name"; // 执行缓存删除 disabledIndex.removeMcpServerByName(namespaceId, mcpName); // 验证缓存删除方法没有被调用(因为缓存被禁用) verify(cacheIndex, never()).removeIndex(namespaceId, mcpName); } @Test void testRemoveMcpServerByIdWhenCacheDisabled() { // 创建禁用缓存的实例 final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0); final String mcpId = "test-mcp-id-123"; // 执行缓存删除 disabledIndex.removeMcpServerById(mcpId); // 验证缓存删除方法没有被调用(因为缓存被禁用) verify(cacheIndex, never()).removeIndex(mcpId); } @Test void testRemoveMcpServerByNameWithNullParameters() { // 测试 null 参数 cachedIndex.removeMcpServerByName(null, null); cachedIndex.removeMcpServerByName("namespace", null); cachedIndex.removeMcpServerByName(null, "mcpName"); // 验证缓存删除方法没有被调用(因为参数为 null 或空) verify(cacheIndex, never()).removeIndex(anyString(), anyString()); } @Test void testRemoveMcpServerByIdWithNullParameter() { // 测试 null 参数 cachedIndex.removeMcpServerById(null); // 验证缓存删除方法没有被调用(因为参数为 null) verify(cacheIndex, never()).removeIndex(anyString()); } @Test void testRemoveMcpServerByNameWithEmptyParameters() { // 测试空字符串参数 cachedIndex.removeMcpServerByName("", ""); cachedIndex.removeMcpServerByName("namespace", ""); cachedIndex.removeMcpServerByName("", "mcpName"); // 空字符串应该仍然调用缓存删除方法 verify(cacheIndex).removeIndex("", ""); verify(cacheIndex).removeIndex("namespace", ""); verify(cacheIndex).removeIndex("", "mcpName"); } @Test void testRemoveMcpServerByIdWithEmptyParameter() { // 测试空字符串参数 cachedIndex.removeMcpServerById(""); // 空字符串应该仍然调用缓存删除方法 verify(cacheIndex).removeIndex(""); } // 补充的测试用例 @Test void testGetMcpServerByIdWithCacheDisabledAndNotFound() { // 创建禁用缓存的实例 final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0); final String mcpId = "test-id-123"; // 模拟数据库查询结果为null ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class); when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse); // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace("test-namespace"); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 执行查询 McpServerIndexData result = disabledIndex.getMcpServerById(mcpId); // 验证结果为null assertNull(result); // 验证缓存没有被调用 verify(cacheIndex, never()).getMcpServerById(anyString()); verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString()); // 验证数据库查询被调用 verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class)); } @Test void testGetMcpServerByIdWithCacheMissAndNotFound() { final String mcpId = "test-id-123"; // 模拟缓存未命中 when(cacheIndex.getMcpServerById(mcpId)).thenReturn(null); // 模拟数据库查询结果为null(未找到) ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class); when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse); // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace("test-namespace"); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 执行查询 McpServerIndexData result = cachedIndex.getMcpServerById(mcpId); // 验证结果为null assertNull(result); // 验证缓存被调用,数据库查询也被调用 verify(cacheIndex).getMcpServerById(mcpId); verify(configQueryChainService).handle(any(ConfigQueryChainRequest.class)); // 验证缓存未被更新(因为未找到) verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString()); } @Test void testGetMcpServerByNameWithInvalidParameters() { // 设置默认的空 mock 结果 final Page emptyPage = new Page<>(); emptyPage.setPageItems(new ArrayList<>()); emptyPage.setTotalCount(0); when(configDetailService.findConfigInfoPage(anyString(), anyInt(), anyInt(), any(), anyString(), anyString(), any())).thenReturn(emptyPage); // 测试null参数 - 当两个都为null时,返回null McpServerIndexData result1 = cachedIndex.getMcpServerByName(null, "test-name"); assertNull(result1); McpServerIndexData result2 = cachedIndex.getMcpServerByName("test-namespace", null); assertNull(result2); McpServerIndexData result3 = cachedIndex.getMcpServerByName(null, null); assertNull(result3); // 测试空字符串参数 // 当namespaceId为空时,会调用getFirstMcpServerByName List emptyNamespaceList = new ArrayList<>(); when(namespaceOperationService.getNamespaceList()).thenReturn(emptyNamespaceList); McpServerIndexData result4 = cachedIndex.getMcpServerByName("", "test-name"); assertNull(result4); // 恢复正常的命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace("test-namespace"); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 当name为空字符串时,实现仍然会调用缓存查询,这是正常行为 // 确保我们的mock能够处理这种情况 when(cacheIndex.getMcpServerByName("test-namespace", "")).thenReturn(null); McpServerIndexData result5 = cachedIndex.getMcpServerByName("test-namespace", ""); assertNull(result5); // 验证缓存被调用了空字符串情况 verify(cacheIndex).getMcpServerByName("test-namespace", ""); } @Test void testGetMcpServerByNameWithCacheDisabledAndNotFound() { // 创建禁用缓存的实例 final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0); final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; // 模拟数据库查询结果为null final Page mockPage = new Page<>(); mockPage.setPageItems(new ArrayList<>()); mockPage.setTotalCount(0); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行查询 McpServerIndexData result = disabledIndex.getMcpServerByName(namespaceId, mcpName); // 验证结果为null assertNull(result); // 验证缓存没有被调用 verify(cacheIndex, never()).getMcpServerByName(anyString(), anyString()); verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString()); // 验证数据库查询被调用 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); } @Test void testGetMcpServerByNameWithCacheMissAndNotFound() { final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; // 模拟缓存未命中 when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(null); // 模拟数据库查询结果为null final Page mockPage = new Page<>(); mockPage.setPageItems(new ArrayList<>()); mockPage.setTotalCount(0); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行查询 McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName); // 验证结果为null assertNull(result); // 验证缓存被调用,数据库查询也被调用 verify(cacheIndex).getMcpServerByName(namespaceId, mcpName); verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); // 验证缓存未被更新(因为未找到) verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString()); } @Test void testSearchMcpServerByNameWithNullName() { final String namespaceId = "test-namespace"; final String mcpId = "test-id-123"; // 模拟数据库查询结果 final Page mockPage = new Page<>(); List configList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo.setTenant(namespaceId); configInfo.setContent("{\"id\":\"" + mcpId + "\"}"); configList.add(configInfo); mockPage.setPageItems(configList); mockPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行搜索,name为null Page result = cachedIndex.searchMcpServerByNameWithPage(namespaceId, null, Constants.MCP_LIST_SEARCH_BLUR, 1, 10); // 验证结果 assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); McpServerIndexData indexData = result.getPageItems().get(0); assertEquals(mcpId, indexData.getId()); assertEquals(namespaceId, indexData.getNamespaceId()); // 验证数据库查询被调用 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); } @Test void testSearchMcpServerByNameWithEmptyName() { final String namespaceId = "test-namespace"; final String mcpId = "test-id-123"; // 模拟数据库查询结果 final Page mockPage = new Page<>(); List configList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo.setTenant(namespaceId); configInfo.setContent("{\"id\":\"" + mcpId + "\"}"); configList.add(configInfo); mockPage.setPageItems(configList); mockPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行搜索,name为空字符串 Page result = cachedIndex.searchMcpServerByNameWithPage(namespaceId, "", Constants.MCP_LIST_SEARCH_ACCURATE, 1, 10); // 验证结果 assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); McpServerIndexData indexData = result.getPageItems().get(0); assertEquals(mcpId, indexData.getId()); assertEquals(namespaceId, indexData.getNamespaceId()); // 验证数据库查询被调用 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); } @Test void testSearchMcpServerByNameWithBlurSearch() { final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; final String mcpId = "test-id-123"; // 模拟数据库查询结果 final Page mockPage = new Page<>(); List configList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo.setTenant(namespaceId); configInfo.setContent("{\"id\":\"" + mcpId + "\"}"); configList.add(configInfo); mockPage.setPageItems(configList); mockPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行搜索 Page result = cachedIndex.searchMcpServerByNameWithPage(namespaceId, mcpName, Constants.MCP_LIST_SEARCH_BLUR, 1, 10); // 验证结果 assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); McpServerIndexData indexData = result.getPageItems().get(0); assertEquals(mcpId, indexData.getId()); assertEquals(namespaceId, indexData.getNamespaceId()); // 验证数据库查询被调用 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); } @Test void testSearchMcpServerByNameWithPagination() { final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; final String mcpId = "test-id-123"; // 模拟数据库查询结果 final Page mockPage = new Page<>(); List configList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo.setTenant(namespaceId); configInfo.setContent("{\"id\":\"" + mcpId + "\"}"); configList.add(configInfo); mockPage.setPageItems(configList); mockPage.setTotalCount(15); // 总数15,测试分页 when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(3), eq(5), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行搜索,pageNo=3, limit=5 Page result = cachedIndex.searchMcpServerByNameWithPage(namespaceId, mcpName, Constants.MCP_LIST_SEARCH_ACCURATE, 3, 5); // 验证结果 assertNotNull(result); assertEquals(15, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); assertEquals(3, result.getPageNumber()); assertEquals(3, result.getPagesAvailable()); // ceil(15/5) = 3 McpServerIndexData indexData = result.getPageItems().get(0); assertEquals(mcpId, indexData.getId()); assertEquals(namespaceId, indexData.getNamespaceId()); // 验证数据库查询被调用 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(3), eq(5), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any()); } @Test void testFetchOrderedNamespaceList() { // 模拟命名空间列表(无序) final List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace1 = new com.alibaba.nacos.api.model.response.Namespace(); namespace1.setNamespace("b-namespace"); com.alibaba.nacos.api.model.response.Namespace namespace2 = new com.alibaba.nacos.api.model.response.Namespace(); namespace2.setNamespace("a-namespace"); com.alibaba.nacos.api.model.response.Namespace namespace3 = new com.alibaba.nacos.api.model.response.Namespace(); namespace3.setNamespace("c-namespace"); namespaceList.add(namespace1); namespaceList.add(namespace2); namespaceList.add(namespace3); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 通过调用依赖该方法的函数来间接测试 final String mcpId = "test-id-123"; // 模拟缓存未命中 when(cacheIndex.getMcpServerById(mcpId)).thenReturn(null); // 模拟数据库查询结果 ConfigQueryChainResponse mockResponse = mock(ConfigQueryChainResponse.class); when(mockResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse); // 执行查询 cachedIndex.getMcpServerById(mcpId); // 验证命名空间服务被调用 verify(namespaceOperationService).getNamespaceList(); } @Test void testMapMcpServerVersionConfigToIndexData() { // 通过调用依赖该方法的函数来间接测试 final String namespaceId = "test-namespace"; final String mcpName = "test-mcp"; final String mcpId = "test-id-123"; // 模拟缓存未命中 when(cacheIndex.getMcpServerByName(namespaceId, mcpName)).thenReturn(null); // 模拟数据库查询结果 final Page mockPage = new Page<>(); List configList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId(mcpId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo.setTenant(namespaceId); configList.add(configInfo); mockPage.setPageItems(configList); mockPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(namespaceId), any())).thenReturn(mockPage); // 执行查询 McpServerIndexData result = cachedIndex.getMcpServerByName(namespaceId, mcpName); // 验证结果,确保mapMcpServerVersionConfigToIndexData方法正确执行 assertNotNull(result); assertEquals(mcpId, result.getId()); assertEquals(namespaceId, result.getNamespaceId()); } @Test void testTriggerCacheSyncWhenCacheDisabled() { // 创建禁用缓存的实例 final CachedMcpServerIndex disabledIndex = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, false, 0); // 执行手动同步 disabledIndex.triggerCacheSync(); // 验证数据库查询没有被调用 verify(configDetailService, never()).findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(), anyString(), any()); } @Test void testStartSyncTask() { when(scheduledExecutor.scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class))).then( (Answer>) invocation -> { invocation.getArgument(0, Runnable.class).run(); return null; }); // 创建一个新的实例来测试startSyncTask方法 new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, true, 10); // 验证调度任务已启动 verify(scheduledExecutor).scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class)); verify(namespaceOperationService).getNamespaceList(); } @Test void testStartSyncTaskWithException() { when(scheduledExecutor.scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class))).then( (Answer>) invocation -> { invocation.getArgument(0, Runnable.class).run(); return null; }); when(namespaceOperationService.getNamespaceList()).thenThrow(new RuntimeException("test")); // 创建一个新的实例来测试startSyncTask方法 new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, true, 10); // 验证调度任务已启动 verify(scheduledExecutor).scheduleWithFixedDelay(any(Runnable.class), eq(10L), eq(10L), any(TimeUnit.class)); } @Test void testDestroy() { // 模拟一个已经存在的任务 when(scheduledExecutor.scheduleWithFixedDelay(any(Runnable.class), anyLong(), anyLong(), any(TimeUnit.class))).then((Answer) invocation -> mock(ScheduledFuture.class)); // 创建一个新的实例来测试destroy方法 CachedMcpServerIndex indexToDestroy = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, true, 300); // 调用destroy方法 indexToDestroy.destroy(); // 验证调度任务被取消和线程池被关闭 verify(scheduledExecutor).shutdown(); } @Test void testDestroyWithExceptionHandling() { // 模拟scheduledExecutor.shutdown()抛出异常 doThrow(new RuntimeException("Shutdown failed")).when(scheduledExecutor).shutdown(); // 创建一个新的实例来测试destroy方法 CachedMcpServerIndex indexToDestroy = new CachedMcpServerIndex(configDetailService, namespaceOperationService, configQueryChainService, cacheIndex, scheduledExecutor, true, 300); // 调用destroy方法不应该抛出异常 indexToDestroy.destroy(); } @Test void testSyncCacheFromDatabase() { // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace1 = new com.alibaba.nacos.api.model.response.Namespace(); namespace1.setNamespace("namespace-1"); com.alibaba.nacos.api.model.response.Namespace namespace2 = new com.alibaba.nacos.api.model.response.Namespace(); namespace2.setNamespace("namespace-2"); namespaceList.add(namespace1); namespaceList.add(namespace2); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 模拟每个命名空间的搜索结果 final Page mockPage1 = new Page<>(); List configList1 = new ArrayList<>(); ConfigInfo configInfo1 = new ConfigInfo(); configInfo1.setDataId("server1" + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo1.setTenant("namespace-1"); configInfo1.setContent("{\"id\":\"server1\"}"); configList1.add(configInfo1); mockPage1.setPageItems(configList1); mockPage1.setTotalCount(1); final Page mockPage2 = new Page<>(); List configList2 = new ArrayList<>(); ConfigInfo configInfo2 = new ConfigInfo(); configInfo2.setDataId("server2" + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX); configInfo2.setTenant("namespace-2"); configInfo2.setContent("{\"id\":\"server2\"}"); configList2.add(configInfo2); mockPage2.setPageItems(configList2); mockPage2.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any())).thenReturn(mockPage1); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-2"), any())).thenReturn(mockPage2); // 调用syncCacheFromDatabase方法(通过triggerCacheSync) cachedIndex.triggerCacheSync(); // 验证为每个命名空间调用了搜索 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any()); verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-2"), any()); // 验证缓存被更新。当 name=null 时,updateIndex 的第二个参数为 null verify(cacheIndex).updateIndex(eq("namespace-1"), isNull(), eq("server1")); verify(cacheIndex).updateIndex(eq("namespace-2"), isNull(), eq("server2")); } @Test void testSyncCacheFromDatabaseWithSearchException() { // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace("namespace-1"); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 模拟搜索时抛出异常 when(configDetailService.findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(), anyString(), any())).thenThrow(new RuntimeException("Database error")); // 调用syncCacheFromDatabase方法(通过triggerCacheSync) cachedIndex.triggerCacheSync(); // 即使出现异常也应该继续执行而不会中断 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any()); } @Test void testSyncCacheFromDatabaseWithEmptyResult() { // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace("namespace-1"); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 模拟空的搜索结果 Page mockPage = new Page<>(); mockPage.setPageItems(new ArrayList<>()); mockPage.setTotalCount(0); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any())).thenReturn(mockPage); // 调用syncCacheFromDatabase方法(通过triggerCacheSync) cachedIndex.triggerCacheSync(); // 验证搜索被调用但缓存未更新 verify(configDetailService).findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(1000), anyString(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq("namespace-1"), any()); // 没有数据所以不需要更新缓存 verify(cacheIndex, never()).updateIndex(anyString(), anyString(), anyString()); } @Test void testSyncCacheFromDatabaseWithException() { // 模拟命名空间列表 List namespaceList = new ArrayList<>(); com.alibaba.nacos.api.model.response.Namespace namespace = new com.alibaba.nacos.api.model.response.Namespace(); namespace.setNamespace("test-namespace"); namespaceList.add(namespace); when(namespaceOperationService.getNamespaceList()).thenReturn(namespaceList); // 模拟搜索时抛出异常 when(configDetailService.findConfigInfoPage(anyString(), anyInt(), anyInt(), anyString(), anyString(), anyString(), any())).thenThrow(new RuntimeException("Test exception")); // 通过调用triggerCacheSync来触发syncCacheFromDatabase cachedIndex.triggerCacheSync(); // 验证异常被处理,不会导致程序崩溃 verify(namespaceOperationService).getNamespaceList(); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/index/McpCachePerformanceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import com.alibaba.nacos.ai.config.McpCacheIndexProperties; import org.junit.jupiter.api.Test; /** * MCP缓存性能测试. * * @author misselvexu */ public class McpCachePerformanceTest { private static final int THREAD_COUNT = 10; private static final int OPERATION_COUNT = 10000; private static final int CACHE_SIZE = 1000; // 实际测试中使用的键数量,确保在缓存容量范围内,避免频繁缓存替换 private static final int EFFECTIVE_KEY_COUNT = 800; /** * Create test configuration properties. */ private static McpCacheIndexProperties createTestProperties(int maxSize, long expireTimeSeconds, long cleanupIntervalSeconds) { McpCacheIndexProperties properties = new McpCacheIndexProperties(); properties.setMaxSize(maxSize); properties.setExpireTimeSeconds(expireTimeSeconds); properties.setCleanupIntervalSeconds(cleanupIntervalSeconds); return properties; } public static void main(String[] args) { System.out.println("开始MCP缓存性能测试..."); // 测试内存缓存性能 testMemoryCachePerformance(); // 测试并发性能 testConcurrentPerformance(); // 验证修复效果:确保生成的键数量在合理范围内 // testKeyGenerationValidation(); // 现在作为JUnit测试运行 System.out.println("性能测试完成!"); } /** * 测试内存缓存性能. */ private static void testMemoryCachePerformance() { System.out.println("\n=== 内存缓存性能测试 ==="); MemoryMcpCacheIndex cacheIndex = new MemoryMcpCacheIndex(createTestProperties(CACHE_SIZE, 3600, 300)); // 预热缓存 - 使用有效键数量,避免超出缓存容量 System.out.println("预热缓存..."); for (int i = 0; i < EFFECTIVE_KEY_COUNT; i++) { String namespaceId = "namespace-" + (i % 10); String mcpName = "mcp-" + i; String mcpId = "id-" + i; cacheIndex.updateIndex(namespaceId, mcpName, mcpId); } // 测试查询性能 System.out.println("测试查询性能..."); long startTime = System.nanoTime(); for (int i = 0; i < OPERATION_COUNT; i++) { String namespaceId = "namespace-" + (i % 10); String mcpName = "mcp-" + (i % EFFECTIVE_KEY_COUNT); cacheIndex.getMcpId(namespaceId, mcpName); } long endTime = System.nanoTime(); long duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime); System.out.printf("查询 %d 次操作耗时: %d ms\n", OPERATION_COUNT, duration); System.out.printf("平均每次查询耗时: %.2f μs\n", (duration * 1000.0) / OPERATION_COUNT); // 测试更新性能 System.out.println("测试更新性能..."); startTime = System.nanoTime(); for (int i = 0; i < OPERATION_COUNT; i++) { String namespaceId = "namespace-" + (i % 10); // 模拟真实的更新场景:大部分更新现有键,少部分创建新键 int keyIndex = i % (EFFECTIVE_KEY_COUNT + 100); // 允许少量超出有效范围的新键 String mcpName = "mcp-" + keyIndex; String mcpId = "id-" + keyIndex; cacheIndex.updateIndex(namespaceId, mcpName, mcpId); } endTime = System.nanoTime(); duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime); System.out.printf("更新 %d 次操作耗时: %d ms\n", OPERATION_COUNT, duration); System.out.printf("平均每次更新耗时: %.2f μs\n", (duration * 1000.0) / OPERATION_COUNT); // 显示缓存统计 McpCacheIndex.CacheStats stats = cacheIndex.getStats(); System.out.printf("缓存统计 - 命中次数: %d, 未命中次数: %d, 命中率: %.2f%%, 缓存大小: %d\n", stats.getHitCount(), stats.getMissCount(), stats.getHitRate() * 100, stats.getSize()); } /** * 测试并发性能. * *

修复说明: * 1. 使用EFFECTIVE_KEY_COUNT限制实际使用的键数量,避免超出缓存容量 2. 在并发更新测试中,80%操作更新现有键,20%操作创建新键,更真实地模拟实际场景 3. * 确保生成的键数量在合理范围内,避免频繁的缓存替换和内存压力 */ private static void testConcurrentPerformance() { System.out.println("\n=== 并发性能测试 ==="); MemoryMcpCacheIndex cacheIndex = new MemoryMcpCacheIndex(createTestProperties(CACHE_SIZE, 3600, 300)); // 预热缓存 - 使用有效键数量,避免超出缓存容量 System.out.println("预热缓存..."); for (int i = 0; i < EFFECTIVE_KEY_COUNT; i++) { String namespaceId = "namespace-" + (i % 10); String mcpName = "mcp-" + i; String mcpId = "id-" + i; cacheIndex.updateIndex(namespaceId, mcpName, mcpId); } // 并发查询测试 System.out.printf("启动 %d 个线程进行并发查询测试...\n", THREAD_COUNT); ExecutorService executor = Executors.newFixedThreadPool(THREAD_COUNT); CountDownLatch latch = new CountDownLatch(THREAD_COUNT); AtomicLong totalOperations = new AtomicLong(0); long startTime = System.nanoTime(); for (int t = 0; t < THREAD_COUNT; t++) { final int threadId = t; executor.submit(() -> { try { for (int i = 0; i < OPERATION_COUNT; i++) { String namespaceId = "namespace-" + (threadId % 10); String mcpName = "mcp-" + (i % EFFECTIVE_KEY_COUNT); cacheIndex.getMcpId(namespaceId, mcpName); totalOperations.incrementAndGet(); } } finally { latch.countDown(); } }); } try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } long endTime = System.nanoTime(); long duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime); System.out.printf("并发查询 %d 次操作耗时: %d ms\n", totalOperations.get(), duration); System.out.printf("平均每次查询耗时: %.2f μs\n", (duration * 1000.0) / totalOperations.get()); System.out.printf("吞吐量: %.2f ops/ms\n", (double) totalOperations.get() / duration); // 并发更新测试 System.out.printf("启动 %d 个线程进行并发更新测试...\n", THREAD_COUNT); CountDownLatch updateLatch = new CountDownLatch(THREAD_COUNT); AtomicLong totalUpdates = new AtomicLong(0); startTime = System.nanoTime(); for (int t = 0; t < THREAD_COUNT; t++) { final int threadId = t; executor.submit(() -> { try { for (int i = 0; i < OPERATION_COUNT; i++) { String namespaceId = "namespace-" + (threadId % 10); // 使用模运算确保键的数量在有效范围内,模拟真实的缓存更新场景 // 80%的操作更新现有键,20%的操作创建新键(在有效范围内) int keyIndex; if (i % 5 == 0) { // 20%的操作:创建新键(在有效范围内) keyIndex = (threadId * 50 + i) % EFFECTIVE_KEY_COUNT; } else { // 80%的操作:更新现有键 keyIndex = i % EFFECTIVE_KEY_COUNT; } String mcpName = "mcp-" + keyIndex; String mcpId = "id-" + keyIndex; cacheIndex.updateIndex(namespaceId, mcpName, mcpId); totalUpdates.incrementAndGet(); } } finally { updateLatch.countDown(); } }); } try { updateLatch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } endTime = System.nanoTime(); duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime); System.out.printf("并发更新 %d 次操作耗时: %d ms\n", totalUpdates.get(), duration); System.out.printf("平均每次更新耗时: %.2f μs\n", (duration * 1000.0) / totalUpdates.get()); System.out.printf("吞吐量: %.2f ops/ms\n", (double) totalUpdates.get() / duration); executor.shutdown(); // 显示最终缓存统计 McpCacheIndex.CacheStats stats = cacheIndex.getStats(); System.out.printf("最终缓存统计 - 命中次数: %d, 未命中次数: %d, 命中率: %.2f%%, 缓存大小: %d\n", stats.getHitCount(), stats.getMissCount(), stats.getHitRate() * 100, stats.getSize()); } /** * 测试缓存大小对性能的影响. */ private static void testCacheSizeImpact() { System.out.println("\n=== 缓存大小对性能的影响测试 ==="); int[] cacheSizes = {100, 1000, 10000, 100000}; for (int size : cacheSizes) { System.out.printf("\n测试缓存大小: %d\n", size); MemoryMcpCacheIndex cacheIndex = new MemoryMcpCacheIndex(createTestProperties(size, 3600, 300)); // 预热缓存 for (int i = 0; i < size; i++) { String namespaceId = "namespace-" + (i % 10); String mcpName = "mcp-" + i; String mcpId = "id-" + i; cacheIndex.updateIndex(namespaceId, mcpName, mcpId); } // 测试查询性能 long startTime = System.nanoTime(); for (int i = 0; i < 10000; i++) { String namespaceId = "namespace-" + (i % 10); String mcpName = "mcp-" + (i % size); cacheIndex.getMcpId(namespaceId, mcpName); } long endTime = System.nanoTime(); long duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime); System.out.printf("查询 10000 次操作耗时: %d ms, 平均: %.2f μs\n", duration, (duration * 1000.0) / 10000); // 显示内存使用情况 Runtime runtime = Runtime.getRuntime(); long memoryUsed = runtime.totalMemory() - runtime.freeMemory(); System.out.printf("内存使用: %.2f MB\n", memoryUsed / (1024.0 * 1024.0)); } } /** * 验证修复效果:确保生成的键数量在合理范围内. */ @Test void testKeyGenerationValidation() { System.out.println("\n=== 键生成验证测试 ==="); int threadCount = 10; int operationCount = 10000; int cacheSize = 1000; int effectiveKeyCount = 800; // 模拟并发更新测试中的键生成逻辑 Set generatedKeys = new HashSet<>(); for (int t = 0; t < threadCount; t++) { final int threadId = t; for (int i = 0; i < operationCount; i++) { int keyIndex; if (i % 5 == 0) { // 20%的操作:创建新键(在有效范围内) keyIndex = (threadId * 50 + i) % effectiveKeyCount; } else { // 80%的操作:更新现有键 keyIndex = i % effectiveKeyCount; } String mcpName = "mcp-" + keyIndex; generatedKeys.add(mcpName); } } System.out.printf("线程数: %d, 操作数: %d, 缓存大小: %d, 有效键数: %d\n", threadCount, operationCount, cacheSize, effectiveKeyCount); System.out.printf("实际生成的唯一键数量: %d\n", generatedKeys.size()); System.out.printf("键数量是否在合理范围内: %s\n", generatedKeys.size() <= effectiveKeyCount ? "是" : "否"); // 验证键的范围 boolean allKeysInRange = generatedKeys.stream().allMatch(key -> { try { int keyNum = Integer.parseInt(key.substring(4)); // 去掉"mcp-"前缀 return keyNum >= 0 && keyNum < effectiveKeyCount; } catch (NumberFormatException e) { return false; } }); System.out.printf("所有键都在有效范围内: %s\n", allKeysInRange ? "是" : "否"); // 断言验证 assertTrue(generatedKeys.size() <= effectiveKeyCount, "生成的键数量应该不超过有效键数量"); assertTrue(allKeysInRange, "所有生成的键都应该在有效范围内"); // 验证修复效果:键数量应该远小于原来的100,000 assertTrue(generatedKeys.size() < 1000, "修复后生成的键数量应该远小于原来的100,000"); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/index/MemoryMcpCacheIndexTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import com.alibaba.nacos.ai.config.McpCacheIndexProperties; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.test.util.ReflectionTestUtils; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class MemoryMcpCacheIndexTest { private MemoryMcpCacheIndex cache; private McpCacheIndexProperties props; @BeforeEach void setUp() { props = new McpCacheIndexProperties(); props.setMaxSize(3); props.setExpireTimeSeconds(2); // 2秒过期 cache = new MemoryMcpCacheIndex(props); } @Test void testPutAndGet() { cache.updateIndex("ns", "name", "id1"); assertEquals("id1", cache.getMcpId("ns", "name")); McpServerIndexData data = cache.getMcpServerById("id1"); assertNotNull(data); assertEquals("id1", data.getId()); assertEquals("ns", data.getNamespaceId()); } @Test void testRemoveByNameAndId() { cache.updateIndex("ns", "name", "id1"); cache.removeIndex("ns", "name"); assertNull(cache.getMcpId("ns", "name")); assertNull(cache.getMcpServerById("id1")); cache.updateIndex("ns", "name", "id2"); cache.removeIndex("id2"); assertNull(cache.getMcpId("ns", "name")); assertNull(cache.getMcpServerById("id2")); } @Test void testClear() { cache.updateIndex("ns1", "a", "id1"); cache.updateIndex("ns2", "b", "id2"); cache.clear(); assertEquals(0, cache.getSize()); assertNull(cache.getMcpId("ns1", "a")); assertNull(cache.getMcpId("ns2", "b")); } @Test void testLruEviction() { cache.updateIndex("ns", "a", "id1"); cache.updateIndex("ns", "b", "id2"); cache.updateIndex("ns", "c", "id3"); // 访问id1,保持活跃 cache.getMcpServerById("id1"); // 插入新元素,应该淘汰id2(最久未访问) cache.updateIndex("ns", "d", "id4"); assertNull(cache.getMcpServerById("id2")); assertNotNull(cache.getMcpServerById("id1")); assertNotNull(cache.getMcpServerById("id3")); assertNotNull(cache.getMcpServerById("id4")); assertEquals(3, cache.getSize()); } @Test void testExpire() throws InterruptedException { cache.updateIndex("ns", "a", "id1"); Thread.sleep(2100); // 超过2秒 assertNull(cache.getMcpServerById("id1")); assertNull(cache.getMcpId("ns", "a")); assertEquals(0, cache.getSize()); } @Test void testUpdateExistingEntry() { cache.updateIndex("ns", "a", "id1"); cache.updateIndex("ns", "a", "id1"); // 再次put assertEquals("id1", cache.getMcpId("ns", "a")); assertEquals(1, cache.getSize()); } @Test void testStats() { cache.updateIndex("ns", "a", "id1"); cache.getMcpId("ns", "a"); // hit cache.getMcpId("ns", "b"); // miss cache.getMcpId("ns", "a"); // hit McpCacheIndex.CacheStats stats = cache.getStats(); assertEquals(2, stats.getHitCount()); assertEquals(1, stats.getMissCount()); assertEquals(0, stats.getEvictionCount()); assertEquals(1, stats.getSize()); } @Test void testConcurrentPutAndGet() throws InterruptedException { int threadCount = 3; // 减少线程数 int opCount = 10; // 减少操作数 ExecutorService executor = Executors.newFixedThreadPool(threadCount); CountDownLatch latch = new CountDownLatch(threadCount); for (int i = 0; i < threadCount; i++) { int idx = i; executor.submit(() -> { try { for (int j = 0; j < opCount; j++) { String ns = "ns" + (idx % 3); String name = "name" + (j % 5); String id = "id" + (idx * opCount + j); cache.updateIndex(ns, name, id); cache.getMcpId(ns, name); } } finally { latch.countDown(); } }); } // 增加等待时间 boolean completed = latch.await(10, TimeUnit.SECONDS); assertTrue(completed, "All threads should complete within timeout"); // 关闭线程池并等待所有任务完成 executor.shutdown(); boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS); assertTrue(terminated, "Executor should terminate within timeout"); // 验证缓存大小不超过限制,并且缓存功能正常 int finalSize = cache.getSize(); assertTrue(finalSize <= props.getMaxSize(), "Cache size " + finalSize + " should not exceed maxSize " + props.getMaxSize()); // 验证缓存仍然可以正常工作 if (finalSize > 0) { // 尝试获取一个存在的key,验证缓存功能 cache.updateIndex("test", "test", "test-id"); String result = cache.getMcpId("test", "test"); assertNotNull(result, "Cache should still work after concurrent operations"); } } @Test void testRemoveNonExist() { cache.removeIndex("ns", "not-exist"); cache.removeIndex("not-exist-id"); // 不抛异常 } @Test void testPutNullOrBlank() { cache.updateIndex(null, "a", "id1"); cache.updateIndex("ns", null, "id1"); cache.updateIndex("ns", "a", null); assertNull(cache.getMcpId(null, "a")); assertNull(cache.getMcpId("ns", null)); assertNull(cache.getMcpId("ns", "a")); assertEquals(0, cache.getSize()); } @Test void testEvictionCount() { cache.updateIndex("ns", "a", "id1"); cache.updateIndex("ns", "b", "id2"); cache.updateIndex("ns", "c", "id3"); cache.updateIndex("ns", "d", "id4"); // 淘汰1个 assertEquals(1, cache.getStats().getEvictionCount()); } @Test void testExpireDoesNotAffectOthers() throws InterruptedException { cache.updateIndex("ns", "a", "id1"); cache.updateIndex("ns", "b", "id2"); Thread.sleep(2100); assertNull(cache.getMcpServerById("id1")); assertNull(cache.getMcpServerById("id2")); assertEquals(0, cache.getSize()); } @Test void testGetMcpServerByName() { cache.updateIndex("ns", "a", "id1"); McpServerIndexData data = cache.getMcpServerByName("ns", "a"); assertNotNull(data); assertEquals("id1", data.getId()); assertEquals("ns", data.getNamespaceId()); assertNull(cache.getMcpServerByName("ns", "not-exist")); } // 新增缓存删除功能测试 @Test void testRemoveByNameDeletesCorrectMapping() { // 准备多个映射 cache.updateIndex("ns1", "name1", "id1"); cache.updateIndex("ns1", "name2", "id2"); cache.updateIndex("ns2", "name1", "id3"); // 删除特定命名空间的特定名称 cache.removeIndex("ns1", "name1"); // 验证只有正确的映射被删除 assertNull(cache.getMcpId("ns1", "name1")); assertNull(cache.getMcpServerById("id1")); // 验证其他映射仍然存在 assertEquals("id2", cache.getMcpId("ns1", "name2")); assertEquals("id3", cache.getMcpId("ns2", "name1")); assertNotNull(cache.getMcpServerById("id2")); assertNotNull(cache.getMcpServerById("id3")); assertEquals(2, cache.getSize()); } @Test void testRemoveByIdDeletesAllRelatedMappings() { // 准备数据,一个ID对应一个名称映射 cache.updateIndex("ns1", "name1", "id1"); cache.updateIndex("ns1", "name2", "id2"); cache.updateIndex("ns2", "name1", "id3"); // 通过ID删除 cache.removeIndex("id2"); // 验证ID对应的数据被删除 assertNull(cache.getMcpServerById("id2")); // 验证名称到ID的映射也被清理 assertNull(cache.getMcpId("ns1", "name2")); // 验证其他数据仍然存在 assertEquals("id1", cache.getMcpId("ns1", "name1")); assertEquals("id3", cache.getMcpId("ns2", "name1")); assertNotNull(cache.getMcpServerById("id1")); assertNotNull(cache.getMcpServerById("id3")); assertEquals(2, cache.getSize()); } @Test void testRemoveWithInvalidMappingConsistency() { // 准备数据 cache.updateIndex("ns1", "name1", "id1"); // 验证数据存在 assertEquals("id1", cache.getMcpId("ns1", "name1")); assertNotNull(cache.getMcpServerById("id1")); // 删除名称映射 cache.removeIndex("ns1", "name1"); // 验证数据被完全清理 assertNull(cache.getMcpId("ns1", "name1")); assertNull(cache.getMcpServerById("id1")); assertEquals(0, cache.getSize()); } @Test void testRemoveNonExistentEntries() { // 添加一些数据 cache.updateIndex("ns1", "name1", "id1"); // 尝试删除不存在的条目 cache.removeIndex("non-exist-ns", "non-exist-name"); cache.removeIndex("non-exist-id"); // 验证原有数据不受影响 assertEquals("id1", cache.getMcpId("ns1", "name1")); assertNotNull(cache.getMcpServerById("id1")); assertEquals(1, cache.getSize()); } @Test void testRemoveWithNullParameters() { // 添加一些数据 cache.updateIndex("ns1", "name1", "id1"); // 尝试用null参数删除 cache.removeIndex(null, null); cache.removeIndex("ns1", null); cache.removeIndex(null, "name1"); cache.removeIndex((String) null); // 验证原有数据不受影响 assertEquals("id1", cache.getMcpId("ns1", "name1")); assertNotNull(cache.getMcpServerById("id1")); assertEquals(1, cache.getSize()); } @Test void testRemoveWithEmptyParameters() { // 添加一些数据,包括空字符串键 cache.updateIndex("ns1", "name1", "id1"); cache.updateIndex("", "", "id2"); cache.updateIndex("ns2", "", "id3"); // 删除空字符串键 cache.removeIndex("", ""); // 验证空字符串键的数据被删除 assertNull(cache.getMcpId("", "")); assertNull(cache.getMcpServerById("id2")); // 验证其他数据不受影响 assertEquals("id1", cache.getMcpId("ns1", "name1")); assertNull(cache.getMcpId("ns2", "")); assertEquals(1, cache.getSize()); } @Test void testRemoveAfterEviction() { // 填满缓存 cache.updateIndex("ns", "name1", "id1"); cache.updateIndex("ns", "name2", "id2"); cache.updateIndex("ns", "name3", "id3"); // 添加新元素触发淘汰 cache.updateIndex("ns", "name4", "id4"); // id1应该被淘汰了(LRU) assertNull(cache.getMcpServerById("id1")); assertEquals(3, cache.getSize()); // 尝试删除已被淘汰的元素 cache.removeIndex("ns", "name1"); cache.removeIndex("id1"); // 验证缓存大小和其他元素不受影响 assertEquals(3, cache.getSize()); assertNotNull(cache.getMcpServerById("id2")); assertNotNull(cache.getMcpServerById("id3")); assertNotNull(cache.getMcpServerById("id4")); } @Test void testConcurrentRemoveOperations() throws InterruptedException { int threadCount = 3; int opCount = 10; ExecutorService executor = Executors.newFixedThreadPool(threadCount); CountDownLatch latch = new CountDownLatch(threadCount); // 先填充数据 for (int i = 0; i < threadCount * opCount; i++) { cache.updateIndex("ns" + (i % 3), "name" + i, "id" + i); } int initialSize = cache.getSize(); // 并发删除操作 for (int i = 0; i < threadCount; i++) { int threadIndex = i; executor.submit(() -> { try { for (int j = 0; j < opCount; j++) { int index = threadIndex * opCount + j; if (index % 2 == 0) { // 通过名称删除 cache.removeIndex("ns" + (index % 3), "name" + index); } else { // 通过ID删除 cache.removeIndex("id" + index); } } } finally { latch.countDown(); } }); } // 等待所有线程完成 boolean completed = latch.await(60, TimeUnit.SECONDS); assertTrue(completed, "All threads should complete within timeout"); executor.shutdown(); boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS); assertTrue(terminated, "Executor should terminate within timeout"); // 验证所有数据都被删除 assertEquals(0, cache.getSize()); // 验证缓存仍然可以正常工作 cache.updateIndex("test", "test", "test-id"); assertEquals("test-id", cache.getMcpId("test", "test")); assertEquals(1, cache.getSize()); } // 补充测试用例 @Test void testGetMcpIdWithInvalidParameters() { // 测试null参数 assertNull(cache.getMcpId(null, "name")); assertNull(cache.getMcpId("namespace", null)); assertNull(cache.getMcpId(null, null)); // 测试空字符串参数 assertNull(cache.getMcpId("", "name")); assertNull(cache.getMcpId("namespace", "")); assertNull(cache.getMcpId("", "")); } @Test void testGetMcpIdWithNonExistentEntry() { // 测试不存在的条目 assertNull(cache.getMcpId("non-existent-namespace", "non-existent-name")); } @Test void testGetMcpIdWithExpiredEntry() throws InterruptedException { // 添加一个条目 cache.updateIndex("ns", "name", "id1"); assertEquals("id1", cache.getMcpId("ns", "name")); // 等待过期 Thread.sleep(2100); // 再次获取应该返回null assertNull(cache.getMcpId("ns", "name")); } @Test void testGetMcpServerByIdWithInvalidParameters() { // 测试null参数 assertNull(cache.getMcpServerById(null)); // 测试空字符串参数 assertNull(cache.getMcpServerById("")); } @Test void testGetMcpServerByIdWithNonExistentEntry() { // 测试不存在的条目 assertNull(cache.getMcpServerById("non-existent-id")); } @Test void testGetMcpServerByIdWithExpiredEntry() throws InterruptedException { // 添加一个条目 cache.updateIndex("ns", "name", "id1"); assertNotNull(cache.getMcpServerById("id1")); // 等待过期 Thread.sleep(2100); // 再次获取应该返回null assertNull(cache.getMcpServerById("id1")); } @Test void testGetMcpServerByIdUpdatesLru() { // 填满缓存 cache.updateIndex("ns", "name1", "id1"); cache.updateIndex("ns", "name2", "id2"); cache.updateIndex("ns", "name3", "id3"); // 访问id1,使其成为最近使用的 assertNotNull(cache.getMcpServerById("id1")); // 添加新元素,应该淘汰id2而不是id1 cache.updateIndex("ns", "name4", "id4"); // 验证id1仍然存在,id2被淘汰 assertNotNull(cache.getMcpServerById("id1")); assertNull(cache.getMcpServerById("id2")); assertNotNull(cache.getMcpServerById("id3")); assertNotNull(cache.getMcpServerById("id4")); } @Test void testShutdown() { // 添加一些数据 cache.updateIndex("ns", "name", "id1"); assertEquals(1, cache.getSize()); // 调用shutdown cache.shutdown(); // 验证缓存被清空 assertEquals(0, cache.getSize()); assertNull(cache.getMcpId("ns", "name")); assertNull(cache.getMcpServerById("id1")); } @Test void testShutdownTimeout() throws InterruptedException { ScheduledExecutorService executorService = (ScheduledExecutorService) ReflectionTestUtils.getField(cache, "cleanupScheduler"); executorService.shutdownNow(); ScheduledExecutorService mockExecutorService = Mockito.mock(ScheduledExecutorService.class); ReflectionTestUtils.setField(cache, "cleanupScheduler", mockExecutorService); cache.shutdown(); verify(mockExecutorService).shutdownNow(); } @Test void testShutdownWithInterruptedException() throws InterruptedException { ScheduledExecutorService executorService = (ScheduledExecutorService) ReflectionTestUtils.getField(cache, "cleanupScheduler"); executorService.shutdownNow(); ScheduledExecutorService mockExecutorService = Mockito.mock(ScheduledExecutorService.class); when(mockExecutorService.awaitTermination(anyLong(), any())).thenThrow(new InterruptedException()); ReflectionTestUtils.setField(cache, "cleanupScheduler", mockExecutorService); cache.shutdown(); verify(mockExecutorService).shutdownNow(); } @Test void testDuplicateShutdown() throws InterruptedException { cache.shutdown(); ScheduledExecutorService mockExecutorService = Mockito.mock(ScheduledExecutorService.class); ReflectionTestUtils.setField(cache, "cleanupScheduler", mockExecutorService); cache.shutdown(); verify(mockExecutorService, never()).awaitTermination(anyLong(), any()); } @Test void testNoExpiredEntries() throws InterruptedException { McpCacheIndexProperties shortExpireProps = new McpCacheIndexProperties(); shortExpireProps.setMaxSize(100); shortExpireProps.setExpireTimeSeconds(-1); // 1秒过期 shortExpireProps.setCleanupIntervalSeconds(1); MemoryMcpCacheIndex noExpireCache = new MemoryMcpCacheIndex(shortExpireProps); try { noExpireCache.updateIndex("ns", "name", "id1"); assertEquals("id1", noExpireCache.getMcpId("ns", "name")); Thread.sleep(1500); assertEquals("id1", noExpireCache.getMcpId("ns", "name")); } finally { noExpireCache.shutdown(); } } @Test void testCleanupExpiredEntries() throws InterruptedException { // 创建一个具有短过期时间的缓存实例 McpCacheIndexProperties shortExpireProps = new McpCacheIndexProperties(); shortExpireProps.setMaxSize(100); shortExpireProps.setExpireTimeSeconds(1); // 1秒过期 shortExpireProps.setCleanupIntervalSeconds(1); MemoryMcpCacheIndex shortExpireCache = new MemoryMcpCacheIndex(shortExpireProps); try { // 添加一些条目 shortExpireCache.updateIndex("ns1", "name1", "id1"); shortExpireCache.updateIndex("ns2", "name2", "id2"); shortExpireCache.updateIndex("ns3", "name3", "id3"); // 验证条目存在 assertEquals("id1", shortExpireCache.getMcpId("ns1", "name1")); assertEquals("id2", shortExpireCache.getMcpId("ns2", "name2")); assertEquals("id3", shortExpireCache.getMcpId("ns3", "name3")); // 等待过期和清理 Thread.sleep(1500); // 触发清理(通过获取来间接触发) shortExpireCache.getMcpId("ns1", "name1"); // 验证过期条目被清理 assertNull(shortExpireCache.getMcpId("ns1", "name1")); assertNull(shortExpireCache.getMcpId("ns2", "name2")); assertNull(shortExpireCache.getMcpId("ns3", "name3")); // 验证统计信息 McpCacheIndex.CacheStats stats = shortExpireCache.getStats(); assertEquals(0, stats.getSize()); } finally { shortExpireCache.shutdown(); } } @Test void testCleanupExpiredEntriesDoesNotAffectValidEntries() throws InterruptedException { // 创建一个具有不同过期时间的缓存实例 McpCacheIndexProperties mixedProps = new McpCacheIndexProperties(); mixedProps.setMaxSize(100); mixedProps.setExpireTimeSeconds(2); // 2秒过期 mixedProps.setCleanupIntervalSeconds(1); MemoryMcpCacheIndex mixedCache = new MemoryMcpCacheIndex(mixedProps); try { // 添加一些条目 mixedCache.updateIndex("ns1", "name1", "id1"); // 这个会过期 Thread.sleep(1100); // 等待1.1秒 mixedCache.updateIndex("ns2", "name2", "id2"); // 这个不会过期 // 验证两个条目都存在 assertEquals("id1", mixedCache.getMcpId("ns1", "name1")); assertEquals("id2", mixedCache.getMcpId("ns2", "name2")); // 再等待1.1秒,使第一个条目过期但第二个不过期 Thread.sleep(1100); // 触发清理(通过获取来间接触发) mixedCache.getMcpId("ns1", "name1"); // 验证只有过期的条目被清理 assertNull(mixedCache.getMcpId("ns1", "name1")); assertEquals("id2", mixedCache.getMcpId("ns2", "name2")); // 验证统计数据 McpCacheIndex.CacheStats stats = mixedCache.getStats(); assertEquals(1, stats.getSize()); } finally { mixedCache.shutdown(); } } @Test void testConcurrentAccessDuringCleanup() throws InterruptedException { // 创建一个具有短过期时间的缓存实例 McpCacheIndexProperties concurrentProps = new McpCacheIndexProperties(); concurrentProps.setMaxSize(100); concurrentProps.setExpireTimeSeconds(1); // 1秒过期 concurrentProps.setCleanupIntervalSeconds(1); MemoryMcpCacheIndex concurrentCache = new MemoryMcpCacheIndex(concurrentProps); try { int threadCount = 5; int opCount = 20; ExecutorService executor = Executors.newFixedThreadPool(threadCount); CountDownLatch latch = new CountDownLatch(threadCount); // 添加初始数据 for (int i = 0; i < opCount; i++) { concurrentCache.updateIndex("ns", "name" + i, "id" + i); } // 并发执行读写和清理操作 for (int i = 0; i < threadCount; i++) { final int threadIndex = i; executor.submit(() -> { try { for (int j = 0; j < opCount; j++) { int index = threadIndex * opCount + j; // 读取操作 concurrentCache.getMcpId("ns", "name" + (index % opCount)); concurrentCache.getMcpServerById("id" + (index % opCount)); // 写入操作 concurrentCache.updateIndex("ns", "newname" + index, "newid" + index); // 删除操作 if (index % 2 == 0) { concurrentCache.removeIndex("ns", "newname" + index); } else { concurrentCache.removeIndex("newid" + index); } } } catch (Exception e) { // 忽略并发访问中可能发生的异常 } finally { latch.countDown(); } }); } // 等待所有线程完成 boolean completed = latch.await(30, TimeUnit.SECONDS); assertTrue(completed, "All threads should complete within timeout"); executor.shutdown(); boolean terminated = executor.awaitTermination(10, TimeUnit.SECONDS); assertTrue(terminated, "Executor should terminate within timeout"); // 验证缓存仍然可以正常工作 concurrentCache.updateIndex("final", "final", "final-id"); assertEquals("final-id", concurrentCache.getMcpId("final", "final")); } finally { concurrentCache.shutdown(); } } @Test void testCleanupExpiredEntriesWithException() throws InterruptedException { McpCacheIndexProperties concurrentProps = new McpCacheIndexProperties(); concurrentProps.setMaxSize(100); concurrentProps.setExpireTimeSeconds(1); // 1秒过期 concurrentProps.setCleanupIntervalSeconds(1); MemoryMcpCacheIndex testCache = new MemoryMcpCacheIndex(concurrentProps); try { testCache.updateIndex("ns", "name", "id"); ReflectionTestUtils.setField(testCache, "properties", null); TimeUnit.MILLISECONDS.sleep(1100); } finally { testCache.shutdown(); } } @Test void testCleanupExpiredEntriesAfterShutdown() throws InterruptedException { McpCacheIndexProperties concurrentProps = new McpCacheIndexProperties(); concurrentProps.setMaxSize(100); concurrentProps.setExpireTimeSeconds(1); // 1秒过期 concurrentProps.setCleanupIntervalSeconds(1); MemoryMcpCacheIndex testCache = new MemoryMcpCacheIndex(concurrentProps); try { ReflectionTestUtils.setField(testCache, "shutdown", true); TimeUnit.MILLISECONDS.sleep(1100); } finally { ScheduledExecutorService cleanupScheduler = (ScheduledExecutorService) ReflectionTestUtils.getField(testCache, "cleanupScheduler"); cleanupScheduler.shutdownNow(); } } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/index/PlainMcpServerIndexTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.index; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerVersionInfo; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.core.service.NamespaceOperationService; 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.junit.jupiter.MockitoExtension; import java.util.LinkedList; import java.util.List; import java.util.UUID; 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.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class PlainMcpServerIndexTest { @Mock private ConfigDetailService configDetailService; @Mock private NamespaceOperationService namespaceOperationService; @Mock private ConfigQueryChainService configQueryChainService; PlainMcpServerIndex plainMcpServerIndex; @BeforeEach void setUp() { plainMcpServerIndex = new PlainMcpServerIndex(namespaceOperationService, configDetailService, configQueryChainService); } @AfterEach void tearDown() { } @Test void searchMcpServerByNameWithNamespaceIdByAccurateNotFound() { Page searchPage = mockConfigInfo(0, 0, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(searchPage); Page result = plainMcpServerIndex.searchMcpServerByNameWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", Constants.MCP_LIST_SEARCH_ACCURATE, 1, 10); assertEquals(0, result.getTotalCount()); assertEquals(1, result.getPageNumber()); assertEquals(0, result.getPagesAvailable()); assertEquals(0, result.getPageItems().size()); } @Test void searchMcpServerByNameWithNamespaceIdByAccurate() { Page searchPage = mockConfigInfo(1, 1, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(10), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(searchPage); Page result = plainMcpServerIndex.searchMcpServerByNameWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", Constants.MCP_LIST_SEARCH_ACCURATE, 1, 10); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageNumber()); assertEquals(1, result.getPagesAvailable()); assertEquals(1, result.getPageItems().size()); } @Test void searchMcpServerByNameWithNamespaceIdByBlur() { Page searchPage = mockConfigInfo(10, 10, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), eq("*"), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(searchPage); Page result = plainMcpServerIndex.searchMcpServerByNameWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 1, 10); assertEquals(10, result.getTotalCount()); assertEquals(1, result.getPageNumber()); assertEquals(1, result.getPagesAvailable()); assertEquals(10, result.getPageItems().size()); } @Test void searchMcpServerByNameWithMultiplePagesFirstPage() { Page searchPage = mockConfigInfoWithPagination(25, 10, 1, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(1), eq(10), eq("*"), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(searchPage); Page result = plainMcpServerIndex.searchMcpServerByNameWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 1, 10); assertEquals(25, result.getTotalCount()); assertEquals(1, result.getPageNumber()); assertEquals(3, result.getPagesAvailable()); assertEquals(10, result.getPageItems().size()); } @Test void searchMcpServerByNameWithMultiplePagesSecondPage() { Page searchPage = mockConfigInfoWithPagination(25, 10, 2, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(2), eq(10), eq("*"), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(searchPage); Page result = plainMcpServerIndex.searchMcpServerByNameWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 2, 10); assertEquals(25, result.getTotalCount()); assertEquals(2, result.getPageNumber()); assertEquals(3, result.getPagesAvailable()); assertEquals(10, result.getPageItems().size()); } @Test void searchMcpServerByNameWithMultiplePagesLastPage() { Page searchPage = mockConfigInfoWithPagination(25, 5, 3, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_BLUR), eq(3), eq(10), eq("*"), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(searchPage); Page result = plainMcpServerIndex.searchMcpServerByNameWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_BLUR, 3, 10); assertEquals(25, result.getTotalCount()); assertEquals(3, result.getPageNumber()); assertEquals(3, result.getPagesAvailable()); assertEquals(5, result.getPageItems().size()); } @Test void getMcpServerByIdWithEmptyId() { assertNull(plainMcpServerIndex.getMcpServerById("")); } @Test void getMcpServerByIdNotFound() { String id = UUID.randomUUID().toString(); when(namespaceOperationService.getNamespaceList()).thenReturn(mockNamespaceList(1, false)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(null)); assertNull(plainMcpServerIndex.getMcpServerById(id)); } @Test void getMcpServerByIdFound() { String id = UUID.randomUUID().toString(); when(namespaceOperationService.getNamespaceList()).thenReturn(mockNamespaceList(1, false)); McpServerBasicInfo mcpServerBasicInfo = mockServerVersionInfo(id); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(mcpServerBasicInfo)); McpServerIndexData result = plainMcpServerIndex.getMcpServerById(id); assertEquals(id, result.getId()); assertEquals("namespaceId-0", result.getNamespaceId()); } @Test void getMcpServerByNameNotFound() { Page countPage = mockConfigInfo(0, 0, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(countPage); assertNull(plainMcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName")); } @Test void getMcpServerByNameFound() { Page countPage = mockConfigInfo(1, 1, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(configDetailService.findConfigInfoPage(eq(Constants.MCP_LIST_SEARCH_ACCURATE), eq(1), eq(1), isNull(), eq(Constants.MCP_SERVER_VERSIONS_GROUP), eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), anyMap())).thenReturn(countPage); McpServerIndexData result = plainMcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); assertNotNull(result); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertDoesNotThrow(() -> UUID.fromString(result.getId())); } @Test void removeMcpServerByName() { assertDoesNotThrow( () -> plainMcpServerIndex.removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName")); } @Test void removeMcpServerById() { assertDoesNotThrow(() -> plainMcpServerIndex.removeMcpServerById(UUID.randomUUID().toString())); } private ConfigQueryChainResponse mockConfigQueryChainResponse(Object obj) { ConfigQueryChainResponse mockResponse = new ConfigQueryChainResponse(); if (null != obj) { mockResponse.setContent(JacksonUtils.toJson(obj)); mockResponse.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); } else { mockResponse.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); } return mockResponse; } private List mockNamespaceList(int size, boolean withDefaultNs) { List list = new LinkedList<>(); if (withDefaultNs) { Namespace namespace = new Namespace(); namespace.setNamespace(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); namespace.setNamespaceShowName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); list.add(namespace); } size = withDefaultNs ? size - 1 : size; for (int i = 0; i < size; i++) { Namespace namespace = new Namespace(); namespace.setNamespace("namespaceId-" + i); namespace.setNamespaceShowName("namespace-" + i); list.add(namespace); } return list; } private Page mockConfigInfo(int total, int size, String namespaceId) { Page mockConfigInfo = new Page<>(); mockConfigInfo.setTotalCount(total); mockConfigInfo.setPagesAvailable(size == 0 ? 0 : total / size); mockConfigInfo.setPageNumber(1); List list = new LinkedList<>(); for (int i = 0; i < size; i++) { ConfigInfo configInfo = new ConfigInfo(); configInfo.setTenant(namespaceId); configInfo.setContent(JacksonUtils.toJson(mockServerVersionInfo(UUID.randomUUID().toString()))); list.add(configInfo); } mockConfigInfo.setPageItems(list); return mockConfigInfo; } private Page mockConfigInfoWithPagination(int total, int currentPageSize, int pageNumber, String namespaceId) { Page mockConfigInfo = new Page<>(); mockConfigInfo.setTotalCount(total); mockConfigInfo.setPageNumber(pageNumber); mockConfigInfo.setPagesAvailable((int) Math.ceil((double) total / (double) currentPageSize)); List list = new LinkedList<>(); for (int i = 0; i < currentPageSize; i++) { ConfigInfo configInfo = new ConfigInfo(); configInfo.setTenant(namespaceId); configInfo.setContent(JacksonUtils.toJson(mockServerVersionInfo(UUID.randomUUID().toString()))); list.add(configInfo); } mockConfigInfo.setPageItems(list); return mockConfigInfo; } private McpServerVersionInfo mockServerVersionInfo(String id) { McpServerVersionInfo serverVersionInfo = new McpServerVersionInfo(); serverVersionInfo.setId(id); serverVersionInfo.setName("mcpName"); serverVersionInfo.setLatestPublishedVersion("9.9.9"); List versionDetails = new LinkedList<>(); versionDetails.add(mockVersion("1.0.0")); versionDetails.add(mockVersion("9.9.9")); serverVersionInfo.setVersions(versionDetails); return serverVersionInfo; } private ServerVersionDetail mockVersion(String version) { ServerVersionDetail versionDetail = new ServerVersionDetail(); versionDetail.setVersion(version); return versionDetail; } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/param/AgentHttpParamExtractorTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.param; import com.alibaba.nacos.api.ai.model.a2a.AgentCapabilities; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.JacksonUtils; import jakarta.servlet.http.HttpServletRequest; 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.junit.jupiter.MockitoExtension; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AgentHttpParamExtractorTest { @Mock HttpServletRequest request; AgentHttpParamExtractor httpParamExtractor; @BeforeEach void setUp() { httpParamExtractor = new AgentHttpParamExtractor(); } @Test void extractParamWithNamespaceIdAndAgentName() throws NacosException { String agentName = "testAgent"; when(request.getParameter("namespaceId")).thenReturn("testNs"); when(request.getParameter("agentName")).thenReturn(agentName); when(request.getParameterMap()).thenReturn( Map.of("namespaceId", new String[] {"testNs"}, "agentName", new String[] {agentName})); List actual = httpParamExtractor.extractParam(request); assertEquals(1, actual.size()); assertEquals("testNs", actual.get(0).getNamespaceId()); assertEquals(agentName, actual.get(0).getAgentName()); } @Test void extractParamWithAgentCard() throws NacosException { AgentCard agentCard = new AgentCard(); agentCard.setName("testAgentFromCard"); agentCard.setDescription("Test agent card"); AgentCapabilities capabilities = new AgentCapabilities(); agentCard.setCapabilities(capabilities); String agentCardJson = JacksonUtils.toJson(agentCard); when(request.getParameter("namespaceId")).thenReturn("testNs"); when(request.getParameter("agentName")).thenReturn("shouldBeOverridden"); when(request.getParameter("agentCard")).thenReturn(agentCardJson); when(request.getParameterMap()).thenReturn( Map.of("namespaceId", new String[] {"testNs"}, "agentName", new String[] {"shouldBeOverridden"}, "agentCard", new String[] {agentCardJson})); List actual = httpParamExtractor.extractParam(request); assertEquals(1, actual.size()); assertEquals("testNs", actual.get(0).getNamespaceId()); assertEquals("testAgentFromCard", actual.get(0).getAgentName()); } @Test void extractParamWithInvalidAgentCardJson() throws NacosException { when(request.getParameter("namespaceId")).thenReturn("testNs"); when(request.getParameter("agentName")).thenReturn("testAgent"); when(request.getParameter("agentCard")).thenReturn("{invalidJson"); when(request.getParameterMap()).thenReturn( Map.of("namespaceId", new String[] {"testNs"}, "agentName", new String[] {"testAgent"}, "agentCard", new String[] {"{invalidJson"})); List actual = httpParamExtractor.extractParam(request); assertEquals(1, actual.size()); assertEquals("testNs", actual.get(0).getNamespaceId()); assertEquals("", actual.get(0).getAgentName()); } @Test void extractParamWithEmptyParameters() throws NacosException { when(request.getParameterMap()).thenReturn(new java.util.HashMap<>()); List actual = httpParamExtractor.extractParam(request); assertEquals(1, actual.size()); assertTrue(actual.get(0).getNamespaceId() == null || actual.get(0).getNamespaceId().isEmpty()); assertTrue(actual.get(0).getAgentName() == null || actual.get(0).getAgentName().isEmpty()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/param/McpHttpParamExtractorTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.param; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import jakarta.servlet.http.HttpServletRequest; 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.junit.jupiter.MockitoExtension; import java.util.List; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class McpHttpParamExtractorTest { @Mock HttpServletRequest request; McpHttpParamExtractor httpParamExtractor; @BeforeEach void setUp() { httpParamExtractor = new McpHttpParamExtractor(); } @Test void extractParam() throws NacosException { String id = UUID.randomUUID().toString(); when(request.getParameter("namespaceId")).thenReturn("testNs"); when(request.getParameter("mcpName")).thenReturn("testMcp"); when(request.getParameter("mcpId")).thenReturn(id); List actual = httpParamExtractor.extractParam(request); assertEquals(1, actual.size()); assertEquals(id, actual.get(0).getMcpId()); assertEquals("testNs", actual.get(0).getNamespaceId()); assertEquals("testMcp", actual.get(0).getMcpName()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/McpServerEndpointRequestHandlerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerRemoteServiceConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.McpServerEndpointRequest; import com.alibaba.nacos.api.ai.remote.response.McpServerEndpointResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; 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.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class McpServerEndpointRequestHandlerTest { @Mock private EphemeralClientOperationServiceImpl clientOperationService; @Mock private McpServerOperationService mcpServerOperationService; @Mock private McpServerIndex mcpServerIndex; @Mock private RequestMeta meta; McpServerEndpointRequestHandler requestHandler; @BeforeEach void setUp() { requestHandler = new McpServerEndpointRequestHandler(clientOperationService, mcpServerOperationService, mcpServerIndex); } @AfterEach void tearDown() { } @Test void handleWithInvalidParameters() throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); McpServerEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "parameters `mcpName` can't be empty or null"); } @Test void handleForNotFound() throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setAddress("1.1.1.1"); request.setPort(3306); request.setMcpName("test"); McpServerEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.NOT_FOUND, "MCP server `test` not found in namespaceId: `public`"); } @Test void handleForRegisterEndpoint() throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setAddress("1.1.1.1"); request.setPort(3306); request.setMcpName("test"); request.setVersion("1.0.0"); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); String id = UUID.randomUUID().toString(); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, "1.0.0")).thenReturn(buildMockMcpServerDetailInfo()); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); McpServerEndpointResponse response = requestHandler.handle(request, meta); assertEquals(AiRemoteConstants.REGISTER_ENDPOINT, response.getType()); verify(clientOperationService).registerInstance(any(Service.class), any(Instance.class), eq("TEST_CONNECTION_ID")); } @Test void handleForDeregisterEndpoint() throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setAddress("1.1.1.1"); request.setPort(3306); request.setMcpName("test"); request.setType(AiRemoteConstants.DE_REGISTER_ENDPOINT); String id = UUID.randomUUID().toString(); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null)).thenReturn(buildMockMcpServerDetailInfo()); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); McpServerEndpointResponse response = requestHandler.handle(request, meta); assertEquals(AiRemoteConstants.DE_REGISTER_ENDPOINT, response.getType()); verify(clientOperationService).deregisterInstance(any(Service.class), any(Instance.class), eq("TEST_CONNECTION_ID")); } @Test void handleForInvalidType() throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setAddress("1.1.1.1"); request.setPort(3306); request.setMcpName("test"); request.setType("INVALID_TYPE"); String id = UUID.randomUUID().toString(); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null)).thenReturn(buildMockMcpServerDetailInfo()); McpServerEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "parameter `type` should be registerEndpoint or deregisterEndpoint, but was INVALID_TYPE"); } @Test void handleForRegisterFrontendEndpoint() throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setAddress("1.1.1.1"); request.setPort(3306); request.setMcpName("test"); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); String id = UUID.randomUUID().toString(); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); McpServerDetailInfo detailInfo = buildMockMcpServerDetailInfo(); detailInfo.getRemoteServerConfig() .setFrontEndpointConfigList(Collections.singletonList(new FrontEndpointConfig())); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setEndpointData(detailInfo.getRemoteServerConfig().getServiceRef()); detailInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null)).thenReturn(detailInfo); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); McpServerEndpointResponse response = requestHandler.handle(request, meta); assertEquals(AiRemoteConstants.REGISTER_ENDPOINT, response.getType()); verify(clientOperationService).registerInstance(any(Service.class), any(Instance.class), eq("TEST_CONNECTION_ID")); } @Test void handleForRegisterFrontendEndpointNotFound() throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setAddress("1.1.1.1"); request.setPort(3306); request.setMcpName("test"); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); String id = UUID.randomUUID().toString(); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); McpServerDetailInfo detailInfo = buildMockMcpServerDetailInfo(); detailInfo.getRemoteServerConfig() .setFrontEndpointConfigList(Collections.singletonList(new FrontEndpointConfig())); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); detailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0).setEndpointData("127.0.0.1:8848"); detailInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null)).thenReturn(detailInfo); McpServerEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.NOT_FOUND, "The Mcp Server Ref endpoint service not found."); } McpServerDetailInfo buildMockMcpServerDetailInfo() { McpServerDetailInfo result = new McpServerDetailInfo(); result.setName("test"); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); serviceRef.setGroupName(Constants.MCP_SERVER_ENDPOINT_GROUP); serviceRef.setServiceName("test"); McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); remoteServiceConfig.setServiceRef(serviceRef); result.setRemoteServerConfig(remoteServiceConfig); return result; } private void assertErrorResponse(McpServerEndpointResponse response, int code, String message) { assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(code, response.getErrorCode()); assertEquals(message, response.getMessage()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/QueryMcpServerRequestHandlerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.remote.request.QueryMcpServerRequest; import com.alibaba.nacos.api.ai.remote.response.QueryMcpServerResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.response.ResponseCode; 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.junit.jupiter.MockitoExtension; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class QueryMcpServerRequestHandlerTest { @Mock private McpServerOperationService mcpServerOperationService; @Mock private McpServerIndex mcpServerIndex; QueryMcpServerRequestHandler requestHandler; @BeforeEach void setUp() { requestHandler = new QueryMcpServerRequestHandler(mcpServerOperationService, mcpServerIndex); } @AfterEach void tearDown() { } @Test void handleWithInvalidParam() throws NacosException { QueryMcpServerRequest request = new QueryMcpServerRequest(); QueryMcpServerResponse response = requestHandler.handle(request, null); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.INVALID_PARAM, response.getErrorCode()); assertEquals("parameters `mcpName` can't be empty or null", response.getMessage()); } @Test void handleMcpServerNotFound() throws NacosException { QueryMcpServerRequest request = new QueryMcpServerRequest(); request.setMcpName("test"); QueryMcpServerResponse response = requestHandler.handle(request, null); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.NOT_FOUND, response.getErrorCode()); assertEquals("MCP server `test` not found in namespaceId: `public`", response.getMessage()); } @Test void handle() throws NacosException { QueryMcpServerRequest request = new QueryMcpServerRequest(); request.setMcpName("test"); McpServerIndexData indexData = new McpServerIndexData(); indexData.setId(UUID.randomUUID().toString()); indexData.setNamespaceId("public"); when(mcpServerIndex.getMcpServerByName("public", "test")).thenReturn(indexData); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); when(mcpServerOperationService.getMcpServerDetail("public", indexData.getId(), null, null)).thenReturn( mcpServerDetailInfo); QueryMcpServerResponse response = requestHandler.handle(request, null); assertEquals(mcpServerDetailInfo, response.getMcpServerDetailInfo()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/QueryPromptRequestHandlerTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.service.prompt.PromptClientOperationService; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.remote.request.QueryPromptRequest; import com.alibaba.nacos.api.ai.remote.response.QueryPromptResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.response.ResponseCode; 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.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class QueryPromptRequestHandlerTest { @Mock private PromptClientOperationService promptClientOperationService; private QueryPromptRequestHandler handler; @BeforeEach void setUp() { handler = new QueryPromptRequestHandler(promptClientOperationService); } @Test void handleShouldReturnInvalidParamWhenPromptKeyBlank() { QueryPromptRequest request = new QueryPromptRequest(); QueryPromptResponse response = handler.handle(request, null); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.INVALID_PARAM, response.getErrorCode()); } @Test void handleShouldReturnNotModifiedWhenServiceThrows304() throws NacosException { QueryPromptRequest request = new QueryPromptRequest(); request.setPromptKey("p1"); when(promptClientOperationService.queryPrompt("public", "p1", null, null, null)) .thenThrow(new NacosException(NacosException.NOT_MODIFIED, "prompt data is up to date")); QueryPromptResponse response = handler.handle(request, null); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.NOT_MODIFIED, response.getErrorCode()); } @Test void handleShouldMapPromptFieldsWhenSuccess() throws NacosException { QueryPromptRequest request = new QueryPromptRequest(); request.setPromptKey("p1"); PromptVersionInfo versionInfo = new PromptVersionInfo(); versionInfo.setPromptKey("p1"); versionInfo.setVersion("1.0.0"); versionInfo.setTemplate("hello"); versionInfo.setMd5("m1"); when(promptClientOperationService.queryPrompt("public", "p1", null, null, null)).thenReturn(versionInfo); QueryPromptResponse response = handler.handle(request, null); Prompt prompt = response.getPromptInfo(); assertNotNull(prompt); assertEquals("p1", prompt.getPromptKey()); assertEquals("1.0.0", prompt.getVersion()); assertEquals("hello", prompt.getTemplate()); assertEquals("m1", prompt.getMd5()); } @Test void handleShouldProcessNamespaceBeforeQuery() throws NacosException { QueryPromptRequest request = new QueryPromptRequest(); request.setPromptKey("p1"); request.setNamespaceId(""); when(promptClientOperationService.queryPrompt("public", "p1", null, null, null)).thenReturn(new PromptVersionInfo()); handler.handle(request, null); verify(promptClientOperationService).queryPrompt("public", "p1", null, null, null); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/ReleaseMcpServerRequestHandlerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.service.McpEndpointOperationService; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.ai.remote.request.ReleaseMcpServerRequest; import com.alibaba.nacos.api.ai.remote.response.ReleaseMcpServerResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.naming.core.v2.pojo.Service; 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.junit.jupiter.MockitoExtension; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNotNull; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ReleaseMcpServerRequestHandlerTest { @Mock private McpServerOperationService mcpServerOperationService; @Mock private McpEndpointOperationService endpointOperationService; @Mock private McpServerIndex mcpServerIndex; @Mock private RequestMeta meta; ReleaseMcpServerRequestHandler requestHandler; @BeforeEach void setUp() { requestHandler = new ReleaseMcpServerRequestHandler(mcpServerOperationService, endpointOperationService, mcpServerIndex); } @AfterEach void tearDown() { } @Test void handleWithInvalidParameter() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); ReleaseMcpServerResponse response = requestHandler.handle(request, null); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter 'serverSpecification' type McpServerBasicInfo is not present"); McpServerBasicInfo serverSpecification = new McpServerBasicInfo(); request.setServerSpecification(serverSpecification); response = requestHandler.handle(request, null); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter 'serverSpecification.name' type String is not present"); serverSpecification.setName("test"); response = requestHandler.handle(request, null); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `serverSpecification.versionDetail.version` not present"); ServerVersionDetail serverVersionDetail = new ServerVersionDetail(); serverSpecification.setVersionDetail(serverVersionDetail); response = requestHandler.handle(request, null); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `serverSpecification.versionDetail.version` not present"); } @Test void handleReleaseExistedServerAndVersion() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(false, false)); McpServerDetailInfo detailInfo = buildMockServerDetail(); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenReturn(detailInfo); when(meta.getConnectionId()).thenReturn("111"); ReleaseMcpServerResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.CONFLICT, "Mcp Server test and target version 1.0.0 already exist, do not do release"); } @Test void handleReleaseNewServerForSse() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(false, false)); String id = UUID.randomUUID().toString(); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SERVER_NOT_FOUND, "")); when(endpointOperationService.generateService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test::1.0.0")).thenReturn( Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "test")); when(mcpServerOperationService.createMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(request.getServerSpecification()), isNull(), isNotNull())).thenReturn(id); when(meta.getConnectionId()).thenReturn("111"); ReleaseMcpServerResponse response = requestHandler.handle(request, meta); assertEquals(id, response.getMcpId()); } @Test void handleReleaseNewServerForSseWithSpecifiedEndpoint() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(false, false)); request.setEndpointSpecification(new McpEndpointSpec()); String id = UUID.randomUUID().toString(); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SERVER_NOT_FOUND, "")); when(mcpServerOperationService.createMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(request.getServerSpecification()), isNull(), isNotNull())).thenReturn(id); when(meta.getConnectionId()).thenReturn("111"); ReleaseMcpServerResponse response = requestHandler.handle(request, meta); assertEquals(id, response.getMcpId()); verify(endpointOperationService, never()).generateService(anyString(), anyString()); } @Test void handleReleaseNewServerForStdio() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(true, false)); String id = UUID.randomUUID().toString(); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SERVER_NOT_FOUND, "")); when(mcpServerOperationService.createMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(request.getServerSpecification()), isNull(), isNull())).thenReturn(id); when(meta.getConnectionId()).thenReturn("111"); ReleaseMcpServerResponse response = requestHandler.handle(request, meta); assertEquals(id, response.getMcpId()); } @Test void handleReleaseNewVersionWithoutLatest() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(false, false)); String id = UUID.randomUUID().toString(); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SEVER_VERSION_NOT_FOUND, "")); when(meta.getConnectionId()).thenReturn("111"); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); when(endpointOperationService.generateService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test::1.0.0")).thenReturn( Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "test")); ReleaseMcpServerResponse response = requestHandler.handle(request, meta); assertEquals(id, response.getMcpId()); verify(mcpServerOperationService).updateMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(false), eq(request.getServerSpecification()), isNull(), isNotNull(), eq(false)); } @Test void handleReleaseNewVersionWithoutLatestWithSpecifiedEndpoint() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(false, false)); request.setEndpointSpecification(new McpEndpointSpec()); String id = UUID.randomUUID().toString(); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SEVER_VERSION_NOT_FOUND, "")); when(meta.getConnectionId()).thenReturn("111"); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); ReleaseMcpServerResponse response = requestHandler.handle(request, meta); assertEquals(id, response.getMcpId()); verify(mcpServerOperationService).updateMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(false), eq(request.getServerSpecification()), isNull(), isNotNull(), eq(false)); verify(endpointOperationService, never()).generateService(anyString(), anyString()); } @Test void handleReleaseNewVersionWithLatest() throws NacosException { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(false, true)); String id = UUID.randomUUID().toString(); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.MCP_SEVER_VERSION_NOT_FOUND, "")); when(meta.getConnectionId()).thenReturn("111"); McpServerIndexData indexData = McpServerIndexData.newIndexData(id, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test")).thenReturn(indexData); when(endpointOperationService.generateService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "test::1.0.0")).thenReturn( Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "test")); ReleaseMcpServerResponse response = requestHandler.handle(request, meta); assertEquals(id, response.getMcpId()); verify(mcpServerOperationService).updateMcpServer(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(true), eq(request.getServerSpecification()), isNull(), isNotNull(), eq(false)); } @Test void handleReleaseWithException() throws NacosException { NacosApiException exceptedException = new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.SERVER_ERROR, "test"); ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setServerSpecification(buildMockServerSpecification(false, true)); when(mcpServerOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "test", "1.0.0")).thenThrow(exceptedException); try { requestHandler.handle(request, meta); } catch (NacosApiException e) { assertEquals(exceptedException, e); } } private McpServerBasicInfo buildMockServerSpecification(boolean isStdio, boolean isLatest) { McpServerBasicInfo result = new McpServerBasicInfo(); result.setName("test"); ServerVersionDetail serverVersionDetail = new ServerVersionDetail(); serverVersionDetail.setVersion("1.0.0"); if (isLatest) { serverVersionDetail.setIs_latest(true); } result.setVersionDetail(serverVersionDetail); if (!isStdio) { result.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); result.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); } else { result.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); result.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); } return result; } private McpServerDetailInfo buildMockServerDetail() { McpServerDetailInfo result = new McpServerDetailInfo(); result.setName("test"); ServerVersionDetail serverVersionDetail = new ServerVersionDetail(); serverVersionDetail.setVersion("1.0.0"); result.setVersionDetail(serverVersionDetail); result.setId(UUID.randomUUID().toString()); return result; } private void assertErrorResponse(ReleaseMcpServerResponse response, int code, String message) { assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(code, response.getErrorCode()); assertEquals(message, response.getMessage()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/a2a/AgentEndpointRequestHandlerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.a2a.identity.AgentIdCodecHolder; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.AgentEndpointRequest; import com.alibaba.nacos.api.ai.remote.response.AgentEndpointResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; 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.junit.jupiter.MockitoExtension; import java.util.Map; 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.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AgentEndpointRequestHandlerTest { @Mock private EphemeralClientOperationServiceImpl clientOperationService; @Mock private AgentIdCodecHolder agentIdCodecHolder; @Mock private RequestMeta meta; private AgentEndpointRequestHandler requestHandler; private Instance capturedInstance; @BeforeEach void setUp() { requestHandler = new AgentEndpointRequestHandler(clientOperationService, agentIdCodecHolder); capturedInstance = null; } @AfterEach void tearDown() { } @Test void handleWithInvalidAgentName() throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `agentName` can't be empty or null"); } @Test void handleWithNullEndpoint() throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); request.setAgentName("test"); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `endpoint` can't be null"); } @Test void handleWithEmptyEndpointVersion() throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); request.setAgentName("test"); AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("1.1.1.1"); endpoint.setPort(8080); request.setEndpoint(endpoint); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `endpoint.version` can't be empty or null"); } @Test void handleWithInvalidType() throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); request.setAgentName("test"); AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("1.1.1.1"); endpoint.setPort(8080); endpoint.setVersion("1.0.0"); request.setEndpoint(endpoint); request.setType("INVALID_TYPE"); when(agentIdCodecHolder.encode("test")).thenReturn("test"); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "parameter `type` should be registerEndpoint or deregisterEndpoint, but was INVALID_TYPE"); } @Test void handleForRegisterEndpoint() throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); request.setAgentName("test"); request.setNamespaceId("public"); AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("1.1.1.1"); endpoint.setPort(8080); endpoint.setVersion("1.0.0"); endpoint.setPath("/test"); endpoint.setTransport("JSONRPC"); endpoint.setSupportTls(false); endpoint.setProtocol("HTTP"); endpoint.setQuery("param1=value1¶m2=value2"); request.setEndpoint(endpoint); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); when(agentIdCodecHolder.encode("test")).thenReturn("test"); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); // Mock the registerInstance method to capture the Instance argument doAnswer(invocation -> { capturedInstance = invocation.getArgument(1); validateInstanceMetadata(capturedInstance); return null; }).when(clientOperationService).registerInstance(any(Service.class), any(Instance.class), eq("TEST_CONNECTION_ID")); AgentEndpointResponse response = requestHandler.handle(request, meta); assertEquals(AiRemoteConstants.REGISTER_ENDPOINT, response.getType()); verify(clientOperationService).registerInstance(any(Service.class), any(Instance.class), eq("TEST_CONNECTION_ID")); } @Test void handleForDeregisterEndpoint() throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); request.setAgentName("test"); request.setNamespaceId("public"); AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("1.1.1.1"); endpoint.setPort(8080); endpoint.setVersion("1.0.0"); endpoint.setPath("/test"); endpoint.setTransport("JSONRPC"); endpoint.setSupportTls(false); endpoint.setProtocol("HTTPS"); endpoint.setQuery("token=abc123"); request.setEndpoint(endpoint); request.setType(AiRemoteConstants.DE_REGISTER_ENDPOINT); when(agentIdCodecHolder.encode("test")).thenReturn("test"); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); // Mock the deregisterInstance method to capture the Instance argument doAnswer(invocation -> { capturedInstance = invocation.getArgument(1); validateInstanceMetadata(capturedInstance); return null; }).when(clientOperationService).deregisterInstance(any(Service.class), any(Instance.class), eq("TEST_CONNECTION_ID")); AgentEndpointResponse response = requestHandler.handle(request, meta); assertEquals(AiRemoteConstants.DE_REGISTER_ENDPOINT, response.getType()); verify(clientOperationService).deregisterInstance(any(Service.class), any(Instance.class), eq("TEST_CONNECTION_ID")); } private void assertErrorResponse(AgentEndpointResponse response, int code, String message) { assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(code, response.getErrorCode()); assertEquals(message, response.getMessage()); } private void validateInstanceMetadata(Instance instance) { Map metadata = instance.getMetadata(); assertTrue(metadata.containsKey(Constants.A2A.AGENT_ENDPOINT_PATH_KEY)); assertTrue(metadata.containsKey(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY)); assertTrue(metadata.containsKey(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS)); assertTrue(metadata.containsKey(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY)); assertTrue(metadata.containsKey(Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/a2a/BatchAgentEndpointRequestHandlerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.a2a.identity.AgentIdCodecHolder; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.BatchAgentEndpointRequest; import com.alibaba.nacos.api.ai.remote.response.AgentEndpointResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl; 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.junit.jupiter.MockitoExtension; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; 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.eq; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class BatchAgentEndpointRequestHandlerTest { @Mock private EphemeralClientOperationServiceImpl clientOperationService; @Mock private AgentIdCodecHolder agentIdCodecHolder; @Mock private RequestMeta meta; private BatchAgentEndpointRequestHandler requestHandler; private List capturedInstances; @BeforeEach void setUp() { requestHandler = new BatchAgentEndpointRequestHandler(clientOperationService, agentIdCodecHolder); capturedInstances = null; } @AfterEach void tearDown() { } @Test void handleWithInvalidAgentName() throws NacosException { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `agentName` can't be empty or null"); } @Test void handleWithNullEndpoints() throws NacosException { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); request.setAgentName("test"); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `endpoints` can't be empty or null, if want to deregister, please use deregister API."); } @Test void handleWithEmptyEndpoints() throws NacosException { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); request.setAgentName("test"); request.setEndpoints(Arrays.asList()); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `endpoints` can't be empty or null, if want to deregister, please use deregister API."); } @Test void handleWithEmptyEndpointVersion() throws NacosException { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); request.setAgentName("test"); AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("1.1.1.1"); endpoint.setPort(8080); request.setEndpoints(Arrays.asList(endpoint)); AgentEndpointResponse response = requestHandler.handle(request, meta); assertErrorResponse(response, NacosException.INVALID_PARAM, "Required parameter `endpoint.version` can't be empty or null."); } @Test void handleWithDifferentVersions() throws NacosException { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); request.setAgentName("test"); AgentEndpoint endpoint1 = new AgentEndpoint(); endpoint1.setAddress("1.1.1.1"); endpoint1.setPort(8080); endpoint1.setVersion("1.0.0"); AgentEndpoint endpoint2 = new AgentEndpoint(); endpoint2.setAddress("2.2.2.2"); endpoint2.setPort(9090); endpoint2.setVersion("2.0.0"); request.setEndpoints(Arrays.asList(endpoint1, endpoint2)); AgentEndpointResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.INVALID_PARAM, response.getErrorCode()); assertTrue(response.getMessage() .startsWith("Required parameter `endpoint.version` can't be different, current includes:")); assertTrue(response.getMessage().contains("1.0.0")); assertTrue(response.getMessage().contains("2.0.0")); } @Test void handleForBatchRegisterEndpoint() throws NacosException { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); request.setAgentName("test"); request.setNamespaceId("public"); AgentEndpoint endpoint1 = new AgentEndpoint(); endpoint1.setAddress("1.1.1.1"); endpoint1.setPort(8080); endpoint1.setVersion("1.0.0"); endpoint1.setPath("/test1"); endpoint1.setTransport("JSONRPC"); endpoint1.setSupportTls(false); endpoint1.setProtocol("HTTP"); endpoint1.setQuery("param1=value1"); AgentEndpoint endpoint2 = new AgentEndpoint(); endpoint2.setAddress("2.2.2.2"); endpoint2.setPort(9090); endpoint2.setVersion("1.0.0"); endpoint2.setPath("/test2"); endpoint2.setTransport("GRPC"); endpoint2.setSupportTls(true); endpoint2.setProtocol("HTTPS"); endpoint2.setQuery("param2=value2"); Collection endpoints = Arrays.asList(endpoint1, endpoint2); request.setEndpoints(endpoints); when(agentIdCodecHolder.encode("test")).thenReturn("test"); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); // Mock the batchRegisterInstance method to capture the Instance list argument doAnswer(invocation -> { capturedInstances = invocation.getArgument(1); for (Instance instance : capturedInstances) { validateInstanceMetadata(instance); } return null; }).when(clientOperationService) .batchRegisterInstance(any(Service.class), any(List.class), eq("TEST_CONNECTION_ID")); AgentEndpointResponse response = requestHandler.handle(request, meta); assertEquals(AiRemoteConstants.BATCH_REGISTER_ENDPOINT, response.getType()); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); verify(clientOperationService).batchRegisterInstance(any(Service.class), any(List.class), eq("TEST_CONNECTION_ID")); // Verify captured instances assertEquals(2, capturedInstances.size()); Instance instance1 = capturedInstances.get(0); assertEquals("1.1.1.1", instance1.getIp()); assertEquals(8080, instance1.getPort()); Instance instance2 = capturedInstances.get(1); assertEquals("2.2.2.2", instance2.getIp()); assertEquals(9090, instance2.getPort()); } private void assertErrorResponse(AgentEndpointResponse response, int code, String message) { assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(code, response.getErrorCode()); assertEquals(message, response.getMessage()); } private void validateInstanceMetadata(Instance instance) { Map metadata = instance.getMetadata(); assertTrue(metadata.containsKey(Constants.A2A.AGENT_ENDPOINT_PATH_KEY)); assertTrue(metadata.containsKey(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY)); assertTrue(metadata.containsKey(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS)); assertTrue(metadata.containsKey(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY)); assertTrue(metadata.containsKey(Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/a2a/QueryAgentCardRequestHandlerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.service.a2a.A2aServerOperationService; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.remote.request.QueryAgentCardRequest; import com.alibaba.nacos.api.ai.remote.response.QueryAgentCardResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; 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.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class QueryAgentCardRequestHandlerTest { @Mock private A2aServerOperationService a2aServerOperationService; @Mock private RequestMeta meta; private QueryAgentCardRequestHandler requestHandler; @BeforeEach void setUp() { requestHandler = new QueryAgentCardRequestHandler(a2aServerOperationService); } @AfterEach void tearDown() { } @Test void handleWithInvalidAgentName() throws NacosException { QueryAgentCardRequest request = new QueryAgentCardRequest(); QueryAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.INVALID_PARAM, response.getErrorCode()); assertEquals("parameters `agentName` can't be empty or null", response.getMessage()); } @Test void handleWithValidParameters() throws NacosException { QueryAgentCardRequest request = new QueryAgentCardRequest(); request.setAgentName("test"); request.setNamespaceId("public"); AgentCardDetailInfo mockAgentCard = new AgentCardDetailInfo(); mockAgentCard.setName("test"); when(a2aServerOperationService.getAgentCard("public", "test", null, null)).thenReturn(mockAgentCard); QueryAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(mockAgentCard, response.getAgentCardDetailInfo()); assertNull(response.getMessage()); } @Test void handleWithException() throws NacosException { QueryAgentCardRequest request = new QueryAgentCardRequest(); request.setAgentName("test"); request.setNamespaceId("public"); when(a2aServerOperationService.getAgentCard("public", "test", null, null)).thenThrow( new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.SERVER_ERROR, "test error")); QueryAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.SERVER_ERROR, response.getErrorCode()); assertEquals("test error", response.getMessage()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/handler/a2a/ReleaseAgentCardRequestHandlerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.handler.a2a; import com.alibaba.nacos.ai.service.a2a.A2aServerOperationService; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.remote.request.ReleaseAgentCardRequest; import com.alibaba.nacos.api.ai.remote.response.ReleaseAgentCardResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; 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.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ReleaseAgentCardRequestHandlerTest { @Mock private A2aServerOperationService a2aServerOperationService; @Mock private RequestMeta meta; private ReleaseAgentCardRequestHandler requestHandler; @BeforeEach void setUp() { requestHandler = new ReleaseAgentCardRequestHandler(a2aServerOperationService); } @AfterEach void tearDown() { } @Test void handleWithNullAgentCard() throws NacosException { ReleaseAgentCardRequest request = new ReleaseAgentCardRequest(); ReleaseAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.INVALID_PARAM, response.getErrorCode()); assertEquals("parameters `agentCard` can't be null", response.getMessage()); } @Test void handleWithValidNewAgentCard() throws NacosException { final ReleaseAgentCardRequest request = new ReleaseAgentCardRequest(); AgentCard agentCard = new AgentCard(); agentCard.setName("test"); agentCard.setVersion("1.0.0"); agentCard.setProtocolVersion("0.3.0"); agentCard.setPreferredTransport("JSONRPC"); agentCard.setUrl("https://example.com"); request.setAgentCard(agentCard); request.setNamespaceId("public"); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); when(a2aServerOperationService.getAgentCard("public", "test", "1.0.0", "")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.AGENT_NOT_FOUND, "")); ReleaseAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); assertNull(response.getMessage()); verify(a2aServerOperationService).registerAgent(any(AgentCard.class), anyString(), anyString()); } @Test void handleWithValidNewVersionAgentCard() throws NacosException { final ReleaseAgentCardRequest request = new ReleaseAgentCardRequest(); AgentCard agentCard = new AgentCard(); agentCard.setName("test"); agentCard.setVersion("1.0.0"); agentCard.setProtocolVersion("0.3.0"); agentCard.setPreferredTransport("JSONRPC"); agentCard.setUrl("https://example.com"); request.setAgentCard(agentCard); request.setNamespaceId("public"); request.setSetAsLatest(true); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); AgentCardDetailInfo existAgentCard = new AgentCardDetailInfo(); existAgentCard.setName("test"); existAgentCard.setVersion("0.9.0"); when(a2aServerOperationService.getAgentCard("public", "test", "1.0.0", "")).thenThrow( new NacosApiException(NacosException.NOT_FOUND, ErrorCode.AGENT_VERSION_NOT_FOUND, "")); ReleaseAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); assertNull(response.getMessage()); verify(a2aServerOperationService).updateAgentCard(any(AgentCard.class), anyString(), anyString(), eq(true)); } @Test void handleWithExistingAgentCard() throws NacosException { final ReleaseAgentCardRequest request = new ReleaseAgentCardRequest(); AgentCard agentCard = new AgentCard(); agentCard.setName("test"); agentCard.setVersion("1.0.0"); agentCard.setProtocolVersion("0.3.0"); agentCard.setPreferredTransport("JSONRPC"); agentCard.setUrl("https://example.com"); request.setAgentCard(agentCard); request.setNamespaceId("public"); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); AgentCardDetailInfo existAgentCard = new AgentCardDetailInfo(); existAgentCard.setName("test"); existAgentCard.setVersion("1.0.0"); when(a2aServerOperationService.getAgentCard("public", "test", "1.0.0", "")).thenReturn(existAgentCard); ReleaseAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); verify(a2aServerOperationService, never()).registerAgent(any(AgentCard.class), anyString(), anyString()); verify(a2aServerOperationService, never()).updateAgentCard(any(AgentCard.class), anyString(), anyString(), anyBoolean()); } @Test void handleWithOtherException() throws NacosException { final ReleaseAgentCardRequest request = new ReleaseAgentCardRequest(); AgentCard agentCard = new AgentCard(); agentCard.setName("test"); agentCard.setVersion("1.0.0"); agentCard.setProtocolVersion("0.3.0"); agentCard.setPreferredTransport("JSONRPC"); agentCard.setUrl("https://example.com"); request.setAgentCard(agentCard); request.setNamespaceId("public"); when(meta.getConnectionId()).thenReturn("TEST_CONNECTION_ID"); when(a2aServerOperationService.getAgentCard("public", "test", "1.0.0", "")).thenThrow( new NacosApiException(NacosException.SERVER_ERROR, ErrorCode.SERVER_ERROR, "test error")); ReleaseAgentCardResponse response = requestHandler.handle(request, meta); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(NacosException.SERVER_ERROR, response.getErrorCode()); assertEquals("test error", response.getMessage()); verify(a2aServerOperationService, never()).registerAgent(any(AgentCard.class), anyString(), anyString()); verify(a2aServerOperationService, never()).updateAgentCard(any(AgentCard.class), anyString(), anyString(), anyBoolean()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/remote/manager/AiConnectionBasedClientManagerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.remote.manager; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionMeta; import com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo; import com.alibaba.nacos.naming.core.v2.client.ClientAttributes; import com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient; import com.alibaba.nacos.naming.core.v2.client.manager.impl.ConnectionBasedClientManager; 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.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.HashMap; 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.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AiConnectionBasedClientManagerTest { private static final String CONNECTION_ID = "1111111111_127.0.0.1_12345"; @Mock ConnectionBasedClientManager delegate; @Mock Connection connection; ConnectionMeta connectionMeta; AiConnectionBasedClientManager connectionBasedClientManager; @BeforeEach void setUp() { connectionBasedClientManager = new AiConnectionBasedClientManager(delegate); connectionMeta = new ConnectionMeta(CONNECTION_ID, "127.0.0.1", "127.0.0.1", 12345, 12345, ConnectionType.GRPC.getType(), "3.0.0", null, new HashMap<>()); } @AfterEach void tearDown() { } @Test void clientConnectedNotAiConnection() { when(connection.getMetaInfo()).thenReturn(connectionMeta); connectionBasedClientManager.clientConnected(connection); verify(delegate, never()).clientConnected(anyString(), any(ClientAttributes.class)); } @Test void clientConnected() { when(connection.getMetaInfo()).thenReturn(connectionMeta); connectionMeta.getLabels().put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_AI); connectionBasedClientManager.clientConnected(connection); verify(delegate).clientConnected(eq(CONNECTION_ID), any(ClientAttributes.class)); } @Test void clientConnectedByClient() { ConnectionBasedClient client = new ConnectionBasedClient(CONNECTION_ID, true, 0L); connectionBasedClientManager.clientConnected(client); verify(delegate).clientConnected(client); } @Test void syncClientConnected() { ClientAttributes clientAttributes = new ClientAttributes(); connectionBasedClientManager.syncClientConnected(CONNECTION_ID, clientAttributes); verify(delegate).syncClientConnected(CONNECTION_ID, clientAttributes); } @Test void clientDisConnectedNotAiConnection() { when(connection.getMetaInfo()).thenReturn(connectionMeta); connectionBasedClientManager.clientDisConnected(connection); verify(delegate, never()).clientDisconnected(anyString()); } @Test void clientDisconnected() { when(connection.getMetaInfo()).thenReturn(connectionMeta); connectionMeta.getLabels().put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_AI); connectionBasedClientManager.clientDisConnected(connection); verify(delegate).clientDisconnected(CONNECTION_ID); } @Test void getClient() { ConnectionBasedClient client = new ConnectionBasedClient(CONNECTION_ID, true, 0L); when(delegate.getClient(CONNECTION_ID)).thenReturn(client); assertEquals(client, connectionBasedClientManager.getClient(CONNECTION_ID)); } @Test void contains() { assertFalse(connectionBasedClientManager.contains(CONNECTION_ID)); when(delegate.contains(CONNECTION_ID)).thenReturn(true); assertTrue(connectionBasedClientManager.contains(CONNECTION_ID)); } @Test void allClientId() { when(delegate.allClientId()).thenReturn(Collections.singleton(CONNECTION_ID)); assertEquals(1, connectionBasedClientManager.allClientId().size()); assertEquals(CONNECTION_ID, connectionBasedClientManager.allClientId().iterator().next()); } @Test void isResponsibleClient() { ConnectionBasedClient client = new ConnectionBasedClient(CONNECTION_ID, true, 0L); assertFalse(connectionBasedClientManager.isResponsibleClient(client)); when(delegate.isResponsibleClient(client)).thenReturn(true); assertTrue(connectionBasedClientManager.isResponsibleClient(client)); } @Test void verifyClient() { DistroClientVerifyInfo verifyData = new DistroClientVerifyInfo(CONNECTION_ID, 0L); assertFalse(connectionBasedClientManager.verifyClient(verifyData)); when(delegate.verifyClient(verifyData)).thenReturn(true); assertTrue(connectionBasedClientManager.verifyClient(verifyData)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/McpEndpointOperationServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.naming.core.InstanceOperator; import com.alibaba.nacos.naming.core.ServiceOperator; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; 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.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class McpEndpointOperationServiceTest { @Mock private ServiceOperator serviceOperator; @Mock private InstanceOperator instanceOperator; @Mock private NamingMetadataManager metadataManager; McpEndpointOperationService endpointOperationService; @BeforeEach void setUp() { endpointOperationService = new McpEndpointOperationService(serviceOperator, instanceOperator, metadataManager); } @AfterEach void tearDown() { Service service = Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName::1.0.0"); ServiceManager.getInstance().removeSingleton(service); } @Test void createMcpServerEndpointServiceIfNecessaryTypeRefWithoutMsg() { McpEndpointSpec mcpEndpointSpec = new McpEndpointSpec(); mcpEndpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF); assertThrows(NacosApiException.class, () -> endpointOperationService.createMcpServerEndpointServiceIfNecessary( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", mcpEndpointSpec, false), "`namespaceId`, `groupName`, `serviceName` should be in remoteServerConfig data if type is `REF`"); mcpEndpointSpec.getData().put("namespaceId", AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); assertThrows(NacosApiException.class, () -> endpointOperationService.createMcpServerEndpointServiceIfNecessary( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", mcpEndpointSpec, false), "`namespaceId`, `groupName`, `serviceName` should be in remoteServerConfig data if type is `REF`"); mcpEndpointSpec.getData().put("groupName", "groupName"); assertThrows(NacosApiException.class, () -> endpointOperationService.createMcpServerEndpointServiceIfNecessary( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", mcpEndpointSpec, false), "`namespaceId`, `groupName`, `serviceName` should be in remoteServerConfig data if type is `REF`"); } @Test void createMcpServerEndpointServiceIfNecessaryTypeRef() throws NacosException { McpEndpointSpec mcpEndpointSpec = new McpEndpointSpec(); mcpEndpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF); mcpEndpointSpec.getData().put("namespaceId", AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); mcpEndpointSpec.getData().put("groupName", "groupName"); mcpEndpointSpec.getData().put("serviceName", "serviceName"); Service service = endpointOperationService.createMcpServerEndpointServiceIfNecessary( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", mcpEndpointSpec, false); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, service.getNamespace()); assertEquals("groupName", service.getGroup()); assertEquals("serviceName", service.getName()); } @Test void createMcpServerEndpointServiceIfNecessaryTypeDirectWithoutExistService() throws NacosException { McpEndpointSpec mcpEndpointSpec = new McpEndpointSpec(); mcpEndpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); mcpEndpointSpec.getData().put("address", "127.0.0.1"); mcpEndpointSpec.getData().put("port", "8848"); Service service = endpointOperationService.createMcpServerEndpointServiceIfNecessary( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", mcpEndpointSpec, false); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, service.getNamespace()); assertEquals(Constants.MCP_SERVER_ENDPOINT_GROUP, service.getGroup()); assertEquals("mcpName::1.0.0", service.getName()); verify(serviceOperator).create(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(NamingUtils.getGroupedName("mcpName::1.0.0", Constants.MCP_SERVER_ENDPOINT_GROUP)), any(ServiceMetadata.class)); verify(instanceOperator).registerInstance(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(Constants.MCP_SERVER_ENDPOINT_GROUP), eq("mcpName::1.0.0"), any(Instance.class)); } @Test void createMcpServerEndpointServiceIfNecessaryTypeDirectWithExistService() throws NacosException { ServiceManager.getInstance().getSingleton( Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName::1.0.0")); McpEndpointSpec mcpEndpointSpec = new McpEndpointSpec(); mcpEndpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); mcpEndpointSpec.getData().put("address", "127.0.0.1"); mcpEndpointSpec.getData().put("port", "8848"); Service service = endpointOperationService.createMcpServerEndpointServiceIfNecessary( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", mcpEndpointSpec, false); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, service.getNamespace()); assertEquals(Constants.MCP_SERVER_ENDPOINT_GROUP, service.getGroup()); assertEquals("mcpName::1.0.0", service.getName()); verify(serviceOperator, never()).create(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(NamingUtils.getGroupedName("mcpName::1.0.0", Constants.MCP_SERVER_ENDPOINT_GROUP)), any(ServiceMetadata.class)); verify(instanceOperator).registerInstance(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), eq(Constants.MCP_SERVER_ENDPOINT_GROUP), eq("mcpName::1.0.0"), any(Instance.class)); } @Test void getMcpServerEndpointInstances() throws NacosException { Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8848); ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setHosts(Collections.singletonList(instance)); when(instanceOperator.listInstance(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName", null, "", true)).thenReturn(serviceInfo); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); serviceRef.setGroupName(Constants.MCP_SERVER_ENDPOINT_GROUP); serviceRef.setServiceName("mcpName"); List actual = endpointOperationService.getMcpServerEndpointInstances(serviceRef); assertEquals(1, actual.size()); assertEquals("127.0.0.1", actual.get(0).getIp()); assertEquals(8848, actual.get(0).getPort()); } @Test void deleteMcpServerEndpointServiceForNonExistService() throws NacosException { endpointOperationService.deleteMcpServerEndpointService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); verify(instanceOperator, never()).removeInstance(anyString(), anyString(), anyString(), any(Instance.class)); verify(serviceOperator, never()).delete(anyString(), anyString()); } @Test void deleteMcpServerEndpointServiceForRefService() throws NacosException { Service service = Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName"); ServiceManager.getInstance().getSingleton(service); when(metadataManager.getServiceMetadata(service)).thenReturn(Optional.empty()); endpointOperationService.deleteMcpServerEndpointService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); verify(instanceOperator, never()).removeInstance(anyString(), anyString(), anyString(), any(Instance.class)); verify(serviceOperator, never()).delete(anyString(), anyString()); } @Test void deleteMcpServerEndpointService() throws NacosException { Service service = Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName"); ServiceManager.getInstance().getSingleton(service); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.getExtendData() .put(Constants.MCP_SERVER_ENDPOINT_METADATA_MARK, Constants.MCP_SERVER_ENDPOINT_METADATA_MARK); when(metadataManager.getServiceMetadata(service)).thenReturn(Optional.of(serviceMetadata)); List instances = new LinkedList<>(); Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8848); instances.add(instance); instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(9848); instances.add(instance); ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setHosts(instances); when(instanceOperator.listInstance(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName", null, "", false)).thenReturn(serviceInfo); endpointOperationService.deleteMcpServerEndpointService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); for (Instance each : instances) { verify(instanceOperator).removeInstance(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName", each); } verify(serviceOperator).delete(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, NamingUtils.getGroupedName("mcpName", Constants.MCP_SERVER_ENDPOINT_GROUP)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/McpExternalDataAdaptorTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.DisplayName; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.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.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; /** * Test for McpExternalDataAdaptor with MCP Registry support. * Covers file, json, and url import types. * * @author nacos */ @DisplayName("McpExternalDataAdaptor Tests") class McpExternalDataAdaptorTest { private McpExternalDataAdaptor adaptor; @Mock private HttpClient mockHttpClient; @BeforeEach void setUp() { MockitoAnnotations.openMocks(this); adaptor = new McpExternalDataAdaptor(); } // ==================== FILE TYPE IMPORT TESTS ==================== @Test @DisplayName("Should adapt valid MCP seed file to server list") void testAdaptValidSeedFile() throws Exception { String seedFileData = """ [ { "name": "ai.aliengiraffe/spotdb", "description": "Test Server", "version": "0.1.0", "repository": { "url": "https://github.com/test/repo" } } ] """; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("file"); request.setData(seedFileData); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertFalse(result.isEmpty()); assertEquals(1, result.size()); assertEquals("ai.aliengiraffe/spotdb", result.get(0).getName()); assertEquals("Test Server", result.get(0).getDescription()); assertNotNull(result.get(0).getId()); } @Test @DisplayName("Should handle multiple servers in seed file") void testAdaptMultipleServersInSeedFile() throws Exception { String seedFileData = """ [ { "name": "server1", "description": "First Server", "version": "1.0.0", "repository": { "url": "https://github.com/test/repo1" } }, { "name": "server2", "description": "Second Server", "version": "2.0.0", "repository": { "url": "https://github.com/test/repo2" } } ] """; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("file"); request.setData(seedFileData); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertEquals(2, result.size()); assertEquals("server1", result.get(0).getName()); assertEquals("server2", result.get(1).getName()); } @Test @DisplayName("Should handle empty seed file") void testAdaptEmptySeedFile() throws Exception { String emptyFileData = "[]"; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("file"); request.setData(emptyFileData); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertTrue(result.isEmpty()); } @Test @DisplayName("Should throw exception for invalid seed file JSON") void testAdaptInvalidSeedFileJson() throws Exception { String invalidJson = "[{invalid json}]"; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("file"); request.setData(invalidJson); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } // ==================== JSON TYPE IMPORT TESTS ==================== @Test @DisplayName("Should adapt valid single server JSON to server list") void testAdaptValidServerJson() throws Exception { String serverJson = """ { "name": "ai.alpic.test/test-mcp-server", "description": "Alpic Test MCP Server - great server!", "version": "0.0.1", "repository": {}, "remotes": [ { "type": "streamable-http", "url": "https://test.alpic.ai/" } ] } """; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData(serverJson); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertEquals(1, result.size()); assertEquals("ai.alpic.test/test-mcp-server", result.get(0).getName()); assertNotNull(result.get(0).getRemoteServerConfig()); } @Test @DisplayName("Should adapt JSON with packages (stdio protocol)") void testAdaptJsonWithPackages() throws Exception { String serverJson = """ { "name": "ai.aliengiraffe/spotdb", "description": "Ephemeral data sandbox", "version": "0.1.0", "repository": {}, "packages": [ { "registryType": "oci", "identifier": "docker.io/aliengiraffe/spotdb:0.1.0", "transport": { "type": "stdio" } } ] } """; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData(serverJson); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertEquals(1, result.size()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, result.get(0).getProtocol()); } @Test @DisplayName("Should handle JSON without remotes and packages") void testAdaptJsonWithoutRemotes() throws Exception { String serverJson = """ { "name": "minimal-server", "description": "Minimal", "version": "1.0.0", "repository": {} } """; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData(serverJson); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertEquals(1, result.size()); assertEquals("minimal-server", result.get(0).getName()); assertNull(result.get(0).getProtocol()); } @Test @DisplayName("Should throw exception for invalid JSON format") void testAdaptInvalidServerJson() throws Exception { String invalidJson = "{invalid: json}"; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData(invalidJson); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } @Test @DisplayName("Should throw exception for empty JSON") void testAdaptEmptyJson() throws Exception { String emptyJson = ""; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData(emptyJson); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } // ==================== URL TYPE IMPORT TESTS ==================== @Test @DisplayName("Should adapt valid URL response with single page") void testAdaptValidUrlResponse() throws Exception { String mockResponse = """ { "servers": [ { "server": { "name": "ai.aliengiraffe/spotdb", "description": "Ephemeral data sandbox", "version": "0.1.0", "repository": { "url": "https://github.com/aliengiraffe/spotdb" }, "packages": [ { "registryType": "oci", "identifier": "docker.io/aliengiraffe/spotdb:0.1.0", "transport": { "type": "stdio" } } ] }, "_meta": { "io.modelcontextprotocol.registry/official": { "status": "active", "publishedAt": "2025-10-09T17:05:17.793149Z", "updatedAt": "2025-10-09T17:05:17.793149Z", "isLatest": true } } } ], "metadata": { "nextCursor": null, "count": 1 } } """; setupHttpClientMock(200, mockResponse); McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://registry.modelcontextprotocol.io/search"); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertFalse(result.isEmpty()); assertEquals("ai.aliengiraffe/spotdb", result.get(0).getName()); } @Test @DisplayName("Should handle pagination with cursor") void testAdaptUrlResponseWithPagination() throws Exception { String firstPageResponse = """ { "servers": [ { "server": { "name": "server1", "version": "0.1.0", "repository": {}, "remotes": [ { "type": "streamable-http", "url": "https://test1.com/" } ] }, "_meta": { "io.modelcontextprotocol.registry/official": { "status": "active", "publishedAt": "2025-10-09T17:05:17.793149Z", "updatedAt": "2025-10-09T17:05:17.793149Z", "isLatest": true } } } ], "metadata": { "nextCursor": "cursor_page2", "count": 1 } } """; String secondPageResponse = """ { "servers": [ { "server": { "name": "server2", "version": "0.2.0", "repository": {}, "remotes": [ { "type": "streamable-http", "url": "https://test2.com/" } ] }, "_meta": { "io.modelcontextprotocol.registry/official": { "status": "active", "publishedAt": "2025-10-09T17:05:17.793149Z", "updatedAt": "2025-10-09T17:05:17.793149Z", "isLatest": true } } } ], "metadata": { "nextCursor": null, "count": 1 } } """; setupHttpClientMockForPagination(firstPageResponse, secondPageResponse); McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://registry.modelcontextprotocol.io/search"); request.setLimit(-1); // Fetch all pages List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); // Should have servers from both pages assertNotNull(result); assertTrue(result.size() >= 1); } @Test @DisplayName("Should handle URL with search parameter") void testAdaptUrlWithSearchParameter() throws Exception { String mockResponse = """ { "servers": [], "metadata": { "nextCursor": null, "count": 0 } } """; setupHttpClientMock(200, mockResponse); McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://registry.modelcontextprotocol.io/search"); request.setSearch("spotdb"); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertTrue(result.isEmpty()); } @Test @DisplayName("Should handle URL with explicit limit parameter") void testAdaptUrlWithLimitParameter() throws Exception { String mockResponse = """ { "servers": [ { "server": { "name": "server1", "version": "0.1.0", "repository": {} }, "_meta": { "io.modelcontextprotocol.registry/official": { "status": "active", "publishedAt": "2025-10-09T17:05:17.793149Z", "updatedAt": "2025-10-09T17:05:17.793149Z", "isLatest": true } } } ], "metadata": { "nextCursor": null, "count": 1 } } """; setupHttpClientMock(200, mockResponse); McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://registry.modelcontextprotocol.io/search"); request.setLimit(10); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); } @Test @DisplayName("Should throw exception for HTTP 404 error") void testAdaptUrlWith404Error() throws Exception { setupHttpClientMock(404, "Not Found"); McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://registry.modelcontextprotocol.io/notfound"); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } @Test @DisplayName("Should throw exception for HTTP 500 error") void testAdaptUrlWith500Error() throws Exception { setupHttpClientMock(500, "Internal Server Error"); McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://registry.modelcontextprotocol.io/search"); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } @Test @DisplayName("Should throw exception for invalid response JSON") void testAdaptUrlWithInvalidResponseJson() throws Exception { setupHttpClientMock(200, "{invalid json}"); McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://registry.modelcontextprotocol.io/search"); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } // ==================== BOUNDARY & EXCEPTION TESTS ==================== @Test @DisplayName("Should throw exception for blank URL") void testAdaptBlankUrl() throws Exception { McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData(" "); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } @Test @DisplayName("Should throw exception for null data") void testAdaptNullData() throws Exception { McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData(null); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } @Test @DisplayName("Should throw exception for unsupported import type") void testAdaptUnsupportedImportType() throws Exception { McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("unsupported"); request.setData("{\"name\":\"test\"}"); assertThrows(Exception.class, () -> adaptor.adaptExternalDataToNacosMcpServerFormat(request)); } @Test @DisplayName("Should handle JSON with multiple remotes of different types") void testAdaptJsonWithMultipleRemotes() throws Exception { String serverJson = """ { "name": "multi-remote-server", "description": "Server with multiple remotes", "version": "1.0.0", "repository": {}, "remotes": [ { "type": "streamable-http", "url": "https://remote1.com:8080/path1" }, { "type": "sse", "url": "https://remote2.com/path2" } ] } """; McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData(serverJson); List result = adaptor.adaptExternalDataToNacosMcpServerFormat(request); assertNotNull(result); assertEquals(1, result.size()); assertNotNull(result.get(0).getRemoteServerConfig()); assertEquals(2, result.get(0).getRemoteServerConfig().getFrontEndpointConfigList().size()); } @Test @DisplayName("Should generate consistent IDs for same server name") void testConsistentIdGeneration() throws Exception { String serverJson = """ { "name": "consistent-server", "description": "Test", "version": "1.0.0", "repository": {} } """; McpServerImportRequest request1 = new McpServerImportRequest(); request1.setImportType("json"); request1.setData(serverJson); McpServerImportRequest request2 = new McpServerImportRequest(); request2.setImportType("json"); request2.setData(serverJson); List result1 = adaptor.adaptExternalDataToNacosMcpServerFormat(request1); List result2 = adaptor.adaptExternalDataToNacosMcpServerFormat(request2); assertEquals(result1.get(0).getId(), result2.get(0).getId()); } // ==================== HELPER METHODS ==================== private void setupHttpClientMock(int statusCode, String responseBody) throws Exception { @SuppressWarnings("unchecked") HttpResponse mockResponse = mock(HttpResponse.class); when(mockResponse.statusCode()).thenReturn(statusCode); when(mockResponse.body()).thenReturn(responseBody); when(mockHttpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandler.class))) .thenReturn(mockResponse); adaptor.setHttpClient(mockHttpClient); } private void setupHttpClientMockForPagination(String firstPage, String secondPage) throws Exception { @SuppressWarnings("unchecked") HttpResponse mockResponse1 = mock(HttpResponse.class); when(mockResponse1.statusCode()).thenReturn(200); when(mockResponse1.body()).thenReturn(firstPage); @SuppressWarnings("unchecked") HttpResponse mockResponse2 = mock(HttpResponse.class); when(mockResponse2.statusCode()).thenReturn(200); when(mockResponse2.body()).thenReturn(secondPage); when(mockHttpClient.send(any(HttpRequest.class), any(HttpResponse.BodyHandler.class))) .thenReturn(mockResponse1) .thenReturn(mockResponse2); adaptor.setHttpClient(mockHttpClient); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/McpServerCacheInvalidateServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.utils.GroupKey; 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.junit.jupiter.MockitoExtension; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; /** * Unit test for McpServerCacheInvalidateService. * * @author xinluo */ @ExtendWith(MockitoExtension.class) class McpServerCacheInvalidateServiceTest { @Mock private McpServerIndex mcpServerIndex; private McpServerCacheInvalidateService service; private static final String TEST_NAMESPACE = "test-namespace"; private static final String TEST_SERVER_ID = "test-server-id"; private static final String TEST_DATAID_VERSION = TEST_SERVER_ID + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX; private static final String TEST_GROUP_KEY_VERSION = GroupKey.getKey(TEST_DATAID_VERSION, Constants.MCP_SERVER_VERSIONS_GROUP, TEST_NAMESPACE); @BeforeEach void setUp() { service = new McpServerCacheInvalidateService(mcpServerIndex); } @AfterEach void tearDown() { service = null; } /** * Test handling MCP server version config change event successfully. */ @Test void testHandleMcpServerVersionConfigChangeEvent() { // Given: MCP server version config change event LocalDataChangeEvent event = new LocalDataChangeEvent(TEST_GROUP_KEY_VERSION); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should be invalidated verify(mcpServerIndex, times(1)).removeMcpServerById(eq(TEST_SERVER_ID)); } /** * Test handling non-MCP server config change event (should be ignored). */ @Test void testHandleNonMcpServerConfigChangeEvent() { // Given: non-MCP server config change event String groupKey = GroupKey.getKey("some-data-id", "some-group", TEST_NAMESPACE); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should NOT be invalidated verify(mcpServerIndex, never()).removeMcpServerById(anyString()); } /** * Test handling MCP server group config (should be ignored after simplification). */ @Test void testHandleMcpServerGroupConfig() { // Given: MCP server group config change event String dataId = TEST_SERVER_ID + "-v1.0.0" + Constants.MCP_SERVER_SPEC_DATA_ID_SUFFIX; String groupKey = GroupKey.getKey(dataId, Constants.MCP_SERVER_GROUP, TEST_NAMESPACE); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should NOT be invalidated (only version group is monitored) verify(mcpServerIndex, never()).removeMcpServerById(anyString()); } /** * Test handling MCP tools group config (should be ignored after simplification). */ @Test void testHandleMcpToolsGroupConfig() { // Given: MCP tools group config change event String dataId = TEST_SERVER_ID + "-v1.0.0" + Constants.MCP_SERVER_TOOL_DATA_ID_SUFFIX; String groupKey = GroupKey.getKey(dataId, Constants.MCP_SERVER_TOOL_GROUP, TEST_NAMESPACE); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should NOT be invalidated (only version group is monitored) verify(mcpServerIndex, never()).removeMcpServerById(anyString()); } /** * Test handling non-LocalDataChangeEvent (should be ignored). */ @Test void testHandleNonLocalDataChangeEvent() { // Given: non-LocalDataChangeEvent - create a LocalDataChangeEvent with non-MCP group String groupKey = GroupKey.getKey("some-data-id", "some-group", TEST_NAMESPACE); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should NOT be invalidated verify(mcpServerIndex, never()).removeMcpServerById(anyString()); } /** * Test handling event with invalid dataId format (missing suffix). */ @Test void testHandleEventWithInvalidDataIdFormat() { // Given: event with invalid dataId (missing suffix) String groupKey = GroupKey.getKey("invalid-data-id", Constants.MCP_SERVER_VERSIONS_GROUP, TEST_NAMESPACE); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should NOT be invalidated verify(mcpServerIndex, never()).removeMcpServerById(anyString()); } /** * Test handling event with empty dataId. */ @Test void testHandleEventWithEmptyDataId() { // Given: event with empty dataId String groupKey = GroupKey.getKey("", Constants.MCP_SERVER_VERSIONS_GROUP, TEST_NAMESPACE); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should NOT be invalidated verify(mcpServerIndex, never()).removeMcpServerById(anyString()); } /** * Test handling event when cache invalidation throws exception. * Exception should be caught and logged, not propagated. */ @Test void testHandleEventWhenCacheInvalidationThrowsException() { // Given: MCP server version config change event and cache invalidation will throw exception LocalDataChangeEvent event = new LocalDataChangeEvent(TEST_GROUP_KEY_VERSION); doThrow(new RuntimeException("Cache invalidation failed")).when(mcpServerIndex) .removeMcpServerById(eq(TEST_SERVER_ID)); // When: handle the event (should not throw exception) service.handleConfigDataChangeEvent(event); // Then: removeMcpServerById should still be called verify(mcpServerIndex, times(1)).removeMcpServerById(eq(TEST_SERVER_ID)); } /** * Test handling multiple events in sequence. */ @Test void testHandleMultipleEvents() { // Given: multiple MCP server version config change events String serverId1 = "server-1"; String serverId2 = "server-2"; String dataId1 = serverId1 + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX; String dataId2 = serverId2 + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX; String groupKey1 = GroupKey.getKey(dataId1, Constants.MCP_SERVER_VERSIONS_GROUP, TEST_NAMESPACE); String groupKey2 = GroupKey.getKey(dataId2, Constants.MCP_SERVER_VERSIONS_GROUP, TEST_NAMESPACE); LocalDataChangeEvent event1 = new LocalDataChangeEvent(groupKey1); LocalDataChangeEvent event2 = new LocalDataChangeEvent(groupKey2); // When: handle both events service.handleConfigDataChangeEvent(event1); service.handleConfigDataChangeEvent(event2); // Then: cache should be invalidated for both servers verify(mcpServerIndex, times(1)).removeMcpServerById(eq(serverId1)); verify(mcpServerIndex, times(1)).removeMcpServerById(eq(serverId2)); } /** * Test handling same event multiple times (idempotent). */ @Test void testHandleSameEventMultipleTimes() { // Given: same MCP server version config change event LocalDataChangeEvent event = new LocalDataChangeEvent(TEST_GROUP_KEY_VERSION); // When: handle the same event twice service.handleConfigDataChangeEvent(event); service.handleConfigDataChangeEvent(event); // Then: cache invalidation should be called twice (idempotent operation) verify(mcpServerIndex, times(2)).removeMcpServerById(eq(TEST_SERVER_ID)); } /** * Test extracting server ID with different namespace. */ @Test void testHandleEventWithDifferentNamespace() { // Given: MCP server version config change event with different namespace String differentNamespace = "different-namespace"; String groupKey = GroupKey.getKey(TEST_DATAID_VERSION, Constants.MCP_SERVER_VERSIONS_GROUP, differentNamespace); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should be invalidated regardless of namespace verify(mcpServerIndex, times(1)).removeMcpServerById(eq(TEST_SERVER_ID)); } /** * Test handling event with special characters in server ID. */ @Test void testHandleEventWithSpecialCharactersInServerId() { // Given: server ID with special characters String specialServerId = "test-server_123.abc"; String dataId = specialServerId + Constants.MCP_SERVER_VERSION_DATA_ID_SUFFIX; String groupKey = GroupKey.getKey(dataId, Constants.MCP_SERVER_VERSIONS_GROUP, TEST_NAMESPACE); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should be invalidated with correct server ID verify(mcpServerIndex, times(1)).removeMcpServerById(eq(specialServerId)); } /** * Test handling event with empty namespace (default namespace). */ @Test void testHandleEventWithEmptyNamespace() { // Given: MCP server version config change event with empty namespace String groupKey = GroupKey.getKey(TEST_DATAID_VERSION, Constants.MCP_SERVER_VERSIONS_GROUP, ""); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should be invalidated verify(mcpServerIndex, times(1)).removeMcpServerById(eq(TEST_SERVER_ID)); } /** * Test handling event with null namespace (default namespace). */ @Test void testHandleEventWithNullNamespace() { // Given: MCP server version config change event with null namespace String groupKey = GroupKey.getKey(TEST_DATAID_VERSION, Constants.MCP_SERVER_VERSIONS_GROUP); LocalDataChangeEvent event = new LocalDataChangeEvent(groupKey); // When: handle the event service.handleConfigDataChangeEvent(event); // Then: cache should be invalidated verify(mcpServerIndex, times(1)).removeMcpServerById(eq(TEST_SERVER_ID)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/McpServerImportServiceTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.McpServerValidationConstants; import com.alibaba.nacos.ai.index.McpCacheIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpServerRemoteServiceConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServerValidationItem; import com.alibaba.nacos.api.exception.NacosException; 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.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Unit tests for McpServerImportService. * * @author WangzJi */ @ExtendWith(MockitoExtension.class) class McpServerImportServiceTest { @Mock private McpExternalDataAdaptor transformService; @Mock private McpServerValidationService validationService; @Mock private McpServerOperationService operationService; @Mock private McpCacheIndex mcpCacheIndex; private McpServerImportService importService; @BeforeEach void setUp() { importService = new McpServerImportService(transformService, validationService, operationService, mcpCacheIndex); } @Test void testValidateImportSuccess() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())) .thenReturn(servers); McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportValidationResult result = importService.validateImport("test-namespace", request); // Then assertNotNull(result); assertTrue(result.isValid()); } @Test void testValidateImportTransformationFailure() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("invalid-json"); request.setImportType("json"); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())) .thenThrow(new RuntimeException("Invalid JSON format")); // When McpServerImportValidationResult result = importService.validateImport("test-namespace", request); // Then assertNotNull(result); assertFalse(result.isValid()); assertNotNull(result.getErrors()); assertFalse(result.getErrors().isEmpty()); assertTrue(result.getErrors().get(0).contains("Import validation failed")); } @Test void testExecuteImportValidationFailure() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("invalid-data"); request.setImportType("json"); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())) .thenThrow(new RuntimeException("Invalid data")); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertFalse(response.isSuccess()); assertNotNull(response.getErrorMessage()); assertTrue(response.getErrorMessage().contains("Import validation failed")); } @Test void testExecuteImportSuccess() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setSelectedServers(new String[] {"server1"}); request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); servers.add(server); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())) .thenReturn(servers); // Mock validation McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(false); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(1, response.getSuccessCount()); assertEquals(0, response.getFailedCount()); assertEquals(0, response.getSkippedCount()); } @Test void testExecuteImportWithSkipInvalidAndNoValidServers() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("data"); request.setImportType("json"); request.setSkipInvalid(true); // Skip invalid servers // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation - invalid result with no valid servers McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(false); List errors = new ArrayList<>(); errors.add("Validation error"); validationResult.setErrors(errors); validationResult.setServers(new ArrayList<>()); // Empty servers list when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); // Should be true since skipInvalid is true and no servers to import assertEquals(0, response.getTotalCount()); } @Test void testExecuteImportWithSkipInvalidAndHasValidServers() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setSkipInvalid(true); // Skip invalid servers request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); servers.add(server); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn(servers); // Mock validation - mixed result with some invalid and some valid McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(false); // Overall invalid List errors = new ArrayList<>(); errors.add("Some validation errors"); validationResult.setErrors(errors); validationResult.setInvalidCount(1); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(false); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); // Since skipInvalid is true and there are valid servers, import should succeed assertTrue(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(1, response.getSuccessCount()); assertEquals(0, response.getFailedCount()); assertEquals(0, response.getSkippedCount()); } @Test void testExecuteImportWithExceptionInImportProcess() throws Exception { // When McpServerImportResponse response = importService.executeImport("test-namespace", null); // Then assertNotNull(response); assertFalse(response.isSuccess()); assertTrue(response.getErrorMessage().contains("Import execution failed")); } @Test void testExecuteImportWithNullValidationServers() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("data"); request.setImportType("json"); request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation - valid result but with null servers list McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); validationResult.setServers(null); // Null servers list when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); assertEquals(0, response.getTotalCount()); assertEquals(0, response.getSuccessCount()); assertEquals(0, response.getFailedCount()); assertEquals(0, response.getSkippedCount()); } @Test void testExecuteImportWithSelectedServersFiltering() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setSelectedServers(new String[] {"server1", "server3"}); // Select specific servers request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation with multiple servers McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); // Server1 - valid and selected McpServerValidationItem item1 = new McpServerValidationItem(); item1.setServerId("server1"); item1.setServerName("Test Server 1"); item1.setStatus(McpServerValidationConstants.STATUS_VALID); item1.setExists(false); McpServerDetailInfo server1 = new McpServerDetailInfo(); server1.setId("server1"); server1.setName("Test Server 1"); item1.setServer(server1); // Server2 - valid but not selected (should not be imported) McpServerValidationItem item2 = new McpServerValidationItem(); item2.setServerId("server2"); item2.setServerName("Test Server 2"); item2.setStatus(McpServerValidationConstants.STATUS_VALID); item2.setExists(false); McpServerDetailInfo server2 = new McpServerDetailInfo(); server2.setId("server2"); server2.setName("Test Server 2"); item2.setServer(server2); // Server3 - valid and selected McpServerValidationItem item3 = new McpServerValidationItem(); item3.setServerId("server3"); item3.setServerName("Test Server 3"); item3.setStatus(McpServerValidationConstants.STATUS_VALID); item3.setExists(false); McpServerDetailInfo server3 = new McpServerDetailInfo(); server3.setId("server3"); server3.setName("Test Server 3"); item3.setServer(server3); List validationItems = new ArrayList<>(); validationItems.add(item1); validationItems.add(item2); validationItems.add(item3); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); // Only server1 and server3 are valid and selected assertEquals(2, response.getTotalCount()); assertEquals(2, response.getSuccessCount()); assertEquals(0, response.getFailedCount()); assertEquals(0, response.getSkippedCount()); } @Test void testExecuteImportSkipExistingServer() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setOverrideExisting(false); // Do not override existing servers // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation with existing server McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(true); // Server already exists McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(0, response.getSuccessCount()); assertEquals(0, response.getFailedCount()); assertEquals(1, response.getSkippedCount()); // Server should be skipped verify(operationService, never()).updateMcpServer(anyString(), anyBoolean(), any(), any(), any(), anyBoolean()); verify(operationService, never()).createMcpServer(anyString(), any(), any(), any()); } @Test void testExecuteImportUpdateExistingServer() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setOverrideExisting(true); // Override existing servers // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock existing server in cache index McpServerIndexData data = new McpServerIndexData(); when(mcpCacheIndex.getMcpServerByName(eq("test-namespace"), eq("Test Server"))).thenReturn(data); // Mock validation with existing server McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(true); // Server already exists McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(1, response.getSuccessCount()); assertEquals(0, response.getFailedCount()); assertEquals(0, response.getSkippedCount()); verify(operationService).updateMcpServer(eq("test-namespace"), eq(true), any(), any(), any(), anyBoolean()); verify(operationService, never()).createMcpServer(anyString(), any(), any(), any()); } @Test void testExecuteImportCreateNewServer() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setOverrideExisting(false); // Do not override existing servers // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation with new server McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(false); // Server does not exist McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(1, response.getSuccessCount()); assertEquals(0, response.getFailedCount()); assertEquals(0, response.getSkippedCount()); verify(operationService).createMcpServer(eq("test-namespace"), any(), any(), any()); verify(operationService, never()).updateMcpServer(anyString(), anyBoolean(), any(), any(), any(), anyBoolean()); } @Test void testExecuteImportWithEndpointSpecConversion() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation with server that has endpoint config McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(false); McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); server.setProtocol("http"); McpServerRemoteServiceConfig remoteConfig = new McpServerRemoteServiceConfig(); FrontEndpointConfig endpointConfig = new FrontEndpointConfig(); endpointConfig.setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); endpointConfig.setEndpointData("127.0.0.1:8080"); remoteConfig.setFrontEndpointConfigList(Arrays.asList(endpointConfig)); server.setRemoteServerConfig(remoteConfig); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(1, response.getSuccessCount()); verify(operationService).createMcpServer(eq("test-namespace"), any(), any(), any()); } @Test void testExecuteImportWithStdioProtocolNoEndpointSpec() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation with server that has stdio protocol McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(false); McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); // STDIO protocol item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertTrue(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(1, response.getSuccessCount()); verify(operationService).createMcpServer(eq("test-namespace"), any(), any(), any()); } @Test void testExecuteImportFailureDuringServerCreation() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(false); McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // Mock operation service to throw exception doThrow(new NacosException(500, "Failed to create server")).when(operationService) .createMcpServer(anyString(), any(), any(), any()); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); assertFalse(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(0, response.getSuccessCount()); assertEquals(1, response.getFailedCount()); assertEquals(0, response.getSkippedCount()); assertNotNull(response.getResults()); assertEquals(1, response.getResults().size()); assertEquals("failed", response.getResults().get(0).getStatus()); } @Test void testExecuteImportWithEndpointSpecExceptionHandling() throws Exception { // Given McpServerImportRequest request = new McpServerImportRequest(); request.setData("{\"servers\":[]}"); request.setImportType("json"); request.setOverrideExisting(false); // Mock transformation List servers = new ArrayList<>(); when(transformService.adaptExternalDataToNacosMcpServerFormat(any())).thenReturn( servers); // Mock validation with server that causes exception in endpoint spec conversion McpServerImportValidationResult validationResult = new McpServerImportValidationResult(); validationResult.setValid(true); McpServerValidationItem item = new McpServerValidationItem(); item.setServerId("server1"); item.setServerName("Test Server"); item.setStatus(McpServerValidationConstants.STATUS_VALID); item.setExists(false); McpServerDetailInfo server = new McpServerDetailInfo(); server.setId("server1"); server.setName("Test Server"); server.setProtocol("http"); McpServerRemoteServiceConfig remoteConfig = new McpServerRemoteServiceConfig(); FrontEndpointConfig endpointConfig = new FrontEndpointConfig(); // Create an object that will cause exception when processed in convertToEndpointSpec endpointConfig.setEndpointData(new Object() { @Override public String toString() { throw new RuntimeException("Test exception"); } }); remoteConfig.setFrontEndpointConfigList(Arrays.asList(endpointConfig)); server.setRemoteServerConfig(remoteConfig); item.setServer(server); List validationItems = new ArrayList<>(); validationItems.add(item); validationResult.setServers(validationItems); when(validationService.validateServers(anyString(), any())).thenReturn(validationResult); // When McpServerImportResponse response = importService.executeImport("test-namespace", request); // Then assertNotNull(response); // Since the exception is thrown in endpoint spec conversion, the server import should fail assertFalse(response.isSuccess()); assertEquals(1, response.getTotalCount()); assertEquals(0, response.getSuccessCount()); assertEquals(1, response.getFailedCount()); verify(operationService, never()).createMcpServer(eq("test-namespace"), any(), any(), any()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/McpServerOperationServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.index.McpServerIndex; import com.alibaba.nacos.ai.model.mcp.McpServerIndexData; import com.alibaba.nacos.ai.model.mcp.McpServerStorageInfo; import com.alibaba.nacos.ai.utils.McpConfigUtils; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.FrontEndpointConfig; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.EncryptObject; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerRemoteServiceConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServerVersionInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.ai.model.mcp.McpTool; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.naming.core.v2.pojo.Service; 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.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.UUID; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class McpServerOperationServiceTest { @Mock private ConfigQueryChainService configQueryChainService; @Mock private ConfigOperationService configOperationService; @Mock private McpToolOperationService toolOperationService; @Mock private McpEndpointOperationService endpointOperationService; @Mock private McpServerIndex mcpServerIndex; @Mock private SyncEffectService syncEffectService; McpServerOperationService serverOperationService; @BeforeEach void setUp() { serverOperationService = new McpServerOperationService(configQueryChainService, configOperationService, toolOperationService, endpointOperationService, mcpServerIndex, syncEffectService); } @AfterEach void tearDown() { } @Test void listMcpServerWithPage() { String id = mockId(); Page mockIndexData = mockIndexData(id); McpServerVersionInfo mockMcpServer = mockServerVersionInfo(id); when(mcpServerIndex.searchMcpServerByNameWithPage(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_ACCURATE, 1, 100)).thenReturn(mockIndexData); ConfigQueryChainResponse mockResponse = mockConfigQueryChainResponse(mockMcpServer); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(mockResponse); Page result = serverOperationService.listMcpServerWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_ACCURATE, 1, 100); assertEquals(1, result.getPageNumber()); assertEquals(1, result.getPagesAvailable()); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); assertEquals(id, result.getPageItems().get(0).getId()); assertEquals("mcpName", result.getPageItems().get(0).getName()); assertEquals("9.9.9", result.getPageItems().get(0).getVersion()); } @Test void listMcpServerWithOverPageNo() { Page mockIndexData = new Page<>(); mockIndexData.setPageNumber(10); mockIndexData.setPagesAvailable(1); mockIndexData.setTotalCount(1); mockIndexData.setPageItems(Collections.emptyList()); when(mcpServerIndex.searchMcpServerByNameWithPage(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_ACCURATE, 10, 100)).thenReturn(mockIndexData); Page result = serverOperationService.listMcpServerWithPage( AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, Constants.MCP_LIST_SEARCH_ACCURATE, 10, 100); assertEquals(10, result.getPageNumber()); assertEquals(1, result.getPagesAvailable()); assertEquals(1, result.getTotalCount()); assertEquals(0, result.getPageItems().size()); } @Test void getMcpServerDetailByIdFoundStdioTypeWithToolsWithNamespace() throws NacosException { String id = mockId(); ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse( mockStorageInfo(id, true, true, AiConstants.Mcp.MCP_PROTOCOL_STDIO)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); when(toolOperationService.getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9"))).thenReturn(new McpToolSpecification()); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNotNull(actual.getToolSpec()); } @Test void getMcpServerDetailByIdFoundSseTypeWithoutToolsWithNamespace() throws NacosException { String id = mockId(); final ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); final McpServerStorageInfo mockStorageInfo = mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_SSE); final McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); serviceRef.setNamespaceId(Constants.MCP_SERVER_ENDPOINT_GROUP); serviceRef.setServiceName("mcpName"); remoteServiceConfig.setServiceRef(serviceRef); mockStorageInfo.setRemoteServerConfig(remoteServiceConfig); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(mockStorageInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8848); when(endpointOperationService.getMcpServerEndpointInstances(any(McpServiceRef.class))).thenReturn( Collections.singletonList(instance)); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); verify(toolOperationService, never()).getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9")); assertEquals("127.0.0.1", actual.getBackendEndpoints().get(0).getAddress()); assertEquals(8848, actual.getBackendEndpoints().get(0).getPort()); } @Test void getMcpServerDetailByIdFoundHttpTypeWithoutToolsWithNamespace() throws NacosException { String id = mockId(); final ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); final McpServerStorageInfo mockStorageInfo = mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_HTTP); final McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); serviceRef.setNamespaceId(Constants.MCP_SERVER_ENDPOINT_GROUP); serviceRef.setServiceName("mcpName"); remoteServiceConfig.setServiceRef(serviceRef); remoteServiceConfig.setExportPath("/nacos"); mockStorageInfo.setRemoteServerConfig(remoteServiceConfig); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(mockStorageInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8848); when(endpointOperationService.getMcpServerEndpointInstances(any(McpServiceRef.class))).thenReturn( Collections.singletonList(instance)); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); verify(toolOperationService, never()).getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9")); assertEquals("127.0.0.1", actual.getBackendEndpoints().get(0).getAddress()); assertEquals(8848, actual.getBackendEndpoints().get(0).getPort()); assertEquals("/nacos", actual.getBackendEndpoints().get(0).getPath()); } @Test void getMcpServerDetailByIdWithServiceRefFrontEndpoint() throws NacosException { String id = mockId(); final ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); final McpServerStorageInfo mockStorageInfo = mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_HTTP); final McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); serviceRef.setNamespaceId(Constants.MCP_SERVER_ENDPOINT_GROUP); serviceRef.setServiceName("mcpName"); remoteServiceConfig.setFrontEndpointConfigList(new LinkedList<>()); remoteServiceConfig.getFrontEndpointConfigList().add(new FrontEndpointConfig()); remoteServiceConfig.getFrontEndpointConfigList().get(0).setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); remoteServiceConfig.getFrontEndpointConfigList().get(0).setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointData(serviceRef); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_REF); remoteServiceConfig.getFrontEndpointConfigList().get(0).setPath("/sse"); mockStorageInfo.setRemoteServerConfig(remoteServiceConfig); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(mockStorageInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8848); when(endpointOperationService.getMcpServerEndpointInstances(any(McpServiceRef.class))).thenReturn( Collections.singletonList(instance)); when(endpointOperationService.getMcpServerEndpointInstances(isNull())).thenReturn( Collections.emptyList()); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); verify(toolOperationService, never()).getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9")); assertEquals("127.0.0.1", actual.getFrontendEndpoints().get(0).getAddress()); assertEquals(8848, actual.getFrontendEndpoints().get(0).getPort()); assertEquals("/sse", actual.getFrontendEndpoints().get(0).getPath()); } @Test void getMcpServerDetailByIdWithDirectFrontEndpoint() throws NacosException { String id = mockId(); final ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); final McpServerStorageInfo mockStorageInfo = mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_HTTP); final McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); remoteServiceConfig.setFrontEndpointConfigList(new LinkedList<>()); remoteServiceConfig.getFrontEndpointConfigList().add(new FrontEndpointConfig()); remoteServiceConfig.getFrontEndpointConfigList().get(0).setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); remoteServiceConfig.getFrontEndpointConfigList().get(0).setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointData("127.0.0.1:8848"); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); remoteServiceConfig.getFrontEndpointConfigList().get(0).setPath("/sse"); mockStorageInfo.setRemoteServerConfig(remoteServiceConfig); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(mockStorageInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); when(endpointOperationService.getMcpServerEndpointInstances(isNull())).thenReturn( Collections.emptyList()); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); verify(toolOperationService, never()).getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9")); assertEquals("127.0.0.1", actual.getFrontendEndpoints().get(0).getAddress()); assertEquals(8848, actual.getFrontendEndpoints().get(0).getPort()); assertEquals("/sse", actual.getFrontendEndpoints().get(0).getPath()); } @Test void getMcpServerDetailByIdWithDirectNoPortFrontEndpoint() throws NacosException { String id = mockId(); final ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); final McpServerStorageInfo mockStorageInfo = mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_HTTP); final McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); remoteServiceConfig.setFrontEndpointConfigList(new LinkedList<>()); remoteServiceConfig.getFrontEndpointConfigList().add(new FrontEndpointConfig()); remoteServiceConfig.getFrontEndpointConfigList().get(0).setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); remoteServiceConfig.getFrontEndpointConfigList().get(0).setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointData("127.0.0.1"); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); remoteServiceConfig.getFrontEndpointConfigList().get(0).setPath("/sse"); mockStorageInfo.setRemoteServerConfig(remoteServiceConfig); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(mockStorageInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); when(endpointOperationService.getMcpServerEndpointInstances(isNull())).thenReturn( Collections.emptyList()); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); verify(toolOperationService, never()).getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9")); assertEquals("127.0.0.1", actual.getFrontendEndpoints().get(0).getAddress()); assertEquals(80, actual.getFrontendEndpoints().get(0).getPort()); assertEquals("/sse", actual.getFrontendEndpoints().get(0).getPath()); } @Test void getMcpServerDetailByIdWithDirectNoPortForHttpsFrontEndpoint() throws NacosException { String id = mockId(); final ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); final McpServerStorageInfo mockStorageInfo = mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_HTTP); final McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); remoteServiceConfig.setFrontEndpointConfigList(new LinkedList<>()); remoteServiceConfig.getFrontEndpointConfigList().add(new FrontEndpointConfig()); remoteServiceConfig.getFrontEndpointConfigList().get(0).setProtocol(Constants.PROTOCOL_TYPE_HTTPS); remoteServiceConfig.getFrontEndpointConfigList().get(0).setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointData("127.0.0.1"); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); remoteServiceConfig.getFrontEndpointConfigList().get(0).setPath("/sse"); mockStorageInfo.setRemoteServerConfig(remoteServiceConfig); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(mockStorageInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); when(endpointOperationService.getMcpServerEndpointInstances(isNull())).thenReturn( Collections.emptyList()); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); verify(toolOperationService, never()).getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9")); assertEquals("127.0.0.1", actual.getFrontendEndpoints().get(0).getAddress()); assertEquals(443, actual.getFrontendEndpoints().get(0).getPort()); assertEquals("/sse", actual.getFrontendEndpoints().get(0).getPath()); } @Test void getMcpServerDetailByIdWithToBackendFrontEndpoint() throws NacosException { String id = mockId(); final ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); final McpServerStorageInfo mockStorageInfo = mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_HTTP); final McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); serviceRef.setNamespaceId(Constants.MCP_SERVER_ENDPOINT_GROUP); serviceRef.setServiceName("mcpName"); remoteServiceConfig.setServiceRef(serviceRef); remoteServiceConfig.setExportPath("/nacos"); remoteServiceConfig.setFrontEndpointConfigList(Collections.singletonList(new FrontEndpointConfig())); remoteServiceConfig.getFrontEndpointConfigList().get(0).setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); remoteServiceConfig.getFrontEndpointConfigList().get(0).setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); remoteServiceConfig.getFrontEndpointConfigList().get(0).setEndpointType(AiConstants.Mcp.MCP_FRONT_ENDPOINT_TYPE_TO_BACK); remoteServiceConfig.getFrontEndpointConfigList().get(0).setPath("/nacos"); mockStorageInfo.setRemoteServerConfig(remoteServiceConfig); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(mockStorageInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8848); when(endpointOperationService.getMcpServerEndpointInstances(any(McpServiceRef.class))).thenReturn( Collections.singletonList(instance)); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); verify(toolOperationService, never()).getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, McpConfigUtils.formatServerToolSpecDataId(id, "9.9.9")); assertEquals("127.0.0.1", actual.getBackendEndpoints().get(0).getAddress()); assertEquals(8848, actual.getBackendEndpoints().get(0).getPort()); assertEquals("/nacos", actual.getBackendEndpoints().get(0).getPath()); assertEquals("127.0.0.1", actual.getFrontendEndpoints().get(0).getAddress()); assertEquals(8848, actual.getFrontendEndpoints().get(0).getPort()); assertEquals("/nacos", actual.getFrontendEndpoints().get(0).getPath()); } @Test void getMcpServerDetailByIdNotFoundWithNamespace() throws NacosException { String id = mockId(); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(null)); assertThrows(NacosApiException.class, () -> serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null)); } @Test void getMcpServerDetailByIdFoundWithNamespace() throws NacosException { String id = mockId(); ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse( mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_STDIO)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); } @Test void getMcpServerDetailByIdNotFoundWithVersion() throws NacosException { String id = mockId(); ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse(null); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); assertThrows(NacosApiException.class, () -> serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, "non-exist")); } @Test void getMcpServerDetailByIdFoundWithVersion() throws NacosException { String id = mockId(); ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse( mockStorageInfo(id, false, false, AiConstants.Mcp.MCP_PROTOCOL_STDIO)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, null, "1.0.0"); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("1.0.0", actual.getVersion()); assertEquals("1.0.0", actual.getVersionDetail().getVersion()); assertFalse(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); } @Test void getMcpServerDetailByNameNotFound() throws NacosException { when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(null)); assertThrows(NacosApiException.class, () -> serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "mcpName", null)); } @Test void getMcpServerDetailByNameFound() throws NacosException { String id = mockId(); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName")).thenReturn( mockIndexData(id).getPageItems().get(0)); ConfigQueryChainResponse versionDataResponse = mockConfigQueryChainResponse(mockServerVersionInfo(id)); ConfigQueryChainResponse storageDataResponse = mockConfigQueryChainResponse( mockStorageInfo(id, true, false, AiConstants.Mcp.MCP_PROTOCOL_STDIO)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionDataResponse, storageDataResponse); McpServerDetailInfo actual = serverOperationService.getMcpServerDetail(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, "mcpName", null); assertEquals(id, actual.getId()); assertEquals("mcpName", actual.getName()); assertEquals("9.9.9", actual.getVersion()); assertEquals("9.9.9", actual.getVersionDetail().getVersion()); assertTrue(actual.getVersionDetail().getIs_latest()); assertNull(actual.getToolSpec()); } @Test void createMcpServerExistedName() { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName")).thenReturn( mockIndexData("id").getPageItems().get(0)); assertThrows(NacosApiException.class, () -> serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, null)); } @Test void createMcpServerWithoutVersion() throws NacosException { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(null); assertThrows(NacosApiException.class, () -> serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, null)); } @Test void createMcpServerWithOldSpec() throws NacosException { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(null); mockServerBasicInfo.setVersion("1.0.0"); String id = serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, null); assertNotNull(id); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void createMcpServerWithNewSpec() throws NacosException { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersion(null); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.0")); String id = serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, null); assertNotNull(id); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void createMcpServerWithToolSpec() throws NacosException { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.0")); McpToolSpecification toolSpecification = new McpToolSpecification(); toolSpecification.getTools().add(new McpTool()); String id = serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, toolSpecification, null); assertNotNull(id); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(toolOperationService).refreshMcpTool(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), any(McpServerStorageInfo.class), eq(toolSpecification)); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void createMcpServerWithEncryptedToolSpecOnly() throws NacosException { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.0")); McpToolSpecification toolSpecification = new McpToolSpecification(); toolSpecification.setSpecificationType("encrypted"); EncryptObject encryptObject = new EncryptObject(); encryptObject.setData("ciphertext"); toolSpecification.setEncryptData(encryptObject); String id = serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, toolSpecification, null); assertNotNull(id); // should trigger tool refresh even when tools/securitySchemes are empty verify(toolOperationService, times(1)).refreshMcpTool(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), any(McpServerStorageInfo.class), eq(toolSpecification)); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void createMcpServerWithEndpointSpec() throws NacosException { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.0")); mockServerBasicInfo.setRemoteServerConfig(new McpServerRemoteServiceConfig()); McpEndpointSpec endpointSpec = new McpEndpointSpec(); endpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); endpointSpec.setData(new HashMap<>()); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_ADDRESS, "127.0.0.1"); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_PORT, "8848"); when(endpointOperationService.createMcpServerEndpointServiceIfNecessary(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", endpointSpec, false)).thenReturn( Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName")); String id = serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, endpointSpec); assertNotNull(id); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void createMcpServerByCustomIdWithException() throws NacosException { McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.0")); mockServerBasicInfo.setRemoteServerConfig(new McpServerRemoteServiceConfig()); mockServerBasicInfo.setId("invalid Id"); McpEndpointSpec endpointSpec = new McpEndpointSpec(); endpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); endpointSpec.setData(new HashMap<>()); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_ADDRESS, "127.0.0.1"); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_PORT, "8848"); assertThrows(NacosApiException.class, () -> serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, null)); } @Test void createMcpServerByCustomIdWithExistedId() throws NacosException { String id = UUID.randomUUID().toString(); McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.0")); mockServerBasicInfo.setRemoteServerConfig(new McpServerRemoteServiceConfig()); mockServerBasicInfo.setId(id); McpEndpointSpec endpointSpec = new McpEndpointSpec(); endpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); endpointSpec.setData(new HashMap<>()); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_ADDRESS, "127.0.0.1"); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_PORT, "8848"); when(mcpServerIndex.getMcpServerById(id)).thenReturn(new McpServerIndexData()); assertThrows(NacosApiException.class, () -> serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, null)); } @Test void createMcpServerByCustomId() throws NacosException { String id = UUID.randomUUID().toString(); McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(""); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.0")); mockServerBasicInfo.setRemoteServerConfig(new McpServerRemoteServiceConfig()); mockServerBasicInfo.setId(id); McpEndpointSpec endpointSpec = new McpEndpointSpec(); endpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); endpointSpec.setData(new HashMap<>()); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_ADDRESS, "127.0.0.1"); endpointSpec.getData().put(Constants.MCP_SERVER_ENDPOINT_PORT, "8848"); when(endpointOperationService.createMcpServerEndpointServiceIfNecessary(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", "1.0.0", endpointSpec, false)).thenReturn( Service.newService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, Constants.MCP_SERVER_ENDPOINT_GROUP, "mcpName")); String actualId = serverOperationService.createMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo, null, endpointSpec); assertNotNull(actualId); assertEquals(id, actualId); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void updateMcpServerByIdNotFound() { String id = mockId(); McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(id); assertThrows(NacosApiException.class, () -> serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, mockServerBasicInfo, null, null, false)); } @Test void updateMcpServerByIdWithoutVersion() { String id = mockId(); McpServerBasicInfo mockServerBasicInfo = mockServerVersionInfo(id); mockServerBasicInfo.setVersionDetail(null); mockServerBasicInfo.setVersion(null); assertThrows(NacosApiException.class, () -> serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, mockServerBasicInfo, null, null, false)); } @Test void updateMcpServerByIdWithOldSpec() throws NacosException { String id = mockId(); McpServerVersionInfo mockServerBasicInfo = mockServerVersionInfo(id); mockServerBasicInfo.setVersionDetail(null); mockServerBasicInfo.setVersion("1.0.0"); ConfigQueryChainResponse response = mockConfigQueryChainResponse(mockServerBasicInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, mockServerBasicInfo, null, null, false); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void updateMcpServerByIdWithNewSpec() throws NacosException { String id = mockId(); McpServerVersionInfo mockServerBasicInfo = mockServerVersionInfo(id); mockServerBasicInfo.setVersionDetail(mockVersion("9.9.9")); mockServerBasicInfo.setVersion(null); ConfigQueryChainResponse response = mockConfigQueryChainResponse(mockServerBasicInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, mockServerBasicInfo, null, null, false); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void updateMcpServerByIdNewVersion() throws NacosException { String id = mockId(); McpServerVersionInfo mockServerBasicInfo = mockServerVersionInfo(id); mockServerBasicInfo.setVersionDetail(mockVersion("1.0.1")); mockServerBasicInfo.setVersion(null); ConfigQueryChainResponse response = mockConfigQueryChainResponse(mockServerBasicInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, mockServerBasicInfo, null, null, false); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, mockServerBasicInfo.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void updateMcpServerByNameNotFound() { McpServerVersionInfo mockServerBasicInfo = mockServerVersionInfo(null); assertThrows(NacosApiException.class, () -> serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, mockServerBasicInfo, null, null, false)); } @Test void updateMcpServerByNameFound() throws NacosException { String id = mockId(); McpServerVersionInfo mockServerBasicInfo = mockServerVersionInfo(null); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName")).thenReturn( mockIndexData(id).getPageItems().get(0)); mockServerBasicInfo.setVersionDetail(mockVersion("9.9.9")); ConfigQueryChainResponse response = mockConfigQueryChainResponse(mockServerBasicInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, mockServerBasicInfo, null, null, false); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void updateMcpServerWithEncryptedToolSpecOnly() throws NacosException { String id = mockId(); McpServerVersionInfo existing = mockServerVersionInfo(id); ConfigQueryChainResponse response = mockConfigQueryChainResponse(existing); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); McpServerVersionInfo updateSpec = mockServerVersionInfo(id); updateSpec.setVersionDetail(mockVersion("1.0.1")); McpToolSpecification toolSpecification = new McpToolSpecification(); toolSpecification.setSpecificationType("encrypted"); EncryptObject encryptObject = new EncryptObject(); encryptObject.setData("ciphertext"); toolSpecification.setEncryptData(encryptObject); serverOperationService.updateMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, true, updateSpec, toolSpecification, null, false); verify(toolOperationService, times(1)).refreshMcpTool(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), any(McpServerStorageInfo.class), eq(toolSpecification)); verify(configOperationService, times(2)).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, updateSpec.getName()); verify(mcpServerIndex, times(1)).removeMcpServerById(id); } @Test void deleteMcpServerByIdNotFound() { String id = mockId(); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(null)); assertThrows(NacosApiException.class, () -> serverOperationService.deleteMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, id, null)); } @Test void deleteMcpServerById() throws NacosException { String id = mockId(); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(mockServerVersionInfo(id))); serverOperationService.deleteMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, id, null); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(endpointOperationService, times(2)) .deleteMcpServerEndpointService(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), captor.capture()); List allArgs = captor.getAllValues(); assertEquals(2, allArgs.size(), "The actual number of calls does not match the expectation."); assertTrue(allArgs.contains("mcpName::1.0.0"), "Missing deletion call for version 1.0.0"); assertTrue(allArgs.contains("mcpName::9.9.9"), "Missing deletion call for version 9.9.9"); String serverVersionDataId = McpConfigUtils.formatServerVersionInfoDataId(id); verify(configOperationService, times(2)).deleteConfig(serverVersionDataId, Constants.MCP_SERVER_VERSIONS_GROUP, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, null, "nacos", null); verify(mcpServerIndex, times(0)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null); verify(mcpServerIndex, times(1)).removeMcpServerById(id); for (ServerVersionDetail each : mockServerVersionInfo(id).getVersionDetails()) { verify(toolOperationService).deleteMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, each.getVersion()); String serverSpecDataId = McpConfigUtils.formatServerSpecInfoDataId(id, each.getVersion()); verify(configOperationService).deleteConfig(serverSpecDataId, Constants.MCP_SERVER_GROUP, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, null, "nacos", null); } } @Test void deleteMcpServerByName() throws NacosException { String id = mockId(); McpServerVersionInfo mockServerBasicInfo = mockServerVersionInfo(null); when(mcpServerIndex.getMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName")).thenReturn( mockIndexData(id).getPageItems().get(0)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(mockServerBasicInfo)); serverOperationService.deleteMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName", null, null); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(endpointOperationService, times(2)) .deleteMcpServerEndpointService(eq(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE), captor.capture()); List allArgs = captor.getAllValues(); assertEquals(2, allArgs.size(), "The actual number of calls does not match the expectation."); assertTrue(allArgs.contains("mcpName::1.0.0"), "Missing deletion call for version 1.0.0"); assertTrue(allArgs.contains("mcpName::9.9.9"), "Missing deletion call for version 9.9.9"); verify(mcpServerIndex, times(1)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName"); verify(mcpServerIndex, times(0)).removeMcpServerById(null); String serverVersionDataId = McpConfigUtils.formatServerVersionInfoDataId(id); verify(configOperationService, times(2)).deleteConfig(serverVersionDataId, Constants.MCP_SERVER_VERSIONS_GROUP, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, null, "nacos", null); for (ServerVersionDetail each : mockServerVersionInfo(id).getVersionDetails()) { verify(toolOperationService).deleteMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, each.getVersion()); String serverSpecDataId = McpConfigUtils.formatServerSpecInfoDataId(id, each.getVersion()); verify(configOperationService).deleteConfig(serverSpecDataId, Constants.MCP_SERVER_GROUP, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, null, "nacos", null); } } @Test void deleteMcpServerForTargetVersion() throws NacosException { String id = mockId(); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn( mockConfigQueryChainResponse(mockServerVersionInfo(id))); serverOperationService.deleteMcpServer(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, id, "1.0.0"); verify(mcpServerIndex, times(0)).removeMcpServerByName(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null); verify(mcpServerIndex, times(1)).removeMcpServerById(id); verify(endpointOperationService).deleteMcpServerEndpointService(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "mcpName::1.0.0"); String serverVersionDataId = McpConfigUtils.formatServerVersionInfoDataId(id); verify(configOperationService).deleteConfig(serverVersionDataId, Constants.MCP_SERVER_VERSIONS_GROUP, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, null, "nacos", null); verify(toolOperationService).deleteMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, "1.0.0"); String serverSpecDataId = McpConfigUtils.formatServerSpecInfoDataId(id, "1.0.0"); verify(configOperationService).deleteConfig(serverSpecDataId, Constants.MCP_SERVER_GROUP, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, null, "nacos", null); } @Test void invalidateCacheAfterDbUpdateOperationWithDifferentNames() { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String oldMcpName = "oldName"; String newMcpName = "newName"; String mcpServerId = mockId(); // 使用反射调用私有方法 try { java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbUpdateOperation", String.class, String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, oldMcpName, newMcpName, mcpServerId); // 验证方法调用 verify(mcpServerIndex, times(1)).removeMcpServerByName(namespaceId, oldMcpName); verify(mcpServerIndex, times(1)).removeMcpServerByName(namespaceId, newMcpName); verify(mcpServerIndex, times(1)).removeMcpServerById(mcpServerId); } catch (Exception e) { throw new RuntimeException(e); } } @Test void invalidateCacheAfterDbUpdateOperationWithSameNames() { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String oldMcpName = "sameName"; String newMcpName = "sameName"; String mcpServerId = mockId(); // 使用反射调用私有方法 try { java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbUpdateOperation", String.class, String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, oldMcpName, newMcpName, mcpServerId); // 验证方法调用 // 当名称相同时,只调用一次removeMcpServerByName verify(mcpServerIndex, times(1)).removeMcpServerByName(namespaceId, newMcpName); verify(mcpServerIndex, times(1)).removeMcpServerById(mcpServerId); } catch (Exception e) { throw new RuntimeException(e); } } @Test void invalidateCacheAfterDbUpdateOperationWithEmptyNames() { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String oldMcpName = ""; String newMcpName = ""; String mcpServerId = mockId(); // 使用反射调用私有方法 try { java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbUpdateOperation", String.class, String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, oldMcpName, newMcpName, mcpServerId); // 验证方法调用 // 当名称为空时,不调用removeMcpServerByName verify(mcpServerIndex, never()).removeMcpServerByName(anyString(), anyString()); verify(mcpServerIndex, times(1)).removeMcpServerById(mcpServerId); } catch (Exception e) { throw new RuntimeException(e); } } @Test void invalidateCacheAfterDbOperation() { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String mcpName = "testName"; String mcpServerId = mockId(); // 使用反射调用私有方法 try { java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbOperation", String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, mcpName, mcpServerId); // 验证方法调用 verify(mcpServerIndex, times(1)).removeMcpServerByName(namespaceId, mcpName); verify(mcpServerIndex, times(1)).removeMcpServerById(mcpServerId); } catch (Exception e) { throw new RuntimeException(e); } } @Test void invalidateCacheAfterDbOperationWithEmptyName() { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String mcpName = ""; String mcpServerId = mockId(); // 使用反射调用私有方法 try { java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbOperation", String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, mcpName, mcpServerId); // 验证方法调用 // 当名称为空时,不调用removeMcpServerByName verify(mcpServerIndex, never()).removeMcpServerByName(anyString(), anyString()); verify(mcpServerIndex, times(1)).removeMcpServerById(mcpServerId); } catch (Exception e) { throw new RuntimeException(e); } } @Test void invalidateCacheAfterDbOperationWithEmptyId() { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String mcpName = "testName"; String mcpServerId = ""; // 使用反射调用私有方法 try { java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbOperation", String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, mcpName, mcpServerId); // 验证方法调用 verify(mcpServerIndex, times(1)).removeMcpServerByName(namespaceId, mcpName); // 当ID为空时,不调用removeMcpServerById verify(mcpServerIndex, never()).removeMcpServerById(anyString()); } catch (Exception e) { throw new RuntimeException(e); } } @Test void invalidateCacheAfterDbOperationWithException() throws Exception { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String mcpName = "testName"; String mcpServerId = mockId(); // 模拟mcpServerIndex抛出异常 doThrow(new RuntimeException("Test exception")).when(mcpServerIndex).removeMcpServerByName(namespaceId, mcpName); // 使用反射调用私有方法 java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbOperation", String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, mcpName, mcpServerId); // 验证方法被调用,即使有异常 verify(mcpServerIndex, times(1)).removeMcpServerByName(namespaceId, mcpName); verify(mcpServerIndex, never()).removeMcpServerById(mcpServerId); } @Test void invalidateCacheAfterDbUpdateOperationWithException() throws Exception { String namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; String oldMcpName = "oldName"; String newMcpName = "newName"; String mcpServerId = mockId(); // 模拟mcpServerIndex抛出异常 doThrow(new RuntimeException("Test exception")).when(mcpServerIndex).removeMcpServerByName(namespaceId, oldMcpName); // 使用反射调用私有方法 java.lang.reflect.Method method = McpServerOperationService.class.getDeclaredMethod( "invalidateCacheAfterDbUpdateOperation", String.class, String.class, String.class, String.class); method.setAccessible(true); method.invoke(serverOperationService, namespaceId, oldMcpName, newMcpName, mcpServerId); verify(mcpServerIndex, times(1)).removeMcpServerByName(namespaceId, oldMcpName); verify(mcpServerIndex, never()).removeMcpServerByName(namespaceId, newMcpName); verify(mcpServerIndex, never()).removeMcpServerById(mcpServerId); } private Page mockIndexData(String id) { Page indexDataPage = new Page<>(); indexDataPage.setPageNumber(1); indexDataPage.setPagesAvailable(1); indexDataPage.setTotalCount(1); indexDataPage.getPageItems().add(new McpServerIndexData()); indexDataPage.getPageItems().get(0).setId(id); indexDataPage.getPageItems().get(0).setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); return indexDataPage; } private ConfigQueryChainResponse mockConfigQueryChainResponse(Object obj) { ConfigQueryChainResponse mockResponse = new ConfigQueryChainResponse(); if (null != obj) { mockResponse.setContent(JacksonUtils.toJson(obj)); mockResponse.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); } else { mockResponse.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); } return mockResponse; } private McpServerVersionInfo mockServerVersionInfo(String id) { McpServerVersionInfo serverVersionInfo = new McpServerVersionInfo(); serverVersionInfo.setId(id); serverVersionInfo.setName("mcpName"); serverVersionInfo.setLatestPublishedVersion("9.9.9"); List versionDetails = new LinkedList<>(); versionDetails.add(mockVersion("1.0.0")); versionDetails.add(mockVersion("9.9.9")); serverVersionInfo.setVersions(versionDetails); return serverVersionInfo; } private ServerVersionDetail mockVersion(String version) { ServerVersionDetail versionDetail = new ServerVersionDetail(); versionDetail.setVersion(version); return versionDetail; } private McpServerStorageInfo mockStorageInfo(String id, boolean isLatest, boolean withTools, String protocol) { McpServerStorageInfo storageInfo = new McpServerStorageInfo(); storageInfo.setId(id); storageInfo.setProtocol(protocol); storageInfo.setName("mcpName"); String version = isLatest ? "9.9.9" : "1.0.0"; storageInfo.setVersion(version); storageInfo.setVersionDetail(mockVersion(version)); storageInfo.setToolsDescriptionRef(withTools ? McpConfigUtils.formatServerToolSpecDataId(id, version) : null); return storageInfo; } private String mockId() { return UUID.randomUUID().toString(); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/McpServerValidationServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.McpServerValidationConstants; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpServerRemoteServiceConfig; import com.alibaba.nacos.api.ai.model.mcp.McpServerValidationItem; import com.alibaba.nacos.api.ai.model.mcp.McpTool; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.mcp.registry.Package; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.exception.NacosException; 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.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; 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.UUID; 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; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class McpServerValidationServiceTest { private McpServerValidationService mcpServerValidationService; @Mock private McpServerOperationService mcpServerOperationService; @BeforeEach void setUp() { mcpServerValidationService = new McpServerValidationService(); ReflectionTestUtils.setField(mcpServerValidationService, "mcpServerOperationService", mcpServerOperationService); } @Test void validateServersWithNullServers() throws NacosException { String namespaceId = "test-namespace"; McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, null); assertFalse(result.isValid()); assertEquals(0, result.getTotalCount()); assertEquals(0, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertNotNull(result.getErrors()); assertFalse(result.getErrors().isEmpty()); } @Test void validateServersWithEmptyServers() throws NacosException { String namespaceId = "test-namespace"; McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, new ArrayList<>()); assertTrue(result.isValid()); assertEquals(0, result.getTotalCount()); assertEquals(0, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertNotNull(result.getErrors()); assertTrue(result.getErrors().isEmpty()); } @Test void validateServersWithValidServer() throws NacosException { String namespaceId = "test-namespace"; List servers = new ArrayList<>(); McpServerDetailInfo validServer = createValidServer(); servers.add(validServer); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers); assertTrue(result.isValid()); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertNotNull(result.getServers()); assertEquals(1, result.getServers().size()); assertEquals(McpServerValidationConstants.STATUS_VALID, result.getServers().get(0).getStatus()); } @Test void validateServersWithInvalidServer() throws NacosException { final String namespaceId = "test-namespace"; final List servers = new ArrayList<>(); McpServerDetailInfo invalidServer = new McpServerDetailInfo(); initializeVersionDetail(invalidServer); invalidServer.setName(""); // Empty name should be invalid invalidServer.setProtocol("invalid-protocol"); invalidServer.setDescription(""); servers.add(invalidServer); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers); assertFalse(result.isValid()); assertEquals(1, result.getTotalCount()); assertEquals(0, result.getValidCount()); assertEquals(1, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertNotNull(result.getServers()); assertEquals(1, result.getServers().size()); assertEquals(McpServerValidationConstants.STATUS_INVALID, result.getServers().get(0).getStatus()); } @Test void validateServersWithDuplicateServerInBatch() throws NacosException { final String namespaceId = "test-namespace"; List servers = new ArrayList<>(); McpServerDetailInfo server1 = createValidServer(); server1.setName("duplicate-server"); McpServerDetailInfo server2 = createValidServer(); server2.setName("duplicate-server"); // Same name as server1 servers.add(server1); servers.add(server2); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers); assertTrue(result.isValid()); assertEquals(2, result.getTotalCount()); assertEquals(1, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(1, result.getDuplicateCount()); assertNotNull(result.getServers()); assertEquals(2, result.getServers().size()); McpServerValidationItem item1 = result.getServers().get(0); McpServerValidationItem item2 = result.getServers().get(1); // First one should be valid assertEquals(McpServerValidationConstants.STATUS_VALID, item1.getStatus()); // Second one should be duplicate assertEquals(McpServerValidationConstants.STATUS_DUPLICATE, item2.getStatus()); } @Test void validateServersWithExistingServer() throws NacosException { final String namespaceId = "test-namespace"; List servers = new ArrayList<>(); McpServerDetailInfo server = createValidServer(); server.setName("existing-server"); servers.add(server); // Mock that server already exists McpServerDetailInfo existingServer = createValidServer(); existingServer.setId(UUID.randomUUID().toString()); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(existingServer); McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers); assertTrue(result.isValid()); assertEquals(1, result.getTotalCount()); assertEquals(0, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(1, result.getDuplicateCount()); assertNotNull(result.getServers()); assertEquals(1, result.getServers().size()); McpServerValidationItem item = result.getServers().get(0); assertEquals(McpServerValidationConstants.STATUS_DUPLICATE, item.getStatus()); assertTrue(item.isExists()); } @Test void validateServersWithExceptionDuringValidation() throws NacosException { String namespaceId = "test-namespace"; List servers = new ArrayList<>(); McpServerDetailInfo server = createValidServer(); servers.add(server); // Mock exception during validation when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))) .thenThrow(new RuntimeException("Test exception")); McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers); assertFalse(result.isValid()); assertEquals(1, result.getTotalCount()); assertEquals(0, result.getValidCount()); assertEquals(1, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertNotNull(result.getErrors()); assertTrue(result.getErrors().isEmpty()); } @Test void validateSingleServerWithValidServer() throws Exception { String namespaceId = "test-namespace"; McpServerDetailInfo server = createValidServer(); server.setName("valid-server"); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, new HashSet<>()); assertNotNull(item); assertEquals("valid-server", item.getServerName()); assertEquals(McpServerValidationConstants.STATUS_VALID, item.getStatus()); assertNotNull(item.getErrors()); assertTrue(item.getErrors().isEmpty()); assertFalse(item.isExists()); assertEquals(server, item.getServer()); } @Test void validateSingleServerWithMissingName() throws Exception { final String namespaceId = "test-namespace"; McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setName(""); // Empty name server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); server.setDescription("Test description"); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, new HashSet<>()); assertNotNull(item); assertEquals("", item.getServerName()); assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus()); assertNotNull(item.getErrors()); assertFalse(item.getErrors().isEmpty()); assertTrue(item.getErrors().contains("Server name is required")); } @Test void validateSingleServerWithMissingProtocol() throws Exception { final String namespaceId = "test-namespace"; McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setName("test-server"); server.setProtocol(""); // Empty protocol server.setDescription("Test description"); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, new HashSet<>()); assertNotNull(item); assertEquals("test-server", item.getServerName()); assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus()); assertNotNull(item.getErrors()); assertFalse(item.getErrors().isEmpty()); assertTrue(item.getErrors().contains("Protocol is required")); } @Test void validateSingleServerWithInvalidProtocol() throws Exception { final String namespaceId = "test-namespace"; McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setName("test-server"); server.setProtocol("invalid-protocol"); server.setDescription("Test description"); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, new HashSet<>()); assertNotNull(item); assertEquals("test-server", item.getServerName()); assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus()); assertNotNull(item.getErrors()); assertFalse(item.getErrors().isEmpty()); assertTrue(item.getErrors().contains("Invalid protocol: invalid-protocol")); } @Test void validateSingleServerWithMissingDescription() throws Exception { final String namespaceId = "test-namespace"; McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setName("test-server"); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); server.setDescription(""); // Empty description when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, new HashSet<>()); assertNotNull(item); assertEquals("test-server", item.getServerName()); assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus()); assertNotNull(item.getErrors()); assertFalse(item.getErrors().isEmpty()); assertTrue(item.getErrors().contains("Description is required")); } @Test void validateSingleServerWithDuplicateInBatch() throws Exception { String namespaceId = "test-namespace"; McpServerDetailInfo server = createValidServer(); server.setName("duplicate-server"); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); // Add server name + version to existing names set to simulate duplicate in batch java.util.Set existingNames = new java.util.HashSet<>(); existingNames.add("duplicate-server" + server.getVersionDetail().getVersion()); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, existingNames); assertNotNull(item); assertEquals("duplicate-server", item.getServerName()); assertEquals(McpServerValidationConstants.STATUS_DUPLICATE, item.getStatus()); assertNotNull(item.getErrors()); assertFalse(item.getErrors().isEmpty()); assertTrue(item.getErrors().contains("Duplicate server name in import batch: duplicate-server")); } @Test void validateSingleServerWithExceptionDuringExistenceCheck() throws Exception { String namespaceId = "test-namespace"; McpServerDetailInfo server = createValidServer(); server.setName("test-server"); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))) .thenThrow(new RuntimeException("Test exception")); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, new HashSet<>()); assertNotNull(item); assertEquals("test-server", item.getServerName()); // Should still be invalid if existence check fails assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus()); assertNotNull(item.getErrors()); assertFalse(item.getErrors().isEmpty()); assertTrue(item.getErrors().contains("Error checking existing server: Test exception")); } @Test void isValidProtocolWithValidProtocols() throws Exception { // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod("isValidProtocol", String.class); method.setAccessible(true); // Test all valid protocols assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_STDIO)); assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_SSE)); assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_STREAMABLE)); assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_HTTP)); assertTrue((Boolean) method.invoke(mcpServerValidationService, AiConstants.Mcp.MCP_PROTOCOL_DUBBO)); } @Test void isValidProtocolWithInvalidProtocol() throws Exception { // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod("isValidProtocol", String.class); method.setAccessible(true); assertFalse((Boolean) method.invoke(mcpServerValidationService, "invalid-protocol")); assertFalse((Boolean) method.invoke(mcpServerValidationService, "")); assertFalse((Boolean) method.invoke(mcpServerValidationService, (String) null)); } @Test void validateProtocolSpecificConfigWithValidStdioConfig() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); // Valid stdio config with localServerConfig Map localConfig = new HashMap<>(); localConfig.put("command", "test-command"); server.setLocalServerConfig(localConfig); List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertTrue(errors.isEmpty()); } @Test void validateProtocolSpecificConfigWithValidStdioConfigWithPackages() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); // Valid stdio config with packages List packages = new ArrayList<>(); packages.add(new Package()); server.setPackages(packages); server.setLocalServerConfig(null); // No local config, but has packages List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertTrue(errors.isEmpty()); } @Test void validateProtocolSpecificConfigWithInvalidStdioConfig() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); // Invalid stdio config - no local config and no packages server.setLocalServerConfig(null); server.setPackages(null); List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertFalse(errors.isEmpty()); assertTrue(errors.contains("Local server configuration or packages are required for stdio protocol")); } @Test void validateProtocolSpecificConfigWithValidRemoteConfig() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); // Valid remote config McpServerRemoteServiceConfig remoteConfig = new McpServerRemoteServiceConfig(); server.setRemoteServerConfig(remoteConfig); List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertTrue(errors.isEmpty()); } @Test void validateProtocolSpecificConfigWithInvalidRemoteConfig() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); // Invalid remote config - no remote config server.setRemoteServerConfig(null); List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertFalse(errors.isEmpty()); assertTrue(errors.contains("Remote server configuration is required for " + AiConstants.Mcp.MCP_PROTOCOL_HTTP + " protocol")); } @Test void validateProtocolSpecificConfigWithValidToolSpec() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); server.setRemoteServerConfig(new McpServerRemoteServiceConfig()); // Valid tool spec with tools McpToolSpecification toolSpec = new McpToolSpecification(); List tools = new ArrayList<>(); McpTool tool = new McpTool(); tool.setName("test-tool"); tools.add(tool); toolSpec.setTools(tools); server.setToolSpec(toolSpec); List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertTrue(errors.isEmpty()); } @Test void validateProtocolSpecificConfigWithInvalidToolSpec() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); server.setRemoteServerConfig(new McpServerRemoteServiceConfig()); // Invalid tool spec - empty tools list McpToolSpecification toolSpec = new McpToolSpecification(); toolSpec.setTools(new ArrayList<>()); // Empty tools list server.setToolSpec(toolSpec); List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertFalse(errors.isEmpty()); assertTrue(errors.contains("Tool specification should contain at least one tool")); } @Test void validateProtocolSpecificConfigWithNullToolSpec() throws Exception { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); server.setRemoteServerConfig(new McpServerRemoteServiceConfig()); // No tool spec - should be valid server.setToolSpec(null); List errors = new ArrayList<>(); // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateProtocolSpecificConfig", McpServerDetailInfo.class, List.class); method.setAccessible(true); method.invoke(mcpServerValidationService, server, errors); assertNotNull(errors); assertTrue(errors.isEmpty()); } @Test void validateSingleServerDoesNotCallIndexWhenNameIsBlank() throws Exception { final String namespaceId = "test-namespace"; McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setName(""); // Blank name server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); server.setDescription("Test description"); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); // Should not call the index when name is blank // 使用反射调用私有方法 Method method = McpServerValidationService.class.getDeclaredMethod( "validateSingleServer", String.class, McpServerDetailInfo.class, Set.class); method.setAccessible(true); McpServerValidationItem item = (McpServerValidationItem) method.invoke( mcpServerValidationService, namespaceId, server, new HashSet<>()); assertNotNull(item); assertEquals("", item.getServerName()); assertEquals(McpServerValidationConstants.STATUS_INVALID, item.getStatus()); assertEquals("Server name is required", item.getErrors().get(0)); } @Test void validateServersWithMixedValidAndInvalidServers() throws NacosException { final String namespaceId = "test-namespace"; List servers = new ArrayList<>(); // Add a valid server McpServerDetailInfo validServer = createValidServer(); validServer.setName("valid-server"); servers.add(validServer); // Add an invalid server McpServerDetailInfo invalidServer = new McpServerDetailInfo(); initializeVersionDetail(invalidServer); invalidServer.setName(""); // Missing name invalidServer.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); invalidServer.setDescription("Test description"); servers.add(invalidServer); // Add a duplicate server McpServerDetailInfo duplicateServer = createValidServer(); duplicateServer.setName("valid-server"); // Same name as validServer servers.add(duplicateServer); when(mcpServerOperationService.getMcpServerDetail(anyString(), anyString(), anyString(), nullable(String.class))).thenReturn(null); McpServerImportValidationResult result = mcpServerValidationService.validateServers(namespaceId, servers); assertFalse(result.isValid()); assertEquals(3, result.getTotalCount()); assertEquals(1, result.getValidCount()); assertEquals(1, result.getInvalidCount()); assertEquals(1, result.getDuplicateCount()); assertNotNull(result.getServers()); assertEquals(3, result.getServers().size()); } private void initializeVersionDetail(McpServerDetailInfo server) { if (server.getVersionDetail() == null) { ServerVersionDetail versionDetail = new ServerVersionDetail(); versionDetail.setVersion("1.0.0"); server.setVersionDetail(versionDetail); } } private McpServerDetailInfo createValidServer() { McpServerDetailInfo server = new McpServerDetailInfo(); initializeVersionDetail(server); server.setName("test-server"); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); server.setDescription("Test description"); McpServerRemoteServiceConfig remoteConfig = new McpServerRemoteServiceConfig(); server.setRemoteServerConfig(remoteConfig); return server; } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/McpToolOperationServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.utils.McpConfigUtils; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; 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.junit.jupiter.MockitoExtension; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class McpToolOperationServiceTest { @Mock private ConfigQueryChainService configQueryChainService; @Mock private ConfigOperationService configOperationService; McpToolOperationService toolOperationService; @BeforeEach void setUp() { toolOperationService = new McpToolOperationService(configQueryChainService, configOperationService); } @AfterEach void tearDown() { } @Test void refreshMcpTool() throws NacosException { McpServerBasicInfo serverBasicInfo = getMcpServerBasicInfo(); McpToolSpecification toolSpecification = new McpToolSpecification(); toolOperationService.refreshMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, serverBasicInfo, toolSpecification); verify(configOperationService).publishConfig(any(ConfigFormV3.class), any(ConfigRequestInfo.class), isNull()); } @Test void getMcpTool() { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); response.setContent(JacksonUtils.toJson(new McpToolSpecification())); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); String id = UUID.randomUUID().toString(); String version = "1.0.0"; String toolRef = McpConfigUtils.formatServerToolSpecDataId(id, version); McpToolSpecification actual = toolOperationService.getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, toolRef); assertNotNull(actual); } @Test void getMcpToolNotFound() { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); String id = UUID.randomUUID().toString(); String version = "1.0.0"; String toolRef = McpConfigUtils.formatServerToolSpecDataId(id, version); McpToolSpecification actual = toolOperationService.getMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, toolRef); assertNull(actual); } @Test void deleteMcpTool() throws NacosException { String id = UUID.randomUUID().toString(); toolOperationService.deleteMcpTool(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, id, "1.0.0"); verify(configOperationService).deleteConfig(McpConfigUtils.formatServerToolSpecDataId(id, "1.0.0"), Constants.MCP_SERVER_TOOL_GROUP, AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, null, null, "nacos", null); } private McpServerBasicInfo getMcpServerBasicInfo() { String id = UUID.randomUUID().toString(); McpServerBasicInfo serverBasicInfo = new McpServerBasicInfo(); serverBasicInfo.setId(id); serverBasicInfo.setName("mcpName"); serverBasicInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); serverBasicInfo.setDescription("Mock Mcp Server"); serverBasicInfo.setEnabled(true); serverBasicInfo.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); ServerVersionDetail serverVersionDetail = new ServerVersionDetail(); serverVersionDetail.setVersion("1.0.0"); serverBasicInfo.setVersionDetail(serverVersionDetail); return serverBasicInfo; } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/SimpleSyncEffectServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.config.server.model.form.ConfigForm; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.assertTrue; class SimpleSyncEffectServiceTest { SimpleSyncEffectService syncEffectService; @BeforeEach void setUp() { syncEffectService = new SimpleSyncEffectService(); } @Test void toSyncLongStartTime() { long currentTime = System.currentTimeMillis(); syncEffectService.toSync(new ConfigForm(), currentTime, 100, TimeUnit.MILLISECONDS); long waitTime = System.currentTimeMillis() - currentTime; assertTrue(waitTime >= 100); } @Test void toSyncLongStartTimeWithInterruptedException() throws InterruptedException { AtomicLong waitTime = new AtomicLong(); final CountDownLatch latch = new CountDownLatch(1); Thread testThread = new Thread(() -> { long currentTime = System.currentTimeMillis(); syncEffectService.toSync(new ConfigForm(), currentTime, 2000, TimeUnit.MILLISECONDS); waitTime.set(System.currentTimeMillis() - currentTime); latch.countDown(); }); testThread.start(); TimeUnit.MILLISECONDS.sleep(300); testThread.interrupt(); assertTrue(latch.await(2000, TimeUnit.MILLISECONDS)); assertTrue(waitTime.get() < 2000); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/SyncEffectServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service; import com.alibaba.nacos.config.server.model.form.ConfigForm; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertTrue; class SyncEffectServiceTest { private AtomicBoolean invokeMark; private SyncEffectService syncEffectService; @BeforeEach void setUp() { invokeMark = new AtomicBoolean(); syncEffectService = new MockSyncEffectService(); } @Test void toSync() { syncEffectService.toSync(new ConfigForm(), System.currentTimeMillis()); assertTrue(invokeMark.get()); } private class MockSyncEffectService implements SyncEffectService { @Override public void toSync(ConfigForm configForm, long startTimeStamp, long timeout, TimeUnit timeUnit) { invokeMark.set(true); } } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/a2a/A2aServerOperationServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.a2a; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.service.SyncEffectService; import com.alibaba.nacos.ai.service.a2a.identity.AgentIdCodecHolder; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentProvider; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.exception.ConfigAlreadyExistsException; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.pojo.Service; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.util.Collections; import java.util.LinkedList; import java.util.List; 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.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Unit tests for A2aServerOperationServiceTest. * * @author nacos */ @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) public class A2aServerOperationServiceTest { private static final String TEST_NAMESPACE_ID = "test-namespace"; private static final String TEST_AGENT_NAME = "test-agent"; private static final String TEST_AGENT_VERSION = "1.0.0"; private static final String TEST_REGISTRATION_TYPE = "service"; private static final String ENCODED_AGENT_NAME = "encoded-test-agent"; private static final String ENCODED_AGENT_NAME_WITH_VERSION = ENCODED_AGENT_NAME + "-" + TEST_AGENT_VERSION; @Mock private ConfigQueryChainService configQueryChainService; @Mock private ConfigOperationService configOperationService; @Mock private ConfigDetailService configDetailService; @Mock private SyncEffectService syncEffectService; @Mock private AgentIdCodecHolder agentIdCodecHolder; @Mock private ServiceStorage serviceStorage; private A2aServerOperationService a2aServerOperationService; @BeforeEach void setUp() { a2aServerOperationService = new A2aServerOperationService(configQueryChainService, configOperationService, configDetailService, syncEffectService, serviceStorage, agentIdCodecHolder); when(agentIdCodecHolder.encode(anyString())).thenReturn(ENCODED_AGENT_NAME); when(agentIdCodecHolder.encodeForSearch(anyString())).thenReturn(ENCODED_AGENT_NAME); } @Test void testRegisterAgentSuccess() throws NacosException { final AgentCard agentCard = buildTestAgentCard(); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenReturn(true); doNothing().when(syncEffectService).toSync(any(ConfigForm.class), anyLong()); a2aServerOperationService.registerAgent(agentCard, TEST_NAMESPACE_ID, TEST_REGISTRATION_TYPE); verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null)); verify(syncEffectService, times(1)).toSync(any(ConfigForm.class), anyLong()); } @Test void testRegisterAgentAlreadyExists() throws NacosException { AgentCard agentCard = buildTestAgentCard(); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenThrow(new ConfigAlreadyExistsException("Config already exists")); NacosApiException exception = assertThrows(NacosApiException.class, () -> { a2aServerOperationService.registerAgent(agentCard, TEST_NAMESPACE_ID, TEST_REGISTRATION_TYPE); }); assertEquals(NacosException.CONFLICT, exception.getErrCode()); assertEquals(ErrorCode.RESOURCE_CONFLICT.getCode(), exception.getDetailErrCode()); } @Test void testDeleteAgentSuccess() throws NacosException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(response.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.deleteConfig(eq(ENCODED_AGENT_NAME_WITH_VERSION), eq(Constants.A2A.AGENT_VERSION_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null))).thenReturn(true); a2aServerOperationService.deleteAgent(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION); verify(configOperationService, times(1)).deleteConfig(eq(ENCODED_AGENT_NAME_WITH_VERSION), eq(Constants.A2A.AGENT_VERSION_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null)); } @Test void testDeleteAgentDeleteAllVersions() throws NacosException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(response.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.deleteConfig(anyString(), anyString(), anyString(), any(), any(), anyString(), any())).thenReturn(true); // Test deleting all versions (version param is null) a2aServerOperationService.deleteAgent(TEST_NAMESPACE_ID, TEST_AGENT_NAME, null); // Should delete both version-specific config and main config verify(configOperationService, times(2)).deleteConfig(anyString(), anyString(), anyString(), any(), any(), anyString(), any()); } @Test void testDeleteAgentDeleteLastVersion() throws NacosException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(response.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.deleteConfig(eq(ENCODED_AGENT_NAME_WITH_VERSION), eq(Constants.A2A.AGENT_VERSION_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null))).thenReturn(true); when(configOperationService.deleteConfig(eq(ENCODED_AGENT_NAME), eq(Constants.A2A.AGENT_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null))).thenReturn(true); // Test deleting the last version - should also delete main config a2aServerOperationService.deleteAgent(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION); verify(configOperationService).deleteConfig(eq(ENCODED_AGENT_NAME_WITH_VERSION), eq(Constants.A2A.AGENT_VERSION_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null)); verify(configOperationService).deleteConfig(eq(ENCODED_AGENT_NAME), eq(Constants.A2A.AGENT_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null)); } @Test void testDeleteAgentWhenAgentNotFound() throws NacosException { ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); a2aServerOperationService.deleteAgent(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION); verify(configOperationService, times(0)).deleteConfig(anyString(), anyString(), anyString(), any(), any(), anyString(), any()); } @Test void testDeleteAgentWithVersionNotFoundInVersionDetails() throws NacosException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); // Change the version in version details to something else versionInfo.getVersionDetails().get(0).setVersion("2.0.0"); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(response.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.deleteConfig(anyString(), anyString(), anyString(), any(), any(), anyString(), any())).thenReturn(true); // Try to delete a version that doesn't exist in version details a2aServerOperationService.deleteAgent(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION); // Should still delete the config file even if not in version details verify(configOperationService).deleteConfig(eq(ENCODED_AGENT_NAME_WITH_VERSION), eq(Constants.A2A.AGENT_VERSION_GROUP), eq(TEST_NAMESPACE_ID), any(), any(), anyString(), any()); } @Test void testUpdateAgentCardSuccess() throws NacosException { final AgentCard agentCard = buildTestAgentCard(); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardVersionInfo())); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardDetailInfo())); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenReturn(true); doNothing().when(syncEffectService).toSync(any(ConfigForm.class), anyLong()); a2aServerOperationService.updateAgentCard(agentCard, TEST_NAMESPACE_ID, TEST_REGISTRATION_TYPE, true); verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null)); verify(syncEffectService, times(1)).toSync(any(ConfigForm.class), anyLong()); } @Test void testUpdateAgentCardWithExistingVersion() throws NacosException { final AgentCard agentCard = buildTestAgentCard(); AgentCardVersionInfo existingVersionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(existingVersionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardDetailInfo())); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenReturn(true); doNothing().when(syncEffectService).toSync(any(ConfigForm.class), anyLong()); // Update with existing version a2aServerOperationService.updateAgentCard(agentCard, TEST_NAMESPACE_ID, TEST_REGISTRATION_TYPE, true); verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null)); verify(syncEffectService).toSync(any(ConfigForm.class), anyLong()); } @Test void testUpdateAgentCardWithNewVersion() throws NacosException { final AgentCard agentCard = buildTestAgentCard(); AgentCardVersionInfo existingVersionInfo = buildTestAgentCardVersionInfo(); // Modify version to be different from agent card existingVersionInfo.getVersionDetails().get(0).setVersion("0.9.0"); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(existingVersionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardDetailInfo())); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenReturn(true); doNothing().when(syncEffectService).toSync(any(ConfigForm.class), anyLong()); // Update with new version - should add to version list a2aServerOperationService.updateAgentCard(agentCard, TEST_NAMESPACE_ID, TEST_REGISTRATION_TYPE, true); verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null)); verify(syncEffectService).toSync(any(ConfigForm.class), anyLong()); } @Test void testUpdateAgentCardWithoutRegistrationType() throws NacosException { final AgentCard agentCard = buildTestAgentCard(); AgentCardVersionInfo existingVersionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(existingVersionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardDetailInfo())); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenReturn(true); doNothing().when(syncEffectService).toSync(any(ConfigForm.class), anyLong()); // Update without registration type - should use existing one a2aServerOperationService.updateAgentCard(agentCard, TEST_NAMESPACE_ID, null, false); verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null)); verify(syncEffectService).toSync(any(ConfigForm.class), anyLong()); } @Test void testUpdateAgentCardNotSetAsLatest() throws NacosException { final AgentCard agentCard = buildTestAgentCard(); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardVersionInfo())); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardDetailInfo())); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenReturn(true); doNothing().when(syncEffectService).toSync(any(ConfigForm.class), anyLong()); // Update without setting as latest a2aServerOperationService.updateAgentCard(agentCard, TEST_NAMESPACE_ID, TEST_REGISTRATION_TYPE, false); verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null)); verify(syncEffectService, times(1)).toSync(any(ConfigForm.class), anyLong()); } @Test void testUpdateAgentCardSetAsLatestWithMultipleVersions() throws NacosException { final AgentCard agentCard = buildTestAgentCard(); AgentCardVersionInfo existingVersionInfo = buildTestAgentCardVersionInfo(); List newVersionDetail = new LinkedList<>(existingVersionInfo.getVersionDetails()); // Add another version AgentVersionDetail anotherVersion = new AgentVersionDetail(); anotherVersion.setVersion("0.9.0"); anotherVersion.setLatest(false); newVersionDetail.add(anotherVersion); existingVersionInfo.setVersionDetails(newVersionDetail); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(existingVersionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(buildTestAgentCardDetailInfo())); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null))).thenReturn(true); doNothing().when(syncEffectService).toSync(any(ConfigForm.class), anyLong()); // Update and set as latest - should update version details appropriately a2aServerOperationService.updateAgentCard(agentCard, TEST_NAMESPACE_ID, TEST_REGISTRATION_TYPE, true); verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), eq(null)); verify(syncEffectService).toSync(any(ConfigForm.class), anyLong()); } @Test void testListAgentsSuccess() throws NacosException { Page configPage = new Page<>(); ConfigInfo configInfo = new ConfigInfo(); AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); configInfo.setContent(JacksonUtils.toJson(versionInfo)); configPage.setPageItems(Collections.singletonList(configInfo)); configPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.A2A.SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.A2A.AGENT_GROUP), eq(TEST_NAMESPACE_ID), eq(null))).thenReturn(configPage); Page result = a2aServerOperationService.listAgents(TEST_NAMESPACE_ID, TEST_AGENT_NAME, Constants.A2A.SEARCH_BLUR, 1, 10); assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); } @Test void testListAgentsAccurateSearch() throws NacosException { Page configPage = new Page<>(); ConfigInfo configInfo = new ConfigInfo(); AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); configInfo.setContent(JacksonUtils.toJson(versionInfo)); configPage.setPageItems(Collections.singletonList(configInfo)); configPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.A2A.SEARCH_ACCURATE), eq(1), eq(10), anyString(), eq(Constants.A2A.AGENT_GROUP), eq(TEST_NAMESPACE_ID), eq(null))).thenReturn(configPage); Page result = a2aServerOperationService.listAgents(TEST_NAMESPACE_ID, TEST_AGENT_NAME, Constants.A2A.SEARCH_ACCURATE, 1, 10); assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); } @Test void testListAgentsEmptyAgentName() throws NacosException { Page configPage = new Page<>(); ConfigInfo configInfo = new ConfigInfo(); AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); configInfo.setContent(JacksonUtils.toJson(versionInfo)); configPage.setPageItems(Collections.singletonList(configInfo)); configPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.A2A.SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.A2A.AGENT_GROUP), eq(TEST_NAMESPACE_ID), eq(null))).thenReturn(configPage); Page result = a2aServerOperationService.listAgents(TEST_NAMESPACE_ID, null, Constants.A2A.SEARCH_BLUR, 1, 10); assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); } @Test void testListAgentVersionsSuccess() throws NacosApiException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(response.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); List result = a2aServerOperationService.listAgentVersions(TEST_NAMESPACE_ID, TEST_AGENT_NAME); assertNotNull(result); assertEquals(1, result.size()); } @Test void testQueryAgentCardVersionInfoNotFound() throws NacosApiException { ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); NacosApiException exception = assertThrows(NacosApiException.class, () -> { // This is a private method, but we can test it through public methods that use it a2aServerOperationService.listAgentVersions(TEST_NAMESPACE_ID, "non-existent-agent"); }); assertEquals(NacosException.NOT_FOUND, exception.getErrCode()); assertEquals(ErrorCode.AGENT_NOT_FOUND.getCode(), exception.getDetailErrCode()); } @Test void testGetAgentCardSuccess() throws NacosApiException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); AgentCardDetailInfo detailInfo = buildTestAgentCardDetailInfo(); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(detailInfo)); // Mock service storage for endpoint injection Service service = Service.newService(TEST_NAMESPACE_ID, Constants.A2A.AGENT_ENDPOINT_GROUP, ENCODED_AGENT_NAME + "::" + TEST_AGENT_VERSION); ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setHosts(Collections.emptyList()); when(serviceStorage.getData(service)).thenReturn(serviceInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); AgentCardDetailInfo result = a2aServerOperationService.getAgentCard(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION, TEST_REGISTRATION_TYPE); assertNotNull(result); assertEquals(TEST_AGENT_NAME, result.getName()); assertEquals(TEST_AGENT_VERSION, result.getVersion()); } @Test void testGetAgentCardNotFound() throws NacosApiException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); NacosApiException exception = assertThrows(NacosApiException.class, () -> { a2aServerOperationService.getAgentCard(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION, TEST_REGISTRATION_TYPE); }); assertEquals(NacosException.NOT_FOUND, exception.getErrCode()); assertEquals(ErrorCode.AGENT_VERSION_NOT_FOUND.getCode(), exception.getDetailErrCode()); } @Test void testGetAgentCardLatestVersion() throws NacosApiException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); AgentCardDetailInfo detailInfo = buildTestAgentCardDetailInfo(); detailInfo.setPreferredTransport(AiConstants.A2a.A2A_ENDPOINT_DEFAULT_TRANSPORT); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(detailInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); final Service service = Service.newService(TEST_NAMESPACE_ID, Constants.A2A.AGENT_ENDPOINT_GROUP, ENCODED_AGENT_NAME + "::" + TEST_AGENT_VERSION); final ServiceInfo serviceInfo = new ServiceInfo(); com.alibaba.nacos.api.naming.pojo.Instance instance = new com.alibaba.nacos.api.naming.pojo.Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); instance.getMetadata().put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, detailInfo.getPreferredTransport()); serviceInfo.addHost(instance); when(serviceStorage.getData(service)).thenReturn(serviceInfo); // Get latest version (version param is null) AgentCardDetailInfo result = a2aServerOperationService.getAgentCard(TEST_NAMESPACE_ID, TEST_AGENT_NAME, null, TEST_REGISTRATION_TYPE); assertNotNull(result); assertEquals(TEST_AGENT_NAME, result.getName()); assertEquals(TEST_AGENT_VERSION, result.getVersion()); assertEquals(Boolean.TRUE, result.isLatestVersion()); } @Test void testGetAgentCardLatestVersionNotFound() throws NacosApiException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); // No latest version versionInfo.getVersionDetails().get(0).setLatest(false); versionInfo.setLatestPublishedVersion(null); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse); NacosApiException exception = assertThrows(NacosApiException.class, () -> { a2aServerOperationService.getAgentCard(TEST_NAMESPACE_ID, TEST_AGENT_NAME, null, TEST_REGISTRATION_TYPE); }); assertEquals(NacosException.NOT_FOUND, exception.getErrCode()); assertEquals(ErrorCode.AGENT_VERSION_NOT_FOUND.getCode(), exception.getDetailErrCode()); } @Test void testGetAgentCardWithServiceRegistrationType() throws NacosApiException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); AgentCardDetailInfo detailInfo = buildTestAgentCardDetailInfo(); detailInfo.setRegistrationType("service"); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(detailInfo)); Service service = Service.newService(TEST_NAMESPACE_ID, Constants.A2A.AGENT_ENDPOINT_GROUP, ENCODED_AGENT_NAME + "::" + TEST_AGENT_VERSION); ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setHosts(Collections.emptyList()); when(serviceStorage.getData(service)).thenReturn(serviceInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); AgentCardDetailInfo result = a2aServerOperationService.getAgentCard(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION, "service"); assertNotNull(result); assertEquals(TEST_AGENT_NAME, result.getName()); assertEquals(TEST_AGENT_VERSION, result.getVersion()); } @Test void testGetAgentCardWithServiceEndpoints() throws NacosApiException { final AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); AgentCardDetailInfo detailInfo = buildTestAgentCardDetailInfo(); detailInfo.setRegistrationType("service"); detailInfo.setPreferredTransport(AiConstants.A2a.A2A_ENDPOINT_DEFAULT_TRANSPORT); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(detailInfo)); final Service service = Service.newService(TEST_NAMESPACE_ID, Constants.A2A.AGENT_ENDPOINT_GROUP, ENCODED_AGENT_NAME + "::" + TEST_AGENT_VERSION); // Create a service info with hosts final ServiceInfo serviceInfo = new ServiceInfo(); com.alibaba.nacos.api.naming.pojo.Instance instance = new com.alibaba.nacos.api.naming.pojo.Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); instance.getMetadata().put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, detailInfo.getPreferredTransport()); serviceInfo.addHost(instance); when(serviceStorage.getData(service)).thenReturn(serviceInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); AgentCardDetailInfo result = a2aServerOperationService.getAgentCard(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION, "service"); assertNotNull(result); assertEquals(TEST_AGENT_NAME, result.getName()); assertEquals(TEST_AGENT_VERSION, result.getVersion()); } @Test void testGetAgentCardWithEmptyRegistrationType() throws NacosApiException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); AgentCardDetailInfo detailInfo = buildTestAgentCardDetailInfo(); ConfigQueryChainResponse versionResponse = mock(ConfigQueryChainResponse.class); when(versionResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(versionResponse.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); ConfigQueryChainResponse detailResponse = mock(ConfigQueryChainResponse.class); when(detailResponse.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(detailResponse.getContent()).thenReturn(JacksonUtils.toJson(detailInfo)); Service service = Service.newService(TEST_NAMESPACE_ID, Constants.A2A.AGENT_ENDPOINT_GROUP, ENCODED_AGENT_NAME + "::" + TEST_AGENT_VERSION); ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setHosts(Collections.emptyList()); when(serviceStorage.getData(service)).thenReturn(serviceInfo); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(versionResponse) .thenReturn(detailResponse); // Pass empty registration type - should use the one from detailInfo AgentCardDetailInfo result = a2aServerOperationService.getAgentCard(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION, null); assertNotNull(result); assertEquals(TEST_AGENT_NAME, result.getName()); assertEquals(TEST_AGENT_VERSION, result.getVersion()); } @Test void testListAgentsWithNoAgentNameProvided() throws NacosException { Page configPage = new Page<>(); ConfigInfo configInfo = new ConfigInfo(); AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); configInfo.setContent(JacksonUtils.toJson(versionInfo)); configPage.setPageItems(Collections.singletonList(configInfo)); configPage.setTotalCount(1); when(configDetailService.findConfigInfoPage(eq(Constants.A2A.SEARCH_BLUR), eq(1), eq(10), anyString(), eq(Constants.A2A.AGENT_GROUP), eq(TEST_NAMESPACE_ID), eq(null))).thenReturn(configPage); // Call with empty agent name Page result = a2aServerOperationService.listAgents(TEST_NAMESPACE_ID, "", Constants.A2A.SEARCH_BLUR, 1, 10); assertNotNull(result); assertEquals(1, result.getTotalCount()); assertEquals(1, result.getPageItems().size()); } @Test void testDeleteAgentWithEmptyVersion() throws NacosException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(response.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.deleteConfig(anyString(), anyString(), anyString(), any(), any(), anyString(), any())).thenReturn(true); // Test deleting with empty version string - should behave like null a2aServerOperationService.deleteAgent(TEST_NAMESPACE_ID, TEST_AGENT_NAME, ""); // Should delete all versions verify(configOperationService, times(2)).deleteConfig(anyString(), anyString(), anyString(), any(), any(), anyString(), any()); } @Test void testDeleteAgentVersionNotLatest() throws NacosException { AgentCardVersionInfo versionInfo = buildTestAgentCardVersionInfo(); // Make it not the latest version versionInfo.setLatestPublishedVersion("2.0.0"); versionInfo.getVersionDetails().get(0).setLatest(false); List newVersionDetails = new LinkedList<>(versionInfo.getVersionDetails()); versionInfo.setVersionDetails(newVersionDetails); ConfigQueryChainResponse response = mock(ConfigQueryChainResponse.class); when(response.getStatus()).thenReturn(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(response.getContent()).thenReturn(JacksonUtils.toJson(versionInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); when(configOperationService.deleteConfig(eq(ENCODED_AGENT_NAME_WITH_VERSION), eq(Constants.A2A.AGENT_VERSION_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null))).thenReturn(true); // Add a second version so it doesn't delete the main config AgentVersionDetail secondVersion = new AgentVersionDetail(); secondVersion.setVersion("2.0.0"); secondVersion.setLatest(true); versionInfo.getVersionDetails().add(secondVersion); a2aServerOperationService.deleteAgent(TEST_NAMESPACE_ID, TEST_AGENT_NAME, TEST_AGENT_VERSION); verify(configOperationService, times(1)).deleteConfig(eq(ENCODED_AGENT_NAME_WITH_VERSION), eq(Constants.A2A.AGENT_VERSION_GROUP), eq(TEST_NAMESPACE_ID), eq(null), eq(null), eq("nacos"), eq(null)); } private AgentCard buildTestAgentCard() { AgentCard agentCard = new AgentCard(); agentCard.setName(TEST_AGENT_NAME); agentCard.setVersion(TEST_AGENT_VERSION); agentCard.setDescription("Test Agent Description"); AgentProvider agentProvider = new AgentProvider(); agentProvider.setOrganization("Test Organization"); agentCard.setProvider(agentProvider); agentCard.setPreferredTransport("http"); return agentCard; } private AgentCardDetailInfo buildTestAgentCardDetailInfo() { AgentCardDetailInfo detailInfo = new AgentCardDetailInfo(); detailInfo.setName(TEST_AGENT_NAME); detailInfo.setVersion(TEST_AGENT_VERSION); detailInfo.setDescription("Test Agent Description"); AgentProvider agentProvider = new AgentProvider(); agentProvider.setOrganization("Test Organization"); detailInfo.setProvider(agentProvider); detailInfo.setRegistrationType(TEST_REGISTRATION_TYPE); return detailInfo; } private AgentCardVersionInfo buildTestAgentCardVersionInfo() { AgentCardVersionInfo versionInfo = new AgentCardVersionInfo(); versionInfo.setName(TEST_AGENT_NAME); versionInfo.setVersion(TEST_AGENT_VERSION); versionInfo.setLatestPublishedVersion(TEST_AGENT_VERSION); versionInfo.setRegistrationType(TEST_REGISTRATION_TYPE); AgentVersionDetail versionDetail = new AgentVersionDetail(); versionDetail.setVersion(TEST_AGENT_VERSION); versionDetail.setLatest(true); versionInfo.setVersionDetails(Collections.singletonList(versionDetail)); return versionInfo; } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/a2a/identity/AgentIdCodecHolderTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.a2a.identity; 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.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.ObjectProvider; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AgentIdCodecHolderTest { @Mock private ObjectProvider agentIdCodecsProvider; @Mock private AgentIdCodec agentIdCodec; private AgentIdCodecHolder agentIdCodecHolder; @BeforeEach void setUp() { when(agentIdCodecsProvider.getIfAvailable(any())).thenReturn(agentIdCodec); agentIdCodecHolder = new AgentIdCodecHolder(agentIdCodecsProvider); } @Test void testConstructorWithNoAvailableCodec() { when(agentIdCodecsProvider.getIfAvailable(any())).thenCallRealMethod(); assertDoesNotThrow(() -> new AgentIdCodecHolder(agentIdCodecsProvider)); } @Test void testEncode() { when(agentIdCodec.encode("test-agent")).thenReturn("encoded-agent"); String result = agentIdCodecHolder.encode("test-agent"); assertEquals("encoded-agent", result); } @Test void testEncodeForSearch() { when(agentIdCodec.encodeForSearch("test-agent")).thenReturn("encoded-search-agent"); String result = agentIdCodecHolder.encodeForSearch("test-agent"); assertEquals("encoded-search-agent", result); } @Test void testDecode() { when(agentIdCodec.decode("encoded-agent")).thenReturn("test-agent"); String result = agentIdCodecHolder.decode("encoded-agent"); assertEquals("test-agent", result); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/a2a/identity/AsciiAgentIdCodecTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.a2a.identity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class AsciiAgentIdCodecTest { private AsciiAgentIdCodec agentIdCodec; @BeforeEach public void setUp() { agentIdCodec = new AsciiAgentIdCodec(); } // Does not encode when name is already valid @Test void testUsageDoesNotEncodeValidNames() { String input = "abc"; assertEquals(input, agentIdCodec.encode(input)); } // Round-trip encode/decode when input contains a space @Test void testValidSpecialCharsAreKept() { String input = "name-ok.:"; assertEquals(input, agentIdCodec.encode(input)); } @Test void testValidNamesWithNumber() { String input = "name-ok.1:2"; String encoded = agentIdCodec.encode(input); assertEquals("____:name-ok._049:_050", encoded); assertEquals(input, agentIdCodec.decode(encoded)); } @Test void testEncodeAndDecodeWhenNameContainsSpace() { String input = "hello world"; String encoded = agentIdCodec.encode(input); assertEquals("____:hello_032world", encoded); assertEquals(input, agentIdCodec.decode(encoded)); } // Valid special characters (._:-) should be preserved without encoding // Round-trip encode/decode for mixed unicode letters and ASCII @Test void testRoundTripUnicodeChars() { String input = " Ω test"; String encoded = agentIdCodec.encode(input); assertEquals("____:_032Ω_032test", encoded); String decoded = agentIdCodec.decode(encoded); assertEquals(input, decoded); } // Input starts with underscore followed by hex-like sequence; verify behavior policy @Test void testUnderscoreFollowedByHexAmbiguityHandledByPolicy() { String original = "1 _abcd"; String encoded = agentIdCodec.encode(original); assertEquals("____:_049_032_095abcd", encoded); assertEquals(original, agentIdCodec.decode(encoded)); } // Round-trip for extreme boundary code points (NUL and U+FF) @Test void testBoundaryCharacters() { String input = " ~"; String encoded = agentIdCodec.encode(input); assertEquals("____:_032_126", encoded); String decoded = agentIdCodec.decode(encoded); assertEquals(input, decoded); } // Encoding keeps empty string as-is and preserves a single underscore @Test void testEncodeKeepsEmptyAndUnderscore() { String empty = ""; String encodedEmpty = agentIdCodec.encode(empty); assertEquals("", encodedEmpty); String underscoreOnly = "_"; String encodedUnderscore = agentIdCodec.encode(underscoreOnly); assertEquals("____:_095", encodedUnderscore); } // Encoding is idempotent for already-encoded output; decode restores original @Test void testAlreadyEncodedStringIsIdempotentOnEncode() { String original = "with space and Ω and tab\t"; String first = agentIdCodec.encode(original); String second = agentIdCodec.encode(first); assertEquals("____:with_032space_032and_032Ω_032and_032tab_009", first); // encodeName should not double-encode an already valid string assertEquals(first, second); // decode should restore original assertEquals(original, agentIdCodec.decode(first)); } // Round-trip for mixture of ASCII, control (tab), and underscore suffix @Test void testMixedUnicodeAndControlCharactersRoundTrip() { String original = "A B\tC_"; String encoded = agentIdCodec.encode(original); assertEquals("____:A_032B_009C_095", encoded); String decoded = agentIdCodec.decode(encoded); assertEquals(original, decoded); } // Decoding a string with encoded prefix returns body; encoding leaves valid input unchanged @Test void testDecodeNameWithFakeEncodedPrefixBody() { String fake = "____:hello"; // This string is already valid; encodeName should return as-is assertEquals(fake, agentIdCodec.encode(fake)); // decodeName should strip prefix and return body unchanged assertEquals("hello", agentIdCodec.decode(fake)); } @Test void testDecodeWithoutPrefix() { String fake = "hello"; assertEquals(fake, agentIdCodec.decode(fake)); } @Test void testDecodeWithIllegalString() { String fake = "____:hello_02aworld"; assertEquals("hello_02aworld", agentIdCodec.decode(fake)); } @Test void testEncodeForSearchWithEmpty() { String empty = ""; assertEquals("", agentIdCodec.encodeForSearch(empty)); } @Test void testEncodeForNotEncode() { String input = "hello"; assertEquals(input, agentIdCodec.encodeForSearch(input)); } @Test void testEncodeForSearchWithUnderscore() { String underscore = "_"; assertEquals("_095", agentIdCodec.encodeForSearch(underscore)); } @Test void testEncodeForSearchWithSpecialChars() { String specialChars = "hello world"; assertEquals("hello_032world", agentIdCodec.encodeForSearch(specialChars)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/prompt/PromptAdminOperationServiceImplTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.ai.utils.PromptDataIdUtils; import com.alibaba.nacos.api.ai.model.prompt.PromptDescriptor; import com.alibaba.nacos.api.ai.model.prompt.PromptLabelVersionMapping; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; 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.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.HashMap; import java.util.List; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class PromptAdminOperationServiceImplTest { private static final String PROMPT_GROUP = "nacos-ai-prompt"; @Mock private ConfigOperationService configOperationService; @Mock private ConfigDetailService configDetailService; @Mock private ConfigInfoPersistService configInfoPersistService; private PromptAdminOperationServiceImpl service; @BeforeEach void setUp() { service = new PromptAdminOperationServiceImpl(configOperationService, configDetailService, configInfoPersistService); } @Test void publishPromptVersionShouldPublishVersionMetaAndLatestWhenFirstPublish() throws NacosException { String ns = "public"; String key = "p1"; String version = "1.0.0"; String mappingDataId = PromptDataIdUtils.buildLabelVersionMappingDataId(key); String versionDataId = PromptDataIdUtils.buildVersionDataId(key, version); when(configInfoPersistService.findConfigInfo(mappingDataId, PROMPT_GROUP, ns)).thenReturn(null); ConfigInfoWrapper latestSource = new ConfigInfoWrapper(); latestSource.setContent("{\"version\":\"1.0.0\",\"template\":\"hello\"}"); when(configInfoPersistService.findConfigInfo(versionDataId, PROMPT_GROUP, ns)).thenReturn(null) .thenReturn(latestSource); boolean result = service.publishPromptVersion(ns, key, version, "hello", "c1", "desc", List.of("a", "a", "b"), List.of(), "u1", "127.0.0.1"); assertTrue(result); ArgumentCaptor formCaptor = ArgumentCaptor.forClass(ConfigForm.class); ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(ConfigRequestInfo.class); verify(configOperationService, atLeastOnce()).publishConfig(formCaptor.capture(), reqCaptor.capture(), eq(null)); List forms = formCaptor.getAllValues(); assertEquals(4, forms.size()); assertTrue( forms.stream().anyMatch(f -> PromptDataIdUtils.buildVersionDataId(key, version).equals(f.getDataId()))); assertTrue(forms.stream() .anyMatch(f -> PromptDataIdUtils.buildLabelVersionMappingDataId(key).equals(f.getDataId()))); assertTrue(forms.stream().anyMatch(f -> PromptDataIdUtils.buildDescriptorDataId(key).equals(f.getDataId()))); assertTrue(forms.stream().anyMatch(f -> PromptDataIdUtils.buildLatestDataId(key).equals(f.getDataId()))); } @Test void publishPromptVersionShouldRejectDescriptionOrBizTagsWhenNotFirstPublish() { String ns = "public"; String key = "p1"; String version = "1.1.0"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); metaConfig.setMd5("m1"); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); assertThrows(NacosException.class, () -> service.publishPromptVersion(ns, key, version, "hello", null, "desc", null, List.of(), "u1", "127.0.0.1")); } @Test void bindLabelShouldThrowNotFoundWhenVersionNotExists() { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); metaConfig.setMd5("m1"); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); assertThrows(NacosException.class, () -> service.bindLabel(ns, key, "prod", "2.0.0", "u1", "127.0.0.1")); } @Test void queryPromptDetailShouldResolveByLabel() throws NacosException { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); mapping.getLabels().put("prod", "1.0.0"); mapping.setLatestVersion("1.0.0"); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); PromptVersionInfo versionInfo = new PromptVersionInfo(); versionInfo.setVersion("1.0.0"); versionInfo.setTemplate("hello"); ConfigAllInfo allInfo = new ConfigAllInfo(); allInfo.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(versionInfo)); allInfo.setMd5("m1"); allInfo.setCreateUser("u1"); when(configInfoPersistService.findConfigAllInfo(PromptDataIdUtils.buildVersionDataId(key, "1.0.0"), PROMPT_GROUP, ns)).thenReturn(allInfo); PromptVersionInfo actual = service.queryPromptDetail(ns, key, null, "prod"); assertEquals(key, actual.getPromptKey()); assertEquals("1.0.0", actual.getVersion()); assertEquals("m1", actual.getMd5()); assertEquals("u1", actual.getSrcUser()); } @Test void listPromptVersionsShouldSortBySemverDescAndPaginate() throws NacosException { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0", "2.0.0"))); mapping.setLabels(new HashMap<>()); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); PromptVersionInfo versionInfo = new PromptVersionInfo(); versionInfo.setVersion("2.0.0"); versionInfo.setCommitMsg("msg"); ConfigAllInfo allInfo = new ConfigAllInfo(); allInfo.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(versionInfo)); allInfo.setCreateUser("u1"); when(configInfoPersistService.findConfigAllInfo(PromptDataIdUtils.buildVersionDataId(key, "2.0.0"), PROMPT_GROUP, ns)).thenReturn(allInfo); List items = service.listPromptVersions(ns, key, 1, 1).getPageItems(); assertEquals(1, items.size()); assertEquals("2.0.0", items.get(0).getVersion()); } @Test void publishPromptVersionShouldThrowWhenVersionInvalid() { assertThrows(NacosException.class, () -> service.publishPromptVersion("public", "p1", "1.0", "hello", null, null, null, List.of(), "u1", "127.0.0.1")); } @Test void publishPromptVersionShouldThrowWhenTemplateBlank() { assertThrows(NacosException.class, () -> service.publishPromptVersion("public", "p1", "1.0.0", " ", null, null, null, List.of(), "u1", "127.0.0.1")); } @Test void publishPromptVersionShouldThrowConflictWhenMetaContainsVersion() { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); assertThrows(NacosException.class, () -> service.publishPromptVersion(ns, key, "1.0.0", "hello", null, null, null, List.of(), "u1", "127.0.0.1")); } @Test void publishPromptVersionShouldThrowConflictWhenVersionConfigAlreadyExists() { String ns = "public"; String key = "p1"; String version = "1.0.0"; ConfigInfoWrapper existedVersion = new ConfigInfoWrapper(); existedVersion.setContent("exists"); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(null); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildVersionDataId(key, version), PROMPT_GROUP, ns)).thenReturn(existedVersion); assertThrows(NacosException.class, () -> service.publishPromptVersion(ns, key, version, "hello", null, null, null, List.of(), "u1", "127.0.0.1")); } @Test void bindLabelShouldPublishMetaWhenSuccess() throws NacosException { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); metaConfig.setMd5("m1"); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); service.bindLabel(ns, key, "prod", "1.0.0", "u1", "127.0.0.1"); ArgumentCaptor formCaptor = ArgumentCaptor.forClass(ConfigForm.class); verify(configOperationService).publishConfig(formCaptor.capture(), any(ConfigRequestInfo.class), eq(null)); assertEquals(PromptDataIdUtils.buildLabelVersionMappingDataId(key), formCaptor.getValue().getDataId()); } @Test void unbindLabelShouldPublishMetaWhenSuccess() throws NacosException { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); mapping.getLabels().put("prod", "1.0.0"); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); metaConfig.setMd5("m1"); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); service.unbindLabel(ns, key, "prod", "u1", "127.0.0.1"); ArgumentCaptor formCaptor = ArgumentCaptor.forClass(ConfigForm.class); verify(configOperationService).publishConfig(formCaptor.capture(), any(ConfigRequestInfo.class), eq(null)); PromptLabelVersionMapping written = com.alibaba.nacos.common.utils.JacksonUtils.toObj( formCaptor.getValue().getContent(), PromptLabelVersionMapping.class); assertNull(written.getLabels().get("prod")); } @Test void deletePromptShouldDeleteMetaLatestAndAllVersionConfigs() throws NacosException { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0", "2.0.0"))); mapping.setLabels(new HashMap<>()); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); service.deletePrompt(ns, key, "u1", "127.0.0.1"); verify(configOperationService, times(5)).deleteConfig(anyString(), eq(PROMPT_GROUP), eq(ns), eq(null), eq("127.0.0.1"), eq("u1"), eq(null)); } @Test void updatePromptMetadataShouldPublishMergedMeta() throws NacosException { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); PromptDescriptor descriptor = new PromptDescriptor(); descriptor.setPromptKey(key); descriptor.setBizTags(new ArrayList<>(List.of("old"))); descriptor.setDescription("old"); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); ConfigInfoWrapper adminConfig = new ConfigInfoWrapper(); adminConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(descriptor)); metaConfig.setMd5("m1"); adminConfig.setMd5("m2"); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildDescriptorDataId(key), PROMPT_GROUP, ns)).thenReturn(adminConfig); service.updatePromptMetadata(ns, key, "new", List.of("a", "b"), "u1", "127.0.0.1"); ArgumentCaptor formCaptor = ArgumentCaptor.forClass(ConfigForm.class); verify(configOperationService).publishConfig(formCaptor.capture(), any(ConfigRequestInfo.class), eq(null)); assertEquals(PromptDataIdUtils.buildDescriptorDataId(key), formCaptor.getValue().getDataId()); PromptDescriptor written = com.alibaba.nacos.common.utils.JacksonUtils.toObj(formCaptor.getValue().getContent(), PromptDescriptor.class); assertEquals("new", written.getDescription()); assertEquals(2, written.getBizTags().size()); } @Test void listPromptsShouldUseMetaPatternAndFilterInvalidJson() throws NacosException { String ns = "public"; Page page = new Page<>(); page.setPageNumber(1); page.setPagesAvailable(1); page.setTotalCount(2); ConfigInfo ok = new ConfigInfo(); ok.setDataId("p1.descriptor.json"); PromptDescriptor descriptor = new PromptDescriptor(); descriptor.setPromptKey("p1"); descriptor.setBizTags(new ArrayList<>(List.of("x"))); ok.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(descriptor)); ConfigInfo bad = new ConfigInfo(); bad.setDataId("p2.descriptor.json"); bad.setContent("not-json"); page.setPageItems(List.of(ok, bad)); when(configDetailService.findConfigInfoPage(eq("blur"), eq(1), eq(10), eq("*p*.descriptor.json"), eq(PROMPT_GROUP), eq(ns), eq(null))).thenReturn(page); ConfigInfoWrapper mappingConfig = new ConfigInfoWrapper(); PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey("p1"); mapping.setLatestVersion("1.0.0"); mappingConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId("p1"), PROMPT_GROUP, ns)).thenReturn(mappingConfig); Page actual = service.listPrompts(ns, "p", "blur", null, 1, 10); assertEquals(1, actual.getPageItems().size()); } @Test void queryPromptDetailShouldThrowWhenVersionConfigMissing() { String ns = "public"; String key = "p1"; PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(key); mapping.setVersions(new ArrayList<>(List.of("1.0.0"))); mapping.setLabels(new HashMap<>()); mapping.setLatestVersion("1.0.0"); ConfigInfoWrapper metaConfig = new ConfigInfoWrapper(); metaConfig.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(mapping)); when(configInfoPersistService.findConfigInfo(PromptDataIdUtils.buildLabelVersionMappingDataId(key), PROMPT_GROUP, ns)).thenReturn(metaConfig); when(configInfoPersistService.findConfigAllInfo(PromptDataIdUtils.buildVersionDataId(key, "1.0.0"), PROMPT_GROUP, ns)).thenReturn(null); assertThrows(NacosException.class, () -> service.queryPromptDetail(ns, key, null, null)); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/prompt/PromptClientOperationServiceImplTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptLabelVersionMapping; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.config.server.utils.GroupKey2; 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 java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; 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.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.mockStatic; @ExtendWith(MockitoExtension.class) class PromptClientOperationServiceImplTest { @Mock private ConfigQueryChainService configQueryChainService; private PromptClientOperationServiceImpl service; @BeforeEach void setUp() { service = new PromptClientOperationServiceImpl(configQueryChainService); } @Test void queryPromptShouldThrowInvalidParamWhenPromptKeyBlank() { assertThrows(NacosException.class, () -> service.queryPrompt("public", "", null, null, null)); } @Test void queryPromptShouldThrowNotFoundWhenMetaMissing() { when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(notFoundResponse()); assertThrows(NacosException.class, () -> service.queryPrompt("public", "p1", null, null, null)); } @Test void queryPromptShouldReturnPromptVersionInfoWhenLabelResolved() throws NacosException { PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey("p1"); mapping.setLabels(new HashMap<>()); mapping.getLabels().put("prod", "1.0.0"); mapping.setVersions(new ArrayList<>()); mapping.getVersions().add("1.0.0"); mapping.setLatestVersion("1.0.0"); PromptVersionInfo versionInfo = new PromptVersionInfo(); versionInfo.setVersion("1.0.0"); versionInfo.setTemplate("hello"); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(foundResponse(mapping, "m-meta")) .thenReturn(foundResponse(versionInfo, "m-ver")); PromptVersionInfo actual = service.queryPrompt("public", "p1", null, "prod", null); assertEquals("p1", actual.getPromptKey()); assertEquals("1.0.0", actual.getVersion()); assertEquals("hello", actual.getTemplate()); assertEquals("m-ver", actual.getMd5()); } @Test void queryPromptShouldInvalidateMetaCacheWhenVersionDataMissing() throws Exception { PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey("p1"); mapping.setLabels(new HashMap<>()); mapping.setVersions(new ArrayList<>()); mapping.getVersions().add("1.0.0"); mapping.setLatestVersion("1.0.0"); String metaDataId = "p1.label-version-mapping.json"; String versionDataId = "p1.1.0.0.json"; when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenAnswer(invocation -> { ConfigQueryChainRequest request = invocation.getArgument(0); if (metaDataId.equals(request.getDataId())) { return foundResponse(mapping, "m-meta"); } if (versionDataId.equals(request.getDataId())) { return notFoundResponse(); } return notFoundResponse(); }); service.getPromptMeta("public", "p1"); assertEquals(1, getMetaCache().size()); assertThrows(NacosException.class, () -> service.queryPrompt("public", "p1", "1.0.0", null, null)); assertTrue(getMetaCache().isEmpty()); } @Test void getPromptMetaShouldReturnCloneAndUseCache() throws Exception { PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey("p1"); mapping.setLabels(new HashMap<>()); mapping.getLabels().put("prod", "1.0.0"); mapping.setVersions(new ArrayList<>()); mapping.getVersions().add("1.0.0"); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(foundResponse(mapping, "m-meta")); PromptMetaInfo first = service.getPromptMeta("public", "p1"); first.getLabels().put("gray", "2.0.0"); PromptMetaInfo second = service.getPromptMeta("public", "p1"); assertNotNull(second.getLabels().get("prod")); assertEquals(null, second.getLabels().get("gray")); verify(configQueryChainService, times(1)).handle(any(ConfigQueryChainRequest.class)); } @Test void queryPromptShouldThrowNotModifiedWhenMd5UpToDate() throws NacosException { PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey("p1"); mapping.setLabels(new HashMap<>()); mapping.setVersions(new ArrayList<>()); mapping.getVersions().add("1.0.0"); mapping.setLatestVersion("1.0.0"); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(foundResponse(mapping, "m-meta")); String groupKey = GroupKey2.getKey("p1.1.0.0.json", "nacos-ai-prompt", "public"); try (MockedStatic mocked = mockStatic(ConfigCacheService.class)) { mocked.when(() -> ConfigCacheService.isUptodate(groupKey, "m1")) .thenReturn(true); assertThrows(NacosException.class, () -> service.queryPrompt("public", "p1", "1.0.0", null, "m1")); } } @Test void getPromptMetaShouldReloadWhenCacheExpired() throws NacosException { String oldExpire = System.getProperty("nacos.prompt.meta.cache.expireSeconds"); System.setProperty("nacos.prompt.meta.cache.expireSeconds", "0"); try { PromptClientOperationServiceImpl expiredService = new PromptClientOperationServiceImpl(configQueryChainService); PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey("p1"); mapping.setLabels(new HashMap<>()); mapping.setVersions(new ArrayList<>()); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(foundResponse(mapping, "m1")) .thenReturn(foundResponse(mapping, "m2")); expiredService.getPromptMeta("public", "p1"); expiredService.getPromptMeta("public", "p1"); verify(configQueryChainService, times(2)).handle(any(ConfigQueryChainRequest.class)); } finally { if (oldExpire == null) { System.clearProperty("nacos.prompt.meta.cache.expireSeconds"); } else { System.setProperty("nacos.prompt.meta.cache.expireSeconds", oldExpire); } } } @Test void invalidateMetaCacheShouldNoopWhenPromptKeyBlank() throws Exception { PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey("p1"); mapping.setLabels(new HashMap<>()); mapping.setVersions(new ArrayList<>()); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(foundResponse(mapping, "m1")); service.getPromptMeta("public", "p1"); assertEquals(1, getMetaCache().size()); service.invalidateMetaCache("public", ""); assertEquals(1, getMetaCache().size()); } @Test void getPromptMetaShouldEvictWhenCacheReachMaxSize() throws Exception { String oldMax = System.getProperty("nacos.prompt.meta.cache.maxSize"); System.setProperty("nacos.prompt.meta.cache.maxSize", "1"); try { PromptClientOperationServiceImpl maxSizeService = new PromptClientOperationServiceImpl(configQueryChainService); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenAnswer(invocation -> { ConfigQueryChainRequest req = invocation.getArgument(0); PromptLabelVersionMapping mapping = new PromptLabelVersionMapping(); mapping.setPromptKey(req.getDataId().startsWith("p1") ? "p1" : "p2"); mapping.setLabels(new HashMap<>()); mapping.setVersions(new ArrayList<>()); return foundResponse(mapping, "m"); }); maxSizeService.getPromptMeta("public", "p1"); maxSizeService.getPromptMeta("public", "p2"); Field field = PromptClientOperationServiceImpl.class.getDeclaredField("metaCache"); field.setAccessible(true); Map cache = (Map) field.get(maxSizeService); assertEquals(1, cache.size()); } finally { if (oldMax == null) { System.clearProperty("nacos.prompt.meta.cache.maxSize"); } else { System.setProperty("nacos.prompt.meta.cache.maxSize", oldMax); } } } @SuppressWarnings("unchecked") private Map getMetaCache() throws Exception { Field field = PromptClientOperationServiceImpl.class.getDeclaredField("metaCache"); field.setAccessible(true); return (Map) field.get(service); } private ConfigQueryChainResponse foundResponse(Object content, String md5) { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); response.setContent(com.alibaba.nacos.common.utils.JacksonUtils.toJson(content)); response.setMd5(md5); return response; } private ConfigQueryChainResponse notFoundResponse() { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); return response; } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/prompt/PromptMetaCacheInvalidateServiceTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.utils.GroupKey; 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.junit.jupiter.MockitoExtension; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class PromptMetaCacheInvalidateServiceTest { @Mock private PromptClientOperationService promptClientOperationService; private PromptMetaCacheInvalidateService service; @BeforeEach void setUp() { service = new PromptMetaCacheInvalidateService(promptClientOperationService); } @Test void onEventShouldIgnoreWhenGroupNotPromptGroup() { LocalDataChangeEvent event = new LocalDataChangeEvent( GroupKey.getKey("p1.label-version-mapping.json", "other-group", "public")); service.onEvent(event); verify(promptClientOperationService, never()).invalidateMetaCache("public", "p1"); } @Test void onEventShouldIgnoreWhenDataIdNotMeta() { LocalDataChangeEvent event = new LocalDataChangeEvent(GroupKey.getKey("p1.descriptor.json", "nacos-ai-prompt", "public")); service.onEvent(event); verify(promptClientOperationService, never()).invalidateMetaCache("public", "p1"); } @Test void onEventShouldInvalidateCacheWhenMetaChanged() { LocalDataChangeEvent event = new LocalDataChangeEvent( GroupKey.getKey("p1.label-version-mapping.json", "nacos-ai-prompt", "public")); service.onEvent(event); verify(promptClientOperationService).invalidateMetaCache("public", "p1"); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/prompt/PromptMetaUtilsTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.prompt; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; 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.junit.jupiter.api.Assertions.assertTrue; class PromptMetaUtilsTest { @Test void normalizeMetaShouldInitCollectionsWhenNullFields() { PromptMetaInfo meta = new PromptMetaInfo(); PromptMetaInfo normalized = PromptMetaUtils.normalizeMeta(meta); assertNotNull(normalized.getLabels()); assertNotNull(normalized.getVersions()); assertNotNull(normalized.getBizTags()); } @Test void cloneMetaShouldDeepCopyCollections() { PromptMetaInfo meta = new PromptMetaInfo(); meta.setPromptKey("p1"); meta.setLabels(new HashMap<>()); meta.getLabels().put("prod", "1.0.0"); meta.setVersions(new ArrayList<>(List.of("1.0.0"))); meta.setBizTags(new ArrayList<>(List.of("finance"))); PromptMetaInfo copied = PromptMetaUtils.cloneMeta(meta); copied.getLabels().put("gray", "1.0.1"); copied.getVersions().add("1.0.1"); copied.getBizTags().add("ops"); assertEquals(1, meta.getLabels().size()); assertEquals(1, meta.getVersions().size()); assertEquals(1, meta.getBizTags().size()); } @Test void resolveTargetVersionShouldPreferLabelOverVersion() throws NacosException { PromptMetaInfo meta = new PromptMetaInfo(); meta.setLabels(new HashMap<>()); meta.getLabels().put("prod", "2.0.0"); meta.setVersions(new ArrayList<>(List.of("1.0.0", "2.0.0"))); meta.setLatestVersion("2.0.0"); String actual = PromptMetaUtils.resolveTargetVersion(meta, "1.0.0", "prod"); assertEquals("2.0.0", actual); } @Test void resolveTargetVersionShouldThrowWhenLabelNotFound() { PromptMetaInfo meta = new PromptMetaInfo(); meta.setLabels(new HashMap<>()); meta.setVersions(new ArrayList<>(List.of("1.0.0"))); meta.setLatestVersion("1.0.0"); assertThrows(NacosException.class, () -> PromptMetaUtils.resolveTargetVersion(meta, null, "prod")); } @Test void resolveTargetVersionShouldThrowWhenVersionInvalid() { PromptMetaInfo meta = new PromptMetaInfo(); meta.setLabels(new HashMap<>()); meta.setVersions(new ArrayList<>(List.of("1.0.0"))); meta.setLatestVersion("1.0.0"); assertThrows(NacosException.class, () -> PromptMetaUtils.resolveTargetVersion(meta, "1.0", null)); } @Test void resolveTargetVersionShouldFallbackToLatestOrThrow() throws NacosException { PromptMetaInfo meta = new PromptMetaInfo(); meta.setLabels(new HashMap<>()); meta.setVersions(new ArrayList<>(List.of("1.0.0"))); meta.setLatestVersion("1.0.0"); assertEquals("1.0.0", PromptMetaUtils.resolveTargetVersion(meta, null, null)); meta.setLatestVersion(null); NacosException ex = assertThrows(NacosException.class, () -> PromptMetaUtils.resolveTargetVersion(meta, null, null)); assertTrue(ex.getErrMsg().contains("latest version")); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/service/skills/SkillOperationServiceImplTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.service.skills; import com.alibaba.nacos.ai.service.SyncEffectService; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.ai.model.skills.SkillResource; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.exception.ConfigAlreadyExistsException; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; 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.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; 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.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Test for SkillOperationServiceImpl. * * @author nacos */ @ExtendWith(MockitoExtension.class) class SkillOperationServiceImplTest { @Mock private ConfigQueryChainService configQueryChainService; @Mock private ConfigOperationService configOperationService; @Mock private ConfigInfoPersistService configInfoPersistService; @Mock private SyncEffectService syncEffectService; private SkillOperationServiceImpl skillOperationService; @BeforeEach void setUp() { skillOperationService = new SkillOperationServiceImpl( configQueryChainService, configOperationService, configInfoPersistService, syncEffectService); } @Test void testRegisterSkillSuccessfully() throws NacosException { // Given Skill skill = createValidSkill(); String namespaceId = "test-namespace"; when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull())) .thenReturn(Boolean.TRUE); // When String result = skillOperationService.registerSkill(skill, namespaceId); // Then assertEquals(skill.getName(), result); verify(configOperationService, times(1)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull()); verify(syncEffectService, times(1)).toSync(any(ConfigForm.class), any(Long.class)); } @Test void testRegisterSkillWithResources() throws NacosException { // Given Skill skill = createValidSkillWithResources(); String namespaceId = "test-namespace"; when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull())) .thenReturn(Boolean.TRUE); // When String result = skillOperationService.registerSkill(skill, namespaceId); // Then assertEquals(skill.getName(), result); // Should publish main config + resource configs verify(configOperationService, times(2)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull()); } @Test void testRegisterSkillWithBlankName() { // Given Skill skill = createValidSkill(); skill.setName(""); String namespaceId = "test-namespace"; // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> skillOperationService.registerSkill(skill, namespaceId)); assertEquals("Skill name is required", exception.getMessage()); } @Test void testRegisterSkillWithInvalidName() { // Given Skill skill = createValidSkill(); skill.setName("invalid-name-123"); // Contains numbers String namespaceId = "test-namespace"; // When & Then assertThrows(NacosApiException.class, () -> skillOperationService.registerSkill(skill, namespaceId)); } @Test void testRegisterSkillWithDoubleUnderscore() throws NacosException { // Given: skill name and resource names may contain double underscores Skill skill = createValidSkill(); skill.setName("test__skill"); // Contains double underscore String namespaceId = "test-namespace"; when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull())) .thenReturn(Boolean.TRUE); // When String result = skillOperationService.registerSkill(skill, namespaceId); // Then assertEquals("test__skill", result); } @Test void testRegisterSkillAlreadyExists() throws NacosException { // Given Skill skill = createValidSkill(); String namespaceId = "test-namespace"; when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull())) .thenThrow(new ConfigAlreadyExistsException("Config already exists")); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> skillOperationService.registerSkill(skill, namespaceId)); assertEquals(NacosException.CONFLICT, exception.getErrCode()); } @Test void testGetSkillDetailSuccessfully() throws NacosException { // Given String namespaceId = "test-namespace"; String skillName = "test-skill"; ConfigQueryChainResponse response = createMockConfigResponse(); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(response); // When Skill skill = skillOperationService.getSkillDetail(namespaceId, skillName); // Then assertNotNull(skill); assertEquals(skillName, skill.getName()); assertEquals("Test description", skill.getDescription()); } @Test void testGetSkillDetailNotFound() { // Given String namespaceId = "test-namespace"; String skillName = "non-existent-skill"; ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(response); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> skillOperationService.getSkillDetail(namespaceId, skillName)); assertEquals(NacosException.NOT_FOUND, exception.getErrCode()); } @Test void testUpdateSkillSuccessfully() throws NacosException { // Given Skill skill = createValidSkill(); String namespaceId = "test-namespace"; ConfigQueryChainResponse response = createMockConfigResponse(); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(response); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull())) .thenReturn(Boolean.TRUE); // When skillOperationService.updateSkill(skill, namespaceId); // Then ArgumentCaptor requestInfoCaptor = ArgumentCaptor.forClass(ConfigRequestInfo.class); verify(configOperationService, times(1)).publishConfig(any(ConfigForm.class), requestInfoCaptor.capture(), isNull()); assertEquals(Boolean.TRUE, requestInfoCaptor.getValue().getUpdateForExist()); } @Test void testUpdateSkillNotFound() { // Given Skill skill = createValidSkill(); String namespaceId = "test-namespace"; ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(response); // When & Then assertThrows(NacosApiException.class, () -> skillOperationService.updateSkill(skill, namespaceId)); } @Test void testDeleteSkillSuccessfully() throws NacosException { // Given String namespaceId = "test-namespace"; String skillName = "test-skill"; ConfigQueryChainResponse response = createMockConfigResponse(); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(response); // When skillOperationService.deleteSkill(namespaceId, skillName); // Then verify(configOperationService, times(1)).deleteConfig(eq("skill.json"), eq("skill_" + skillName), eq(namespaceId), isNull(), isNull(), eq("nacos"), isNull()); } @Test void testDeleteSkillAlreadyDeleted() throws NacosException { // Given String namespaceId = "test-namespace"; String skillName = "test-skill"; ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))) .thenReturn(response); // When skillOperationService.deleteSkill(namespaceId, skillName); // Then verify(configOperationService, never()).deleteConfig(anyString(), anyString(), anyString(), isNull(), isNull(), anyString(), isNull()); } @Test void testListSkillsSuccessfully() throws NacosException { // Given String namespaceId = "test-namespace"; Page configInfoPage = createMockConfigInfoPage(); when(configInfoPersistService.findConfigInfoLike4Page(eq(1), eq(10), eq("skill.json"), anyString(), eq(namespaceId), isNull())) .thenReturn(configInfoPage); // When Page result = skillOperationService.listSkills(namespaceId, null, null, 1, 10); // Then assertNotNull(result); assertEquals(1, result.getPageNumber()); assertEquals(1, result.getPageItems().size()); } @Test void testUploadSkillFromZip() throws NacosException, IOException { // Given String namespaceId = "test-namespace"; byte[] zipBytes = createValidZipBytes(); when(configOperationService.publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull())) .thenReturn(Boolean.TRUE); // When String result = skillOperationService.uploadSkillFromZip(namespaceId, zipBytes); // Then assertNotNull(result); verify(configOperationService, times(1)).publishConfig(any(ConfigForm.class), any(ConfigRequestInfo.class), isNull()); } /** * Create a valid skill for testing. */ private Skill createValidSkill() { Skill skill = new Skill(); skill.setName("test-skill"); skill.setDescription("Test description"); skill.setInstruction("Test instruction"); return skill; } /** * Create a skill with resources for testing. */ private Skill createValidSkillWithResources() { Skill skill = createValidSkill(); Map resources = new HashMap<>(); SkillResource resource = new SkillResource(); resource.setName("test.sh"); resource.setType("script"); resource.setContent("#!/bin/bash"); resources.put("test", resource); skill.setResource(resources); return skill; } /** * Create a mock config response. */ private ConfigQueryChainResponse createMockConfigResponse() { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); Map mainConfig = new HashMap<>(); mainConfig.put("name", "test-skill"); mainConfig.put("description", "Test description"); mainConfig.put("instruction", "Test instruction"); mainConfig.put("resource", new HashMap<>()); response.setContent(JacksonUtils.toJson(mainConfig)); return response; } /** * Create a mock config info page. */ private Page createMockConfigInfoPage() { Page page = new Page<>(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId("skill.json"); configInfo.setGroup("skill_test-skill"); Map mainConfig = new HashMap<>(); mainConfig.put("name", "test-skill"); mainConfig.put("description", "Test description"); mainConfig.put("instruction", "Test instruction"); mainConfig.put("resource", new HashMap<>()); configInfo.setContent(JacksonUtils.toJson(mainConfig)); page.setPageItems(List.of(configInfo)); page.setTotalCount(1); page.setPageNumber(1); page.setPagesAvailable(1); return page; } /** * Create valid zip bytes for testing. */ private byte[] createValidZipBytes() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { // Add SKILL.md ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/AgentCardUtilTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCapabilities; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentInterface; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; import java.util.Map; 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.assertTrue; class AgentCardUtilTest { @Test void testBuildAgentCardDetailInfo() { // Given AgentCard agentCard = createTestAgentCard(); String registrationType = AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE; // When AgentCardDetailInfo result = AgentCardUtil.buildAgentCardDetailInfo(agentCard, registrationType); // Then assertNotNull(result); assertEquals(registrationType, result.getRegistrationType()); assertAgentCardEquals(agentCard, result); } @Test void testBuildAgentCardVersionInfoWithLatest() { // Given AgentCard agentCard = createTestAgentCard(); String registrationType = AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE; boolean isLatest = true; // When AgentCardVersionInfo result = AgentCardUtil.buildAgentCardVersionInfo(agentCard, registrationType, isLatest); // Then assertNotNull(result); assertEquals(registrationType, result.getRegistrationType()); assertEquals(agentCard.getVersion(), result.getLatestPublishedVersion()); assertNotNull(result.getVersionDetails()); assertEquals(1, result.getVersionDetails().size()); AgentVersionDetail versionDetail = result.getVersionDetails().get(0); assertEquals(agentCard.getVersion(), versionDetail.getVersion()); assertTrue(versionDetail.isLatest()); assertNotNull(versionDetail.getCreatedAt()); assertNotNull(versionDetail.getUpdatedAt()); } @Test void testBuildAgentCardVersionInfoWithoutLatest() { // Given AgentCard agentCard = createTestAgentCard(); String registrationType = AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE; boolean isLatest = false; // When AgentCardVersionInfo result = AgentCardUtil.buildAgentCardVersionInfo(agentCard, registrationType, isLatest); // Then assertNotNull(result); assertEquals(registrationType, result.getRegistrationType()); assertNotNull(result.getVersionDetails()); assertEquals(1, result.getVersionDetails().size()); AgentVersionDetail versionDetail = result.getVersionDetails().get(0); assertEquals(agentCard.getVersion(), versionDetail.getVersion()); assertEquals(isLatest, versionDetail.isLatest()); assertNotNull(versionDetail.getCreatedAt()); assertNotNull(versionDetail.getUpdatedAt()); } @Test void testBuildAgentVersionDetail() { // Given AgentCard agentCard = createTestAgentCard(); boolean isLatest = true; // When AgentVersionDetail result = AgentCardUtil.buildAgentVersionDetail(agentCard, isLatest); // Then assertNotNull(result); assertEquals(agentCard.getVersion(), result.getVersion()); assertEquals(isLatest, result.isLatest()); assertNotNull(result.getCreatedAt()); assertNotNull(result.getUpdatedAt()); } @Test void testUpdateUpdateTime() { // Given AgentVersionDetail versionDetail = new AgentVersionDetail(); versionDetail.setUpdatedAt("2023-01-01T00:00:00Z"); // When AgentCardUtil.updateUpdateTime(versionDetail); // Then assertNotNull(versionDetail.getUpdatedAt()); // We can't assert exact value since it's based on current time, but we can ensure it's not null } @Test void testBuildAgentInterfaceWithTlsSupport() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, "true"); metadata.put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, "JSONRPC"); metadata.put(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, "/agent"); instance.setMetadata(metadata); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("https://127.0.0.1:8080/agent", result.getUrl()); assertEquals("JSONRPC", result.getTransport()); } @Test void testBuildAgentInterfaceWithoutTlsSupport() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, "false"); metadata.put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, "JSONRPC"); metadata.put(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, "/agent"); instance.setMetadata(metadata); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("http://127.0.0.1:8080/agent", result.getUrl()); assertEquals("JSONRPC", result.getTransport()); } @Test void testBuildAgentInterfaceWithoutPath() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, "false"); metadata.put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, "JSONRPC"); instance.setMetadata(metadata); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("http://127.0.0.1:8080", result.getUrl()); assertEquals("JSONRPC", result.getTransport()); } @Test void testBuildAgentInterfaceWithPathWithoutLeadingSlash() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, "false"); metadata.put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, "JSONRPC"); metadata.put(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, "agent"); instance.setMetadata(metadata); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("http://127.0.0.1:8080/agent", result.getUrl()); assertEquals("JSONRPC", result.getTransport()); } @Test void testBuildAgentInterfaceWithProtocolField() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, "false"); metadata.put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, "GRPC"); metadata.put(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, "/agent"); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY, "grpc"); instance.setMetadata(metadata); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("grpc://127.0.0.1:8080/agent", result.getUrl()); assertEquals("GRPC", result.getTransport()); } @Test void testBuildAgentInterfaceWithQueryField() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, "false"); metadata.put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, "JSONRPC"); metadata.put(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, "/agent"); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY, "param1=value1¶m2=value2"); instance.setMetadata(metadata); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("http://127.0.0.1:8080/agent?param1=value1¶m2=value2", result.getUrl()); assertEquals("JSONRPC", result.getTransport()); } @Test void testBuildAgentInterfaceWithProtocolAndQueryFields() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS, "true"); metadata.put(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY, "JSONRPC"); metadata.put(Constants.A2A.AGENT_ENDPOINT_PATH_KEY, "/agent"); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY, "https"); metadata.put(Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY, "token=abc123"); instance.setMetadata(metadata); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("https://127.0.0.1:8080/agent?token=abc123", result.getUrl()); assertEquals("JSONRPC", result.getTransport()); } @Test void testBuildAgentInterfaceWithEmptyMetadata() { // Given Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(8080); instance.setMetadata(Collections.emptyMap()); // When AgentInterface result = AgentCardUtil.buildAgentInterface(instance); // Then assertNotNull(result); assertEquals("http://127.0.0.1:8080", result.getUrl()); assertEquals(null, result.getTransport()); } @Test void testGetCurrentTimeDoesNotThrow() { // This test ensures the private method getCurrentTime works without exceptions assertDoesNotThrow(() -> { // We can't directly test the private method, but we can test methods that use it AgentVersionDetail versionDetail = new AgentVersionDetail(); AgentCardUtil.updateUpdateTime(versionDetail); assertNotNull(versionDetail.getUpdatedAt()); }); } private AgentCard createTestAgentCard() { AgentCard agentCard = new AgentCard(); agentCard.setProtocolVersion("1.0"); agentCard.setName("test-agent"); agentCard.setDescription("Test Agent"); agentCard.setVersion("1.0.0"); agentCard.setIconUrl("http://example.com/icon.png"); agentCard.setCapabilities(new AgentCapabilities()); agentCard.setSkills(Collections.emptyList()); agentCard.setUrl("http://example.com/agent"); agentCard.setPreferredTransport("JSONRPC"); agentCard.setAdditionalInterfaces(Collections.emptyList()); agentCard.setDocumentationUrl("http://example.com/docs"); agentCard.setDefaultInputModes(Collections.emptyList()); agentCard.setDefaultOutputModes(Collections.emptyList()); agentCard.setSupportsAuthenticatedExtendedCard(false); return agentCard; } private void assertAgentCardEquals(AgentCard expected, AgentCard actual) { assertEquals(expected.getProtocolVersion(), actual.getProtocolVersion()); assertEquals(expected.getName(), actual.getName()); assertEquals(expected.getDescription(), actual.getDescription()); assertEquals(expected.getVersion(), actual.getVersion()); assertEquals(expected.getIconUrl(), actual.getIconUrl()); assertEquals(expected.getUrl(), actual.getUrl()); assertEquals(expected.getPreferredTransport(), actual.getPreferredTransport()); assertEquals(expected.getDocumentationUrl(), actual.getDocumentationUrl()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/AgentEndpointUtilTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; 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.junit.jupiter.api.Assertions.assertTrue; class AgentEndpointUtilTest { @Test void testTransferToInstance() throws NacosApiException { // Given AgentEndpoint endpoint = createTestAgentEndpoint(); // When Instance instance = AgentEndpointUtil.transferToInstance(endpoint); // Then assertNotNull(instance); assertEquals(endpoint.getAddress(), instance.getIp()); assertEquals(endpoint.getPort(), instance.getPort()); Map metadata = instance.getMetadata(); assertNotNull(metadata); assertEquals(endpoint.getPath(), metadata.get(Constants.A2A.AGENT_ENDPOINT_PATH_KEY)); assertEquals(endpoint.getTransport(), metadata.get(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY)); assertEquals(String.valueOf(endpoint.isSupportTls()), metadata.get(Constants.A2A.NACOS_AGENT_ENDPOINT_SUPPORT_TLS)); assertEquals(endpoint.getProtocol(), metadata.get(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY)); assertEquals(endpoint.getQuery(), metadata.get(Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY)); assertDoesNotThrow(instance::validate); } @Test void testTransferToInstanceWithEmptyFields() throws NacosApiException { // Given AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("127.0.0.1"); endpoint.setPort(8080); endpoint.setProtocol(""); // Leave other fields as null/empty // When Instance instance = AgentEndpointUtil.transferToInstance(endpoint); // Then assertNotNull(instance); assertEquals(endpoint.getAddress(), instance.getIp()); assertEquals(endpoint.getPort(), instance.getPort()); Map metadata = instance.getMetadata(); assertNotNull(metadata); assertEquals("", metadata.get(Constants.A2A.AGENT_ENDPOINT_PATH_KEY)); assertEquals("", metadata.get(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY)); assertEquals("", metadata.get(Constants.A2A.NACOS_AGENT_ENDPOINT_QUERY_KEY)); assertDoesNotThrow(instance::validate); } @Test void testTransferToInstanceWithGrpcProtocol() throws NacosApiException { // Given AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("127.0.0.1"); endpoint.setPort(8080); endpoint.setProtocol("grpc"); endpoint.setTransport("GRPC"); // When Instance instance = AgentEndpointUtil.transferToInstance(endpoint); // Then assertNotNull(instance); assertEquals(endpoint.getAddress(), instance.getIp()); assertEquals(endpoint.getPort(), instance.getPort()); Map metadata = instance.getMetadata(); assertNotNull(metadata); assertEquals(endpoint.getProtocol(), metadata.get(Constants.A2A.NACOS_AGENT_ENDPOINT_PROTOCOL_KEY)); assertEquals(endpoint.getTransport(), metadata.get(Constants.A2A.AGENT_ENDPOINT_TRANSPORT_KEY)); assertDoesNotThrow(instance::validate); } @Test void testTransferToInstances() throws NacosApiException { // Given Collection endpoints = Arrays.asList(createTestAgentEndpoint(), createAnotherTestAgentEndpoint()); // When List instances = AgentEndpointUtil.transferToInstances(endpoints); // Then assertNotNull(instances); assertEquals(2, instances.size()); Instance firstInstance = instances.get(0); assertEquals("127.0.0.1", firstInstance.getIp()); assertEquals(8080, firstInstance.getPort()); Instance secondInstance = instances.get(1); assertEquals("192.168.1.100", secondInstance.getIp()); assertEquals(9090, secondInstance.getPort()); // Validate all instances for (Instance instance : instances) { assertDoesNotThrow(instance::validate); } } @Test void testTransferToInstanceWithNullEndpoint() { // Given AgentEndpoint endpoint = null; // When & Then assertThrows(NullPointerException.class, () -> AgentEndpointUtil.transferToInstance(endpoint)); } @Test void testTransferToInstancesWithEmptyCollection() throws NacosApiException { // Given Collection endpoints = Arrays.asList(); // When List instances = AgentEndpointUtil.transferToInstances(endpoints); // Then assertNotNull(instances); assertTrue(instances.isEmpty()); } private AgentEndpoint createTestAgentEndpoint() { AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setTransport("JSONRPC"); endpoint.setAddress("127.0.0.1"); endpoint.setPort(8080); endpoint.setPath("/agent"); endpoint.setSupportTls(true); endpoint.setVersion("1.0.0"); endpoint.setProtocol("https"); endpoint.setQuery("param1=value1¶m2=value2"); return endpoint; } private AgentEndpoint createAnotherTestAgentEndpoint() { AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setTransport("GRPC"); endpoint.setAddress("192.168.1.100"); endpoint.setPort(9090); endpoint.setPath("/grpc-agent"); endpoint.setSupportTls(false); endpoint.setVersion("2.0.0"); endpoint.setProtocol("http"); endpoint.setQuery("token=abc123"); return endpoint; } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/AgentRequestUtilTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCapabilities; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.remote.request.AbstractAgentRequest; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.common.utils.JacksonUtils; import org.junit.jupiter.api.Test; import java.util.Collections; 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; class AgentRequestUtilTest { @Test void testParseAgentCardSuccessfully() throws NacosApiException { // Given AgentCardForm agentCardForm = new AgentCardForm(); AgentCard agentCard = createValidAgentCard(); String agentCardJson = JacksonUtils.toJson(agentCard); agentCardForm.setAgentCard(agentCardJson); // When AgentCard result = AgentRequestUtil.parseAgentCard(agentCardForm); // Then assertNotNull(result); assertEquals(agentCard.getName(), result.getName()); assertEquals(agentCard.getVersion(), result.getVersion()); assertEquals(agentCard.getProtocolVersion(), result.getProtocolVersion()); assertEquals(agentCard.getPreferredTransport(), result.getPreferredTransport()); assertEquals(agentCard.getUrl(), result.getUrl()); assertEquals(agentCard.getDescription(), result.getDescription()); } @Test void testParseAgentCardWithInvalidJson() { // Given AgentCardForm agentCardForm = new AgentCardForm(); agentCardForm.setAgentCard("{ invalid json }"); // When & Then assertThrows(NacosApiException.class, () -> AgentRequestUtil.parseAgentCard(agentCardForm)); } @Test void testValidateAgentCardWithValidCard() { // Given AgentCard agentCard = createValidAgentCard(); // When & Then assertDoesNotThrow(() -> AgentRequestUtil.validateAgentCard(agentCard)); } @Test void testValidateAgentCardWithMissingName() { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setName(null); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> AgentRequestUtil.validateAgentCard(agentCard)); assertEquals("Required parameter `agentCard.name` not present", exception.getMessage()); } @Test void testValidateAgentCardWithEmptyName() { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setName(""); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> AgentRequestUtil.validateAgentCard(agentCard)); assertEquals("Required parameter `agentCard.name` not present", exception.getMessage()); } @Test void testValidateAgentCardWithMissingVersion() { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setVersion(null); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> AgentRequestUtil.validateAgentCard(agentCard)); assertEquals("Required parameter `agentCard.version` not present", exception.getMessage()); } @Test void testValidateAgentCardWithMissingProtocolVersion() { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setProtocolVersion(null); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> AgentRequestUtil.validateAgentCard(agentCard)); assertEquals("Required parameter `agentCard.protocolVersion` not present", exception.getMessage()); } @Test void testValidateAgentCardWithMissingPreferredTransport() { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setPreferredTransport(null); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> AgentRequestUtil.validateAgentCard(agentCard)); assertEquals("Required parameter `agentCard.preferredTransport` not present", exception.getMessage()); } @Test void testValidateAgentCardWithMissingUrl() { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setUrl(null); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> AgentRequestUtil.validateAgentCard(agentCard)); assertEquals("Required parameter `agentCard.url` not present", exception.getMessage()); } @Test void testValidateAgentCardWithNullDescriptionShouldSetEmptyString() throws NacosApiException { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setDescription(null); // When AgentRequestUtil.validateAgentCard(agentCard); // Then assertEquals("", agentCard.getDescription()); } @Test void testValidateAgentCardWithNullCapabilitiesShouldSetDefault() throws NacosApiException { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setCapabilities(null); // When AgentRequestUtil.validateAgentCard(agentCard); // Then assertNotNull(agentCard.getCapabilities()); } @Test void testValidateAgentCardWithNullDefaultInputModesShouldSetEmptyList() throws NacosApiException { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setDefaultInputModes(null); // When AgentRequestUtil.validateAgentCard(agentCard); // Then assertNotNull(agentCard.getDefaultInputModes()); assertEquals(0, agentCard.getDefaultInputModes().size()); } @Test void testValidateAgentCardWithNullDefaultOutputModesShouldSetEmptyList() throws NacosApiException { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setDefaultOutputModes(null); // When AgentRequestUtil.validateAgentCard(agentCard); // Then assertNotNull(agentCard.getDefaultOutputModes()); assertEquals(0, agentCard.getDefaultOutputModes().size()); } @Test void testValidateAgentCardWithNullSkillsShouldSetEmptyList() throws NacosApiException { // Given AgentCard agentCard = createValidAgentCard(); agentCard.setSkills(null); // When AgentRequestUtil.validateAgentCard(agentCard); // Then assertNotNull(agentCard.getSkills()); assertEquals(0, agentCard.getSkills().size()); } @Test void testFillNamespaceIdWithEmptyNamespaceId() { // Given AbstractAgentRequest request = new AbstractAgentRequest() { }; request.setNamespaceId(""); // When AgentRequestUtil.fillNamespaceId(request); // Then assertEquals(AiConstants.A2a.A2A_DEFAULT_NAMESPACE, request.getNamespaceId()); } @Test void testFillNamespaceIdWithNullNamespaceId() { // Given AbstractAgentRequest request = new AbstractAgentRequest() { }; // When AgentRequestUtil.fillNamespaceId(request); // Then assertEquals(AiConstants.A2a.A2A_DEFAULT_NAMESPACE, request.getNamespaceId()); } @Test void testFillNamespaceIdWithValidNamespaceId() { // Given String customNamespaceId = "custom-namespace"; AbstractAgentRequest request = new AbstractAgentRequest() { }; request.setNamespaceId(customNamespaceId); // When AgentRequestUtil.fillNamespaceId(request); // Then assertEquals(customNamespaceId, request.getNamespaceId()); } private AgentCard createValidAgentCard() { AgentCard agentCard = new AgentCard(); agentCard.setProtocolVersion("1.0"); agentCard.setName("test-agent"); agentCard.setVersion("1.0.0"); agentCard.setPreferredTransport("JSONRPC"); agentCard.setUrl("http://example.com/agent"); agentCard.setDescription("Test Agent"); agentCard.setCapabilities(new AgentCapabilities()); agentCard.setDefaultInputModes(Collections.emptyList()); agentCard.setDefaultOutputModes(Collections.emptyList()); agentCard.setSkills(Collections.emptyList()); return agentCard; } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/McpConfigUtilsTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; 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 McpConfigUtilsTest { private static final String TEST_SERVER_ID = "test-server-id"; private static final String TEST_VERSION = "v1.0.0"; private static final String TEST_SERVER_NAME = "my-mcp-server"; @Test void testFormatServerVersionInfoDataId() { String result = McpConfigUtils.formatServerVersionInfoDataId(TEST_SERVER_ID); assertEquals("%s-mcp-versions.json", Constants.SERVER_VERSION_CONFIG_DATA_ID_TEMPLATE); assertEquals("test-server-id-mcp-versions.json", result); } @Test void testFormatServerSpecInfoDataId() { String result = McpConfigUtils.formatServerSpecInfoDataId(TEST_SERVER_ID, TEST_VERSION); assertEquals("%s-%s-mcp-server.json", Constants.SERVER_SPECIFICATION_CONFIG_DATA_ID_TEMPLATE); assertEquals("test-server-id-v1.0.0-mcp-server.json", result); } @Test void testFormatServerToolSpecDataId() { String result = McpConfigUtils.formatServerToolSpecDataId(TEST_SERVER_ID, TEST_VERSION); assertEquals("%s-%s-mcp-tools.json", Constants.SERVER_TOOLS_SPEC_CONFIG_DATA_ID_TEMPLATE); assertEquals("test-server-id-v1.0.0-mcp-tools.json", result); } @Test void testFormatServerNameTagBlurSearchValue() { String result = McpConfigUtils.formatServerNameTagBlurSearchValue(TEST_SERVER_NAME); assertEquals("mcpServerName=*my-mcp-server*", result); } @Test void testFormatServerNameTagAccurateSearchValue() { String result = McpConfigUtils.formatServerNameTagAccurateSearchValue(TEST_SERVER_NAME); assertEquals("mcpServerName=my-mcp-server", result); } @Test void testIsConfigFoundPositive() { assertTrue(McpConfigUtils.isConfigFound( ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL)); } @Test void testIsConfigFoundNegative() { assertFalse(McpConfigUtils.isConfigFound( ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND)); } @Test void testIsConfigNotFoundPositive() { assertTrue(McpConfigUtils.isConfigNotFound( ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND)); } @Test void testIsConfigNotFoundNegative() { assertFalse(McpConfigUtils.isConfigNotFound( ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL)); } @Test void testBuildMcpServerVersionConfigTagsWithValidName() { String result = McpConfigUtils.buildMcpServerVersionConfigTags(TEST_SERVER_NAME); assertEquals("nacos.internal.config=mcp,mcpServerName=my-mcp-server", result); } @Test void testBuildMcpServerVersionConfigTagsWithEmptyName() { String result = McpConfigUtils.buildMcpServerVersionConfigTags(""); assertEquals("nacos.internal.config=mcp,mcpServerName=", result); } @Test void testBuildMcpServerVersionConfigTagsWithNullName() { String result = McpConfigUtils.buildMcpServerVersionConfigTags(null); assertEquals("nacos.internal.config=mcp,mcpServerName=null", result); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/McpRequestUtilTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.form.mcp.admin.McpDetailForm; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServiceRef; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class McpRequestUtilTest { private static final String MCP_SERVER_SPEC_OLD = "{\"protocol\":\"stdio\",\"name\":\"nacos-mcp-server\"," + "\"description\":\"nacos local mcp server(test version)\",\"version\":\"0.1.0\",\"enabled\":true,\"localServerConfig\":{}}"; private static final String MCP_SERVER_SPEC_NEW = "{\"protocol\":\"stdio\",\"frontProtocol\":\"stdio\",\"name\":\"nacos-mcp-server\"," + "\"id\":\"\",\"description\":\"nacos local mcp server(test version)\",\"versionDetail\":{\"version\":\"1.0.0\"}," + "\"enabled\":true,\"localServerConfig\":{}}'"; private static final String MCP_TOOL_SPEC = "{\"tools\":[{\"name\":\"list_namespace\",\"description\":\"list namespace in nacos\"," + "\"inputSchema\":{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\",\"description\":\"aaa\"}}}," + "\"outputSchema\":{\"type\":\"object\",\"properties\":{\"result\":{\"type\":\"string\"}}}}]," + "\"toolsMeta\":{\"list_namespace\":{\"invokeContext\":{\"path\":\"/xxx\",\"method\":\"GET\"},\"enabled\":true," + "\"templates\":{\"json-go-tamplate\":{\"templateType\":\"string\",\"requestTemplate\":{\"url\":\"\",\"method\":\"GET\"," + "\"headers\":[],\"argsToJsonBody\":false,\"argsToUrlParam\":true,\"argsToFormBody\":true,\"body\":\"string\"}," + "\"responseTemplate\":{\"body\":\"string\"}}}}}}"; private static final String MCP_ENDPOINT_SPEC = "{\"type\":\"DIRECT\",\"data\":{\"address\":\"127.0.0.1\",\"port\":8848}}"; @Test void parseMcpServerBasicInfoWithOldData() throws NacosApiException { McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setServerSpecification(MCP_SERVER_SPEC_OLD); McpServerBasicInfo actual = McpRequestUtil.parseMcpServerBasicInfo(mcpForm); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, actual.getProtocol()); assertEquals("nacos-mcp-server", actual.getName()); assertEquals("nacos local mcp server(test version)", actual.getDescription()); assertEquals("0.1.0", actual.getVersion()); assertNull(actual.getVersionDetail()); assertTrue(actual.isEnabled()); assertTrue(actual.getLocalServerConfig().isEmpty()); } @Test void parseMcpServerBasicInfoWithNewData() throws NacosApiException { McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setServerSpecification(MCP_SERVER_SPEC_NEW); McpServerBasicInfo actual = McpRequestUtil.parseMcpServerBasicInfo(mcpForm); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, actual.getProtocol()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, actual.getFrontProtocol()); assertEquals("nacos-mcp-server", actual.getName()); assertEquals("nacos local mcp server(test version)", actual.getDescription()); assertNull(actual.getVersion()); assertEquals("1.0.0", actual.getVersionDetail().getVersion()); assertTrue(actual.isEnabled()); assertTrue(actual.getLocalServerConfig().isEmpty()); } @Test void parseMcpServerBasicInfoWithNewDataNoName() throws NacosApiException { McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setServerSpecification(MCP_SERVER_SPEC_NEW.replace("nacos-mcp-server", "")); mcpForm.setMcpName("nacos-mcp-server"); McpServerBasicInfo actual = McpRequestUtil.parseMcpServerBasicInfo(mcpForm); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, actual.getProtocol()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, actual.getFrontProtocol()); assertEquals("nacos-mcp-server", actual.getName()); assertEquals("nacos local mcp server(test version)", actual.getDescription()); assertNull(actual.getVersion()); assertEquals("1.0.0", actual.getVersionDetail().getVersion()); assertTrue(actual.isEnabled()); assertTrue(actual.getLocalServerConfig().isEmpty()); } @Test void parseMcpServerBasicInfoWithWrongData() { McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setServerSpecification("{"); assertThrows(NacosApiException.class, () -> McpRequestUtil.parseMcpServerBasicInfo(mcpForm), "serverSpecification or toolSpecification is invalid. Can't be parsed."); } @Test void parseMcpToolsWithoutToolSpec() throws NacosApiException { McpDetailForm mcpForm = new McpDetailForm(); assertNull(McpRequestUtil.parseMcpTools(mcpForm)); } @Test void parseMcpToolsWithWrongData() { McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setToolSpecification("{"); assertThrows(NacosApiException.class, () -> McpRequestUtil.parseMcpTools(mcpForm), "serverSpecification or toolSpecification is invalid. Can't be parsed."); } @Test void parseMcpToolsSuccess() throws NacosApiException { McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setToolSpecification(MCP_TOOL_SPEC); McpToolSpecification actual = McpRequestUtil.parseMcpTools(mcpForm); assertEquals(1, actual.getTools().size()); assertEquals("list_namespace", actual.getTools().get(0).getName()); assertEquals("list namespace in nacos", actual.getTools().get(0).getDescription()); assertEquals(2, actual.getTools().get(0).getInputSchema().size()); assertNotNull(actual.getTools().get(0).getOutputSchema()); assertEquals("object", actual.getTools().get(0).getOutputSchema().get("type")); assertEquals(1, actual.getToolsMeta().size()); assertNotNull(actual.getToolsMeta().get("list_namespace")); assertNotNull(actual.getToolsMeta().get("list_namespace").getInvokeContext()); assertTrue(actual.getToolsMeta().get("list_namespace").isEnabled()); assertNotNull(actual.getToolsMeta().get("list_namespace").getTemplates()); } @Test void parseMcpEndpointSpecForStdioType() throws NacosApiException { McpServerBasicInfo mcpServerBasicInfo = new McpServerBasicInfo(); mcpServerBasicInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setEndpointSpecification(MCP_ENDPOINT_SPEC); assertNull(McpRequestUtil.parseMcpEndpointSpec(mcpServerBasicInfo, mcpForm)); } @Test void parseMcpEndpointSpecWithoutSpec() { McpServerBasicInfo mcpServerBasicInfo = new McpServerBasicInfo(); mcpServerBasicInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); assertThrows(NacosApiException.class, () -> McpRequestUtil.parseMcpEndpointSpec(mcpServerBasicInfo, new McpDetailForm()), "request parameter `endpointSpecification` is required if mcp server type not `local`."); } @Test void parseMcpEndpointSpecSuccess() throws NacosApiException { McpServerBasicInfo mcpServerBasicInfo = new McpServerBasicInfo(); mcpServerBasicInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); McpDetailForm mcpForm = new McpDetailForm(); mcpForm.setEndpointSpecification(MCP_ENDPOINT_SPEC); McpEndpointSpec actual = McpRequestUtil.parseMcpEndpointSpec(mcpServerBasicInfo, mcpForm); assertEquals(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT, actual.getType()); assertEquals(2, actual.getData().size()); assertEquals("127.0.0.1", actual.getData().get("address")); assertEquals("8848", actual.getData().get("port")); } @Test void transferToMcpServiceRefForMcpServiceRef() { McpServiceRef mcpServiceRef = new McpServiceRef(); McpServiceRef actual = McpRequestUtil.transferToMcpServiceRef(mcpServiceRef); assertEquals(mcpServiceRef, actual); } @Test void transferToMcpServiceRefForMap() { Map input = new HashMap<>(); input.put("namespaceId", "test"); input.put("groupName", "testGroup"); input.put("serviceName", "testService"); McpServiceRef actual = McpRequestUtil.transferToMcpServiceRef(input); assertEquals("test", actual.getNamespaceId()); assertEquals("testGroup", actual.getGroupName()); assertEquals("testService", actual.getServiceName()); } @Test void transferToMcpServiceRefForOther() { assertThrows(IllegalArgumentException.class, () -> McpRequestUtil.transferToMcpServiceRef(new Object())); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/McpRequestUtilsTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.remote.request.QueryMcpServerRequest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class McpRequestUtilsTest { @Test void fillNamespaceId() { QueryMcpServerRequest request = new QueryMcpServerRequest(); McpRequestUtil.fillNamespaceId(request); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, request.getNamespaceId()); request.setNamespaceId("test"); McpRequestUtil.fillNamespaceId(request); assertEquals("test", request.getNamespaceId()); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/PromptDataIdUtilsTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; 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.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class PromptDataIdUtilsTest { @Test void buildDataIdsShouldReturnExpectedFormat() { assertEquals("p1.descriptor.json", PromptDataIdUtils.buildMetaDataId("p1")); assertEquals("p1.descriptor.json", PromptDataIdUtils.buildDescriptorDataId("p1")); assertEquals("p1.label-version-mapping.json", PromptDataIdUtils.buildLabelVersionMappingDataId("p1")); assertEquals("p1.json", PromptDataIdUtils.buildLatestDataId("p1")); assertEquals("p1.1.0.0.json", PromptDataIdUtils.buildVersionDataId("p1", "1.0.0")); } @Test void isMetaDataIdShouldMatchOnlyMetaSuffix() { assertTrue(PromptDataIdUtils.isMetaDataId("p1.descriptor.json")); assertTrue(PromptDataIdUtils.isDescriptorDataId("p1.descriptor.json")); assertTrue(PromptDataIdUtils.isLabelVersionMappingDataId("p1.label-version-mapping.json")); assertFalse(PromptDataIdUtils.isMetaDataId("p1.json")); assertFalse(PromptDataIdUtils.isMetaDataId("")); } @Test void extractPromptKeyFromMetaDataIdShouldReturnNullForInvalid() { assertEquals("p1", PromptDataIdUtils.extractPromptKeyFromMetaDataId("p1.descriptor.json")); assertEquals("p1", PromptDataIdUtils.extractPromptKeyFromLabelVersionMappingDataId("p1.label-version-mapping.json")); assertNull(PromptDataIdUtils.extractPromptKeyFromMetaDataId("p1.json")); } } ================================================ FILE: ai/src/test/java/com/alibaba/nacos/ai/utils/SkillZipParserTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.ai.utils; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillResource; import com.alibaba.nacos.api.ai.model.skills.SkillUtils; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.Base64; import java.util.Map; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test for SkillZipParser. * * @author nacos */ class SkillZipParserTest { @Test void testParseSkillFromZipWithValidSkillMd() throws Exception { // Given byte[] zipBytes = createValidSkillZip(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then assertNotNull(skill); assertEquals("test-skill", skill.getName()); assertEquals("Test skill description", skill.getDescription()); assertEquals("This is a test instruction", skill.getInstruction().trim()); assertEquals("test-namespace", skill.getNamespaceId()); } @Test void testParseSkillFromZipWithSkillMdInSubdir() throws Exception { // Given byte[] zipBytes = createSkillZipWithSubdir(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then assertNotNull(skill); assertEquals("test-skill", skill.getName()); assertEquals("Test skill description", skill.getDescription()); } @Test void testParseSkillFromZipWithResources() throws Exception { // Given byte[] zipBytes = createSkillZipWithResources(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then assertNotNull(skill); assertNotNull(skill.getResource()); assertTrue(skill.getResource().size() > 0); } @Test void testParseSkillFromZipWithEscapedYamlValues() throws Exception { // Given byte[] zipBytes = createZipWithEscapedYamlValues(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then assertNotNull(skill); assertEquals("test\\skill\"name", skill.getName()); assertEquals("desc\\folder\"quoted", skill.getDescription()); } @Test void testParseSkillFromZipWithoutSkillMd() throws IOException { // Given byte[] zipBytes = createZipWithoutSkillMd(); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace")); assertTrue(exception.getMessage().contains("SKILL.md file not found")); } @Test void testParseSkillFromZipWithInvalidYaml() throws IOException { // Given byte[] zipBytes = createZipWithInvalidYaml(); // When & Then assertThrows(NacosApiException.class, () -> SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace")); } @Test void testParseSkillFromZipWithMissingName() throws IOException { // Given byte[] zipBytes = createZipWithMissingName(); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace")); assertTrue(exception.getMessage().contains("name")); } @Test void testParseSkillFromZipWithMissingDescription() throws IOException { // Given byte[] zipBytes = createZipWithMissingDescription(); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace")); assertTrue(exception.getMessage().contains("description")); } @Test void testParseSkillFromZipWithInstructionsHeader() throws Exception { // Given byte[] zipBytes = createZipWithInstructionsHeader(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then assertNotNull(skill); assertTrue(skill.getInstruction().contains("instruction content")); } @Test void testParseSkillFromZipWithBinaryResource() throws Exception { // Given: zip with a .ttf (binary) and SKILL.md byte[] zipBytes = createSkillZipWithBinaryResource(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then: binary resource is Base64-encoded and has metadata encoding=base64 (key = generateResourceId("canvas-fonts", "font.ttf")) String fontKey = SkillUtils.generateResourceId("canvas-fonts", "font.ttf"); assertNotNull(skill); assertNotNull(skill.getResource()); assertTrue(skill.getResource().containsKey(fontKey)); SkillResource font = skill.getResource().get(fontKey); assertEquals("font.ttf", font.getName()); assertNotNull(font.getContent()); assertTrue(font.getContent().length() > 0); byte[] decoded = Base64.getDecoder().decode(font.getContent()); assertNotNull(decoded); assertEquals(4, decoded.length); assertEquals(0, decoded[0]); assertEquals(1, decoded[1]); Map meta = font.getMetadata(); assertNotNull(meta); assertEquals("base64", meta.get("encoding")); } @Test void testParseSkillFromZipExceedsSizeLimit() throws IOException { // Given: zip larger than MAX_UPLOAD_ZIP_BYTES (10MB) int overSize = (int) (Constants.Skills.MAX_UPLOAD_ZIP_BYTES + 1024); byte[] zipBytes = createValidSkillZip(); byte[] largeZip = new byte[overSize]; System.arraycopy(zipBytes, 0, largeZip, 0, zipBytes.length); // When & Then NacosApiException exception = assertThrows(NacosApiException.class, () -> SkillZipParser.parseSkillFromZip(largeZip, "test-namespace")); assertTrue(exception.getMessage().contains("must not exceed")); assertTrue(exception.getMessage().contains("10")); } @Test void testParseSkillFromZipIgnoresMacOsMetadataFiles() throws Exception { // Given: zip contains macOS AppleDouble file (._LICENSE.txt) and normal resource byte[] zipBytes = createSkillZipWithMacOsMetadataFiles(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then: skill parses OK and ._* files are not in resources (key = generateResourceId("references", "readme.md")) String readmeKey = SkillUtils.generateResourceId("references", "readme.md"); assertNotNull(skill); assertNotNull(skill.getResource()); assertEquals(1, skill.getResource().size()); assertTrue(skill.getResource().containsKey(readmeKey)); assertFalse(skill.getResource().containsKey("._LICENSE")); assertFalse(skill.getResource().keySet().stream().anyMatch(k -> k.startsWith("._"))); } @Test void testParseSkillFromZipIncludesFilesUnderSkillRoot() throws Exception { // Given: zip with file directly under skill folder (e.g. algorithmic-art/LICENSE.txt) byte[] zipBytes = createSkillZipWithFileUnderSkillRoot(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then: LICENSE.txt is included as resource with empty type (key = generateResourceId("", "LICENSE.txt")) String licenseKey = SkillUtils.generateResourceId("", "LICENSE.txt"); assertNotNull(skill); assertNotNull(skill.getResource()); assertTrue(skill.getResource().containsKey(licenseKey)); assertEquals("LICENSE.txt", skill.getResource().get(licenseKey).getName()); assertEquals("", skill.getResource().get(licenseKey).getType() == null ? "" : skill.getResource().get(licenseKey).getType()); assertTrue(skill.getResource().get(licenseKey).getContent().contains("MIT License")); } /** * Create a valid skill zip file. */ private byte[] createValidSkillZip() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { // Add SKILL.md ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a skill zip with SKILL.md in subdirectory. */ private byte[] createSkillZipWithSubdir() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { // Add SKILL.md in subdirectory ZipEntry entry = new ZipEntry("test-skill/SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a skill zip with resources. */ private byte[] createSkillZipWithResources() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { // Add SKILL.md ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); // Add resource file entry = new ZipEntry("scripts/test.sh"); zos.putNextEntry(entry); zos.write("#!/bin/bash\necho 'test'".getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a zip without SKILL.md. */ private byte[] createZipWithoutSkillMd() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("other-file.txt"); zos.putNextEntry(entry); zos.write("content".getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a zip with invalid YAML. */ private byte[] createZipWithInvalidYaml() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "Invalid content without YAML front matter"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a zip with missing name. */ private byte[] createZipWithMissingName() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a zip with missing description. */ private byte[] createZipWithMissingDescription() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a zip with escaped YAML values in front matter. */ private byte[] createZipWithEscapedYamlValues() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: \"test\\\\skill\\\"name\"\n" + "description: \"desc\\\\folder\\\"quoted\"\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a skill zip with a file directly under skill root (skillName/LICENSE.txt). * Parser should include it as resource with empty type. */ private byte[] createSkillZipWithFileUnderSkillRoot() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("test-skill/SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); entry = new ZipEntry("test-skill/LICENSE.txt"); zos.putNextEntry(entry); zos.write("MIT License\nCopyright (c) 2025".getBytes()); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a skill zip that contains macOS metadata files (._*) like ._LICENSE.txt. * Parser should ignore them and only include normal resources. */ private byte[] createSkillZipWithMacOsMetadataFiles() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("test-skill/SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); entry = new ZipEntry("test-skill/references/readme.md"); zos.putNextEntry(entry); zos.write("# Readme".getBytes()); zos.closeEntry(); entry = new ZipEntry("test-skill/._LICENSE.txt"); zos.putNextEntry(entry); zos.write(new byte[] { 0, 5, 0, 0 }); // binary AppleDouble-like content zos.closeEntry(); } return baos.toByteArray(); } /** * Create a skill zip that contains a binary file (.ttf). Parser should store it as Base64 with metadata encoding=base64. */ private byte[] createSkillZipWithBinaryResource() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("test-skill/SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; zos.write(skillMd.getBytes()); zos.closeEntry(); entry = new ZipEntry("test-skill/canvas-fonts/font.ttf"); zos.putNextEntry(entry); zos.write(new byte[] { 0, 1, 2, 3 }); // minimal binary content zos.closeEntry(); } return baos.toByteArray(); } /** * Create a zip with Instructions header. */ private byte[] createZipWithInstructionsHeader() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "## Instructions\n" + "instruction content"; zos.write(skillMd.getBytes()); zos.closeEntry(); } return baos.toByteArray(); } @Test void testParseSkillFromZipWithUtf8Bom() throws Exception { // Given: SKILL.md content starts with UTF-8 BOM (EF BB BF) byte[] zipBytes = createSkillZipWithUtf8Bom(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then: BOM is stripped and skill parses correctly assertNotNull(skill); assertEquals("test-skill", skill.getName()); assertEquals("Test skill description", skill.getDescription()); assertEquals("This is a test instruction", skill.getInstruction().trim()); } @Test void testParseSkillFromZipWithUtf8BomInSubdir() throws Exception { // Given: SKILL.md in subdirectory with UTF-8 BOM byte[] zipBytes = createSkillZipWithUtf8BomInSubdir(); // When Skill skill = SkillZipParser.parseSkillFromZip(zipBytes, "test-namespace"); // Then: BOM is stripped and skill parses correctly assertNotNull(skill); assertEquals("test-skill", skill.getName()); assertEquals("Test skill description", skill.getDescription()); } /** * Create a skill zip where SKILL.md content starts with UTF-8 BOM (EF BB BF). */ private byte[] createSkillZipWithUtf8Bom() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("SKILL.md"); zos.putNextEntry(entry); // UTF-8 BOM bytes followed by normal SKILL.md content String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; byte[] bom = new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF }; byte[] content = skillMd.getBytes("UTF-8"); byte[] withBom = new byte[bom.length + content.length]; System.arraycopy(bom, 0, withBom, 0, bom.length); System.arraycopy(content, 0, withBom, bom.length, content.length); zos.write(withBom); zos.closeEntry(); } return baos.toByteArray(); } /** * Create a skill zip where SKILL.md in subdirectory has UTF-8 BOM. */ private byte[] createSkillZipWithUtf8BomInSubdir() throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ZipOutputStream zos = new ZipOutputStream(baos)) { ZipEntry entry = new ZipEntry("test-skill/SKILL.md"); zos.putNextEntry(entry); String skillMd = "---\n" + "name: test-skill\n" + "description: Test skill description\n" + "---\n\n" + "This is a test instruction"; byte[] bom = new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF }; byte[] content = skillMd.getBytes("UTF-8"); byte[] withBom = new byte[bom.length + content.length]; System.arraycopy(bom, 0, withBom, 0, bom.length); System.arraycopy(content, 0, withBom, bom.length, content.length); zos.write(withBom); zos.closeEntry(); } return baos.toByteArray(); } } ================================================ FILE: api/pom.xml ================================================ com.alibaba.nacos nacos-all ${revision} 4.0.0 nacos-api jar nacos-api ${project.version} https://nacos.io Nacos api pom.xml file ${client.java.version} ${client.java.version} com.fasterxml.jackson.core jackson-core com.fasterxml.jackson.core jackson-databind org.springframework spring-test test io.grpc grpc-netty-shaded io.grpc grpc-protobuf io.grpc grpc-stub io.grpc grpc-util io.grpc grpc-inprocess com.google.api.grpc proto-google-common-protos com.google.protobuf protobuf-java javax.annotation javax.annotation-api maven-compiler-plugin ${maven-compiler-plugin.version} ${client.java.version} ${client.java.version} ${client.java.version} true true -parameters maven-compiler-plugin ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/NacosFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api; import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.LockService; import com.alibaba.nacos.api.lock.NacosLockFactory; import com.alibaba.nacos.api.naming.NamingFactory; import com.alibaba.nacos.api.naming.NamingMaintainFactory; import com.alibaba.nacos.api.naming.NamingMaintainService; import com.alibaba.nacos.api.naming.NamingService; import java.util.Properties; /** * Nacos Factory. * * @author Nacos */ public class NacosFactory { /** * Create config service. * * @param properties init param * @return config * @throws NacosException Exception */ public static ConfigService createConfigService(Properties properties) throws NacosException { return ConfigFactory.createConfigService(properties); } /** * Create config service. * * @param serverAddr server list * @return config * @throws NacosException Exception */ public static ConfigService createConfigService(String serverAddr) throws NacosException { return ConfigFactory.createConfigService(serverAddr); } /** * Create naming service. * * @param serverAddr server list * @return Naming * @throws NacosException Exception */ public static NamingService createNamingService(String serverAddr) throws NacosException { return NamingFactory.createNamingService(serverAddr); } /** * Create naming service. * * @param properties init param * @return Naming * @throws NacosException Exception */ public static NamingService createNamingService(Properties properties) throws NacosException { return NamingFactory.createNamingService(properties); } /** * Create maintain service. * * @param serverAddr server address * @return NamingMaintainService * @throws NacosException Exception * @deprecated use {@link com.alibaba.nacos.maintainer.client.naming.NamingMaintainerFactory} in nacos-maintainer-client artifact tp replaced. */ @Deprecated public static NamingMaintainService createMaintainService(String serverAddr) throws NacosException { return NamingMaintainFactory.createMaintainService(serverAddr); } /** * Create maintain service. * * @param properties server address * @return NamingMaintainService * @throws NacosException Exception * @deprecated use {@link com.alibaba.nacos.maintainer.client.naming.NamingMaintainerFactory} in nacos-maintainer-client artifact tp replaced. */ @Deprecated public static NamingMaintainService createMaintainService(Properties properties) throws NacosException { return NamingMaintainFactory.createMaintainService(properties); } /** * Create lock service. * * @param properties init param * @return lock service * @throws NacosException Exception */ public static LockService createLockService(Properties properties) throws NacosException { return NacosLockFactory.createLockService(properties); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/PropertyKeyConst.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api; /** * Property Key Const. * * @author Nacos */ public class PropertyKeyConst { public static final String IS_USE_CLOUD_NAMESPACE_PARSING = "isUseCloudNamespaceParsing"; public static final String IS_USE_ENDPOINT_PARSING_RULE = "isUseEndpointParsingRule"; public static final String ENDPOINT = "endpoint"; public static final String ENDPOINT_QUERY_PARAMS = "endpointQueryParams"; public static final String ENDPOINT_PORT = "endpointPort"; public static final String ENDPOINT_CONTEXT_PATH = "endpointContextPath"; public static final String ENDPOINT_CLUSTER_NAME = "endpointClusterName"; public static final String ENDPOINT_REFRESH_INTERVAL_SECONDS = "endpointRefreshIntervalSeconds"; @Deprecated public static final String SERVER_NAME = "serverName"; public static final String NAMESPACE = "namespace"; public static final String USERNAME = "username"; public static final String PASSWORD = "password"; public static final String ACCESS_KEY = "accessKey"; public static final String SECRET_KEY = "secretKey"; public static final String RAM_ROLE_NAME = "ramRoleName"; public static final String SERVER_ADDR = "serverAddr"; public static final String CONTEXT_PATH = "contextPath"; /** * Please use {@link #ENDPOINT_CLUSTER_NAME} replaced. */ @Deprecated public static final String CLUSTER_NAME = "clusterName"; /** * Default is {@code "false"}, if true, and without {@link #ENDPOINT_CLUSTER_NAME}, use {@link #CLUSTER_NAME} to set * endpoint cluster name. */ @Deprecated public static final String IS_ADAPT_CLUSTER_NAME_USAGE = "isAdaptClusterNameUsage"; public static final String ENCODE = "encode"; public static final String CONFIG_LONG_POLL_TIMEOUT = "configLongPollTimeout"; public static final String CONFIG_RETRY_TIME = "configRetryTime"; public static final String CONFIG_REQUEST_TIMEOUT = "configRequestTimeout"; public static final String CLIENT_WORKER_MAX_THREAD_COUNT = "clientWorkerMaxThreadCount"; public static final String CLIENT_WORKER_THREAD_COUNT = "clientWorkerThreadCount"; public static final String MAX_RETRY = "maxRetry"; public static final String ENABLE_REMOTE_SYNC_CONFIG = "enableRemoteSyncConfig"; public static final String NAMING_LOAD_CACHE_AT_START = "namingLoadCacheAtStart"; public static final String NAMING_CACHE_REGISTRY_DIR = "namingCacheRegistryDir"; public static final String NAMING_CLIENT_BEAT_THREAD_COUNT = "namingClientBeatThreadCount"; public static final String NAMING_POLLING_MAX_THREAD_COUNT = "namingPollingMaxThreadCount"; public static final String NAMING_POLLING_THREAD_COUNT = "namingPollingThreadCount"; public static final String NAMING_REQUEST_DOMAIN_RETRY_COUNT = "namingRequestDomainMaxRetryCount"; public static final String NAMING_PUSH_EMPTY_PROTECTION = "namingPushEmptyProtection"; public static final String NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE = "namingAsyncQuerySubscribeService"; public static final String REDO_DELAY_TIME = "redoDelayTime"; public static final String REDO_DELAY_THREAD_COUNT = "redoDelayThreadCount"; public static final String SIGNATURE_REGION_ID = "signatureRegionId"; public static final String LOG_ALL_PROPERTIES = "logAllProperties"; /** * Since 2.3.3, For some situation like java agent using nacos-client which can't use env ram info. */ public static final String IS_USE_RAM_INFO_PARSING = "isUseRamInfoParsing"; /** * Get the key value of some variable value from the system property. */ public static class SystemEnv { public static final String ALIBABA_ALIWARE_ENDPOINT_PORT = "ALIBABA_ALIWARE_ENDPOINT_PORT"; public static final String ALIBABA_ALIWARE_ENDPOINT_CONTEXT_PATH = "ALIBABA_ALIWARE_ENDPOINT_CONTEXT_PATH"; public static final String ALIBABA_ALIWARE_NAMESPACE = "ALIBABA_ALIWARE_NAMESPACE"; public static final String ALIBABA_ALIWARE_ENDPOINT_URL = "ALIBABA_ALIWARE_ENDPOINT_URL"; } /** * Client Metric Switch. */ public static final String ENABLE_CLIENT_METRICS = "enableClientMetrics"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/SystemPropertyKeyConst.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api; /** * Support for reading the value of the specified variable from the -D parameter. * *

Properties that are preferred to which in {@link PropertyKeyConst} * * @author pbting */ public interface SystemPropertyKeyConst { String NAMING_SERVER_PORT = "nacos.naming.exposed.port"; /** * In the cloud (Alibaba Cloud or other cloud vendors) environment, whether to enable namespace resolution in the * cloud environment. *

* The default is on. *

*/ String IS_USE_CLOUD_NAMESPACE_PARSING = "nacos.use.cloud.namespace.parsing"; /** * In the cloud environment, if the process level requires a globally uniform namespace, it can be specified with * the -D parameter. */ String ANS_NAMESPACE = "ans.namespace"; /** * It is also supported by the -D parameter. */ String IS_USE_ENDPOINT_PARSING_RULE = "nacos.use.endpoint.parsing.rule"; /** * Since 2.3.3, For some situation like java agent using nacos-client which can't use env ram info. */ String IS_USE_RAM_INFO_PARSING = "nacos.use.ram.info.parsing"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/ClientAbilities.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability; import com.alibaba.nacos.api.config.ability.ClientConfigAbility; import com.alibaba.nacos.api.naming.ability.ClientNamingAbility; import com.alibaba.nacos.api.remote.ability.ClientRemoteAbility; import java.io.Serializable; /** * abilities of nacos client. * * @author liuzunfei * @version $Id: ClientAbilities.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ @Deprecated public class ClientAbilities implements Serializable { private static final long serialVersionUID = -3590789441404549261L; private ClientRemoteAbility remoteAbility = new ClientRemoteAbility(); private ClientConfigAbility configAbility = new ClientConfigAbility(); private ClientNamingAbility namingAbility = new ClientNamingAbility(); public ClientRemoteAbility getRemoteAbility() { return remoteAbility; } public void setRemoteAbility(ClientRemoteAbility remoteAbility) { this.remoteAbility = remoteAbility; } public ClientConfigAbility getConfigAbility() { return configAbility; } public void setConfigAbility(ClientConfigAbility configAbility) { this.configAbility = configAbility; } public ClientNamingAbility getNamingAbility() { return namingAbility; } public void setNamingAbility(ClientNamingAbility namingAbility) { this.namingAbility = namingAbility; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/ServerAbilities.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability; import com.alibaba.nacos.api.config.ability.ServerConfigAbility; import com.alibaba.nacos.api.naming.ability.ServerNamingAbility; import com.alibaba.nacos.api.remote.ability.ServerRemoteAbility; import java.io.Serializable; import java.util.Objects; /** * abilities of nacos server. * * @author liuzunfei * @version $Id: ServerAbilities.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ @Deprecated public class ServerAbilities implements Serializable { private static final long serialVersionUID = -2120543002911304171L; private ServerRemoteAbility remoteAbility = new ServerRemoteAbility(); private ServerConfigAbility configAbility = new ServerConfigAbility(); private ServerNamingAbility namingAbility = new ServerNamingAbility(); public ServerRemoteAbility getRemoteAbility() { return remoteAbility; } public void setRemoteAbility(ServerRemoteAbility remoteAbility) { this.remoteAbility = remoteAbility; } public ServerConfigAbility getConfigAbility() { return configAbility; } public void setConfigAbility(ServerConfigAbility configAbility) { this.configAbility = configAbility; } public ServerNamingAbility getNamingAbility() { return namingAbility; } public void setNamingAbility(ServerNamingAbility namingAbility) { this.namingAbility = namingAbility; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServerAbilities that = (ServerAbilities) o; return Objects.equals(remoteAbility, that.remoteAbility) && Objects.equals(configAbility, that.configAbility) && Objects.equals(namingAbility, that.namingAbility); } @Override public int hashCode() { return Objects.hash(remoteAbility, configAbility, namingAbility); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/constant/AbilityKey.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.constant; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; /** * Ability key constant. It is used to constrain the ability key.
* Ensure that return value of {@link AbilityKey#getName()} is unique under one specify {@link AbilityMode}. * * @author Daydreamer * @date 2022/8/31 12:27 **/ public enum AbilityKey { /** * Server support register or deregister persistent instance by grpc. */ SERVER_PERSISTENT_INSTANCE_BY_GRPC("supportPersistentInstanceByGrpc", "support persistent instance by grpc", AbilityMode.SERVER), /** * For fuzzy watch naming or config. */ SERVER_FUZZY_WATCH("fuzzyWatch", "Server whether support fuzzy watch service or config", AbilityMode.SERVER), /** * For Distributed Lock. */ SERVER_DISTRIBUTED_LOCK("lock", "Server whether support distributed lock", AbilityMode.SERVER), /** * For AI module MCP registry. */ SERVER_MCP_REGISTRY("mcp", "Server whether support release mcp server and register endpoint for mcp server", AbilityMode.SERVER), /** * For AI module Agent & Agent Card registry. */ SERVER_AGENT_REGISTRY("agent", "Server whether support release agent server and register endpoint for agent server", AbilityMode.SERVER), /** * For fuzzy watch naming or config. */ SDK_CLIENT_FUZZY_WATCH("fuzzyWatch", "Client whether support fuzzy watch service or config", AbilityMode.SDK_CLIENT), /** * For Distributed Lock. */ SDK_CLIENT_DISTRIBUTED_LOCK("lock", "Client whether support distributed lock", AbilityMode.SDK_CLIENT), /** * For AI module MCP registry. */ SDK_MCP_REGISTRY("mcp", "Client whether support release mcp server and register endpoint for mcp server", AbilityMode.SDK_CLIENT), /** * For AI module Agent & Agent Card registry. */ SDK_AGENT_REGISTRY("agent", "Client whether support release agent server and register endpoint for agent server", AbilityMode.SDK_CLIENT), /** * For Test temporarily. */ CLUSTER_CLIENT_TEST_1("test_1", "just for junit test", AbilityMode.CLUSTER_CLIENT); /** * the name of a certain ability. */ private final String keyName; /** * description or comment about this ability. */ private final String description; /** * ability mode, which endpoint hold this ability. */ private final AbilityMode mode; AbilityKey(String keyName, String description, AbilityMode mode) { this.keyName = keyName; this.description = description; this.mode = mode; } public String getName() { return keyName; } public String getDescription() { return description; } public AbilityMode getMode() { return mode; } /** * All key set. */ private static final Map> ALL_ABILITIES = new HashMap<>(); /** * Get all keys. * * @return all keys */ public static Collection getAllValues(AbilityMode mode) { return Collections.unmodifiableCollection(ALL_ABILITIES.get(mode).values()); } /** * Get all names. * * @return all names */ public static Collection getAllNames(AbilityMode mode) { return Collections.unmodifiableCollection(ALL_ABILITIES.get(mode).keySet()); } /** * Whether contains this name. * * @param name key name * @return whether contains */ public static boolean isLegalKey(AbilityMode mode, String name) { return ALL_ABILITIES.get(mode).containsKey(name); } /** * Map the string key to enum. * * @param abilities map * @return enum map */ public static Map mapEnum(AbilityMode mode, Map abilities) { if (abilities == null || abilities.isEmpty()) { return Collections.emptyMap(); } return abilities.entrySet().stream().filter(entry -> isLegalKey(mode, entry.getKey())) .collect(Collectors.toMap((entry) -> getEnum(mode, entry.getKey()), Map.Entry::getValue)); } /**. * Map the string key to enum * * @param abilities map * @return enum map */ public static Map mapStr(Map abilities) { if (abilities == null || abilities.isEmpty()) { return Collections.emptyMap(); } return abilities.entrySet().stream() .collect(Collectors.toMap((entry) -> entry.getKey().getName(), Map.Entry::getValue)); } /**. * getter to obtain enum * * @param key string key * @return enum */ public static AbilityKey getEnum(AbilityMode mode, String key) { return ALL_ABILITIES.get(mode).get(key); } static { // check for developer // ensure that name filed is unique under a AbilityMode try { for (AbilityKey value : AbilityKey.values()) { AbilityMode mode = value.getMode(); Map map = ALL_ABILITIES.getOrDefault(mode, new HashMap<>()); AbilityKey previous = map.putIfAbsent(value.getName(), value); if (previous != null) { throw new IllegalStateException( "Duplicate key name field " + value + " and " + previous + " under mode: " + mode); } ALL_ABILITIES.put(mode, map); } } catch (Throwable t) { // for developer checking t.printStackTrace(); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/constant/AbilityMode.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.constant; /** * Ability mode. * * @author Daydreamer * @date 2023/9/25 12:32 **/ public enum AbilityMode { /** * for server ability. */ SERVER, /** * for sdk client. */ SDK_CLIENT, /** * for cluster client. */ CLUSTER_CLIENT; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/constant/AbilityStatus.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.constant; /**. * @author Daydreamer * @description It is used to know a certain ability whether supporting. * @date 2022/8/31 12:27 **/ public enum AbilityStatus { /**. * Support a certain ability */ SUPPORTED, /**. * Not support a certain ability */ NOT_SUPPORTED, /**. * Cannot find ability table, unknown */ UNKNOWN } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/initializer/AbilityInitializer.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.initializer; /** * Nacos ability initializer. * * @author xiweng.yy */ @Deprecated public interface AbilityInitializer { /** * Initialize target type abilities content. * * @param abilities abilities */ void initialize(A abilities); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/initializer/AbilityPostProcessor.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.initializer; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import java.util.Map; /** * Nacos ability post processor, load by spi. * * @author Daydreamer-ia */ public interface AbilityPostProcessor { /** * process before loading by Ability Controller . * * @param mode mode: sdk client, server or cluster client * @param abilities abilities */ void process(AbilityMode mode, Map abilities); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/register/AbstractAbilityRegistry.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.register; import com.alibaba.nacos.api.ability.constant.AbilityKey; import java.util.Collections; import java.util.HashMap; import java.util.Map; /**. * @author Daydreamer * @description Operation for bit table. * @date 2022/7/12 19:23 **/ public abstract class AbstractAbilityRegistry { protected final Map supportedAbilities = new HashMap<>(); /**. * get static ability current server supports * * @return static ability */ public Map getSupportedAbilities() { return Collections.unmodifiableMap(supportedAbilities); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/register/impl/ClusterClientAbilities.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.register.impl; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.register.AbstractAbilityRegistry; import java.util.Map; /** * It is used to register cluster client abilities. * * @author Daydreamer **/ public class ClusterClientAbilities extends AbstractAbilityRegistry { private static final ClusterClientAbilities INSTANCE = new ClusterClientAbilities(); { /* * example: * There is a function named "compression". * The key is from

AbilityKey

, the value is whether turn on. * * You can add a new public field in

AbilityKey

like: * DATA_COMPRESSION("compression", "description about this ability") * * And then you need to declare whether turn on in the ability table, you can: * supportedAbilities.put(AbilityKey.DATA_COMPRESSION, true); means that current client support compression. * */ // put ability here, which you want current client supports } /** * get static ability current cluster client supports. * * @return static ability */ public static Map getStaticAbilities() { return INSTANCE.getSupportedAbilities(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/register/impl/SdkClientAbilities.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.register.impl; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.register.AbstractAbilityRegistry; import java.util.Map; /** * It is used to register client abilities. * * @author Daydreamer * @date 2022/8/31 12:32 **/ public class SdkClientAbilities extends AbstractAbilityRegistry { private static final SdkClientAbilities INSTANCE = new SdkClientAbilities(); { /* * example: * There is a function named "compression". * The key is from

AbilityKey

, the value is whether turn on. * * You can add a new public field in

AbilityKey

like: * DATA_COMPRESSION("compression", "description about this ability") * * And then you need to declare whether turn on in the ability table, you can: * supportedAbilities.put(AbilityKey.DATA_COMPRESSION, true); means that current client support compression. * */ // put ability here, which you want current client supports supportedAbilities.put(AbilityKey.SDK_CLIENT_FUZZY_WATCH, true); supportedAbilities.put(AbilityKey.SDK_CLIENT_DISTRIBUTED_LOCK, true); supportedAbilities.put(AbilityKey.SDK_MCP_REGISTRY, true); supportedAbilities.put(AbilityKey.SDK_AGENT_REGISTRY, true); } /**. * get static ability current server supports * * @return static ability */ public static Map getStaticAbilities() { return INSTANCE.getSupportedAbilities(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ability/register/impl/ServerAbilities.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.register.impl; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.register.AbstractAbilityRegistry; import java.util.Map; /** * It is used to register server abilities. * * @author Daydreamer * @date 2022/8/31 12:32 **/ public class ServerAbilities extends AbstractAbilityRegistry { private static final ServerAbilities INSTANCE = new ServerAbilities(); { /* * example: * There is a function named "compression". * The key is from

AbilityKey

, the value is whether turn on. * * You can add a new public field in

AbilityKey

like: * DATA_COMPRESSION("compression", "description about this ability") * * And then you need to declare whether turn on in the ability table, you can: * supportedAbilities.put(AbilityKey.DATA_COMPRESSION, true); means that current client support compression. * */ // put ability here, which you want current server supports supportedAbilities.put(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC, true); supportedAbilities.put(AbilityKey.SERVER_FUZZY_WATCH, true); supportedAbilities.put(AbilityKey.SERVER_DISTRIBUTED_LOCK, true); supportedAbilities.put(AbilityKey.SERVER_MCP_REGISTRY, true); supportedAbilities.put(AbilityKey.SERVER_AGENT_REGISTRY, true); } /**. * get static ability current server supports * * @return static ability */ public static Map getStaticAbilities() { return INSTANCE.getSupportedAbilities(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/A2aService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.listener.AbstractNacosAgentCardListener; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.utils.StringUtils; import java.util.Collection; /** * Nacos AI A2A client service interface. * * @author xiweng.yy */ public interface A2aService { /** * Get agent card with nacos extension detail with latest version. * * @param agentName name of agent card * @return agent card with nacos extension detail * @throws NacosException if request parameter is invalid or agent card not found or handle error */ default AgentCardDetailInfo getAgentCard(String agentName) throws NacosException { return getAgentCard(agentName, StringUtils.EMPTY); } /** * Get agent card with nacos extension detail with target version. * * @param agentName name of agent card * @param version target version, if null or empty, get latest version * @return agent card with nacos extension detail * @throws NacosException if request parameter is invalid or agent card not found or handle error */ default AgentCardDetailInfo getAgentCard(String agentName, String version) throws NacosException { return getAgentCard(agentName, version, StringUtils.EMPTY); } /** * Get agent card with nacos extension detail with target version. * * @param agentName name of agent card * @param version target version, if null or empty, get latest version * @param registrationType {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_URL} or * {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_SERVICE} default is empty, means use agent card * setting in nacos. * @return agent card with nacos extension detail * @throws NacosException if request parameter is invalid or agent card not found or handle error */ AgentCardDetailInfo getAgentCard(String agentName, String version, String registrationType) throws NacosException; /** * Release new agent card or new version with default service type endpoint. * *

* If current agent card and version exist, This API will do nothing. If current agent card exist but version not * exist, This API will release new version. If current t agent card not exist, This API will release new agent * card. *

* * @param agentCard agent card need to release * @throws NacosException if request parameter is invalid or handle error */ default void releaseAgentCard(AgentCard agentCard) throws NacosException { releaseAgentCard(agentCard, AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE); } /** * Release new agent card or new version. * *

* If current agent card and version exist, This API will do nothing. If current agent card exist but version not * exist, This API will release new version. If current t agent card not exist, This API will release new agent * card. *

* * @param agentCard agent card need to release * @param registrationType {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_URL} or * {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_SERVICE} * @throws NacosException if request parameter is invalid or handle error */ default void releaseAgentCard(AgentCard agentCard, String registrationType) throws NacosException { releaseAgentCard(agentCard, registrationType, false); } /** * Release new agent card or new version. * *

* If current agent card and version exist, This API will do nothing. If current agent card exist but version not * exist, This API will release new version. If current t agent card not exist, This API will release new agent * card. *

* * @param agentCard agent card need to release * @param registrationType {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_URL} or * {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_SERVICE} * @param setAsLatest whether set new version as latest, default is false. This parameter is only effect when * new version is released. If current agent card not exist, whatever this parameter is, it * will be set as latest. * @throws NacosException if request parameter is invalid or handle error */ void releaseAgentCard(AgentCard agentCard, String registrationType, boolean setAsLatest) throws NacosException; /** * Register endpoint to agent card. * * @param agentName name of agent * @param version version of this endpoint * @param address address for this endpoint * @param port port of this endpoint * @throws NacosException if request parameter is invalid or handle error or agent not found */ default void registerAgentEndpoint(String agentName, String version, String address, int port) throws NacosException { registerAgentEndpoint(agentName, version, address, port, AiConstants.A2a.A2A_ENDPOINT_DEFAULT_TRANSPORT); } /** * Register endpoint to agent card. * * @param agentName name of agent * @param version version of this endpoint * @param address address for this endpoint * @param port port of this endpoint * @param transport supported transport, according to A2A protocol, it should be `JSONRPC`, `GRPC` and `HTTP+JSON` * @throws NacosException if request parameter is invalid or handle error or agent not found */ default void registerAgentEndpoint(String agentName, String version, String address, int port, String transport) throws NacosException { registerAgentEndpoint(agentName, version, address, port, transport, StringUtils.EMPTY); } /** * Register endpoint to agent card. * * @param agentName name of agent * @param version version of this endpoint * @param address address for this endpoint * @param port port of this endpoint * @param transport supported transport, according to A2A protocol, it should be `JSONRPC`, `GRPC` and `HTTP+JSON` * @param path The path of endpoint request * @throws NacosException if request parameter is invalid or handle error or agent not found */ default void registerAgentEndpoint(String agentName, String version, String address, int port, String transport, String path) throws NacosException { registerAgentEndpoint(agentName, version, address, port, transport, path, false); } /** * Register endpoint to agent card. * * @param agentName name of agent * @param version version of this endpoint * @param address address for this endpoint * @param port port of this endpoint * @param transport supported transport, according to A2A protocol, it should be `JSONRPC`, `GRPC` and `HTTP+JSON` * @param path The path of endpoint request * @param supportTls whether support tls * @throws NacosException if request parameter is invalid or handle error or agent not found */ default void registerAgentEndpoint(String agentName, String version, String address, int port, String transport, String path, boolean supportTls) throws NacosException { AgentEndpoint agentEndpoint = new AgentEndpoint(); agentEndpoint.setAddress(address); agentEndpoint.setPort(port); agentEndpoint.setTransport(transport); agentEndpoint.setPath(path); agentEndpoint.setSupportTls(supportTls); agentEndpoint.setVersion(version); registerAgentEndpoint(agentName, agentEndpoint); } /** * Register endpoint to agent card. * * @param agentName name of agent * @param endpoint endpoint info * @throws NacosException if request parameter is invalid or handle error or agent not found */ void registerAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException; /** * Batch register endpoints to agent card. * *

* Conflict with {@link #registerAgentEndpoint(String, AgentEndpoint)}, this API will overwrite all endpoint * registered by {@link #registerAgentEndpoint(String, AgentEndpoint)}. *

* * @param agentName name of agent * @param endpoints collection of endpoints * @throws NacosException if request parameter is invalid or handle error or agent not found * @since 3.1.1 */ void registerAgentEndpoint(String agentName, Collection endpoints) throws NacosException; /** * Deregister endpoint from agent card which registered by this client. * *

* Only endpoint registered by this client can be deregistered. Other endpoint registered by other clients, call * this API will no any effect. *

* * @param agentName name of agent * @param version version of this endpoint * @param address address for this endpoint * @param port port of this endpoint * @throws NacosException if request parameter is invalid or handle error or agent not found */ default void deregisterAgentEndpoint(String agentName, String version, String address, int port) throws NacosException { AgentEndpoint agentEndpoint = new AgentEndpoint(); agentEndpoint.setAddress(address); agentEndpoint.setPort(port); agentEndpoint.setVersion(version); deregisterAgentEndpoint(agentName, agentEndpoint); } /** * Deregister endpoint from agent card which registered by this client. * *

* Only endpoint registered by this client can be deregistered. Other endpoint registered by other clients, call * this API will no any effect. *

* * @param agentName name of agent * @param endpoint endpoint info * @throws NacosException if request parameter is invalid or handle error or agent not found */ void deregisterAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException; /** * Subscribe agent card. * * @param agentName name of agent * @param agentCardListener the callback listener for agent card * @return current agent card when subscribe success * @throws NacosException if request parameter is invalid or handle error */ default AgentCardDetailInfo subscribeAgentCard(String agentName, AbstractNacosAgentCardListener agentCardListener) throws NacosException { return subscribeAgentCard(agentName, StringUtils.EMPTY, agentCardListener); } /** * Subscribe agent card. * * @param agentName name of agent * @param version version of agent, if empty or null, means subscribe latest version * @param agentCardListener the callback listener for agent card * @return current agent card when subscribe success, nullable if agent card not found * @throws NacosException if request parameter is invalid or handle error */ AgentCardDetailInfo subscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException; /** * Unsubscribe agent card. * * @param agentName name of agent * @param agentCardListener the callback listener for agent card * @throws NacosException if request parameter is invalid or handle error */ default void unsubscribeAgentCard(String agentName, AbstractNacosAgentCardListener agentCardListener) throws NacosException { unsubscribeAgentCard(agentName, StringUtils.EMPTY, agentCardListener); } /** * Unsubscribe agent card. * * @param agentName name of agent * @param version version of agent, if empty or null, means unsubscribe latest version * @param agentCardListener the callback listener for agent card * @throws NacosException if request parameter is invalid or handle error */ void unsubscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/AiFactory.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai; import com.alibaba.nacos.api.exception.NacosException; import java.lang.reflect.Constructor; import java.util.Properties; /** * Nacos AI client factory. * * @author xiweng.yy */ public class AiFactory { /** * Create a new AI service. * * @param properties ai service properties * @return new ai service * @throws NacosException nacos exception */ public static AiService createAiService(Properties properties) throws NacosException { try { Class driverImplClass = Class.forName("com.alibaba.nacos.client.ai.NacosAiService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); return (AiService) constructor.newInstance(properties); } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/AiService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai; import com.alibaba.nacos.api.ai.listener.AbstractNacosMcpServerListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosPromptListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosSkillListener; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.exception.NacosException; /** * Nacos AI client service interface. * * @author xiweng.yy */ public interface AiService extends A2aService { /** * Get mcp server detail info for latest version. * * @param mcpName name of mcp server * @return detail information of MCP server * @throws NacosException if request parameter is invalid or mcp server not found or handle error */ default McpServerDetailInfo getMcpServer(String mcpName) throws NacosException { return getMcpServer(mcpName, null); } /** * Get mcp server detail info. * * @param mcpName name of MCP name * @param version version of MCP, if null, will get the latest version * @return detail information of MCP server * @throws NacosException if request parameter is invalid or mcp server not found or handle error */ McpServerDetailInfo getMcpServer(String mcpName, String version) throws NacosException; /** * Release new mcp server or release new version of exist mcp server request. * *

* If mcp server is not exist, will create an new mcp server with parameter specification. * If mcp server is exist, but version in specification is new one, request will create a new version of mcp server. * If mcp server is exist, and version in specification is exist, request will do nothing. *

* * @param serverSpecification mcp server specification * @param toolSpecification mcp server tool specification * @return mcp id * @throws NacosException if request parameter is invalid or handle error */ default String releaseMcpServer(McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification) throws NacosException { return releaseMcpServer(serverSpecification, toolSpecification, null); } /** * Release new mcp server or release new version of exist mcp server request. * *

* If mcp server is not exist, will create an new mcp server with parameter specification. * If mcp server is exist, but version in specification is new one, request will create a new version of mcp server. * If mcp server is exist, and version in specification is exist, request will do nothing. *

* * @param serverSpecification mcp server specification * @param toolSpecification mcp server tool specification * @param endpointSpecification mcp server endpoint specification, optional, if null, will create ref service auto. * @return mcp id * @throws NacosException if request parameter is invalid or handle error */ String releaseMcpServer(McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException; /** * Register an endpoint into target mcp server for all version. * * @param mcpName name of mcp server * @param address address of endpoint * @param port port of endpoint * @throws NacosException if request parameter is invalid or handle error */ default void registerMcpServerEndpoint(String mcpName, String address, int port) throws NacosException { registerMcpServerEndpoint(mcpName, address, port, null); } /** * Register an endpoint into target mcp server for target version. * * @param mcpName name of mcp server * @param address address of endpoint * @param port port of endpoint * @param version version of mcp server * @throws NacosException if request parameter is invalid or handle error */ void registerMcpServerEndpoint(String mcpName, String address, int port, String version) throws NacosException; /** * Deregister an endpoint from target mcp server for any version. * *

* The registered endpoint must be registered by this client service. * If the registered endpoint is registered by other client service, the endpoint will fail to deregister. *

* * @param mcpName name of mcp server * @param address address of endpoint * @param port port of endpoint * @throws NacosException if request parameter is invalid or handle error */ void deregisterMcpServerEndpoint(String mcpName, String address, int port) throws NacosException; /** * Subscribe mcp server. * * @param mcpName name of mcp server * @param mcpServerListener listener of mcp server, callback when mcp server is changed * @return The detail info of mcp server at current time * @throws NacosException if request parameter is invalid or handle error */ default McpServerDetailInfo subscribeMcpServer(String mcpName, AbstractNacosMcpServerListener mcpServerListener) throws NacosException { return subscribeMcpServer(mcpName, null, mcpServerListener); } /** * Subscribe mcp server. * * @param mcpName name of mcp server * @param version version of mcp server * @param mcpServerListener listener of mcp server, callback when mcp server is changed * @return The detail info of mcp server at current time, nullable if agent card not found * @throws NacosException if request parameter is invalid or handle error */ McpServerDetailInfo subscribeMcpServer(String mcpName, String version, AbstractNacosMcpServerListener mcpServerListener) throws NacosException; /** * Un-subscribe mcp server. * * @param mcpName name of mcp server * @param mcpServerListener listener of mcp server * @throws NacosException if request parameter is invalid or handle error */ default void unsubscribeMcpServer(String mcpName, AbstractNacosMcpServerListener mcpServerListener) throws NacosException { unsubscribeMcpServer(mcpName, null, mcpServerListener); } /** * Un-subscribe mcp server. * * @param mcpName name of mcp server * @param version version of mcp server * @param mcpServerListener listener of mcp server * @throws NacosException if request parameter is invalid or handle error */ void unsubscribeMcpServer(String mcpName, String version, AbstractNacosMcpServerListener mcpServerListener) throws NacosException; /** * Load skill by skill name. * *

* This method will query the skill main configuration and all resource configurations, * then assemble them into a complete Skill object. *

* * @param skillName skill name (unique identifier) * @return complete Skill object with all resources * @throws NacosException if skill not found or query error */ Skill loadSkill(String skillName) throws NacosException; /** * Subscribe skill. * * @param skillName name of skill * @param skillListener listener of skill, callback when skill configuration is changed * @return The skill object at current time, nullable if skill not found * @throws NacosException if request parameter is invalid or handle error */ Skill subscribeSkill(String skillName, AbstractNacosSkillListener skillListener) throws NacosException; /** * Un-subscribe skill. * * @param skillName name of skill * @param skillListener listener of skill * @throws NacosException if request parameter is invalid or handle error */ void unsubscribeSkill(String skillName, AbstractNacosSkillListener skillListener) throws NacosException; // ==================== Prompt Management APIs ==================== /** * Get prompt by prompt key. * * @param promptKey prompt key (unique identifier) * @return prompt object with current version * @throws NacosException if prompt not found or query error */ Prompt getPrompt(String promptKey) throws NacosException; /** * Get prompt by prompt key and target version. * * @param promptKey prompt key (unique identifier) * @param version target prompt version, if null, will get latest version * @return prompt object with target version * @throws NacosException if prompt not found or query error */ Prompt getPromptByVersion(String promptKey, String version) throws NacosException; /** * Get prompt by prompt key and target label. * * @param promptKey prompt key (unique identifier) * @param label target prompt label * @return prompt object with target label * @throws NacosException if prompt not found or query error */ Prompt getPromptByLabel(String promptKey, String label) throws NacosException; /** * Subscribe prompt changes. * * @param promptKey prompt key * @param version target prompt version, optional * @param label target prompt label, optional * @param promptListener listener for prompt changes * @return current prompt object, may be null if prompt not found * @throws NacosException if request parameter is invalid or handle error */ Prompt subscribePrompt(String promptKey, String version, String label, AbstractNacosPromptListener promptListener) throws NacosException; /** * Un-subscribe prompt changes. * * @param promptKey prompt key * @param version target prompt version, optional * @param label target prompt label, optional * @param promptListener listener for prompt changes * @throws NacosException if request parameter is invalid or handle error */ void unsubscribePrompt(String promptKey, String version, String label, AbstractNacosPromptListener promptListener) throws NacosException; /** * Shutdown the AI service and close resources. * * @throws NacosException exception. */ void shutdown() throws NacosException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/constant/AiConstants.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.constant; import com.alibaba.nacos.api.ai.model.mcp.registry.McpServerStatusEnum; /** * Nacos Ai contants. * * @author xiweng.yy */ public class AiConstants { public static class Mcp { public static final String MCP_DEFAULT_NAMESPACE = "public"; public static final String MCP_PROTOCOL_STDIO = "stdio"; public static final String MCP_PROTOCOL_SSE = "mcp-sse"; public static final String MCP_PROTOCOL_STREAMABLE = "mcp-streamable"; public static final String MCP_PROTOCOL_HTTP = "http"; public static final String MCP_PROTOCOL_DUBBO = "dubbo"; public static final String MCP_ENDPOINT_TYPE_REF = "REF"; public static final String MCP_ENDPOINT_TYPE_DIRECT = "DIRECT"; public static final String MCP_FRONT_ENDPOINT_TYPE_TO_BACK = "BACKEND"; public static final String MCP_STATUS_ACTIVE = McpServerStatusEnum.ACTIVE.getName(); public static final String MCP_STATUS_DEPRECATED = McpServerStatusEnum.DEPRECATED.getName(); public static final String MCP_STATUS_DELETED = McpServerStatusEnum.DELETED.getName(); public static final String OFFICIAL_TRANSPORT_SSE = "sse"; public static final String OFFICIAL_TRANSPORT_STREAMABLE = "streamable-http"; } public static final String AI_TRANSPORT_MODE = "nacosAiTransportMode"; public static final String AI_TRANSPORT_MODE_GRPC = "grpc"; public static final String AI_TRANSPORT_MODE_HTTP = "http"; public static final String AI_REQUEST_TIMEOUT = "nacosAiRequestTimeout"; public static final String AI_MCP_SERVER_CACHE_UPDATE_INTERVAL = "nacosAiMcpServerCacheUpdateInterval"; public static final String AI_AGENT_CARD_CACHE_UPDATE_INTERVAL = "nacosAiAgentCardCacheUpdateInterval"; public static final String AI_PROMPT_CACHE_UPDATE_INTERVAL = "nacosAiPromptCacheUpdateInterval"; public static final long DEFAULT_AI_CACHE_UPDATE_INTERVAL = 10000L; public static class A2a { public static final String A2A_DEFAULT_NAMESPACE = "public"; /** * Default endpoint type using `url` field of agent card directly when discovery * a2a agent. */ public static final String A2A_ENDPOINT_TYPE_URL = "URL"; /** * Default endpoint type using `backend` service of agent when discovery a2a * agent. */ public static final String A2A_ENDPOINT_TYPE_SERVICE = "SERVICE"; public static final String A2A_ENDPOINT_DEFAULT_TRANSPORT = "JSONRPC"; public static final String A2A_ENDPOINT_DEFAULT_PROTOCOL = "HTTP"; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/AbstractNacosAgentCardListener.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; /** * Nacos AI module agent card event lister. * * @author xiweng.yy */ public abstract class AbstractNacosAgentCardListener implements NacosAiListener { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/AbstractNacosMcpServerListener.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; /** * Nacos AI module mcp server event lister. * * @author xiweng.yy */ public abstract class AbstractNacosMcpServerListener implements NacosAiListener { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/AbstractNacosPromptListener.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; /** * Nacos AI module prompt event listener. * *

Extend this class to receive prompt change notifications.

* * @author nacos */ public abstract class AbstractNacosPromptListener implements NacosAiListener { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/AbstractNacosSkillListener.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; /** * Nacos AI module skill event listener. * * @author nacos */ public abstract class AbstractNacosSkillListener implements NacosAiListener { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/NacosAgentCardEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; /** * Nacos AI Module agent card event. * * @author xiweng.yy */ public class NacosAgentCardEvent implements NacosAiEvent { private final String agentName; private final AgentCardDetailInfo agentCard; public NacosAgentCardEvent(AgentCardDetailInfo agentCard) { this.agentName = agentCard.getName(); this.agentCard = agentCard; } public String getAgentName() { return agentName; } public AgentCardDetailInfo getAgentCard() { return agentCard; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/NacosAiEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; /** * Nacos AI module listener event. * * @author nkorange */ public interface NacosAiEvent { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/NacosAiListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import java.util.concurrent.Executor; /** * Nacos AI module event Listener. * * @author Nacos */ public interface NacosAiListener { /** * callback event. * * @param event event */ void onEvent(E event); /** * Get executor to do callback this listener. If return null, use default executor to call this listener. * * @return Executor */ default Executor getExecutor() { return null; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/NacosMcpServerEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; /** * Nacos AI module event for mcp server. * * @author xiweng.yy */ public class NacosMcpServerEvent implements NacosAiEvent { private final String mcpId; private final String namespaceId; private final String mcpName; private final McpServerDetailInfo mcpServerDetailInfo; public NacosMcpServerEvent(McpServerDetailInfo mcpServerDetailInfo) { this.mcpServerDetailInfo = mcpServerDetailInfo; this.mcpId = mcpServerDetailInfo.getId(); this.namespaceId = mcpServerDetailInfo.getNamespaceId(); this.mcpName = mcpServerDetailInfo.getName(); } public String getMcpId() { return mcpId; } public String getNamespaceId() { return namespaceId; } public String getMcpName() { return mcpName; } public McpServerDetailInfo getMcpServerDetailInfo() { return mcpServerDetailInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/NacosPromptEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import com.alibaba.nacos.api.ai.model.prompt.Prompt; /** * Nacos AI Module prompt event. * *

This event is triggered when a prompt configuration changes.

* * @author nacos */ public class NacosPromptEvent implements NacosAiEvent { private final String promptKey; private final Prompt prompt; public NacosPromptEvent(String promptKey, Prompt prompt) { this.promptKey = promptKey; this.prompt = prompt; } /** * Get the prompt key. * * @return prompt key */ public String getPromptKey() { return promptKey; } /** * Get the prompt object. * * @return prompt object, may be null if prompt is deleted */ public Prompt getPrompt() { return prompt; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/listener/NacosSkillEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import com.alibaba.nacos.api.ai.model.skills.Skill; /** * Nacos AI Module skill event. * * @author nacos */ public class NacosSkillEvent implements NacosAiEvent { private final String skillName; private final Skill skill; public NacosSkillEvent(String skillName, Skill skill) { this.skillName = skillName; this.skill = skill; } public String getSkillName() { return skillName; } public Skill getSkill() { return skill; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentAuthentication.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.List; import java.util.Objects; /** * AgentAuthentication. * * @author KiteSoar */ public class AgentAuthentication { private List schemes; private String credentials; public List getSchemes() { return schemes; } public void setSchemes(List schemes) { this.schemes = schemes; } public String getCredentials() { return credentials; } public void setCredentials(String credentials) { this.credentials = credentials; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } AgentAuthentication that = (AgentAuthentication) o; return Objects.equals(schemes, that.schemes) && Objects.equals(credentials, that.credentials); } @Override public int hashCode() { return Objects.hash(schemes, credentials); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentCapabilities.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.List; import java.util.Objects; /** * AgentCapabilities. * * @author KiteSoar */ public class AgentCapabilities { private Boolean streaming; private Boolean pushNotifications; private Boolean stateTransitionHistory; private List extensions; public Boolean getStreaming() { return streaming; } public void setStreaming(Boolean streaming) { this.streaming = streaming; } public Boolean getPushNotifications() { return pushNotifications; } public void setPushNotifications(Boolean pushNotifications) { this.pushNotifications = pushNotifications; } public Boolean getStateTransitionHistory() { return stateTransitionHistory; } public void setStateTransitionHistory(Boolean stateTransitionHistory) { this.stateTransitionHistory = stateTransitionHistory; } public List getExtensions() { return extensions; } public void setExtensions(List extensions) { this.extensions = extensions; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } AgentCapabilities that = (AgentCapabilities) o; return Objects.equals(streaming, that.streaming) && Objects.equals(pushNotifications, that.pushNotifications) && Objects.equals(stateTransitionHistory, that.stateTransitionHistory) && Objects.equals(extensions, that.extensions); } @Override public int hashCode() { return Objects.hash(streaming, pushNotifications, stateTransitionHistory, extensions); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentCard.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.List; import java.util.Map; import java.util.Objects; /** * AgentCard. * * @author KiteSoar */ public class AgentCard extends AgentCardBasicInfo { private String url; private String preferredTransport; private List additionalInterfaces; private AgentProvider provider; private String documentationUrl; private Map securitySchemes; private List>> security; private List defaultInputModes; private List defaultOutputModes; private Boolean supportsAuthenticatedExtendedCard; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getPreferredTransport() { return preferredTransport; } public void setPreferredTransport(String preferredTransport) { this.preferredTransport = preferredTransport; } public List getAdditionalInterfaces() { return additionalInterfaces; } public void setAdditionalInterfaces(List additionalInterfaces) { this.additionalInterfaces = additionalInterfaces; } public AgentProvider getProvider() { return provider; } public void setProvider(AgentProvider provider) { this.provider = provider; } public String getDocumentationUrl() { return documentationUrl; } public void setDocumentationUrl(String documentationUrl) { this.documentationUrl = documentationUrl; } public Map getSecuritySchemes() { return securitySchemes; } public void setSecuritySchemes(Map securitySchemes) { this.securitySchemes = securitySchemes; } public List>> getSecurity() { return security; } public void setSecurity(List>> security) { this.security = security; } public List getDefaultInputModes() { return defaultInputModes; } public void setDefaultInputModes(List defaultInputModes) { this.defaultInputModes = defaultInputModes; } public List getDefaultOutputModes() { return defaultOutputModes; } public void setDefaultOutputModes(List defaultOutputModes) { this.defaultOutputModes = defaultOutputModes; } public Boolean getSupportsAuthenticatedExtendedCard() { return supportsAuthenticatedExtendedCard; } public void setSupportsAuthenticatedExtendedCard(Boolean supportsAuthenticatedExtendedCard) { this.supportsAuthenticatedExtendedCard = supportsAuthenticatedExtendedCard; } @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; } AgentCard agentCard = (AgentCard) o; return super.equals(agentCard) && Objects.equals(url, agentCard.url) && Objects.equals(preferredTransport, agentCard.preferredTransport) && Objects.equals(additionalInterfaces, agentCard.additionalInterfaces) && Objects.equals(provider, agentCard.provider) && Objects.equals(documentationUrl, agentCard.documentationUrl) && Objects.equals(securitySchemes, agentCard.securitySchemes) && Objects.equals(security, agentCard.security) && Objects.equals(defaultInputModes, agentCard.defaultInputModes) && Objects.equals(defaultOutputModes, agentCard.defaultOutputModes) && Objects.equals(supportsAuthenticatedExtendedCard, agentCard.supportsAuthenticatedExtendedCard); } @Override public int hashCode() { return Objects.hash(super.hashCode(), url, preferredTransport, additionalInterfaces, provider, documentationUrl, securitySchemes, security, defaultInputModes, defaultOutputModes, supportsAuthenticatedExtendedCard); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentCardBasicInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.List; import java.util.Objects; /** * Basic info of agent card. * * @author xiweng.yy */ public class AgentCardBasicInfo { private String protocolVersion; private String name; private String description; private String version; private String iconUrl; private AgentCapabilities capabilities; private List skills; public String getProtocolVersion() { return protocolVersion; } public void setProtocolVersion(String protocolVersion) { this.protocolVersion = protocolVersion; } 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 String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getIconUrl() { return iconUrl; } public void setIconUrl(String iconUrl) { this.iconUrl = iconUrl; } public AgentCapabilities getCapabilities() { return capabilities; } public void setCapabilities(AgentCapabilities capabilities) { this.capabilities = capabilities; } public List getSkills() { return skills; } public void setSkills(List skills) { this.skills = skills; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AgentCardBasicInfo that = (AgentCardBasicInfo) o; return Objects.equals(protocolVersion, that.protocolVersion) && Objects.equals(name, that.name) && Objects.equals(description, that.description) && Objects.equals(version, that.version) && Objects.equals(iconUrl, that.iconUrl) && Objects.equals(capabilities, that.capabilities) && Objects.equals(skills, that.skills); } @Override public int hashCode() { return Objects.hash(protocolVersion, name, description, version, iconUrl, capabilities, skills); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentCardDetailInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.ai.constant.AiConstants; /** * Agent Card detail info with Nacos extension field. * * @author xiweng.yy */ public class AgentCardDetailInfo extends AgentCard { private String registrationType = AiConstants.A2a.A2A_ENDPOINT_TYPE_URL; private Boolean latestVersion; public String getRegistrationType() { return registrationType; } public void setRegistrationType(String registrationType) { this.registrationType = registrationType; } public Boolean isLatestVersion() { return latestVersion; } public void setLatestVersion(Boolean latestVersion) { this.latestVersion = latestVersion; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentCardVersionInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.List; import java.util.Objects; /** * AgentCardVersionInfo. * * @author KiteSoar */ public class AgentCardVersionInfo extends AgentCardBasicInfo { private String latestPublishedVersion; private List versionDetails; private String registrationType; public String getLatestPublishedVersion() { return latestPublishedVersion; } public void setLatestPublishedVersion(String latestPublishedVersion) { this.latestPublishedVersion = latestPublishedVersion; } public List getVersionDetails() { return versionDetails; } public void setVersionDetails(List versionDetails) { this.versionDetails = versionDetails; } public String getRegistrationType() { return registrationType; } public void setRegistrationType(String registrationType) { this.registrationType = registrationType; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } if (!super.equals(o)) { return false; } AgentCardVersionInfo that = (AgentCardVersionInfo) o; return Objects.equals(latestPublishedVersion, that.latestPublishedVersion) && Objects.equals(versionDetails, that.versionDetails) && Objects.equals(registrationType, that.registrationType); } @Override public int hashCode() { return Objects.hash(super.hashCode(), latestPublishedVersion, versionDetails, registrationType); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentEndpoint.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.utils.StringUtils; import java.util.Objects; /** * Agent endpoint for A2A protocol. * *

* Details split version of {@link AgentInterface}. *

* * @author xiweng.yy */ public class AgentEndpoint { /** * Same with {@link AgentInterface#transport}, Default `JSONRPC`. */ private String transport = AiConstants.A2a.A2A_ENDPOINT_DEFAULT_TRANSPORT; /** * Will be joined with {@link #port}, {@link #path}, {@link #protocol}. Such as `
...` */ private String address; private int port; private String path = StringUtils.EMPTY; /** * If {@code true}, the target {@link AgentInterface} should be `https`, otherwise should be `http`. Default {@code false}. */ private boolean supportTls; private String version; /** * Custom Protocol for A2A transport. Default `HTTP`. * * @since 3.1.1 */ private String protocol = AiConstants.A2a.A2A_ENDPOINT_DEFAULT_PROTOCOL; /** * Custom query for A2A url. * * @since 3.1.1 */ private String query; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getTransport() { return transport; } public void setTransport(String transport) { this.transport = transport; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public boolean isSupportTls() { return supportTls; } public void setSupportTls(boolean supportTls) { this.supportTls = supportTls; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getQuery() { return query; } public void setQuery(String query) { this.query = query; } /** * Only simple check address(IP or domain) and port. * * @param endpoint target endpoint * @return {@code true} if is equal, otherwise {@code false} */ public boolean simpleEquals(AgentEndpoint endpoint) { return Objects.equals(address, endpoint.address) && Objects.equals(port, endpoint.port); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AgentEndpoint endpoint = (AgentEndpoint) o; return port == endpoint.port && supportTls == endpoint.supportTls && Objects.equals(transport, endpoint.transport) && Objects.equals(address, endpoint.address) && Objects.equals(path, endpoint.path) && Objects.equals(version, endpoint.version) && Objects.equals(protocol, endpoint.protocol) && Objects.equals(query, endpoint.query); } @Override public int hashCode() { return Objects.hash(transport, address, port, path, supportTls, version, protocol, query); } @Override public String toString() { return "AgentEndpoint{" + "transport='" + transport + '\'' + ", address='" + address + '\'' + ", port=" + port + ", path='" + path + '\'' + ", supportTls=" + supportTls + ", version='" + version + '\'' + ", protocol='" + protocol + '\'' + ", query='" + query + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentExtension.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.Map; import java.util.Objects; /** * AgentExtension. * * @author KiteSoar */ public class AgentExtension { private String uri; private String description; private Boolean required; private Map params; public String getUri() { return uri; } public void setUri(String uri) { this.uri = uri; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Boolean getRequired() { return required; } public void setRequired(Boolean required) { this.required = required; } public Map getParams() { return params; } public void setParams(Map params) { this.params = params; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } AgentExtension that = (AgentExtension) o; return Objects.equals(uri, that.uri) && Objects.equals(description, that.description) && Objects.equals( required, that.required) && Objects.equals(params, that.params); } @Override public int hashCode() { return Objects.hash(uri, description, required, params); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentInterface.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.Objects; /** * AgentInterface. * * @author KiteSoar */ public class AgentInterface { private String url; private String transport; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getTransport() { return transport; } public void setTransport(String transport) { this.transport = transport; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } AgentInterface that = (AgentInterface) o; return Objects.equals(url, that.url) && Objects.equals(transport, that.transport); } @Override public int hashCode() { return Objects.hash(url, transport); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentProvider.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.Objects; /** * AgentInterface. * * @author KiteSoar */ public class AgentProvider { private String organization; private String url; public String getOrganization() { return organization; } public void setOrganization(String organization) { this.organization = organization; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } AgentProvider that = (AgentProvider) o; return Objects.equals(organization, that.organization) && Objects.equals(url, that.url); } @Override public int hashCode() { return Objects.hash(organization, url); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentSkill.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.List; import java.util.Objects; /** * AgentSkill. * * @author KiteSoar */ public class AgentSkill { private String id; private String name; private String description; private List tags; private List examples; private List inputModes; private List outputModes; public String getId() { return id; } public void setId(String id) { this.id = id; } 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 List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } public List getExamples() { return examples; } public void setExamples(List examples) { this.examples = examples; } public List getInputModes() { return inputModes; } public void setInputModes(List inputModes) { this.inputModes = inputModes; } public List getOutputModes() { return outputModes; } public void setOutputModes(List outputModes) { this.outputModes = outputModes; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } AgentSkill that = (AgentSkill) o; return Objects.equals(id, that.id) && Objects.equals(name, that.name) && Objects.equals(description, that.description) && Objects.equals(tags, that.tags) && Objects.equals(examples, that.examples) && Objects.equals(inputModes, that.inputModes) && Objects.equals(outputModes, that.outputModes); } @Override public int hashCode() { return Objects.hash(id, name, description, tags, examples, inputModes, outputModes); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/AgentVersionDetail.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.Objects; /** * AgentVersionDetail. * * @author KiteSoar */ public class AgentVersionDetail { private String version; private String createdAt; private String updatedAt; private boolean isLatest; public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getCreatedAt() { return createdAt; } public void setCreatedAt(String createdAt) { this.createdAt = createdAt; } public String getUpdatedAt() { return updatedAt; } public void setUpdatedAt(String updatedAt) { this.updatedAt = updatedAt; } public boolean isLatest() { return isLatest; } public void setLatest(boolean latest) { isLatest = latest; } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } AgentVersionDetail that = (AgentVersionDetail) o; return Objects.equals(version, that.version) && Objects.equals(createdAt, that.createdAt) && Objects.equals( updatedAt, that.updatedAt) && Objects.equals(isLatest, that.isLatest); } @Override public int hashCode() { return Objects.hash(version, createdAt, updatedAt, isLatest); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/a2a/SecurityScheme.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import java.util.HashMap; /** * SecurityScheme. * * @author KiteSoar */ public class SecurityScheme extends HashMap { private static final long serialVersionUID = -708604225878249736L; public SecurityScheme() { super(4); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/EncryptObject.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.io.Serializable; import java.util.Map; /** * Encrypted payload wrapper for MCP tool specification. * Holds ciphertext and encryption metadata (algorithm, iv, keyId, version, etc.). * @author luoxiner */ public class EncryptObject implements Serializable { private static final long serialVersionUID = 1L; /** * The ciphertext or encoded payload. */ private String data; /** * Additional encryption metadata, e.g. alg, iv, keyId, version. */ private Map encryptInfo; public String getData() { return data; } public void setData(String data) { this.data = data; } public Map getEncryptInfo() { return encryptInfo; } public void setEncryptInfo(Map encryptInfo) { this.encryptInfo = encryptInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/FrontEndpointConfig.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.KeyValueInput; import java.util.List; import com.alibaba.nacos.api.ai.constant.AiConstants; /** * Specific endpoint information exposed to the outside. * * @author OmCheeLin */ public class FrontEndpointConfig { private String type; private String protocol; private String endpointType; /** * According To the {@link #endpointType}, the data type will be different. *
    *
  • If {@link AiConstants.Mcp#MCP_ENDPOINT_TYPE_REF}, the data type is {@link McpServiceRef}
  • *
  • If {@link AiConstants.Mcp#MCP_ENDPOINT_TYPE_DIRECT}, the data type is {@link String}
  • *
  • If {@link AiConstants.Mcp#MCP_FRONT_ENDPOINT_TYPE_TO_BACK}, the data is {@code null}
  • *
*/ private Object endpointData; private String path; private List headers; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getEndpointType() { return endpointType; } public void setEndpointType(String endpointType) { this.endpointType = endpointType; } public Object getEndpointData() { return endpointData; } public void setEndpointData(Object endpointData) { this.endpointData = endpointData; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public List getHeaders() { return headers; } public void setHeaders(List headers) { this.headers = headers; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpCapability.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; /** * AI MCP Server Capability enum, Mark the MCP Server support which capability such as support tools, prompts or resources. * * @author xiweng.yy */ public enum McpCapability { /** * The MCP Server provider tools. */ TOOL, /** * The MCP Server provider prompts. */ PROMPT, /** * The MCP Server provider resources. */ RESOURCE; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpEndpointInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.util.List; import com.alibaba.nacos.api.ai.model.mcp.registry.KeyValueInput; /** * AI MCP backend endpoint info. * * @author xiweng.yy */ public class McpEndpointInfo { /** * Indicate the protocol of the endpoint (http / https). */ private String protocol; private String address; private int port; private String path; private List headers; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } 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 List getHeaders() { return headers; } public void setHeaders(List headers) { this.headers = headers; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpEndpointSpec.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.constant.AiConstants; import java.util.HashMap; import java.util.Map; /** * AI MCP Server Endpoint Specification. * * @author xiweng.yy */ public class McpEndpointSpec { /** * Endpoint type. Should be {@link AiConstants.Mcp#MCP_ENDPOINT_TYPE_DIRECT} or * {@link AiConstants.Mcp#MCP_ENDPOINT_TYPE_REF}. */ private String type; /** * Endpoint data. Depend on the `type`, the data should be different. *

* If `type` is {@link AiConstants.Mcp#MCP_ENDPOINT_TYPE_DIRECT}, the data should be include `address` and `port` to * spec mcp server endpoint. *

*

* If `type` is {@link AiConstants.Mcp#MCP_ENDPOINT_TYPE_REF}, the data should be include `namespaceId`, `groupName` and `serviceName` * to spec the ref server which already register into Nacos. *

*/ private Map data = new HashMap<>(); public String getType() { return type; } public void setType(String type) { this.type = type; } public Map getData() { return data; } public void setData(Map data) { this.data = data; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerBasicInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.registry.Icon; import com.alibaba.nacos.api.ai.model.mcp.registry.Package; import com.alibaba.nacos.api.ai.model.mcp.registry.Repository; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import java.util.List; import java.util.Map; /** * AI Mcp server spec in nacos. * * @author xiweng.yy */ public class McpServerBasicInfo { private String namespaceId; private String id; private String name; /** * It should be {@link AiConstants.Mcp#MCP_PROTOCOL_STDIO}, {@link AiConstants.Mcp#MCP_PROTOCOL_SSE}, * {@link AiConstants.Mcp#MCP_PROTOCOL_STREAMABLE}, {@link AiConstants.Mcp#MCP_PROTOCOL_HTTP} or {@link AiConstants.Mcp#MCP_PROTOCOL_DUBBO}. */ private String protocol; private String frontProtocol; private String description; private Repository repository; private List packages; private List icons; private String websiteUrl; private ServerVersionDetail versionDetail; /** * Please use {@link #versionDetail} replaced. */ private String version; /** * Should be set when `type` is not {@link AiConstants.Mcp#MCP_PROTOCOL_STDIO}. */ private McpServerRemoteServiceConfig remoteServerConfig; /** * Should be set when `type` is {@link AiConstants.Mcp#MCP_PROTOCOL_STDIO}. */ private Map localServerConfig; private boolean enabled = true; /** * Current lifecycle status of MCP server, should be one of * {@link AiConstants.Mcp#MCP_STATUS_ACTIVE} or {@link AiConstants.Mcp#MCP_STATUS_DEPRECATED}. * Default is {@link AiConstants.Mcp#MCP_STATUS_ACTIVE}. */ private String status = AiConstants.Mcp.MCP_STATUS_ACTIVE; /** * Auto discovery capabilities by Nacos. No need to set when create or update Mcp server. */ private List capabilities; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getProtocol() { return protocol; } public void setProtocol(String protocol) { this.protocol = protocol; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public McpServerRemoteServiceConfig getRemoteServerConfig() { return remoteServerConfig; } public void setRemoteServerConfig(McpServerRemoteServiceConfig remoteServerConfig) { this.remoteServerConfig = remoteServerConfig; } public Map getLocalServerConfig() { return localServerConfig; } public void setLocalServerConfig(Map localServerConfig) { this.localServerConfig = localServerConfig; } public String getFrontProtocol() { return frontProtocol; } public void setFrontProtocol(String frontProtocol) { this.frontProtocol = frontProtocol; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public List getCapabilities() { return capabilities; } public void setCapabilities(List capabilities) { this.capabilities = capabilities; } public ServerVersionDetail getVersionDetail() { return versionDetail; } public void setVersionDetail(ServerVersionDetail versionDetail) { this.versionDetail = versionDetail; } public String getId() { return id; } public void setId(String id) { this.id = id; } public Repository getRepository() { return repository; } public void setRepository(Repository repository) { this.repository = repository; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public List getPackages() { return packages; } public void setPackages(List packages) { this.packages = packages; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public List getIcons() { return icons; } public void setIcons(List icons) { this.icons = icons; } public String getWebsiteUrl() { return websiteUrl; } public void setWebsiteUrl(String websiteUrl) { this.websiteUrl = websiteUrl; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerDetailInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import java.util.List; /** * AI Mcp server spec in nacos. * * @author xiweng.yy */ public class McpServerDetailInfo extends McpServerBasicInfo { private List backendEndpoints; private List frontendEndpoints; private McpToolSpecification toolSpec; private List allVersions; public List getBackendEndpoints() { return backendEndpoints; } public void setBackendEndpoints(List backendEndpoints) { this.backendEndpoints = backendEndpoints; } public List getFrontendEndpoints() { return frontendEndpoints; } public void setFrontendEndpoints(List frontendEndpoints) { this.frontendEndpoints = frontendEndpoints; } public McpToolSpecification getToolSpec() { return toolSpec; } public void setToolSpec(McpToolSpecification toolSpec) { this.toolSpec = toolSpec; } public List getAllVersions() { return allVersions; } public void setAllVersions(List allVersions) { this.allVersions = allVersions; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportRequest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.io.Serializable; /** * MCP Server Import Request. * * @author nacos */ public class McpServerImportRequest implements Serializable { private static final long serialVersionUID = 1L; /** * Import type: file, url, json. */ private String importType; /** * Import source data. */ private String data; /** * Whether to override existing servers. */ private boolean overrideExisting = false; /** * Whether to validate only (preview mode). */ private boolean validateOnly = false; /** * Whether to skip invalid servers and continue importing valid ones. * Default false keeps previous behavior (fail fast when any invalid exists). */ private boolean skipInvalid = false; /** * Selected server IDs for import (for selective import). */ private String[] selectedServers; /** * Optional start cursor for URL-based pagination. * Only effective when importType = url. */ private String cursor; /** * Optional page size limit for URL import (items per page). * Only effective when importType = url. If null, server-side default applies. */ private Integer limit; /** * Optional fuzzy search keyword for registry listing. * Only effective when importType = url. When present, backend will append it * to the registry query string as `search` for server-side fuzzy filtering. */ private String search; public String getImportType() { return importType; } public void setImportType(String importType) { this.importType = importType; } public String getData() { return data; } public void setData(String data) { this.data = data; } public boolean isOverrideExisting() { return overrideExisting; } public void setOverrideExisting(boolean overrideExisting) { this.overrideExisting = overrideExisting; } public boolean isValidateOnly() { return validateOnly; } public void setValidateOnly(boolean validateOnly) { this.validateOnly = validateOnly; } public String[] getSelectedServers() { return selectedServers; } public void setSelectedServers(String[] selectedServers) { this.selectedServers = selectedServers; } public String getCursor() { return cursor; } public void setCursor(String cursor) { this.cursor = cursor; } public Integer getLimit() { return limit; } public void setLimit(Integer limit) { this.limit = limit; } public String getSearch() { return search; } public void setSearch(String search) { this.search = search; } public boolean isSkipInvalid() { return skipInvalid; } public void setSkipInvalid(boolean skipInvalid) { this.skipInvalid = skipInvalid; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportResponse.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.io.Serializable; import java.util.List; /** * MCP Server Import Response. * * @author nacos */ public class McpServerImportResponse implements Serializable { private static final long serialVersionUID = 1L; /** * Import success. */ private boolean success; /** * Total count of servers to import. */ private int totalCount = 0; /** * Successfully imported count. */ private int successCount = 0; /** * Failed import count. */ private int failedCount = 0; /** * Skipped count (duplicates). */ private int skippedCount = 0; /** * Import results for each server. */ private List results; /** * Overall error message. */ private String errorMessage; public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public int getSuccessCount() { return successCount; } public void setSuccessCount(int successCount) { this.successCount = successCount; } public int getFailedCount() { return failedCount; } public void setFailedCount(int failedCount) { this.failedCount = failedCount; } public int getSkippedCount() { return skippedCount; } public void setSkippedCount(int skippedCount) { this.skippedCount = skippedCount; } public List getResults() { return results; } public void setResults(List results) { this.results = results; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportResult.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.io.Serializable; /** * MCP Server Import Result. * * @author nacos */ public class McpServerImportResult implements Serializable { private static final long serialVersionUID = 1L; /** * Server name. */ private String serverName; /** * Server ID after import. */ private String serverId; /** * Import status: success, failed, skipped. */ private String status; /** * Error message if failed. */ private String errorMessage; /** * Conflict type if skipped. */ private String conflictType; public String getServerName() { return serverName; } public void setServerName(String serverName) { this.serverName = serverName; } public String getServerId() { return serverId; } public void setServerId(String serverId) { this.serverId = serverId; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public String getErrorMessage() { return errorMessage; } public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } public String getConflictType() { return conflictType; } public void setConflictType(String conflictType) { this.conflictType = conflictType; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportValidationResult.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.io.Serializable; import java.util.List; /** * MCP Server Import Validation Result. * * @author nacos */ public class McpServerImportValidationResult implements Serializable { private static final long serialVersionUID = 1L; /** * Validation success. */ private boolean valid; /** * Parsed servers count. */ private int totalCount; /** * Valid servers count. */ private int validCount; /** * Invalid servers count. */ private int invalidCount; /** * Duplicate servers count. */ private int duplicateCount; /** * Parsed and validated servers. */ private List servers; /** * Overall validation errors. */ private List errors; /** * Cursor for fetching next page (URL import only). Null if no more pages. */ private String nextCursor; /** * Whether there are more pages available to load. */ private boolean hasMore; public boolean isValid() { return valid; } public void setValid(boolean valid) { this.valid = valid; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public int getValidCount() { return validCount; } public void setValidCount(int validCount) { this.validCount = validCount; } public int getInvalidCount() { return invalidCount; } public void setInvalidCount(int invalidCount) { this.invalidCount = invalidCount; } public int getDuplicateCount() { return duplicateCount; } public void setDuplicateCount(int duplicateCount) { this.duplicateCount = duplicateCount; } public List getServers() { return servers; } public void setServers(List servers) { this.servers = servers; } public List getErrors() { return errors; } public void setErrors(List errors) { this.errors = errors; } public String getNextCursor() { return nextCursor; } public void setNextCursor(String nextCursor) { this.nextCursor = nextCursor; } public boolean isHasMore() { return hasMore; } public void setHasMore(boolean hasMore) { this.hasMore = hasMore; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerRemoteServiceConfig.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.util.List; /** * AI MCP server remote service config. * * @author xiweng.yy */ public class McpServerRemoteServiceConfig { private McpServiceRef serviceRef; private String exportPath; private List frontEndpointConfigList; public McpServiceRef getServiceRef() { return serviceRef; } public void setServiceRef(McpServiceRef serviceRef) { this.serviceRef = serviceRef; } public String getExportPath() { return exportPath; } public void setExportPath(String exportPath) { this.exportPath = exportPath; } public List getFrontEndpointConfigList() { return frontEndpointConfigList; } public void setFrontEndpointConfigList(List frontEndpointConfigList) { this.frontEndpointConfigList = frontEndpointConfigList; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerValidationItem.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.io.Serializable; import java.util.List; /** * MCP Server Validation Item. * * @author nacos */ public class McpServerValidationItem implements Serializable { private static final long serialVersionUID = 1L; /** * Server name. */ private String serverName; /** * Server ID (generated or provided). */ private String serverId; /** * Validation status: valid, invalid, duplicate. */ private String status; /** * Validation errors. */ private List errors; /** * Whether server exists. */ private boolean exists; /** * Transformed server detail. */ private McpServerDetailInfo server; /** * Whether selected for import. */ private boolean selected = true; public String getServerName() { return serverName; } public void setServerName(String serverName) { this.serverName = serverName; } public String getServerId() { return serverId; } public void setServerId(String serverId) { this.serverId = serverId; } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public List getErrors() { return errors; } public void setErrors(List errors) { this.errors = errors; } public boolean isExists() { return exists; } public void setExists(boolean exists) { this.exists = exists; } public McpServerDetailInfo getServer() { return server; } public void setServer(McpServerDetailInfo server) { this.server = server; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServerVersionInfo.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import java.util.List; /** * McpServerVersionInfo. * @author xinluo */ @SuppressWarnings({"checkstyle:MethodName", "checkstyle:ParameterName", "checkstyle:MemberName", "checkstyle:SummaryJavadoc"}) public class McpServerVersionInfo extends McpServerBasicInfo { private String latestPublishedVersion; private List versionDetails; public String getLatestPublishedVersion() { return latestPublishedVersion; } public void setLatestPublishedVersion(String latestPublishedVersion) { this.latestPublishedVersion = latestPublishedVersion; } public List getVersionDetails() { return versionDetails; } public void setVersions(List versionDetails) { this.versionDetails = versionDetails; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpServiceRef.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; /** * AI MCP Backend service reference. The Ref record the MCP Server backend service is a service registered in nacos. * * @author xiweng.yy */ public class McpServiceRef { private String namespaceId; private String groupName; private String serviceName; private String transportProtocol; public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public void setTransportProtocol(String transportProtocol) { this.transportProtocol = transportProtocol; } public String getTransportProtocol() { return transportProtocol; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpTool.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Map; /** * AI MCP Tool. * * @author xiweng.yy */ public class McpTool { private String name; private String description; private Map inputSchema; private Map outputSchema; /** * MCP protocol meta field. See MCP specification for `_meta` usage. */ @JsonProperty("_meta") private Map meta; /** * MCP Tool annotations - additional properties describing a Tool to clients. */ private McpToolAnnotations annotations; 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 getInputSchema() { return inputSchema; } public void setInputSchema(Map inputSchema) { this.inputSchema = inputSchema; } public Map getOutputSchema() { return outputSchema; } public void setOutputSchema(Map outputSchema) { this.outputSchema = outputSchema; } public Map getMeta() { return meta; } public void setMeta(Map meta) { this.meta = meta; } public McpToolAnnotations getAnnotations() { return annotations; } public void setAnnotations(McpToolAnnotations annotations) { this.annotations = annotations; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpToolAnnotations.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; /** * MCP Tool Annotations - Additional properties describing a Tool to clients. * *

* NOTE: all properties in ToolAnnotations are hints. * They are not guaranteed to provide a faithful description of tool behavior. * Clients should never make tool use decisions based on ToolAnnotations * received from untrusted servers. *

* * @author xiweng.yy */ public class McpToolAnnotations { /** * A human-readable title for the tool. */ private String title; /** * If true, the tool does not modify its environment. Default: false */ private Boolean readOnlyHint; /** * If true, the tool may perform destructive updates to its environment. * If false, the tool performs only additive updates. * (This property is meaningful only when readOnlyHint == false) * Default: true */ private Boolean destructiveHint; /** * If true, calling the tool repeatedly with the same arguments * will have no additional effect on its environment. * (This property is meaningful only when readOnlyHint == false) * Default: false */ private Boolean idempotentHint; /** * If true, this tool may interact with an "open world" of external entities. * If false, the tool's domain of interaction is closed. * Default: true */ private Boolean openWorldHint; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Boolean getReadOnlyHint() { return readOnlyHint; } public void setReadOnlyHint(Boolean readOnlyHint) { this.readOnlyHint = readOnlyHint; } public Boolean getDestructiveHint() { return destructiveHint; } public void setDestructiveHint(Boolean destructiveHint) { this.destructiveHint = destructiveHint; } public Boolean getIdempotentHint() { return idempotentHint; } public void setIdempotentHint(Boolean idempotentHint) { this.idempotentHint = idempotentHint; } public Boolean getOpenWorldHint() { return openWorldHint; } public void setOpenWorldHint(Boolean openWorldHint) { this.openWorldHint = openWorldHint; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpToolMeta.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.util.Map; /** * AI MCP Tool metadata. * * @author xiweng.yy */ public class McpToolMeta { private Map invokeContext; private boolean enabled = true; private Map templates; public Map getInvokeContext() { return invokeContext; } public void setInvokeContext(Map invokeContext) { this.invokeContext = invokeContext; } public boolean isEnabled() { return enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } public Map getTemplates() { return templates; } public void setTemplates(Map templates) { this.templates = templates; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/McpToolSpecification.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * Mcp Tool specification. * * @author xiweng.yy */ public class McpToolSpecification { /** * Tool specification storage type. Defaults to "normal" (plaintext storage). * When set to "encrypted" (or vendor-specific like "encrypt-kms"), server will persist encryptData as-is * and skip parsing tools/securitySchemes. */ private String specificationType; /** * Encrypted payload and metadata when specificationType indicates encryption. */ private EncryptObject encryptData; private List tools = new LinkedList<>(); private Map toolsMeta = new HashMap<>(1); private List securitySchemes = new ArrayList<>(); private Map extensions = new HashMap<>(1); public String getSpecificationType() { return specificationType; } public void setSpecificationType(String specificationType) { this.specificationType = specificationType; } public EncryptObject getEncryptData() { return encryptData; } public void setEncryptData(EncryptObject encryptData) { this.encryptData = encryptData; } public List getTools() { return tools; } public void setTools(List tools) { this.tools = tools; } public Map getToolsMeta() { return toolsMeta; } public void setToolsMeta(Map toolsMeta) { this.toolsMeta = toolsMeta; } public List getSecuritySchemes() { return securitySchemes; } public void setSecuritySchemes(List securitySchemes) { this.securitySchemes = securitySchemes; } public Map getExtensions() { return extensions; } public void setExtensions(Map extensions) { this.extensions = extensions; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/SecurityScheme.java ================================================ package com.alibaba.nacos.api.ai.model.mcp; /** * SecurityScheme 表示安全认证方案的模型,包括类型、方案、位置、名称及默认凭证等信息. * 用于描述 API 的安全机制. * * @author xinluo */ public class SecurityScheme { /** * ID of the security scheme. Will be used and reference by tools. */ private String id; /** * Type of the security scheme. Possible values are: 'http', 'apiKey', 'localEnv' or other custom extension. */ private String type; /** * Scheme of the security scheme. Used when {@link #type} is `http`. Possible values are: `basic` 或 `bearer`. */ private String scheme; /** * Location of the security scheme. Possible values are: `query`, `header`. */ private String in; /** * Name of the security scheme. Used when {@link #type} is `apiKey` or `localEnv`. * e.g. the key name for `apiKey` or environment name for `localEnv`. */ private String name; /** * The default credential when leak input identity by properties. Optional. */ private String defaultCredential; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getScheme() { return scheme; } public void setScheme(String scheme) { this.scheme = scheme; } public String getIn() { return in; } public void setIn(String in) { this.in = in; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDefaultCredential() { return defaultCredential; } public void setDefaultCredential(String defaultCredential) { this.defaultCredential = defaultCredential; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/Argument.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; /** * Argument union for MCP registry (named | positional). * Aligns with components.schemas.Argument. * * @author xinluo */ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", defaultImpl = NamedArgument.class) @JsonSubTypes({ @JsonSubTypes.Type(value = PositionalArgument.class, name = "positional"), @JsonSubTypes.Type(value = NamedArgument.class, name = "named") }) public interface Argument { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/Icon.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonValue; import java.util.List; /** * Icon per Icon schema: * An optionally-sized icon that can be displayed in a user interface. * Required: src (HTTPS URI). Optional: mimeType, sizes, theme. * *

* Fields align with components.schemas.Icon. *

* * @author xinluo */ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class Icon { @JsonProperty(value = "src", required = true) private String src; private MimeType mimeType; private List sizes; private Theme theme; /** * Get src. * * @return src */ public String getSrc() { return src; } /** * Set src. * * @param src src */ public void setSrc(String src) { this.src = src; } /** * Get mime type. * * @return mime type */ public MimeType getMimeType() { return mimeType; } /** * Set mime type. * * @param mimeType mime type */ public void setMimeType(MimeType mimeType) { this.mimeType = mimeType; } /** * Get sizes. * * @return sizes */ public List getSizes() { return sizes; } /** * Set sizes. * * @param sizes sizes */ public void setSizes(List sizes) { this.sizes = sizes; } /** * Get theme. * * @return theme */ public Theme getTheme() { return theme; } /** * Set theme. * * @param theme theme */ public void setTheme(Theme theme) { this.theme = theme; } /** * Mime type enum: image/png, image/jpeg, image/jpg, image/svg+xml, image/webp. * Serialized/deserialized as the lowercase string value. */ public static enum MimeType { /** * PNG mime type. */ IMAGE_PNG("image/png"), /** * JPEG mime type. */ IMAGE_JPEG("image/jpeg"), /** * JPG mime type. */ IMAGE_JPG("image/jpg"), /** * SVG XML mime type. */ IMAGE_SVG_XML("image/svg+xml"), /** * WebP mime type. */ IMAGE_WEBP("image/webp"); private final String value; /** * Constructor. * * @param value value */ MimeType(String value) { this.value = value; } /** * Get value. * * @return value */ @JsonValue public String getValue() { return value; } /** * Create from value. * * @param value value * @return MimeType */ @JsonCreator public static MimeType fromValue(String value) { for (MimeType t : MimeType.values()) { if (t.value.equalsIgnoreCase(value)) { return t; } } throw new IllegalArgumentException("Unknown mimeType: " + value); } } /** * Theme enum: light or dark. * Serialized/deserialized as the lowercase string value. */ public static enum Theme { /** * Light theme. */ LIGHT("light"), /** * Dark theme. */ DARK("dark"); private final String value; /** * Constructor. * * @param value value */ Theme(String value) { this.value = value; } /** * Get value. * * @return value */ @JsonValue public String getValue() { return value; } /** * Create from value. * * @param value value * @return Theme */ @JsonCreator public static Theme fromValue(String value) { for (Theme t : Theme.values()) { if (t.value.equalsIgnoreCase(value)) { return t; } } throw new IllegalArgumentException("Unknown theme: " + value); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/Input.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; /** * Input per components.schemas.Input. * * @author xinluo */ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class Input { private String description; private Boolean isRequired; private String format; private String value; private Boolean isSecret; private String defaultValue; private List choices; private String placeholder; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Boolean getIsRequired() { return isRequired; } public void setIsRequired(Boolean isRequired) { this.isRequired = isRequired; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } public String getValue() { return value; } public void setValue(String value) { this.value = value; } public Boolean getIsSecret() { return isSecret; } public void setIsSecret(Boolean isSecret) { this.isSecret = isSecret; } public String getDefaultValue() { return defaultValue; } public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; } public List getChoices() { return choices; } public void setChoices(List choices) { this.choices = choices; } /** * Get placeholder. * * @return placeholder */ public String getPlaceholder() { return placeholder; } /** * Set placeholder. * * @param placeholder placeholder */ public void setPlaceholder(String placeholder) { this.placeholder = placeholder; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/InputWithVariables.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import java.util.Map; /** * InputWithVariables per components.schemas.InputWithVariables. * * @author xinluo */ @JsonIgnoreProperties(ignoreUnknown = true) public class InputWithVariables extends Input { private Map variables; public Map getVariables() { return variables; } public void setVariables(Map variables) { this.variables = variables; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/KeyValueInput.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * KeyValueInput used for headers / env vars. * * @author xinluo */ @JsonIgnoreProperties(ignoreUnknown = true) public class KeyValueInput extends InputWithVariables { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/McpErrorResponse.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * Error response wrapper with single error string. * * @author xinluo */ @JsonIgnoreProperties(ignoreUnknown = true) public class McpErrorResponse { private String error; public String getError() { return error; } public void setError(String error) { this.error = error; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/McpRegistryServerDetail.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.HashMap; import java.util.List; import java.util.Map; /** * McpRegistryServerDetail (renamed from ServerDetail). * * @author xinluo */ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class McpRegistryServerDetail { private String name; private String description; private String title; private Repository repository; private String version; private String websiteUrl; private List icons; @JsonProperty("$schema") private String schema; private List packages; private List remotes; @JsonProperty("_meta") private Meta meta; @JsonInclude(JsonInclude.Include.NON_NULL) public static class Meta { @JsonProperty("io.modelcontextprotocol.registry/publisher-provided") private Map publisherMeta; @JsonAnySetter private Map extensionMeta = new HashMap<>(); public Map getPublisherMeta() { return publisherMeta; } public void setPublisherMeta(Map publisherMeta) { this.publisherMeta = publisherMeta; } @JsonAnyGetter public Map getExtensionMeta() { return extensionMeta; } } public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } public List getPackages() { return packages; } public void setPackages(List packages) { this.packages = packages; } public List getRemotes() { return remotes; } public void setRemotes(List remotes) { this.remotes = remotes; } public Meta getMeta() { return meta; } public void setMeta(Meta meta) { this.meta = meta; } 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 String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Repository getRepository() { return repository; } public void setRepository(Repository repository) { this.repository = repository; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getWebsiteUrl() { return websiteUrl; } public void setWebsiteUrl(String websiteUrl) { this.websiteUrl = websiteUrl; } public List getIcons() { return icons; } public void setIcons(List icons) { this.icons = icons; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/McpRegistryServerList.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; /** * McpRegistryServerList (renamed from ServerList) aligns with registry package. * * @author xinluo */ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class McpRegistryServerList { private List servers; @JsonInclude(JsonInclude.Include.NON_NULL) private Metadata metadata; public List getServers() { return servers; } public void setServers(List servers) { this.servers = servers; } public Metadata getMetadata() { return metadata; } public void setMetadata(Metadata metadata) { this.metadata = metadata; } @JsonInclude(JsonInclude.Include.NON_NULL) public static class Metadata { @JsonProperty("nextCursor") @JsonAlias("next_cursor") private String nextCursor; private Integer count; public Metadata() { } public Metadata(String nextCursor, Integer count) { this.nextCursor = nextCursor; this.count = count; } public String getNextCursor() { return nextCursor; } public void setNextCursor(String nextCursor) { this.nextCursor = nextCursor; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/McpServerStatusEnum.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; /** * McpServerStatusEnum. * * @author xinluo */ public enum McpServerStatusEnum { /** * active. */ ACTIVE("active"), /** * deleted. */ DELETED("deleted"), /** * deprecated. */ DEPRECATED("deprecated"); /** * name. */ private final String name; McpServerStatusEnum(String name) { this.name = name; } public String getName() { return name; } /** * parse string status to enum. * return null if status is not valid. * @param status status. * @return McpServerStatusEnum. */ public static McpServerStatusEnum parseStatus(String status) { for (McpServerStatusEnum value : values()) { if (value.getName().equals(status)) { return value; } } return null; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/NamedArgument.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeName; /** * NamedArgument per components.schemas.NamedArgument. * * @author xinluo */ @JsonTypeName("named") @JsonIgnoreProperties(ignoreUnknown = true) @JsonInclude(JsonInclude.Include.NON_NULL) public class NamedArgument extends InputWithVariables implements Argument { private String type = "named"; private String name; private Boolean isRepeated; private String valueHint; /** * Get type. * * @return type */ public String getType() { return type; } /** * Set type. * * @param type type */ public void setType(String type) { this.type = type; } /** * Get name. * * @return name */ public String getName() { return name; } /** * Set name. * * @param name name */ public void setName(String name) { this.name = name; } /** * Get is repeated flag. * * @return is repeated */ public Boolean getIsRepeated() { return isRepeated; } /** * Set is repeated flag. * * @param isRepeated is repeated */ public void setIsRepeated(Boolean isRepeated) { this.isRepeated = isRepeated; } /** * Get value hint. * * @return value hint */ public String getValueHint() { return valueHint; } /** * Set value hint. * * @param valueHint value hint */ public void setValueHint(String valueHint) { this.valueHint = valueHint; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/OfficialMeta.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; /** * Official metadata inside _meta. * * @author xinluo */ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class OfficialMeta { private String publishedAt; private String updatedAt; private Boolean isLatest; private String status; /** * Get published at timestamp. * * @return published at */ public String getPublishedAt() { return publishedAt; } /** * Set published at timestamp. * * @param publishedAt published at */ public void setPublishedAt(String publishedAt) { this.publishedAt = publishedAt; } /** * Get updated at timestamp. * * @return updated at */ public String getUpdatedAt() { return updatedAt; } /** * Set updated at timestamp. * * @param updatedAt updated at */ public void setUpdatedAt(String updatedAt) { this.updatedAt = updatedAt; } /** * Get is latest flag. * * @return is latest */ public Boolean getIsLatest() { return isLatest; } /** * Set is latest flag. * * @param isLatest is latest */ public void setIsLatest(Boolean isLatest) { this.isLatest = isLatest; } /** * Get status. * * @return status */ public String getStatus() { return status; } /** * Set status. * * @param status status */ public void setStatus(String status) { this.status = status; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/Package.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.util.List; /** * Package per components.schemas.Package. * * @author xinluo */ @JsonIgnoreProperties(ignoreUnknown = true) public class Package { private String registryType; private String registryBaseUrl; private String identifier; private String version; private String fileSha256; private String runtimeHint; private List runtimeArguments; private List packageArguments; private List environmentVariables; /** * Transport field - required, supports multiple transport types (stdio/streamable-http/sse). */ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true) @JsonSubTypes({ @JsonSubTypes.Type(value = StdioTransport.class, name = "stdio"), @JsonSubTypes.Type(value = StreamableHttpTransport.class, name = "streamable-http"), @JsonSubTypes.Type(value = SseTransport.class, name = "sse") }) @JsonIgnoreProperties(ignoreUnknown = true) private Object transport; /** * Get registry type. * * @return registry type */ public String getRegistryType() { return registryType; } /** * Set registry type. * * @param registryType registry type */ public void setRegistryType(String registryType) { this.registryType = registryType; } /** * Get registry base URL. * * @return registry base URL */ public String getRegistryBaseUrl() { return registryBaseUrl; } /** * Set registry base URL. * * @param registryBaseUrl registry base URL */ public void setRegistryBaseUrl(String registryBaseUrl) { this.registryBaseUrl = registryBaseUrl; } /** * Get identifier. * * @return identifier */ public String getIdentifier() { return identifier; } /** * Set identifier. * * @param identifier identifier */ public void setIdentifier(String identifier) { this.identifier = identifier; } /** * Get version. * * @return version */ public String getVersion() { return version; } /** * Set version. * * @param version version */ public void setVersion(String version) { this.version = version; } /** * Get file SHA 256. * * @return file SHA 256 */ public String getFileSha256() { return fileSha256; } /** * Set file SHA 256. * * @param fileSha256 file SHA 256 */ public void setFileSha256(String fileSha256) { this.fileSha256 = fileSha256; } /** * Get runtime hint. * * @return runtime hint */ public String getRuntimeHint() { return runtimeHint; } /** * Set runtime hint. * * @param runtimeHint runtime hint */ public void setRuntimeHint(String runtimeHint) { this.runtimeHint = runtimeHint; } /** * Get runtime arguments. * * @return runtime arguments */ public List getRuntimeArguments() { return runtimeArguments; } /** * Set runtime arguments. * * @param runtimeArguments runtime arguments */ public void setRuntimeArguments(List runtimeArguments) { this.runtimeArguments = runtimeArguments; } /** * Get package arguments. * * @return package arguments */ public List getPackageArguments() { return packageArguments; } /** * Set package arguments. * * @param packageArguments package arguments */ public void setPackageArguments(List packageArguments) { this.packageArguments = packageArguments; } /** * Get environment variables. * * @return environment variables */ public List getEnvironmentVariables() { return environmentVariables; } /** * Set environment variables. * * @param environmentVariables environment variables */ public void setEnvironmentVariables(List environmentVariables) { this.environmentVariables = environmentVariables; } /** * Get transport. * * @return transport */ public Object getTransport() { return transport; } /** * Set transport. * * @param transport transport */ public void setTransport(Object transport) { this.transport = transport; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/PositionalArgument.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeName; /** * PositionalArgument per components.schemas.PositionalArgument. * * @author xinluo */ @JsonTypeName("positional") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class PositionalArgument extends InputWithVariables implements Argument { private String type = "positional"; private String valueHint; private Boolean isRepeated; /** * Get type. * * @return type */ public String getType() { return type; } /** * Set type. * * @param type type */ public void setType(String type) { this.type = type; } /** * Get value hint. * * @return value hint */ public String getValueHint() { return valueHint; } /** * Set value hint. * * @param valueHint value hint */ public void setValueHint(String valueHint) { this.valueHint = valueHint; } /** * Get is repeated flag. * * @return is repeated */ public Boolean getIsRepeated() { return isRepeated; } /** * Set is repeated flag. * * @param isRepeated is repeated */ public void setIsRepeated(Boolean isRepeated) { this.isRepeated = isRepeated; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/Remote.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import java.util.List; /** * Remote per components.schemas.Remote. * * @author xinluo */ @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class Remote { private String type; private String url; private List headers; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public List getHeaders() { return headers; } public void setHeaders(List headers) { this.headers = headers; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/Repository.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * Repository per components.schemas.Repository. * * @author xinluo */ @JsonIgnoreProperties(ignoreUnknown = true) public class Repository { private String url; private String source; private String id; private String subfolder; public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getSource() { return source; } public void setSource(String source) { this.source = source; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getSubfolder() { return subfolder; } public void setSubfolder(String subfolder) { this.subfolder = subfolder; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/ServerResponse.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.HashMap; import java.util.Map; /** * ServerResponse. * * @author xinluo */ @JsonIgnoreProperties(ignoreUnknown = true) public class ServerResponse { private McpRegistryServerDetail server; @JsonProperty("_meta") private Meta meta; public McpRegistryServerDetail getServer() { return server; } public void setServer(McpRegistryServerDetail server) { this.server = server; } public Meta getMeta() { return meta; } public void setMeta(Meta meta) { this.meta = meta; } /** * _meta wrapper allowing extension namespaces. * * @author xinluo */ @JsonInclude(JsonInclude.Include.NON_NULL) public static class Meta { @JsonProperty("io.modelcontextprotocol.registry/official") private OfficialMeta official; @JsonAnySetter private Map additionalMetadata = new HashMap<>(); public OfficialMeta getOfficial() { return official; } public void setOfficial(OfficialMeta official) { this.official = official; } @JsonAnyGetter public Map getAdditionalMetadata() { return additionalMetadata; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/ServerVersionDetail.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; /** * ServerVersionDetail. * * @author xinluo */ @SuppressWarnings({"checkstyle:MethodName", "checkstyle:ParameterName", "checkstyle:MemberName", "checkstyle:SummaryJavadoc"}) @JsonIgnoreProperties(ignoreUnknown = true) public class ServerVersionDetail { private String version; private String release_date; private Boolean is_latest; public String getRelease_date() { return release_date; } public String getVersion() { return version; } public void setRelease_date(String releaseDate) { this.release_date = releaseDate; } public void setVersion(String version) { this.version = version; } public void setIs_latest(Boolean is_latest) { this.is_latest = is_latest; } public Boolean getIs_latest() { return is_latest; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/SseTransport.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeName; import java.util.List; /** * SseTransport per components.schemas.SseTransport. * Transport type using Server-Sent Events for MCP server communication. * * @author xinluo */ @JsonTypeName("sse") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class SseTransport { private String type = "sse"; private String url; private List headers; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public List getHeaders() { return headers; } public void setHeaders(List headers) { this.headers = headers; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/StdioTransport.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeName; /** * Stdio transport configuration. * * @author xinluo */ @JsonTypeName("stdio") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class StdioTransport { private String type = "stdio"; public String getType() { return type; } public void setType(String type) { this.type = type; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/mcp/registry/StreamableHttpTransport.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonTypeName; import java.util.List; /** * StreamableHttpTransport per components.schemas.StreamableHttpTransport. * Transport type using streamable HTTP for MCP server communication. * * @author xinluo */ @JsonTypeName("streamable-http") @JsonInclude(JsonInclude.Include.NON_NULL) @JsonIgnoreProperties(ignoreUnknown = true) public class StreamableHttpTransport { private String type = "streamable-http"; private String url; private List headers; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public List getHeaders() { return headers; } public void setHeaders(List headers) { this.headers = headers; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/Prompt.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.io.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Prompt entity for AI Prompt management. * *

Prompt is stored as a Nacos configuration with fixed group "nacos-ai-prompt" * and dataId "{promptKey}.json". The content is stored as JSON format.

* * @author nacos */ public class Prompt implements Serializable { private static final long serialVersionUID = 1L; /** * Prompt key (unique identifier within namespace). */ private String promptKey; /** * Prompt version in format "major.minor.patch" (e.g., "1.0.0"). */ private String version; /** * Prompt template content. */ private String template; /** * MD5 hash of the prompt content (for CAS operations). */ private String md5; /** * Variable definitions with optional default values. Null for legacy prompts without variable metadata. */ private List variables; public Prompt() { } public Prompt(String promptKey, String version, String template) { this.promptKey = promptKey; this.version = version; this.template = template; } public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getTemplate() { return template; } public void setTemplate(String template) { this.template = template; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public List getVariables() { return variables; } public void setVariables(List variables) { this.variables = variables; } /** * Render the prompt template by replacing variables with provided values. * *

Variables in the template are specified using {{variableName}} syntax. * This method first applies default values from variable definitions, * then overrides with user-provided values.

* *

Example: *

     * Prompt prompt = new Prompt("greeting", "1.0.0", "Hello {{name}}, welcome to {{place}}!");
     * Map<String, String> userVars = new HashMap<>();
     * userVars.put("name", "Alice");
     * userVars.put("place", "Nacos");
     * String result = prompt.render(userVars);
     * // Result: "Hello Alice, welcome to Nacos!"
     * 
*

* * @param userVariables map of variable names to their values (key: variable name, value: replacement value) * @return rendered prompt content with variables replaced, or the original template if no values available */ public String render(Map userVariables) { if (template == null) { return null; } Map merged = new HashMap<>(); if (variables != null) { for (PromptVariable v : variables) { if (v.getDefaultValue() != null) { merged.put(v.getName(), v.getDefaultValue()); } } } if (userVariables != null) { merged.putAll(userVariables); } if (merged.isEmpty()) { return template; } String result = template; for (Map.Entry entry : merged.entrySet()) { String placeholder = "{{" + entry.getKey() + "}}"; String value = entry.getValue() != null ? entry.getValue() : ""; result = result.replace(placeholder, value); } return result; } @Override public String toString() { return "Prompt{" + "promptKey='" + promptKey + '\'' + ", version='" + version + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/PromptDescriptor.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Prompt descriptor for control plane fields. * * @author nacos */ public class PromptDescriptor implements Serializable { private static final long serialVersionUID = 1L; private int schemaVersion = 1; private String promptKey; private String description; private List bizTags = new ArrayList<>(); private Long gmtModified; public int getSchemaVersion() { return schemaVersion; } public void setSchemaVersion(int schemaVersion) { this.schemaVersion = schemaVersion; } public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List getBizTags() { return bizTags; } public void setBizTags(List bizTags) { this.bizTags = bizTags; } public Long getGmtModified() { return gmtModified; } public void setGmtModified(Long gmtModified) { this.gmtModified = gmtModified; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/PromptLabelVersionMapping.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Prompt runtime label/version mapping information. * * @author nacos */ public class PromptLabelVersionMapping implements Serializable { private static final long serialVersionUID = 1L; private int schemaVersion = 1; private String promptKey; private List versions = new ArrayList<>(); private Map labels = new HashMap<>(); private String latestVersion; private Long gmtModified; public int getSchemaVersion() { return schemaVersion; } public void setSchemaVersion(int schemaVersion) { this.schemaVersion = schemaVersion; } public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } public List getVersions() { return versions; } public void setVersions(List versions) { this.versions = versions; } public Map getLabels() { return labels; } public void setLabels(Map labels) { this.labels = labels; } public String getLatestVersion() { return latestVersion; } public void setLatestVersion(String latestVersion) { this.latestVersion = latestVersion; } public Long getGmtModified() { return gmtModified; } public void setGmtModified(Long gmtModified) { this.gmtModified = gmtModified; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/PromptMetaInfo.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Prompt meta information. * * @author nacos */ public class PromptMetaInfo extends PromptMetaSummary { private static final long serialVersionUID = 1L; private List versions = new ArrayList<>(); private Map labels = new HashMap<>(); public List getVersions() { return versions; } public void setVersions(List versions) { this.versions = versions; } public Map getLabels() { return labels; } public void setLabels(Map labels) { this.labels = labels; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/PromptMetaSummary.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Prompt meta summary for prompt list response. * * @author nacos */ public class PromptMetaSummary implements Serializable { private static final long serialVersionUID = 1L; private int schemaVersion = 1; private String promptKey; private String description; private List bizTags = new ArrayList<>(); private String latestVersion; private Long gmtModified; public int getSchemaVersion() { return schemaVersion; } public void setSchemaVersion(int schemaVersion) { this.schemaVersion = schemaVersion; } public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public List getBizTags() { return bizTags; } public void setBizTags(List bizTags) { this.bizTags = bizTags; } public String getLatestVersion() { return latestVersion; } public void setLatestVersion(String latestVersion) { this.latestVersion = latestVersion; } public Long getGmtModified() { return gmtModified; } public void setGmtModified(Long gmtModified) { this.gmtModified = gmtModified; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/PromptVariable.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.io.Serializable; /** * Prompt variable definition with optional default value. * *

Represents a variable placeholder (e.g., {{variableName}}) in a prompt template, * along with its optional default value and description.

* * @author nacos */ public class PromptVariable implements Serializable { private static final long serialVersionUID = 1L; /** * Variable name (matches the placeholder name in template, e.g., "question" for {{question}}). */ private String name; /** * Default value for this variable. Null means the variable has no default (considered required). */ private String defaultValue; /** * Optional description explaining the purpose or expected content of this variable. */ private String description; public PromptVariable() { } public PromptVariable(String name, String defaultValue, String description) { this.name = name; this.defaultValue = defaultValue; this.description = description; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDefaultValue() { return defaultValue; } public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } @Override public String toString() { return "PromptVariable{" + "name='" + name + '\'' + ", defaultValue='" + defaultValue + '\'' + ", description='" + description + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/PromptVersionInfo.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.util.List; /** * Prompt version information. * * @author nacos */ public class PromptVersionInfo extends PromptVersionSummary { private static final long serialVersionUID = 1L; private String template; private String md5; private List variables; public String getTemplate() { return template; } public void setTemplate(String template) { this.template = template; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public List getVariables() { return variables; } public void setVariables(List variables) { this.variables = variables; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/prompt/PromptVersionSummary.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.prompt; import java.io.Serializable; /** * Prompt version summary for prompt version list response. * * @author nacos */ public class PromptVersionSummary implements Serializable { private static final long serialVersionUID = 1L; private String promptKey; private String version; private String commitMsg; private String srcUser; private Long gmtModified; public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getCommitMsg() { return commitMsg; } public void setCommitMsg(String commitMsg) { this.commitMsg = commitMsg; } public String getSrcUser() { return srcUser; } public void setSrcUser(String srcUser) { this.srcUser = srcUser; } public Long getGmtModified() { return gmtModified; } public void setGmtModified(Long gmtModified) { this.gmtModified = gmtModified; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/skills/Skill.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.skills; import java.util.Map; /** * Claude Skill entity for independent Skills management. * Simplified structure with core fields only. * * @author nacos */ public class Skill { /** * Namespace ID (Nacos management field). */ private String namespaceId; /** * Skill name (unique identifier, only allows English letters, underscore, and hyphen). */ private String name; /** * Skill description. */ private String description; /** * Claude instruction (note: singular instruction). */ private String instruction; /** * Resource map (note: singular resource, key is resource name). */ private Map resource; public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } 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 String getInstruction() { return instruction; } public void setInstruction(String instruction) { this.instruction = instruction; } public Map getResource() { return resource; } public void setResource(Map resource) { this.resource = resource; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillBasicInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.skills; import java.util.Objects; /** * Skill basic info for list response. * * @author nacos */ public class SkillBasicInfo { private String namespaceId; private String name; private String description; private Long updateTime; public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } 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 Long getUpdateTime() { return updateTime; } public void setUpdateTime(Long updateTime) { this.updateTime = updateTime; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } SkillBasicInfo that = (SkillBasicInfo) o; return Objects.equals(namespaceId, that.namespaceId) && Objects.equals(name, that.name) && Objects.equals(description, that.description) && Objects.equals(updateTime, that.updateTime); } @Override public int hashCode() { return Objects.hash(namespaceId, name, description, updateTime); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillResource.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.skills; import java.util.Map; /** * Claude Skill Resource structure. * * @author nacos */ public class SkillResource { /** * Resource name (includes file extension, e.g., config_check_template.json). */ private String name; /** * Resource type: template, data, script, etc. */ private String type; /** * Resource content (string format, read from independent configuration). */ private String content; /** * Resource metadata (optional). */ private Map metadata; /** * Get resource unique identifier. * Format: "type::name" if type is not blank, otherwise "name". * The separator "::" is used because it's not in the allowed character set for type and name. * * @return resource unique identifier */ public String getResourceIdentifier() { if (type != null && !type.trim().isEmpty()) { return type + "::" + name; } return name; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public Map getMetadata() { return metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/model/skills/SkillUtils.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.skills; import com.alibaba.nacos.api.utils.StringUtils; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Map; /** * Utility class for Skill operations. * * @author nacos */ public class SkillUtils { private static final String NEWLINE = "\n"; private static final String EMPTY_STRING = ""; private static final String COLON = ":"; private static final String DOUBLE_QUOTE = "\""; private static final String SINGLE_QUOTE = "'"; private static final String ESCAPED_DOUBLE_QUOTE = "\\\""; /** * Strategy for handling existing skill directories. */ public enum ExistingDirectoryStrategy { /** * Overwrite existing directory (delete and recreate). */ OVERWRITE, /** * Backup existing directory by renaming it with timestamp suffix. */ BACKUP, /** * Throw exception if directory already exists. */ FAIL } /** * Convert Skill object to SKILL.md markdown content. * * @param skill the Skill object to convert * @return SKILL.md markdown content */ public static String toMarkdown(Skill skill) { if (skill == null) { return EMPTY_STRING; } StringBuilder markdown = new StringBuilder(); // YAML front matter markdown.append("---\n"); markdown.append("name: ").append(escapeYamlValue(skill.getName())).append("\n"); markdown.append("description: ").append(escapeYamlValue(skill.getDescription())).append("\n"); markdown.append("---\n\n"); // Instruction content if (!StringUtils.isBlank(skill.getInstruction())) { String instruction = skill.getInstruction().trim(); markdown.append(instruction); // Ensure there's a newline at the end if instruction doesn't end with one if (!instruction.isEmpty() && !instruction.endsWith(NEWLINE)) { markdown.append(NEWLINE); } } return markdown.toString(); } /** * Escape YAML value to handle special characters. * If value contains special characters (colon, quotes, newlines), wrap it in double quotes. * * @param value the value to escape * @return escaped YAML value */ private static String escapeYamlValue(String value) { if (value == null) { return EMPTY_STRING; } // If value contains special characters, wrap in double quotes if (value.contains(COLON) || value.contains(DOUBLE_QUOTE) || value.contains(SINGLE_QUOTE) || value.contains(NEWLINE)) { // Escape double quotes in the value return DOUBLE_QUOTE + value.replace(DOUBLE_QUOTE, ESCAPED_DOUBLE_QUOTE) + DOUBLE_QUOTE; } return value; } /** * Sync Skill object to local directory. * Creates the skill directory structure, SKILL.md file, and resource files. * Uses OVERWRITE strategy by default. * * @param skill the Skill object to sync * @param baseDir the base directory path where the skill directory will be created * @throws IOException if file operations fail * @throws IllegalArgumentException if skill is null or skill name is blank */ public static void syncToLocal(Skill skill, String baseDir) throws IOException { syncToLocal(skill, baseDir, ExistingDirectoryStrategy.OVERWRITE); } /** * Sync Skill object to local directory with strategy. * Creates the skill directory structure, SKILL.md file, and resource files. * Uses atomic operation: creates temporary directory first, writes all files, * then renames to final directory to ensure integrity. * * @param skill the Skill object to sync * @param baseDir the base directory path where the skill directory will be created * @param strategy the strategy for handling existing directories * @throws IOException if file operations fail * @throws IllegalArgumentException if skill is null or skill name is blank * @throws FileAlreadyExistsException if directory exists and strategy is FAIL */ public static void syncToLocal(Skill skill, String baseDir, ExistingDirectoryStrategy strategy) throws IOException { if (skill == null) { throw new IllegalArgumentException("Skill cannot be null"); } if (StringUtils.isBlank(skill.getName())) { throw new IllegalArgumentException("Skill name cannot be blank"); } if (StringUtils.isBlank(baseDir)) { throw new IllegalArgumentException("Base directory cannot be blank"); } if (strategy == null) { strategy = ExistingDirectoryStrategy.OVERWRITE; } // Create skill directory path: {baseDir}/{skillName} Path basePath = Paths.get(baseDir); Path skillDir = basePath.resolve(skill.getName()); // Delegate to core implementation syncToLocalCore(skill, skillDir, basePath, strategy); } /** * Sync Skill object to local directory with custom skill directory name. * Creates the skill directory structure, SKILL.md file, and resource files. * Uses OVERWRITE strategy by default. * * @param skill the Skill object to sync * @param baseDir the base directory path where the skill directory will be created * @param skillDirName the custom directory name for the skill (if null, uses skill name) * @throws IOException if file operations fail * @throws IllegalArgumentException if skill is null or baseDir is blank */ public static void syncToLocal(Skill skill, String baseDir, String skillDirName) throws IOException { syncToLocal(skill, baseDir, skillDirName, ExistingDirectoryStrategy.OVERWRITE); } /** * Sync Skill object to local directory with custom skill directory name and strategy. * Creates the skill directory structure, SKILL.md file, and resource files. * Uses atomic operation: creates temporary directory first, writes all files, * then renames to final directory to ensure integrity. * * @param skill the Skill object to sync * @param baseDir the base directory path where the skill directory will be created * @param skillDirName the custom directory name for the skill (if null, uses skill name) * @param strategy the strategy for handling existing directories * @throws IOException if file operations fail * @throws IllegalArgumentException if skill is null or baseDir is blank * @throws FileAlreadyExistsException if directory exists and strategy is FAIL */ public static void syncToLocal(Skill skill, String baseDir, String skillDirName, ExistingDirectoryStrategy strategy) throws IOException { if (skill == null) { throw new IllegalArgumentException("Skill cannot be null"); } if (StringUtils.isBlank(baseDir)) { throw new IllegalArgumentException("Base directory cannot be blank"); } if (strategy == null) { strategy = ExistingDirectoryStrategy.OVERWRITE; } // Use custom directory name or fall back to skill name String dirName = !StringUtils.isBlank(skillDirName) ? skillDirName : skill.getName(); if (StringUtils.isBlank(dirName)) { throw new IllegalArgumentException("Skill directory name cannot be blank"); } // Create skill directory path: {baseDir}/{skillDirName} Path basePath = Paths.get(baseDir); Path skillDir = basePath.resolve(dirName); // Delegate to core implementation syncToLocalCore(skill, skillDir, basePath, strategy); } /** * Core implementation for syncing Skill to local directory. * This method contains the common logic for all syncToLocal variants. * * @param skill the Skill object to sync * @param skillDir the target skill directory path * @param basePath the base directory path * @param strategy the strategy for handling existing directories * @throws IOException if file operations fail * @throws FileAlreadyExistsException if directory exists and strategy is FAIL */ private static void syncToLocalCore(Skill skill, Path skillDir, Path basePath, ExistingDirectoryStrategy strategy) throws IOException { // Step 1: If strategy is FAIL, check if directory exists and throw exception immediately if (strategy == ExistingDirectoryStrategy.FAIL) { if (Files.exists(skillDir) && Files.isDirectory(skillDir)) { throw new FileAlreadyExistsException("Skill directory already exists: " + skillDir); } } // Step 2: Create temporary directory and write all files String dirName = skillDir.getFileName().toString(); Path tempSkillDir = basePath.resolve(dirName + ".tmp." + System.currentTimeMillis()); try { // Create temporary skill directory Files.createDirectories(tempSkillDir); // Write SKILL.md file String markdownContent = toMarkdown(skill); Path skillMdPath = tempSkillDir.resolve("SKILL.md"); Files.write(skillMdPath, markdownContent.getBytes(StandardCharsets.UTF_8)); // Write resource files if (skill.getResource() != null && !skill.getResource().isEmpty()) { for (Map.Entry entry : skill.getResource().entrySet()) { SkillResource resource = entry.getValue(); if (resource == null) { continue; } String resourceName = resource.getName(); if (StringUtils.isBlank(resourceName)) { // Use key as resource name if name is blank resourceName = entry.getKey(); } String resourceType = resource.getType(); String resourceContent = resource.getContent(); // Determine resource file path Path resourcePath; if (!StringUtils.isBlank(resourceType)) { // Resources with type: {tempSkillDir}/{type}/{resourceName} Path typeDir = tempSkillDir.resolve(resourceType); Files.createDirectories(typeDir); resourcePath = typeDir.resolve(resourceName); } else { // Resources without type: {tempSkillDir}/{resourceName} resourcePath = tempSkillDir.resolve(resourceName); } // Write resource content (use empty string if content is null) String content = resourceContent != null ? resourceContent : ""; Files.write(resourcePath, content.getBytes(StandardCharsets.UTF_8)); } } // Step 3: All files written successfully, now handle final directory boolean oldDirExists = Files.exists(skillDir) && Files.isDirectory(skillDir); if (!oldDirExists) { // Old directory doesn't exist, directly rename temp directory to final directory Files.move(tempSkillDir, skillDir, StandardCopyOption.ATOMIC_MOVE); } else { // Old directory exists, need to backup first // Step 3.1: Rename old directory to backup directory Path backupDir = createBackupDirectoryPath(skillDir); Files.move(skillDir, backupDir, StandardCopyOption.ATOMIC_MOVE); // Step 3.2: Rename temp directory to final directory Files.move(tempSkillDir, skillDir, StandardCopyOption.ATOMIC_MOVE); // Step 3.3: Handle backup directory based on strategy if (strategy == ExistingDirectoryStrategy.OVERWRITE) { // Delete backup directory deleteDirectory(backupDir); } // If strategy is BACKUP, keep the backup directory (do nothing) } } catch (Exception e) { // Clean up temporary directory on failure if (Files.exists(tempSkillDir)) { try { deleteDirectory(tempSkillDir); } catch (IOException cleanupException) { // Log but don't throw - original exception is more important } } throw e; } } /** * Create backup directory path with timestamp suffix. * If backup directory already exists, append counter to ensure uniqueness. * * @param skillDir the skill directory path * @return backup directory path */ private static Path createBackupDirectoryPath(Path skillDir) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss"); String timestamp = dateFormat.format(new Date()); Path backupDir = skillDir.getParent().resolve(skillDir.getFileName().toString() + ".backup." + timestamp); // If backup directory already exists, append counter int counter = 1; Path finalBackupDir = backupDir; while (Files.exists(finalBackupDir)) { finalBackupDir = skillDir.getParent().resolve( skillDir.getFileName().toString() + ".backup." + timestamp + "." + counter); counter++; } return finalBackupDir; } /** * Recursively delete a directory and all its contents. * * @param directory the directory to delete * @throws IOException if deletion fails */ private static void deleteDirectory(Path directory) throws IOException { if (!Files.exists(directory)) { return; } // Delete files before directories Files.walk(directory) .sorted((a, b) -> b.compareTo(a)) .forEach(path -> { try { Files.delete(path); } catch (IOException e) { throw new RuntimeException("Failed to delete: " + path, e); } }); } /** * Main config dataId for skill. */ public static final String SKILL_MAIN_DATA_ID = "skill.json"; /** * Resource config dataId prefix. */ public static final String RESOURCE_DATA_ID_PREFIX = "resource_"; /** * Resource config dataId suffix. */ public static final String RESOURCE_DATA_ID_SUFFIX = ".json"; /** * Skill group prefix. */ public static final String SKILL_GROUP_PREFIX = "skill_"; private static final String DOUBLE_UNDERSCORE = "__"; private static final String FILE_EXTENSION_PATTERN = ".*\\.[a-zA-Z0-9]+$"; /** * Configuration info containing dataId and group. */ public static class ConfigInfo { private final String dataId; private final String group; public ConfigInfo(String dataId, String group) { this.dataId = dataId; this.group = group; } public String getDataId() { return dataId; } public String getGroup() { return group; } } /** * Generate resource ID from resource type and name. * Format: {type}_{resourcename} * If resourcename ends with .xx, convert the last . to __ * Slashes in type are encoded as dots so that dataId (resource_{resourceId}.json) is valid in Nacos. * * @param type resource type (can be null or empty; may contain / for multi-level paths) * @param resourceName resource name * @return resource ID (safe for use in config dataId) */ public static String generateResourceId(String type, String resourceName) { if (resourceName == null || resourceName.trim().isEmpty()) { return ""; } // If resourcename ends with .xx, convert the last . to __ String processedName = resourceName; if (resourceName.matches(FILE_EXTENSION_PATTERN)) { // Replace only the last dot before the extension int lastDotIndex = resourceName.lastIndexOf('.'); if (lastDotIndex > 0) { processedName = resourceName.substring(0, lastDotIndex) + DOUBLE_UNDERSCORE + resourceName.substring(lastDotIndex + 1); } } if (type != null && !type.trim().isEmpty()) { // Encode / as . so dataId has no slash (Nacos config key compatibility) String safeType = type.trim().replace("/", "."); return safeType + "_" + processedName; } else { return processedName; } } /** * Build skill main config info (dataId and group). * This is the unified method for building main config mapping. * * @param skillName name of skill * @return ConfigInfo containing dataId and group * @throws IllegalArgumentException if skillName is blank */ public static ConfigInfo buildSkillMainConfigInfo(String skillName) { if (StringUtils.isBlank(skillName)) { throw new IllegalArgumentException("Skill name cannot be blank"); } return new ConfigInfo(SKILL_MAIN_DATA_ID, SKILL_GROUP_PREFIX + skillName); } /** * Build skill resource config info (dataId and group). * This is the unified method for building resource config mapping. * * @param skillName name of skill * @param type resource type (can be null or empty) * @param resourceName resource name * @return ConfigInfo containing dataId and group * @throws IllegalArgumentException if skillName or resourceName is blank */ public static ConfigInfo buildSkillResourceConfigInfo(String skillName, String type, String resourceName) { if (StringUtils.isBlank(skillName)) { throw new IllegalArgumentException("Skill name cannot be blank"); } if (StringUtils.isBlank(resourceName)) { throw new IllegalArgumentException("Resource name cannot be blank"); } String resourceId = generateResourceId(type, resourceName); String dataId = RESOURCE_DATA_ID_PREFIX + resourceId + RESOURCE_DATA_ID_SUFFIX; String group = SKILL_GROUP_PREFIX + skillName; return new ConfigInfo(dataId, group); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/AiRemoteConstants.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote; /** * Retain all ai module request type constants. * * @author xiweng.yy */ public class AiRemoteConstants { public static final String REGISTER_ENDPOINT = "registerEndpoint"; public static final String BATCH_REGISTER_ENDPOINT = "batchRegisterEndpoint"; public static final String DE_REGISTER_ENDPOINT = "deregisterEndpoint"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/AbstractAgentRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.Request; /** * Nacos AI module agent request. * * @author xiweng.yy */ public class AbstractAgentRequest extends Request { private String namespaceId; private String agentName; @Override public String getModule() { return Constants.AI.AI_MODULE; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getAgentName() { return agentName; } public void setAgentName(String agentName) { this.agentName = agentName; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/AbstractMcpRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.Request; /** * Nacos AI module mcp request. * * @author xiweng.yy */ public abstract class AbstractMcpRequest extends Request { private String namespaceId; private String mcpId; private String mcpName; @Override public String getModule() { return Constants.AI.AI_MODULE; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getMcpId() { return mcpId; } public void setMcpId(String mcpId) { this.mcpId = mcpId; } public String getMcpName() { return mcpName; } public void setMcpName(String mcpName) { this.mcpName = mcpName; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/AbstractPromptRequest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.Request; /** * Nacos AI module prompt request. * * @author nacos */ public abstract class AbstractPromptRequest extends Request { private String namespaceId; private String promptKey; @Override public String getModule() { return Constants.AI.AI_MODULE; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getPromptKey() { return promptKey; } public void setPromptKey(String promptKey) { this.promptKey = promptKey; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/AgentEndpointRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; /** * Register or Deregister endpoint for agent to nacos AI module request. * * @author xiweng.yy */ public class AgentEndpointRequest extends AbstractAgentRequest { private AgentEndpoint endpoint; /** * Should be {@link AiRemoteConstants#REGISTER_ENDPOINT} or {@link AiRemoteConstants#DE_REGISTER_ENDPOINT}. */ private String type; public AgentEndpoint getEndpoint() { return endpoint; } public void setEndpoint(AgentEndpoint endpoint) { this.endpoint = endpoint; } public String getType() { return type; } public void setType(String type) { this.type = type; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/BatchAgentEndpointRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import java.util.Collection; /** * Batch Register endpoints for agent to nacos AI module request. * * @author xiweng.yy */ public class BatchAgentEndpointRequest extends AbstractAgentRequest { private Collection endpoints; public Collection getEndpoints() { return endpoints; } public void setEndpoints(Collection endpoints) { this.endpoints = endpoints; } /** * Should be {@link AiRemoteConstants#BATCH_REGISTER_ENDPOINT}. */ public String getType() { return AiRemoteConstants.BATCH_REGISTER_ENDPOINT; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/McpServerEndpointRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; /** * Register or Deregister endpoint for mcp server to nacos AI module request. * * @author xiweng.yy */ public class McpServerEndpointRequest extends AbstractMcpRequest { private String address; private int port; /** * The provided mcp server version of this endpoint, if empty, the endpoint will be return for all versions. */ private String version; /** * Should be {@link AiRemoteConstants#REGISTER_ENDPOINT} or {@link AiRemoteConstants#DE_REGISTER_ENDPOINT}. */ private String type; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/QueryAgentCardRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; /** * Nacos AI module query agent card request. * * @author xiweng.yy */ public class QueryAgentCardRequest extends AbstractAgentRequest { private String version; private String registrationType; public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getRegistrationType() { return registrationType; } public void setRegistrationType(String registrationType) { this.registrationType = registrationType; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/QueryMcpServerRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; /** * Nacos AI module query mcp request. * * @author xiweng.yy */ public class QueryMcpServerRequest extends AbstractMcpRequest { private String version; public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/QueryPromptRequest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; /** * Nacos AI module query prompt request. * * @author nacos */ public class QueryPromptRequest extends AbstractPromptRequest { private String version; private String label; private String md5; public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/ReleaseAgentCardRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; /** * Nacos AI module release new agent card or new version of exist agent card request. * * @author xiweng.yy */ public class ReleaseAgentCardRequest extends AbstractAgentRequest { private AgentCard agentCard; private String registrationType = AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE; private boolean setAsLatest; public AgentCard getAgentCard() { return agentCard; } public void setAgentCard(AgentCard agentCard) { this.agentCard = agentCard; } public String getRegistrationType() { return registrationType; } public void setRegistrationType(String registrationType) { this.registrationType = registrationType; } public boolean isSetAsLatest() { return setAsLatest; } public void setSetAsLatest(boolean setAsLatest) { this.setAsLatest = setAsLatest; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/request/ReleaseMcpServerRequest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; /** * Nacos AI module release new mcp server or new version of exist mcp server request. * *

* If mcp server is not exist, request will create an new mcp server with parameter specification. * If mcp server is exist, but version in specification is new one, request will create a new version of mcp server. * If mcp server is exist, and version in specification is exist, request will do nothing. *

* * @author xiweng.yy */ public class ReleaseMcpServerRequest extends AbstractMcpRequest { private McpServerBasicInfo serverSpecification; private McpToolSpecification toolSpecification; private McpEndpointSpec endpointSpecification; public McpServerBasicInfo getServerSpecification() { return serverSpecification; } public void setServerSpecification(McpServerBasicInfo serverSpecification) { this.serverSpecification = serverSpecification; } public McpToolSpecification getToolSpecification() { return toolSpecification; } public void setToolSpecification(McpToolSpecification toolSpecification) { this.toolSpecification = toolSpecification; } public McpEndpointSpec getEndpointSpecification() { return endpointSpecification; } public void setEndpointSpecification(McpEndpointSpec endpointSpecification) { this.endpointSpecification = endpointSpecification; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/response/AgentEndpointResponse.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.remote.response.Response; /** * Register or Deregister endpoint for agent to nacos AI module response. * * @author xiweng.yy */ public class AgentEndpointResponse extends Response { /** * Should be {@link AiRemoteConstants#REGISTER_ENDPOINT}, {@link AiRemoteConstants#DE_REGISTER_ENDPOINT}, * {@link AiRemoteConstants#BATCH_REGISTER_ENDPOINT}. */ private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/response/McpServerEndpointResponse.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.remote.response.Response; /** * Register or Deregister endpoint for mcp server to nacos AI module response. * * @author xiweng.yy */ public class McpServerEndpointResponse extends Response { /** * Should be {@link AiRemoteConstants#REGISTER_ENDPOINT} or {@link AiRemoteConstants#DE_REGISTER_ENDPOINT}. */ private String type; public String getType() { return type; } public void setType(String type) { this.type = type; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/response/QueryAgentCardResponse.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.remote.response.Response; /** * Nacos AI module query agent card response. * * @author xiweng.yy */ public class QueryAgentCardResponse extends Response { private AgentCardDetailInfo agentCardDetailInfo; public AgentCardDetailInfo getAgentCardDetailInfo() { return agentCardDetailInfo; } public void setAgentCardDetailInfo(AgentCardDetailInfo agentCardDetailInfo) { this.agentCardDetailInfo = agentCardDetailInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/response/QueryMcpServerResponse.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.remote.response.Response; /** * Nacos AI module query mcp server response. * * @author xiweng.yy */ public class QueryMcpServerResponse extends Response { private McpServerDetailInfo mcpServerDetailInfo; public McpServerDetailInfo getMcpServerDetailInfo() { return mcpServerDetailInfo; } public void setMcpServerDetailInfo(McpServerDetailInfo mcpServerDetailInfo) { this.mcpServerDetailInfo = mcpServerDetailInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/response/QueryPromptResponse.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.remote.response.Response; /** * Nacos AI module query prompt response. * * @author nacos */ public class QueryPromptResponse extends Response { private Prompt promptInfo; public Prompt getPromptInfo() { return promptInfo; } public void setPromptInfo(Prompt promptInfo) { this.promptInfo = promptInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/response/ReleaseAgentCardResponse.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * Nacos AI module release new agent card or new version of exist agent card response. * * @author xiweng.yy */ public class ReleaseAgentCardResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/ai/remote/response/ReleaseMcpServerResponse.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * Nacos AI module release new mcp server or new version of exist mcp server response. * * @author xiweng.yy */ public class ReleaseMcpServerResponse extends Response { private String mcpId; public String getMcpId() { return mcpId; } public void setMcpId(String mcpId) { this.mcpId = mcpId; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/annotation/NacosApi.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.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; /** * An annotation for Nacos API v2 Controller. * @author dongyafei * @date 2022/7/22 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NacosApi { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/annotation/NacosInjected.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.annotation; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.naming.NamingService; 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 to inject {@link ConfigService} or {@link NamingService} instance into the target Bean. * * @author Mercy * @see ConfigService * @see NamingService * @see NacosProperties * @since 0.2.1 */ @Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NacosInjected { /** * The {@link NacosProperties} attribute, If not specified, it will use global Nacos Properties. * * @return the default value is {@link NacosProperties} */ NacosProperties properties() default @NacosProperties; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/annotation/NacosProperties.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.annotation; import com.alibaba.nacos.api.PropertyKeyConst; 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 for Nacos Properties. * * @author Mercy * @see PropertyKeyConst * @since 0.2.1 */ @Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NacosProperties { /** * The prefix of property name of Nacos. */ String PREFIX = "nacos."; /** * The property name of "endpoint". */ String ENDPOINT = "endpoint"; /** * The property name of "namespace". */ String NAMESPACE = "namespace"; /** * The property name of "access-key". */ String ACCESS_KEY = "access-key"; /** * The property name of "secret-key". */ String SECRET_KEY = "secret-key"; /** * The property name of "server-addr". */ String SERVER_ADDR = "server-addr"; /** * The property name of "context-path". */ String CONTEXT_PATH = "context-path"; /** * The property name of "cluster-name". */ String CLUSTER_NAME = "cluster-name"; /** * The property name of "encode". */ String ENCODE = "encode"; /** * The property name of "long-poll.timeout". */ String CONFIG_LONG_POLL_TIMEOUT = "configLongPollTimeout"; /** * The property name of "config.retry.time". */ String CONFIG_RETRY_TIME = "configRetryTime"; /** * The property name of "maxRetry". */ String MAX_RETRY = "maxRetry"; /** * The property name of "enableRemoteSyncConfig". */ String ENABLE_REMOTE_SYNC_CONFIG = "enableRemoteSyncConfig"; /** * The property name of "username". */ String USERNAME = "username"; /** * The property name of "password". */ String PASSWORD = "password"; /** * The placeholder of endpoint, the value is "${nacos.endpoint:}". */ String ENDPOINT_PLACEHOLDER = "${" + PREFIX + ENDPOINT + ":}"; /** * The placeholder of endpoint, the value is "${nacos.namespace:}". */ String NAMESPACE_PLACEHOLDER = "${" + PREFIX + NAMESPACE + ":}"; /** * The placeholder of endpoint, the value is "${nacos.access-key:}". */ String ACCESS_KEY_PLACEHOLDER = "${" + PREFIX + ACCESS_KEY + ":}"; /** * The placeholder of endpoint, the value is "${nacos.secret-key:}". */ String SECRET_KEY_PLACEHOLDER = "${" + PREFIX + SECRET_KEY + ":}"; /** * The placeholder of endpoint, the value is ${nacos.server-addr:}". */ String SERVER_ADDR_PLACEHOLDER = "${" + PREFIX + SERVER_ADDR + ":}"; /** * The placeholder of endpoint, the value is ${nacos.context-path:}. */ String CONTEXT_PATH_PLACEHOLDER = "${" + PREFIX + CONTEXT_PATH + ":}"; /** * The placeholder of endpoint, the value is "${nacos.cluster-name:}". */ String CLUSTER_NAME_PLACEHOLDER = "${" + PREFIX + CLUSTER_NAME + ":}"; /** * The placeholder of {@link NacosProperties#ENCODE encode}, the value is "${nacos.encode:UTF-8}". */ String ENCODE_PLACEHOLDER = "${" + PREFIX + ENCODE + ":UTF-8}"; /** * The placeholder of {@link NacosProperties#CONFIG_LONG_POLL_TIMEOUT configLongPollTimeout}, the value is * "${nacos.configLongPollTimeout:}". */ String CONFIG_LONG_POLL_TIMEOUT_PLACEHOLDER = "${" + PREFIX + CONFIG_LONG_POLL_TIMEOUT + ":}"; /** * The placeholder of {@link NacosProperties#CONFIG_RETRY_TIME configRetryTime}, the value is * "${nacos.configRetryTime:}". */ String CONFIG_RETRY_TIME_PLACEHOLDER = "${" + PREFIX + CONFIG_RETRY_TIME + ":}"; /** * The placeholder of {@link NacosProperties#MAX_RETRY maxRetry}, the value is "${nacos.maxRetry:}". */ String MAX_RETRY_PLACEHOLDER = "${" + PREFIX + MAX_RETRY + ":}"; /** * The placeholder of {@link NacosProperties#ENABLE_REMOTE_SYNC_CONFIG enableRemoteSyncConfig}, the value is * "${nacos.enableRemoteSyncConfig:}". */ String ENABLE_REMOTE_SYNC_CONFIG_PLACEHOLDER = "${" + PREFIX + ENABLE_REMOTE_SYNC_CONFIG + ":}"; /** * The placeholder of endpoint, the value is "${nacos.username:}". */ String USERNAME_PLACEHOLDER = "${" + PREFIX + USERNAME + ":}"; /** * The placeholder of endpoint, the value is "${nacos.password:}". */ String PASSWORD_PLACEHOLDER = "${" + PREFIX + PASSWORD + ":}"; /** * The property of "endpoint". * * @return empty as default value * @see #ENDPOINT_PLACEHOLDER */ String endpoint() default ENDPOINT_PLACEHOLDER; /** * The property of "namespace". * * @return empty as default value * @see #NAMESPACE_PLACEHOLDER */ String namespace() default NAMESPACE_PLACEHOLDER; /** * The property of "access-key". * * @return empty as default value * @see #ACCESS_KEY_PLACEHOLDER */ String accessKey() default ACCESS_KEY_PLACEHOLDER; /** * The property of "secret-key". * * @return empty as default value * @see #SECRET_KEY_PLACEHOLDER */ String secretKey() default SECRET_KEY_PLACEHOLDER; /** * The property of "server-addr". * * @return empty as default value * @see #SERVER_ADDR_PLACEHOLDER */ String serverAddr() default SERVER_ADDR_PLACEHOLDER; /** * The property of "context-path". * * @return empty as default value * @see #CONTEXT_PATH_PLACEHOLDER */ String contextPath() default CONTEXT_PATH_PLACEHOLDER; /** * The property of "cluster-name". * * @return empty as default value * @see #CLUSTER_NAME_PLACEHOLDER */ String clusterName() default CLUSTER_NAME_PLACEHOLDER; /** * The property of "encode". * * @return "UTF-8" as default value * @see #ENCODE_PLACEHOLDER */ String encode() default ENCODE_PLACEHOLDER; /** * The property of "configLongPollTimeout". * * @return empty as default value * @see #CONFIG_LONG_POLL_TIMEOUT_PLACEHOLDER */ String configLongPollTimeout() default CONFIG_LONG_POLL_TIMEOUT_PLACEHOLDER; /** * The property of "configRetryTime". * * @return empty as default value * @see #CONFIG_RETRY_TIME_PLACEHOLDER */ String configRetryTime() default CONFIG_RETRY_TIME_PLACEHOLDER; /** * The property of "maxRetry". * * @return empty as default value * @see #MAX_RETRY */ String maxRetry() default MAX_RETRY_PLACEHOLDER; /** * The property of "enableRemoteSyncConfig". * * @return empty as default value * @see #ENABLE_REMOTE_SYNC_CONFIG */ String enableRemoteSyncConfig() default ENABLE_REMOTE_SYNC_CONFIG_PLACEHOLDER; /** * The property of "username". * * @return empty as default value * @see #USERNAME_PLACEHOLDER */ String username() default USERNAME_PLACEHOLDER; /** * The property of "password". * * @return empty as default value * @see #PASSWORD_PLACEHOLDER */ String password() default PASSWORD_PLACEHOLDER; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Entity.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; import java.util.Map; /** * CMDB entity. * * @author nkorange * @since 0.7.0 */ public class Entity { private String type; private String name; private Map labels; public String getType() { return type; } public void setType(String type) { this.type = type; } 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; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; /** * CMDB entity event. * * @author nkorange * @since 0.7.0 */ public class EntityEvent { private EntityEventType type; private String entityName; private String entityType; public EntityEventType getType() { return type; } public void setType(EntityEventType type) { this.type = type; } public String getEntityName() { return entityName; } public void setEntityName(String entityName) { this.entityName = entityName; } public String getEntityType() { return entityType; } public void setEntityType(String entityType) { this.entityType = entityType; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/EntityEventType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; /** * CMDB entity event type. * * @author nkorange * @since 0.7.0 */ public enum EntityEventType { /** * Add or update entity. */ ENTITY_ADD_OR_UPDATE, /** * Remove entity. */ ENTITY_REMOVE } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/Label.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; import java.util.Set; /** * CMDB label. * * @author nkorange * @since 0.7.0 */ public class Label { private String name; private Set values; private String description; public String getName() { return name; } public void setName(String name) { this.name = name; } public Set getValues() { return values; } public void setValues(Set values) { this.values = values; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/cmdb/pojo/PreservedEntityTypes.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; /** * CMDB preserved entity type. * * @author nkorange * @since 0.7.0 */ public enum PreservedEntityTypes { /** * Ip. */ ip, /** * Service. */ service } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/cmdb/spi/CmdbService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.spi; import com.alibaba.nacos.api.cmdb.pojo.Entity; import com.alibaba.nacos.api.cmdb.pojo.EntityEvent; import com.alibaba.nacos.api.cmdb.pojo.Label; import java.util.List; import java.util.Map; import java.util.Set; /** * Service to visit CMDB store. * * @author nkorange * @since 0.7.0 */ public interface CmdbService { /** * Get all label names stored in CMDB. * * @return label name set */ Set getLabelNames(); /** * Get all possible entity types in CMDB. * * @return all entity types */ Set getEntityTypes(); /** * Get label info. * * @param labelName label name * @return label info */ Label getLabel(String labelName); /** * Get label value of label name of ip. * * @param entityName entity name * @param entityType entity type * @param labelName target label name * @return label value */ String getLabelValue(String entityName, String entityType, String labelName); /** * Get all label value of ip. * * @param entityName entity name * @param entityType entity type * @return all label values */ Map getLabelValues(String entityName, String entityType); /** * Dump all entities in CMDB. * * @return all entities */ Map> getAllEntities(); /** * get label change events. * * @param timestamp start time of generated events * @return label events */ List getEntityEvents(long timestamp); /** * Get single entity. * * @param entityName name of entity * @param entityType type of entity * @return entity. */ Entity getEntity(String entityName, String entityType); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/common/Constants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.common; import java.util.concurrent.TimeUnit; /** * Constants. * * @author Nacos */ public class Constants { public static final String CLIENT_VERSION = "3.0.0"; public static final int DATA_IN_BODY_VERSION = 204; public static final String DEFAULT_GROUP = "DEFAULT_GROUP"; public static final String APPNAME = "AppName"; public static final String CLIENT_VERSION_KEY = "ClientVersion"; public static final String CLIENT_IP = "ClientIp"; public static final String UNKNOWN_APP = "UnknownApp"; public static final String DEFAULT_DOMAINNAME = "commonconfig.config-host.taobao.com"; public static final String DAILY_DOMAINNAME = "commonconfig.taobao.net"; public static final String NULL = ""; public static final String DATA_ID = "dataId"; public static final String TENANT = "tenant"; public static final String GROUP = "group"; public static final String GROUP_NAME = "groupName"; public static final String NAMESPACE_ID = "namespaceId"; public static final String TARGET_NAMESPACE_ID = "targetNamespaceId"; public static final String LAST_MODIFIED = "Last-Modified"; public static final String ACCEPT_ENCODING = "Accept-Encoding"; public static final String CONTENT_ENCODING = "Content-Encoding"; public static final String PROBE_MODIFY_REQUEST = "Listening-Configs"; public static final String PROBE_MODIFY_RESPONSE = "Probe-Modify-Response"; public static final String PROBE_MODIFY_RESPONSE_NEW = "Probe-Modify-Response-New"; public static final String USE_ZIP = "true"; public static final String CONTENT_MD5 = "Content-MD5"; public static final String CONFIG_VERSION = "Config-Version"; public static final String CONFIG_TYPE = "Config-Type"; public static final String ENCRYPTED_DATA_KEY = "Encrypted-Data-Key"; public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; public static final String SPACING_INTERVAL = "client-spacing-interval"; public static final String BASE_PATH = "/v1/cs"; public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs"; public static final String TOKEN = "token"; public static final String ACCESS_TOKEN = "accessToken"; public static final String TOKEN_TTL = "tokenTtl"; public static final String GLOBAL_ADMIN = "globalAdmin"; public static final String USERNAME = "username"; public static final String TOKEN_REFRESH_WINDOW = "tokenRefreshWindow"; public static final Integer SDK_GRPC_PORT_DEFAULT_OFFSET = 1000; public static final Integer CLUSTER_GRPC_PORT_DEFAULT_OFFSET = 1001; /** * second. */ public static final int ASYNC_UPDATE_ADDRESS_INTERVAL = 300; /** * second. */ public static final int POLLING_INTERVAL_TIME = 15; /** * millisecond. */ public static final int ONCE_TIMEOUT = 2000; /** * millisecond. */ public static final int SO_TIMEOUT = 60000; /** * millisecond. */ public static final int CONFIG_LONG_POLL_TIMEOUT = 30000; /** * millisecond. */ public static final int MIN_CONFIG_LONG_POLL_TIMEOUT = 10000; /** * millisecond. */ public static final int CONFIG_RETRY_TIME = 2000; /** * Maximum number of retries. */ public static final int MAX_RETRY = 3; /** * millisecond. */ public static final int RECV_WAIT_TIMEOUT = ONCE_TIMEOUT * 5; public static final String ENCODE = "UTF-8"; public static final String MAP_FILE = "map-file.js"; public static final int FLOW_CONTROL_THRESHOLD = 20; public static final int FLOW_CONTROL_SLOT = 10; public static final int FLOW_CONTROL_INTERVAL = 1000; public static final float DEFAULT_PROTECT_THRESHOLD = 0.0F; public static final String LINE_SEPARATOR = Character.toString((char) 1); public static final String WORD_SEPARATOR = Character.toString((char) 2); public static final String LONGPOLLING_LINE_SEPARATOR = "\r\n"; public static final String CLIENT_APPNAME_HEADER = "Client-AppName"; public static final String CLIENT_REQUEST_TS_HEADER = "Client-RequestTS"; public static final String CLIENT_REQUEST_TOKEN_HEADER = "Client-RequestToken"; public static final int ATOMIC_MAX_SIZE = 1000; public static final String NAMING_INSTANCE_ID_SPLITTER = "#"; public static final int NAMING_INSTANCE_ID_SEG_COUNT = 4; public static final String NAMING_HTTP_HEADER_SPLITTER = "\\|"; public static final String DEFAULT_CLUSTER_NAME = "DEFAULT"; public static final long DEFAULT_HEART_BEAT_TIMEOUT = TimeUnit.SECONDS.toMillis(15); public static final long DEFAULT_IP_DELETE_TIMEOUT = TimeUnit.SECONDS.toMillis(30); public static final long DEFAULT_HEART_BEAT_INTERVAL = TimeUnit.SECONDS.toMillis(5); public static final String DEFAULT_NAMESPACE_ID = "public"; public static final boolean DEFAULT_USE_CLOUD_NAMESPACE_PARSING = true; public static final int WRITE_REDIRECT_CODE = 307; public static final String SERVICE_INFO_SPLITER = "@@"; public static final int SERVICE_INFO_SPLIT_COUNT = 2; public static final String NULL_STRING = "null"; public static final String NUMBER_PATTERN_STRING = "^\\d+$"; public static final String ANY_PATTERN = ".*"; public static final String DEFAULT_INSTANCE_ID_GENERATOR = "simple"; public static final String SNOWFLAKE_INSTANCE_ID_GENERATOR = "snowflake"; public static final String HTTP_PREFIX = "http"; public static final String ALL_PATTERN = "*"; public static final String COLON = ":"; public static final String LINE_BREAK = "\n"; public static final String POUND = "#"; public static final String VIPSERVER_TAG = "Vipserver-Tag"; public static final String AMORY_TAG = "Amory-Tag"; public static final String LOCATION_TAG = "Location-Tag"; public static final String CHARSET_KEY = "charset"; public static final String CLUSTER_NAME_PATTERN_STRING = "^[0-9a-zA-Z-]+$"; /** * millisecond. */ public static final long DEFAULT_REDO_DELAY_TIME = 3000L; public static final int DEFAULT_REDO_THREAD_COUNT = 1; public static final String APP_CONN_LABELS_KEY = "nacos.app.conn.labels"; public static final String DOT = "."; public static final String WEIGHT = "weight"; public static final String PROPERTIES_KEY = "properties"; public static final String JVM_KEY = "jvm"; public static final String ENV_KEY = "env"; public static final String APP_CONN_LABELS_PREFERRED = "nacos_app_conn_labels_preferred"; public static final String APP_CONN_PREFIX = "app_"; public static final String CONFIG_GRAY_LABEL = "nacos.config.gray.label"; /** * Since 2.3.3, For some situation like java agent using nacos-client which can't use env ram info. */ public static final String DEFAULT_USE_RAM_INFO_PARSING = "true"; public static final String CLIENT_MODULE_TYPE = "clientModuleType"; /** * The constants in config directory. */ public static class Config { public static final String CONFIG_MODULE = "config"; public static final String NOTIFY_HEADER = "notify"; } /** * The constants in naming directory. */ public static class Naming { public static final String NAMING_MODULE = "naming"; public static final String CMDB_CONTEXT_TYPE = "CMDB"; } public static final String FUZZY_WATCH_PATTERN_SPLITTER = ">>"; /** * fuzzy watch sync type of watch init notify. */ public static final String FUZZY_WATCH_INIT_NOTIFY = "FUZZY_WATCH_INIT_NOTIFY"; /** * fuzzy watch sync type of watch init notify finish. */ public static final String FINISH_FUZZY_WATCH_INIT_NOTIFY = "FINISH_FUZZY_WATCH_INIT_NOTIFY"; /** * fuzzy watch sync type of watch diff sync notify. */ public static final String FUZZY_WATCH_DIFF_SYNC_NOTIFY = "FUZZY_WATCH_DIFF_SYNC_NOTIFY"; /** * fuzzy watch sync type of watch resource changed. */ public static final String FUZZY_WATCH_RESOURCE_CHANGED = "FUZZY_WATCH_RESOURCE_CHANGED"; /** * watch type of watch. */ public static final String WATCH_TYPE_WATCH = "WATCH"; /** * watch type of cancel watch. */ public static final String WATCH_TYPE_CANCEL_WATCH = "CANCEL_WATCH"; /** * The constants in config fuzzy watch changed type directory. */ public static class ConfigChangedType { public static final String ADD_CONFIG = "ADD_CONFIG"; public static final String DELETE_CONFIG = "DELETE_CONFIG"; public static final String CONFIG_CHANGED = "CONFIG_CHANGED"; } /** * The constants in naming fuzzy watch changed type directory. */ public static class ServiceChangedType { public static final String ADD_SERVICE = "ADD_SERVICE"; public static final String DELETE_SERVICE = "DELETE_SERVICE"; public static final String INSTANCE_CHANGED = "INSTANCE_CHANGED"; public static final String HEART_BEAT = "HEART_BEAT"; } /** * The constants in lock directory. */ public static class Lock { public static final String LOCK_MODULE = "lock"; } /** * The constants in remote directory. */ public static class Remote { public static final String INTERNAL_MODULE = "internal"; } /** * The constants in exception directory. */ public static class Exception { public static final int SERIALIZE_ERROR_CODE = 100; public static final int DESERIALIZE_ERROR_CODE = 101; public static final int FIND_DATASOURCE_ERROR_CODE = 102; public static final int FIND_TABLE_ERROR_CODE = 103; } /** * The constants in AI directory. */ public static class AI { public static final String AI_MODULE = "ai"; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/common/NodeState.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.common; /** * The life cycle state of a node plays an important role. * *

1.3.0 The unified sinking operation should be done first, and the node state * should be radiated out later, mainly for whether the request can be processed and so on

* * @author liaochuntao */ public enum NodeState { /** * Node is starting. */ STARTING, /** * Node is up and ready for request. */ UP, /** * Node may Crash. */ SUSPICIOUS, /** * Node is out of service, something abnormal happened. */ DOWN, /** * The Node is isolated. */ ISOLATION, } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/common/ResponseCode.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.common; /** * Response code definitions. * *

This class and inherited classes define codes separated from HTTP code to provide richer and preciser information of * the API results. A recommended rule for defining response code is: *

  • Global and common code starts with 10001. *
  • Naming module code starts with 20001. *
  • Config module code starts with 30001. *
  • Core module code starts with 40001. * * @author nkorange * @since 1.2.0 */ public class ResponseCode { /** * Everything normal. */ public static final int OK = 10200; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ConfigChangeEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import java.util.Collection; import java.util.Map; /** * ConfigChangeEvent. * * @author rushsky518 */ public class ConfigChangeEvent { private final Map data; public ConfigChangeEvent(Map data) { this.data = data; } public ConfigChangeItem getChangeItem(String key) { return data.get(key); } public Collection getChangeItems() { return data.values(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ConfigChangeItem.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; /** * ConfigChangeItem. * * @author rushsky518 */ public class ConfigChangeItem { private String key; private String oldValue; private String newValue; private PropertyChangeType type; public ConfigChangeItem(String key, String oldValue, String newValue) { this.key = key; this.oldValue = oldValue; this.newValue = newValue; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public String getOldValue() { return oldValue; } public void setOldValue(String oldValue) { this.oldValue = oldValue; } public String getNewValue() { return newValue; } public void setNewValue(String newValue) { this.newValue = newValue; } public PropertyChangeType getType() { return type; } public void setType(PropertyChangeType type) { this.type = type; } @Override public String toString() { return "ConfigChangeItem{" + "key='" + key + '\'' + ", oldValue='" + oldValue + '\'' + ", newValue='" + newValue + '\'' + ", type=" + type + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ConfigFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import java.lang.reflect.Constructor; import java.util.Properties; /** * Config Factory. * * @author Nacos */ public class ConfigFactory { /** * Create Config. * * @param properties init param * @return ConfigService * @throws NacosException Exception */ public static ConfigService createConfigService(Properties properties) throws NacosException { try { Class driverImplClass = Class.forName("com.alibaba.nacos.client.config.NacosConfigService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); ConfigService vendorImpl = (ConfigService) constructor.newInstance(properties); return vendorImpl; } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } } /** * Create Config. * * @param serverAddr serverList * @return Config * @throws NacosException create configService failed Exception */ public static ConfigService createConfigService(String serverAddr) throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr); return createConfigService(properties); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ConfigQueryResult.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import java.io.Serializable; /** * Config query result containing content and metadata. * *

    This class provides access to configuration content along with * its metadata like MD5 hash for CAS operations.

    * * @author nacos * @since 3.0 */ public class ConfigQueryResult implements Serializable { private static final long serialVersionUID = 1L; /** * Configuration content. */ private String content; /** * MD5 hash of the content. */ private String md5; /** * Configuration type (json, yaml, properties, etc.). */ private String configType; /** * Encrypted data key (if encryption is enabled). */ private String encryptedDataKey; public ConfigQueryResult() { } public ConfigQueryResult(String content, String md5) { this.content = content; this.md5 = md5; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public String getConfigType() { return configType; } public void setConfigType(String configType) { this.configType = configType; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } @Override public String toString() { return "ConfigQueryResult{" + "content='" + (content != null ? content.substring(0, Math.min(50, content.length())) + "..." : "null") + '\'' + ", md5='" + md5 + '\'' + ", configType='" + configType + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ConfigService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import java.util.Set; import java.util.concurrent.Future; /** * Config Service Interface. * * @author Nacos */ public interface ConfigService { /** * Get config. * * @param dataId dataId * @param group group * @param timeoutMs read timeout * @return config value * @throws NacosException NacosException */ String getConfig(String dataId, String group, long timeoutMs) throws NacosException; /** * Get config with full result including MD5. * *

    This method returns a {@link ConfigQueryResult} containing both * the configuration content and its MD5 hash, which can be used for * CAS (Compare-And-Swap) operations.

    * * @param dataId dataId * @param group group * @param timeoutMs read timeout * @return config query result containing content and md5 * @throws NacosException NacosException * @since 3.0 */ default ConfigQueryResult getConfigWithResult(String dataId, String group, long timeoutMs) throws NacosException { // Default implementation returns content only, without MD5 String content = getConfig(dataId, group, timeoutMs); return new ConfigQueryResult(content, null); } /** * Get config and register Listener. * *

    If you want to pull it yourself when the program starts to get the configuration for the first time, and the * registered Listener is used for future configuration updates, you can keep the original code unchanged, just add * the system parameter: enableRemoteSyncConfig = "true" ( But there is network overhead); therefore we recommend * that you use this interface directly * * @param dataId dataId * @param group group * @param timeoutMs read timeout * @param listener {@link Listener} * @return config value * @throws NacosException NacosException */ String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException; /** * Add a listener to the configuration, after the server modified the configuration, the client will use the * incoming listener callback. Recommended asynchronous processing, the application can implement the getExecutor * method in the ManagerListener, provide a thread pool of execution. If not provided, use the main thread callback, May * block other configurations or be blocked by other configurations. * * @param dataId dataId * @param group group * @param listener listener * @throws NacosException NacosException */ void addListener(String dataId, String group, Listener listener) throws NacosException; /** * Publish config. * * @param dataId dataId * @param group group * @param content content * @return Whether publish * @throws NacosException NacosException */ boolean publishConfig(String dataId, String group, String content) throws NacosException; /** * Publish config. * * @param dataId dataId * @param group group * @param content content * @param type config type {@link ConfigType} * @return Whether publish * @throws NacosException NacosException */ boolean publishConfig(String dataId, String group, String content, String type) throws NacosException; /** * Cas Publish config. * * @param dataId dataId * @param group group * @param content content * @param casMd5 casMd5 prev content's md5 to cas. * @return Whether publish * @throws NacosException NacosException */ boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException; /** * Cas Publish config. * * @param dataId dataId * @param group group * @param content content * @param casMd5 casMd5 prev content's md5 to cas. * @param type config type {@link ConfigType} * @return Whether publish * @throws NacosException NacosException */ boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) throws NacosException; /** * Remove config. * * @param dataId dataId * @param group group * @return whether remove * @throws NacosException NacosException */ boolean removeConfig(String dataId, String group) throws NacosException; /** * Remove listener. * * @param dataId dataId * @param group group * @param listener listener */ void removeListener(String dataId, String group, Listener listener); /** * Get server status. * * @return whether health */ String getServerStatus(); /** * add config filter. * It is recommended to use {@link com.alibaba.nacos.api.config.filter.AbstractConfigFilter} to expand the filter. * * @param configFilter filter * @since 2.3.0 */ void addConfigFilter(IConfigFilter configFilter); /** * Shutdown the resource service. * * @throws NacosException exception. */ void shutDown() throws NacosException; /** * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified * fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy listeners allow for * pattern-based subscription to configurations, where the fixed group name represents the group and dataId patterns * specified for subscription. * * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @throws NacosException NacosException * @since 3.0 */ void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration. After the server modifies the configuration matching the specified * dataId pattern and fixed group name, the client will utilize the incoming fuzzy listener callback. Fuzzy * listeners allow for pattern-based subscription to configurations. * * @param dataIdPattern The pattern to match dataIds for subscription. * @param groupNamePattern The pattern to match group name representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy listener to be added. * @throws NacosException NacosException * @since 3.0 */ void fuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration and retrieve all configs that match the specified fixed group name. * Fuzzy listeners allow for pattern-based subscription to configs, where the fixed group name represents the group * and dataId patterns specified for subscription. * * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified fixed group name. * @throws NacosException NacosException * @since 3.0 */ Future> fuzzyWatchWithGroupKeys(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Add a fuzzy listener to the configuration and retrieve all configs that match the specified dataId pattern and * fixed group name. Fuzzy listeners allow for pattern-based subscription to configs. * * @param dataIdPattern The pattern to match dataIds for subscription. * @param groupNamePattern The group name pattern representing the group and dataId patterns to subscribe to. * @param watcher The fuzzy watcher to be added. * @return CompletableFuture containing collection of configs that match the specified dataId pattern and fixed * group name. * @throws NacosException NacosException * @since 3.0 */ Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified fixed group name. * * @param groupNamePattern The group name pattern for fuzzy watch. * @param watcher The event watcher to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; /** * Cancel fuzzy listen and remove the event listener for a specified service name pattern and fixed group name. * * @param dataIdPattern The pattern to match dataId for fuzzy watch. * @param groupNamePattern The group name pattern for fuzzy watch. * @param watcher The event listener to be removed. * @throws NacosException If an error occurs during the cancellation process. * @since 3.0 */ void cancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ConfigType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import com.alibaba.nacos.api.utils.StringUtils; import java.util.HashMap; import java.util.Map; /** * Config data type. * * @author liaochuntao **/ public enum ConfigType { /** * config type is "properties". */ PROPERTIES("properties"), /** * config type is "xml". */ XML("xml"), /** * config type is "json". */ JSON("json"), /** * config type is "text". */ TEXT("text"), /** * config type is "html". */ HTML("html"), /** * config type is "yaml". */ YAML("yaml"), /** * config type is "toml". */ TOML("toml"), /** * not a real type. */ UNSET("unset"); private final String type; private static final Map LOCAL_MAP = new HashMap<>(); static { for (ConfigType configType : values()) { LOCAL_MAP.put(configType.getType(), configType); } } ConfigType(String type) { this.type = type; } public String getType() { return type; } public static ConfigType getDefaultType() { return TEXT; } /** * check input type is valid. * * @param type config type * @return it the type valid */ public static Boolean isValidType(String type) { if (StringUtils.isBlank(type)) { return false; } return null != LOCAL_MAP.get(type); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/PropertyChangeType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; /** * Property Change Type. * * @author rushsky518 */ public enum PropertyChangeType { /** * add. */ ADDED, /** * modified. */ MODIFIED, /** * deleted. */ DELETED } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ability/ClientConfigAbility.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.ability; import java.io.Serializable; /** * config abilities of nacos client. * * @author liuzunfei * @version $Id: ClientConfigAbility.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ public class ClientConfigAbility implements Serializable { private static final long serialVersionUID = 2442741206510725737L; /** * support remote metrics get. */ private boolean supportRemoteMetrics; public boolean isSupportRemoteMetrics() { return supportRemoteMetrics; } public void setSupportRemoteMetrics(boolean supportRemoteMetrics) { this.supportRemoteMetrics = supportRemoteMetrics; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/ability/ServerConfigAbility.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.ability; import java.io.Serializable; import java.util.Objects; /** * config abilities of nacos server. * * @author liuzunfei * @version $Id: ServerConfigAbility.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ public class ServerConfigAbility implements Serializable { private static final long serialVersionUID = -4976152499731684230L; /** * support remote metrics get. */ private boolean supportRemoteMetrics; public boolean isSupportRemoteMetrics() { return supportRemoteMetrics; } public void setSupportRemoteMetrics(boolean supportRemoteMetrics) { this.supportRemoteMetrics = supportRemoteMetrics; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServerConfigAbility that = (ServerConfigAbility) o; return supportRemoteMetrics == that.supportRemoteMetrics; } @Override public int hashCode() { return Objects.hash(supportRemoteMetrics); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.annotation; import com.alibaba.nacos.api.annotation.NacosProperties; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.convert.NacosConfigConverter; 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 static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; /** * Annotation that marks a method as a listener for Nacos Config change. * * @author Mercy * @since 0.2.0 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface NacosConfigListener { /** * Nacos Group ID. * * @return default value {@link Constants#DEFAULT_GROUP}; */ String groupId() default DEFAULT_GROUP; /** * Nacos Data ID. * * @return required value. */ String dataId(); /** * Nacos Config type. * * @return default value is {@link ConfigType#UNSET} */ ConfigType type() default ConfigType.UNSET; /** * Specify {@link NacosConfigConverter Nacos configuraion convertor} class to convert target type instance. * * @return The implementation class of {@link NacosConfigConverter} */ Class converter() default NacosConfigConverter.class; /** * The {@link NacosProperties} attribute, If not specified, it will use global Nacos Properties. * * @return the default value is {@link NacosProperties} */ NacosProperties properties() default @NacosProperties; /** * Maximum timeout value of execution in milliseconds, which is used to prevent long-time blocking execution * impacting others. * * @return default value is 1000 */ long timeout() default 1000L; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosConfigurationProperties.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.annotation; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.annotation.NacosProperties; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; 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 static com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP; /** * An annotation for Nacos configuration Properties for binding POJO as Properties Object. * * @author Mercy * @see PropertyKeyConst * @since 0.2.0 */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NacosConfigurationProperties { /** * config prefix name. * * @return default value is "" */ String prefix() default ""; /** * Nacos Group ID. * * @return default value {@link Constants#DEFAULT_GROUP}; */ String groupId() default DEFAULT_GROUP; /** * Nacos Data ID. * * @return required value. */ String dataId(); /** * config style. * * @return default value is {@link ConfigType#UNSET} */ ConfigType type() default ConfigType.UNSET; /** * It indicates the properties of current doBind bean is auto-refreshed when Nacos configuration is changed. * * @return default value is false */ boolean autoRefreshed() default false; /** * Flag to indicate that when binding to this object invalid fields should be ignored. Invalid means invalid * according to the binder that is used, and usually this means fields of the wrong type (or that cannot be coerced * into the correct type). * * @return the flag value (default false) */ boolean ignoreInvalidFields() default false; /** * Flag to indicate that when binding to this object fields with periods in their names should be ignored. * * @return the flag value (default false) */ boolean ignoreNestedProperties() default false; /** * Flag to indicate that when binding to this object unknown fields should be ignored. An unknown field could be a * sign of a mistake in the Properties. * * @return the flag value (default true) */ boolean ignoreUnknownFields() default true; /** * Flag to indicate that an exception should be raised if a Validator is available and validation fails. If it is * set to false, validation errors will be swallowed. They will be logged, but not propagated to the caller. * * @return the flag value (default true) */ boolean exceptionIfInvalid() default true; /** * The {@link NacosProperties} attribute, If not specified, it will use global Nacos Properties. * * @return the default value is {@link NacosProperties} */ NacosProperties properties() default @NacosProperties; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosIgnore.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.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; /** * An annotation for ignore field from annotated {@link NacosConfigurationProperties} Properties Object. * * @author Mercy * @see NacosConfigurationProperties * @see NacosProperty * @since 0.2.0 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NacosIgnore { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosProperty.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.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; /** * An annotation for Nacos Property name of Nacos Configuration to bind a field from annotated {@link * NacosConfigurationProperties} Properties Object. * * @author Mercy * @see NacosConfigurationProperties * @see NacosIgnore * @since 0.2.0 */ @Target({ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NacosProperty { /** * The property name of Nacos Configuration to bind a field. * * @return property name */ String value(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/annotation/NacosValue.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.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; /** * Annotation which extends value to support auto-refresh. * * @author hxy1991 * @since 0.2.0 */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface NacosValue { /** * The actual value expression: e.g. "#{systemProperties.myProp}". * * @return value expression */ String value(); /** * It indicates that the currently bound property is auto-refreshed when Nacos configuration is changed. * * @return default value is false */ boolean autoRefreshed() default false; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/convert/NacosConfigConverter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.convert; /** * Nacos Config Converter. * * @param the target type that wanted * @author Mercy * @since 0.2.0 */ public interface NacosConfigConverter { /** * can convert to be target type or not. * * @param targetType the type of target * @return If can , return true, or false */ boolean canConvert(Class targetType); /** * Convert the Nacos' config of type S to target type T. * * @param config the Nacos's config to convert, which must be an instance of S (never {@code null}) * @return the converted object, which must be an instance of T (potentially {@code null}) */ T convert(String config); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/filter/AbstractConfigFilter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.filter; /** * Config Filter Interface default implementation. * * @author luyanbo(RobberPhex) */ public abstract class AbstractConfigFilter implements IConfigFilter { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigContext.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.filter; /** * Config Context Interface. * * @author Nacos */ public interface IConfigContext { /** * Get context param by key. * * @param key parameter key * @return context */ Object getParameter(String key); /** * Set context param. * * @param key key * @param value value */ void setParameter(String key, Object value); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.filter; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; /** * Config Filter Interface. * *

    DO NOT implement this interface directly, you should extend AbstractConfigFilter. * * @author Nacos * @see AbstractConfigFilter */ public interface IConfigFilter { /** * Init. * * @param properties Filter Config */ void init(Properties properties); /** * do filter. * * @param request request * @param response response * @param filterChain filter Chain * @throws NacosException exception */ void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain) throws NacosException; /** * Get order. * * @return order number */ int getOrder(); /** * Get filterName. * * @return filter name */ String getFilterName(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigFilterChain.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.filter; import com.alibaba.nacos.api.exception.NacosException; /** * Config Filter Chain Interface. * * @author Nacos */ public interface IConfigFilterChain { /** * Filter action. * * @param request request * @param response response * @throws NacosException NacosException */ void doFilter(IConfigRequest request, IConfigResponse response) throws NacosException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.filter; /** * Config Request Interface. * * @author Nacos */ public interface IConfigRequest { /** * put param. * * @param key key * @param value value */ void putParameter(String key, Object value); /** * get param. * * @param key key * @return value */ Object getParameter(String key); /** * get config context. * * @return {@link IConfigContext} */ IConfigContext getConfigContext(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/filter/IConfigResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.filter; /** * Config Response Interface. * * @author Nacos */ public interface IConfigResponse { /** * get param. * * @param key key * @return value */ Object getParameter(String key); /** * put param. * * @param key key * @param value value */ void putParameter(String key, Object value); /** * Get config context. * * @return configContext */ IConfigContext getConfigContext(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcher.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import java.util.concurrent.Executor; /** * AbstractFuzzyListenListener is an abstract class that provides basic functionality for listening to fuzzy * configuration changes in Nacos. * * @author stone-98 * @date 2024/3/4 */ public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher, FuzzyWatchLoadWatcher { /** * Get executor for execute this receive. * * @return Executor */ public Executor getExecutor() { return null; } @Override public void onPatternOverLimit() { // do nothing default } @Override public void onConfigReachUpLimit() { // do nothing default } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import java.util.concurrent.Executor; /** * Listener Adapter,use default notify thread. * * @author water.lyl */ public abstract class AbstractListener implements Listener { /** * Use default executor. */ @Override public Executor getExecutor() { return null; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/AbstractSharedListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import java.util.concurrent.Executor; /** * Shared Listener. * * @author Nacos */ public abstract class AbstractSharedListener implements Listener { private volatile String dataId; private volatile String group; public final void fillContext(String dataId, String group) { this.dataId = dataId; this.group = group; } @Override public final void receiveConfigInfo(String configInfo) { innerReceive(dataId, group, configInfo); } @Override public Executor getExecutor() { return null; } /** * receive. * * @param dataId data ID * @param group group * @param configInfo content */ public abstract void innerReceive(String dataId, String group, String configInfo); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigChangeParser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import com.alibaba.nacos.api.config.ConfigChangeItem; import java.io.IOException; import java.util.Map; /** * ConfigChangeParser. * * @author rushsky518 */ public interface ConfigChangeParser { /** * Judge type. * * @param type data type * @return true if is responsible type, otherwise false */ boolean isResponsibleFor(String type); /** * Compare old and new data. * * @param oldContent old content * @param newContent new content * @param type data type * @return key and change item map * @throws IOException io exception */ Map doParse(String oldContent, String newContent, String type) throws IOException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import com.alibaba.nacos.api.common.Constants; /** * Represents a fuzzy listening configuration change event. * *

    This event indicates that a change has occurred in a configuration that matches a fuzzy listening pattern. * * @author stone-98 * @date 2024/3/12 */ public class ConfigFuzzyWatchChangeEvent { /** * The group of the configuration that has changed. */ private String group; /** * The data ID of the configuration that has changed. */ private String dataId; /** * The namespace of the configuration that has changed. */ private String namespace; /** * The change type of local watcher , contains {"ADD_CONFIG", "DELETE_CONFIG"}. * {@link Constants.ConfigChangedType}. */ private String changedType; /** * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", * "FUZZY_WATCH_DIFF_SYNC_NOTIFY"}. */ private String syncType; /** * Constructs a FuzzyListenConfigChangeEvent with the specified parameters. * * @param group The group of the configuration that has changed * @param dataId The data ID of the configuration that has changed * @param changedType The type of change that has occurred */ private ConfigFuzzyWatchChangeEvent(String namespace, String group, String dataId, String changedType, String syncType) { this.group = group; this.dataId = dataId; this.namespace = namespace; this.changedType = changedType; this.syncType = syncType; } /** * Constructs and returns a new FuzzyListenConfigChangeEvent with the specified parameters. * * @param group The group of the configuration that has changed * @param dataId The data ID of the configuration that has changed * @param changedType The type of change that has occurred * @return A new FuzzyListenConfigChangeEvent instance */ public static ConfigFuzzyWatchChangeEvent build(String namespace, String group, String dataId, String changedType, String syncType) { return new ConfigFuzzyWatchChangeEvent(namespace, group, dataId, changedType, syncType); } public String getNamespace() { return namespace; } public String getGroup() { return group; } public String getDataId() { return dataId; } public String getChangedType() { return changedType; } public String getSyncType() { return syncType; } @Override public String toString() { return "ConfigFuzzyWatchChangeEvent{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + ", namespace='" + namespace + '\'' + ", changedType='" + changedType + '\'' + ", syncType='" + syncType + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchEventWatcher.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import java.util.concurrent.Executor; /** * fuzzy watch config changes. * * @author stone-98 * @date 2024/3/4 */ public interface FuzzyWatchEventWatcher { /** * Callback method invoked when a fuzzy configuration change event occurs. * * @param event The fuzzy configuration change event */ void onEvent(ConfigFuzzyWatchChangeEvent event); /** * Get executor for execute this receive. * * @return Executor */ Executor getExecutor(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/FuzzyWatchLoadWatcher.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; /** * config fuzzy watch watcher that triggered when loader over limit. * @author shiyiyue */ public interface FuzzyWatchLoadWatcher { /** * triggered when server pattern count over limit. */ void onPatternOverLimit(); /** * triggered when pattern match config count over limit. */ void onConfigReachUpLimit(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/listener/Listener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import java.util.concurrent.Executor; /** * Listener for watch config. * * @author Nacos */ public interface Listener { /** * Get executor for execute this receive. * * @return Executor */ Executor getExecutor(); /** * Receive config info. * * @param configInfo config info */ void receiveConfigInfo(final String configInfo); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/ConfigBasicInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import java.io.Serializable; /** * Nacos configuration basic information. * * @author xiweng.yy */ public class ConfigBasicInfo implements Serializable { private static final long serialVersionUID = 2662049844183052399L; /** * The actual storage identity of the configuration, which no actual meanings for usage. * *

    * Different storage datasource will have different id. Such as Relational Database the id is auto-generated table ids. *

    *

    * Why to string serialize? The ui(JavaScript) handle id will lose the accuracy when large long, If directly return long type, * such as 862926428394491904, ui will replace it as 862926428394491900, so that can't found the configuration in later operation. *

    */ @JsonSerialize(using = ToStringSerializer.class) private Long id; private String namespaceId; private String groupName; private String dataId; private String md5; private String type; private String appName; private long createTime; private long modifyTime; private String desc; private String configTags; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public long getCreateTime() { return createTime; } public void setCreateTime(long createTime) { this.createTime = createTime; } public long getModifyTime() { return modifyTime; } public void setModifyTime(long modifyTime) { this.modifyTime = modifyTime; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getConfigTags() { return configTags; } public void setConfigTags(String configTags) { this.configTags = configTags; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/ConfigCloneInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import java.io.Serializable; /** * Nacos configuration cloned information. * * @author xiweng.yy */ public class ConfigCloneInfo implements Serializable { private static final long serialVersionUID = -53761233218121703L; /** * The id of need to be cloned configuration, which is the actual storage id not data id. Get from {@link ConfigBasicInfo#getId()}. */ private Long configId; /** * The new group name of configuration after cloned. Optional, if not set, will use the original group name. */ private String targetGroupName; /** * The new data id of configuration after cloned. Optional, if not set, will use the original group name. */ private String targetDataId; public Long getConfigId() { return configId; } public void setConfigId(Long configId) { this.configId = configId; } public String getTargetGroupName() { return targetGroupName; } public void setTargetGroupName(String targetGroupName) { this.targetGroupName = targetGroupName; } public String getTargetDataId() { return targetDataId; } public void setTargetDataId(String targetDataId) { this.targetDataId = targetDataId; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/ConfigDetailInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; /** * Nacos configuration detail information. * * @author xiweng.yy */ public class ConfigDetailInfo extends ConfigBasicInfo { private static final long serialVersionUID = -6659977504609721215L; private String content; private String encryptedDataKey; private String createUser; private String createIp; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } public String getCreateUser() { return createUser; } public void setCreateUser(String createUser) { this.createUser = createUser; } public String getCreateIp() { return createIp; } public void setCreateIp(String createIp) { this.createIp = createIp; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/ConfigGrayInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; /** * Nacos configuration gray information. * * @author xiweng.yy */ public class ConfigGrayInfo extends ConfigDetailInfo { private static final long serialVersionUID = 4462719176825261439L; private String grayName; private String grayRule; public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getGrayRule() { return grayRule; } public void setGrayRule(String grayRule) { this.grayRule = grayRule; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/ConfigHistoryBasicInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; /** * Nacos configuration history basic information. * * @author xiweng.yy */ public class ConfigHistoryBasicInfo extends ConfigBasicInfo { private static final long serialVersionUID = -5429814695967367742L; private String srcIp; private String srcUser; /** * Operation type, include inserting, updating and deleting. */ private String opType; private String publishType; public String getSrcIp() { return srcIp; } public void setSrcIp(String srcIp) { this.srcIp = srcIp; } public String getSrcUser() { return srcUser; } public void setSrcUser(String srcUser) { this.srcUser = srcUser; } public String getOpType() { return opType; } public void setOpType(String opType) { this.opType = opType; } public String getPublishType() { return publishType; } public void setPublishType(String publishType) { this.publishType = publishType; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/ConfigHistoryDetailInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; /** * Nacos configuration history detail information. * * @author xiweng.yy */ public class ConfigHistoryDetailInfo extends ConfigHistoryBasicInfo { private static final long serialVersionUID = 5498431203024164923L; private String content; private String encryptedDataKey; private String grayName; private String extInfo; public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getExtInfo() { return extInfo; } public void setExtInfo(String extInfo) { this.extInfo = extInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/ConfigListenerInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import java.util.Map; /** * Nacos configuration listeners information. * * @author xiweng.yy */ public class ConfigListenerInfo { public static final String QUERY_TYPE_CONFIG = "config"; public static final String QUERY_TYPE_IP = "ip"; private String queryType; private Map listenersStatus; public String getQueryType() { return queryType; } public void setQueryType(String queryType) { this.queryType = queryType; } public Map getListenersStatus() { return listenersStatus; } public void setListenersStatus(Map listenersStatus) { this.listenersStatus = listenersStatus; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/model/SameConfigPolicy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; /** * SameConfigPolicy. * * @author klw */ public enum SameConfigPolicy { /** * Abort import on duplicate. */ ABORT, /** * Skipping on duplicate. */ SKIP, /** * Overwrite on duplicate. */ OVERWRITE } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractConfigRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.Request; /** * abstract request of config module request,all config module request should extends this class. * * @author liuzunfei * @version $Id: ConfigCommonRequest.java, v 0.1 2020年07月13日 9:05 PM liuzunfei Exp $ */ public abstract class AbstractConfigRequest extends Request { private String dataId; private String group; private String tenant; public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } @Override public String getModule() { return Constants.Config.CONFIG_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/AbstractFuzzyWatchNotifyRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.remote.request.ServerRequest; import static com.alibaba.nacos.api.common.Constants.Config.CONFIG_MODULE; /** * AbstractFuzzyListenNotifyRequest. * * @author stone-98 * @date 2024/3/14 */ public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { public AbstractFuzzyWatchNotifyRequest() { } @Override public String getModule() { return CONFIG_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ClientConfigMetricRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.ServerRequest; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Objects; /** * request of config module metrics. * * @author liuzunfei * @version $Id: ClientConfigMetricRequest.java, v 0.1 2020年12月30日 9:05 PM liuzunfei Exp $ */ public class ClientConfigMetricRequest extends ServerRequest { private List metricsKeys = new ArrayList<>(); @Override public String getModule() { return Constants.Config.CONFIG_MODULE; } public List getMetricsKeys() { return metricsKeys; } public void setMetricsKeys(List metricsKeys) { this.metricsKeys = metricsKeys; } public static class MetricsKey implements Serializable { private static final long serialVersionUID = -2731160029960311757L; String type; String key; public static final String CACHE_DATA = "cacheData"; public static final String SNAPSHOT_DATA = "snapshotData"; /** * build metrics key. * * @param type type. * @param key key. * @return metric key. */ public static MetricsKey build(String type, String key) { MetricsKey metricsKey = new MetricsKey(); metricsKey.type = type; metricsKey.key = key; return metricsKey; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } @Override public String toString() { return "MetricsKey{" + "type='" + type + '\'' + ", key='" + key + '\'' + '}'; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } MetricsKey that = (MetricsKey) o; return Objects.equals(type, that.type) && Objects.equals(key, that.key); } @Override public int hashCode() { return Objects.hash(type, key); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchListenRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import java.util.ArrayList; import java.util.List; /** * request of listening a batch of configs. * * @author liuzunfei * @version $Id: ConfigBatchListenRequest.java, v 0.1 2020年07月27日 7:46 PM liuzunfei Exp $ */ public class ConfigBatchListenRequest extends AbstractConfigRequest { /** * listen or remove listen. */ private boolean listen = true; private List configListenContexts = new ArrayList<>(); /** * add listen config. * * @param group group. * @param dataId dataId. * @param tenant tenant. * @param md5 md5. */ public void addConfigListenContext(String group, String dataId, String tenant, String md5) { ConfigListenContext configListenContext = new ConfigListenContext(); configListenContext.dataId = dataId; configListenContext.group = group; configListenContext.md5 = md5; configListenContext.tenant = tenant; configListenContexts.add(configListenContext); } /** * Getter method for property configListenContexts. * * @return property value of configListenContexts */ public List getConfigListenContexts() { return configListenContexts; } /** * Setter method for property configListenContexts. * * @param configListenContexts value to be assigned to property configListenContexts */ public void setConfigListenContexts(List configListenContexts) { this.configListenContexts = configListenContexts; } /** * Getter method for property listen. * * @return property value of listen */ public boolean isListen() { return listen; } /** * Setter method for property listen. * * @param listen value to be assigned to property listen */ public void setListen(boolean listen) { this.listen = listen; } public static class ConfigListenContext { String group; String md5; String dataId; String tenant; public ConfigListenContext() { } @Override public String toString() { return "ConfigListenContext{" + "group='" + group + '\'' + ", md5='" + md5 + '\'' + ", dataId='" + dataId + '\'' + ", tenant='" + tenant + '\'' + '}'; } /** * Getter method for property group. * * @return property value of group */ public String getGroup() { return group; } /** * Setter method for property groupId. * * @param group value to be assigned to property groupId */ public void setGroup(String group) { this.group = group; } /** * Getter method for property md5. * * @return property value of md5 */ public String getMd5() { return md5; } /** * Setter method for property md5. * * @param md5 value to be assigned to property md5 */ public void setMd5(String md5) { this.md5 = md5; } /** * Getter method for property dataId. * * @return property value of dataId */ public String getDataId() { return dataId; } /** * Setter method for property dataId. * * @param dataId value to be assigned to property dataId */ public void setDataId(String dataId) { this.dataId = dataId; } /** * Getter method for property tenant. * * @return property value of tenant */ public String getTenant() { return tenant; } /** * Setter method for property tenant. * * @param tenant value to be assigned to property tenant */ public void setTenant(String tenant) { this.tenant = tenant; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigChangeNotifyRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.ServerRequest; /** * ConfigChangeNotifyRequest. * * @author liuzunfei * @version $Id: ConfigChangeNotifyRequest.java, v 0.1 2020年07月14日 3:20 PM liuzunfei Exp $ */ public class ConfigChangeNotifyRequest extends ServerRequest { String dataId; String group; String tenant; public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } /** * build success response. * * @param dataId dataId * @param group group * @param tenant tenant * @return ConfigChangeNotifyResponse */ public static ConfigChangeNotifyRequest build(String dataId, String group, String tenant) { ConfigChangeNotifyRequest request = new ConfigChangeNotifyRequest(); request.setDataId(dataId); request.setGroup(group); request.setTenant(tenant); return request; } @Override public String getModule() { return Constants.Config.CONFIG_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; /** * Represents a request to notify changes when a fuzzy watched configuration changed. * *

    This request is used to notify clients about changes in configurations that match fuzzy listening patterns. * * @author stone-98 * @date 2024/3/13 */ public class ConfigFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { /** * The groupKey of the configuration that has changed. */ private String groupKey; /** * Indicates whether the configuration exists or not. */ private String changeType; /** * Constructs an empty FuzzyListenNotifyChangeRequest. */ public ConfigFuzzyWatchChangeNotifyRequest() { } /** * Constructs a FuzzyListenNotifyChangeRequest with the specified parameters. * * @param groupKey The group of the configuration that has changed * @param changeType Indicates whether the configuration exists or not */ public ConfigFuzzyWatchChangeNotifyRequest(String groupKey, String changeType) { this.groupKey = groupKey; this.changeType = changeType; } public String getGroupKey() { return groupKey; } public void setGroupKey(String groupKey) { this.groupKey = groupKey; } public String getChangeType() { return changeType; } public void setChangeType(String changeType) { this.changeType = changeType; } /** * Returns a string representation of the FuzzyListenNotifyChangeRequest. * * @return A string representation of the request */ @Override public String toString() { return "FuzzyListenNotifyChangeRequest{" + '\'' + ", groupKey='" + groupKey + '\'' + ", changeType=" + changeType + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.Request; import java.util.Set; /** * Represents a request for batch fuzzy listening configurations. * *

    This request is used to request batch fuzzy listening configurations from the server. It contains a set of * contexts, each representing a fuzzy listening context. * * @author stone-98 * @date 2024/3/4 */ public class ConfigFuzzyWatchRequest extends Request { /** * The namespace or tenant associated with the configurations. */ private String groupKeyPattern; private Set receivedGroupKeys; /** * Flag indicating whether to listen for changes. */ private String watchType; /** * Flag indicating whether the client is initializing. */ private boolean isInitializing; /** * Constructs an empty ConfigBatchFuzzyListenRequest. */ public ConfigFuzzyWatchRequest() { } public String getGroupKeyPattern() { return groupKeyPattern; } public void setGroupKeyPattern(String groupKeyPattern) { this.groupKeyPattern = groupKeyPattern; } public Set getReceivedGroupKeys() { return receivedGroupKeys; } public void setReceivedGroupKeys(Set receivedGroupKeys) { this.receivedGroupKeys = receivedGroupKeys; } public String getWatchType() { return watchType; } public void setWatchType(String watchType) { this.watchType = watchType; } public boolean isInitializing() { return isInitializing; } public void setInitializing(boolean initializing) { isInitializing = initializing; } /** * Get the module name for this request. * * @return The module name */ @Override public String getModule() { return Constants.Config.CONFIG_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import java.util.Set; /** * Represents a request to notify the difference between client and server side. * *

    This request is used to notify clients about the difference in configurations that match fuzzy listening * patterns. * * @author stone-98 * @date 2024/3/6 */ public class ConfigFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest { /** * The pattern used to match group keys for the configurations. */ private String groupKeyPattern; /** * The set of contexts containing information about the configurations. */ private Set contexts; /** * see FUZZY_WATCH_INIT_NOTIFY,FINISH_FUZZY_WATCH_INIT_NOTIFY,FUZZY_WATCH_DIFF_SYNC_NOTIFY. */ private String syncType; private int totalBatch; private int currentBatch; public String getSyncType() { return syncType; } public void setSyncType(String syncType) { this.syncType = syncType; } public int getTotalBatch() { return totalBatch; } public void setTotalBatch(int totalBatch) { this.totalBatch = totalBatch; } public int getCurrentBatch() { return currentBatch; } public void setCurrentBatch(int currentBatch) { this.currentBatch = currentBatch; } /** * Constructs an empty FuzzyListenNotifyDiffRequest. */ public ConfigFuzzyWatchSyncRequest() { } /** * Constructs a FuzzyListenNotifyDiffRequest with the specified parameters. * * @param groupKeyPattern The pattern used to match group keys for the configurations * @param contexts The set of contexts containing information about the configurations */ private ConfigFuzzyWatchSyncRequest(String syncType, String groupKeyPattern, Set contexts, int totalBatch, int currentBatch) { this.groupKeyPattern = groupKeyPattern; this.contexts = contexts; this.syncType = syncType; this.currentBatch = currentBatch; this.totalBatch = totalBatch; } /** * Builds an initial FuzzyListenNotifyDiffRequest with the specified set of contexts and group key pattern. * * @param contexts The set of contexts containing information about the configurations * @param groupKeyPattern The pattern used to match group keys for the configurations * @return An initial FuzzyListenNotifyDiffRequest */ public static ConfigFuzzyWatchSyncRequest buildSyncRequest(String syncType, Set contexts, String groupKeyPattern, int totalBatch, int currentBatch) { return new ConfigFuzzyWatchSyncRequest(syncType, groupKeyPattern, contexts, totalBatch, currentBatch); } /** * Builds fuzzy watch init finish request. * * @param groupKeyPattern The pattern used to match group keys for the configurations * @return A final FuzzyListenNotifyDiffRequest */ public static ConfigFuzzyWatchSyncRequest buildInitFinishRequest(String groupKeyPattern) { return new ConfigFuzzyWatchSyncRequest(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, groupKeyPattern, null, 0, 0); } public String getGroupKeyPattern() { return groupKeyPattern; } public void setGroupKeyPattern(String groupKeyPattern) { this.groupKeyPattern = groupKeyPattern; } public Set getContexts() { return contexts; } public void setContexts(Set contexts) { this.contexts = contexts; } /** * Represents context information about a configuration. */ public static class Context { String groupKey; /** * see {@link com.alibaba.nacos.api.common.Constants.ConfigChangedType ADD_CONFIG&} ADD_CONFIG: a new config * should be added for clientside . DELETE_CONFIG: a config should be removed for clientside . */ private String changedType; /** * Constructs an empty Context object. */ public Context() { } /** * Builds a new context object with the provided parameters. * * @param groupKey The groupKey associated of the configuration. * @param changedType The type of the configuration change event. * @return A new context object initialized with the provided parameters. */ public static Context build(String groupKey, String changedType) { Context context = new Context(); context.setGroupKey(groupKey); context.setChangedType(changedType); return context; } public String getGroupKey() { return groupKey; } public void setGroupKey(String groupKey) { this.groupKey = groupKey; } public String getChangedType() { return changedType; } public void setChangedType(String changedType) { this.changedType = changedType; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigPublishRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import java.util.HashMap; import java.util.Map; /** * request to publish a config. * * @author liuzunfei * @version $Id: ConfigPublishRequest.java, v 0.1 2020年07月16日 4:30 PM liuzunfei Exp $ */ public class ConfigPublishRequest extends AbstractConfigRequest { String content; String casMd5; private Map additionMap; public ConfigPublishRequest() { } public ConfigPublishRequest(String dataId, String group, String tenant, String content) { this.content = content; super.setGroup(group); super.setTenant(tenant); super.setDataId(dataId); } /** * get additional param. * * @param key key of param. * @return value of param ,return null if not exist. */ public String getAdditionParam(String key) { return additionMap == null ? null : additionMap.get(key); } /** * put additional param value. will override if exist. * * @param key key of param. * @param value value of param. */ public void putAdditionalParam(String key, String value) { if (additionMap == null) { additionMap = new HashMap<>(2); } additionMap.put(key, value); } /** * Getter method for property content. * * @return property value of content */ public String getContent() { return content; } /** * Setter method for property content. * * @param content value to be assigned to property content */ public void setContent(String content) { this.content = content; } /** * Getter method for property casMd5. * * @return property value of casMd5 */ public String getCasMd5() { return casMd5; } /** * Setter method for property casMd5. * * @param casMd5 value to be assigned to property content */ public void setCasMd5(String casMd5) { this.casMd5 = casMd5; } /** * Getter method for property casMd5. * * @return property value of casMd5 */ public Map getAdditionMap() { return additionMap; } /** * Setter method for property additionMap. * * @param additionMap value to be assigned to property additionMap */ public void setAdditionMap(Map additionMap) { this.additionMap = additionMap; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigQueryRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; /** * request to query config content. * * @author liuzunfei * @version $Id: ConfigQueryRequest.java, v 0.1 2020年07月13日 9:06 PM liuzunfei Exp $ */ public class ConfigQueryRequest extends AbstractConfigRequest { private String tag; /** * request builder. * * @param dataId dataId * @param group group * @param tenant tenant * @return ConfigQueryRequest instance. */ public static ConfigQueryRequest build(String dataId, String group, String tenant) { ConfigQueryRequest request = new ConfigQueryRequest(); request.setDataId(dataId); request.setGroup(group); request.setTenant(tenant); return request; } /** * Getter method for property tag. * * @return property value of tag */ public String getTag() { return tag; } /** * Setter method for property tag. * * @param tag value to be assigned to property tag */ public void setTag(String tag) { this.tag = tag; } public boolean isNotify() { String notify = getHeader(Constants.Config.NOTIFY_HEADER, Boolean.FALSE.toString()); return Boolean.parseBoolean(notify); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/ConfigRemoveRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; /** * request to remove a config . * * @author liuzunfei * @version $Id: ConfigRemoveRequest.java, v 0.1 2020年07月16日 4:31 PM liuzunfei Exp $ */ public class ConfigRemoveRequest extends AbstractConfigRequest { String tag; public ConfigRemoveRequest() { } public ConfigRemoveRequest(String dataId, String group, String tenant, String tag) { super.setDataId(dataId); super.setGroup(group); super.setTenant(tenant); this.tag = tag; } /** * Getter method for property tag. * * @return property value of tag */ public String getTag() { return tag; } /** * Setter method for property tag. * * @param tag value to be assigned to property tag */ public void setTag(String tag) { this.tag = tag; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/request/cluster/ConfigChangeClusterSyncRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request.cluster; import com.alibaba.nacos.api.config.remote.request.AbstractConfigRequest; /** * config change sync request on clusters. * * @author liuzunfei * @version $Id: ConfigChangeClusterSyncRequest.java, v 0.1 2020年08月11日 4:30 PM liuzunfei Exp $ */ public class ConfigChangeClusterSyncRequest extends AbstractConfigRequest { long lastModified; String grayName; @Deprecated boolean isBeta; @Deprecated String tag; public boolean isBeta() { return isBeta; } public void setBeta(boolean beta) { isBeta = beta; } /** * Getter method for property tag. * * @return property value of tag */ public String getTag() { return tag; } /** * Setter method for property tag. * * @param tag value to be assigned to property tag */ public void setTag(String tag) { this.tag = tag; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } /** * Getter method for property lastModified. * * @return property value of lastModified */ public long getLastModified() { return lastModified; } /** * Setter method for property lastModified. * * @param lastModified value to be assigned to property lastModified */ public void setLastModified(long lastModified) { this.lastModified = lastModified; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ClientConfigMetricResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; import java.util.HashMap; import java.util.Map; /** * client config metrics response. * * @author liuzunfei * @version $Id: ClientConfigMetricResponse.java, v 0.1 2020年12月30日 2:59 PM liuzunfei Exp $ */ public class ClientConfigMetricResponse extends Response { private Map metrics = new HashMap<>(); public Map getMetrics() { return metrics; } public void setMetrics(Map metrics) { this.metrics = metrics; } public void putMetric(String key, Object value) { metrics.put(key, value); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigChangeBatchListenResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import java.util.ArrayList; import java.util.List; /** * ConfigChangeBatchListenResponse. * * @author liuzunfei * @version $Id: ConfigChangeBatchListenResponse.java, v 0.1 2020年07月14日 3:07 PM liuzunfei Exp $ */ public class ConfigChangeBatchListenResponse extends Response { List changedConfigs = new ArrayList<>(); public ConfigChangeBatchListenResponse() { } /** * add changed config. * * @param dataId dataId. * @param group group. * @param tenant tenant. */ public void addChangeConfig(String dataId, String group, String tenant) { ConfigContext configContext = new ConfigContext(); configContext.dataId = dataId; configContext.group = group; configContext.tenant = tenant; changedConfigs.add(configContext); } /** * Getter method for property changedConfigs. * * @return property value of changedConfigs */ public List getChangedConfigs() { return changedConfigs; } /** * Setter method for property changedConfigs. * * @param changedConfigs value to be assigned to property changedConfigs */ public void setChangedConfigs(List changedConfigs) { this.changedConfigs = changedConfigs; } /** * build fail response. * * @param errorMessage errorMessage. * @return response. */ public static ConfigChangeBatchListenResponse buildFailResponse(String errorMessage) { ConfigChangeBatchListenResponse response = new ConfigChangeBatchListenResponse(); response.setResultCode(ResponseCode.FAIL.getCode()); response.setMessage(errorMessage); return response; } public static class ConfigContext { String group; String dataId; String tenant; public ConfigContext() { } /** * Getter method for property groupId. * * @return property value of groupId */ public String getGroup() { return group; } /** * Setter method for property groupId. * * @param group value to be assigned to property groupId */ public void setGroup(String group) { this.group = group; } /** * Getter method for property dataId. * * @return property value of dataId */ public String getDataId() { return dataId; } /** * Setter method for property dataId. * * @param dataId value to be assigned to property dataId */ public void setDataId(String dataId) { this.dataId = dataId; } /** * Getter method for property tenant. * * @return property value of tenant */ public String getTenant() { return tenant; } /** * Setter method for property tenant. * * @param tenant value to be assigned to property tenant */ public void setTenant(String tenant) { this.tenant = tenant; } @Override public String toString() { return "ConfigContext{" + "group='" + group + '\'' + ", dataId='" + dataId + '\'' + ", tenant='" + tenant + '\'' + '}'; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigChangeNotifyResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * config change notify response from client. * @author liuzunfei * @version $Id: ConfigChangeNotifyResponse.java, v 0.1 2020年09月01日 2:59 PM liuzunfei Exp $ */ public class ConfigChangeNotifyResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchChangeNotifyResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * FuzzyListenNotifyChangeResponse. * * @author stone-98 * @date 2024/3/18 */ public class ConfigFuzzyWatchChangeNotifyResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * ConfigBatchFuzzyListenResponse. * * @author stone-98 * @date 2024/3/4 */ public class ConfigFuzzyWatchResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigFuzzyWatchSyncResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * FuzzyListenNotifyChangeResponse. * * @author stone-98 * @date 2024/3/18 */ public class ConfigFuzzyWatchSyncResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigPublishResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; /** * ConfigPublishResponse. * * @author liuzunfei * @version $Id: ConfigPublishResponse.java, v 0.1 2020年07月16日 4:59 PM liuzunfei Exp $ */ public class ConfigPublishResponse extends Response { public ConfigPublishResponse() { super(); } /** * Build success response. * * @return response. */ public static ConfigPublishResponse buildSuccessResponse() { return new ConfigPublishResponse(); } /** * Build fail response. * * @return response. */ public static ConfigPublishResponse buildFailResponse(int errorCode, String errorMsg) { ConfigPublishResponse configPublishResponse = new ConfigPublishResponse(); configPublishResponse.setResultCode(ResponseCode.FAIL.getCode()); configPublishResponse.setMessage(errorMsg); configPublishResponse.setErrorCode(errorCode); return configPublishResponse; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigQueryResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * ConfigQueryResponse. * * @author liuzunfei * @version $Id: ConfigQueryResponse.java, v 0.1 2020年07月14日 2:47 PM liuzunfei Exp $ */ public class ConfigQueryResponse extends Response { public static final int CONFIG_NOT_FOUND = 300; public static final int CONFIG_QUERY_CONFLICT = 400; public static final int NO_RIGHT = 403; String content; String encryptedDataKey; String contentType; String md5; long lastModified; boolean isBeta; String tag; public ConfigQueryResponse() { } /** * Build fail response. * * @param errorCode errorCode. * @param message message. * @return response. */ public static ConfigQueryResponse buildFailResponse(int errorCode, String message) { ConfigQueryResponse response = new ConfigQueryResponse(); response.setErrorInfo(errorCode, message); return response; } /** * Build success response. * * @param content content. * @return response. */ public static ConfigQueryResponse buildSuccessResponse(String content) { ConfigQueryResponse response = new ConfigQueryResponse(); response.setContent(content); return response; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } public boolean isBeta() { return isBeta; } public void setBeta(boolean beta) { isBeta = beta; } /** * Getter method for property content. * * @return property value of content */ public String getContent() { return content; } /** * Setter method for property content. * * @param content value to be assigned to property content */ public void setContent(String content) { this.content = content; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } public String getEncryptedDataKey() { return encryptedDataKey; } /** * Getter method for property contentType. * * @return property value of contentType */ public String getContentType() { return contentType; } /** * Setter method for property contentType. * * @param contentType value to be assigned to property contentType */ public void setContentType(String contentType) { this.contentType = contentType; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/ConfigRemoveResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; /** * ConfigRemoveResponse. * * @author liuzunfei * @version $Id: ConfigRemoveResponse.java, v 0.1 2020年07月16日 4:59 PM liuzunfei Exp $ */ public class ConfigRemoveResponse extends Response { public ConfigRemoveResponse() { super(); } /** * Build success response. * * @return response. */ public static ConfigRemoveResponse buildSuccessResponse() { return new ConfigRemoveResponse(); } /** * Build fail response. * * @return response. */ public static ConfigRemoveResponse buildFailResponse(String errorMsg) { ConfigRemoveResponse removeResponse = new ConfigRemoveResponse(); removeResponse.setResultCode(ResponseCode.FAIL.getCode()); removeResponse.setMessage(errorMsg); return removeResponse; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/config/remote/response/cluster/ConfigChangeClusterSyncResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response.cluster; import com.alibaba.nacos.api.remote.response.Response; /** * config change sync response on clusters. * * @author liuzunfei * @version $Id: ConfigChangeClusterSyncResponse.java, v 0.1 2020年08月11日 4:32 PM liuzunfei Exp $ */ public class ConfigChangeClusterSyncResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/exception/NacosException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.utils.StringUtils; /** * Nacos Exception. * * @author Nacos */ public class NacosException extends Exception { /** * serialVersionUID. */ private static final long serialVersionUID = -3913902031489277776L; private int errCode; private String errMsg; private Throwable causeThrowable; public NacosException() { } public NacosException(final int errCode, final String errMsg) { super(errMsg); this.errCode = errCode; this.errMsg = errMsg; } public NacosException(final int errCode, final Throwable throwable) { super(throwable); this.errCode = errCode; this.setCauseThrowable(throwable); } public NacosException(final int errCode, final String errMsg, final Throwable throwable) { super(errMsg, throwable); this.errCode = errCode; this.errMsg = errMsg; this.setCauseThrowable(throwable); } public int getErrCode() { return this.errCode; } public String getErrMsg() { if (!StringUtils.isBlank(this.errMsg)) { return this.errMsg; } if (this.causeThrowable != null) { return this.causeThrowable.getMessage(); } return Constants.NULL; } public void setErrCode(final int errCode) { this.errCode = errCode; } public void setErrMsg(final String errMsg) { this.errMsg = errMsg; } public void setCauseThrowable(final Throwable throwable) { this.causeThrowable = this.getCauseThrowable(throwable); } private Throwable getCauseThrowable(final Throwable t) { if (t.getCause() == null) { return t; } return this.getCauseThrowable(t.getCause()); } @Override public String toString() { return "ErrCode:" + getErrCode() + ", ErrMsg:" + getErrMsg(); } /* * client error code. * -400 -503 throw exception to user. */ /** * invalid param(参数错误). */ public static final int CLIENT_INVALID_PARAM = -400; /** * client disconnect. */ public static final int CLIENT_DISCONNECT = -401; /** * over client threshold(超过client端的限流阈值). */ public static final int CLIENT_OVER_THRESHOLD = -503; /* * server error code. * 400 403 throw exception to user * 500 502 503 change ip and retry */ /** * invalid param(参数错误). */ public static final int INVALID_PARAM = 400; /** * no right(鉴权失败). */ public static final int NO_RIGHT = 403; /** * not found. */ public static final int NOT_FOUND = 404; /** * not modified. */ public static final int NOT_MODIFIED = 304; /** * conflict(写并发冲突). */ public static final int CONFLICT = 409; /** * config already exists(配置已存在). */ public static final int CONFIG_ALREADY_EXISTS = 410; /** * server error(server异常,如超时). */ public static final int SERVER_ERROR = 500; /** * server not implemented(server不支持该请求,可能该版本未实现功能,或请求了错误的API). */ public static final int SERVER_NOT_IMPLEMENTED = 501; /** * client error(client异常,返回给服务端). */ public static final int CLIENT_ERROR = -500; /** * bad gateway(路由异常,如nginx后面的Server挂掉). */ public static final int BAD_GATEWAY = 502; /** * over threshold(超过server端的限流阈值). */ public static final int OVER_THRESHOLD = 503; /** * Server is not started. */ public static final int INVALID_SERVER_STATUS = 300; /** * Connection is not registered. */ public static final int UN_REGISTER = 301; /** * No Handler Found. */ public static final int NO_HANDLER = 302; public static final int RESOURCE_NOT_FOUND = -404; /** * http client error code, ome exceptions that occurred when there use the Nacos RestTemplate and Nacos * AsyncRestTemplate. */ public static final int HTTP_CLIENT_ERROR_CODE = -500; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/exception/api/NacosApiException.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.api; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.utils.StringUtils; /** Exception for open API.
    * errCode -> HTTP status code inherited from {@link NacosException}
    * errMsg -> detail error message inherited from {@link NacosException}
    * detailErrCode -> error code for api v2.0
    * errAbstract -> abstract error message for api v2.0 * @author dongyafei * @date 2022/7/22 */ public class NacosApiException extends NacosException { /** * serialVersionUID. */ private static final long serialVersionUID = 2245627968556056573L; /** * error code for api v2.0. */ private int detailErrCode; /** * abstract error description for api v2.0. */ private String errAbstract; public NacosApiException() { } public NacosApiException(int statusCode, ErrorCode errorCode, Throwable throwable, String message) { super(statusCode, message, throwable); this.detailErrCode = errorCode.getCode(); this.errAbstract = errorCode.getMsg(); } public NacosApiException(int statusCode, ErrorCode errorCode, String message) { super(statusCode, message); this.detailErrCode = errorCode.getCode(); this.errAbstract = errorCode.getMsg(); } public int getDetailErrCode() { return detailErrCode; } public String getErrAbstract() { if (!StringUtils.isBlank(this.errAbstract)) { return this.errAbstract; } return Constants.NULL; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/exception/runtime/NacosDeserializationException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; import java.lang.reflect.Type; import static com.alibaba.nacos.api.common.Constants.Exception.DESERIALIZE_ERROR_CODE; /** * Nacos deserialization exception. * * @author yangyi */ public class NacosDeserializationException extends NacosRuntimeException { private static final long serialVersionUID = -2742350751684273728L; private static final String DEFAULT_MSG = "Nacos deserialize failed. "; private static final String MSG_FOR_SPECIFIED_CLASS = "Nacos deserialize for class [%s] failed. "; private static final String ERROR_MSG_FOR_SPECIFIED_CLASS = "Nacos deserialize for class [%s] failed, cause error[%s]. "; private Class targetClass; public NacosDeserializationException() { super(DESERIALIZE_ERROR_CODE); } public NacosDeserializationException(Class targetClass) { super(DESERIALIZE_ERROR_CODE, String.format(MSG_FOR_SPECIFIED_CLASS, targetClass.getName())); this.targetClass = targetClass; } public NacosDeserializationException(Type targetType) { super(DESERIALIZE_ERROR_CODE, String.format(MSG_FOR_SPECIFIED_CLASS, targetType.toString())); } public NacosDeserializationException(Throwable throwable) { super(DESERIALIZE_ERROR_CODE, DEFAULT_MSG, throwable); } public NacosDeserializationException(Class targetClass, Throwable throwable) { super(DESERIALIZE_ERROR_CODE, String.format(ERROR_MSG_FOR_SPECIFIED_CLASS, targetClass.getName(), throwable.getMessage()), throwable); this.targetClass = targetClass; } public NacosDeserializationException(Type targetType, Throwable throwable) { super(DESERIALIZE_ERROR_CODE, String.format(ERROR_MSG_FOR_SPECIFIED_CLASS, targetType.toString(), throwable.getMessage()), throwable); } public Class getTargetClass() { return targetClass; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/exception/runtime/NacosLoadException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; /** * Nacos load exception. * * @author hujun */ public class NacosLoadException extends RuntimeException { private static final long serialVersionUID = 3513491993982295562L; public NacosLoadException(String errMsg) { super(errMsg); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/exception/runtime/NacosRuntimeException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; /** * Nacos runtime exception. * * @author yangyi */ public class NacosRuntimeException extends RuntimeException { private static final long serialVersionUID = 3513491993982293262L; public static final String ERROR_MESSAGE_FORMAT = "errCode: %d, errMsg: %s "; private final int errCode; public NacosRuntimeException(int errCode) { super(); this.errCode = errCode; } public NacosRuntimeException(int errCode, String errMsg) { super(String.format(ERROR_MESSAGE_FORMAT, errCode, errMsg)); this.errCode = errCode; } public NacosRuntimeException(int errCode, Throwable throwable) { super(throwable); this.errCode = errCode; } public NacosRuntimeException(int errCode, String errMsg, Throwable throwable) { super(String.format(ERROR_MESSAGE_FORMAT, errCode, errMsg), throwable); this.errCode = errCode; } public int getErrCode() { return errCode; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/exception/runtime/NacosSerializationException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; import static com.alibaba.nacos.api.common.Constants.Exception.SERIALIZE_ERROR_CODE; /** * Nacos serialization exception. * * @author yangyi */ public class NacosSerializationException extends NacosRuntimeException { private static final long serialVersionUID = -4308536346316915612L; private static final String DEFAULT_MSG = "Nacos serialize failed. "; private static final String MSG_FOR_SPECIFIED_CLASS = "Nacos serialize for class [%s] failed. "; private Class serializedClass; public NacosSerializationException() { super(SERIALIZE_ERROR_CODE); } public NacosSerializationException(Class serializedClass) { super(SERIALIZE_ERROR_CODE, String.format(MSG_FOR_SPECIFIED_CLASS, serializedClass.getName())); this.serializedClass = serializedClass; } public NacosSerializationException(Throwable throwable) { super(SERIALIZE_ERROR_CODE, DEFAULT_MSG, throwable); } public NacosSerializationException(Class serializedClass, Throwable throwable) { super(SERIALIZE_ERROR_CODE, String.format(MSG_FOR_SPECIFIED_CLASS, serializedClass.getName()), throwable); this.serializedClass = serializedClass; } public Class getSerializedClass() { return serializedClass; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/grpc/auto/BiRequestStreamGrpc.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.grpc.auto; import static io.grpc.MethodDescriptor.generateFullMethodName; import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall; import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall; import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall; /** */ @javax.annotation.Generated( value = "by gRPC proto compiler (version 1.14.0)", comments = "Source: nacos_grpc_service.proto") public final class BiRequestStreamGrpc { private BiRequestStreamGrpc() {} public static final String SERVICE_NAME = "BiRequestStream"; // Static method descriptors that strictly reflect the proto. private static volatile io.grpc.MethodDescriptor getRequestBiStreamMethod; @io.grpc.stub.annotations.RpcMethod( fullMethodName = SERVICE_NAME + '/' + "requestBiStream", requestType = com.alibaba.nacos.api.grpc.auto.Payload.class, responseType = com.alibaba.nacos.api.grpc.auto.Payload.class, methodType = io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) public static io.grpc.MethodDescriptor getRequestBiStreamMethod() { io.grpc.MethodDescriptor getRequestBiStreamMethod; if ((getRequestBiStreamMethod = BiRequestStreamGrpc.getRequestBiStreamMethod) == null) { synchronized (BiRequestStreamGrpc.class) { if ((getRequestBiStreamMethod = BiRequestStreamGrpc.getRequestBiStreamMethod) == null) { BiRequestStreamGrpc.getRequestBiStreamMethod = getRequestBiStreamMethod = io.grpc.MethodDescriptor.newBuilder() .setType(io.grpc.MethodDescriptor.MethodType.BIDI_STREAMING) .setFullMethodName(generateFullMethodName( "BiRequestStream", "requestBiStream")) .setSampledToLocalTracing(true) .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( com.alibaba.nacos.api.grpc.auto.Payload.getDefaultInstance())) .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( com.alibaba.nacos.api.grpc.auto.Payload.getDefaultInstance())) .setSchemaDescriptor(new BiRequestStreamMethodDescriptorSupplier("requestBiStream")) .build(); } } } return getRequestBiStreamMethod; } /** * Creates a new async stub that supports all call types for the service */ public static BiRequestStreamStub newStub(io.grpc.Channel channel) { return new BiRequestStreamStub(channel); } /** * Creates a new blocking-style stub that supports unary and streaming output calls on the service */ public static BiRequestStreamBlockingStub newBlockingStub( io.grpc.Channel channel) { return new BiRequestStreamBlockingStub(channel); } /** * Creates a new ListenableFuture-style stub that supports unary calls on the service */ public static BiRequestStreamFutureStub newFutureStub( io.grpc.Channel channel) { return new BiRequestStreamFutureStub(channel); } /** */ public static abstract class BiRequestStreamImplBase implements io.grpc.BindableService { /** *

         * Sends a commonRequest
         * 
    */ public io.grpc.stub.StreamObserver requestBiStream( io.grpc.stub.StreamObserver responseObserver) { return asyncUnimplementedStreamingCall(getRequestBiStreamMethod(), responseObserver); } @Override public final io.grpc.ServerServiceDefinition bindService() { return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) .addMethod( getRequestBiStreamMethod(), asyncBidiStreamingCall( new MethodHandlers< com.alibaba.nacos.api.grpc.auto.Payload, com.alibaba.nacos.api.grpc.auto.Payload>( this, METHODID_REQUEST_BI_STREAM))) .build(); } } /** */ public static final class BiRequestStreamStub extends io.grpc.stub.AbstractStub { private BiRequestStreamStub(io.grpc.Channel channel) { super(channel); } private BiRequestStreamStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { super(channel, callOptions); } @Override protected BiRequestStreamStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { return new BiRequestStreamStub(channel, callOptions); } /** *
         * Sends a commonRequest
         * 
    */ public io.grpc.stub.StreamObserver requestBiStream( io.grpc.stub.StreamObserver responseObserver) { return asyncBidiStreamingCall( getChannel().newCall(getRequestBiStreamMethod(), getCallOptions()), responseObserver); } } /** */ public static final class BiRequestStreamBlockingStub extends io.grpc.stub.AbstractStub { private BiRequestStreamBlockingStub(io.grpc.Channel channel) { super(channel); } private BiRequestStreamBlockingStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { super(channel, callOptions); } @Override protected BiRequestStreamBlockingStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { return new BiRequestStreamBlockingStub(channel, callOptions); } } /** */ public static final class BiRequestStreamFutureStub extends io.grpc.stub.AbstractStub { private BiRequestStreamFutureStub(io.grpc.Channel channel) { super(channel); } private BiRequestStreamFutureStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { super(channel, callOptions); } @Override protected BiRequestStreamFutureStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { return new BiRequestStreamFutureStub(channel, callOptions); } } private static final int METHODID_REQUEST_BI_STREAM = 0; private static final class MethodHandlers implements io.grpc.stub.ServerCalls.UnaryMethod, io.grpc.stub.ServerCalls.ServerStreamingMethod, io.grpc.stub.ServerCalls.ClientStreamingMethod, io.grpc.stub.ServerCalls.BidiStreamingMethod { private final BiRequestStreamImplBase serviceImpl; private final int methodId; MethodHandlers(BiRequestStreamImplBase serviceImpl, int methodId) { this.serviceImpl = serviceImpl; this.methodId = methodId; } @Override @SuppressWarnings("unchecked") public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { switch (methodId) { default: throw new AssertionError(); } } @Override @SuppressWarnings("unchecked") public io.grpc.stub.StreamObserver invoke( io.grpc.stub.StreamObserver responseObserver) { switch (methodId) { case METHODID_REQUEST_BI_STREAM: return (io.grpc.stub.StreamObserver) serviceImpl.requestBiStream( (io.grpc.stub.StreamObserver) responseObserver); default: throw new AssertionError(); } } } private static abstract class BiRequestStreamBaseDescriptorSupplier implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { BiRequestStreamBaseDescriptorSupplier() {} @Override public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { return com.alibaba.nacos.api.grpc.auto.NacosGrpcService.getDescriptor(); } @Override public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { return getFileDescriptor().findServiceByName("BiRequestStream"); } } private static final class BiRequestStreamFileDescriptorSupplier extends BiRequestStreamBaseDescriptorSupplier { BiRequestStreamFileDescriptorSupplier() {} } private static final class BiRequestStreamMethodDescriptorSupplier extends BiRequestStreamBaseDescriptorSupplier implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { private final String methodName; BiRequestStreamMethodDescriptorSupplier(String methodName) { this.methodName = methodName; } @Override public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { return getServiceDescriptor().findMethodByName(methodName); } } private static volatile io.grpc.ServiceDescriptor serviceDescriptor; public static io.grpc.ServiceDescriptor getServiceDescriptor() { io.grpc.ServiceDescriptor result = serviceDescriptor; if (result == null) { synchronized (BiRequestStreamGrpc.class) { result = serviceDescriptor; if (result == null) { serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) .setSchemaDescriptor(new BiRequestStreamFileDescriptorSupplier()) .addMethod(getRequestBiStreamMethod()) .build(); } } } return result; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/grpc/auto/Metadata.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.grpc.auto; /** * Protobuf type {@code Metadata} */ public final class Metadata extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:Metadata) MetadataOrBuilder { private static final long serialVersionUID = 0L; // Use Metadata.newBuilder() to construct. private Metadata(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private Metadata() { type_ = ""; clientIp_ = ""; } @Override @SuppressWarnings({"unused"}) protected Object newInstance( UnusedPrivateParameter unused) { return new Metadata(); } @Override public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } private Metadata( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { this(); if (extensionRegistry == null) { throw new NullPointerException(); } 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; case 26: { String s = input.readStringRequireUtf8(); type_ = s; break; } case 58: { if (!((mutable_bitField0_ & 0x00000001) != 0)) { headers_ = com.google.protobuf.MapField.newMapField( HeadersDefaultEntryHolder.defaultEntry); mutable_bitField0_ |= 0x00000001; } com.google.protobuf.MapEntry headers__ = input.readMessage( HeadersDefaultEntryHolder.defaultEntry.getParserForType(), extensionRegistry); headers_.getMutableMap().put( headers__.getKey(), headers__.getValue()); break; } case 66: { String s = input.readStringRequireUtf8(); clientIp_ = s; break; } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { done = true; } 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 NacosGrpcService.internal_static_Metadata_descriptor; } @SuppressWarnings({"rawtypes"}) @Override protected com.google.protobuf.MapField internalGetMapField( int number) { switch (number) { case 7: return internalGetHeaders(); default: throw new RuntimeException( "Invalid map field number: " + number); } } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return NacosGrpcService.internal_static_Metadata_fieldAccessorTable .ensureFieldAccessorsInitialized( Metadata.class, Builder.class); } public static final int TYPE_FIELD_NUMBER = 3; private volatile Object type_; /** * string type = 3; */ public String getType() { Object ref = type_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); type_ = s; return s; } } /** * string type = 3; */ public com.google.protobuf.ByteString getTypeBytes() { Object ref = type_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (String) ref); type_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int CLIENTIP_FIELD_NUMBER = 8; private volatile Object clientIp_; /** * string clientIp = 8; */ public String getClientIp() { Object ref = clientIp_; if (ref instanceof String) { return (String) ref; } else { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); clientIp_ = s; return s; } } /** * string clientIp = 8; */ public com.google.protobuf.ByteString getClientIpBytes() { Object ref = clientIp_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (String) ref); clientIp_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } public static final int HEADERS_FIELD_NUMBER = 7; private static final class HeadersDefaultEntryHolder { static final com.google.protobuf.MapEntry< String, String> defaultEntry = com.google.protobuf.MapEntry .newDefaultInstance( NacosGrpcService.internal_static_Metadata_HeadersEntry_descriptor, com.google.protobuf.WireFormat.FieldType.STRING, "", com.google.protobuf.WireFormat.FieldType.STRING, ""); } private com.google.protobuf.MapField< String, String> headers_; private com.google.protobuf.MapField internalGetHeaders() { if (headers_ == null) { return com.google.protobuf.MapField.emptyMapField( HeadersDefaultEntryHolder.defaultEntry); } return headers_; } public int getHeadersCount() { return internalGetHeaders().getMap().size(); } /** * map<string, string> headers = 7; */ public boolean containsHeaders( String key) { if (key == null) { throw new NullPointerException(); } return internalGetHeaders().getMap().containsKey(key); } /** * Use {@link #getHeadersMap()} instead. */ @Deprecated public java.util.Map getHeaders() { return getHeadersMap(); } /** * map<string, string> headers = 7; */ public java.util.Map getHeadersMap() { return internalGetHeaders().getMap(); } /** * map<string, string> headers = 7; */ public String getHeadersOrDefault( String key, String defaultValue) { if (key == null) { throw new NullPointerException(); } java.util.Map map = internalGetHeaders().getMap(); return map.getOrDefault(key, defaultValue); } /** * map<string, string> headers = 7; */ public String getHeadersOrThrow( String key) { if (key == null) { throw new NullPointerException(); } java.util.Map map = internalGetHeaders().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 (!getTypeBytes().isEmpty()) { com.google.protobuf.GeneratedMessageV3.writeString(output, 3, type_); } com.google.protobuf.GeneratedMessageV3 .serializeStringMapTo( output, internalGetHeaders(), HeadersDefaultEntryHolder.defaultEntry, 7); if (!getClientIpBytes().isEmpty()) { com.google.protobuf.GeneratedMessageV3.writeString(output, 8, clientIp_); } unknownFields.writeTo(output); } @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; size = 0; if (!getTypeBytes().isEmpty()) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(3, type_); } for (java.util.Map.Entry entry : internalGetHeaders().getMap().entrySet()) { com.google.protobuf.MapEntry headers__ = HeadersDefaultEntryHolder.defaultEntry.newBuilderForType() .setKey(entry.getKey()) .setValue(entry.getValue()) .build(); size += com.google.protobuf.CodedOutputStream .computeMessageSize(7, headers__); } if (!getClientIpBytes().isEmpty()) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(8, clientIp_); } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof Metadata)) { return super.equals(obj); } Metadata other = (Metadata) obj; if (!getType() .equals(other.getType())) return false; if (!getClientIp() .equals(other.getClientIp())) return false; if (!internalGetHeaders().equals( other.internalGetHeaders())) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + TYPE_FIELD_NUMBER; hash = (53 * hash) + getType().hashCode(); hash = (37 * hash) + CLIENTIP_FIELD_NUMBER; hash = (53 * hash) + getClientIp().hashCode(); if (!internalGetHeaders().getMap().isEmpty()) { hash = (37 * hash) + HEADERS_FIELD_NUMBER; hash = (53 * hash) + internalGetHeaders().hashCode(); } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } public static Metadata parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static Metadata parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static Metadata parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static Metadata parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static Metadata parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static Metadata parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static Metadata parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } public static Metadata 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 Metadata parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseDelimitedWithIOException(PARSER, input); } public static Metadata 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 Metadata parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } public static Metadata 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(Metadata 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; } /** * Protobuf type {@code Metadata} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:Metadata) MetadataOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return NacosGrpcService.internal_static_Metadata_descriptor; } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMapField( int number) { switch (number) { case 7: return internalGetHeaders(); default: throw new RuntimeException( "Invalid map field number: " + number); } } @SuppressWarnings({"rawtypes"}) protected com.google.protobuf.MapField internalGetMutableMapField( int number) { switch (number) { case 7: return internalGetMutableHeaders(); default: throw new RuntimeException( "Invalid map field number: " + number); } } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return NacosGrpcService.internal_static_Metadata_fieldAccessorTable .ensureFieldAccessorsInitialized( Metadata.class, Builder.class); } // Construct using com.alibaba.nacos.api.grpc.auto.Metadata.newBuilder() private Builder() { maybeForceBuilderInitialization(); } private Builder( BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessageV3 .alwaysUseFieldBuilders) { } } @Override public Builder clear() { super.clear(); type_ = ""; clientIp_ = ""; internalGetMutableHeaders().clear(); return this; } @Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return NacosGrpcService.internal_static_Metadata_descriptor; } @Override public Metadata getDefaultInstanceForType() { return Metadata.getDefaultInstance(); } @Override public Metadata build() { Metadata result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public Metadata buildPartial() { Metadata result = new Metadata(this); int from_bitField0_ = bitField0_; result.type_ = type_; result.clientIp_ = clientIp_; result.headers_ = internalGetHeaders(); result.headers_.makeImmutable(); onBuilt(); return result; } @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 Metadata) { return mergeFrom((Metadata)other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(Metadata other) { if (other == Metadata.getDefaultInstance()) return this; if (!other.getType().isEmpty()) { type_ = other.type_; onChanged(); } if (!other.getClientIp().isEmpty()) { clientIp_ = other.clientIp_; onChanged(); } internalGetMutableHeaders().mergeFrom( other.internalGetHeaders()); this.mergeUnknownFields(other.unknownFields); 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 { Metadata parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { parsedMessage = (Metadata) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { mergeFrom(parsedMessage); } } return this; } private int bitField0_; private Object type_ = ""; /** * string type = 3; */ public String getType() { Object ref = type_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); type_ = s; return s; } else { return (String) ref; } } /** * string type = 3; */ public com.google.protobuf.ByteString getTypeBytes() { Object ref = type_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (String) ref); type_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** * string type = 3; */ public Builder setType( String value) { if (value == null) { throw new NullPointerException(); } type_ = value; onChanged(); return this; } /** * string type = 3; */ public Builder clearType() { type_ = getDefaultInstance().getType(); onChanged(); return this; } /** * string type = 3; */ public Builder setTypeBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); type_ = value; onChanged(); return this; } private Object clientIp_ = ""; /** * string clientIp = 8; */ public String getClientIp() { Object ref = clientIp_; if (!(ref instanceof String)) { com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; String s = bs.toStringUtf8(); clientIp_ = s; return s; } else { return (String) ref; } } /** * string clientIp = 8; */ public com.google.protobuf.ByteString getClientIpBytes() { Object ref = clientIp_; if (ref instanceof String) { com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8( (String) ref); clientIp_ = b; return b; } else { return (com.google.protobuf.ByteString) ref; } } /** * string clientIp = 8; */ public Builder setClientIp( String value) { if (value == null) { throw new NullPointerException(); } clientIp_ = value; onChanged(); return this; } /** * string clientIp = 8; */ public Builder clearClientIp() { clientIp_ = getDefaultInstance().getClientIp(); onChanged(); return this; } /** * string clientIp = 8; */ public Builder setClientIpBytes( com.google.protobuf.ByteString value) { if (value == null) { throw new NullPointerException(); } checkByteStringIsUtf8(value); clientIp_ = value; onChanged(); return this; } private com.google.protobuf.MapField< String, String> headers_; private com.google.protobuf.MapField internalGetHeaders() { if (headers_ == null) { return com.google.protobuf.MapField.emptyMapField( HeadersDefaultEntryHolder.defaultEntry); } return headers_; } private com.google.protobuf.MapField internalGetMutableHeaders() { onChanged(); if (headers_ == null) { headers_ = com.google.protobuf.MapField.newMapField( HeadersDefaultEntryHolder.defaultEntry); } if (!headers_.isMutable()) { headers_ = headers_.copy(); } return headers_; } public int getHeadersCount() { return internalGetHeaders().getMap().size(); } /** * map<string, string> headers = 7; */ public boolean containsHeaders( String key) { if (key == null) { throw new NullPointerException(); } return internalGetHeaders().getMap().containsKey(key); } /** * Use {@link #getHeadersMap()} instead. */ @Deprecated public java.util.Map getHeaders() { return getHeadersMap(); } /** * map<string, string> headers = 7; */ public java.util.Map getHeadersMap() { return internalGetHeaders().getMap(); } /** * map<string, string> headers = 7; */ public String getHeadersOrDefault( String key, String defaultValue) { if (key == null) { throw new NullPointerException(); } java.util.Map map = internalGetHeaders().getMap(); return map.containsKey(key) ? map.get(key) : defaultValue; } /** * map<string, string> headers = 7; */ public String getHeadersOrThrow( String key) { if (key == null) { throw new NullPointerException(); } java.util.Map map = internalGetHeaders().getMap(); if (!map.containsKey(key)) { throw new IllegalArgumentException(); } return map.get(key); } public Builder clearHeaders() { internalGetMutableHeaders().getMutableMap() .clear(); return this; } /** * map<string, string> headers = 7; */ public Builder removeHeaders( String key) { if (key == null) { throw new NullPointerException(); } internalGetMutableHeaders().getMutableMap() .remove(key); return this; } /** * Use alternate mutation accessors instead. */ @Deprecated public java.util.Map getMutableHeaders() { return internalGetMutableHeaders().getMutableMap(); } /** * map<string, string> headers = 7; */ public Builder putHeaders( String key, String value) { if (key == null) { throw new NullPointerException(); } if (value == null) { throw new NullPointerException(); } internalGetMutableHeaders().getMutableMap() .put(key, value); return this; } /** * map<string, string> headers = 7; */ public Builder putAllHeaders( java.util.Map values) { internalGetMutableHeaders().getMutableMap() .putAll(values); 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:Metadata) } // @@protoc_insertion_point(class_scope:Metadata) private static final Metadata DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new Metadata(); } public static Metadata getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { @Override public Metadata parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return new Metadata(input, extensionRegistry); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @Override public com.google.protobuf.Parser getParserForType() { return PARSER; } @Override public Metadata getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/grpc/auto/MetadataOrBuilder.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.grpc.auto; public interface MetadataOrBuilder extends // @@protoc_insertion_point(interface_extends:Metadata) com.google.protobuf.MessageOrBuilder { /** * string type = 3; */ String getType(); /** * string type = 3; */ com.google.protobuf.ByteString getTypeBytes(); /** * string clientIp = 8; */ String getClientIp(); /** * string clientIp = 8; */ com.google.protobuf.ByteString getClientIpBytes(); /** * map<string, string> headers = 7; */ int getHeadersCount(); /** * map<string, string> headers = 7; */ boolean containsHeaders( String key); /** * Use {@link #getHeadersMap()} instead. */ @Deprecated java.util.Map getHeaders(); /** * map<string, string> headers = 7; */ java.util.Map getHeadersMap(); /** * map<string, string> headers = 7; */ String getHeadersOrDefault( String key, String defaultValue); /** * map<string, string> headers = 7; */ String getHeadersOrThrow( String key); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/grpc/auto/NacosGrpcService.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.grpc.auto; public final class NacosGrpcService { private NacosGrpcService() {} 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_Metadata_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_Metadata_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_Metadata_HeadersEntry_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_Metadata_HeadersEntry_fieldAccessorTable; static final com.google.protobuf.Descriptors.Descriptor internal_static_Payload_descriptor; static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_Payload_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { return descriptor; } private static com.google.protobuf.Descriptors.FileDescriptor descriptor; static { String[] descriptorData = { "\n\030nacos_grpc_service.proto\032\031google/proto" + "buf/any.proto\032\037google/protobuf/timestamp" + ".proto\"\203\001\n\010Metadata\022\014\n\004type\030\003 \001(\t\022\020\n\010cli" + "entIp\030\010 \001(\t\022\'\n\007headers\030\007 \003(\0132\026.Metadata." + "HeadersEntry\032.\n\014HeadersEntry\022\013\n\003key\030\001 \001(" + "\t\022\r\n\005value\030\002 \001(\t:\0028\001\"J\n\007Payload\022\033\n\010metad" + "ata\030\002 \001(\0132\t.Metadata\022\"\n\004body\030\003 \001(\0132\024.goo" + "gle.protobuf.Any28\n\rRequestStream\022\'\n\rreq" + "uestStream\022\010.Payload\032\010.Payload\"\0000\0012*\n\007Re" + "quest\022\037\n\007request\022\010.Payload\032\010.Payload\"\0002>" + "\n\017BiRequestStream\022+\n\017requestBiStream\022\010.P" + "ayload\032\010.Payload\"\000(\0010\001B#\n\037com.alibaba.na" + "cos.api.grpc.autoP\001b\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] { com.google.protobuf.AnyProto.getDescriptor(), com.google.protobuf.TimestampProto.getDescriptor(), }); internal_static_Metadata_descriptor = getDescriptor().getMessageTypes().get(0); internal_static_Metadata_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_Metadata_descriptor, new String[] { "Type", "ClientIp", "Headers", }); internal_static_Metadata_HeadersEntry_descriptor = internal_static_Metadata_descriptor.getNestedTypes().get(0); internal_static_Metadata_HeadersEntry_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_Metadata_HeadersEntry_descriptor, new String[] { "Key", "Value", }); internal_static_Payload_descriptor = getDescriptor().getMessageTypes().get(1); internal_static_Payload_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_Payload_descriptor, new String[] { "Metadata", "Body", }); com.google.protobuf.AnyProto.getDescriptor(); com.google.protobuf.TimestampProto.getDescriptor(); } // @@protoc_insertion_point(outer_class_scope) } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/grpc/auto/Payload.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.grpc.auto; /** * Protobuf type {@code Payload} */ public final class Payload extends com.google.protobuf.GeneratedMessageV3 implements // @@protoc_insertion_point(message_implements:Payload) PayloadOrBuilder { private static final long serialVersionUID = 0L; // Use Payload.newBuilder() to construct. private Payload(com.google.protobuf.GeneratedMessageV3.Builder builder) { super(builder); } private Payload() { } @Override @SuppressWarnings({"unused"}) protected Object newInstance( UnusedPrivateParameter unused) { return new Payload(); } @Override public final com.google.protobuf.UnknownFieldSet getUnknownFields() { return this.unknownFields; } private Payload( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { this(); if (extensionRegistry == null) { throw new NullPointerException(); } 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; case 18: { Metadata.Builder subBuilder = null; if (metadata_ != null) { subBuilder = metadata_.toBuilder(); } metadata_ = input.readMessage(Metadata.parser(), extensionRegistry); if (subBuilder != null) { subBuilder.mergeFrom(metadata_); metadata_ = subBuilder.buildPartial(); } break; } case 26: { com.google.protobuf.Any.Builder subBuilder = null; if (body_ != null) { subBuilder = body_.toBuilder(); } body_ = input.readMessage(com.google.protobuf.Any.parser(), extensionRegistry); if (subBuilder != null) { subBuilder.mergeFrom(body_); body_ = subBuilder.buildPartial(); } break; } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { done = true; } 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 NacosGrpcService.internal_static_Payload_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return NacosGrpcService.internal_static_Payload_fieldAccessorTable .ensureFieldAccessorsInitialized( Payload.class, Builder.class); } public static final int METADATA_FIELD_NUMBER = 2; private Metadata metadata_; /** * .Metadata metadata = 2; */ public boolean hasMetadata() { return metadata_ != null; } /** * .Metadata metadata = 2; */ public Metadata getMetadata() { return metadata_ == null ? Metadata.getDefaultInstance() : metadata_; } /** * .Metadata metadata = 2; */ public MetadataOrBuilder getMetadataOrBuilder() { return getMetadata(); } public static final int BODY_FIELD_NUMBER = 3; private com.google.protobuf.Any body_; /** * .google.protobuf.Any body = 3; */ public boolean hasBody() { return body_ != null; } /** * .google.protobuf.Any body = 3; */ public com.google.protobuf.Any getBody() { return body_ == null ? com.google.protobuf.Any.getDefaultInstance() : body_; } /** * .google.protobuf.Any body = 3; */ public com.google.protobuf.AnyOrBuilder getBodyOrBuilder() { return getBody(); } 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 (metadata_ != null) { output.writeMessage(2, getMetadata()); } if (body_ != null) { output.writeMessage(3, getBody()); } unknownFields.writeTo(output); } @Override public int getSerializedSize() { int size = memoizedSize; if (size != -1) return size; size = 0; if (metadata_ != null) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(2, getMetadata()); } if (body_ != null) { size += com.google.protobuf.CodedOutputStream .computeMessageSize(3, getBody()); } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; } @Override public boolean equals(final Object obj) { if (obj == this) { return true; } if (!(obj instanceof Payload)) { return super.equals(obj); } Payload other = (Payload) obj; if (hasMetadata() != other.hasMetadata()) return false; if (hasMetadata()) { if (!getMetadata() .equals(other.getMetadata())) return false; } if (hasBody() != other.hasBody()) return false; if (hasBody()) { if (!getBody() .equals(other.getBody())) return false; } if (!unknownFields.equals(other.unknownFields)) return false; return true; } @Override public int hashCode() { if (memoizedHashCode != 0) { return memoizedHashCode; } int hash = 41; hash = (19 * hash) + getDescriptor().hashCode(); if (hasMetadata()) { hash = (37 * hash) + METADATA_FIELD_NUMBER; hash = (53 * hash) + getMetadata().hashCode(); } if (hasBody()) { hash = (37 * hash) + BODY_FIELD_NUMBER; hash = (53 * hash) + getBody().hashCode(); } hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; } public static Payload parseFrom( java.nio.ByteBuffer data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static Payload parseFrom( java.nio.ByteBuffer data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static Payload parseFrom( com.google.protobuf.ByteString data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static Payload parseFrom( com.google.protobuf.ByteString data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static Payload parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data); } public static Payload parseFrom( byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return PARSER.parseFrom(data, extensionRegistry); } public static Payload parseFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } public static Payload 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 Payload parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseDelimitedWithIOException(PARSER, input); } public static Payload 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 Payload parseFrom( com.google.protobuf.CodedInputStream input) throws java.io.IOException { return com.google.protobuf.GeneratedMessageV3 .parseWithIOException(PARSER, input); } public static Payload 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(Payload 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; } /** * Protobuf type {@code Payload} */ public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder implements // @@protoc_insertion_point(builder_implements:Payload) PayloadOrBuilder { public static final com.google.protobuf.Descriptors.Descriptor getDescriptor() { return NacosGrpcService.internal_static_Payload_descriptor; } @Override protected FieldAccessorTable internalGetFieldAccessorTable() { return NacosGrpcService.internal_static_Payload_fieldAccessorTable .ensureFieldAccessorsInitialized( Payload.class, Builder.class); } // Construct using com.alibaba.nacos.api.grpc.auto.Payload.newBuilder() private Builder() { maybeForceBuilderInitialization(); } private Builder( BuilderParent parent) { super(parent); maybeForceBuilderInitialization(); } private void maybeForceBuilderInitialization() { if (com.google.protobuf.GeneratedMessageV3 .alwaysUseFieldBuilders) { } } @Override public Builder clear() { super.clear(); if (metadataBuilder_ == null) { metadata_ = null; } else { metadata_ = null; metadataBuilder_ = null; } if (bodyBuilder_ == null) { body_ = null; } else { body_ = null; bodyBuilder_ = null; } return this; } @Override public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { return NacosGrpcService.internal_static_Payload_descriptor; } @Override public Payload getDefaultInstanceForType() { return Payload.getDefaultInstance(); } @Override public Payload build() { Payload result = buildPartial(); if (!result.isInitialized()) { throw newUninitializedMessageException(result); } return result; } @Override public Payload buildPartial() { Payload result = new Payload(this); if (metadataBuilder_ == null) { result.metadata_ = metadata_; } else { result.metadata_ = metadataBuilder_.build(); } if (bodyBuilder_ == null) { result.body_ = body_; } else { result.body_ = bodyBuilder_.build(); } onBuilt(); return result; } @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 Payload) { return mergeFrom((Payload)other); } else { super.mergeFrom(other); return this; } } public Builder mergeFrom(Payload other) { if (other == Payload.getDefaultInstance()) return this; if (other.hasMetadata()) { mergeMetadata(other.getMetadata()); } if (other.hasBody()) { mergeBody(other.getBody()); } this.mergeUnknownFields(other.unknownFields); 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 { Payload parsedMessage = null; try { parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); } catch (com.google.protobuf.InvalidProtocolBufferException e) { parsedMessage = (Payload) e.getUnfinishedMessage(); throw e.unwrapIOException(); } finally { if (parsedMessage != null) { mergeFrom(parsedMessage); } } return this; } private Metadata metadata_; private com.google.protobuf.SingleFieldBuilderV3< Metadata, Metadata.Builder, MetadataOrBuilder> metadataBuilder_; /** * .Metadata metadata = 2; */ public boolean hasMetadata() { return metadataBuilder_ != null || metadata_ != null; } /** * .Metadata metadata = 2; */ public Metadata getMetadata() { if (metadataBuilder_ == null) { return metadata_ == null ? Metadata.getDefaultInstance() : metadata_; } else { return metadataBuilder_.getMessage(); } } /** * .Metadata metadata = 2; */ public Builder setMetadata(Metadata value) { if (metadataBuilder_ == null) { if (value == null) { throw new NullPointerException(); } metadata_ = value; onChanged(); } else { metadataBuilder_.setMessage(value); } return this; } /** * .Metadata metadata = 2; */ public Builder setMetadata( Metadata.Builder builderForValue) { if (metadataBuilder_ == null) { metadata_ = builderForValue.build(); onChanged(); } else { metadataBuilder_.setMessage(builderForValue.build()); } return this; } /** * .Metadata metadata = 2; */ public Builder mergeMetadata(Metadata value) { if (metadataBuilder_ == null) { if (metadata_ != null) { metadata_ = Metadata.newBuilder(metadata_).mergeFrom(value).buildPartial(); } else { metadata_ = value; } onChanged(); } else { metadataBuilder_.mergeFrom(value); } return this; } /** * .Metadata metadata = 2; */ public Builder clearMetadata() { if (metadataBuilder_ == null) { metadata_ = null; onChanged(); } else { metadata_ = null; metadataBuilder_ = null; } return this; } /** * .Metadata metadata = 2; */ public Metadata.Builder getMetadataBuilder() { onChanged(); return getMetadataFieldBuilder().getBuilder(); } /** * .Metadata metadata = 2; */ public MetadataOrBuilder getMetadataOrBuilder() { if (metadataBuilder_ != null) { return metadataBuilder_.getMessageOrBuilder(); } else { return metadata_ == null ? Metadata.getDefaultInstance() : metadata_; } } /** * .Metadata metadata = 2; */ private com.google.protobuf.SingleFieldBuilderV3< Metadata, Metadata.Builder, MetadataOrBuilder> getMetadataFieldBuilder() { if (metadataBuilder_ == null) { metadataBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<>( getMetadata(), getParentForChildren(), isClean()); metadata_ = null; } return metadataBuilder_; } private com.google.protobuf.Any body_; private com.google.protobuf.SingleFieldBuilderV3< com.google.protobuf.Any, com.google.protobuf.Any.Builder, com.google.protobuf.AnyOrBuilder> bodyBuilder_; /** * .google.protobuf.Any body = 3; */ public boolean hasBody() { return bodyBuilder_ != null || body_ != null; } /** * .google.protobuf.Any body = 3; */ public com.google.protobuf.Any getBody() { if (bodyBuilder_ == null) { return body_ == null ? com.google.protobuf.Any.getDefaultInstance() : body_; } else { return bodyBuilder_.getMessage(); } } /** * .google.protobuf.Any body = 3; */ public Builder setBody(com.google.protobuf.Any value) { if (bodyBuilder_ == null) { if (value == null) { throw new NullPointerException(); } body_ = value; onChanged(); } else { bodyBuilder_.setMessage(value); } return this; } /** * .google.protobuf.Any body = 3; */ public Builder setBody( com.google.protobuf.Any.Builder builderForValue) { if (bodyBuilder_ == null) { body_ = builderForValue.build(); onChanged(); } else { bodyBuilder_.setMessage(builderForValue.build()); } return this; } /** * .google.protobuf.Any body = 3; */ public Builder mergeBody(com.google.protobuf.Any value) { if (bodyBuilder_ == null) { if (body_ != null) { body_ = com.google.protobuf.Any.newBuilder(body_).mergeFrom(value).buildPartial(); } else { body_ = value; } onChanged(); } else { bodyBuilder_.mergeFrom(value); } return this; } /** * .google.protobuf.Any body = 3; */ public Builder clearBody() { if (bodyBuilder_ == null) { body_ = null; onChanged(); } else { body_ = null; bodyBuilder_ = null; } return this; } /** * .google.protobuf.Any body = 3; */ public com.google.protobuf.Any.Builder getBodyBuilder() { onChanged(); return getBodyFieldBuilder().getBuilder(); } /** * .google.protobuf.Any body = 3; */ public com.google.protobuf.AnyOrBuilder getBodyOrBuilder() { if (bodyBuilder_ != null) { return bodyBuilder_.getMessageOrBuilder(); } else { return body_ == null ? com.google.protobuf.Any.getDefaultInstance() : body_; } } /** * .google.protobuf.Any body = 3; */ private com.google.protobuf.SingleFieldBuilderV3< com.google.protobuf.Any, com.google.protobuf.Any.Builder, com.google.protobuf.AnyOrBuilder> getBodyFieldBuilder() { if (bodyBuilder_ == null) { bodyBuilder_ = new com.google.protobuf.SingleFieldBuilderV3<>( getBody(), getParentForChildren(), isClean()); body_ = null; } return bodyBuilder_; } @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:Payload) } // @@protoc_insertion_point(class_scope:Payload) private static final Payload DEFAULT_INSTANCE; static { DEFAULT_INSTANCE = new Payload(); } public static Payload getDefaultInstance() { return DEFAULT_INSTANCE; } private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { @Override public Payload parsePartialFrom( com.google.protobuf.CodedInputStream input, com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws com.google.protobuf.InvalidProtocolBufferException { return new Payload(input, extensionRegistry); } }; public static com.google.protobuf.Parser parser() { return PARSER; } @Override public com.google.protobuf.Parser getParserForType() { return PARSER; } @Override public Payload getDefaultInstanceForType() { return DEFAULT_INSTANCE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/grpc/auto/PayloadOrBuilder.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.grpc.auto; public interface PayloadOrBuilder extends // @@protoc_insertion_point(interface_extends:Payload) com.google.protobuf.MessageOrBuilder { /** * .Metadata metadata = 2; */ boolean hasMetadata(); /** * .Metadata metadata = 2; */ Metadata getMetadata(); /** * .Metadata metadata = 2; */ MetadataOrBuilder getMetadataOrBuilder(); /** * .google.protobuf.Any body = 3; */ boolean hasBody(); /** * .google.protobuf.Any body = 3; */ com.google.protobuf.Any getBody(); /** * .google.protobuf.Any body = 3; */ com.google.protobuf.AnyOrBuilder getBodyOrBuilder(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/grpc/auto/RequestGrpc.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.grpc.auto; import io.grpc.ClientCall; import static io.grpc.MethodDescriptor.generateFullMethodName; import static io.grpc.stub.ClientCalls.asyncUnaryCall; import static io.grpc.stub.ClientCalls.blockingUnaryCall; import static io.grpc.stub.ClientCalls.futureUnaryCall; import static io.grpc.stub.ServerCalls.asyncUnaryCall; import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall; /** * */ @javax.annotation.Generated( value = "by gRPC proto compiler (version 1.14.0)", comments = "Source: nacos_grpc_service.proto") public final class RequestGrpc { private RequestGrpc() {} public static final String SERVICE_NAME = "Request"; // Static method descriptors that strictly reflect the proto. private static volatile io.grpc.MethodDescriptor getRequestMethod; @io.grpc.stub.annotations.RpcMethod( fullMethodName = SERVICE_NAME + '/' + "request", requestType = com.alibaba.nacos.api.grpc.auto.Payload.class, responseType = com.alibaba.nacos.api.grpc.auto.Payload.class, methodType = io.grpc.MethodDescriptor.MethodType.UNARY) public static io.grpc.MethodDescriptor getRequestMethod() { io.grpc.MethodDescriptor getRequestMethod; if ((getRequestMethod = RequestGrpc.getRequestMethod) == null) { synchronized (RequestGrpc.class) { if ((getRequestMethod = RequestGrpc.getRequestMethod) == null) { RequestGrpc.getRequestMethod = getRequestMethod = io.grpc.MethodDescriptor.newBuilder() .setType(io.grpc.MethodDescriptor.MethodType.UNARY) .setFullMethodName(generateFullMethodName( "Request", "request")) .setSampledToLocalTracing(true) .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( com.alibaba.nacos.api.grpc.auto.Payload.getDefaultInstance())) .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( com.alibaba.nacos.api.grpc.auto.Payload.getDefaultInstance())) .setSchemaDescriptor(new RequestMethodDescriptorSupplier("request")) .build(); } } } return getRequestMethod; } /** * Creates a new async stub that supports all call types for the service */ public static RequestStub newStub(io.grpc.Channel channel) { return new RequestStub(channel); } /** * Creates a new blocking-style stub that supports unary and streaming output calls on the service */ public static RequestBlockingStub newBlockingStub( io.grpc.Channel channel) { return new RequestBlockingStub(channel); } /** * Creates a new ListenableFuture-style stub that supports unary calls on the service */ public static RequestFutureStub newFutureStub( io.grpc.Channel channel) { return new RequestFutureStub(channel); } /** */ public static abstract class RequestImplBase implements io.grpc.BindableService { /** *
         * Sends a commonRequest
         * 
    */ public void request(com.alibaba.nacos.api.grpc.auto.Payload request, io.grpc.stub.StreamObserver responseObserver) { asyncUnimplementedUnaryCall(getRequestMethod(), responseObserver); } @Override public final io.grpc.ServerServiceDefinition bindService() { return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) .addMethod( getRequestMethod(), asyncUnaryCall( new MethodHandlers< com.alibaba.nacos.api.grpc.auto.Payload, com.alibaba.nacos.api.grpc.auto.Payload>( this, METHODID_REQUEST))) .build(); } } /** */ public static final class RequestStub extends io.grpc.stub.AbstractStub { private RequestStub(io.grpc.Channel channel) { super(channel); } private RequestStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { super(channel, callOptions); } @Override protected RequestStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { return new RequestStub(channel, callOptions); } /** *
         * Sends a commonRequest
         * 
    */ public void request(com.alibaba.nacos.api.grpc.auto.Payload request, io.grpc.stub.StreamObserver responseObserver) { asyncUnaryCall( getChannel().newCall(getRequestMethod(), getCallOptions()), request, responseObserver); } } /** */ public static final class RequestBlockingStub extends io.grpc.stub.AbstractStub { private RequestBlockingStub(io.grpc.Channel channel) { super(channel); } private RequestBlockingStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { super(channel, callOptions); } @Override protected RequestBlockingStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { return new RequestBlockingStub(channel, callOptions); } /** *
         * Sends a commonRequest
         * 
    */ public com.alibaba.nacos.api.grpc.auto.Payload request(com.alibaba.nacos.api.grpc.auto.Payload request) { return blockingUnaryCall( getChannel(), getRequestMethod(), getCallOptions(), request); } } /** */ public static final class RequestFutureStub extends io.grpc.stub.AbstractStub { private RequestFutureStub(io.grpc.Channel channel) { super(channel); } private RequestFutureStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { super(channel, callOptions); } @Override protected RequestFutureStub build(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { return new RequestFutureStub(channel, callOptions); } /** *
         * Sends a commonRequest
         * 
    */ public com.google.common.util.concurrent.ListenableFuture request( com.alibaba.nacos.api.grpc.auto.Payload request) { return futureUnaryCall(getChannel().newCall(getRequestMethod(), getCallOptions()), request); } } private static final int METHODID_REQUEST = 0; private static final class MethodHandlers implements io.grpc.stub.ServerCalls.UnaryMethod, io.grpc.stub.ServerCalls.ServerStreamingMethod, io.grpc.stub.ServerCalls.ClientStreamingMethod, io.grpc.stub.ServerCalls.BidiStreamingMethod { private final RequestImplBase serviceImpl; private final int methodId; MethodHandlers(RequestImplBase serviceImpl, int methodId) { this.serviceImpl = serviceImpl; this.methodId = methodId; } @Override @SuppressWarnings("unchecked") public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { switch (methodId) { case METHODID_REQUEST: serviceImpl.request((com.alibaba.nacos.api.grpc.auto.Payload) request, (io.grpc.stub.StreamObserver) responseObserver); break; default: throw new AssertionError(); } } @Override @SuppressWarnings("unchecked") public io.grpc.stub.StreamObserver invoke( io.grpc.stub.StreamObserver responseObserver) { switch (methodId) { default: throw new AssertionError(); } } } private static abstract class RequestBaseDescriptorSupplier implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { RequestBaseDescriptorSupplier() {} @Override public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { return com.alibaba.nacos.api.grpc.auto.NacosGrpcService.getDescriptor(); } @Override public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { return getFileDescriptor().findServiceByName("Request"); } } private static final class RequestFileDescriptorSupplier extends RequestBaseDescriptorSupplier { RequestFileDescriptorSupplier() {} } private static final class RequestMethodDescriptorSupplier extends RequestBaseDescriptorSupplier implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { private final String methodName; RequestMethodDescriptorSupplier(String methodName) { this.methodName = methodName; } @Override public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { return getServiceDescriptor().findMethodByName(methodName); } } private static volatile io.grpc.ServiceDescriptor serviceDescriptor; public static io.grpc.ServiceDescriptor getServiceDescriptor() { io.grpc.ServiceDescriptor result = serviceDescriptor; if (result == null) { synchronized (RequestGrpc.class) { result = serviceDescriptor; if (result == null) { serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) .setSchemaDescriptor(new RequestFileDescriptorSupplier()) .addMethod(getRequestMethod()) .build(); } } } return result; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/LockService.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.model.LockInstance; /** * Nacos Lock Process. * *

    lock => {@link LockService#lock(LockInstance)} -> {@link LockInstance#lock(LockService)} -> * {@link LockService#remoteTryLock(LockInstance)}
    unLock => {@link LockService#unLock(LockInstance)} -> * {@link LockInstance#unLock(LockService)} -> {@link LockService#remoteReleaseLock(LockInstance)} * * @author 985492783@qq.com * @date 2023/8/24 19:49 */ public interface LockService { /** * Real lock method expose to user to acquire the lock.
    It will call {@link LockInstance#lock(LockService)} *
    * * @param instance instance * @return Boolean * @throws NacosException NacosException */ Boolean lock(LockInstance instance) throws NacosException; /** * Real lock method expose to user to release the lock.
    It will call {@link LockInstance#unLock(LockService)} *
    * * @param instance instance * @return Boolean * @throws NacosException NacosException */ Boolean unLock(LockInstance instance) throws NacosException; /** * use grpc request to try lock. * * @param instance instance * @return Boolean * @throws NacosException NacosException */ Boolean remoteTryLock(LockInstance instance) throws NacosException; /** * use grpc request to release lock. * * @param instance instance * @return Boolean * @throws NacosException NacosException */ Boolean remoteReleaseLock(LockInstance instance) throws NacosException; /** * Shutdown the Resources, such as Thread Pool. * * @throws NacosException exception. */ void shutdown() throws NacosException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/NacosLockFactory.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock; import com.alibaba.nacos.api.exception.NacosException; import java.lang.reflect.Constructor; import java.util.Properties; /** * lock Factory. * * @author 985492783@qq.com * @date 2023/8/25 0:40 */ public class NacosLockFactory { /** * Create a new lock service. * * @param properties lock service properties * @return new lock service * @throws NacosException nacos exception */ public static LockService createLockService(Properties properties) throws NacosException { try { Class driverImplClass = Class.forName("com.alibaba.nacos.client.lock.NacosLockService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); return (LockService) constructor.newInstance(properties); } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/common/LockConstants.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.common; /** * lock constant. * * @author 985492783@qq.com * @date 2023/8/23 15:53 */ public class LockConstants { public static final String NACOS_LOCK_TYPE = "NACOS_LOCK"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/constant/PropertyConstants.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.constant; /** * lock properties constants. * @author 985492783@qq.com * @description PropertyConstants * @date 2023/6/28 17:38 */ public class PropertyConstants { public static final String LOCK_REQUEST_TIMEOUT = "lockRequestTimeout"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/model/LockInstance.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.model; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.LockService; import java.io.Serializable; import java.util.Map; /** * lock info entity. * * @author 985492783@qq.com * @date 2023/6/28 2:46 */ public class LockInstance implements Serializable { private static final long serialVersionUID = -3460985546826875524L; private String key; private Long expiredTime; private Map params; private String lockType; public LockInstance(String key, Long expiredTime, String lockType) { this.key = key; this.expiredTime = expiredTime; this.lockType = lockType; } public LockInstance() { } public Long getExpiredTime() { return expiredTime; } public void setExpiredTime(Long expiredTime) { this.expiredTime = expiredTime; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } public Map getParams() { return params; } public void setParams(Map params) { this.params = params; } /** * Will call {@link LockService#remoteTryLock(LockInstance)} request grpc to get lock and do something.
    can be * {@link Override} to do some client special logic. * * @param lockService {@link LockService} * @return Boolean {@link Boolean} * @throws NacosException NacosException */ public Boolean lock(LockService lockService) throws NacosException { return lockService.remoteTryLock(this); } /** * Will call {@link LockService#remoteReleaseLock(LockInstance)} request grpc to release lock and do something.
    * can be {@link Override} to do some client special logic. * * @param lockService {@link LockService} * @return Boolean {@link Boolean} * @throws NacosException NacosException */ public Boolean unLock(LockService lockService) throws NacosException { return lockService.remoteReleaseLock(this); } /** * spi get lock type. * * @return type */ public String getLockType() { return lockType; } public void setLockType(String lockType) { this.lockType = lockType; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/remote/AbstractLockRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.remote; import com.alibaba.nacos.api.remote.request.Request; import static com.alibaba.nacos.api.common.Constants.Lock.LOCK_MODULE; /** * lock grpc request. * * @author 985492783@qq.com * @description LockRequest * @date 2023/6/29 12:00 */ public abstract class AbstractLockRequest extends Request { @Override public String getModule() { return LOCK_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/remote/LockOperationEnum.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.remote; import java.io.Serializable; /** * lock operation. * @author 985492783@qq.com */ public enum LockOperationEnum implements Serializable { /** * Acquire. */ ACQUIRE, /** * Release. */ RELEASE, /** * Expire. */ EXPIRE; private static final long serialVersionUID = -241044344531890549L; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/remote/request/LockOperationRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.remote.request; import com.alibaba.nacos.api.lock.model.LockInstance; import com.alibaba.nacos.api.lock.remote.AbstractLockRequest; import com.alibaba.nacos.api.lock.remote.LockOperationEnum; /** * grpc acquire lock request. * * @author 985492783@qq.com * @description AcquireLockRequest * @date 2023/6/29 12:01 */ public class LockOperationRequest extends AbstractLockRequest { private LockInstance lockInstance; private LockOperationEnum lockOperationEnum; public LockInstance getLockInstance() { return lockInstance; } public void setLockInstance(LockInstance lockInstance) { this.lockInstance = lockInstance; } public LockOperationEnum getLockOperationEnum() { return lockOperationEnum; } public void setLockOperationEnum(LockOperationEnum lockOperationEnum) { this.lockOperationEnum = lockOperationEnum; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/lock/remote/response/LockOperationResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.remote.response; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; /** * grpc acquire lock response. * * @author 985492783@qq.com * @description AcquireLockResponse * @date 2023/6/29 13:51 */ public class LockOperationResponse extends Response { private Object result; public LockOperationResponse() { } public LockOperationResponse(Boolean result) { this.result = result; } /** * create success response. * @param result result * @return LockOperationResponse */ public static LockOperationResponse success(Boolean result) { LockOperationResponse response = new LockOperationResponse(result); return response; } /** * create fail response. * @param message message * @return LockOperationResponse */ public static LockOperationResponse fail(String message) { LockOperationResponse response = new LockOperationResponse(false); response.setResultCode(ResponseCode.FAIL.getCode()); response.setMessage(message); return response; } public Object getResult() { return result; } public void setResult(Object result) { this.result = result; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/NacosForm.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model; import com.alibaba.nacos.api.exception.api.NacosApiException; import java.io.Serializable; /** * Nacos HTTP Form API Object. * * @author xiweng.yy */ public interface NacosForm extends Serializable { /** * check form parameters while valid. * * @throws NacosApiException when form parameters is invalid. */ void validate() throws NacosApiException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/Page.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model; import java.io.Serializable; import java.util.ArrayList; import java.util.List; /** * Page. * * @author boyan * @date 2010-5-6 */ public class Page implements Serializable { static final long serialVersionUID = 1234544030560484292L; /** * totalCount. */ private int totalCount; /** * pageNumber. */ private int pageNumber; /** * pagesAvailable. */ private int pagesAvailable; /** * pageItems. */ private List pageItems = new ArrayList<>(); public void setPageNumber(int pageNumber) { this.pageNumber = pageNumber; } public void setPagesAvailable(int pagesAvailable) { this.pagesAvailable = pagesAvailable; } public void setPageItems(List pageItems) { this.pageItems = pageItems; } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount = totalCount; } public int getPageNumber() { return pageNumber; } public int getPagesAvailable() { return pagesAvailable; } public List getPageItems() { return pageItems; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/response/ConnectionInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import java.util.Map; /** * Nacos client connection information. * * @author Nacos */ public class ConnectionInfo { private boolean traced = false; private Map abilityTable; private ConnectionMetaInfo metaInfo; public boolean isTraced() { return traced; } public void setTraced(boolean traced) { this.traced = traced; } public void setAbilityTable(Map abilityTable) { this.abilityTable = abilityTable; } public Map getAbilityTable() { return this.abilityTable; } public ConnectionMetaInfo getMetaInfo() { return metaInfo; } public void setMetaInfo(ConnectionMetaInfo metaInfo) { this.metaInfo = metaInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/response/ConnectionMetaInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * Nacos client connection metadata information. * * @author Nacos */ public class ConnectionMetaInfo { String connectType; String clientIp; String remoteIp; int remotePort; int localPort; String version; String connectionId; Date createTime; long lastActiveTime; String appName; String namespaceId; private Map labels = new HashMap<>(); public Map getLabels() { return labels; } public void setLabels(Map labels) { this.labels = labels; } public String getClientIp() { return clientIp; } public void setClientIp(String clientIp) { this.clientIp = clientIp; } public String getRemoteIp() { return remoteIp; } public void setRemoteIp(String remoteIp) { this.remoteIp = remoteIp; } public int getRemotePort() { return remotePort; } public void setRemotePort(int remotePort) { this.remotePort = remotePort; } public String getConnectionId() { return connectionId; } public void setConnectionId(String connectionId) { this.connectionId = connectionId; } public Date getCreateTime() { return createTime; } public void setCreateTime(Date createTime) { this.createTime = createTime; } public long getLastActiveTime() { return lastActiveTime; } public void setLastActiveTime(long lastActiveTime) { this.lastActiveTime = lastActiveTime; } public String getConnectType() { return connectType; } public void setConnectType(String connectType) { this.connectType = connectType; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public int getLocalPort() { return localPort; } public void setLocalPort(int localPort) { this.localPort = localPort; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/response/IdGeneratorInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; /** * Id generator vo. * * @author wuzhiguo */ public class IdGeneratorInfo { private String resource; private IdInfo info; public String getResource() { return resource; } public void setResource(String resource) { this.resource = resource; } public IdInfo getInfo() { return info; } public void setInfo(IdInfo info) { this.info = info; } public static class IdInfo { private Long currentId; private Long workerId; public Long getCurrentId() { return currentId; } public void setCurrentId(Long currentId) { this.currentId = currentId; } public Long getWorkerId() { return workerId; } public void setWorkerId(Long workerId) { this.workerId = workerId; } @Override public String toString() { return "IdInfo{" + "currentId=" + currentId + ", workerId=" + workerId + '}'; } } @Override public String toString() { return "IdGeneratorVO{" + "resource='" + resource + '\'' + ", info=" + info + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/response/NacosMember.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import com.alibaba.nacos.api.ability.ServerAbilities; import com.alibaba.nacos.api.common.NodeState; import com.alibaba.nacos.api.utils.StringUtils; import java.io.Serializable; import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.TreeMap; /** * Nacos server member information. * * @author xiweng.yy */ public class NacosMember implements Serializable { private static final long serialVersionUID = 6295022126554026016L; private String ip; private int port = -1; private volatile NodeState state = NodeState.UP; private Map extendInfo = Collections.synchronizedMap(new TreeMap<>()); private String address = ""; @Deprecated private ServerAbilities abilities = new ServerAbilities(); public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; this.address = ip + ":" + port; } public int getPort() { return port; } public void setPort(int port) { this.port = port; this.address = ip + ":" + port; } public NodeState getState() { return state; } public void setState(NodeState state) { this.state = state; } public Map getExtendInfo() { return extendInfo; } public void setExtendInfo(Map extendInfo) { this.extendInfo = extendInfo; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public ServerAbilities getAbilities() { return abilities; } public void setAbilities(ServerAbilities abilities) { this.abilities = abilities; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } NacosMember that = (NacosMember) o; return port == that.port && StringUtils.equals(ip, that.ip); } @Override public String toString() { return "Member{" + "ip='" + ip + '\'' + ", port=" + port + ", state=" + state + ", extendInfo=" + extendInfo + '}'; } @Override public int hashCode() { return Objects.hash(ip, port); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/response/Namespace.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; /** * Namespace. * * @author diamond */ public class Namespace { private String namespace; private String namespaceShowName; private String namespaceDesc; private int quota; private int configCount; private int type; public String getNamespaceShowName() { return namespaceShowName; } public void setNamespaceShowName(String namespaceShowName) { this.namespaceShowName = namespaceShowName; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public Namespace() { } public Namespace(String namespace, String namespaceShowName) { this.namespace = namespace; this.namespaceShowName = namespaceShowName; } public Namespace(String namespace, String namespaceShowName, int quota, int configCount, int type) { this.namespace = namespace; this.namespaceShowName = namespaceShowName; this.quota = quota; this.configCount = configCount; this.type = type; } public Namespace(String namespace, String namespaceShowName, String namespaceDesc, int quota, int configCount, int type) { this.namespace = namespace; this.namespaceShowName = namespaceShowName; this.quota = quota; this.configCount = configCount; this.type = type; this.namespaceDesc = namespaceDesc; } public String getNamespaceDesc() { return namespaceDesc; } public void setNamespaceDesc(String namespaceDesc) { this.namespaceDesc = namespaceDesc; } public int getQuota() { return quota; } public void setQuota(int quota) { this.quota = quota; } public int getConfigCount() { return configCount; } public void setConfigCount(int configCount) { this.configCount = configCount; } public int getType() { return type; } public void setType(int type) { this.type = type; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/response/ServerLoaderMetric.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import com.alibaba.nacos.api.utils.StringUtils; import java.util.Map; /** * Server loader metric. * * @author yunye * @since 3.0.0-beta */ public class ServerLoaderMetric { private String address; private int sdkConCount; private int conCount; private String load; private String cpu; public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public int getSdkConCount() { return sdkConCount; } public void setSdkConCount(int sdkConCount) { this.sdkConCount = sdkConCount; } public int getConCount() { return conCount; } public void setConCount(int conCount) { this.conCount = conCount; } public String getLoad() { return load; } public void setLoad(String load) { this.load = load; } public String getCpu() { return cpu; } public void setCpu(String cpu) { this.cpu = cpu; } public static class Builder { private ServerLoaderMetric serverLoaderMetric = new ServerLoaderMetric(); public static Builder newBuilder() { return new Builder(); } public ServerLoaderMetric build() { return serverLoaderMetric; } public Builder withAddress(String address) { serverLoaderMetric.setAddress(address); return this; } /** * convert map to {@link ServerLoaderMetric}. * * @param metric map of server loader metric * @return builder */ public Builder convertFromMap(Map metric) { serverLoaderMetric.setSdkConCount(convertInt(metric, "sdkConCount", 0)); serverLoaderMetric.setConCount(convertInt(metric, "conCount", 0)); serverLoaderMetric.setLoad(metric.get("load")); serverLoaderMetric.setCpu(metric.get("cpu")); return this; } private int convertInt(Map metric, String key, int defaultValue) { String value = metric.get(key); if (!StringUtils.isBlank(value)) { return Integer.parseInt(value); } return defaultValue; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/response/ServerLoaderMetrics.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import java.util.List; /** * Server loader metric summary. * * @author yunye * @since 3.0.0 */ public class ServerLoaderMetrics { /** * load metric of all servers. */ private List detail; /** * The number of all server nodes. */ private int memberCount; /** * The number of server nodes with load metric data. */ private int metricsCount; /** * Whether all server nodes return load indicators. */ private boolean completed; /** * The maximum number of SDK connections for the server node. */ private int max; /** * The minimum number of SDK connections for the server node. */ private int min; /** * total / server size. */ private int avg; /** * (total / server size) * 1.1 . */ private String threshold; /** * The total number of SDK connections for all server nodes. */ private int total; public List getDetail() { return detail; } public void setDetail(List detail) { this.detail = detail; } public int getMemberCount() { return memberCount; } public void setMemberCount(int memberCount) { this.memberCount = memberCount; } public int getMetricsCount() { return metricsCount; } public void setMetricsCount(int metricsCount) { this.metricsCount = metricsCount; } public boolean isCompleted() { return completed; } public void setCompleted(boolean completed) { this.completed = completed; } public int getMax() { return max; } public void setMax(int max) { this.max = max; } public int getMin() { return min; } public void setMin(int min) { this.min = min; } public int getAvg() { return avg; } public void setAvg(int avg) { this.avg = avg; } public String getThreshold() { return threshold; } public void setThreshold(String threshold) { this.threshold = threshold; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/v2/ErrorCode.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.v2; /** * Response Error Code. * * @author dongyafei * @date 2022/7/22 */ public enum ErrorCode { /** * success. */ SUCCESS(0, "success"), /** * parameter missing. */ PARAMETER_MISSING(10000, "parameter missing"), /** * access denied. */ ACCESS_DENIED(10001, "access denied"), /** * data access error. */ DATA_ACCESS_ERROR(10002, "data access error"), /** * 'tenant' parameter error. */ TENANT_PARAM_ERROR(20001, "'tenant' parameter error"), /** * parameter validate error. */ PARAMETER_VALIDATE_ERROR(20002, "parameter validate error"), /** * MediaType Error. */ MEDIA_TYPE_ERROR(20003, "MediaType Error"), /** * resource not found. */ RESOURCE_NOT_FOUND(20004, "resource not found"), /** * resource conflict. */ RESOURCE_CONFLICT(20005, "resource conflict"), /** * config listener is null. */ CONFIG_LISTENER_IS_NULL(20006, "config listener is null"), /** * config listener error. */ CONFIG_LISTENER_ERROR(20007, "config listener error"), /** * invalid dataId. */ INVALID_DATA_ID(20008, "invalid dataId"), /** * parameter mismatch. */ PARAMETER_MISMATCH(20009, "parameter mismatch"), /** * config gray request error. */ CONFIG_GRAY_OVER_MAX_VERSION_COUNT(20010, "config gray version version over max count"), /** * config gray tag v2 rule format invalid. */ CONFIG_GRAY_RULE_FORMAT_INVALID(20011, "config gray rule format invalid"), /** * config gray tag v2 rule version invalid. */ CONFIG_GRAY_VERSION_INVALID(20012, "config gray rule version invalid"), /** * config gray request error. */ CONFIG_GRAY_NAME_UNRECOGNIZED_ERROR(20013, "config gray name not recognized"), /** * reach cluster quota. */ OVER_CLUSTER_QUOTA(5031, "cluster capacity reach quota"), /** * reach group quota. */ OVER_GROUP_QUOTA(5032, "group capacity reach quota"), /** * reach tenant quota. */ OVER_TENANT_QUOTA(5033, "tenant capacity reach quota"), /** * over max content size. */ OVER_MAX_SIZE(5034, "config content size is over limit"), /** * service name error. */ SERVICE_NAME_ERROR(21000, "service name error"), /** * weight error. */ WEIGHT_ERROR(21001, "weight error"), /** * instance metadata error. */ INSTANCE_METADATA_ERROR(21002, "instance metadata error"), /** * instance not found. */ INSTANCE_NOT_FOUND(21003, "instance not found"), /** * instance error. */ INSTANCE_ERROR(21004, "instance error"), /** * service metadata error. */ SERVICE_METADATA_ERROR(21005, "service metadata error"), /** * selector error. */ SELECTOR_ERROR(21006, "selector error"), /** * service already exist. */ SERVICE_ALREADY_EXIST(21007, "service already exist"), /** * service not exist. */ SERVICE_NOT_EXIST(21008, "service not exist"), /** * service delete failure. */ SERVICE_DELETE_FAILURE(21009, "service delete failure"), /** * healthy param miss. */ HEALTHY_PARAM_MISS(21010, "healthy param miss"), /** * health check still running. */ HEALTH_CHECK_STILL_RUNNING(21011, "health check still running"), /** * illegal namespace. */ ILLEGAL_NAMESPACE(22000, "illegal namespace"), /** * namespace not exist. */ NAMESPACE_NOT_EXIST(22001, "namespace not exist"), /** * namespace already exist. */ NAMESPACE_ALREADY_EXIST(22002, "namespace already exist"), /** * illegal state. */ ILLEGAL_STATE(23000, "illegal state"), /** * node info error. */ NODE_INFO_ERROR(23001, "node info error"), /** * node down failure. */ NODE_DOWN_FAILURE(23002, "node down failure"), /** * server error. */ SERVER_ERROR(30000, "server error"), /** * API will be deprecated. */ API_DEPRECATED(40000, "API deprecated."), /** * The `functionMode` of API is disabled. */ API_FUNCTION_DISABLED(40001, "API function disabled."), /** * MCP Server not found any version. */ MCP_SERVER_NOT_FOUND(50000, "MCP server not found"), /** * MCP Server target version not found. */ MCP_SEVER_VERSION_NOT_FOUND(50001, "MCP server version not found"), MCP_SERVER_VERSION_EXIST(50002, "MCP server version has existed"), MCP_SERVER_REF_ENDPOINT_SERVICE_NOT_FOUND(50003, "MCP server ref endpoint service not found"), AGENT_NOT_FOUND(50100, "Agent not found"), AGENT_VERSION_NOT_FOUND(50101, "Agent version not found"), AGENT_VERSION_EXIST(50102, "Agent version already existed"), /** * Config use 100001 ~ 100999. **/ METADATA_ILLEGAL(100002, "Imported metadata is invalid"), DATA_VALIDATION_FAILED(100003, "No valid data was read"), PARSING_DATA_FAILED(100004, "Failed to parse data"), DATA_EMPTY(100005, "Imported file data is empty"), NO_SELECTED_CONFIG(100006, "No configuration selected"), FUZZY_WATCH_PATTERN_OVER_LIMIT(50310, "fuzzy watch pattern over limit"), FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT(50311, "fuzzy watch pattern matched count over limit"); private final Integer code; private final String msg; public Integer getCode() { return code; } public String getMsg() { return msg; } public static ErrorCode getErrorCode(String name) { for (ErrorCode errorCode : ErrorCode.values()) { if (errorCode.name().equals(name)) { return errorCode; } } return null; } ErrorCode(Integer code, String msg) { this.code = code; this.msg = msg; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/v2/Result.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.v2; import java.io.Serializable; /** * Response Result. * * @author dongyafei * @date 2022/7/12 */ public class Result implements Serializable { private static final long serialVersionUID = 6258345442767540526L; private final Integer code; private final String message; private final T data; public Result(Integer code, String message, T data) { this.code = code; this.message = message; this.data = data; } public Result() { this(null); } public Result(T data) { this(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), data); } public Result(Integer code, String message) { this(code, message, null); } /** * Success return with nothing. * @param data type * @return Result */ public static Result success() { return new Result<>(); } /** * Success return with data. * @param data type * @return Result */ public static Result success(T data) { return new Result<>(data); } /** * Failed return with message and detail error information. * @return Result */ public static Result failure(String message) { return new Result<>(ErrorCode.SERVER_ERROR.getCode(), message); } /** * Failed return with errorCode and message. * @param data type * @return Result */ public static Result failure(ErrorCode errorCode) { return new Result<>(errorCode.getCode(), errorCode.getMsg()); } /** * Failed return with errorCode, message and data. * @param data type * @return Result */ public static Result failure(ErrorCode errorCode, T data) { return new Result<>(errorCode.getCode(), errorCode.getMsg(), data); } /** * Failed return with code, message and data. * @param data type * @param code error code * @param msg error message * @return Result */ public static Result failure(Integer code, String msg, T data) { return new Result<>(code, msg, data); } @Override public String toString() { return "Result{" + "errorCode=" + code + ", message='" + message + '\'' + ", data=" + data + '}'; } public Integer getCode() { return code; } public String getMessage() { return message; } public T getData() { return data; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/model/v2/SupportedLanguage.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.v2; /** * Supported languages for announcements. * * @author zhangyukun on:2024/9/24 */ public enum SupportedLanguage { /** * Chinese language. */ ZH_CN("zh-CN"), /** * English language. */ EN_US("en-US"); private final String language; SupportedLanguage(String language) { this.language = language; } public String getLanguage() { return language; } /** * Check if the given language is supported. * * @param language the language to check * @return true if the language is supported, false otherwise */ public static boolean isSupported(String language) { for (SupportedLanguage lang : SupportedLanguage.values()) { if (lang.getLanguage().equals(language)) { return true; } } return false; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/CommonParams.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; /** * Common parameters for service discovery. * * @author nkorange * @since 1.0.0 */ public class CommonParams { public static final String CODE = "code"; public static final String SERVICE_NAME = "serviceName"; public static final String CLUSTER_NAME = "clusterName"; public static final String NAMESPACE_ID = "namespaceId"; public static final String GROUP_NAME = "groupName"; public static final String LIGHT_BEAT_ENABLED = "lightBeatEnabled"; public static final String NAMING_REQUEST_TIMEOUT = "namingRequestTimeout"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/NamingFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; import java.lang.reflect.Constructor; import java.util.Properties; /** * Naming Factory. * * @author nkorange */ public class NamingFactory { /** * Create a new naming service. * * @param serverList server list * @return new naming service * @throws NacosException nacos exception */ public static NamingService createNamingService(String serverList) throws NacosException { try { Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService"); Constructor constructor = driverImplClass.getConstructor(String.class); return (NamingService) constructor.newInstance(serverList); } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } } /** * Create a new naming service. * * @param properties naming service properties * @return new naming service * @throws NacosException nacos exception */ public static NamingService createNamingService(Properties properties) throws NacosException { try { Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); return (NamingService) constructor.newInstance(properties); } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/NamingMaintainFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; import java.lang.reflect.Constructor; import java.util.Properties; /** * Maintain service factory. * * @author liaochuntao * @since 1.0.1 * @deprecated use {@link com.alibaba.nacos.maintainer.client.naming.NamingMaintainerFactory} in nacos-maintainer-client artifact tp replaced. */ @Deprecated public class NamingMaintainFactory { /** * create a new maintain service. * * @param serverList server list * @return new maintain service * @throws NacosException nacos exception */ public static NamingMaintainService createMaintainService(String serverList) throws NacosException { try { Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingMaintainService"); Constructor constructor = driverImplClass.getConstructor(String.class); return (NamingMaintainService) constructor.newInstance(serverList); } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } } /** * create a new maintain service. * * @param properties properties * @return new maintain service * @throws NacosException nacos exception */ public static NamingMaintainService createMaintainService(Properties properties) throws NacosException { try { Class driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingMaintainService"); Constructor constructor = driverImplClass.getConstructor(Properties.class); return (NamingMaintainService) constructor.newInstance(properties); } catch (Throwable e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/NamingMaintainService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.selector.AbstractSelector; import java.util.Map; /** * Operations related to Nacos. * * @author liaochuntao * @since 1.0.1 * @deprecated After 3.3.0, Use {@link com.alibaba.nacos.maintainer.client.naming.NamingMaintainerService} in * nacos-maintainer-client artifact tp replaced. */ @Deprecated public interface NamingMaintainService { /** * update instance info. * * @param serviceName service name * @param instance instance * @throws NacosException nacos exception */ void updateInstance(String serviceName, Instance instance) throws NacosException; /** * update instance info. * * @param serviceName service name * @param groupName group name * @param instance instance * @throws NacosException nacos exception */ void updateInstance(String serviceName, String groupName, Instance instance) throws NacosException; /** * query service. * * @param serviceName service name * @return service * @throws NacosException nacos exception */ Service queryService(String serviceName) throws NacosException; /** * query service. * * @param serviceName service name * @param groupName group name * @return service * @throws NacosException nacos exception */ Service queryService(String serviceName, String groupName) throws NacosException; /** * create service to Nacos. * * @param serviceName name of service * @throws NacosException nacos exception */ void createService(String serviceName) throws NacosException; /** * create service to Nacos. * * @param serviceName name of service * @param groupName group of service * @throws NacosException nacos exception */ void createService(String serviceName, String groupName) throws NacosException; /** * create service to Nacos. * * @param serviceName name of service * @param groupName group of service * @param protectThreshold protectThreshold of service * @throws NacosException nacos exception */ void createService(String serviceName, String groupName, float protectThreshold) throws NacosException; /** * create service to Nacos. * * @param serviceName name of service * @param groupName group of service * @param protectThreshold protectThreshold of service * @param expression expression of selector * @throws NacosException nacos exception */ void createService(String serviceName, String groupName, float protectThreshold, String expression) throws NacosException; /** * create service to Nacos. * * @param service name of service * @param selector selector * @throws NacosException nacos exception */ void createService(Service service, AbstractSelector selector) throws NacosException; /** * delete service from Nacos. * * @param serviceName name of service * @return if delete service success return true * @throws NacosException nacos exception */ boolean deleteService(String serviceName) throws NacosException; /** * delete service from Nacos. * * @param serviceName name of service * @param groupName group of service * @return if delete service success return true * @throws NacosException nacos exception */ boolean deleteService(String serviceName, String groupName) throws NacosException; /** * update service to Nacos. * * @param serviceName name of service * @param groupName group of service * @param protectThreshold protectThreshold of service * @throws NacosException nacos exception */ void updateService(String serviceName, String groupName, float protectThreshold) throws NacosException; /** * update service to Nacos. * * @param serviceName name of service * @param groupName group of service * @param protectThreshold protectThreshold of service * @param metadata metadata of service * @throws NacosException nacos exception */ void updateService(String serviceName, String groupName, float protectThreshold, Map metadata) throws NacosException; /** * update service to Nacos with selector. * * @param service {@link Service} pojo of service * @param selector {@link AbstractSelector} pojo of selector * @throws NacosException nacos exception */ void updateService(Service service, AbstractSelector selector) throws NacosException; /** * Shutdown the resource service. * * @throws NacosException exception. */ void shutDown() throws NacosException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/NamingResponseCode.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.common.ResponseCode; /** * Business response code of naming module * *

    Every code stays between 20001 and 29999. * * @author nkorange * @author 1.2.0 */ public class NamingResponseCode extends ResponseCode { /** * The requested resource is not found. */ public static final int RESOURCE_NOT_FOUND = 20404; /** * Stop or no need to retry. */ public static final int NO_NEED_RETRY = 21600; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/NamingService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; 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; import java.util.List; import java.util.concurrent.Future; /** * Naming Service. * * @author nkorange */ public interface NamingService { /** * register an instance to service. * * @param serviceName name of service * @param ip instance ip * @param port instance port * @throws NacosException nacos exception */ void registerInstance(String serviceName, String ip, int port) throws NacosException; /** * register an instance to service. * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @throws NacosException nacos exception */ void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException; /** * register an instance to service with specified cluster name. * * @param serviceName name of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throws NacosException nacos exception */ void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; /** * register an instance to service with specified cluster name. * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throws NacosException nacos exception */ void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException; /** * register an instance to service with specified instance properties. * * @param serviceName name of service * @param instance instance to register * @throws NacosException nacos exception */ void registerInstance(String serviceName, Instance instance) throws NacosException; /** * register an instance to service with specified instance properties. * * @param serviceName name of service * @param groupName group of service * @param instance instance to register * @throws NacosException nacos exception */ void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException; /** * batch register instance to service with specified instance properties. * * @param serviceName name of service * @param groupName group of service * @param instances instances to register * @throws NacosException nacos exception * @since 2.1.1 */ void batchRegisterInstance(String serviceName, String groupName, List instances) throws NacosException; /** * batch deRegister instance to service with specified instance properties. * * @param serviceName name of service * @param groupName group of service * @param instances instances to deRegister * @throws NacosException nacos exception * @since 2.2.0 */ void batchDeregisterInstance(String serviceName, String groupName, List instances) throws NacosException; /** * deregister instance from a service. * * @param serviceName name of service * @param ip instance ip * @param port instance port * @throws NacosException nacos exception */ void deregisterInstance(String serviceName, String ip, int port) throws NacosException; /** * deregister instance from a service. * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @throws NacosException nacos exception */ void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException; /** * deregister instance with specified cluster name from a service. * * @param serviceName name of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throws NacosException nacos exception */ void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException; /** * deregister instance with specified cluster name from a service. * * @param serviceName name of service * @param groupName group of service * @param ip instance ip * @param port instance port * @param clusterName instance cluster name * @throws NacosException nacos exception */ void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException; /** * deregister instance with full instance information and default groupName. * * @param serviceName name of service * @param instance instance * @throws NacosException nacos exception */ void deregisterInstance(String serviceName, Instance instance) throws NacosException; /** * deregister instance with full instance information. * * @param serviceName name of service * @param groupName group of service * @param instance instance information * @throws NacosException nacos exception */ void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException; /** * get all instances of a service. * * @param serviceName name of service * @return A list of instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName) throws NacosException; /** * get all instances of a service. * * @param serviceName name of service * @param groupName group of service * @return A list of instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName, String groupName) throws NacosException; /** * Get all instances of a service. * * @param serviceName name of service * @param subscribe if subscribe the service * @return A list of instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName, boolean subscribe) throws NacosException; /** * Get all instances of a service. * * @param serviceName name of service * @param groupName group of service * @param subscribe if subscribe the service * @return A list of instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException; /** * Get all instances within specified clusters of a service. * * @param serviceName name of service * @param clusters list of cluster * @return A list of qualified instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName, List clusters) throws NacosException; /** * Get all instances within specified clusters of a service. * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @return A list of qualified instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName, String groupName, List clusters) throws NacosException; /** * Get all instances within specified clusters of a service. * * @param serviceName name of service * @param clusters list of cluster * @param subscribe if subscribe the service * @return A list of qualified instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException; /** * Get all instances within specified clusters of a service. * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param subscribe if subscribe the service * @return A list of qualified instance * @throws NacosException nacos exception */ List getAllInstances(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException; /** * Get qualified instances of service. * * @param serviceName name of service. * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, boolean healthy) throws NacosException; /** * Get qualified instances of service. * * @param serviceName name of service * @param groupName group of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException; /** * Get qualified instances of service. * * @param serviceName name of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException; /** * Get qualified instances of service. * * @param serviceName name of service * @param groupName group of service * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException; /** * Get qualified instances within specified clusters of service. * * @param serviceName name of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException; /** * Get qualified instances within specified clusters of service. * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, String groupName, List clusters, boolean healthy) throws NacosException; /** * Get qualified instances within specified clusters of service. * * @param serviceName name of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, List clusters, boolean healthy, boolean subscribe) throws NacosException; /** * Get qualified instances within specified clusters of service. * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param healthy a flag to indicate returning healthy or unhealthy instances * @param subscribe if subscribe the service * @return A qualified list of instance * @throws NacosException nacos exception */ List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException; /** * Select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName) throws NacosException; /** * Select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @param groupName group of service * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException; /** * select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @param subscribe if subscribe the service * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException; /** * select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @param groupName group of service * @param subscribe if subscribe the service * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException; /** * Select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @param clusters a list of clusters should the instance belongs to * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException; /** * Select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @param groupName group of service * @param clusters a list of clusters should the instance belongs to * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters) throws NacosException; /** * Select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @param clusters a list of clusters should the instance belongs to * @param subscribe if subscribe the service * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException; /** * Select one healthy instance of service using predefined load balance strategy. * * @param serviceName name of service * @param groupName group of service * @param clusters a list of clusters should the instance belongs to * @param subscribe if subscribe the service * @return qualified instance * @throws NacosException nacos exception */ Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException; /** * Subscribe service to receive events of instances alteration. * * @param serviceName name of service * @param listener event listener * @throws NacosException nacos exception */ void subscribe(String serviceName, EventListener listener) throws NacosException; /** * Subscribe service to receive events of instances alteration. * * @param serviceName name of service * @param groupName group of service * @param listener event listener * @throws NacosException nacos exception */ void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException; /** * Subscribe service to receive events of instances alteration. * * @param serviceName name of service * @param clusters list of cluster * @param listener event listener * @throws NacosException nacos exception */ void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException; /** * Subscribe service to receive events of instances alteration. * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param listener event listener * @throws NacosException nacos exception */ void subscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; /** * Subscribe service to receive events of instances alteration. * * @param serviceName name of service * @param selector selector of instances * @param listener event listener * @throws NacosException nacos exception */ void subscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; /** * Subscribe service to receive events of instances alteration. * * @param serviceName name of service * @param groupName group of service * @param selector selector of instances * @param listener event listener * @throws NacosException nacos exception */ void subscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException; /** * Unsubscribe event listener of service. * * @param serviceName name of service * @param listener event listener * @throws NacosException nacos exception */ void unsubscribe(String serviceName, EventListener listener) throws NacosException; /** * unsubscribe event listener of service. * * @param serviceName name of service * @param groupName group of service * @param listener event listener * @throws NacosException nacos exception */ void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException; /** * Unsubscribe event listener of service. * * @param serviceName name of service * @param clusters list of cluster * @param listener event listener * @throws NacosException nacos exception */ void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException; /** * Unsubscribe event listener of service. * * @param serviceName name of service * @param groupName group of service * @param clusters list of cluster * @param listener event listener * @throws NacosException nacos exception */ void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException; /** * Unsubscribe event listener of service. * * @param serviceName name of service * @param selector selector of instances * @param listener event listener * @throws NacosException nacos exception */ void unsubscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException; /** * Unsubscribe event listener of service. * * @param serviceName name of service * @param groupName group of service * @param selector selector of instances * @param listener event listener * @throws NacosException nacos exception */ void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException; /** * According to matching rules, watch services within a specific scope, and receive notifications when * changes occur in the services within the scope. * When given a fixed group name, watch changes in all services under this group. * * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * According to matching rules, watch services within a specific scope, and receive notifications when * changes occur in the services within the scope. * When provided with a fixed group name and pattern of service name, watch changes in services under * this group that match the specified pattern. * * @param serviceNamePattern service name pattern for fuzzy watch * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ void fuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * According to matching rules, watch services within a specific scope, and receive notifications when * changes occur in the services within the scope. * When given a fixed group name, watch changes in all services under this group. * * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener * @return matched service keys. * @throws NacosException nacos exception */ Future> fuzzyWatchWithServiceKeys(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * According to matching rules, watch services within a specific scope, and receive notifications when * changes occur in the services within the scope. * When provided with a fixed group name and pattern of service name, watch changes in services under * this group that match the specified pattern. * * @param serviceNamePattern service name pattern for fuzzy watch * @param groupNamePattern group name pattern for fuzzy watch * @param listener event listener * @return matched service keys. * @throws NacosException nacos exception */ Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * Cancel fuzzy watch, and remove event listener of a pattern. * * @param groupNamePattern group name for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * Cancel fuzzy watch, and remove event listener of a pattern. * * @param serviceNamePattern service name pattern for fuzzy watch * @param groupNamePattern fixed group name for fuzzy watch * @param listener event listener * @throws NacosException nacos exception */ void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException; /** * Get all service names from server. * * @param pageNo page index * @param pageSize page size * @return list of service names * @throws NacosException nacos exception */ ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException; /** * Get all service names from server. * * @param pageNo page index * @param pageSize page size * @param groupName group name * @return list of service names * @throws NacosException nacos exception */ ListView getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException; /** * Get all service names from server with selector. * * @param pageNo page index * @param pageSize page size * @param selector selector to filter the resource * @return list of service names * @throws NacosException nacos exception * @since 0.7.0 * @deprecated after 3.3.0. */ @Deprecated ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException; /** * Get all service names from server with selector. * * @param pageNo page index * @param pageSize page size * @param groupName group name * @param selector selector to filter the resource * @return list of service names * @throws NacosException nacos exception * @deprecated after 3.3.0. */ @Deprecated ListView getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException; /** * Get all subscribed services of current client. * * @return subscribed services * @throws NacosException nacos exception */ List getSubscribeServices() throws NacosException; /** * get server health status. * * @return is server healthy */ String getServerStatus(); /** * Shutdown the resource service. * * @throws NacosException exception. */ void shutDown() throws NacosException; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/PreservedMetadataKeys.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; /** * Some keys of metadata that are recognized by Nacos. * * @author nkorange * @since 1.0.0 */ public class PreservedMetadataKeys { /** * The key to indicate the registry source of service instance, such as Dubbo, SpringCloud, etc. */ public static final String REGISTER_SOURCE = "preserved.register.source"; public static final String HEART_BEAT_TIMEOUT = "preserved.heart.beat.timeout"; public static final String IP_DELETE_TIMEOUT = "preserved.ip.delete.timeout"; public static final String HEART_BEAT_INTERVAL = "preserved.heart.beat.interval"; public static final String INSTANCE_ID_GENERATOR = "preserved.instance.id.generator"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/ability/ClientNamingAbility.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.ability; import java.io.Serializable; /** * naming abilities of nacos client. * * @author liuzunfei * @version $Id: ClientNamingAbility.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ public class ClientNamingAbility implements Serializable { private static final long serialVersionUID = 7643941846828882862L; private boolean supportDeltaPush; private boolean supportRemoteMetric; public boolean isSupportDeltaPush() { return supportDeltaPush; } public void setSupportDeltaPush(boolean supportDeltaPush) { this.supportDeltaPush = supportDeltaPush; } public boolean isSupportRemoteMetric() { return supportRemoteMetric; } public void setSupportRemoteMetric(boolean supportRemoteMetric) { this.supportRemoteMetric = supportRemoteMetric; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/ability/ServerNamingAbility.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.ability; import java.io.Serializable; import java.util.Objects; /** * naming abilities of nacos server. * * @author liuzunfei * @version $Id: ServerNamingAbility.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ public class ServerNamingAbility implements Serializable { private static final long serialVersionUID = 8308895444341445512L; /** * Nacos server can use SOFA-Jraft to handle persist service and metadata. */ private boolean supportJraft; public boolean isSupportJraft() { return supportJraft; } public void setSupportJraft(boolean supportJraft) { this.supportJraft = supportJraft; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof ServerNamingAbility)) { return false; } ServerNamingAbility that = (ServerNamingAbility) o; return supportJraft == that.supportJraft; } @Override public int hashCode() { return Objects.hash(supportJraft); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractEventListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; import java.util.concurrent.Executor; /** * Abstract event listener, to support handle event by user custom executor. * * @author horizonzy * @since 1.4.1 */ public abstract class AbstractEventListener implements EventListener { /** * Get executor for execute this receive. * * @return Executor */ public Executor getExecutor() { return null; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcher.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; import java.util.concurrent.Executor; /** * Abstract fuzzy watch event listener, to support handle event by user custom executor. * * @author tanyongquan */ public abstract class AbstractFuzzyWatchEventWatcher implements FuzzyWatchEventWatcher, FuzzyWatchLoadWatcher { @Override public Executor getExecutor() { return null; } @Override public void onPatternOverLimit() { //do nothing default } @Override public void onServiceReachUpLimit() { //do nothing default } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/Event.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; /** * Event Interface. * * @author nkorange */ public interface Event { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/EventListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; /** * Event Listener. * * @author Nacos */ public interface EventListener { /** * callback event. * * @param event event */ void onEvent(Event event); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; /** * Fuzzy Watch Notify Event. * * @author tanyongquan */ public class FuzzyWatchChangeEvent implements Event { private String serviceName; private String groupName; private String namespace; private String changeType; private String syncType; public FuzzyWatchChangeEvent() { } public FuzzyWatchChangeEvent(String serviceName, String groupName, String namespace, String changeType, String syncType) { this.changeType = changeType; this.serviceName = serviceName; this.groupName = groupName; this.namespace = namespace; this.syncType = syncType; } public String getServiceName() { return serviceName; } public String getGroupName() { return groupName; } public String getNamespace() { return namespace; } /** * The change type of local watcher , contains {"ADD_SERVICE", "DELETE_SERVICE"}. see Constants.ServiceChangedType */ public String getChangeType() { return changeType; } /** * the sync type that trigger this changed,contains {"FUZZY_WATCH_INIT_NOTIFY","FUZZY_WATCH_RESOURCE_CHANGED", * "FUZZY_WATCH_DIFF_SYNC_NOTIFY"}. * * @return The sync type that triggered this change */ public String getSyncType() { return syncType; } @Override public String toString() { return "FuzzyWatchChangeEvent{" + "serviceName='" + serviceName + '\'' + ", groupName='" + groupName + '\'' + ", namespace='" + namespace + '\'' + ", changeType='" + changeType + '\'' + ", syncType='" + syncType + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchEventWatcher.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; import java.util.concurrent.Executor; /** * Abstract fuzzy watch event listener, to support handle event by user custom executor. * * @author tanyongquan */ public interface FuzzyWatchEventWatcher { /** * executor to notify event, using nacos internal notifier if null . * @return The executor for event notification */ Executor getExecutor(); /** * handle FuzzyWatchChangeEvent. * @param event fuzzy watch change event. */ void onEvent(FuzzyWatchChangeEvent event); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchLoadWatcher.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; /** * naming fuzzy watch watcher that triggered when loader over limit. * @author shiyiyue */ public interface FuzzyWatchLoadWatcher { /** * triggered when server pattern count over limit. */ void onPatternOverLimit(); /** * triggered when pattern match service count over limit. */ void onServiceReachUpLimit(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/listener/NamingEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; import com.alibaba.nacos.api.naming.pojo.Instance; import java.util.List; /** * Naming Event. * * @author nkorange */ public class NamingEvent implements Event { private String serviceName; private String groupName; private String clusters; private List instances; public NamingEvent(String serviceName, List instances) { this.serviceName = serviceName; this.instances = instances; } public NamingEvent(String serviceName, String groupName, String clusters, List instances) { this.serviceName = serviceName; this.groupName = groupName; this.clusters = clusters; this.instances = instances; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public List getInstances() { return instances; } public void setInstances(List instances) { this.instances = instances; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getClusters() { return clusters; } public void setClusters(String clusters) { this.clusters = clusters; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/Cluster.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp; import java.io.Serializable; import java.util.HashMap; import java.util.Map; /** * Cluster. * *

    The class will be serialized to json, and there are some variables and method can't use Camel naming rule for * compatibility * * @author nkorange */ @SuppressWarnings("checkstyle:abbreviationaswordinname") public class Cluster implements Serializable { private static final long serialVersionUID = -7196138840047197271L; /** * Name of belonging service. */ private String serviceName; /** * Name of cluster. */ private String name; /** * Health check config of this cluster. */ private AbstractHealthChecker healthChecker = new Tcp(); /** * Default registered port for instances in this cluster. */ private int defaultPort = 80; /** * Default health check port of instances in this cluster. */ private int defaultCheckPort = 80; /** * Whether or not use instance port to do health check. */ private boolean useIpPort4Check = true; private Map metadata = new HashMap<>(); public Cluster() { } public Cluster(String clusterName) { this.name = clusterName; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getName() { return name; } public void setName(String name) { this.name = name; } public AbstractHealthChecker getHealthChecker() { return healthChecker; } public void setHealthChecker(AbstractHealthChecker healthChecker) { this.healthChecker = healthChecker; } public int getDefaultPort() { return defaultPort; } public void setDefaultPort(int defaultPort) { this.defaultPort = defaultPort; } public int getDefaultCheckPort() { return defaultCheckPort; } public void setDefaultCheckPort(int defaultCheckPort) { this.defaultCheckPort = defaultCheckPort; } public boolean isUseIpPort4Check() { return useIpPort4Check; } public void setUseIpPort4Check(boolean useIpPort4Check) { this.useIpPort4Check = useIpPort4Check; } public Map getMetadata() { return metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/Instance.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.utils.StringUtils; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * Instance. * * @author nkorange */ @JsonInclude(Include.NON_NULL) public class Instance implements NacosForm { private static final long serialVersionUID = -742906310567291979L; private static final int MAX_PORT = 65535; /** * unique id of this instance. */ private String instanceId; /** * instance ip. */ private String ip; /** * instance port. */ private int port; /** * instance weight. */ private double weight = 1.0D; /** * instance health status. */ private boolean healthy = true; /** * If instance is enabled to accept request. */ private boolean enabled = true; /** * If instance is ephemeral. * * @since 1.0.0 */ private boolean ephemeral = true; /** * cluster information of instance. */ private String clusterName; /** * Service information of instance. */ private String serviceName; /** * user extended attributes. */ private Map metadata = new HashMap<>(); public String getInstanceId() { return this.instanceId; } public void setInstanceId(final String instanceId) { this.instanceId = instanceId; } public String getIp() { return this.ip; } public void setIp(final String ip) { this.ip = ip; } public int getPort() { return this.port; } public void setPort(final int port) { this.port = port; } public double getWeight() { return this.weight; } public void setWeight(final double weight) { this.weight = weight; } public boolean isHealthy() { return this.healthy; } public void setHealthy(final boolean healthy) { this.healthy = healthy; } public String getClusterName() { return this.clusterName; } public void setClusterName(final String clusterName) { this.clusterName = clusterName; } public String getServiceName() { return this.serviceName; } public void setServiceName(final String serviceName) { this.serviceName = serviceName; } public Map getMetadata() { return this.metadata; } public void setMetadata(final Map metadata) { this.metadata = metadata; } /** * add meta data. * * @param key meta data key * @param value meta data value */ public void addMetadata(final String key, final String value) { if (metadata == null) { metadata = new HashMap<>(4); } metadata.put(key, value); } public boolean isEnabled() { return this.enabled; } public void setEnabled(final boolean enabled) { this.enabled = enabled; } public boolean isEphemeral() { return this.ephemeral; } public void setEphemeral(final boolean ephemeral) { this.ephemeral = ephemeral; } @Override public void validate() throws NacosApiException { fillDefaultValue(); if (StringUtils.isBlank(ip)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'ip' type String is not present"); } if (port < 0 || port > MAX_PORT) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, "Required parameter 'port' type int is require 0 ~ 65535"); } } private void fillDefaultValue() { if (StringUtils.isBlank(clusterName)) { clusterName = Constants.DEFAULT_CLUSTER_NAME; } } @Override public String toString() { return "Instance{" + "instanceId='" + instanceId + '\'' + ", ip='" + ip + '\'' + ", port=" + port + ", weight=" + weight + ", healthy=" + healthy + ", enabled=" + enabled + ", ephemeral=" + ephemeral + ", clusterName='" + clusterName + '\'' + ", serviceName='" + serviceName + '\'' + ", metadata=" + metadata + '}'; } public String toInetAddr() { return ip + ":" + port; } @Override public boolean equals(final Object obj) { if (!(obj instanceof Instance)) { return false; } final Instance host = (Instance) obj; return Instance.strEquals(host.toString(), toString()); } @Override public int hashCode() { return toString().hashCode(); } private static boolean strEquals(final String str1, final String str2) { return Objects.equals(str1, str2); } public long getInstanceHeartBeatInterval() { return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_INTERVAL, Constants.DEFAULT_HEART_BEAT_INTERVAL); } public long getInstanceHeartBeatTimeOut() { return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, Constants.DEFAULT_HEART_BEAT_TIMEOUT); } public long getIpDeleteTimeout() { return getMetaDataByKeyWithDefault(PreservedMetadataKeys.IP_DELETE_TIMEOUT, Constants.DEFAULT_IP_DELETE_TIMEOUT); } public String getInstanceIdGenerator() { return getMetaDataByKeyWithDefault(PreservedMetadataKeys.INSTANCE_ID_GENERATOR, Constants.DEFAULT_INSTANCE_ID_GENERATOR); } /** * Returns {@code true} if this metadata contains the specified key. * * @param key metadata key * @return {@code true} if this metadata contains the specified key */ public boolean containsMetadata(final String key) { if (getMetadata() == null || getMetadata().isEmpty()) { return false; } return getMetadata().containsKey(key); } private long getMetaDataByKeyWithDefault(final String key, final long defaultValue) { if (getMetadata() == null || getMetadata().isEmpty()) { return defaultValue; } final String value = getMetadata().get(key); if (NamingUtils.isNumber(value)) { return Long.parseLong(value); } return defaultValue; } private String getMetaDataByKeyWithDefault(final String key, final String defaultValue) { if (getMetadata() == null || getMetadata().isEmpty()) { return defaultValue; } return getMetadata().get(key); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/ListView.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import java.util.List; /** * ListView. * * @author nkorange */ public class ListView { private List data; private int count; public List getData() { return data; } public void setData(List data) { this.data = data; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } @Override public String toString() { return "ListView{" + "data=" + data + ", count=" + count + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/Service.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.api.selector.Selector; import com.alibaba.nacos.api.utils.StringUtils; import java.util.HashMap; import java.util.Map; /** * Service of Nacos. * *

    We introduce a 'service --> cluster --> instance' model, in which service stores a list of clusters, which contains a * list of instances. * *

    Typically we put some unique properties between instances to service level. * * @author nkorange */ public class Service implements NacosForm { private static final long serialVersionUID = -3470985546826874460L; private String namespaceId; private String groupName; private String name; private boolean ephemeral; private float protectThreshold = 0.0F; private Map metadata = new HashMap<>(); private Selector selector = new NoneSelector(); public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isEphemeral() { return ephemeral; } public void setEphemeral(boolean ephemeral) { this.ephemeral = ephemeral; } public float getProtectThreshold() { return protectThreshold; } public void setProtectThreshold(float protectThreshold) { this.protectThreshold = protectThreshold; } public Map getMetadata() { return metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } public void addMetadata(String key, String value) { this.metadata.put(key, value); } public Selector getSelector() { return selector; } public void setSelector(Selector selector) { this.selector = selector; } @Override public void validate() throws NacosApiException { fillDefaultValue(); if (StringUtils.isBlank(name)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter 'name' type String is not present"); } } /** * fill default value. */ public void fillDefaultValue() { if (StringUtils.isBlank(namespaceId)) { namespaceId = Constants.DEFAULT_NAMESPACE_ID; } if (StringUtils.isBlank(groupName)) { groupName = Constants.DEFAULT_GROUP; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/ServiceInfo.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude.Include; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Service Information with instances and without cluster information, used in data pushing and cached for nacos-client. * * @author nkorange * @author shizhengxing */ @JsonInclude(Include.NON_NULL) public class ServiceInfo implements Cloneable { /** * file name pattern: groupName@@name@@clusters. */ private static final int GROUP_POSITION = 0; private static final int SERVICE_POSITION = 1; private static final int CLUSTER_POSITION = 2; private static final int FILE_NAME_PARTS = 3; @JsonIgnore private String jsonFromServer = EMPTY; private static final String EMPTY = ""; private static final String DEFAULT_CHARSET = "UTF-8"; private String name; private String groupName; private String clusters; private long cacheMillis = 1000L; private List hosts = new ArrayList<>(); private long lastRefTime = 0L; private String checksum = ""; private volatile boolean allIps = false; private volatile boolean reachProtectionThreshold = false; public ServiceInfo() { } public boolean isAllIps() { return allIps; } public void setAllIps(boolean allIps) { this.allIps = allIps; } /** * There is only one form of the key:groupName@@name@@clusters. This constructor used by DiskCache.read(String) and * FailoverReactor.FailoverFileReader,you should know that 'groupName' must not be null,and 'clusters' can be null. */ public ServiceInfo(final String key) { String[] keys = key.split(Constants.SERVICE_INFO_SPLITER); if (keys.length >= FILE_NAME_PARTS) { this.groupName = keys[GROUP_POSITION]; this.name = keys[SERVICE_POSITION]; this.clusters = keys[CLUSTER_POSITION]; } else if (keys.length == CLUSTER_POSITION) { this.groupName = keys[GROUP_POSITION]; this.name = keys[SERVICE_POSITION]; } else { //defensive programming throw new IllegalArgumentException("Can't parse out 'groupName',but it must not be null!"); } } public ServiceInfo(String name, String clusters) { this.name = name; this.clusters = clusters; } public int ipCount() { return hosts.size(); } public boolean expired() { return System.currentTimeMillis() - lastRefTime > cacheMillis; } public void setHosts(List hosts) { this.hosts = hosts; } public void addHost(Instance host) { hosts.add(host); } public void addAllHosts(List hosts) { this.hosts.addAll(hosts); } public List getHosts() { return new ArrayList<>(hosts); } public boolean isValid() { return hosts != null; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public void setLastRefTime(long lastRefTime) { this.lastRefTime = lastRefTime; } public long getLastRefTime() { return lastRefTime; } public String getClusters() { return clusters; } public void setClusters(String clusters) { this.clusters = clusters; } public long getCacheMillis() { return cacheMillis; } public void setCacheMillis(long cacheMillis) { this.cacheMillis = cacheMillis; } /** * Judge whether service info is validate. * * @return true if validate, otherwise false */ public boolean validate() { if (isAllIps()) { return true; } if (hosts == null) { return false; } boolean existValidHosts = false; for (Instance host : hosts) { if (host.isHealthy() && host.getWeight() > 0) { existValidHosts = true; break; } } return existValidHosts; } @JsonIgnore public String getJsonFromServer() { return jsonFromServer; } public void setJsonFromServer(String jsonFromServer) { this.jsonFromServer = jsonFromServer; } @JsonIgnore public String getKey() { String serviceName = getGroupedServiceName(); return getKey(serviceName, clusters); } @JsonIgnore public static String getKey(String name, String clusters) { if (!isEmpty(clusters)) { return name + Constants.SERVICE_INFO_SPLITER + clusters; } return name; } @JsonIgnore public String getKeyWithoutClusters() { return getGroupedServiceName(); } @JsonIgnore public String getKeyEncoded() { String serviceName = getGroupedServiceName(); try { serviceName = URLEncoder.encode(serviceName, DEFAULT_CHARSET); } catch (UnsupportedEncodingException ignored) { } return getKey(serviceName, clusters); } private String getGroupedServiceName() { String serviceName = this.name; if (!isEmpty(groupName) && !serviceName.contains(Constants.SERVICE_INFO_SPLITER)) { serviceName = groupName + Constants.SERVICE_INFO_SPLITER + serviceName; } return serviceName; } /** * Get {@link ServiceInfo} from key. * * @param key key of service info * @return new service info */ public static ServiceInfo fromKey(final String key) { return new ServiceInfo(key); } @Override public String toString() { return getKey(); } public String getChecksum() { return checksum; } public void setChecksum(String checksum) { this.checksum = checksum; } private static boolean isEmpty(String str) { return str == null || str.length() == 0; } public boolean isReachProtectionThreshold() { return reachProtectionThreshold; } public void setReachProtectionThreshold(boolean reachProtectionThreshold) { this.reachProtectionThreshold = reachProtectionThreshold; } @Override public ServiceInfo clone() { ServiceInfo cloned = new ServiceInfo(); cloned.jsonFromServer = this.jsonFromServer; cloned.name = this.name; cloned.groupName = this.groupName; cloned.clusters = this.clusters; cloned.cacheMillis = this.cacheMillis; cloned.lastRefTime = this.lastRefTime; cloned.checksum = this.checksum; cloned.allIps = this.allIps; cloned.reachProtectionThreshold = this.reachProtectionThreshold; cloned.hosts = new ArrayList<>(); if (this.hosts != null) { for (Instance host : this.hosts) { Instance clonedHost = new Instance(); clonedHost.setInstanceId(host.getInstanceId()); clonedHost.setIp(host.getIp()); clonedHost.setPort(host.getPort()); clonedHost.setWeight(host.getWeight()); clonedHost.setHealthy(host.isHealthy()); clonedHost.setEnabled(host.isEnabled()); clonedHost.setEphemeral(host.isEphemeral()); clonedHost.setClusterName(host.getClusterName()); clonedHost.setServiceName(host.getServiceName()); if (host.getMetadata() != null) { Map clonedMetadata = new HashMap<>(host.getMetadata()); clonedHost.setMetadata(clonedMetadata); } cloned.hosts.add(clonedHost); } } return cloned; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/builder/InstanceBuilder.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.builder; import com.alibaba.nacos.api.naming.pojo.Instance; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * Builder for {@link Instance}. * * @author xiweng.yy */ public class InstanceBuilder { private String instanceId; private String ip; private Integer port; private Double weight; private Boolean healthy; private Boolean enabled; private Boolean ephemeral; private String clusterName; private String serviceName; private Map metadata = new HashMap<>(); private InstanceBuilder() { } public InstanceBuilder setInstanceId(String instanceId) { this.instanceId = instanceId; return this; } public InstanceBuilder setIp(String ip) { this.ip = ip; return this; } public InstanceBuilder setPort(Integer port) { this.port = port; return this; } public InstanceBuilder setWeight(Double weight) { this.weight = weight; return this; } public InstanceBuilder setHealthy(Boolean healthy) { this.healthy = healthy; return this; } public InstanceBuilder setEnabled(Boolean enabled) { this.enabled = enabled; return this; } public InstanceBuilder setEphemeral(Boolean ephemeral) { this.ephemeral = ephemeral; return this; } public InstanceBuilder setClusterName(String clusterName) { this.clusterName = clusterName; return this; } public InstanceBuilder setServiceName(String serviceName) { this.serviceName = serviceName; return this; } public InstanceBuilder setMetadata(Map metadata) { this.metadata = metadata; return this; } public InstanceBuilder addMetadata(String metaKey, String metaValue) { this.metadata.put(metaKey, metaValue); return this; } /** * Build a new {@link Instance}. * * @return new instance */ public Instance build() { Instance result = new Instance(); if (!Objects.isNull(instanceId)) { result.setInstanceId(instanceId); } if (!Objects.isNull(ip)) { result.setIp(ip); } if (!Objects.isNull(port)) { result.setPort(port); } if (!Objects.isNull(weight)) { result.setWeight(weight); } if (!Objects.isNull(healthy)) { result.setHealthy(healthy); } if (!Objects.isNull(enabled)) { result.setEnabled(enabled); } if (!Objects.isNull(ephemeral)) { result.setEphemeral(ephemeral); } if (!Objects.isNull(clusterName)) { result.setClusterName(clusterName); } if (!Objects.isNull(serviceName)) { result.setServiceName(serviceName); } result.setMetadata(metadata); return result; } public static InstanceBuilder newBuilder() { return new InstanceBuilder(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/healthcheck/AbstractHealthChecker.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker.None; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Mysql; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import java.io.Serializable; /** * Abstract health checker. * * @author nkorange */ @JsonTypeInfo(use = Id.NAME, property = "type", defaultImpl = None.class) @JsonSubTypes({@JsonSubTypes.Type(name = Http.TYPE, value = Http.class), @JsonSubTypes.Type(name = Mysql.TYPE, value = Mysql.class), @JsonSubTypes.Type(name = Tcp.TYPE, value = Tcp.class), @JsonSubTypes.Type(name = None.TYPE, value = None.class)}) public abstract class AbstractHealthChecker implements Cloneable, Serializable { private static final long serialVersionUID = 3848305577423336421L; @JsonIgnore protected final String type; protected AbstractHealthChecker(String type) { this.type = type; } public String getType() { return type; } /** * Clone all fields of this instance to another one. * * @return Another instance with exactly the same fields * @throws CloneNotSupportedException clone not supported exception */ @Override public abstract AbstractHealthChecker clone() throws CloneNotSupportedException; /** * Default implementation of Health checker. */ public static class None extends AbstractHealthChecker { public static final String TYPE = "NONE"; private static final long serialVersionUID = -760631831097384737L; public None() { super(TYPE); } @Override public AbstractHealthChecker clone() throws CloneNotSupportedException { return new None(); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/healthcheck/HealthCheckType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Mysql; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Health check type. * * @author nkorange */ public enum HealthCheckType { /** * TCP type. */ TCP(Tcp.class), /** * HTTP type. */ HTTP(Http.class), /** * MySQL type. */ MYSQL(Mysql.class), /** * No check. */ NONE(AbstractHealthChecker.None.class); private final Class healthCheckerClass; /** * In JDK 1.6, the map need full class for general. So ignore check style. */ @SuppressWarnings("checkstyle:linelength") private static final Map> EXTEND = new ConcurrentHashMap<>(); HealthCheckType(Class healthCheckerClass) { this.healthCheckerClass = healthCheckerClass; } /** * Register extend health checker. * * @param type type name of extend health checker * @param healthCheckerClass class of extend health checker */ public static void registerHealthChecker(String type, Class healthCheckerClass) { if (!EXTEND.containsKey(type)) { EXTEND.put(type, healthCheckerClass); HealthCheckerFactory.registerSubType(healthCheckerClass, type); } } /** * Get health checker class from type. * * @param type type name of extend health checker * @return registered class if have, otherwise default class */ public static Class ofHealthCheckerClass(String type) { HealthCheckType enumType; try { enumType = valueOf(type); } catch (Exception e) { return EXTEND.get(type); } return enumType.healthCheckerClass; } public static List> getLoadedHealthCheckerClasses() { List> all = new ArrayList<>(); for (HealthCheckType type : values()) { all.add(type.healthCheckerClass); } for (Map.Entry> entry : EXTEND.entrySet()) { all.add(entry.getValue()); } return all; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/healthcheck/HealthCheckerFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.api.exception.runtime.NacosSerializationException; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker.None; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; import java.io.IOException; /** * health checker factory. * * @author yangyi */ public class HealthCheckerFactory { private static final ObjectMapper MAPPER = new ObjectMapper(); static { MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } /** * Register new sub type of health checker to factory for serialize and deserialize. * * @param extendHealthChecker extend health checker */ public static void registerSubType(AbstractHealthChecker extendHealthChecker) { registerSubType(extendHealthChecker.getClass(), extendHealthChecker.getType()); } /** * Register new sub type of health checker to factory for serialize and deserialize. * * @param extendHealthCheckerClass extend health checker * @param typeName typeName of health checker */ public static void registerSubType(Class extendHealthCheckerClass, String typeName) { MAPPER.registerSubtypes(new NamedType(extendHealthCheckerClass, typeName)); } /** * Create default {@link None} health checker. * * @return new none health checker */ public static None createNoneHealthChecker() { return new None(); } /** * Deserialize and create an instance of health checker. * * @param jsonString json string of health checker * @return new instance */ public static AbstractHealthChecker deserialize(String jsonString) { try { return MAPPER.readValue(jsonString, AbstractHealthChecker.class); } catch (IOException e) { throw new NacosDeserializationException(AbstractHealthChecker.class, e); } } /** * Serialize an instance of health checker to json. * * @param healthChecker health checker instance * @return son string after serializing */ public static String serialize(AbstractHealthChecker healthChecker) { try { return MAPPER.writeValueAsString(healthChecker); } catch (JsonProcessingException e) { throw new NacosSerializationException(healthChecker.getClass(), e); } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/healthcheck/impl/Http.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck.impl; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.api.utils.StringUtils; import com.fasterxml.jackson.annotation.JsonIgnore; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * Implementation of health checker for HTTP. * * @author yangyi */ public class Http extends AbstractHealthChecker { public static final String TYPE = "HTTP"; private static final long serialVersionUID = 551826315222362349L; private String path = ""; private String headers = ""; private int expectedResponseCode = 200; public Http() { super(Http.TYPE); } public int getExpectedResponseCode() { return this.expectedResponseCode; } public void setExpectedResponseCode(final int expectedResponseCode) { this.expectedResponseCode = expectedResponseCode; } public String getPath() { return this.path; } public void setPath(final String path) { this.path = path; } public String getHeaders() { return this.headers; } public void setHeaders(final String headers) { this.headers = headers; } @JsonIgnore public Map getCustomHeaders() { if (StringUtils.isBlank(headers)) { return Collections.emptyMap(); } final Map headerMap = new HashMap<>(16); for (final String s : headers.split(Constants.NAMING_HTTP_HEADER_SPLITTER)) { final String[] splits = s.split(":"); if (splits.length != 2) { continue; } headerMap.put(StringUtils.trim(splits[0]), StringUtils.trim(splits[1])); } return headerMap; } @Override public int hashCode() { return Objects.hash(path, headers, expectedResponseCode); } @Override public boolean equals(final Object obj) { if (!(obj instanceof Http)) { return false; } final Http other = (Http) obj; if (!StringUtils.equals(path, other.getPath())) { return false; } if (!StringUtils.equals(headers, other.getHeaders())) { return false; } return expectedResponseCode == other.getExpectedResponseCode(); } @Override public Http clone() throws CloneNotSupportedException { final Http config = new Http(); config.setPath(getPath()); config.setHeaders(getHeaders()); config.setExpectedResponseCode(getExpectedResponseCode()); return config; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/healthcheck/impl/Mysql.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck.impl; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.api.utils.StringUtils; import java.util.Objects; /** * Implementation of health checker for MYSQL. * * @author yangyi */ public class Mysql extends AbstractHealthChecker { public static final String TYPE = "MYSQL"; private static final long serialVersionUID = 7928108094599401491L; private String user; private String pwd; private String cmd; public Mysql() { super(Mysql.TYPE); } public String getCmd() { return this.cmd; } public String getPwd() { return this.pwd; } public String getUser() { return this.user; } public void setUser(final String user) { this.user = user; } public void setCmd(final String cmd) { this.cmd = cmd; } public void setPwd(final String pwd) { this.pwd = pwd; } @Override public int hashCode() { return Objects.hash(user, pwd, cmd); } @Override public boolean equals(final Object obj) { if (!(obj instanceof Mysql)) { return false; } final Mysql other = (Mysql) obj; if (!StringUtils.equals(user, other.getUser())) { return false; } if (!StringUtils.equals(pwd, other.getPwd())) { return false; } return StringUtils.equals(cmd, other.getCmd()); } @Override public Mysql clone() throws CloneNotSupportedException { final Mysql config = new Mysql(); config.setUser(getUser()); config.setPwd(getPwd()); config.setCmd(getCmd()); return config; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/healthcheck/impl/Tcp.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck.impl; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import java.util.Objects; /** * Implementation of health checker for TCP. * * @author yangyi */ public class Tcp extends AbstractHealthChecker { public static final String TYPE = "TCP"; private static final long serialVersionUID = -9116042038157496294L; public Tcp() { super(TYPE); } @Override public int hashCode() { return Objects.hash(TYPE); } @Override public boolean equals(Object obj) { return obj instanceof Tcp; } @Override public Tcp clone() throws CloneNotSupportedException { return new Tcp(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientPublisherInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import java.io.Serializable; /** * Nacos naming client publisher information. * * @author xiweng.yy */ public class ClientPublisherInfo implements Serializable { private static final long serialVersionUID = -4858433977035198914L; private String clientId; private String ip; private int port; private String clusterName; public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getClusterName() { return clusterName; } public void setClusterName(String clusterName) { this.clusterName = clusterName; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientServiceInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import java.io.Serializable; /** * Nacos naming client service information. * * @author xiweng.yy */ public class ClientServiceInfo implements Serializable { private static final long serialVersionUID = 7400821120040393395L; private String namespaceId; private String groupName; private String serviceName; private ClientPublisherInfo publisherInfo; private ClientSubscriberInfo subscriberInfo; public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public ClientPublisherInfo getPublisherInfo() { return publisherInfo; } public void setPublisherInfo(ClientPublisherInfo publisherInfo) { this.publisherInfo = publisherInfo; } public ClientSubscriberInfo getSubscriberInfo() { return subscriberInfo; } public void setSubscriberInfo(ClientSubscriberInfo subscriberInfo) { this.subscriberInfo = subscriberInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientSubscriberInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import java.io.Serializable; /** * Nacos naming client subscriber information. * * @author xiweng.yy */ public class ClientSubscriberInfo implements Serializable { private static final long serialVersionUID = 1889748153147644741L; private String clientId; private String appName; private String agent; private String address; public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getAgent() { return agent; } public void setAgent(String agent) { this.agent = agent; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientSummaryInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import java.io.Serializable; /** * Nacos naming {@link com.alibaba.nacos.naming.core.v2.client.Client} summary information. * * @author xiweng.yy */ public class ClientSummaryInfo implements Serializable { private static final long serialVersionUID = -4482158251664716884L; private String clientId; private boolean ephemeral; private long lastUpdatedTime; /** * The type of client, `connection` for upper 2.0 client, otherwise is `ipPort`. */ private String clientType; /** * Following fields are only for `connection` {@link #clientType}. */ private String connectType; private String appName; private String version; private String clientIp; private int clientPort; public String getClientId() { return clientId; } public void setClientId(String clientId) { this.clientId = clientId; } public boolean isEphemeral() { return ephemeral; } public void setEphemeral(boolean ephemeral) { this.ephemeral = ephemeral; } public long getLastUpdatedTime() { return lastUpdatedTime; } public void setLastUpdatedTime(long lastUpdatedTime) { this.lastUpdatedTime = lastUpdatedTime; } public String getClientType() { return clientType; } public void setClientType(String clientType) { this.clientType = clientType; } public String getConnectType() { return connectType; } public void setConnectType(String connectType) { this.connectType = connectType; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getClientIp() { return clientIp; } public void setClientIp(String clientIp) { this.clientIp = clientIp; } public int getClientPort() { return clientPort; } public void setClientPort(int clientPort) { this.clientPort = clientPort; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClusterInfo.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.NacosForm; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.api.utils.StringUtils; import java.util.List; import java.util.Map; /** * Cluster detail information for nacos maintain client, contain cluster detail information; instance information is optional. * *

    * Cluster detail information can't get by independent, must be get by {@link ServiceDetailInfo#getClusterMap()} *

    * * @author xiweng.yy */ public class ClusterInfo implements NacosForm { private static final long serialVersionUID = 2146881454057032105L; private String clusterName; private AbstractHealthChecker healthChecker; private int healthyCheckPort = 80; /** * Whether Nacos use instance port to do health check. */ private boolean useInstancePortForCheck = true; private Map metadata; private List hosts; /** * Getter method for property hosts. * * @return property value of hosts */ public List getHosts() { return hosts; } /** * Setter method for property hosts . * * @param hosts value to be assigned to property hosts */ public void setHosts(List hosts) { this.hosts = hosts; } public String getClusterName() { return clusterName; } public void setClusterName(String clusterName) { this.clusterName = clusterName; } public AbstractHealthChecker getHealthChecker() { return healthChecker; } public void setHealthChecker(AbstractHealthChecker healthChecker) { this.healthChecker = healthChecker; } public Map getMetadata() { return metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } public int getHealthyCheckPort() { return healthyCheckPort; } public void setHealthyCheckPort(int healthyCheckPort) { this.healthyCheckPort = healthyCheckPort; } public boolean isUseInstancePortForCheck() { return useInstancePortForCheck; } public void setUseInstancePortForCheck(boolean useInstancePortForCheck) { this.useInstancePortForCheck = useInstancePortForCheck; } @Override public void validate() throws NacosApiException { if (StringUtils.isEmpty(clusterName)) { this.clusterName = Constants.DEFAULT_CLUSTER_NAME; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/InstanceMetadataBatchResult.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import java.io.Serializable; import java.util.List; /** * Nacos instance metadata batch operation result, include updated instance ip list. * * @author xiweng.yy */ public class InstanceMetadataBatchResult implements Serializable { private static final long serialVersionUID = -5793871911227035729L; private List updated; public InstanceMetadataBatchResult() { } public InstanceMetadataBatchResult(List updated) { this.updated = updated; } public List getUpdated() { return updated; } public void setUpdated(List updated) { this.updated = updated; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/MetricsInfo.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.annotation.JsonInclude; import java.io.Serializable; /** * Nacos Naming Metrics Information. * * @author xiweng.yy */ @JsonInclude(JsonInclude.Include.NON_NULL) public class MetricsInfo implements Serializable { private static final long serialVersionUID = -5064297490423743871L; private String status; private Integer serviceCount; private Integer instanceCount; private Integer subscribeCount; private Integer clientCount; private Integer connectionBasedClientCount; private Integer ephemeralIpPortClientCount; private Integer persistentIpPortClientCount; private Integer responsibleClientCount; public MetricsInfo() { } public String getStatus() { return status; } public void setStatus(String status) { this.status = status; } public Integer getServiceCount() { return serviceCount; } public void setServiceCount(Integer serviceCount) { this.serviceCount = serviceCount; } public Integer getInstanceCount() { return instanceCount; } public void setInstanceCount(Integer instanceCount) { this.instanceCount = instanceCount; } public Integer getSubscribeCount() { return subscribeCount; } public void setSubscribeCount(Integer subscribeCount) { this.subscribeCount = subscribeCount; } public Integer getClientCount() { return clientCount; } public void setClientCount(Integer clientCount) { this.clientCount = clientCount; } public Integer getConnectionBasedClientCount() { return connectionBasedClientCount; } public void setConnectionBasedClientCount(Integer connectionBasedClientCount) { this.connectionBasedClientCount = connectionBasedClientCount; } public Integer getEphemeralIpPortClientCount() { return ephemeralIpPortClientCount; } public void setEphemeralIpPortClientCount(Integer ephemeralIpPortClientCount) { this.ephemeralIpPortClientCount = ephemeralIpPortClientCount; } public Integer getPersistentIpPortClientCount() { return persistentIpPortClientCount; } public void setPersistentIpPortClientCount(Integer persistentIpPortClientCount) { this.persistentIpPortClientCount = persistentIpPortClientCount; } public Integer getResponsibleClientCount() { return responsibleClientCount; } public void setResponsibleClientCount(Integer responsibleClientCount) { this.responsibleClientCount = responsibleClientCount; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/ServiceDetailInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.alibaba.nacos.api.selector.Selector; import java.io.Serializable; import java.util.Map; /** * Service detail information for nacos maintain client, contain service detail information and cluster information. * * @author xiweng.yy */ public class ServiceDetailInfo implements Serializable { private static final long serialVersionUID = 6351606608785841722L; private String namespaceId; private String serviceName; private String groupName; private Map clusterMap; private Map metadata; private float protectThreshold; private Selector selector; private Boolean ephemeral; /** * Getter method for property serviceName. * * @return property value of serviceName */ public String getServiceName() { return serviceName; } /** * Setter method for property serviceName . * * @param serviceName value to be assigned to property serviceName */ public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } /** * Getter method for property clusterMap. * * @return property value of clusterMap */ public Map getClusterMap() { return clusterMap; } /** * Setter method for property clusterMap . * * @param clusterMap value to be assigned to property clusterMap */ public void setClusterMap(Map clusterMap) { this.clusterMap = clusterMap; } /** * Getter method for property metadata. * * @return property value of metadata */ public Map getMetadata() { return metadata; } /** * Setter method for property metadata . * * @param metadata value to be assigned to property metadata */ public void setMetadata(Map metadata) { this.metadata = metadata; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public float getProtectThreshold() { return protectThreshold; } public void setProtectThreshold(float protectThreshold) { this.protectThreshold = protectThreshold; } public Selector getSelector() { return selector; } public void setSelector(Selector selector) { this.selector = selector; } public Boolean isEphemeral() { return ephemeral; } public void setEphemeral(boolean ephemeral) { this.ephemeral = ephemeral; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/ServiceView.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; /** * Service view. * * @author nkorange */ public class ServiceView { private String name; private String groupName; private int clusterCount; private int ipCount; private int healthyInstanceCount; private String triggerFlag; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public int getClusterCount() { return clusterCount; } public void setClusterCount(int clusterCount) { this.clusterCount = clusterCount; } public int getIpCount() { return ipCount; } public void setIpCount(int ipCount) { this.ipCount = ipCount; } public int getHealthyInstanceCount() { return healthyInstanceCount; } public void setHealthyInstanceCount(int healthyInstanceCount) { this.healthyInstanceCount = healthyInstanceCount; } public String getTriggerFlag() { return triggerFlag; } public void setTriggerFlag(String triggerFlag) { this.triggerFlag = triggerFlag; } @Override public String toString() { return "ServiceView{" + "name='" + name + '\'' + ", groupName='" + groupName + '\'' + ", clusterCount=" + clusterCount + ", ipCount=" + ipCount + ", healthyInstanceCount=" + healthyInstanceCount + ", triggerFlag='" + triggerFlag + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/pojo/maintainer/SubscriberInfo.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import java.io.Serializable; /** * Nacos service subscriber information. * * @author xiweng.yy */ public class SubscriberInfo implements Serializable { private static final long serialVersionUID = -3803634488440573042L; private String namespaceId; private String groupName; private String serviceName; private String ip; private int port; private String agent; private String appName; public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String getAgent() { return agent; } public void setAgent(String agent) { this.agent = agent; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getAddress() { return ip + ":" + port; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/NamingRemoteConstants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote; /** * Retain all naming module request type constants. * * @author liuzunfei * @author xiweng.yy */ public class NamingRemoteConstants { public static final String REGISTER_INSTANCE = "registerInstance"; public static final String BATCH_REGISTER_INSTANCE = "batchRegisterInstance"; public static final String DE_REGISTER_INSTANCE = "deregisterInstance"; public static final String QUERY_SERVICE = "queryService"; public static final String SUBSCRIBE_SERVICE = "subscribeService"; public static final String NOTIFY_SUBSCRIBER = "notifySubscriber"; public static final String LIST_SERVICE = "listService"; public static final String FORWARD_INSTANCE = "forwardInstance"; public static final String FORWARD_HEART_BEAT = "forwardHeartBeat"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractFuzzyWatchNotifyRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.remote.request.ServerRequest; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** * Abstract fuzzy watch notify request, including basic fuzzy watch notify information. * * @author tanyongquan */ public abstract class AbstractFuzzyWatchNotifyRequest extends ServerRequest { private String syncType; public AbstractFuzzyWatchNotifyRequest() { } public AbstractFuzzyWatchNotifyRequest(String syncType) { this.syncType = syncType; } public String getSyncType() { return syncType; } public void setSyncType(String syncType) { this.syncType = syncType; } @Override public String getModule() { return NAMING_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/AbstractNamingRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.remote.request.Request; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** * Uniform remote request of naming module. * * @author liuzunfei */ public abstract class AbstractNamingRequest extends Request { private String namespace; private String serviceName; private String groupName; public AbstractNamingRequest() { } public AbstractNamingRequest(String namespace, String serviceName, String groupName) { this.namespace = namespace; this.serviceName = serviceName; this.groupName = groupName; } @Override public String getModule() { return NAMING_MODULE; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/BatchInstanceRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.Instance; import java.util.List; /** * The client registers multiple service instance request. * * @author chenhao26 */ public class BatchInstanceRequest extends AbstractNamingRequest { private String type; /** * save all service instance. */ private List instances; public BatchInstanceRequest() { } public BatchInstanceRequest(String namespace, String serviceName, String groupName, String type, List instances) { super(namespace, serviceName, groupName); this.type = type; this.instances = instances; } public void setType(String type) { this.type = type; } public String getType() { return this.type; } public List getInstances() { return instances; } public void setInstances(List instances) { this.instances = instances; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/InstanceRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.Instance; /** * Nacos instances request. * * @author xiweng.yy */ public class InstanceRequest extends AbstractNamingRequest { private String type; private Instance instance; public InstanceRequest() { } public InstanceRequest(String namespace, String serviceName, String groupName, String type, Instance instance) { super(namespace, serviceName, groupName); this.type = type; this.instance = instance; } public void setType(String type) { this.type = type; } public String getType() { return this.type; } public void setInstance(Instance instance) { this.instance = instance; } public Instance getInstance() { return instance; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; /** * Nacos fuzzy watch notify service change request, use it when one of the services changes. * * @author tanyongquan */ public class NamingFuzzyWatchChangeNotifyRequest extends AbstractFuzzyWatchNotifyRequest { private String serviceKey; private String changedType; public NamingFuzzyWatchChangeNotifyRequest() { } public NamingFuzzyWatchChangeNotifyRequest(String serviceKey, String changedType) { super(FUZZY_WATCH_RESOURCE_CHANGED); this.serviceKey = serviceKey; this.changedType = changedType; } public String getServiceKey() { return serviceKey; } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } public String getChangedType() { return changedType; } public void setChangedType(String changedType) { this.changedType = changedType; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.remote.request.Request; import java.util.Set; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** * Nacos naming fuzzy watch service request. * * @author tanyongquan */ public class NamingFuzzyWatchRequest extends Request { private boolean isInitializing; private String namespace; /** * The namespace or tenant associated with the configurations. */ private String groupKeyPattern; private Set receivedGroupKeys; private String watchType; public NamingFuzzyWatchRequest() { } public NamingFuzzyWatchRequest(String groupKeyPattern, String watchType) { this.watchType = watchType; this.groupKeyPattern = groupKeyPattern; } public String getGroupKeyPattern() { return groupKeyPattern; } public void setGroupKeyPattern(String groupKeyPattern) { this.groupKeyPattern = groupKeyPattern; } public String getWatchType() { return watchType; } public void setWatchType(String watchType) { this.watchType = watchType; } public Set getReceivedGroupKeys() { return receivedGroupKeys; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public boolean isInitializing() { return isInitializing; } public void setInitializing(boolean initializing) { isInitializing = initializing; } public void setReceivedGroupKeys(Set receivedGroupKeys) { this.receivedGroupKeys = receivedGroupKeys; } @Override public String getModule() { return NAMING_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import java.util.Set; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** * fuzzy watch sync request from Nacos server. * * @author shiyiyue */ public class NamingFuzzyWatchSyncRequest extends AbstractFuzzyWatchNotifyRequest { /** * The pattern used to match service keys for the services. */ private String groupKeyPattern; /** * The set of contexts containing information about the service. */ private Set contexts; private int totalBatch; private int currentBatch; public NamingFuzzyWatchSyncRequest() { } public NamingFuzzyWatchSyncRequest(String pattern, String syncType, Set contexts) { super(syncType); this.groupKeyPattern = pattern; this.contexts = contexts; } public int getTotalBatch() { return totalBatch; } public void setTotalBatch(int totalBatch) { this.totalBatch = totalBatch; } public int getCurrentBatch() { return currentBatch; } public void setCurrentBatch(int currentBatch) { this.currentBatch = currentBatch; } /** * Build SyncNotifyRequest. * * @param pattern pattern * @param syncType syncType * @param contexts contexts * @param totalBatch totalBatch * @param currentBatch currentBatch * @return A new NamingFuzzyWatchSyncRequest instance */ public static NamingFuzzyWatchSyncRequest buildSyncNotifyRequest(String pattern, String syncType, Set contexts, int totalBatch, int currentBatch) { NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = new NamingFuzzyWatchSyncRequest(pattern, syncType, contexts); namingFuzzyWatchSyncRequest.currentBatch = currentBatch; namingFuzzyWatchSyncRequest.totalBatch = totalBatch; return namingFuzzyWatchSyncRequest; } public String getGroupKeyPattern() { return groupKeyPattern; } public void setGroupKeyPattern(String groupKeyPattern) { this.groupKeyPattern = groupKeyPattern; } public Set getContexts() { return contexts; } public void setContexts(Set contexts) { this.contexts = contexts; } @Override public String getModule() { return NAMING_MODULE; } /** * fuzzy watch sync context. */ public static class Context { /** * service key. */ String serviceKey; /** * changed type. */ private String changedType; /** * Constructs an empty Context object. */ public Context() { } /** * Builds a new context object with the provided parameters. * * @param serviceKey The groupKey associated of the configuration. * @param changedType The type of the configuration change event. * @return A new context object initialized with the provided parameters. */ public static NamingFuzzyWatchSyncRequest.Context build(String serviceKey, String changedType) { NamingFuzzyWatchSyncRequest.Context context = new NamingFuzzyWatchSyncRequest.Context(); context.setServiceKey(serviceKey); context.setChangedType(changedType); return context; } public String getServiceKey() { return serviceKey; } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } public String getChangedType() { return changedType; } public void setChangedType(String changedType) { this.changedType = changedType; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/NotifySubscriberRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.remote.request.ServerRequest; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; /** * Notify subscriber request. * * @author xiweng.yy */ public class NotifySubscriberRequest extends ServerRequest { private String namespace; private String serviceName; private String groupName; private ServiceInfo serviceInfo; public NotifySubscriberRequest() { } @Override public String getModule() { return NAMING_MODULE; } private NotifySubscriberRequest(ServiceInfo serviceInfo) { this.serviceInfo = serviceInfo; } public static NotifySubscriberRequest buildNotifySubscriberRequest(ServiceInfo serviceInfo) { return new NotifySubscriberRequest(serviceInfo); } public ServiceInfo getServiceInfo() { return serviceInfo; } public void setServiceInfo(ServiceInfo serviceInfo) { this.serviceInfo = serviceInfo; } public String getNamespace() { return namespace; } public void setNamespace(String namespace) { this.namespace = namespace; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/PersistentInstanceRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.Instance; /** * Nacos persistent instances request. * * @author blake.qiu */ public class PersistentInstanceRequest extends AbstractNamingRequest { private String type; private Instance instance; public PersistentInstanceRequest() { } public PersistentInstanceRequest(String namespace, String serviceName, String groupName, String type, Instance instance) { super(namespace, serviceName, groupName); this.type = type; this.instance = instance; } public String getType() { return this.type; } public void setType(String type) { this.type = type; } public Instance getInstance() { return instance; } public void setInstance(Instance instance) { this.instance = instance; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/ServiceListRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; /** * Service list request. * * @author xiweng.yy */ public class ServiceListRequest extends AbstractNamingRequest { private int pageNo; private int pageSize; private String selector; public ServiceListRequest() { } public ServiceListRequest(String namespace, String groupName, int pageNo, int pageSize) { super(namespace, "", groupName); this.pageNo = pageNo; this.pageSize = pageSize; } public int getPageNo() { return pageNo; } public void setPageNo(int pageNo) { this.pageNo = pageNo; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public String getSelector() { return selector; } public void setSelector(String selector) { this.selector = selector; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/ServiceQueryRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; /** * Nacos naming query request. * * @author xiweng.yy */ public class ServiceQueryRequest extends AbstractNamingRequest { private String cluster; private boolean healthyOnly; private int udpPort; public ServiceQueryRequest() { } public ServiceQueryRequest(String namespace, String serviceName, String groupName) { super(namespace, serviceName, groupName); } public String getCluster() { return cluster; } public void setCluster(String cluster) { this.cluster = cluster; } public boolean isHealthyOnly() { return healthyOnly; } public void setHealthyOnly(boolean healthyOnly) { this.healthyOnly = healthyOnly; } public int getUdpPort() { return udpPort; } public void setUdpPort(int udpPort) { this.udpPort = udpPort; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/request/SubscribeServiceRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; /** * Nacos naming subscribe service request. * * @author xiweng.yy */ public class SubscribeServiceRequest extends AbstractNamingRequest { private boolean subscribe; private String clusters; public SubscribeServiceRequest() { } public SubscribeServiceRequest(String namespace, String groupName, String serviceName, String clusters, boolean subscribe) { super(namespace, serviceName, groupName); this.clusters = clusters; this.subscribe = subscribe; } public String getClusters() { return clusters; } public void setClusters(String clusters) { this.clusters = clusters; } public boolean isSubscribe() { return subscribe; } public void setSubscribe(boolean subscribe) { this.subscribe = subscribe; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/BatchInstanceResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; /** * batch instance response. * * @author chenhao26 */ public class BatchInstanceResponse extends InstanceResponse { public BatchInstanceResponse() { super(); } public BatchInstanceResponse(String type) { super(type); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/InstanceResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * Instance response. * * @author xiweng.yy */ public class InstanceResponse extends Response { private String type; public InstanceResponse() { } public InstanceResponse(String type) { this.type = type; } public void setType(String type) { this.type = type; } public String getType() { return type; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * Response for notify fuzzy watcher. * * @author tanyongquan */ public class NamingFuzzyWatchChangeNotifyResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; /** * Nacos naming fuzzy watch service response. * * @author tanyongquan */ public class NamingFuzzyWatchResponse extends Response { public NamingFuzzyWatchResponse() { } public static NamingFuzzyWatchResponse buildSuccessResponse() { return new NamingFuzzyWatchResponse(); } /** * Build fail response. * * @param message error message * @return fail response */ public static NamingFuzzyWatchResponse buildFailResponse(String message) { NamingFuzzyWatchResponse result = new NamingFuzzyWatchResponse(); result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; /** * Nacos naming fuzzy watch service response. * * @author tanyongquan */ public class NamingFuzzyWatchSyncResponse extends Response { public NamingFuzzyWatchSyncResponse() { } public static NamingFuzzyWatchSyncResponse buildSuccessResponse() { return new NamingFuzzyWatchSyncResponse(); } /** * Build fail response. * * @param message error message * @return fail response */ public static NamingFuzzyWatchSyncResponse buildFailResponse(String message) { NamingFuzzyWatchSyncResponse result = new NamingFuzzyWatchSyncResponse(); result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/NotifySubscriberResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.remote.response.Response; /** * response for notify service subscribe. * @author liuzunfei * @version $Id: NotifySubscriberResponse.java, v 0.1 2020年08月06日 5:28 PM liuzunfei Exp $ */ public class NotifySubscriberResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/QueryServiceResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; /** * Nacos naming query request. * * @author xiweng.yy */ public class QueryServiceResponse extends Response { private ServiceInfo serviceInfo; public QueryServiceResponse() { } private QueryServiceResponse(ServiceInfo serviceInfo) { this.serviceInfo = serviceInfo; } /** * Build Success response. * * @param serviceInfo service info * @return service query response */ public static QueryServiceResponse buildSuccessResponse(ServiceInfo serviceInfo) { return new QueryServiceResponse(serviceInfo); } /** * Build fail response. * * @param message message * @return service query response */ public static QueryServiceResponse buildFailResponse(String message) { QueryServiceResponse queryServiceResponse = new QueryServiceResponse(); queryServiceResponse.setResultCode(ResponseCode.FAIL.getCode()); queryServiceResponse.setMessage(message); return queryServiceResponse; } public ServiceInfo getServiceInfo() { return serviceInfo; } public void setServiceInfo(ServiceInfo serviceInfo) { this.serviceInfo = serviceInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/ServiceListResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import java.util.List; /** * Service list response. * * @author xiweng.yy */ public class ServiceListResponse extends Response { private int count; private List serviceNames; public ServiceListResponse() { } private ServiceListResponse(int count, List serviceNames, String message) { this.count = count; this.serviceNames = serviceNames; } public static ServiceListResponse buildSuccessResponse(int count, List serviceNames) { return new ServiceListResponse(count, serviceNames, "success"); } /** * Build fail response. * * @param message error message * @return fail response */ public static ServiceListResponse buildFailResponse(String message) { ServiceListResponse result = new ServiceListResponse(); result.setErrorInfo(ResponseCode.FAIL.getCode(), message); return result; } public int getCount() { return count; } public void setCount(int count) { this.count = count; } public List getServiceNames() { return serviceNames; } public void setServiceNames(List serviceNames) { this.serviceNames = serviceNames; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/remote/response/SubscribeServiceResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.remote.response.Response; /** * Nacos naming subscribe service response. * * @author xiweng.yy */ public class SubscribeServiceResponse extends Response { private ServiceInfo serviceInfo; public SubscribeServiceResponse() { } public SubscribeServiceResponse(int resultCode, String message, ServiceInfo serviceInfo) { super(); setResultCode(resultCode); setMessage(message); this.serviceInfo = serviceInfo; } public ServiceInfo getServiceInfo() { return serviceInfo; } public void setServiceInfo(ServiceInfo serviceInfo) { this.serviceInfo = serviceInfo; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/selector/NamingContext.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import java.util.List; /** * Naming selector context. * * @author lideyou */ public interface NamingContext { /** * Get service name. * * @return service name */ String getServiceName(); /** * Get group name. * * @return group name */ String getGroupName(); /** * Get clusters. * * @return clusters */ String getClusters(); /** * Get current instances. * * @return current instances */ List getInstances(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/selector/NamingResult.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.selector.client.SelectResult; import java.util.List; /** * Naming select result. * * @author lideyou */ public interface NamingResult extends SelectResult> { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/selector/NamingSelector.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.selector; import com.alibaba.nacos.api.selector.client.Selector; /** * Naming selector. * * @author lideyou */ public interface NamingSelector extends Selector { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/spi/generator/InstanceIdGenerator.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.spi.generator; import com.alibaba.nacos.api.naming.pojo.Instance; /** * Generator SPI for Instance Id. * * @author xiweng.yy */ public interface InstanceIdGenerator { /** * Generate instance id. * * @param instance instance * @return instance id */ String generateInstanceId(Instance instance); /** * Generator type. * * @return type */ String type(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/naming/utils/NamingUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.utils.StringUtils; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Pattern; import static com.alibaba.nacos.api.common.Constants.CLUSTER_NAME_PATTERN_STRING; import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; import static com.alibaba.nacos.api.common.Constants.NUMBER_PATTERN_STRING; /** * NamingUtils. * * @author nkorange * @since 1.0.0 */ public class NamingUtils { private static final Pattern CLUSTER_NAME_PATTERN = Pattern.compile(CLUSTER_NAME_PATTERN_STRING); private static final Pattern NUMBER_PATTERN = Pattern.compile(NUMBER_PATTERN_STRING); /** * Returns a combined string with serviceName and groupName. serviceName can not be nil. * *

    In most cases, serviceName can not be nil. In other cases, for search or anything, See {@link * com.alibaba.nacos.api.naming.utils.NamingUtils#getGroupedNameOptional(String, String)} * *

    etc: *

    serviceName | groupName | result

    *

    serviceA | groupA | groupA@@serviceA

    *

    nil | groupA | threw IllegalArgumentException

    * * @return 'groupName@@serviceName' */ public static String getGroupedName(final String serviceName, final String groupName) { if (StringUtils.isBlank(serviceName)) { throw new IllegalArgumentException("Param 'serviceName' is illegal, serviceName is blank"); } if (StringUtils.isBlank(groupName)) { throw new IllegalArgumentException("Param 'groupName' is illegal, groupName is blank"); } final String resultGroupedName = groupName + Constants.SERVICE_INFO_SPLITER + serviceName; return resultGroupedName.intern(); } public static String getServiceKey(String namespace, String group, String serviceName) { if (StringUtils.isBlank(namespace)) { namespace = DEFAULT_NAMESPACE_ID; } return namespace + Constants.SERVICE_INFO_SPLITER + group + Constants.SERVICE_INFO_SPLITER + serviceName; } /** * parse service key items for serviceKey. item[0] for namespace item[1] for group item[2] for service name * * @param serviceKey serviceKey. * @return */ public static String[] parseServiceKey(String serviceKey) { return serviceKey.split(Constants.SERVICE_INFO_SPLITER); } public static String getServiceName(final String serviceNameWithGroup) { if (StringUtils.isBlank(serviceNameWithGroup)) { return StringUtils.EMPTY; } if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) { return serviceNameWithGroup; } return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[1]; } public static String getGroupName(final String serviceNameWithGroup) { if (StringUtils.isBlank(serviceNameWithGroup)) { return StringUtils.EMPTY; } if (!serviceNameWithGroup.contains(Constants.SERVICE_INFO_SPLITER)) { return Constants.DEFAULT_GROUP; } return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[0]; } /** * Check serviceName is compatibility mode or not. * * @param serviceName serviceName * @return if serviceName is compatibility mode, return true */ public static boolean isServiceNameCompatibilityMode(final String serviceName) { return !StringUtils.isBlank(serviceName) && serviceName.contains(Constants.SERVICE_INFO_SPLITER); } /** * check combineServiceName format. the serviceName can't be blank. *
         * serviceName = "@@";                 the length = 0; illegal
         * serviceName = "group@@";            the length = 1; illegal
         * serviceName = "@@serviceName";      the length = 2; illegal
         * serviceName = "group@@serviceName"; the length = 2; legal
         * 
    * * @param combineServiceName such as: groupName@@serviceName */ public static void checkServiceNameFormat(String combineServiceName) { String[] split = combineServiceName.split(Constants.SERVICE_INFO_SPLITER); if (split.length <= 1) { throw new IllegalArgumentException( "Param 'serviceName' is illegal, it should be format as 'groupName@@serviceName'"); } if (split[0].isEmpty()) { throw new IllegalArgumentException("Param 'serviceName' is illegal, groupName can't be empty"); } } /** * Returns a combined string with serviceName and groupName. Such as 'groupName@@serviceName' *

    This method works similar with {@link com.alibaba.nacos.api.naming.utils.NamingUtils#getGroupedName} But not * verify any parameters. * *

    etc: *

    serviceName | groupName | result

    *

    serviceA | groupA | groupA@@serviceA

    *

    nil | groupA | groupA@@

    *

    nil | nil | @@

    * * @return 'groupName@@serviceName' */ public static String getGroupedNameOptional(final String serviceName, final String groupName) { return groupName + Constants.SERVICE_INFO_SPLITER + serviceName; } /** *

    Check instance param about keep alive.

    * *
         * heart beat timeout must > heart beat interval
         * ip delete timeout must  > heart beat interval
         * 
    * * @param instance need checked instance * @throws NacosException if check failed, throw exception */ public static void checkInstanceIsLegal(Instance instance) throws NacosException { if (null == instance) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, "Instance can not be null."); } if (instance.getInstanceHeartBeatTimeOut() < instance.getInstanceHeartBeatInterval() || instance.getIpDeleteTimeout() < instance.getInstanceHeartBeatInterval()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'."); } if (!StringUtils.isEmpty(instance.getClusterName()) && !CLUSTER_NAME_PATTERN.matcher(instance.getClusterName()) .matches()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, String.format("Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: %s)", instance.getClusterName())); } } /** * check batch register is Ephemeral. * * @param instance instance * @throws NacosException NacosException */ public static void checkInstanceIsEphemeral(Instance instance) throws NacosException { if (!instance.isEphemeral()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.INSTANCE_ERROR, String.format("Batch registration does not allow persistent instance registration , Instance:%s", instance)); } } /** * Batch verify the validity of instances. * * @param instances List of instances to be registered * @throws NacosException Nacos */ public static void batchCheckInstanceIsLegal(List instances) throws NacosException { Set newInstanceSet = new HashSet<>(instances); for (Instance instance : newInstanceSet) { checkInstanceIsEphemeral(instance); checkInstanceIsLegal(instance); } } /** * Check string is a number or not. * * @param str a string of digits * @return if it is a string of digits, return true */ public static boolean isNumber(String str) { return !StringUtils.isEmpty(str) && NUMBER_PATTERN.matcher(str).matches(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/plugin/ConfigItemDefinition.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.plugin; import java.io.Serializable; import java.util.List; /** * Plugin configuration item definition. * * @author WangzJi * @since 3.2.0 */ public class ConfigItemDefinition implements Serializable { private static final long serialVersionUID = 1L; /** * Configuration key. */ private String key; /** * Display name. */ private String name; /** * Description. */ private String description; /** * Default value. */ private String defaultValue; /** * Configuration item type. */ private ConfigItemType type; /** * Whether this item is required. */ private boolean required; /** * Enum values (when type is ENUM). */ private List enumValues; public ConfigItemDefinition() { } public ConfigItemDefinition(String key, String name, ConfigItemType type) { this.key = key; this.name = name; this.type = type; } public String getKey() { return key; } public void setKey(String key) { this.key = key; } 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 String getDefaultValue() { return defaultValue; } public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; } public ConfigItemType getType() { return type; } public void setType(ConfigItemType type) { this.type = type; } public boolean isRequired() { return required; } public void setRequired(boolean required) { this.required = required; } public List getEnumValues() { return enumValues; } public void setEnumValues(List enumValues) { this.enumValues = enumValues; } /** * Builder for ConfigItemDefinition. */ public static class Builder { private final ConfigItemDefinition definition; public Builder(String key, String name, ConfigItemType type) { this.definition = new ConfigItemDefinition(key, name, type); } public Builder description(String description) { definition.setDescription(description); return this; } public Builder defaultValue(String defaultValue) { definition.setDefaultValue(defaultValue); return this; } public Builder required(boolean required) { definition.setRequired(required); return this; } public Builder enumValues(List enumValues) { definition.setEnumValues(enumValues); return this; } public ConfigItemDefinition build() { return definition; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/plugin/ConfigItemType.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.plugin; /** * Configuration item type enumeration. * * @author WangzJi * @since 3.2.0 */ public enum ConfigItemType { /** * String type configuration. */ STRING, /** * Number type configuration. */ NUMBER, /** * Boolean type configuration. */ BOOLEAN, /** * Enumeration type configuration. */ ENUM } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/plugin/PluginConfigSpec.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.plugin; import java.util.List; import java.util.Map; /** * Plugin configuration specification interface. * Allows plugins to declare configurable properties. * * @author WangzJi * @since 3.2.0 */ public interface PluginConfigSpec { /** * Get configuration item definitions. * * @return list of configuration item definitions */ List getConfigDefinitions(); /** * Apply configuration to the plugin. * * @param config configuration key-value pairs */ void applyConfig(Map config); /** * Get current configuration. * * @return current configuration as key-value pairs */ Map getCurrentConfig(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/plugin/PluginProvider.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.plugin; import java.util.Map; /** * Plugin provider SPI interface. * *

    Each plugin type should have one implementation to provide plugin instances. * This interface enables automatic plugin discovery through SPI mechanism, * eliminating the need to manually register each plugin type in UnifiedPluginManager. * *

    Example implementation: *

    {@code
     * public class AuthPluginProvider implements PluginProvider {
     *     @Override
     *     public PluginType getPluginType() {
     *         return PluginType.AUTH;
     *     }
     *
     *     @Override
     *     public Map getAllPlugins() {
     *         return AuthPluginManager.getInstance().getAllPlugins();
     *     }
     * }
     * }
    * * @param the plugin service type * @author WangzJi * @since 3.2.0 */ public interface PluginProvider { /** * Get the plugin type this provider manages. * * @return plugin type */ PluginType getPluginType(); /** * Get all plugin instances managed by this provider. * Key is the plugin name, value is the plugin instance. * * @return map of plugin name to plugin instance */ Map getAllPlugins(); /** * Get the order of this provider. Lower values have higher priority. * Used when multiple providers exist for same type. * * @return order value, default is 0 */ default int getOrder() { return 0; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/plugin/PluginStateChecker.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.plugin; /** * Plugin state checker interface. * Used to decouple plugin managers from core module. * * @author WangzJi * @since 3.0.0 */ public interface PluginStateChecker { /** * Check if plugin is enabled. * * @param pluginType plugin type string * @param pluginName plugin name * @return true if plugin is enabled, false otherwise */ boolean isPluginEnabled(String pluginType, String pluginName); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/plugin/PluginStateCheckerHolder.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.plugin; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; /** * Static holder for PluginStateChecker. * Bridges singleton pattern plugin managers with Spring-managed UnifiedPluginManager. * Uses AtomicReference to ensure thread safety. * * @author WangzJi * @since 3.2.0 */ public class PluginStateCheckerHolder { private static final AtomicReference INSTANCE = new AtomicReference<>(); private PluginStateCheckerHolder() { } /** * Set the PluginStateChecker instance. * * @param checker the PluginStateChecker instance */ public static void setInstance(PluginStateChecker checker) { INSTANCE.set(checker); } /** * Get the PluginStateChecker instance. * * @return Optional containing the PluginStateChecker instance, or empty if not set */ public static Optional getInstance() { return Optional.ofNullable(INSTANCE.get()); } /** * Check if a plugin is enabled. * Returns true if no checker is set (backward compatibility). * * @param pluginType plugin type string * @param pluginName plugin name * @return true if plugin is enabled or no checker is set */ public static boolean isPluginEnabled(String pluginType, String pluginName) { PluginStateChecker checker = INSTANCE.get(); if (checker == null) { return true; } return checker.isPluginEnabled(pluginType, pluginName); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/plugin/PluginType.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.plugin; /** * Plugin type enumeration, supports all Nacos plugin types. * * @author WangzJi * @since 3.2.0 */ public enum PluginType { /** * Authentication plugin. */ AUTH("auth", "Authentication plugin"), /** * Datasource dialect plugin. */ DATASOURCE_DIALECT("datasource-dialect", "Datasource dialect plugin"), /** * Config change plugin. */ CONFIG_CHANGE("config-change", "Config change plugin"), /** * Encryption plugin. */ ENCRYPTION("encryption", "Encryption plugin"), /** * Trace plugin. */ TRACE("trace", "Trace plugin"), /** * Environment plugin. */ ENVIRONMENT("environment", "Environment plugin"), /** * Control plugin. */ CONTROL("control", "Control plugin"); private final String type; private final String description; PluginType(String type, String description) { this.type = type; this.description = description; } public String getType() { return type; } public String getDescription() { return description; } /** * Get PluginType from type string. * * @param type type string * @return PluginType * @throws IllegalArgumentException if type is unknown */ public static PluginType fromType(String type) { for (PluginType pt : values()) { if (pt.type.equals(type)) { return pt; } } throw new IllegalArgumentException("Unknown plugin type: " + type); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/AbstractPushCallBack.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; /** * abstract callback of push service. * * @author liuzunfei * @version $Id: PushCallBack.java, v 0.1 2020年07月20日 1:13 PM liuzunfei Exp $ */ public abstract class AbstractPushCallBack implements PushCallBack { private long timeout; public AbstractPushCallBack(long timeout) { this.timeout = timeout; } @Override public long getTimeout() { return timeout; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/AbstractRequestCallBack.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; /** * abstract request call back. * * @author liuzunfei * @version $Id: AbstractRequestCallBack.java, v 0.1 2020年09月07日 3:30 PM liuzunfei Exp $ */ public abstract class AbstractRequestCallBack implements RequestCallBack { long timeoutMills; public AbstractRequestCallBack(long timeoutMill) { this.timeoutMills = timeoutMill; } public AbstractRequestCallBack() { this(3000L); } @Override public long getTimeout() { return timeoutMills; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/DefaultRequestFuture.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import com.alibaba.nacos.api.remote.response.Response; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * default request future. * * @author liuzunfei * @version $Id: DefaultRequestFuture.java, v 0.1 2020年09月01日 6:42 PM liuzunfei Exp $ */ public class DefaultRequestFuture implements RequestFuture { private long timeStamp; private volatile boolean isDone = false; private boolean isSuccess; private RequestCallBack requestCallBack; private Exception exception; private String requestId; private String connectionId; private Response response; private ScheduledFuture timeoutFuture; FutureTrigger futureTrigger; /** * Getter method for property requestCallBack. * * @return property value of requestCallBack */ public RequestCallBack getRequestCallBack() { return requestCallBack; } /** * Getter method for property timeStamp. * * @return property value of timeStamp */ public long getTimeStamp() { return timeStamp; } public DefaultRequestFuture(String connectionId, String requestId) { this(connectionId, requestId, null, null); } public DefaultRequestFuture(String connectionId, String requestId, RequestCallBack requestCallBack, FutureTrigger futureTrigger) { this.timeStamp = System.currentTimeMillis(); this.requestCallBack = requestCallBack; this.requestId = requestId; this.connectionId = connectionId; if (requestCallBack != null) { this.timeoutFuture = RpcScheduledExecutor.TIMEOUT_SCHEDULER.schedule(new TimeoutHandler(), requestCallBack.getTimeout(), TimeUnit.MILLISECONDS); } this.futureTrigger = futureTrigger; } public void setResponse(final Response response) { isDone = true; this.response = response; this.isSuccess = response.isSuccess(); if (this.timeoutFuture != null) { timeoutFuture.cancel(true); } synchronized (this) { notifyAll(); } callBacInvoke(); } public void setFailResult(Exception e) { isDone = true; isSuccess = false; this.exception = e; synchronized (this) { notifyAll(); } callBacInvoke(); } private void callBacInvoke() { if (requestCallBack != null) { if (requestCallBack.getExecutor() != null) { requestCallBack.getExecutor().execute(new CallBackHandler()); } else { new CallBackHandler().run(); } } } public String getRequestId() { return this.requestId; } @Override public boolean isDone() { return isDone; } @Override public Response get() throws InterruptedException { synchronized (this) { while (!isDone) { wait(); } } return response; } @Override public Response get(long timeout) throws TimeoutException, InterruptedException { if (timeout < 0) { synchronized (this) { while (!isDone) { wait(); } } } else if (timeout > 0) { long end = System.currentTimeMillis() + timeout; long waitTime = timeout; synchronized (this) { while (!isDone && waitTime > 0) { wait(waitTime); waitTime = end - System.currentTimeMillis(); } } } if (isDone) { return response; } else { if (timeoutFuture == null && futureTrigger != null) { futureTrigger.triggerOnTimeout(); } throw new TimeoutException( "request timeout after " + timeout + " milliseconds, requestId=" + requestId + ", connectionId=" + connectionId); } } class CallBackHandler implements Runnable { @Override public void run() { if (exception != null) { requestCallBack.onException(exception); } else { requestCallBack.onResponse(response); } } } class TimeoutHandler implements Runnable { public TimeoutHandler() { } @Override public void run() { setFailResult(new TimeoutException( "Timeout After " + requestCallBack.getTimeout() + " milliseconds, requestId=" + requestId + ", connectionId=" + connectionId)); if (futureTrigger != null) { futureTrigger.triggerOnTimeout(); } } } /** * Cleaning something while request has been failed, canceled, timeout. */ public interface FutureTrigger { /** * default trigger for {@link #triggerOnTimeout()} and {@link #triggerOnCancel()}. */ void defaultTrigger(); /** * triggered on timeout . */ default void triggerOnTimeout() { defaultTrigger(); } /** * triggered on cancel. */ default void triggerOnCancel() { defaultTrigger(); } } /** * Getter method for property connectionId. * * @return property value of connectionId */ public String getConnectionId() { return connectionId; } /** * Cancel the request. It should be called in * {@link com.alibaba.nacos.core.remote.grpc.GrpcConnection#sendRequestInner} * NOTE: For sync requests(which without requestCallBack), the cancel operation is always invalid. * * @param mayInterruptIfRunning whether to interrupt the thread */ public void cancel(boolean mayInterruptIfRunning) { synchronized (this) { notifyAll(); } // cancel timeout task. if (timeoutFuture != null && !timeoutFuture.isDone()) { boolean cancel = timeoutFuture.cancel(mayInterruptIfRunning); if (cancel && futureTrigger != null) { futureTrigger.triggerOnCancel(); } } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/Payload.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; /** * payload class sign. * * @author hujun */ public interface Payload { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/PushCallBack.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; /** * callback of push service. * * @author liuzunfei * @version $Id: PushCallBack.java, v 0.1 2020年07月20日 1:13 PM liuzunfei Exp $ */ public interface PushCallBack { /** * Push timeout mills. * * @return timeout milliseconds */ long getTimeout(); /** * invoked on success. */ void onSuccess(); /** * invoked on fail. * * @param e exception throwed. */ void onFail(Throwable e); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/RemoteConstants.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; /** * constants define of remote. * * @author liuzunfei * @version $Id: ConnectionMetaConstants.java, v 0.1 2020年08月13日 1:05 PM liuzunfei Exp $ */ public class RemoteConstants { /** * label key value define. */ public static final String LABEL_SOURCE = "source"; public static final String LABEL_SOURCE_SDK = "sdk"; public static final String LABEL_SOURCE_CLUSTER = "cluster"; public static final String LABEL_MODULE = "module"; public static final String LABEL_MODULE_CONFIG = "config"; public static final String LABEL_MODULE_NAMING = "naming"; public static final String MONITOR_LABEL_NONE = "none"; public static final String LABEL_MODULE_LOCK = "lock"; public static final String LABEL_MODULE_AI = "ai"; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/RequestCallBack.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import com.alibaba.nacos.api.remote.response.Response; import java.util.concurrent.Executor; /** * call back for request. * * @author liuzunfei * @version $Id: PushCallBack.java, v 0.1 2020年09月01日 6:33 PM liuzunfei Exp $ */ public interface RequestCallBack { /** * get executor on callback. * * @return executor. */ Executor getExecutor(); /** * get timeout mills. * * @return timeouts. */ long getTimeout(); /** * called on success. * * @param response response received. */ void onResponse(T response); /** * called on failed. * * @param e exception throwed. */ void onException(Throwable e); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/RequestFuture.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import com.alibaba.nacos.api.remote.response.Response; /** * future for request. * * @author liuzunfei * @version $Id: RequestFuture.java, v 0.1 2020年09月01日 6:31 PM liuzunfei Exp $ */ public interface RequestFuture { /** * check that it is done or not.. * * @return is done . */ boolean isDone(); /** * get response without timeouts. * * @return return response if done. * @throws Exception exception throws . */ Response get() throws Exception; /** * get response with a given timeouts. * * @param timeout timeout milliseconds. * @return return response if done. * @throws Exception exception throws . */ Response get(long timeout) throws Exception; } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/Requester.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; /** * connection interface,define basic operation. * * @author liuzunfei * @version $Id: Requester.java, v 0.1 2020年09月11日 4:05 PM liuzunfei Exp $ */ public interface Requester { /** * send request. * * @param request request. * @param timeoutMills mills of timeouts. * @return response response returned. * @throws NacosException exception throw. */ Response request(Request request, long timeoutMills) throws NacosException; /** * send request. * * @param request request. * @return request future. * @throws NacosException exception throw. */ RequestFuture requestFuture(Request request) throws NacosException; /** * send async request. * * @param request request. * @param requestCallBack callback of request. * @throws NacosException exception throw. */ void asyncRequest(Request request, RequestCallBack requestCallBack) throws NacosException; /** * close connection. */ void close(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/RpcScheduledExecutor.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; /** * rpc scheduler executor . * * @author liuzunfei * @version $Id: RpcScheduledExecutor.java, v 0.1 2020年09月07日 4:12 PM liuzunfei Exp $ */ public class RpcScheduledExecutor extends ScheduledThreadPoolExecutor { public static final RpcScheduledExecutor TIMEOUT_SCHEDULER = new RpcScheduledExecutor(1, "com.alibaba.nacos.remote.TimerScheduler"); public static final RpcScheduledExecutor CONTROL_SCHEDULER = new RpcScheduledExecutor(1, "com.alibaba.nacos.control.DelayScheduler"); public static final RpcScheduledExecutor COMMON_SERVER_EXECUTOR = new RpcScheduledExecutor(1, "com.alibaba.nacos.remote.ServerCommonScheduler"); public RpcScheduledExecutor(int corePoolSize, final String threadName) { super(corePoolSize, new ThreadFactory() { private final AtomicLong index = new AtomicLong(); @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, threadName + "." + index.getAndIncrement()); thread.setDaemon(true); return thread; } }); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/ability/ClientRemoteAbility.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.ability; import java.io.Serializable; /** * remote abilities of nacos client. * * @author liuzunfei * @version $Id: ClientRemoteAbility.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ public class ClientRemoteAbility implements Serializable { private static final long serialVersionUID = -5794603724540260652L; /** * if support remote connection. */ private boolean supportRemoteConnection; public boolean isSupportRemoteConnection() { return this.supportRemoteConnection; } public void setSupportRemoteConnection(boolean supportRemoteConnection) { this.supportRemoteConnection = supportRemoteConnection; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/ability/ServerRemoteAbility.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.ability; import java.io.Serializable; import java.util.Objects; /** * remote abilities of nacos server. * * @author liuzunfei * @version $Id: ServerRemoteAbility.java, v 0.1 2021年01月24日 00:09 AM liuzunfei Exp $ */ public class ServerRemoteAbility implements Serializable { private static final long serialVersionUID = -3069795759506428390L; /** * if support remote connection. */ private boolean supportRemoteConnection; /** * if support grpc report. */ private boolean grpcReportEnabled = true; public boolean isSupportRemoteConnection() { return this.supportRemoteConnection; } public void setSupportRemoteConnection(boolean supportRemoteConnection) { this.supportRemoteConnection = supportRemoteConnection; } public boolean isGrpcReportEnabled() { return grpcReportEnabled; } public void setGrpcReportEnabled(boolean grpcReportEnabled) { this.grpcReportEnabled = grpcReportEnabled; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ServerRemoteAbility that = (ServerRemoteAbility) o; return supportRemoteConnection == that.supportRemoteConnection && grpcReportEnabled == that.grpcReportEnabled; } @Override public int hashCode() { return Objects.hash(supportRemoteConnection, grpcReportEnabled); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/ClientDetectionRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import static com.alibaba.nacos.api.common.Constants.Remote.INTERNAL_MODULE; /** * client active detection request from server. * * @author liuzunfei * @version $Id: ClientDetectionRequest.java, v 0.1 2021年01月20日 2:42 PM liuzunfei Exp $ */ public class ClientDetectionRequest extends ServerRequest { @Override public String getModule() { return INTERNAL_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/ConnectResetRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import static com.alibaba.nacos.api.common.Constants.Remote.INTERNAL_MODULE; /** * ConnectResetRequest. * * @author liuzunfei * @version $Id: ConnectResetRequest.java, v 0.1 2020年07月15日 11:11 AM liuzunfei Exp $ */ public class ConnectResetRequest extends ServerRequest { String serverIp; String serverPort; String connectionId; @Override public String getModule() { return INTERNAL_MODULE; } /** * Getter method for property connectionId. * * @return property value of connectionId */ public String getConnectionId() { return connectionId; } /** * Setter method for property connectionId. * * @param connectionId value to be assigned to property connectionId */ public void setConnectionId(String connectionId) { this.connectionId = connectionId; } /** * Getter method for property serverIp. * * @return property value of serverIp */ public String getServerIp() { return serverIp; } /** * Setter method for property serverIp. * * @param serverIp value to be assigned to property serverIp */ public void setServerIp(String serverIp) { this.serverIp = serverIp; } /** * Getter method for property serverPort. * * @return property value of serverPort */ public String getServerPort() { return serverPort; } /** * Setter method for property serverPort. * * @param serverPort value to be assigned to property serverPort */ public void setServerPort(String serverPort) { this.serverPort = serverPort; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/ConnectionSetupRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import java.util.HashMap; import java.util.Map; /** * request to setup a connection. * * @author liuzunfei * @version $Id: ConnectionSetupRequest.java, v 0.1 2020年08月06日 2:42 PM liuzunfei Exp $ */ public class ConnectionSetupRequest extends InternalRequest { private String clientVersion; private String tenant; private Map labels = new HashMap<>(); private Map abilityTable; public ConnectionSetupRequest() { } public String getClientVersion() { return clientVersion; } public void setClientVersion(String clientVersion) { this.clientVersion = clientVersion; } public Map getLabels() { return labels; } public void setLabels(Map labels) { this.labels = labels; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public Map getAbilityTable() { return abilityTable; } public void setAbilityTable(Map abilityTable) { this.abilityTable = abilityTable; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/HealthCheckRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; /** * request to check server if unimpeded. * * @author liuzunfei * @version $Id: ServerCheckRequest.java, v 0.1 2020年07月22日 8:32 PM liuzunfei Exp $ */ public class HealthCheckRequest extends InternalRequest { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/InternalRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import static com.alibaba.nacos.api.common.Constants.Remote.INTERNAL_MODULE; /** * internal request . * * @author liuzunfei * @version $Id: InternalRequest.java, v 0.1 2020年07月22日 8:33 PM liuzunfei Exp $ */ public abstract class InternalRequest extends Request { @Override public String getModule() { return INTERNAL_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/PushAckRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; /** * push ack request. * * @author liuzunfei * @version $Id: PushAckRequest.java, v 0.1 2020年07月29日 8:25 PM liuzunfei Exp $ */ public class PushAckRequest extends InternalRequest { private String requestId; private boolean success; private Exception exception; /** * build push ack request. * * @param requestId requestId. * @return request. */ public static PushAckRequest build(String requestId, boolean success) { PushAckRequest request = new PushAckRequest(); request.requestId = requestId; request.success = success; return request; } /** * Getter method for property requestId. * * @return property value of requestId */ @Override public String getRequestId() { return requestId; } /** * Setter method for property requestId. * * @param requestId value to be assigned to property requestId */ @Override public void setRequestId(String requestId) { this.requestId = requestId; } /** * Getter method for property success. * * @return property value of success */ public boolean isSuccess() { return success; } /** * Setter method for property success. * * @param success value to be assigned to property success */ public void setSuccess(boolean success) { this.success = success; } /** * Setter method for property exception. * * @param exception value to be assigned to property exception */ public void setException(Exception exception) { this.exception = exception; } /** * Getter method for property exception. * * @return property value of exception */ public Exception getException() { return exception; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/Request.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import com.alibaba.nacos.api.remote.Payload; import java.util.Map; import java.util.TreeMap; /** * Request. * * @author liuzunfei */ public abstract class Request implements Payload { private final Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); private String requestId; /** * put header. * * @param key key of value. * @param value value. */ public void putHeader(String key, String value) { headers.put(key, value); } /** * put headers . * * @param headers headers to put. */ public void putAllHeader(Map headers) { if (headers == null || headers.isEmpty()) { return; } this.headers.putAll(headers); } /** * get a header value . * * @param key key of value. * @return return value of key. return null if not exist. */ public String getHeader(String key) { return headers.get(key); } /** * get a header value of default value. * * @param key key of value. * @param defaultValue default value if key is not exist. * @return return final value. */ public String getHeader(String key, String defaultValue) { String value = headers.get(key); return (value == null) ? defaultValue : value; } /** * Getter method for property requestId. * * @return property value of requestId */ public String getRequestId() { return requestId; } /** * Setter method for property requestId. * * @param requestId value to be assigned to property requestId */ public void setRequestId(String requestId) { this.requestId = requestId; } /** * Getter method for property type. * * @return property value of type */ public abstract String getModule(); /** * Getter method for property headers. * * @return property value of headers */ public Map getHeaders() { return headers; } public void clearHeaders() { this.headers.clear(); } @Override public String toString() { return this.getClass().getSimpleName() + "{" + "headers=" + headers + ", requestId='" + requestId + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/RequestMeta.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.common.Constants; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * RequestMeta info. * * @author liuzunfei * @version $Id: RequestMeta.java, v 0.1 2020年07月14日 10:32 AM liuzunfei Exp $ */ public class RequestMeta { private String connectionId = ""; private String clientIp = ""; private String clientVersion = ""; private Map labels = new HashMap<>(); private Map appLabels = new HashMap<>(); private Map abilityTable; public AbilityStatus getConnectionAbility(AbilityKey abilityKey) { if (abilityTable == null || !abilityTable.containsKey(abilityKey.getName())) { return AbilityStatus.UNKNOWN; } return abilityTable.get(abilityKey.getName()) ? AbilityStatus.SUPPORTED : AbilityStatus.NOT_SUPPORTED; } /** * Setter method for property abilityTable. * * @param abilityTable property value of clientVersion */ public void setAbilityTable(Map abilityTable) { this.abilityTable = abilityTable; } /** * Getter method for property clientVersion. * * @return property value of clientVersion */ public String getClientVersion() { return clientVersion; } /** * Setter method for property clientVersion. * * @param clientVersion value to be assigned to property clientVersion */ public void setClientVersion(String clientVersion) { this.clientVersion = clientVersion; } /** * Getter method for property labels. * * @return property value of labels */ public Map getLabels() { return labels; } /** * Setter method for property labels. * * @param labels value to be assigned to property labels */ public void setLabels(Map labels) { this.labels = labels; extractAppLabels(); } private void extractAppLabels() { HashMap applabelsMap = new HashMap(8) { { put(Constants.APPNAME, labels.get(Constants.APPNAME)); put(Constants.CLIENT_VERSION_KEY, clientVersion); put(Constants.CLIENT_IP, clientIp); } }; labels.entrySet().stream().filter(Objects::nonNull).filter(e -> e.getKey().startsWith(Constants.APP_CONN_PREFIX) && e.getKey().length() > Constants.APP_CONN_PREFIX.length() && !e.getValue().trim().isEmpty()) .forEach(entry -> { applabelsMap.putIfAbsent(entry.getKey().substring(Constants.APP_CONN_PREFIX.length()), entry.getValue()); }); this.appLabels = applabelsMap; } /** * get labels map with filter of starting with prefix #{@link Constants#APP_CONN_PREFIX} and return a new map trim * the prefix #{@link Constants#APP_CONN_PREFIX}. * * @return map of labels. * @date 2024/2/29 */ public Map getAppLabels() { return appLabels; } /** * Getter method for property connectionId. * * @return property value of connectionId */ public String getConnectionId() { return connectionId; } /** * Setter method for property connectionId. * * @param connectionId value to be assigned to property connectionId */ public void setConnectionId(String connectionId) { this.connectionId = connectionId; } /** * Getter method for property clientIp. * * @return property value of clientIp */ public String getClientIp() { return clientIp; } /** * Setter method for property clientIp. * * @param clientIp value to be assigned to property clientIp */ public void setClientIp(String clientIp) { this.clientIp = clientIp; } @Override public String toString() { return "RequestMeta{" + "connectionId='" + connectionId + '\'' + ", clientIp='" + clientIp + '\'' + ", clientVersion='" + clientVersion + '\'' + ", labels=" + labels + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/ServerCheckRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; /** * request to check server if unimpeded. * * @author liuzunfei * @version $Id: ServerCheckRequest.java, v 0.1 2020年07月22日 8:32 PM liuzunfei Exp $ */ public class ServerCheckRequest extends InternalRequest { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/ServerLoaderInfoRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; /** * get server node loader info. * @author liuzunfei * @version $Id: ServerLoaderInfoRequest.java, v 0.1 2020年09月03日 2:45 PM liuzunfei Exp $ */ public class ServerLoaderInfoRequest extends InternalRequest { public ServerLoaderInfoRequest() { } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/ServerReloadRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; /** * reload long connection request. * * @author liuzunfei * @version $Id: ServerReloadRequest.java, v 0.1 2020年11月09日 4:35 PM liuzunfei Exp $ */ public class ServerReloadRequest extends InternalRequest { int reloadCount = 0; String reloadServer; /** * Getter method for property reloadCount. * * @return property value of reloadCount */ public int getReloadCount() { return reloadCount; } /** * Setter method for property reloadCount. * * @param reloadCount value to be assigned to property reloadCount */ public void setReloadCount(int reloadCount) { this.reloadCount = reloadCount; } public String getReloadServer() { return reloadServer; } public void setReloadServer(String reloadServer) { this.reloadServer = reloadServer; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/ServerRequest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; /** * ServerPushResponse. * * @author liuzunfei * @version $Id: ServerPushResponse.java, v 0.1 2020年07月20日 1:21 PM liuzunfei Exp $ */ public abstract class ServerRequest extends Request { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/request/SetupAckRequest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import java.util.Map; import static com.alibaba.nacos.api.common.Constants.Remote.INTERNAL_MODULE; /** * Server tells the client that the connection is established. * * @author Daydreamer. * @date 2022/7/12 19:21 **/ public class SetupAckRequest extends ServerRequest { private Map abilityTable; public SetupAckRequest() { } public SetupAckRequest(Map abilityTable) { this.abilityTable = abilityTable; } public Map getAbilityTable() { return abilityTable; } public void setAbilityTable(Map abilityTable) { this.abilityTable = abilityTable; } @Override public String getModule() { return INTERNAL_MODULE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/ClientDetectionResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; /** * response of client active detection check. * * @author liuzunfei * @version $Id: ServerCheckResponse.java, v 0.1 2021年01月20日 10:37 PM liuzunfei Exp $ */ public class ClientDetectionResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/ConnectResetResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; /** * connection reset response. * * @author liuzunfei * @version $Id: ConnectResetResponse.java, v 0.1 2020年09月01日 2:43 PM liuzunfei Exp $ */ public class ConnectResetResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/ErrorResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; /** * UnKnowResponse. * * @author liuzunfei * @version $Id: UnKnowResponse.java, v 0.1 2020年07月16日 9:47 PM liuzunfei Exp $ */ public class ErrorResponse extends Response { /** * build an error response. * * @param errorCode errorCode * @param msg msg * @return response */ public static Response build(int errorCode, String msg) { ErrorResponse response = new ErrorResponse(); response.setErrorInfo(errorCode, msg); return response; } /** * build an error response. * * @param exception exception * @return response */ public static Response build(Throwable exception) { int errorCode; if (exception instanceof NacosException) { errorCode = ((NacosException) exception).getErrCode(); } else if (exception instanceof NacosRuntimeException) { errorCode = ((NacosRuntimeException) exception).getErrCode(); } else { errorCode = ResponseCode.FAIL.getCode(); } ErrorResponse response = new ErrorResponse(); response.setErrorInfo(errorCode, exception.getMessage()); response.setResultCode(errorCode); return response; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/HealthCheckResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; /** * response of server check. * * @author liuzunfei * @version $Id: ServerCheckResponse.java, v 0.1 2020年07月22日 8:37 PM liuzunfei Exp $ */ public class HealthCheckResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/Response.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; import com.alibaba.nacos.api.remote.Payload; /** * abstract response model via rpc channel. * * @author liuzunfei * @version $Id: Response.java, v 0.1 2020年07月13日 6:03 PM liuzunfei Exp $ */ public abstract class Response implements Payload { int resultCode = ResponseCode.SUCCESS.getCode(); int errorCode; String message; String requestId; /** * Getter method for property requestId. * * @return property value of requestId */ public String getRequestId() { return requestId; } /** * Setter method for property requestId. * * @param requestId value to be assigned to property requestId */ public void setRequestId(String requestId) { this.requestId = requestId; } /** * Check Response is Successed. * * @return success or not. */ public boolean isSuccess() { return this.resultCode == ResponseCode.SUCCESS.getCode(); } /** * Getter method for property resultCode. * * @return property value of resultCode */ public int getResultCode() { return resultCode; } /** * Setter method for property resultCode. * * @param resultCode value to be assigned to property resultCode */ public void setResultCode(int resultCode) { this.resultCode = resultCode; } /** * Getter method for property message. * * @return property value of message */ public String getMessage() { return message; } /** * Setter method for property message. * * @param message value to be assigned to property message */ public void setMessage(String message) { this.message = message; } /** * Getter method for property errorCode. * * @return property value of errorCode */ public int getErrorCode() { return errorCode; } /** * Setter method for property errorCode. * * @param errorCode value to be assigned to property errorCode */ public void setErrorCode(int errorCode) { this.errorCode = errorCode; } public void setErrorInfo(int errorCode, String errorMsg) { this.resultCode = ResponseCode.FAIL.getCode(); this.errorCode = errorCode; this.message = errorMsg; } @Override public String toString() { return "Response{" + "resultCode=" + resultCode + ", errorCode=" + errorCode + ", message='" + message + '\'' + ", requestId='" + requestId + '\'' + '}'; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/ResponseCode.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; /** * ResponseCode. * * @author liuzunfei * @version $Id: ResponseCode.java, v 0.1 2020年07月14日 2:04 PM liuzunfei Exp $ */ public enum ResponseCode { /** * Request success. */ SUCCESS(200, "Response ok"), /** * Request failed. */ FAIL(500, "Response fail"); int code; String desc; ResponseCode(int code, String desc) { this.code = code; this.desc = desc; } /** * Getter method for property code. * * @return property value of code */ public int getCode() { return code; } /** * Getter method for property desc. * * @return property value of desc */ public String getDesc() { return desc; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/ServerCheckResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; /** * response of server check. * * @author liuzunfei * @version $Id: ServerCheckResponse.java, v 0.1 2020年07月22日 8:37 PM liuzunfei Exp $ */ public class ServerCheckResponse extends Response { private String connectionId; private boolean supportAbilityNegotiation; public ServerCheckResponse() { } public ServerCheckResponse(String connectionId, boolean supportAbilityNegotiation) { this.connectionId = connectionId; this.supportAbilityNegotiation = supportAbilityNegotiation; } public String getConnectionId() { return connectionId; } public void setConnectionId(String connectionId) { this.connectionId = connectionId; } public boolean isSupportAbilityNegotiation() { return supportAbilityNegotiation; } public void setSupportAbilityNegotiation(boolean supportAbilityNegotiation) { this.supportAbilityNegotiation = supportAbilityNegotiation; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/ServerLoaderInfoResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; import java.util.HashMap; import java.util.Map; /** * server loader info response. * * @author liuzunfei * @version $Id: ServerLoaderInfoResponse.java, v 0.1 2020年09月03日 2:46 PM liuzunfei Exp $ */ public class ServerLoaderInfoResponse extends Response { String address; Map loaderMetrics = new HashMap<>(); public String getMetricsValue(String key) { return loaderMetrics.get(key); } public void putMetricsValue(String key, String value) { this.loaderMetrics.put(key, value); } /** * Getter method for property loaderMetrics. * * @return property value of loaderMetrics */ public Map getLoaderMetrics() { return loaderMetrics; } /** * Setter method for property loaderMetrics. * * @param loaderMetrics value to be assigned to property loaderMetrics */ public void setLoaderMetrics(Map loaderMetrics) { this.loaderMetrics = loaderMetrics; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/ServerReloadResponse.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; /** * server reload response. * * @author liuzunfei * @version $Id: ServerReloadResponse.java, v 0.1 2020年11月09日 4:37 PM liuzunfei Exp $ */ public class ServerReloadResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/remote/response/SetupAckResponse.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; /**. * @author Daydreamer * @description Server tells the client that the connection is established * @date 2022/7/12 19:21 **/ public class SetupAckResponse extends Response { } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/AbstractCmdbSelector.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.selector.context.CmdbContext; import java.util.List; import static com.alibaba.nacos.api.common.Constants.Naming.CMDB_CONTEXT_TYPE; /** * {@link AbstractCmdbSelector} will provide one default implement of {@link Selector}, users can implement it to use the {@link CmdbContext}. * And return the result as default subclass of {@link Instance}. * * @author chenglu * @date 2021-07-09 21:29 */ public abstract class AbstractCmdbSelector implements Selector, CmdbContext, String> { private static final long serialVersionUID = 56587385358330901L; /** * the labels expression. */ protected String expression; public String getExpression() { return expression; } public void setExpression(String expression) { this.expression = expression; } @Override public Selector, CmdbContext, String> parse(String expression) throws NacosException { this.expression = expression; doParse(expression); return this; } /** * The real parse logic implement by sub class. * * @param expression expression. * @throws NacosException parse failed exception. */ protected abstract void doParse(String expression) throws NacosException; @Override public List select(CmdbContext context) { return doSelect(context); } /** * The real select implement by subclass. * * @param context selector context {@link CmdbContext}. * @return the select result. */ protected abstract List doSelect(CmdbContext context); @Override public String getContextType() { return CMDB_CONTEXT_TYPE; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/AbstractSelector.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; import java.io.Serializable; import java.util.List; /** * Abstract selector that only contains a type, used for api to set selector type without actual selector logic. * * @author nkorange * @since 0.7.0 */ @JsonTypeInfo(use = Id.NAME, property = "type", defaultImpl = NoneSelector.class) public abstract class AbstractSelector implements Serializable, Selector, List, String> { private static final long serialVersionUID = 4530233098102379229L; /** * The type of this selector, each child class should announce its own unique type. */ private final String type; protected AbstractSelector(String type) { this.type = type; } public String getType() { return type; } @Override public Selector, List, String> parse(String expression) throws NacosException { return null; } @Override public List select(List context) { return context; } @Override public String getContextType() { return SelectorType.none.name(); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/ExpressionSelector.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; /** * The selector to filter resource with flexible expression. * * @author nkorange * @since 0.7.0 */ public class ExpressionSelector extends AbstractSelector { /** * Label expression of this selector. */ private String expression; public ExpressionSelector() { super(SelectorType.label.name()); } public String getExpression() { return expression; } public void setExpression(String expression) { this.expression = expression; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/NoneSelector.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; /** * None selector. * * @author liaochuntao * @since 1.0.1 */ public class NoneSelector extends AbstractSelector { public NoneSelector() { super(SelectorType.none.name()); } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/Selector.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; import com.alibaba.nacos.api.exception.NacosException; import com.fasterxml.jackson.annotation.JsonTypeInfo; import java.io.Serializable; /** * The {@link Selector} mainly work on the logic of parse and select. * {@link #parse(Object)} allow user accept the condition which provided by Nacos to build the {@link #select(Object)} judge logic. * {@link #select(Object)} allow user to execute the logic to get the target result they want. * {@link #getType()} will return the type. * {@link #getContextType()} will return the context type which used by {@link #select(Object)}. * *

    * Now, Nacos only provide the {@link AbstractCmdbSelector} for user to implement their own select logic. Other type is waiting. *

    * * @author chenglu * @date 2021-07-09 21:24 */ @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type") public interface Selector extends Serializable { /** * parse the selector, build the inner info which used by {@link #select(Object)}. * * @param expression expression. * @return selector. * @throws NacosException parse failed exception. */ Selector parse(E expression) throws NacosException; /** * select the target result. * * @param context selector context. * @return select result. */ R select(C context); /** * Get the selector type. * * @return selector type. */ String getType(); /** * Get the select context which used by {@link #select(Object)}. * * @return selector context type. */ String getContextType(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/SelectorType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; /** * The types of selector accepted by Nacos. * * @author nkorange * @since 0.7.0 */ public enum SelectorType { /** * not match any type. */ unknown, /** * not filter out any entity. */ none, /** * select by label. */ label, /** * select by cluster. */ cluster, /** * select by health state. */ health, /** * select by enable state. */ enable } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/client/SelectResult.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector.client; /** * Select result. * * @param the type of result * @author lideyou */ public interface SelectResult { /** * Get select result. * * @return select result */ T getResult(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/client/Selector.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector.client; /** * Client selector. * * @param the type of selector context * @param the type of select result * @author lideyou */ public interface Selector { /** * select the target result. * * @param context selector context * @return select result */ E select(C context); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/context/CmdbContext.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector.context; import com.alibaba.nacos.api.cmdb.pojo.Entity; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.selector.Selector; import java.util.List; /** * The CMDB context is given by the {@link SelectorContextBuilder#build(Object, Object)} and used for the * {@link Selector#select(Object)}. * * @author chenglu * @date 2021-07-09 21:31 */ public class CmdbContext { /** * consumer is the instance which provide the info. */ private CmdbInstance consumer; /** * the providers which be selected. */ private List> providers; public CmdbInstance getConsumer() { return consumer; } public void setConsumer(CmdbInstance consumer) { this.consumer = consumer; } public List> getProviders() { return providers; } public void setProviders(List> providers) { this.providers = providers; } @Override public String toString() { return "CmdbContext{" + "consumer=" + consumer + ", providers=" + providers + '}'; } public static class CmdbInstance { /** * the CMDB info of instance. */ private Entity entity; /** * the instance. */ private T instance; public Entity getEntity() { return entity; } public void setEntity(Entity entity) { this.entity = entity; } public T getInstance() { return instance; } public void setInstance(T instance) { this.instance = instance; } @Override public String toString() { return "CmdbInstance{" + "entity=" + entity + ", instance=" + instance + '}'; } } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/selector/context/SelectorContextBuilder.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector.context; import com.alibaba.nacos.api.selector.Selector; /** * The {@link SelectorContextBuilder} mainly for provide the context for {@link Selector#select(Object)}. * It provides {@link #build(Object, Object)} method for build context. And also provide {@link #getContextType()} for get the contextType. * * @author chenglu * @date 2021-07-09 21:34 */ public interface SelectorContextBuilder { /** * build the context for {@link Selector#select(Object)}. The user must provide consumer and provider. * we provide {@link CmdbContext} for user default who want to use the {@link com.alibaba.nacos.api.naming.pojo.Instance}'s CMDB info. * * @param consumer consumer who launch the select. * @param provider the provides who are selected by consumer. * @return selectorContext use by {@link Selector#select(Object)}. */ T build(C consumer, P provider); /** * the contextType. we provide the CMDB context type default. * * @return the context type. */ String getContextType(); } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/utils/NetUtils.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.utils; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.UnknownHostException; import java.util.Enumeration; /** * Net utils. * * @author xuanyin.zy */ public class NetUtils { private static final String CLIENT_LOCAL_IP_PROPERTY = "com.alibaba.nacos.client.local.ip"; private static final String CLIENT_LOCAL_PREFER_HOSTNAME_PROPERTY = "com.alibaba.nacos.client.local.preferHostname"; private static final String LEGAL_LOCAL_IP_PROPERTY = "java.net.preferIPv6Addresses"; private static final String DEFAULT_SOLVE_FAILED_RETURN = "resolve_failed"; private static String localIp; /** * Get local ip. * * @return local ip */ public static String localIp() { if (!StringUtils.isEmpty(localIp)) { return localIp; } if (System.getProperties().containsKey(CLIENT_LOCAL_IP_PROPERTY)) { return localIp = System.getProperty(CLIENT_LOCAL_IP_PROPERTY, getAddress()); } localIp = getAddress(); return localIp; } private static String getAddress() { InetAddress inetAddress = findFirstNonLoopbackAddress(); if (inetAddress == null) { return DEFAULT_SOLVE_FAILED_RETURN; } boolean preferHost = Boolean.parseBoolean(System.getProperty(CLIENT_LOCAL_PREFER_HOSTNAME_PROPERTY)); return preferHost ? inetAddress.getHostName() : inetAddress.getHostAddress(); } private static InetAddress findFirstNonLoopbackAddress() { InetAddress result = null; try { int lowest = Integer.MAX_VALUE; for (Enumeration nics = NetworkInterface.getNetworkInterfaces(); nics.hasMoreElements(); ) { NetworkInterface ifc = nics.nextElement(); if (ifc.isUp()) { if (ifc.getIndex() < lowest || result == null) { lowest = ifc.getIndex(); } else { continue; } for (Enumeration addrs = ifc.getInetAddresses(); addrs.hasMoreElements(); ) { InetAddress address = addrs.nextElement(); boolean isLegalIpVersion = Boolean.parseBoolean(System.getProperty(LEGAL_LOCAL_IP_PROPERTY)) ? address instanceof Inet6Address : address instanceof Inet4Address; if (isLegalIpVersion && !address.isLoopbackAddress()) { result = address; } } } } } catch (Exception ignore) { } if (result != null) { return result; } try { return InetAddress.getLocalHost(); } catch (UnknownHostException ignore) { } return null; } } ================================================ FILE: api/src/main/java/com/alibaba/nacos/api/utils/StringUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.utils; /** * StringUtils. copy from apache common-lang3. * * @author lin-mt */ public class StringUtils { /** * The empty String {@code ""}. * * @since 2.0 */ public static final String EMPTY = ""; /** *

    Checks if a CharSequence is empty ("") or null.

    * *
         * StringUtils.isEmpty(null)      = true
         * StringUtils.isEmpty("")        = true
         * StringUtils.isEmpty(" ")       = false
         * StringUtils.isEmpty("bob")     = false
         * StringUtils.isEmpty("  bob  ") = false
         * 
    * *

    NOTE: This method changed in Lang version 2.0. * It no longer trims the CharSequence. That functionality is available in isBlank().

    * * @param cs the CharSequence to check, may be null * @return {@code true} if the CharSequence is empty or null * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) */ public static boolean isEmpty(final CharSequence cs) { return cs == null || cs.length() == 0; } /** *

    Checks if a CharSequence is whitespace, empty ("") or null.

    * *
         * StringUtils.isBlank(null)      = true
         * StringUtils.isBlank("")        = true
         * StringUtils.isBlank(" ")       = true
         * StringUtils.isBlank("bob")     = false
         * StringUtils.isBlank("  bob  ") = false
         * 
    * * @param cs the CharSequence to check, may be null * @return {@code true} if the CharSequence is null, empty or whitespace * @since 2.0 * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) */ public static boolean isBlank(final CharSequence cs) { final 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; } // Trim //----------------------------------------------------------------------- /** *

    Removes control characters (char <= 32) from both * ends of this String, handling {@code null} by returning {@code null}.

    * *

    The String is trimmed using {@link String#trim()}. * Trim removes start and end characters <= 32.

    * *
         * StringUtils.trim(null)          = null
         * StringUtils.trim("")            = ""
         * StringUtils.trim("     ")       = ""
         * StringUtils.trim("abc")         = "abc"
         * StringUtils.trim("    abc    ") = "abc"
         * 
    * * @param str the String to be trimmed, may be null * @return the trimmed string, {@code null} if null String input */ public static String trim(final String str) { return str == null ? null : str.trim(); } // Equals //----------------------------------------------------------------------- /** *

    Compares two CharSequences, returning {@code true} if they represent * equal sequences of characters.

    * *

    {@code null}s are handled without exceptions. Two {@code null} * references are considered to be equal. The comparison is case sensitive.

    * *
         * StringUtils.equals(null, null)   = true
         * StringUtils.equals(null, "abc")  = false
         * StringUtils.equals("abc", null)  = false
         * StringUtils.equals("abc", "abc") = true
         * StringUtils.equals("abc", "ABC") = false
         * 
    * * @param cs1 the first CharSequence, may be {@code null} * @param cs2 the second CharSequence, may be {@code null} * @return {@code true} if the CharSequences are equal (case-sensitive), or both {@code null} * @see Object#equals(Object) * @since 3.0 Changed signature from equals(String, String) to equals(CharSequence, CharSequence) */ public static boolean equals(final CharSequence cs1, final CharSequence cs2) { if (cs1 == cs2) { return true; } if (cs1 == null || cs2 == null) { return false; } if (cs1 instanceof String && cs2 instanceof String) { return cs1.equals(cs2); } return StringUtils.regionMatches(cs1, false, 0, cs2, 0, Math.max(cs1.length(), cs2.length())); } /** * Green implementation of regionMatches. * * @param cs the {@code CharSequence} to be processed * @param ignoreCase whether or not to be case insensitive * @param thisStart the index to start on the {@code cs} CharSequence * @param substring the {@code CharSequence} to be looked for * @param start the index to start on the {@code substring} CharSequence * @param length character length of the region * @return whether the region matched */ public static boolean regionMatches(final CharSequence cs, final boolean ignoreCase, final int thisStart, final CharSequence substring, final int start, final int length) { if (cs instanceof String && substring instanceof String) { return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); } int index1 = thisStart; int index2 = start; int tmpLen = length; while (tmpLen-- > 0) { final char c1 = cs.charAt(index1++); final char c2 = substring.charAt(index2++); if (c1 == c2) { continue; } if (!ignoreCase) { return false; } // The same check as in String.regionMatches(): if (Character.toUpperCase(c1) != Character.toUpperCase(c2) && Character.toLowerCase(c1) != Character .toLowerCase(c2)) { return false; } } return true; } } ================================================ FILE: api/src/main/proto/nacos_grpc_service.proto ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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"; import "google/protobuf/any.proto"; import "google/protobuf/timestamp.proto"; option java_multiple_files = true; option java_package = "com.alibaba.nacos.api.grpc.auto"; message Metadata { string type = 3; string clientIp = 8; map headers = 7; } message Payload { Metadata metadata = 2; google.protobuf.Any body = 3; } service Request { // Sends a commonRequest rpc request (Payload) returns (Payload) { } } service BiRequestStream { // Sends a biStreamRequest rpc requestBiStream (stream Payload) returns (stream Payload) { } } ================================================ FILE: api/src/main/resources/META-INF/services/com.alibaba.nacos.api.remote.Payload ================================================ # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.api.remote.request.ClientDetectionRequest com.alibaba.nacos.api.remote.request.ConnectionSetupRequest com.alibaba.nacos.api.remote.request.ConnectResetRequest com.alibaba.nacos.api.remote.request.HealthCheckRequest com.alibaba.nacos.api.remote.request.PushAckRequest com.alibaba.nacos.api.remote.request.ServerCheckRequest com.alibaba.nacos.api.remote.request.ServerLoaderInfoRequest com.alibaba.nacos.api.remote.request.ServerReloadRequest com.alibaba.nacos.api.remote.request.SetupAckRequest com.alibaba.nacos.api.remote.response.SetupAckResponse com.alibaba.nacos.api.remote.response.ClientDetectionResponse com.alibaba.nacos.api.remote.response.ConnectResetResponse com.alibaba.nacos.api.remote.response.ErrorResponse com.alibaba.nacos.api.remote.response.HealthCheckResponse com.alibaba.nacos.api.remote.response.ServerCheckResponse com.alibaba.nacos.api.remote.response.ServerLoaderInfoResponse com.alibaba.nacos.api.remote.response.ServerReloadResponse com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchChangeNotifyResponse com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchSyncResponse com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest com.alibaba.nacos.api.naming.remote.request.InstanceRequest com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest com.alibaba.nacos.api.naming.remote.request.ServiceListRequest com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse com.alibaba.nacos.api.naming.remote.response.InstanceResponse com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse com.alibaba.nacos.api.naming.remote.response.ServiceListResponse com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchChangeNotifyResponse com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchSyncResponse com.alibaba.nacos.api.lock.remote.request.LockOperationRequest com.alibaba.nacos.api.lock.remote.response.LockOperationResponse com.alibaba.nacos.api.ai.remote.request.QueryMcpServerRequest com.alibaba.nacos.api.ai.remote.response.QueryMcpServerResponse com.alibaba.nacos.api.ai.remote.request.ReleaseMcpServerRequest com.alibaba.nacos.api.ai.remote.response.ReleaseMcpServerResponse com.alibaba.nacos.api.ai.remote.request.McpServerEndpointRequest com.alibaba.nacos.api.ai.remote.response.McpServerEndpointResponse com.alibaba.nacos.api.ai.remote.request.QueryAgentCardRequest com.alibaba.nacos.api.ai.remote.response.QueryAgentCardResponse com.alibaba.nacos.api.ai.remote.request.QueryPromptRequest com.alibaba.nacos.api.ai.remote.response.QueryPromptResponse com.alibaba.nacos.api.ai.remote.request.ReleaseAgentCardRequest com.alibaba.nacos.api.ai.remote.response.ReleaseAgentCardResponse com.alibaba.nacos.api.ai.remote.request.AgentEndpointRequest com.alibaba.nacos.api.ai.remote.response.AgentEndpointResponse com.alibaba.nacos.api.ai.remote.request.BatchAgentEndpointRequest ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/NacosFactoryTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api; import com.alibaba.nacos.api.config.ConfigFactory; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.NacosLockFactory; import com.alibaba.nacos.api.lock.LockService; import com.alibaba.nacos.api.naming.NamingFactory; import com.alibaba.nacos.api.naming.NamingMaintainFactory; import com.alibaba.nacos.api.naming.NamingMaintainService; import com.alibaba.nacos.api.naming.NamingService; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockStatic; class NacosFactoryTest { @Test void testCreateConfigServiceWithProperties() throws NacosException { ConfigService configService = mock(ConfigService.class); try (MockedStatic configFactoryMock = mockStatic(ConfigFactory.class)) { configFactoryMock.when(() -> ConfigFactory.createConfigService((Properties) null)) .thenReturn(configService); assertNotNull(NacosFactory.createConfigService((Properties) null)); } } @Test void testCreateConfigServiceWithServerAddr() throws NacosException { ConfigService configService = mock(ConfigService.class); try (MockedStatic configFactoryMock = mockStatic(ConfigFactory.class)) { configFactoryMock.when(() -> ConfigFactory.createConfigService("localhost:8848")) .thenReturn(configService); assertNotNull(NacosFactory.createConfigService("localhost:8848")); } } @Test void testCreateNamingServiceWithServerAddr() throws NacosException { NamingService namingService = mock(NamingService.class); try (MockedStatic namingFactoryMock = mockStatic(NamingFactory.class)) { namingFactoryMock.when(() -> NamingFactory.createNamingService("localhost:8848")) .thenReturn(namingService); assertNotNull(NacosFactory.createNamingService("localhost:8848")); } } @Test void testCreateNamingServiceWithProperties() throws NacosException { NamingService namingService = mock(NamingService.class); try (MockedStatic namingFactoryMock = mockStatic(NamingFactory.class)) { namingFactoryMock.when(() -> NamingFactory.createNamingService((Properties) null)) .thenReturn(namingService); assertNotNull(NacosFactory.createNamingService((Properties) null)); } } @Test void testCreateMaintainServiceWithServerAddr() throws NacosException { NamingMaintainService namingMaintainService = mock(NamingMaintainService.class); try (MockedStatic namingMaintainFactoryMock = mockStatic(NamingMaintainFactory.class)) { namingMaintainFactoryMock.when(() -> NamingMaintainFactory.createMaintainService("localhost:8848")) .thenReturn(namingMaintainService); assertNotNull(NacosFactory.createMaintainService("localhost:8848")); } } @Test void testCreateMaintainServiceWithProperties() throws NacosException { NamingMaintainService namingMaintainService = mock(NamingMaintainService.class); try (MockedStatic namingMaintainFactoryMock = mockStatic(NamingMaintainFactory.class)) { namingMaintainFactoryMock.when(() -> NamingMaintainFactory.createMaintainService((Properties) null)) .thenReturn(namingMaintainService); assertNotNull(NacosFactory.createMaintainService((Properties) null)); } } @Test void testCreateLockService() throws NacosException { LockService lockService = mock(LockService.class); try (MockedStatic nacosLockFactoryMock = mockStatic(NacosLockFactory.class)) { nacosLockFactoryMock.when(() -> NacosLockFactory.createLockService((Properties) null)) .thenReturn(lockService); assertNotNull(NacosFactory.createLockService((Properties) null)); } } @Test void testCreateConfigServiceWithPropertiesThrowException() { try (MockedStatic configFactoryMock = mockStatic(ConfigFactory.class)) { configFactoryMock.when(() -> ConfigFactory.createConfigService((Properties) null)) .thenThrow(new NacosException()); assertThrows(NacosException.class, () -> NacosFactory.createConfigService((Properties) null)); } } @Test void testCreateConfigServiceWithServerAddrThrowException() { try (MockedStatic configFactoryMock = mockStatic(ConfigFactory.class)) { configFactoryMock.when(() -> ConfigFactory.createConfigService("localhost:8848")) .thenThrow(new NacosException()); assertThrows(NacosException.class, () -> NacosFactory.createConfigService("localhost:8848")); } } @Test void testCreateNamingServiceWithServerAddrThrowException() { try (MockedStatic namingFactoryMock = mockStatic(NamingFactory.class)) { namingFactoryMock.when(() -> NamingFactory.createNamingService("localhost:8848")) .thenThrow(new NacosException()); assertThrows(NacosException.class, () -> NacosFactory.createNamingService("localhost:8848")); } } @Test void testCreateNamingServiceWithPropertiesThrowException() { try (MockedStatic namingFactoryMock = mockStatic(NamingFactory.class)) { namingFactoryMock.when(() -> NamingFactory.createNamingService((Properties) null)) .thenThrow(new NacosException()); assertThrows(NacosException.class, () -> NacosFactory.createNamingService((Properties) null)); } } @Test void testCreateMaintainServiceWithServerAddrThrowException() { try (MockedStatic namingMaintainFactoryMock = mockStatic(NamingMaintainFactory.class)) { namingMaintainFactoryMock.when(() -> NamingMaintainFactory.createMaintainService("localhost:8848")) .thenThrow(new NacosException()); assertThrows(NacosException.class, () -> NacosFactory.createMaintainService("localhost:8848")); } } @Test void testCreateMaintainServiceWithPropertiesThrowException() { try (MockedStatic namingMaintainFactoryMock = mockStatic(NamingMaintainFactory.class)) { namingMaintainFactoryMock.when(() -> NamingMaintainFactory.createMaintainService((Properties) null)) .thenThrow(new NacosException()); assertThrows(NacosException.class, () -> NacosFactory.createMaintainService((Properties) null)); } } @Test void testCreateLockServiceThrowException() { try (MockedStatic nacosLockFactoryMock = mockStatic(NacosLockFactory.class)) { nacosLockFactoryMock.when(() -> NacosLockFactory.createLockService((Properties) null)) .thenThrow(new NacosException()); assertThrows(NacosException.class, () -> NacosFactory.createLockService((Properties) null)); } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ability/ClientAbilitiesTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientAbilitiesTest { private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { ClientAbilities abilities = new ClientAbilities(); String json = mapper.writeValueAsString(abilities); assertTrue(json.contains("\"remoteAbility\":{")); assertTrue(json.contains("\"configAbility\":{")); assertTrue(json.contains("\"namingAbility\":{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"remoteAbility\":{\"supportRemoteConnection\":false}," + "\"configAbility\":{\"supportRemoteMetrics\":false},\"namingAbility\":{\"supportDeltaPush\":false," + "\"supportRemoteMetric\":false}}"; ClientAbilities abilities = mapper.readValue(json, ClientAbilities.class); assertNotNull(abilities.getRemoteAbility()); assertNotNull(abilities.getNamingAbility()); assertNotNull(abilities.getConfigAbility()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ability/ServerAbilitiesTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class ServerAbilitiesTest { private static ObjectMapper mapper; private ServerAbilities serverAbilities; @BeforeAll static void setUpBeforeClass() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @BeforeEach void setUp() throws Exception { serverAbilities = new ServerAbilities(); } @Test void testSerialize() throws JsonProcessingException { serverAbilities = new ServerAbilities(); String json = mapper.writeValueAsString(serverAbilities); assertTrue(json.contains("\"remoteAbility\":{")); assertTrue(json.contains("\"configAbility\":{")); assertTrue(json.contains("\"namingAbility\":{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"remoteAbility\":{\"supportRemoteConnection\":false}," + "\"configAbility\":{\"supportRemoteMetrics\":false},\"namingAbility\":{\"supportDeltaPush\":false," + "\"supportRemoteMetric\":false}}"; ServerAbilities abilities = mapper.readValue(json, ServerAbilities.class); assertNotNull(abilities.getRemoteAbility()); assertNotNull(abilities.getNamingAbility()); assertNotNull(abilities.getConfigAbility()); } @Test void testEqualsAndHashCode() { assertEquals(serverAbilities, serverAbilities); assertEquals(serverAbilities.hashCode(), serverAbilities.hashCode()); assertNotEquals(null, serverAbilities); assertNotEquals(serverAbilities, new ClientAbilities()); ServerAbilities test = new ServerAbilities(); assertEquals(serverAbilities, test); assertEquals(serverAbilities.hashCode(), test.hashCode()); test.setRemoteAbility(null); assertNotEquals(serverAbilities, test); assertNotEquals(serverAbilities.hashCode(), test.hashCode()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ability/register/impl/ClusterClientAbilitiesTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.register.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class ClusterClientAbilitiesTest { @Test void testGetStaticAbilities() { // TODO add the cluster client abilities. assertTrue(ClusterClientAbilities.getStaticAbilities().isEmpty()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ability/register/impl/SdkClientAbilitiesTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.register.impl; import com.alibaba.nacos.api.ability.constant.AbilityKey; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class SdkClientAbilitiesTest { @Test void testGetStaticAbilities() { assertFalse(SdkClientAbilities.getStaticAbilities().isEmpty()); assertTrue(SdkClientAbilities.getStaticAbilities().get(AbilityKey.SDK_CLIENT_FUZZY_WATCH)); assertTrue(SdkClientAbilities.getStaticAbilities().get(AbilityKey.SDK_CLIENT_DISTRIBUTED_LOCK)); assertTrue(SdkClientAbilities.getStaticAbilities().get(AbilityKey.SDK_MCP_REGISTRY)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ability/register/impl/ServerAbilitiesTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ability.register.impl; import com.alibaba.nacos.api.ability.constant.AbilityKey; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ServerAbilitiesTest { @Test void testGetStaticAbilities() { assertFalse(ServerAbilities.getStaticAbilities().isEmpty()); } @Test void testSupportPersistentInstanceByGrpcAbilities() { assertTrue(ServerAbilities.getStaticAbilities().get(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/A2aServiceDefaultMethodTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai; import com.alibaba.nacos.api.ai.listener.AbstractNacosAgentCardListener; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertTrue; class A2aServiceDefaultMethodTest { private AtomicBoolean invokeMark; A2aService a2aService; @BeforeEach void setUp() { invokeMark = new AtomicBoolean(false); a2aService = new A2aService() { @Override public AgentCardDetailInfo getAgentCard(String agentName, String version, String registrationType) throws NacosException { invokeMark.set(true); return null; } @Override public void releaseAgentCard(AgentCard agentCard, String registrationType, boolean setAsLatest) throws NacosException { invokeMark.set(true); } @Override public void registerAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { invokeMark.set(true); } @Override public void registerAgentEndpoint(String agentName, Collection endpoints) throws NacosException { invokeMark.set(true); } @Override public void deregisterAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { invokeMark.set(true); } @Override public AgentCardDetailInfo subscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException { invokeMark.set(true); return null; } @Override public void unsubscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException { invokeMark.set(true); } }; } @Test void getAgentCard() throws NacosException { a2aService.getAgentCard(""); assertTrue(invokeMark.get()); } @Test void getAgentCardWithVersion() throws NacosException { a2aService.getAgentCard("", "v1.0"); assertTrue(invokeMark.get()); } @Test void releaseAgentCard() throws NacosException { AgentCard agentCard = new AgentCard(); a2aService.releaseAgentCard(agentCard); assertTrue(invokeMark.get()); } @Test void releaseAgentCardWithRegistrationType() throws NacosException { AgentCard agentCard = new AgentCard(); a2aService.releaseAgentCard(agentCard, "SERVICE"); assertTrue(invokeMark.get()); } @Test void registerAgentEndpoint() throws NacosException { a2aService.registerAgentEndpoint("", "v1.0", "127.0.0.1", 8080); assertTrue(invokeMark.get()); } @Test void registerAgentEndpointWithTransport() throws NacosException { a2aService.registerAgentEndpoint("", "v1.0", "127.0.0.1", 8080, "JSONRPC"); assertTrue(invokeMark.get()); } @Test void registerAgentEndpointWithTransportAndPath() throws NacosException { a2aService.registerAgentEndpoint("", "v1.0", "127.0.0.1", 8080, "JSONRPC", "/test"); assertTrue(invokeMark.get()); } @Test void registerAgentEndpointWithFullParams() throws NacosException { a2aService.registerAgentEndpoint("", "v1.0", "127.0.0.1", 8080, "JSONRPC", "/test", true); assertTrue(invokeMark.get()); } @Test void deregisterAgentEndpoint() throws NacosException { a2aService.deregisterAgentEndpoint("", "v1.0", "127.0.0.1", 8080); assertTrue(invokeMark.get()); } @Test void subscribeAgentCard() throws NacosException { a2aService.subscribeAgentCard("", null); } @Test void unsubscribeAgentCard() throws NacosException { a2aService.unsubscribeAgentCard("", null); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/AiFactoryTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.NacosAiService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class AiFactoryTest { @BeforeEach void setUp() { } @AfterEach void tearDown() { } @Test void createAiServiceWithException() { NacosAiService.IS_THROW_EXCEPTION.set(true); assertThrows(NacosException.class, () -> AiFactory.createAiService(new Properties())); } @Test void createAiServiceSuccess() throws NacosException { NacosAiService.IS_THROW_EXCEPTION.set(false); assertNotNull(AiFactory.createAiService(new Properties())); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/constant/AiConstantsStatusMappingTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.constant; import com.alibaba.nacos.api.ai.model.mcp.registry.McpServerStatusEnum; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class AiConstantsStatusMappingTest { @Test void testStatusConstantsAlignedWithEnum() { // Verify that AiConstants.Mcp status constants match McpServerStatusEnum values assertEquals(McpServerStatusEnum.ACTIVE.getName(), AiConstants.Mcp.MCP_STATUS_ACTIVE); assertEquals(McpServerStatusEnum.DEPRECATED.getName(), AiConstants.Mcp.MCP_STATUS_DEPRECATED); assertEquals(McpServerStatusEnum.DELETED.getName(), AiConstants.Mcp.MCP_STATUS_DELETED); } @Test void testStatusConstantValues() { assertEquals("active", AiConstants.Mcp.MCP_STATUS_ACTIVE); assertEquals("deprecated", AiConstants.Mcp.MCP_STATUS_DEPRECATED); assertEquals("deleted", AiConstants.Mcp.MCP_STATUS_DELETED); } @Test void testStatusCanBeParsedFromEnum() { // Verify that we can parse the constants back to enum McpServerStatusEnum active = McpServerStatusEnum.parseStatus(AiConstants.Mcp.MCP_STATUS_ACTIVE); assertEquals(McpServerStatusEnum.ACTIVE, active); McpServerStatusEnum deprecated = McpServerStatusEnum.parseStatus(AiConstants.Mcp.MCP_STATUS_DEPRECATED); assertEquals(McpServerStatusEnum.DEPRECATED, deprecated); McpServerStatusEnum deleted = McpServerStatusEnum.parseStatus(AiConstants.Mcp.MCP_STATUS_DELETED); assertEquals(McpServerStatusEnum.DELETED, deleted); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/listener/NacosAgentCardEventTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class NacosAgentCardEventTest { @Test void testConstructor() { AgentCardDetailInfo agentCardDetailInfo = new AgentCardDetailInfo(); agentCardDetailInfo.setName("testAgent"); NacosAgentCardEvent event = new NacosAgentCardEvent(agentCardDetailInfo); assertEquals(agentCardDetailInfo, event.getAgentCard()); assertEquals(agentCardDetailInfo.getName(), event.getAgentName()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/listener/NacosAiListenerDefaultMethodTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNull; class NacosAiListenerDefaultMethodTest { NacosAiListener aiListener; @BeforeEach void setUp() { aiListener = event -> { }; } @Test void testGetExecutor() { assertNull(aiListener.getExecutor()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/listener/NacosMcpServerEventTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.listener; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import org.junit.jupiter.api.Test; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; class NacosMcpServerEventTest { @Test void testConstructor() { McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("testName"); mcpServerDetailInfo.setId(UUID.randomUUID().toString()); mcpServerDetailInfo.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); NacosMcpServerEvent event = new NacosMcpServerEvent(mcpServerDetailInfo); assertEquals(mcpServerDetailInfo, event.getMcpServerDetailInfo()); assertEquals(mcpServerDetailInfo.getId(), event.getMcpId()); assertEquals(mcpServerDetailInfo.getNamespaceId(), event.getNamespaceId()); assertEquals(mcpServerDetailInfo.getName(), event.getMcpName()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentAuthenticationTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; 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.assertTrue; class AgentAuthenticationTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentAuthentication agentAuthentication = new AgentAuthentication(); agentAuthentication.setCredentials("test-credentials"); List schemes = Arrays.asList("oauth2", "basic"); agentAuthentication.setSchemes(schemes); String json = mapper.writeValueAsString(agentAuthentication); assertNotNull(json); assertTrue(json.contains("\"credentials\":\"test-credentials\"")); assertTrue(json.contains("\"schemes\":[\"oauth2\",\"basic\"]")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"credentials\":\"test-credentials\",\"schemes\":[\"oauth2\",\"basic\"]}"; AgentAuthentication agentAuthentication = mapper.readValue(json, AgentAuthentication.class); assertNotNull(agentAuthentication); assertEquals("test-credentials", agentAuthentication.getCredentials()); assertEquals(2, agentAuthentication.getSchemes().size()); assertEquals("oauth2", agentAuthentication.getSchemes().get(0)); assertEquals("basic", agentAuthentication.getSchemes().get(1)); } @Test void testEqualsAndHashCode() { AgentAuthentication auth1 = new AgentAuthentication(); auth1.setCredentials("test-credentials"); auth1.setSchemes(Arrays.asList("oauth2", "basic")); AgentAuthentication auth2 = new AgentAuthentication(); auth2.setCredentials("test-credentials"); auth2.setSchemes(Arrays.asList("oauth2", "basic")); AgentAuthentication auth3 = new AgentAuthentication(); auth3.setCredentials("other-credentials"); assertEquals(auth1, auth2); assertEquals(auth1.hashCode(), auth2.hashCode()); assertNotEquals(auth1, auth3); assertNotEquals(auth1.hashCode(), auth3.hashCode()); assertNotEquals(auth1, null); assertNotEquals(auth1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentCapabilitiesTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.Map; 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.assertTrue; class AgentCapabilitiesTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentCapabilities agentCapabilities = new AgentCapabilities(); agentCapabilities.setStreaming(true); agentCapabilities.setPushNotifications(false); agentCapabilities.setStateTransitionHistory(true); // Create extension AgentExtension extension = new AgentExtension(); extension.setUri("test-uri"); extension.setDescription("test description"); extension.setRequired(true); Map params = new HashMap<>(); params.put("param1", "value1"); extension.setParams(params); agentCapabilities.setExtensions(Arrays.asList(extension)); String json = mapper.writeValueAsString(agentCapabilities); assertNotNull(json); assertTrue(json.contains("\"streaming\":true")); assertTrue(json.contains("\"pushNotifications\":false")); assertTrue(json.contains("\"stateTransitionHistory\":true")); assertTrue(json.contains("\"uri\":\"test-uri\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"required\":true")); assertTrue(json.contains("\"params\":{\"param1\":\"value1\"}")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"streaming\":true,\"pushNotifications\":false,\"stateTransitionHistory\":true," + "\"extensions\":[{\"uri\":\"test-uri\",\"description\":\"test description\",\"required\":true," + "\"params\":{\"param1\":\"value1\"}}]}"; AgentCapabilities agentCapabilities = mapper.readValue(json, AgentCapabilities.class); assertNotNull(agentCapabilities); assertEquals(true, agentCapabilities.getStreaming()); assertEquals(false, agentCapabilities.getPushNotifications()); assertEquals(true, agentCapabilities.getStateTransitionHistory()); assertEquals(1, agentCapabilities.getExtensions().size()); AgentExtension extension = agentCapabilities.getExtensions().get(0); assertEquals("test-uri", extension.getUri()); assertEquals("test description", extension.getDescription()); assertEquals(true, extension.getRequired()); assertEquals("value1", extension.getParams().get("param1")); } @Test void testEqualsAndHashCode() { AgentCapabilities cap1 = new AgentCapabilities(); cap1.setStreaming(true); cap1.setPushNotifications(false); cap1.setStateTransitionHistory(true); AgentCapabilities cap2 = new AgentCapabilities(); cap2.setStreaming(true); cap2.setPushNotifications(false); cap2.setStateTransitionHistory(true); AgentCapabilities cap3 = new AgentCapabilities(); cap3.setStreaming(false); assertEquals(cap1, cap2); assertEquals(cap1.hashCode(), cap2.hashCode()); assertNotEquals(cap1, cap3); assertNotEquals(cap1.hashCode(), cap3.hashCode()); assertNotEquals(cap1, null); assertNotEquals(cap1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentCardBasicInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; 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.assertTrue; class AgentCardBasicInfoTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentCardBasicInfo agentCardBasicInfo = new AgentCardBasicInfo(); agentCardBasicInfo.setProtocolVersion("1.0"); agentCardBasicInfo.setName("test agent"); agentCardBasicInfo.setDescription("test description"); agentCardBasicInfo.setVersion("1.0.0"); agentCardBasicInfo.setIconUrl("http://test.com/icon.png"); // Create capabilities AgentCapabilities capabilities = new AgentCapabilities(); capabilities.setStreaming(true); agentCardBasicInfo.setCapabilities(capabilities); // Create skills AgentSkill skill = new AgentSkill(); skill.setId("skill-1"); skill.setName("test skill"); agentCardBasicInfo.setSkills(Arrays.asList(skill)); String json = mapper.writeValueAsString(agentCardBasicInfo); assertNotNull(json); assertTrue(json.contains("\"protocolVersion\":\"1.0\"")); assertTrue(json.contains("\"name\":\"test agent\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"iconUrl\":\"http://test.com/icon.png\"")); assertTrue(json.contains("\"capabilities\":{\"streaming\":true")); assertTrue(json.contains("\"skills\":[{\"id\":\"skill-1\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"protocolVersion\":\"1.0\",\"name\":\"test agent\",\"description\":\"test description\"," + "\"version\":\"1.0.0\",\"iconUrl\":\"http://test.com/icon.png\"," + "\"capabilities\":{\"streaming\":true}," + "\"skills\":[{\"id\":\"skill-1\",\"name\":\"test skill\"}]}"; AgentCardBasicInfo agentCardBasicInfo = mapper.readValue(json, AgentCardBasicInfo.class); assertNotNull(agentCardBasicInfo); assertEquals("1.0", agentCardBasicInfo.getProtocolVersion()); assertEquals("test agent", agentCardBasicInfo.getName()); assertEquals("test description", agentCardBasicInfo.getDescription()); assertEquals("1.0.0", agentCardBasicInfo.getVersion()); assertEquals("http://test.com/icon.png", agentCardBasicInfo.getIconUrl()); assertNotNull(agentCardBasicInfo.getCapabilities()); assertEquals(true, agentCardBasicInfo.getCapabilities().getStreaming()); assertEquals(1, agentCardBasicInfo.getSkills().size()); assertEquals("skill-1", agentCardBasicInfo.getSkills().get(0).getId()); assertEquals("test skill", agentCardBasicInfo.getSkills().get(0).getName()); } @Test void testEqualsAndHashCode() { AgentCardBasicInfo card1 = new AgentCardBasicInfo(); card1.setProtocolVersion("1.0"); card1.setName("test agent"); card1.setDescription("test description"); card1.setVersion("1.0.0"); card1.setIconUrl("http://test.com/icon.png"); AgentCardBasicInfo card2 = new AgentCardBasicInfo(); card2.setProtocolVersion("1.0"); card2.setName("test agent"); card2.setDescription("test description"); card2.setVersion("1.0.0"); card2.setIconUrl("http://test.com/icon.png"); AgentCardBasicInfo card3 = new AgentCardBasicInfo(); card3.setProtocolVersion("2.0"); assertEquals(card1, card1); assertEquals(card1, card2); assertEquals(card1.hashCode(), card2.hashCode()); assertNotEquals(card1, card3); assertNotEquals(card1.hashCode(), card3.hashCode()); assertNotEquals(card1, null); assertNotEquals(card1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentCardDetailInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class AgentCardDetailInfoTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentCardDetailInfo agentCardDetailInfo = new AgentCardDetailInfo(); agentCardDetailInfo.setProtocolVersion("1.0"); agentCardDetailInfo.setName("test agent"); agentCardDetailInfo.setDescription("test description"); agentCardDetailInfo.setVersion("1.0.0"); agentCardDetailInfo.setIconUrl("http://test.com/icon.png"); agentCardDetailInfo.setRegistrationType("URL"); agentCardDetailInfo.setLatestVersion(true); String json = mapper.writeValueAsString(agentCardDetailInfo); assertNotNull(json); assertTrue(json.contains("\"protocolVersion\":\"1.0\"")); assertTrue(json.contains("\"name\":\"test agent\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"iconUrl\":\"http://test.com/icon.png\"")); assertTrue(json.contains("\"registrationType\":\"URL\"")); assertTrue(json.contains("\"latestVersion\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"protocolVersion\":\"1.0\",\"name\":\"test agent\",\"description\":\"test description\"," + "\"version\":\"1.0.0\",\"iconUrl\":\"http://test.com/icon.png\"," + "\"registrationType\":\"URL\",\"latestVersion\":true}"; AgentCardDetailInfo agentCardDetailInfo = mapper.readValue(json, AgentCardDetailInfo.class); assertNotNull(agentCardDetailInfo); assertEquals("1.0", agentCardDetailInfo.getProtocolVersion()); assertEquals("test agent", agentCardDetailInfo.getName()); assertEquals("test description", agentCardDetailInfo.getDescription()); assertEquals("1.0.0", agentCardDetailInfo.getVersion()); assertEquals("http://test.com/icon.png", agentCardDetailInfo.getIconUrl()); assertEquals("URL", agentCardDetailInfo.getRegistrationType()); assertEquals(true, agentCardDetailInfo.isLatestVersion()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentCardTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.HashMap; import java.util.ArrayList; import java.util.Map; import java.util.List; 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.assertTrue; class AgentCardTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentCard agentCard = getAgentCard(); String json = mapper.writeValueAsString(agentCard); assertNotNull(json); assertTrue(json.contains("\"protocolVersion\":\"1.0\"")); assertTrue(json.contains("\"name\":\"test agent\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"iconUrl\":\"http://test.com/icon.png\"")); assertTrue(json.contains("\"url\":\"http://test.com/agent\"")); assertTrue(json.contains("\"preferredTransport\":\"JSONRPC\"")); assertTrue(json.contains("\"documentationUrl\":\"http://test.com/docs\"")); assertTrue(json.contains("\"defaultInputModes\":[\"text\",\"voice\"]")); assertTrue(json.contains("\"defaultOutputModes\":[\"text\",\"image\"]")); assertTrue(json.contains("\"supportsAuthenticatedExtendedCard\":true")); assertTrue(json.contains("\"provider\":{\"organization\":\"test-org\"")); assertTrue(json.contains("\"securitySchemes\":{\"default\":{\"type\":\"apiKey\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"protocolVersion\":\"1.0\",\"name\":\"test agent\",\"description\":\"test description\"," + "\"version\":\"1.0.0\",\"iconUrl\":\"http://test.com/icon.png\"," + "\"url\":\"http://test.com/agent\",\"preferredTransport\":\"JSONRPC\"," + "\"documentationUrl\":\"http://test.com/docs\"," + "\"defaultInputModes\":[\"text\",\"voice\"]," + "\"defaultOutputModes\":[\"text\",\"image\"]," + "\"supportsAuthenticatedExtendedCard\":true," + "\"capabilities\":{\"streaming\":true}," + "\"provider\":{\"organization\":\"test-org\",\"url\":\"http://test.org\"}," + "\"securitySchemes\":{\"default\":{\"type\":\"apiKey\",\"name\":\"Authorization\",\"in\":\"header\"}}}"; AgentCard agentCard = mapper.readValue(json, AgentCard.class); assertNotNull(agentCard); assertEquals("1.0", agentCard.getProtocolVersion()); assertEquals("test agent", agentCard.getName()); assertEquals("test description", agentCard.getDescription()); assertEquals("1.0.0", agentCard.getVersion()); assertEquals("http://test.com/icon.png", agentCard.getIconUrl()); assertEquals("http://test.com/agent", agentCard.getUrl()); assertEquals("JSONRPC", agentCard.getPreferredTransport()); assertEquals("http://test.com/docs", agentCard.getDocumentationUrl()); assertEquals(2, agentCard.getDefaultInputModes().size()); assertEquals("text", agentCard.getDefaultInputModes().get(0)); assertEquals("voice", agentCard.getDefaultInputModes().get(1)); assertEquals(2, agentCard.getDefaultOutputModes().size()); assertEquals("text", agentCard.getDefaultOutputModes().get(0)); assertEquals("image", agentCard.getDefaultOutputModes().get(1)); assertEquals(true, agentCard.getSupportsAuthenticatedExtendedCard()); assertNotNull(agentCard.getCapabilities()); assertEquals(true, agentCard.getCapabilities().getStreaming()); assertNotNull(agentCard.getProvider()); assertEquals("test-org", agentCard.getProvider().getOrganization()); assertEquals("http://test.org", agentCard.getProvider().getUrl()); assertNotNull(agentCard.getSecuritySchemes()); assertEquals("apiKey", agentCard.getSecuritySchemes().get("default").get("type")); assertEquals("Authorization", agentCard.getSecuritySchemes().get("default").get("name")); assertEquals("header", agentCard.getSecuritySchemes().get("default").get("in")); } @Test void testEqualsAndHashCode() { AgentCard card1 = new AgentCard(); card1.setProtocolVersion("1.0"); card1.setName("test agent"); card1.setDescription("test description"); card1.setVersion("1.0.0"); card1.setIconUrl("http://test.com/icon.png"); card1.setUrl("http://test.com/agent"); card1.setPreferredTransport("JSONRPC"); AgentCard card2 = new AgentCard(); card2.setProtocolVersion("1.0"); card2.setName("test agent"); card2.setDescription("test description"); card2.setVersion("1.0.0"); card2.setIconUrl("http://test.com/icon.png"); card2.setUrl("http://test.com/agent"); card2.setPreferredTransport("JSONRPC"); AgentCard card3 = new AgentCard(); card3.setProtocolVersion("2.0"); assertEquals(card1, card1); assertEquals(card1, card2); assertEquals(card1.hashCode(), card2.hashCode()); assertNotEquals(card1, card3); assertNotEquals(card1.hashCode(), card3.hashCode()); assertNotEquals(card1, null); assertNotEquals(card1, new Object()); } @Test void testAdditionalInterfaces() { AgentInterface agentInterface = new AgentInterface(); agentInterface.setUrl("http://test.com/interface"); agentInterface.setTransport("JSONRPC"); List interfaces = new ArrayList<>(); interfaces.add(agentInterface); AgentCard agentCard = new AgentCard(); agentCard.setAdditionalInterfaces(interfaces); assertNotNull(agentCard.getAdditionalInterfaces()); assertEquals(1, agentCard.getAdditionalInterfaces().size()); assertEquals("http://test.com/interface", agentCard.getAdditionalInterfaces().get(0).getUrl()); assertEquals("JSONRPC", agentCard.getAdditionalInterfaces().get(0).getTransport()); } @Test void testSecurity() { Map> securityMap = new HashMap<>(); List scopes = new ArrayList<>(); scopes.add("read"); scopes.add("write"); securityMap.put("oauth2", scopes); List>> security = new ArrayList<>(); security.add(securityMap); AgentCard agentCard = new AgentCard(); agentCard.setSecurity(security); assertNotNull(agentCard.getSecurity()); assertEquals(1, agentCard.getSecurity().size()); assertEquals(1, agentCard.getSecurity().get(0).size()); assertTrue(agentCard.getSecurity().get(0).containsKey("oauth2")); assertEquals(2, agentCard.getSecurity().get(0).get("oauth2").size()); assertEquals("read", agentCard.getSecurity().get(0).get("oauth2").get(0)); assertEquals("write", agentCard.getSecurity().get(0).get("oauth2").get(1)); } private static AgentCard getAgentCard() { AgentCard agentCard = new AgentCard(); agentCard.setProtocolVersion("1.0"); agentCard.setName("test agent"); agentCard.setDescription("test description"); agentCard.setVersion("1.0.0"); agentCard.setIconUrl("http://test.com/icon.png"); agentCard.setUrl("http://test.com/agent"); agentCard.setPreferredTransport("JSONRPC"); agentCard.setDocumentationUrl("http://test.com/docs"); agentCard.setDefaultInputModes(Arrays.asList("text", "voice")); agentCard.setDefaultOutputModes(Arrays.asList("text", "image")); agentCard.setSupportsAuthenticatedExtendedCard(true); // Create capabilities AgentCapabilities capabilities = new AgentCapabilities(); capabilities.setStreaming(true); agentCard.setCapabilities(capabilities); // Create provider AgentProvider provider = new AgentProvider(); provider.setOrganization("test-org"); provider.setUrl("http://test.org"); agentCard.setProvider(provider); // Create security scheme SecurityScheme securityScheme = new SecurityScheme(); securityScheme.put("type", "apiKey"); securityScheme.put("name", "Authorization"); securityScheme.put("in", "header"); HashMap securitySchemes = new HashMap<>(); securitySchemes.put("default", securityScheme); agentCard.setSecuritySchemes(securitySchemes); return agentCard; } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentCardVersionInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; 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.assertTrue; class AgentCardVersionInfoTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentCardVersionInfo agentCardVersionInfo = new AgentCardVersionInfo(); agentCardVersionInfo.setProtocolVersion("1.0"); agentCardVersionInfo.setName("test agent"); agentCardVersionInfo.setDescription("test description"); agentCardVersionInfo.setVersion("1.0.0"); agentCardVersionInfo.setIconUrl("http://test.com/icon.png"); agentCardVersionInfo.setLatestPublishedVersion("2.0.0"); agentCardVersionInfo.setRegistrationType("URL"); // Create version detail AgentVersionDetail versionDetail = new AgentVersionDetail(); versionDetail.setVersion("1.0.0"); versionDetail.setLatest(true); agentCardVersionInfo.setVersionDetails(Arrays.asList(versionDetail)); String json = mapper.writeValueAsString(agentCardVersionInfo); assertNotNull(json); assertTrue(json.contains("\"protocolVersion\":\"1.0\"")); assertTrue(json.contains("\"name\":\"test agent\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"iconUrl\":\"http://test.com/icon.png\"")); assertTrue(json.contains("\"latestPublishedVersion\":\"2.0.0\"")); assertTrue(json.contains("\"registrationType\":\"URL\"")); assertTrue(json.contains("\"versionDetails\":[{\"version\":\"1.0.0\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"protocolVersion\":\"1.0\",\"name\":\"test agent\",\"description\":\"test description\"," + "\"version\":\"1.0.0\",\"iconUrl\":\"http://test.com/icon.png\"," + "\"latestPublishedVersion\":\"2.0.0\",\"registrationType\":\"URL\"," + "\"versionDetails\":[{\"version\":\"1.0.0\",\"latest\":true}]}"; AgentCardVersionInfo agentCardVersionInfo = mapper.readValue(json, AgentCardVersionInfo.class); assertNotNull(agentCardVersionInfo); assertEquals("1.0", agentCardVersionInfo.getProtocolVersion()); assertEquals("test agent", agentCardVersionInfo.getName()); assertEquals("test description", agentCardVersionInfo.getDescription()); assertEquals("1.0.0", agentCardVersionInfo.getVersion()); assertEquals("http://test.com/icon.png", agentCardVersionInfo.getIconUrl()); assertEquals("2.0.0", agentCardVersionInfo.getLatestPublishedVersion()); assertEquals("URL", agentCardVersionInfo.getRegistrationType()); assertEquals(1, agentCardVersionInfo.getVersionDetails().size()); assertEquals("1.0.0", agentCardVersionInfo.getVersionDetails().get(0).getVersion()); assertEquals(true, agentCardVersionInfo.getVersionDetails().get(0).isLatest()); } @Test void testEqualsAndHashCode() { AgentCardVersionInfo card1 = new AgentCardVersionInfo(); card1.setProtocolVersion("1.0"); card1.setName("test agent"); card1.setDescription("test description"); card1.setVersion("1.0.0"); card1.setIconUrl("http://test.com/icon.png"); card1.setLatestPublishedVersion("2.0.0"); card1.setRegistrationType("URL"); AgentCardVersionInfo card2 = new AgentCardVersionInfo(); card2.setProtocolVersion("1.0"); card2.setName("test agent"); card2.setDescription("test description"); card2.setVersion("1.0.0"); card2.setIconUrl("http://test.com/icon.png"); card2.setLatestPublishedVersion("2.0.0"); card2.setRegistrationType("URL"); AgentCardVersionInfo card3 = new AgentCardVersionInfo(); card3.setProtocolVersion("2.0"); assertEquals(card1, card2); assertEquals(card1.hashCode(), card2.hashCode()); assertNotEquals(card1, card3); assertNotEquals(card1.hashCode(), card3.hashCode()); assertNotEquals(card1, null); assertNotEquals(card1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentEndpointTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; import static org.junit.jupiter.api.Assertions.assertFalse; class AgentEndpointTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentEndpoint agentEndpoint = new AgentEndpoint(); agentEndpoint.setAddress("127.0.0.1"); agentEndpoint.setPort(8080); agentEndpoint.setTransport("JSONRPC"); agentEndpoint.setPath("/test"); agentEndpoint.setSupportTls(true); agentEndpoint.setVersion("1.0.0"); agentEndpoint.setProtocol("HTTP"); agentEndpoint.setQuery("param1=value1¶m2=value2"); String json = mapper.writeValueAsString(agentEndpoint); assertNotNull(json); assertTrue(json.contains("\"address\":\"127.0.0.1\"")); assertTrue(json.contains("\"port\":8080")); assertTrue(json.contains("\"transport\":\"JSONRPC\"")); assertTrue(json.contains("\"path\":\"/test\"")); assertTrue(json.contains("\"supportTls\":true")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"protocol\":\"HTTP\"")); assertTrue(json.contains("\"query\":\"param1=value1¶m2=value2\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"address\":\"127.0.0.1\",\"port\":8080,\"transport\":\"JSONRPC\"," + "\"path\":\"/test\",\"supportTls\":true,\"version\":\"1.0.0\",\"protocol\":\"HTTP\",\"query\":\"param1=value1¶m2=value2\"}"; AgentEndpoint agentEndpoint = mapper.readValue(json, AgentEndpoint.class); assertNotNull(agentEndpoint); assertEquals("127.0.0.1", agentEndpoint.getAddress()); assertEquals(8080, agentEndpoint.getPort()); assertEquals("JSONRPC", agentEndpoint.getTransport()); assertEquals("/test", agentEndpoint.getPath()); assertTrue(agentEndpoint.isSupportTls()); assertEquals("1.0.0", agentEndpoint.getVersion()); assertEquals("HTTP", agentEndpoint.getProtocol()); assertEquals("param1=value1¶m2=value2", agentEndpoint.getQuery()); } @Test void testEqualsAndHashCode() { AgentEndpoint endpoint1 = new AgentEndpoint(); endpoint1.setAddress("127.0.0.1"); endpoint1.setPort(8080); endpoint1.setTransport("JSONRPC"); endpoint1.setPath("/test"); endpoint1.setSupportTls(true); endpoint1.setVersion("1.0.0"); endpoint1.setProtocol("HTTP"); endpoint1.setQuery("param1=value1"); AgentEndpoint endpoint2 = new AgentEndpoint(); endpoint2.setAddress("127.0.0.1"); endpoint2.setPort(8080); endpoint2.setTransport("JSONRPC"); endpoint2.setPath("/test"); endpoint2.setSupportTls(true); endpoint2.setVersion("1.0.0"); endpoint2.setProtocol("HTTP"); endpoint2.setQuery("param1=value1"); AgentEndpoint endpoint3 = new AgentEndpoint(); endpoint3.setAddress("127.0.0.2"); endpoint3.setProtocol("HTTPS"); endpoint3.setQuery("param1=value2"); assertEquals(endpoint1, endpoint1); assertEquals(endpoint1, endpoint2); assertEquals(endpoint1.hashCode(), endpoint2.hashCode()); assertNotEquals(endpoint1, endpoint3); assertNotEquals(endpoint1.hashCode(), endpoint3.hashCode()); assertNotEquals(endpoint1, null); assertNotEquals(endpoint1, new Object()); } @Test void testSimpleEquals() { AgentEndpoint endpoint1 = new AgentEndpoint(); endpoint1.setAddress("127.0.0.1"); endpoint1.setPort(8080); AgentEndpoint endpoint2 = new AgentEndpoint(); endpoint2.setAddress("127.0.0.1"); endpoint2.setPort(8080); AgentEndpoint endpoint3 = new AgentEndpoint(); endpoint3.setAddress("127.0.0.2"); endpoint3.setPort(8080); AgentEndpoint endpoint4 = new AgentEndpoint(); endpoint4.setAddress("127.0.0.1"); endpoint4.setPort(9090); assertTrue(endpoint1.simpleEquals(endpoint2)); assertFalse(endpoint1.simpleEquals(endpoint3)); assertFalse(endpoint1.simpleEquals(endpoint4)); } @Test void testToString() { AgentEndpoint agentEndpoint = new AgentEndpoint(); agentEndpoint.setAddress("127.0.0.1"); agentEndpoint.setPort(8080); agentEndpoint.setTransport("JSONRPC"); agentEndpoint.setPath("/test"); agentEndpoint.setSupportTls(true); agentEndpoint.setVersion("1.0.0"); agentEndpoint.setProtocol("HTTP"); agentEndpoint.setQuery("param1=value1"); String toStringResult = agentEndpoint.toString(); assertNotNull(toStringResult); assertTrue(toStringResult.contains("transport='JSONRPC'")); assertTrue(toStringResult.contains("address='127.0.0.1'")); assertTrue(toStringResult.contains("port=8080")); assertTrue(toStringResult.contains("path='/test'")); assertTrue(toStringResult.contains("supportTls=true")); assertTrue(toStringResult.contains("version='1.0.0'")); assertTrue(toStringResult.contains("protocol='HTTP'")); assertTrue(toStringResult.contains("query='param1=value1'")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentExtensionTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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.assertTrue; class AgentExtensionTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentExtension agentExtension = new AgentExtension(); agentExtension.setUri("test-uri"); agentExtension.setDescription("test description"); agentExtension.setRequired(true); Map params = new HashMap<>(); params.put("param1", "value1"); params.put("param2", 123); agentExtension.setParams(params); String json = mapper.writeValueAsString(agentExtension); assertNotNull(json); assertTrue(json.contains("\"uri\":\"test-uri\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"required\":true")); assertTrue(json.contains("\"params\":{\"param1\":\"value1\",\"param2\":123}")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"uri\":\"test-uri\",\"description\":\"test description\",\"required\":true," + "\"params\":{\"param1\":\"value1\",\"param2\":123}}"; AgentExtension agentExtension = mapper.readValue(json, AgentExtension.class); assertNotNull(agentExtension); assertEquals("test-uri", agentExtension.getUri()); assertEquals("test description", agentExtension.getDescription()); assertEquals(true, agentExtension.getRequired()); assertEquals("value1", agentExtension.getParams().get("param1")); assertEquals(123, agentExtension.getParams().get("param2")); } @Test void testEqualsAndHashCode() { AgentExtension ext1 = new AgentExtension(); ext1.setUri("test-uri"); ext1.setDescription("test description"); ext1.setRequired(true); Map params1 = new HashMap<>(); params1.put("param1", "value1"); ext1.setParams(params1); AgentExtension ext2 = new AgentExtension(); ext2.setUri("test-uri"); ext2.setDescription("test description"); ext2.setRequired(true); Map params2 = new HashMap<>(); params2.put("param1", "value1"); ext2.setParams(params2); AgentExtension ext3 = new AgentExtension(); ext3.setUri("other-uri"); assertEquals(ext1, ext2); assertEquals(ext1.hashCode(), ext2.hashCode()); assertNotEquals(ext1, ext3); assertNotEquals(ext1.hashCode(), ext3.hashCode()); assertNotEquals(ext1, null); assertNotEquals(ext1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentInterfaceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class AgentInterfaceTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentInterface agentInterface = new AgentInterface(); agentInterface.setUrl("http://test.com/api"); agentInterface.setTransport("JSONRPC"); String json = mapper.writeValueAsString(agentInterface); assertNotNull(json); assertTrue(json.contains("\"url\":\"http://test.com/api\"")); assertTrue(json.contains("\"transport\":\"JSONRPC\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"url\":\"http://test.com/api\",\"transport\":\"JSONRPC\"}"; AgentInterface agentInterface = mapper.readValue(json, AgentInterface.class); assertNotNull(agentInterface); assertEquals("http://test.com/api", agentInterface.getUrl()); assertEquals("JSONRPC", agentInterface.getTransport()); } @Test void testEqualsAndHashCode() { AgentInterface interface1 = new AgentInterface(); interface1.setUrl("http://test.com/api"); interface1.setTransport("JSONRPC"); AgentInterface interface2 = new AgentInterface(); interface2.setUrl("http://test.com/api"); interface2.setTransport("JSONRPC"); AgentInterface interface3 = new AgentInterface(); interface3.setUrl("http://other.com/api"); assertEquals(interface1, interface2); assertEquals(interface1.hashCode(), interface2.hashCode()); assertNotEquals(interface1, interface3); assertNotEquals(interface1.hashCode(), interface3.hashCode()); assertNotEquals(interface1, null); assertNotEquals(interface1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentProviderTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class AgentProviderTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentProvider agentProvider = new AgentProvider(); agentProvider.setOrganization("test-org"); agentProvider.setUrl("http://test.org"); String json = mapper.writeValueAsString(agentProvider); assertNotNull(json); assertTrue(json.contains("\"organization\":\"test-org\"")); assertTrue(json.contains("\"url\":\"http://test.org\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"organization\":\"test-org\",\"url\":\"http://test.org\"}"; AgentProvider agentProvider = mapper.readValue(json, AgentProvider.class); assertNotNull(agentProvider); assertEquals("test-org", agentProvider.getOrganization()); assertEquals("http://test.org", agentProvider.getUrl()); } @Test void testEqualsAndHashCode() { AgentProvider provider1 = new AgentProvider(); provider1.setOrganization("test-org"); provider1.setUrl("http://test.org"); AgentProvider provider2 = new AgentProvider(); provider2.setOrganization("test-org"); provider2.setUrl("http://test.org"); AgentProvider provider3 = new AgentProvider(); provider3.setOrganization("other-org"); assertEquals(provider1, provider2); assertEquals(provider1.hashCode(), provider2.hashCode()); assertNotEquals(provider1, provider3); assertNotEquals(provider1.hashCode(), provider3.hashCode()); assertNotEquals(provider1, null); assertNotEquals(provider1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentSkillTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; 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.assertTrue; class AgentSkillTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentSkill agentSkill = new AgentSkill(); agentSkill.setId("skill-1"); agentSkill.setName("test skill"); agentSkill.setDescription("test description"); List tags = Arrays.asList("tag1", "tag2"); agentSkill.setTags(tags); List examples = Arrays.asList("example1", "example2"); agentSkill.setExamples(examples); List inputModes = Arrays.asList("text", "voice"); agentSkill.setInputModes(inputModes); List outputModes = Arrays.asList("text", "image"); agentSkill.setOutputModes(outputModes); String json = mapper.writeValueAsString(agentSkill); assertNotNull(json); assertTrue(json.contains("\"id\":\"skill-1\"")); assertTrue(json.contains("\"name\":\"test skill\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"tags\":[\"tag1\",\"tag2\"]")); assertTrue(json.contains("\"examples\":[\"example1\",\"example2\"]")); assertTrue(json.contains("\"inputModes\":[\"text\",\"voice\"]")); assertTrue(json.contains("\"outputModes\":[\"text\",\"image\"]")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"id\":\"skill-1\",\"name\":\"test skill\",\"description\":\"test description\"," + "\"tags\":[\"tag1\",\"tag2\"],\"examples\":[\"example1\",\"example2\"]," + "\"inputModes\":[\"text\",\"voice\"],\"outputModes\":[\"text\",\"image\"]}"; AgentSkill agentSkill = mapper.readValue(json, AgentSkill.class); assertNotNull(agentSkill); assertEquals("skill-1", agentSkill.getId()); assertEquals("test skill", agentSkill.getName()); assertEquals("test description", agentSkill.getDescription()); assertEquals(2, agentSkill.getTags().size()); assertEquals("tag1", agentSkill.getTags().get(0)); assertEquals("tag2", agentSkill.getTags().get(1)); assertEquals(2, agentSkill.getExamples().size()); assertEquals("example1", agentSkill.getExamples().get(0)); assertEquals("example2", agentSkill.getExamples().get(1)); assertEquals(2, agentSkill.getInputModes().size()); assertEquals("text", agentSkill.getInputModes().get(0)); assertEquals("voice", agentSkill.getInputModes().get(1)); assertEquals(2, agentSkill.getOutputModes().size()); assertEquals("text", agentSkill.getOutputModes().get(0)); assertEquals("image", agentSkill.getOutputModes().get(1)); } @Test void testEqualsAndHashCode() { AgentSkill skill1 = new AgentSkill(); skill1.setId("skill-1"); skill1.setName("test skill"); skill1.setDescription("test description"); skill1.setTags(Arrays.asList("tag1", "tag2")); skill1.setExamples(Arrays.asList("example1", "example2")); skill1.setInputModes(Arrays.asList("text", "voice")); skill1.setOutputModes(Arrays.asList("text", "image")); AgentSkill skill2 = new AgentSkill(); skill2.setId("skill-1"); skill2.setName("test skill"); skill2.setDescription("test description"); skill2.setTags(Arrays.asList("tag1", "tag2")); skill2.setExamples(Arrays.asList("example1", "example2")); skill2.setInputModes(Arrays.asList("text", "voice")); skill2.setOutputModes(Arrays.asList("text", "image")); AgentSkill skill3 = new AgentSkill(); skill3.setId("skill-2"); assertEquals(skill1, skill2); assertEquals(skill1.hashCode(), skill2.hashCode()); assertNotEquals(skill1, skill3); assertNotEquals(skill1.hashCode(), skill3.hashCode()); assertNotEquals(skill1, null); assertNotEquals(skill1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/AgentVersionDetailTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class AgentVersionDetailTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { AgentVersionDetail agentVersionDetail = new AgentVersionDetail(); agentVersionDetail.setVersion("1.0.0"); agentVersionDetail.setCreatedAt("2023-01-01T00:00:00Z"); agentVersionDetail.setUpdatedAt("2023-01-02T00:00:00Z"); agentVersionDetail.setLatest(true); String json = mapper.writeValueAsString(agentVersionDetail); assertNotNull(json); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"createdAt\":\"2023-01-01T00:00:00Z\"")); assertTrue(json.contains("\"updatedAt\":\"2023-01-02T00:00:00Z\"")); assertTrue(json.contains("\"latest\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"version\":\"1.0.0\",\"createdAt\":\"2023-01-01T00:00:00Z\",\"updatedAt\":\"2023-01-02T00:00:00Z\",\"latest\":true}"; AgentVersionDetail agentVersionDetail = mapper.readValue(json, AgentVersionDetail.class); assertNotNull(agentVersionDetail); assertEquals("1.0.0", agentVersionDetail.getVersion()); assertEquals("2023-01-01T00:00:00Z", agentVersionDetail.getCreatedAt()); assertEquals("2023-01-02T00:00:00Z", agentVersionDetail.getUpdatedAt()); assertEquals(true, agentVersionDetail.isLatest()); } @Test void testEqualsAndHashCode() { AgentVersionDetail version1 = new AgentVersionDetail(); version1.setVersion("1.0.0"); version1.setCreatedAt("2023-01-01T00:00:00Z"); version1.setUpdatedAt("2023-01-02T00:00:00Z"); version1.setLatest(true); AgentVersionDetail version2 = new AgentVersionDetail(); version2.setVersion("1.0.0"); version2.setCreatedAt("2023-01-01T00:00:00Z"); version2.setUpdatedAt("2023-01-02T00:00:00Z"); version2.setLatest(true); AgentVersionDetail version3 = new AgentVersionDetail(); version3.setVersion("2.0.0"); assertEquals(version1, version2); assertEquals(version1.hashCode(), version2.hashCode()); assertNotEquals(version1, version3); assertNotEquals(version1.hashCode(), version3.hashCode()); assertNotEquals(version1, null); assertNotEquals(version1, new Object()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/a2a/SecuritySchemeTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.a2a; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class SecuritySchemeTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { SecurityScheme securityScheme = new SecurityScheme(); securityScheme.put("type", "apiKey"); securityScheme.put("name", "Authorization"); securityScheme.put("in", "header"); String json = mapper.writeValueAsString(securityScheme); assertNotNull(json); assertTrue(json.contains("\"type\":\"apiKey\"")); assertTrue(json.contains("\"name\":\"Authorization\"")); assertTrue(json.contains("\"in\":\"header\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"type\":\"apiKey\",\"name\":\"Authorization\",\"in\":\"header\"}"; SecurityScheme securityScheme = mapper.readValue(json, SecurityScheme.class); assertNotNull(securityScheme); assertEquals("apiKey", securityScheme.get("type")); assertEquals("Authorization", securityScheme.get("name")); assertEquals("header", securityScheme.get("in")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/EncryptObjectTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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 EncryptObjectTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { EncryptObject encryptObject = new EncryptObject(); encryptObject.setData("encryptedData"); Map encryptInfo = new HashMap<>(); encryptInfo.put("alg", "AES"); encryptInfo.put("iv", "initialVector"); encryptObject.setEncryptInfo(encryptInfo); String json = mapper.writeValueAsString(encryptObject); assertTrue(json.contains("\"data\":\"encryptedData\"")); assertTrue(json.contains("\"encryptInfo\":{")); assertTrue(json.contains("\"alg\":\"AES\"")); assertTrue(json.contains("\"iv\":\"initialVector\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"data\":\"encryptedData\",\"encryptInfo\":{\"alg\":\"AES\",\"iv\":\"initialVector\"}}"; EncryptObject result = mapper.readValue(json, EncryptObject.class); assertNotNull(result); assertEquals("encryptedData", result.getData()); assertNotNull(result.getEncryptInfo()); assertEquals("AES", result.getEncryptInfo().get("alg")); assertEquals("initialVector", result.getEncryptInfo().get("iv")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/FrontEndpointConfigTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.KeyValueInput; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; 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 FrontEndpointConfigTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { FrontEndpointConfig frontEndpointConfig = new FrontEndpointConfig(); frontEndpointConfig.setType("sse"); frontEndpointConfig.setProtocol("http"); frontEndpointConfig.setEndpointType("DIRECT"); frontEndpointConfig.setEndpointData("127.0.0.1:8080"); frontEndpointConfig.setPath("/test"); List headers = new ArrayList<>(); KeyValueInput header = new KeyValueInput(); header.setName("Authorization"); header.setValue("Bearer token"); headers.add(header); frontEndpointConfig.setHeaders(headers); String json = mapper.writeValueAsString(frontEndpointConfig); assertTrue(json.contains("\"type\":\"sse\"")); assertTrue(json.contains("\"protocol\":\"http\"")); assertTrue(json.contains("\"endpointType\":\"DIRECT\"")); assertTrue(json.contains("\"endpointData\":\"127.0.0.1:8080\"")); assertTrue(json.contains("\"path\":\"/test\"")); assertTrue(json.contains("\"headers\":[")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"type\":\"sse\",\"protocol\":\"http\",\"endpointType\":\"DIRECT\"," + "\"endpointData\":\"127.0.0.1:8080\",\"path\":\"/test\"," + "\"headers\":[{\"name\":\"Authorization\",\"value\":\"Bearer token\"}]}"; FrontEndpointConfig result = mapper.readValue(json, FrontEndpointConfig.class); assertNotNull(result); assertEquals("sse", result.getType()); assertEquals("http", result.getProtocol()); assertEquals("DIRECT", result.getEndpointType()); assertEquals("127.0.0.1:8080", result.getEndpointData()); assertEquals("/test", result.getPath()); assertNotNull(result.getHeaders()); assertEquals(1, result.getHeaders().size()); assertEquals("Authorization", result.getHeaders().get(0).getName()); assertEquals("Bearer token", result.getHeaders().get(0).getValue()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpCapabilityTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class McpCapabilityTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { String jsonTool = mapper.writeValueAsString(McpCapability.TOOL); String jsonPrompt = mapper.writeValueAsString(McpCapability.PROMPT); String jsonResource = mapper.writeValueAsString(McpCapability.RESOURCE); assertEquals("\"TOOL\"", jsonTool); assertEquals("\"PROMPT\"", jsonPrompt); assertEquals("\"RESOURCE\"", jsonResource); } @Test void testDeserialize() throws JsonProcessingException { assertEquals(McpCapability.TOOL, mapper.readValue("\"TOOL\"", McpCapability.class)); assertEquals(McpCapability.PROMPT, mapper.readValue("\"PROMPT\"", McpCapability.class)); assertEquals(McpCapability.RESOURCE, mapper.readValue("\"RESOURCE\"", McpCapability.class)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpEndpointInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.KeyValueInput; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; 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 McpEndpointInfoTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpEndpointInfo endpointInfo = new McpEndpointInfo(); endpointInfo.setProtocol("https"); endpointInfo.setAddress("127.0.0.1"); endpointInfo.setPort(8080); endpointInfo.setPath("/api/mcp"); List headers = new ArrayList<>(); KeyValueInput header = new KeyValueInput(); header.setName("Content-Type"); header.setValue("application/json"); headers.add(header); endpointInfo.setHeaders(headers); String json = mapper.writeValueAsString(endpointInfo); assertTrue(json.contains("\"protocol\":\"https\"")); assertTrue(json.contains("\"address\":\"127.0.0.1\"")); assertTrue(json.contains("\"port\":8080")); assertTrue(json.contains("\"path\":\"/api/mcp\"")); assertTrue(json.contains("\"headers\":[")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"protocol\":\"https\",\"address\":\"127.0.0.1\",\"port\":8080,\"path\":\"/api/mcp\"," + "\"headers\":[{\"name\":\"Content-Type\",\"value\":\"application/json\"}]}"; McpEndpointInfo result = mapper.readValue(json, McpEndpointInfo.class); assertNotNull(result); assertEquals("https", result.getProtocol()); assertEquals("127.0.0.1", result.getAddress()); assertEquals(8080, result.getPort()); assertEquals("/api/mcp", result.getPath()); assertNotNull(result.getHeaders()); assertEquals(1, result.getHeaders().size()); assertEquals("Content-Type", result.getHeaders().get(0).getName()); assertEquals("application/json", result.getHeaders().get(0).getValue()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpEndpointSpecTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class McpEndpointSpecTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpEndpointSpec mcpEndpointSpec = new McpEndpointSpec(); mcpEndpointSpec.setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); mcpEndpointSpec.getData().put("address", "127.0.0.1"); mcpEndpointSpec.getData().put("port", "8080"); String json = mapper.writeValueAsString(mcpEndpointSpec); assertTrue(json.contains("\"type\":\"DIRECT\"")); assertTrue(json.contains("\"data\":{")); assertTrue(json.contains("\"address\":\"127.0.0.1\"")); assertTrue(json.contains("\"port\":\"8080\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"type\":\"DIRECT\",\"data\":{\"address\":\"127.0.0.1\",\"port\":\"8080\"}}"; McpEndpointSpec mcpEndpointSpec = mapper.readValue(json, McpEndpointSpec.class); assertEquals(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT, mcpEndpointSpec.getType()); assertEquals("127.0.0.1", mcpEndpointSpec.getData().get("address")); assertEquals("8080", mcpEndpointSpec.getData().get("port")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerBasicInfoEnhancedFieldsTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.Icon; import org.junit.jupiter.api.Test; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class McpServerBasicInfoEnhancedFieldsTest { @Test void testNamespaceIdField() { McpServerBasicInfo basicInfo = new McpServerBasicInfo(); basicInfo.setNamespaceId("public"); assertEquals("public", basicInfo.getNamespaceId()); } @Test void testIconsField() { McpServerBasicInfo basicInfo = new McpServerBasicInfo(); Icon icon1 = new Icon(); icon1.setSrc("https://example.com/icon-light.png"); icon1.setMimeType(Icon.MimeType.IMAGE_PNG); icon1.setTheme(Icon.Theme.LIGHT); Icon icon2 = new Icon(); icon2.setSrc("https://example.com/icon-dark.png"); icon2.setMimeType(Icon.MimeType.IMAGE_PNG); icon2.setTheme(Icon.Theme.DARK); basicInfo.setIcons(Arrays.asList(icon1, icon2)); assertNotNull(basicInfo.getIcons()); assertEquals(2, basicInfo.getIcons().size()); assertEquals("light", basicInfo.getIcons().get(0).getTheme().getValue()); assertEquals("dark", basicInfo.getIcons().get(1).getTheme().getValue()); } @Test void testWebsiteUrlField() { McpServerBasicInfo basicInfo = new McpServerBasicInfo(); basicInfo.setWebsiteUrl("https://example.com"); assertEquals("https://example.com", basicInfo.getWebsiteUrl()); } @Test void testAllEnhancedFieldsTogether() { McpServerBasicInfo basicInfo = new McpServerBasicInfo(); basicInfo.setId("server-1"); basicInfo.setName("Test Server"); basicInfo.setNamespaceId("custom"); basicInfo.setWebsiteUrl("https://test.example.com"); basicInfo.setDescription("A test server"); Icon icon = new Icon(); icon.setSrc("https://example.com/logo.svg"); icon.setMimeType(Icon.MimeType.IMAGE_SVG_XML); basicInfo.setIcons(Arrays.asList(icon)); // Verify all fields are accessible assertEquals("server-1", basicInfo.getId()); assertEquals("Test Server", basicInfo.getName()); assertEquals("custom", basicInfo.getNamespaceId()); assertEquals("https://test.example.com", basicInfo.getWebsiteUrl()); assertEquals("A test server", basicInfo.getDescription()); assertEquals(1, basicInfo.getIcons().size()); } @Test void testNamespaceIdInheritedByDetailInfo() { McpServerDetailInfo detailInfo = new McpServerDetailInfo(); detailInfo.setNamespaceId("public"); assertEquals("public", detailInfo.getNamespaceId()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerBasicInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.registry.Package; import com.alibaba.nacos.api.ai.model.mcp.registry.Repository; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; import java.util.UUID; 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 McpServerBasicInfoTest extends BasicRequestTest { @Test void testSerializeForStdio() throws JsonProcessingException { mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); McpServerBasicInfo serverBasicInfo = new McpServerBasicInfo(); String id = UUID.randomUUID().toString(); serverBasicInfo.setId(id); serverBasicInfo.setName("stdioServer"); serverBasicInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); serverBasicInfo.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); serverBasicInfo.setDescription("test stdio server"); serverBasicInfo.setRepository(new Repository()); serverBasicInfo.setVersionDetail(new ServerVersionDetail()); serverBasicInfo.getVersionDetail().setVersion("1.0.0"); serverBasicInfo.getVersionDetail().setIs_latest(true); serverBasicInfo.setLocalServerConfig(new HashMap<>()); serverBasicInfo.setEnabled(true); serverBasicInfo.setCapabilities(Collections.singletonList(McpCapability.TOOL)); serverBasicInfo.setStatus(AiConstants.Mcp.MCP_STATUS_ACTIVE); // 添加 Package 测试 Package pkg = new Package(); pkg.setIdentifier("test-package"); pkg.setVersion("1.0.0"); serverBasicInfo.setPackages(Collections.singletonList(pkg)); String json = mapper.writeValueAsString(serverBasicInfo); assertTrue(json.contains(String.format("\"id\":\"%s\"", id))); assertTrue(json.contains("\"name\":\"stdioServer\"")); assertTrue(json.contains("\"protocol\":\"stdio\"")); assertTrue(json.contains("\"frontProtocol\":\"stdio\"")); assertTrue(json.contains("\"description\":\"test stdio server\"")); assertTrue(json.contains("\"repository\":{}")); assertTrue(json.contains("\"versionDetail\":{")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"is_latest\":true")); assertTrue(json.contains("\"localServerConfig\":{}")); assertTrue(json.contains("\"enabled\":true")); assertTrue(json.contains("\"capabilities\":[\"TOOL\"]")); assertTrue(json.contains("\"status\":\"active\"")); assertTrue(json.contains("\"packages\":[")); } @Test void testDeserializeForStdio() throws JsonProcessingException { String json = "{\"id\":\"3a2c535c-d0a8-44a4-8913-0cef98904ebd\",\"name\":\"stdioServer\"," + "\"protocol\":\"stdio\",\"frontProtocol\":\"stdio\",\"description\":\"test stdio server\"," + "\"repository\":{},\"versionDetail\":{\"version\":\"1.0.0\",\"is_latest\":true}," + "\"localServerConfig\":{},\"enabled\":true,\"capabilities\":[\"TOOL\"],\"status\":\"active\"," + "\"packages\":[{\"identifier\":\"test-package\",\"version\":\"1.0.0\"}]}"; McpServerBasicInfo result = mapper.readValue(json, McpServerBasicInfo.class); assertNotNull(result); assertEquals("3a2c535c-d0a8-44a4-8913-0cef98904ebd", result.getId()); assertEquals("stdioServer", result.getName()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, result.getProtocol()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, result.getFrontProtocol()); assertEquals("test stdio server", result.getDescription()); assertNotNull(result.getRepository()); assertNotNull(result.getVersionDetail()); assertEquals("1.0.0", result.getVersionDetail().getVersion()); assertTrue(result.getVersionDetail().getIs_latest()); assertNotNull(result.getLocalServerConfig()); assertTrue(result.isEnabled()); assertNotNull(result.getCapabilities()); assertEquals(1, result.getCapabilities().size()); assertEquals(McpCapability.TOOL, result.getCapabilities().get(0)); assertEquals(AiConstants.Mcp.MCP_STATUS_ACTIVE, result.getStatus()); assertNotNull(result.getPackages()); assertEquals(1, result.getPackages().size()); assertEquals("test-package", result.getPackages().get(0).getIdentifier()); assertEquals("1.0.0", result.getPackages().get(0).getVersion()); } @Test void testSerializeForSse() throws JsonProcessingException { mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); McpServerBasicInfo serverBasicInfo = new McpServerBasicInfo(); String id = UUID.randomUUID().toString(); serverBasicInfo.setId(id); serverBasicInfo.setName("sseServer"); serverBasicInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); serverBasicInfo.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); serverBasicInfo.setDescription("test sse server"); serverBasicInfo.setRepository(new Repository()); serverBasicInfo.setVersionDetail(new ServerVersionDetail()); serverBasicInfo.getVersionDetail().setVersion("1.0.0"); serverBasicInfo.getVersionDetail().setIs_latest(false); serverBasicInfo.setRemoteServerConfig(new McpServerRemoteServiceConfig()); serverBasicInfo.getRemoteServerConfig().setExportPath("/test"); serverBasicInfo.setEnabled(false); serverBasicInfo.setCapabilities(Collections.singletonList(McpCapability.RESOURCE)); serverBasicInfo.setStatus(AiConstants.Mcp.MCP_STATUS_DEPRECATED); // 添加 Package 测试 Package pkg = new Package(); pkg.setIdentifier("test-package"); pkg.setVersion("1.0.0"); serverBasicInfo.setPackages(Collections.singletonList(pkg)); String json = mapper.writeValueAsString(serverBasicInfo); assertTrue(json.contains(String.format("\"id\":\"%s\"", id))); assertTrue(json.contains("\"name\":\"sseServer\"")); assertTrue(json.contains("\"protocol\":\"mcp-sse\"")); assertTrue(json.contains("\"frontProtocol\":\"mcp-sse\"")); assertTrue(json.contains("\"description\":\"test sse server\"")); assertTrue(json.contains("\"repository\":{}")); assertTrue(json.contains("\"versionDetail\":{")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"is_latest\":false")); assertTrue(json.contains("\"remoteServerConfig\":{")); assertTrue(json.contains("\"exportPath\":\"/test\"")); assertTrue(json.contains("\"enabled\":false")); assertTrue(json.contains("\"capabilities\":[\"RESOURCE\"]")); assertTrue(json.contains("\"status\":\"deprecated\"")); assertTrue(json.contains("\"packages\":[")); } @Test void testDeserializeForSse() throws JsonProcessingException { String json = "{\"id\":\"c769b89b-edb5-4912-8e39-71bf5dc31eab\",\"name\":\"sseServer\"," + "\"protocol\":\"mcp-sse\",\"frontProtocol\":\"mcp-sse\",\"description\":\"test sse server\"," + "\"repository\":{},\"versionDetail\":{\"version\":\"1.0.0\",\"is_latest\":false}," + "\"remoteServerConfig\":{\"exportPath\":\"/test\"},\"enabled\":false," + "\"capabilities\":[\"RESOURCE\"],\"status\":\"deprecated\"," + "\"packages\":[{\"identifier\":\"test-package\",\"version\":\"1.0.0\"}]}"; McpServerBasicInfo result = mapper.readValue(json, McpServerBasicInfo.class); assertNotNull(result); assertEquals("c769b89b-edb5-4912-8e39-71bf5dc31eab", result.getId()); assertEquals("sseServer", result.getName()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_SSE, result.getProtocol()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_SSE, result.getFrontProtocol()); assertEquals("test sse server", result.getDescription()); assertNotNull(result.getRepository()); assertNotNull(result.getVersionDetail()); assertEquals("1.0.0", result.getVersionDetail().getVersion()); assertFalse(result.getVersionDetail().getIs_latest()); assertNotNull(result.getRemoteServerConfig()); assertEquals("/test", result.getRemoteServerConfig().getExportPath()); assertFalse(result.isEnabled()); assertNotNull(result.getCapabilities()); assertEquals(1, result.getCapabilities().size()); assertEquals(McpCapability.RESOURCE, result.getCapabilities().get(0)); assertEquals(AiConstants.Mcp.MCP_STATUS_DEPRECATED, result.getStatus()); assertNotNull(result.getPackages()); assertEquals(1, result.getPackages().size()); assertEquals("test-package", result.getPackages().get(0).getIdentifier()); assertEquals("1.0.0", result.getPackages().get(0).getVersion()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerDetailInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.registry.Repository; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; import java.util.UUID; 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 McpServerDetailInfoTest extends BasicRequestTest { @Test void testSerializeForStdio() throws JsonProcessingException { McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); String id = UUID.randomUUID().toString(); mcpServerDetailInfo.setName("stdioServer"); mcpServerDetailInfo.setId(id); mcpServerDetailInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); mcpServerDetailInfo.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); mcpServerDetailInfo.setDescription("test stdio server"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(false); mcpServerDetailInfo.getVersionDetail().setRelease_date("2025-07-15 23:59:59"); mcpServerDetailInfo.setLocalServerConfig(new HashMap<>()); mcpServerDetailInfo.setCapabilities(Collections.singletonList(McpCapability.TOOL)); mcpServerDetailInfo.setToolSpec(new McpToolSpecification()); mcpServerDetailInfo.setAllVersions(Collections.singletonList(mcpServerDetailInfo.getVersionDetail())); mcpServerDetailInfo.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); mcpServerDetailInfo.setVersion("1.0.0"); String json = mapper.writeValueAsString(mcpServerDetailInfo); assertTrue(json.contains("\"name\":\"stdioServer\"")); assertTrue(json.contains(String.format("\"id\":\"%s\"", id))); assertTrue(json.contains("\"protocol\":\"stdio\"")); assertTrue(json.contains("\"frontProtocol\":\"stdio\"")); assertTrue(json.contains("\"description\":\"test stdio server\"")); assertTrue(json.contains("\"versionDetail\":{")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"is_latest\":false")); assertTrue(json.contains("\"release_date\":\"2025-07-15 23:59:59\"")); assertTrue(json.contains("\"localServerConfig\":{}")); assertTrue(json.contains("\"capabilities\":[\"TOOL\"]")); assertTrue(json.contains("\"toolSpec\":{")); assertTrue(json.contains("\"allVersions\":[{")); } @Test void testDeserializeForStdio() throws JsonProcessingException { String json = "{\"id\":\"3a2c535c-d0a8-44a4-8913-0cef98904ebd\",\"name\":\"stdioServer\",\"protocol\":\"stdio\"," + "\"frontProtocol\":\"stdio\",\"description\":\"test stdio server\",\"versionDetail\":{\"version\":\"1.0.0\"," + "\"release_date\":\"2025-07-15 23:59:59\",\"is_latest\":false},\"localServerConfig\":{},\"enabled\":true," + "\"capabilities\":[\"TOOL\"],\"toolSpec\":{\"tools\":[],\"toolsMeta\":{}},\"allVersions\":[{\"version\":\"1.0.0\"," + "\"release_date\":\"2025-07-15 23:59:59\",\"is_latest\":false}],\"namespaceId\":\"public\", \"version\":\"1.0.0\"}"; McpServerDetailInfo result = mapper.readValue(json, McpServerDetailInfo.class); assertNotNull(result); assertEquals("3a2c535c-d0a8-44a4-8913-0cef98904ebd", result.getId()); assertEquals("stdioServer", result.getName()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, result.getProtocol()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, result.getFrontProtocol()); assertEquals("test stdio server", result.getDescription()); assertNotNull(result.getVersionDetail()); assertEquals("1.0.0", result.getVersionDetail().getVersion()); assertEquals("2025-07-15 23:59:59", result.getVersionDetail().getRelease_date()); assertFalse(result.getVersionDetail().getIs_latest()); assertNotNull(result.getLocalServerConfig()); assertTrue(result.isEnabled()); assertNotNull(result.getCapabilities()); assertEquals(1, result.getCapabilities().size()); assertEquals(McpCapability.TOOL, result.getCapabilities().get(0)); assertNotNull(result.getToolSpec()); assertNotNull(result.getAllVersions()); assertEquals(1, result.getAllVersions().size()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("1.0.0", result.getVersion()); } @Test void testSerializeForSse() throws JsonProcessingException { // Repository是空对象 mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); String id = UUID.randomUUID().toString(); mcpServerDetailInfo.setName("stdioServer"); mcpServerDetailInfo.setId(id); mcpServerDetailInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); mcpServerDetailInfo.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_SSE); mcpServerDetailInfo.setDescription("test sse server"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(false); mcpServerDetailInfo.getVersionDetail().setRelease_date("2025-07-15 23:59:59"); mcpServerDetailInfo.setRemoteServerConfig(new McpServerRemoteServiceConfig()); mcpServerDetailInfo.getRemoteServerConfig().setExportPath("/test"); mcpServerDetailInfo.getRemoteServerConfig().setServiceRef(new McpServiceRef()); mcpServerDetailInfo.getRemoteServerConfig().getServiceRef() .setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); mcpServerDetailInfo.getRemoteServerConfig().getServiceRef().setGroupName("testG"); mcpServerDetailInfo.getRemoteServerConfig().getServiceRef().setServiceName("testS"); mcpServerDetailInfo.getRemoteServerConfig() .setFrontEndpointConfigList(Collections.singletonList(new FrontEndpointConfig())); mcpServerDetailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setType(AiConstants.Mcp.MCP_PROTOCOL_SSE); mcpServerDetailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setProtocol(AiConstants.Mcp.MCP_PROTOCOL_HTTP); mcpServerDetailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0) .setEndpointType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); mcpServerDetailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0).setEndpointData("1.1.1.1:8080"); mcpServerDetailInfo.getRemoteServerConfig().getFrontEndpointConfigList().get(0).setPath("/testFront"); mcpServerDetailInfo.setRepository(new Repository()); mcpServerDetailInfo.setCapabilities(Collections.singletonList(McpCapability.TOOL)); mcpServerDetailInfo.setToolSpec(new McpToolSpecification()); mcpServerDetailInfo.setAllVersions(Collections.singletonList(mcpServerDetailInfo.getVersionDetail())); mcpServerDetailInfo.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); mcpServerDetailInfo.setBackendEndpoints(Collections.singletonList(new McpEndpointInfo())); mcpServerDetailInfo.getBackendEndpoints().get(0).setPath("/testBack"); mcpServerDetailInfo.getBackendEndpoints().get(0).setAddress("1.1.1.1"); mcpServerDetailInfo.getBackendEndpoints().get(0).setPort(3306); mcpServerDetailInfo.setFrontendEndpoints(Collections.emptyList()); String json = mapper.writeValueAsString(mcpServerDetailInfo); assertTrue(json.contains("\"name\":\"stdioServer\"")); assertTrue(json.contains(String.format("\"id\":\"%s\"", id))); assertTrue(json.contains("\"protocol\":\"mcp-sse\"")); assertTrue(json.contains("\"frontProtocol\":\"mcp-sse\"")); assertTrue(json.contains("\"description\":\"test sse server\"")); assertTrue(json.contains("\"versionDetail\":{")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"is_latest\":false")); assertTrue(json.contains("\"release_date\":\"2025-07-15 23:59:59\"")); assertTrue(json.contains("\"remoteServerConfig\":{")); assertTrue(json.contains("\"exportPath\":\"/test\"")); assertTrue(json.contains("\"serviceRef\":{")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"groupName\":\"testG\"")); assertTrue(json.contains("\"serviceName\":\"testS\"")); assertTrue(json.contains("\"capabilities\":[\"TOOL\"]")); assertTrue(json.contains("\"toolSpec\":{")); assertTrue(json.contains("\"allVersions\":[{")); assertTrue(json.contains("\"repository\":{}")); assertTrue(json.contains("\"frontendEndpoints\":[]")); } @Test void testDeserializeForSse() throws JsonProcessingException { String json = "{\"id\":\"c769b89b-edb5-4912-8e39-71bf5dc31eab\",\"name\":\"stdioServer\",\"protocol\":\"mcp-sse\"," + "\"frontProtocol\":\"mcp-sse\",\"description\":\"test sse server\",\"repository\":{},\"versionDetail\":" + "{\"version\":\"1.0.0\",\"release_date\":\"2025-07-15 23:59:59\",\"is_latest\":false}," + "\"remoteServerConfig\":{\"serviceRef\":{\"namespaceId\":\"public\",\"groupName\":\"testG\"," + "\"serviceName\":\"testS\"},\"exportPath\":\"/test\",\"frontEndpointConfigList\":[{\"type\":" + "\"mcp-sse\",\"protocol\":\"http\",\"endpointType\":\"DIRECT\",\"endpointData\":\"1.1.1.1:8080\"," + "\"path\":\"/testFront\"}]},\"enabled\":true,\"capabilities\":[\"TOOL\"],\"backendEndpoints\":" + "[{\"address\":\"1.1.1.1\",\"port\":3306,\"path\":\"/testBack\"}],\"frontendEndpoints\":[],\"toolSpec\":{\"tools\":[]," + "\"toolsMeta\":{}},\"allVersions\":[{\"version\":\"1.0.0\",\"release_date\":\"2025-07-15 23:59:59\"," + "\"is_latest\":false}],\"namespaceId\":\"public\"}"; McpServerDetailInfo result = mapper.readValue(json, McpServerDetailInfo.class); assertNotNull(result); assertEquals("c769b89b-edb5-4912-8e39-71bf5dc31eab", result.getId()); assertEquals("stdioServer", result.getName()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_SSE, result.getProtocol()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_SSE, result.getFrontProtocol()); assertEquals("test sse server", result.getDescription()); assertNotNull(result.getVersionDetail()); assertEquals("1.0.0", result.getVersionDetail().getVersion()); assertEquals("2025-07-15 23:59:59", result.getVersionDetail().getRelease_date()); assertFalse(result.getVersionDetail().getIs_latest()); assertNotNull(result.getRemoteServerConfig()); assertNotNull(result.getRemoteServerConfig().getServiceRef()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getRemoteServerConfig().getServiceRef().getNamespaceId()); assertEquals("testG", result.getRemoteServerConfig().getServiceRef().getGroupName()); assertEquals("testS", result.getRemoteServerConfig().getServiceRef().getServiceName()); assertNotNull(result.getRemoteServerConfig().getExportPath()); assertEquals("/test", result.getRemoteServerConfig().getExportPath()); assertTrue(result.isEnabled()); assertNotNull(result.getCapabilities()); assertEquals(1, result.getCapabilities().size()); assertEquals(McpCapability.TOOL, result.getCapabilities().get(0)); assertNotNull(result.getToolSpec()); assertNotNull(result.getAllVersions()); assertEquals(1, result.getAllVersions().size()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertNotNull(result.getRepository()); assertNotNull(result.getBackendEndpoints()); assertEquals(1, result.getBackendEndpoints().size()); assertEquals("1.1.1.1", result.getBackendEndpoints().get(0).getAddress()); assertEquals(3306, result.getBackendEndpoints().get(0).getPort()); assertEquals("/testBack", result.getBackendEndpoints().get(0).getPath()); assertEquals(1, result.getRemoteServerConfig().getFrontEndpointConfigList().size()); FrontEndpointConfig frontEndpointConfig = result.getRemoteServerConfig().getFrontEndpointConfigList().get(0); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_SSE, frontEndpointConfig.getType()); assertEquals(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT, frontEndpointConfig.getEndpointType()); assertEquals("1.1.1.1:8080", frontEndpointConfig.getEndpointData()); assertEquals("/testFront", frontEndpointConfig.getPath()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_HTTP, frontEndpointConfig.getProtocol()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class McpServerImportRequestTest extends BasicRequestTest { @Test void testSerializeJsonImport() throws JsonProcessingException { McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("json"); request.setData("{\"servers\":[{\"name\":\"test-server\"}]}"); request.setOverrideExisting(true); request.setValidateOnly(false); request.setSelectedServers(new String[]{"server1", "server2"}); request.setCursor("cursor123"); request.setLimit(10); request.setSearch("test"); request.setSkipInvalid(true); String json = mapper.writeValueAsString(request); assertTrue(json.contains("\"importType\":\"json\"")); assertTrue(json.contains("\"data\":\"{\\\"servers\\\":[{\\\"name\\\":\\\"test-server\\\"}]}\"")); assertTrue(json.contains("\"overrideExisting\":true")); assertTrue(json.contains("\"validateOnly\":false")); assertTrue(json.contains("\"selectedServers\":[\"server1\",\"server2\"]")); assertTrue(json.contains("\"cursor\":\"cursor123\"")); assertTrue(json.contains("\"limit\":10")); assertTrue(json.contains("\"search\":\"test\"")); assertTrue(json.contains("\"skipInvalid\":true")); } @Test void testSerializeFileImport() throws JsonProcessingException { McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("file"); request.setData("/path/to/import/file.json"); request.setOverrideExisting(false); request.setValidateOnly(true); request.setCursor("cursor456"); request.setLimit(20); request.setSearch("demo"); request.setSkipInvalid(false); String json = mapper.writeValueAsString(request); assertTrue(json.contains("\"importType\":\"file\"")); assertTrue(json.contains("\"data\":\"/path/to/import/file.json\"")); assertTrue(json.contains("\"overrideExisting\":false")); assertTrue(json.contains("\"validateOnly\":true")); assertTrue(json.contains("\"cursor\":\"cursor456\"")); assertTrue(json.contains("\"limit\":20")); assertTrue(json.contains("\"search\":\"demo\"")); assertTrue(json.contains("\"skipInvalid\":false")); } @Test void testSerializeUrlImport() throws JsonProcessingException { McpServerImportRequest request = new McpServerImportRequest(); request.setImportType("url"); request.setData("https://example.com/mcp-servers.json"); request.setOverrideExisting(false); request.setValidateOnly(false); request.setCursor("cursor789"); request.setLimit(30); request.setSearch("prod"); request.setSkipInvalid(true); String json = mapper.writeValueAsString(request); assertTrue(json.contains("\"importType\":\"url\"")); assertTrue(json.contains("\"data\":\"https://example.com/mcp-servers.json\"")); assertTrue(json.contains("\"overrideExisting\":false")); assertTrue(json.contains("\"validateOnly\":false")); assertTrue(json.contains("\"cursor\":\"cursor789\"")); assertTrue(json.contains("\"limit\":30")); assertTrue(json.contains("\"search\":\"prod\"")); assertTrue(json.contains("\"skipInvalid\":true")); } @Test void testDeserializeJsonImport() throws JsonProcessingException { String json = "{\"importType\":\"json\",\"data\":\"{\\\"servers\\\":[{\\\"name\\\":\\\"test-server\\\"}]}\"," + "\"overrideExisting\":true,\"validateOnly\":false,\"selectedServers\":[\"server1\",\"server2\"]," + "\"cursor\":\"cursor123\",\"limit\":10,\"search\":\"test\",\"skipInvalid\":true}"; McpServerImportRequest result = mapper.readValue(json, McpServerImportRequest.class); assertNotNull(result); assertEquals("json", result.getImportType()); assertEquals("{\"servers\":[{\"name\":\"test-server\"}]}", result.getData()); assertTrue(result.isOverrideExisting()); assertFalse(result.isValidateOnly()); assertNotNull(result.getSelectedServers()); assertEquals(2, result.getSelectedServers().length); assertEquals("server1", result.getSelectedServers()[0]); assertEquals("server2", result.getSelectedServers()[1]); assertEquals("cursor123", result.getCursor()); assertEquals(Integer.valueOf(10), result.getLimit()); assertEquals("test", result.getSearch()); assertTrue(result.isSkipInvalid()); } @Test void testDeserializeFileImport() throws JsonProcessingException { String json = "{\"importType\":\"file\",\"data\":\"/path/to/import/file.json\"," + "\"overrideExisting\":false,\"validateOnly\":true," + "\"cursor\":\"cursor456\",\"limit\":20,\"search\":\"demo\",\"skipInvalid\":false}"; McpServerImportRequest result = mapper.readValue(json, McpServerImportRequest.class); assertNotNull(result); assertEquals("file", result.getImportType()); assertEquals("/path/to/import/file.json", result.getData()); assertFalse(result.isOverrideExisting()); assertTrue(result.isValidateOnly()); assertEquals("cursor456", result.getCursor()); assertEquals(Integer.valueOf(20), result.getLimit()); assertEquals("demo", result.getSearch()); assertFalse(result.isSkipInvalid()); } @Test void testDeserializeUrlImport() throws JsonProcessingException { String json = "{\"importType\":\"url\",\"data\":\"https://example.com/mcp-servers.json\"," + "\"overrideExisting\":false,\"validateOnly\":false," + "\"cursor\":\"cursor789\",\"limit\":30,\"search\":\"prod\",\"skipInvalid\":true}"; McpServerImportRequest result = mapper.readValue(json, McpServerImportRequest.class); assertNotNull(result); assertEquals("url", result.getImportType()); assertEquals("https://example.com/mcp-servers.json", result.getData()); assertFalse(result.isOverrideExisting()); assertFalse(result.isValidateOnly()); assertEquals("cursor789", result.getCursor()); assertEquals(Integer.valueOf(30), result.getLimit()); assertEquals("prod", result.getSearch()); assertTrue(result.isSkipInvalid()); } @Test void testDefaultValues() throws JsonProcessingException { String json = "{\"importType\":\"json\",\"data\":\"{}\"}"; McpServerImportRequest result = mapper.readValue(json, McpServerImportRequest.class); assertNotNull(result); assertEquals("json", result.getImportType()); assertEquals("{}", result.getData()); assertFalse(result.isOverrideExisting()); assertFalse(result.isValidateOnly()); assertFalse(result.isSkipInvalid()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; 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; class McpServerImportResponseTest extends BasicRequestTest { @Test void testSerializeSuccessResponse() throws JsonProcessingException { McpServerImportResponse response = new McpServerImportResponse(); response.setSuccess(true); response.setTotalCount(5); response.setSuccessCount(4); response.setFailedCount(1); response.setSkippedCount(0); McpServerImportResult result1 = new McpServerImportResult(); result1.setServerName("server1"); result1.setServerId("id1"); result1.setStatus("success"); McpServerImportResult result2 = new McpServerImportResult(); result2.setServerName("server2"); result2.setStatus("failed"); result2.setErrorMessage("Connection failed"); response.setResults(Arrays.asList(result1, result2)); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"success\":true")); assertTrue(json.contains("\"totalCount\":5")); assertTrue(json.contains("\"successCount\":4")); assertTrue(json.contains("\"failedCount\":1")); assertTrue(json.contains("\"skippedCount\":0")); assertTrue(json.contains("\"results\":[")); assertTrue(json.contains("\"serverName\":\"server1\"")); assertTrue(json.contains("\"serverId\":\"id1\"")); assertTrue(json.contains("\"status\":\"success\"")); assertTrue(json.contains("\"serverName\":\"server2\"")); assertTrue(json.contains("\"status\":\"failed\"")); assertTrue(json.contains("\"errorMessage\":\"Connection failed\"")); } @Test void testSerializeFailedResponse() throws JsonProcessingException { McpServerImportResponse response = new McpServerImportResponse(); response.setSuccess(false); response.setTotalCount(0); response.setSuccessCount(0); response.setFailedCount(0); response.setSkippedCount(0); response.setErrorMessage("Invalid import data format"); response.setResults(Collections.emptyList()); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"success\":false")); assertTrue(json.contains("\"totalCount\":0")); assertTrue(json.contains("\"successCount\":0")); assertTrue(json.contains("\"failedCount\":0")); assertTrue(json.contains("\"skippedCount\":0")); assertTrue(json.contains("\"errorMessage\":\"Invalid import data format\"")); assertTrue(json.contains("\"results\":[]")); } @Test void testSerializePartialSuccessResponse() throws JsonProcessingException { McpServerImportResponse response = new McpServerImportResponse(); response.setSuccess(true); response.setTotalCount(3); response.setSuccessCount(2); response.setFailedCount(0); response.setSkippedCount(1); McpServerImportResult skippedResult = new McpServerImportResult(); skippedResult.setServerName("existing-server"); skippedResult.setStatus("skipped"); skippedResult.setConflictType("duplicate_name"); response.setResults(Collections.singletonList(skippedResult)); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"success\":true")); assertTrue(json.contains("\"totalCount\":3")); assertTrue(json.contains("\"successCount\":2")); assertTrue(json.contains("\"failedCount\":0")); assertTrue(json.contains("\"skippedCount\":1")); assertTrue(json.contains("\"serverName\":\"existing-server\"")); assertTrue(json.contains("\"status\":\"skipped\"")); assertTrue(json.contains("\"conflictType\":\"duplicate_name\"")); } @Test void testDeserializeSuccessResponse() throws JsonProcessingException { String json = "{\"success\":true,\"totalCount\":5,\"successCount\":4,\"failedCount\":1,\"skippedCount\":0," + "\"results\":[{\"serverName\":\"server1\",\"serverId\":\"id1\",\"status\":\"success\"}," + "{\"serverName\":\"server2\",\"status\":\"failed\",\"errorMessage\":\"Connection failed\"}]}"; McpServerImportResponse result = mapper.readValue(json, McpServerImportResponse.class); assertNotNull(result); assertTrue(result.isSuccess()); assertEquals(5, result.getTotalCount()); assertEquals(4, result.getSuccessCount()); assertEquals(1, result.getFailedCount()); assertEquals(0, result.getSkippedCount()); assertNull(result.getErrorMessage()); assertNotNull(result.getResults()); assertEquals(2, result.getResults().size()); McpServerImportResult importResult1 = result.getResults().get(0); assertEquals("server1", importResult1.getServerName()); assertEquals("id1", importResult1.getServerId()); assertEquals("success", importResult1.getStatus()); McpServerImportResult importResult2 = result.getResults().get(1); assertEquals("server2", importResult2.getServerName()); assertEquals("failed", importResult2.getStatus()); assertEquals("Connection failed", importResult2.getErrorMessage()); } @Test void testDeserializeFailedResponse() throws JsonProcessingException { String json = "{\"success\":false,\"totalCount\":0,\"successCount\":0,\"failedCount\":0,\"skippedCount\":0," + "\"errorMessage\":\"Invalid import data format\",\"results\":[]}"; McpServerImportResponse result = mapper.readValue(json, McpServerImportResponse.class); assertNotNull(result); assertFalse(result.isSuccess()); assertEquals(0, result.getTotalCount()); assertEquals(0, result.getSuccessCount()); assertEquals(0, result.getFailedCount()); assertEquals(0, result.getSkippedCount()); assertEquals("Invalid import data format", result.getErrorMessage()); assertNotNull(result.getResults()); assertEquals(0, result.getResults().size()); } @Test void testDeserializePartialSuccessResponse() throws JsonProcessingException { String json = "{\"success\":true,\"totalCount\":3,\"successCount\":2,\"failedCount\":0,\"skippedCount\":1," + "\"results\":[{\"serverName\":\"existing-server\",\"status\":\"skipped\",\"conflictType\":\"duplicate_name\"}]}"; McpServerImportResponse result = mapper.readValue(json, McpServerImportResponse.class); assertNotNull(result); assertTrue(result.isSuccess()); assertEquals(3, result.getTotalCount()); assertEquals(2, result.getSuccessCount()); assertEquals(0, result.getFailedCount()); assertEquals(1, result.getSkippedCount()); assertNotNull(result.getResults()); assertEquals(1, result.getResults().size()); McpServerImportResult importResult = result.getResults().get(0); assertEquals("existing-server", importResult.getServerName()); assertEquals("skipped", importResult.getStatus()); assertEquals("duplicate_name", importResult.getConflictType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportResultTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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 McpServerImportResultTest extends BasicRequestTest { @Test void testSerializeSuccessResult() throws JsonProcessingException { McpServerImportResult result = new McpServerImportResult(); result.setServerName("test-server"); result.setServerId("server-123"); result.setStatus("success"); String json = mapper.writeValueAsString(result); assertTrue(json.contains("\"serverName\":\"test-server\"")); assertTrue(json.contains("\"serverId\":\"server-123\"")); assertTrue(json.contains("\"status\":\"success\"")); } @Test void testSerializeFailedResult() throws JsonProcessingException { McpServerImportResult result = new McpServerImportResult(); result.setServerName("failed-server"); result.setStatus("failed"); result.setErrorMessage("Connection timeout"); String json = mapper.writeValueAsString(result); assertTrue(json.contains("\"serverName\":\"failed-server\"")); assertTrue(json.contains("\"status\":\"failed\"")); assertTrue(json.contains("\"errorMessage\":\"Connection timeout\"")); } @Test void testSerializeSkippedResult() throws JsonProcessingException { McpServerImportResult result = new McpServerImportResult(); result.setServerName("existing-server"); result.setStatus("skipped"); result.setConflictType("duplicate_name"); String json = mapper.writeValueAsString(result); assertTrue(json.contains("\"serverName\":\"existing-server\"")); assertTrue(json.contains("\"status\":\"skipped\"")); assertTrue(json.contains("\"conflictType\":\"duplicate_name\"")); } @Test void testSerializeCompleteResult() throws JsonProcessingException { McpServerImportResult result = new McpServerImportResult(); result.setServerName("complete-server"); result.setServerId("server-456"); result.setStatus("success"); result.setErrorMessage("Warning: deprecated config"); result.setConflictType("version_conflict"); String json = mapper.writeValueAsString(result); assertTrue(json.contains("\"serverName\":\"complete-server\"")); assertTrue(json.contains("\"serverId\":\"server-456\"")); assertTrue(json.contains("\"status\":\"success\"")); assertTrue(json.contains("\"errorMessage\":\"Warning: deprecated config\"")); assertTrue(json.contains("\"conflictType\":\"version_conflict\"")); } @Test void testDeserializeSuccessResult() throws JsonProcessingException { String json = "{\"serverName\":\"test-server\",\"serverId\":\"server-123\",\"status\":\"success\"}"; McpServerImportResult result = mapper.readValue(json, McpServerImportResult.class); assertNotNull(result); assertEquals("test-server", result.getServerName()); assertEquals("server-123", result.getServerId()); assertEquals("success", result.getStatus()); assertNull(result.getErrorMessage()); assertNull(result.getConflictType()); } @Test void testDeserializeFailedResult() throws JsonProcessingException { String json = "{\"serverName\":\"failed-server\",\"status\":\"failed\",\"errorMessage\":\"Connection timeout\"}"; McpServerImportResult result = mapper.readValue(json, McpServerImportResult.class); assertNotNull(result); assertEquals("failed-server", result.getServerName()); assertNull(result.getServerId()); assertEquals("failed", result.getStatus()); assertEquals("Connection timeout", result.getErrorMessage()); assertNull(result.getConflictType()); } @Test void testDeserializeSkippedResult() throws JsonProcessingException { String json = "{\"serverName\":\"existing-server\",\"status\":\"skipped\",\"conflictType\":\"duplicate_name\"}"; McpServerImportResult result = mapper.readValue(json, McpServerImportResult.class); assertNotNull(result); assertEquals("existing-server", result.getServerName()); assertNull(result.getServerId()); assertEquals("skipped", result.getStatus()); assertNull(result.getErrorMessage()); assertEquals("duplicate_name", result.getConflictType()); } @Test void testDeserializeCompleteResult() throws JsonProcessingException { String json = "{\"serverName\":\"complete-server\",\"serverId\":\"server-456\",\"status\":\"success\"," + "\"errorMessage\":\"Warning: deprecated config\",\"conflictType\":\"version_conflict\"}"; McpServerImportResult result = mapper.readValue(json, McpServerImportResult.class); assertNotNull(result); assertEquals("complete-server", result.getServerName()); assertEquals("server-456", result.getServerId()); assertEquals("success", result.getStatus()); assertEquals("Warning: deprecated config", result.getErrorMessage()); assertEquals("version_conflict", result.getConflictType()); } @Test void testDeserializeMinimalResult() throws JsonProcessingException { String json = "{\"serverName\":\"minimal-server\",\"status\":\"unknown\"}"; McpServerImportResult result = mapper.readValue(json, McpServerImportResult.class); assertNotNull(result); assertEquals("minimal-server", result.getServerName()); assertNull(result.getServerId()); assertEquals("unknown", result.getStatus()); assertNull(result.getErrorMessage()); assertNull(result.getConflictType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerImportValidationResultTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; 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; class McpServerImportValidationResultTest extends BasicRequestTest { @Test void testSerializeValidResult() throws JsonProcessingException { McpServerImportValidationResult result = new McpServerImportValidationResult(); result.setValid(true); result.setTotalCount(3); result.setValidCount(3); result.setInvalidCount(0); result.setDuplicateCount(0); result.setNextCursor("next_cursor_123"); result.setHasMore(true); McpServerValidationItem item1 = new McpServerValidationItem(); item1.setServerName("server1"); item1.setServerId("id1"); item1.setStatus("valid"); item1.setSelected(true); McpServerValidationItem item2 = new McpServerValidationItem(); item2.setServerName("server2"); item2.setServerId("id2"); item2.setStatus("valid"); item2.setSelected(false); result.setServers(Arrays.asList(item1, item2)); result.setErrors(Collections.emptyList()); String json = mapper.writeValueAsString(result); assertTrue(json.contains("\"valid\":true")); assertTrue(json.contains("\"totalCount\":3")); assertTrue(json.contains("\"validCount\":3")); assertTrue(json.contains("\"invalidCount\":0")); assertTrue(json.contains("\"duplicateCount\":0")); assertTrue(json.contains("\"servers\":[")); assertTrue(json.contains("\"serverName\":\"server1\"")); assertTrue(json.contains("\"serverId\":\"id1\"")); assertTrue(json.contains("\"status\":\"valid\"")); assertTrue(json.contains("\"selected\":true")); assertTrue(json.contains("\"selected\":false")); assertTrue(json.contains("\"errors\":[]")); assertTrue(json.contains("\"nextCursor\":\"next_cursor_123\"")); assertTrue(json.contains("\"hasMore\":true")); } @Test void testSerializeInvalidResult() throws JsonProcessingException { McpServerImportValidationResult result = new McpServerImportValidationResult(); result.setValid(false); result.setTotalCount(2); result.setValidCount(1); result.setInvalidCount(1); result.setDuplicateCount(0); result.setNextCursor("next_cursor_456"); result.setHasMore(false); McpServerValidationItem validItem = new McpServerValidationItem(); validItem.setServerName("valid-server"); validItem.setStatus("valid"); validItem.setExists(false); McpServerValidationItem invalidItem = new McpServerValidationItem(); invalidItem.setServerName("invalid-server"); invalidItem.setStatus("invalid"); invalidItem.setErrors(Arrays.asList("Missing protocol", "Invalid port")); result.setServers(Arrays.asList(validItem, invalidItem)); result.setErrors(Arrays.asList("Invalid JSON format", "Missing required fields")); String json = mapper.writeValueAsString(result); assertTrue(json.contains("\"valid\":false")); assertTrue(json.contains("\"totalCount\":2")); assertTrue(json.contains("\"validCount\":1")); assertTrue(json.contains("\"invalidCount\":1")); assertTrue(json.contains("\"duplicateCount\":0")); assertTrue(json.contains("\"serverName\":\"valid-server\"")); assertTrue(json.contains("\"status\":\"valid\"")); assertTrue(json.contains("\"exists\":false")); assertTrue(json.contains("\"serverName\":\"invalid-server\"")); assertTrue(json.contains("\"status\":\"invalid\"")); assertTrue(json.contains("\"errors\":[\"Missing protocol\",\"Invalid port\"]")); assertTrue(json.contains("\"errors\":[\"Invalid JSON format\",\"Missing required fields\"]")); assertTrue(json.contains("\"nextCursor\":\"next_cursor_456\"")); assertTrue(json.contains("\"hasMore\":false")); } @Test void testSerializeDuplicateResult() throws JsonProcessingException { McpServerImportValidationResult result = new McpServerImportValidationResult(); result.setValid(true); result.setTotalCount(2); result.setValidCount(1); result.setInvalidCount(0); result.setDuplicateCount(1); result.setNextCursor("next_cursor_789"); result.setHasMore(true); McpServerValidationItem duplicateItem = new McpServerValidationItem(); duplicateItem.setServerName("existing-server"); duplicateItem.setServerId("existing-id"); duplicateItem.setStatus("duplicate"); duplicateItem.setExists(true); result.setServers(Collections.singletonList(duplicateItem)); result.setErrors(Collections.emptyList()); String json = mapper.writeValueAsString(result); assertTrue(json.contains("\"valid\":true")); assertTrue(json.contains("\"duplicateCount\":1")); assertTrue(json.contains("\"serverName\":\"existing-server\"")); assertTrue(json.contains("\"status\":\"duplicate\"")); assertTrue(json.contains("\"exists\":true")); assertTrue(json.contains("\"nextCursor\":\"next_cursor_789\"")); assertTrue(json.contains("\"hasMore\":true")); } @Test void testDeserializeValidResult() throws JsonProcessingException { String json = "{\"valid\":true,\"totalCount\":3,\"validCount\":3,\"invalidCount\":0,\"duplicateCount\":0," + "\"servers\":[{\"serverName\":\"server1\",\"serverId\":\"id1\",\"status\":\"valid\",\"selected\":true,\"exists\":false}," + "{\"serverName\":\"server2\",\"serverId\":\"id2\",\"status\":\"valid\",\"selected\":false,\"exists\":false}]," + "\"errors\":[],\"nextCursor\":\"next_cursor_123\",\"hasMore\":true}"; McpServerImportValidationResult result = mapper.readValue(json, McpServerImportValidationResult.class); assertNotNull(result); assertTrue(result.isValid()); assertEquals(3, result.getTotalCount()); assertEquals(3, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertEquals("next_cursor_123", result.getNextCursor()); assertTrue(result.isHasMore()); assertNotNull(result.getServers()); assertEquals(2, result.getServers().size()); McpServerValidationItem item1 = result.getServers().get(0); assertEquals("server1", item1.getServerName()); assertEquals("id1", item1.getServerId()); assertEquals("valid", item1.getStatus()); assertTrue(item1.isSelected()); assertFalse(item1.isExists()); McpServerValidationItem item2 = result.getServers().get(1); assertEquals("server2", item2.getServerName()); assertEquals("id2", item2.getServerId()); assertEquals("valid", item2.getStatus()); assertFalse(item2.isSelected()); assertFalse(item2.isExists()); assertNotNull(result.getErrors()); assertEquals(0, result.getErrors().size()); } @Test void testDeserializeInvalidResult() throws JsonProcessingException { String json = "{\"valid\":false,\"totalCount\":2,\"validCount\":1,\"invalidCount\":1,\"duplicateCount\":0," + "\"servers\":[{\"serverName\":\"valid-server\",\"status\":\"valid\",\"exists\":false}," + "{\"serverName\":\"invalid-server\",\"status\":\"invalid\",\"errors\":[\"Missing protocol\",\"Invalid port\"]}]," + "\"errors\":[\"Invalid JSON format\",\"Missing required fields\"]," + "\"nextCursor\":\"next_cursor_456\",\"hasMore\":false}"; McpServerImportValidationResult result = mapper.readValue(json, McpServerImportValidationResult.class); assertNotNull(result); assertFalse(result.isValid()); assertEquals(2, result.getTotalCount()); assertEquals(1, result.getValidCount()); assertEquals(1, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertEquals("next_cursor_456", result.getNextCursor()); assertFalse(result.isHasMore()); assertNotNull(result.getServers()); assertEquals(2, result.getServers().size()); McpServerValidationItem validItem = result.getServers().get(0); assertEquals("valid-server", validItem.getServerName()); assertEquals("valid", validItem.getStatus()); assertFalse(validItem.isExists()); McpServerValidationItem invalidItem = result.getServers().get(1); assertEquals("invalid-server", invalidItem.getServerName()); assertEquals("invalid", invalidItem.getStatus()); assertNotNull(invalidItem.getErrors()); assertEquals(2, invalidItem.getErrors().size()); assertEquals("Missing protocol", invalidItem.getErrors().get(0)); assertEquals("Invalid port", invalidItem.getErrors().get(1)); assertNotNull(result.getErrors()); assertEquals(2, result.getErrors().size()); assertEquals("Invalid JSON format", result.getErrors().get(0)); assertEquals("Missing required fields", result.getErrors().get(1)); } @Test void testDeserializeDuplicateResult() throws JsonProcessingException { String json = "{\"valid\":true,\"totalCount\":2,\"validCount\":1,\"invalidCount\":0,\"duplicateCount\":1," + "\"servers\":[{\"serverName\":\"existing-server\",\"serverId\":\"existing-id\",\"status\":\"duplicate\",\"exists\":true}]," + "\"errors\":[],\"nextCursor\":\"next_cursor_789\",\"hasMore\":true}"; McpServerImportValidationResult result = mapper.readValue(json, McpServerImportValidationResult.class); assertNotNull(result); assertTrue(result.isValid()); assertEquals(2, result.getTotalCount()); assertEquals(1, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(1, result.getDuplicateCount()); assertEquals("next_cursor_789", result.getNextCursor()); assertTrue(result.isHasMore()); assertNotNull(result.getServers()); assertEquals(1, result.getServers().size()); McpServerValidationItem duplicateItem = result.getServers().get(0); assertEquals("existing-server", duplicateItem.getServerName()); assertEquals("existing-id", duplicateItem.getServerId()); assertEquals("duplicate", duplicateItem.getStatus()); assertTrue(duplicateItem.isExists()); assertNotNull(result.getErrors()); assertEquals(0, result.getErrors().size()); } @Test void testDeserializeMinimalResult() throws JsonProcessingException { String json = "{\"valid\":false,\"totalCount\":0,\"validCount\":0,\"invalidCount\":0,\"duplicateCount\":0}"; McpServerImportValidationResult result = mapper.readValue(json, McpServerImportValidationResult.class); assertNotNull(result); assertFalse(result.isValid()); assertEquals(0, result.getTotalCount()); assertEquals(0, result.getValidCount()); assertEquals(0, result.getInvalidCount()); assertEquals(0, result.getDuplicateCount()); assertNull(result.getServers()); assertNull(result.getErrors()); assertNull(result.getNextCursor()); assertFalse(result.isHasMore()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerRemoteServiceConfigTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.KeyValueInput; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; 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 McpServerRemoteServiceConfigTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpServerRemoteServiceConfig remoteServiceConfig = new McpServerRemoteServiceConfig(); remoteServiceConfig.setExportPath("/mcp/export"); McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId("public"); serviceRef.setGroupName("DEFAULT_GROUP"); serviceRef.setServiceName("mcp-service"); remoteServiceConfig.setServiceRef(serviceRef); final List frontEndpointConfigs = new ArrayList<>(); FrontEndpointConfig frontEndpointConfig = new FrontEndpointConfig(); frontEndpointConfig.setType("sse"); frontEndpointConfig.setProtocol("http"); frontEndpointConfig.setEndpointType("DIRECT"); frontEndpointConfig.setEndpointData("127.0.0.1:8080"); frontEndpointConfig.setPath("/front"); List headers = new ArrayList<>(); KeyValueInput header = new KeyValueInput(); header.setName("Authorization"); header.setValue("Bearer token"); headers.add(header); frontEndpointConfig.setHeaders(headers); frontEndpointConfigs.add(frontEndpointConfig); remoteServiceConfig.setFrontEndpointConfigList(frontEndpointConfigs); String json = mapper.writeValueAsString(remoteServiceConfig); assertTrue(json.contains("\"exportPath\":\"/mcp/export\"")); assertTrue(json.contains("\"serviceRef\":{")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"groupName\":\"DEFAULT_GROUP\"")); assertTrue(json.contains("\"serviceName\":\"mcp-service\"")); assertTrue(json.contains("\"frontEndpointConfigList\":[")); assertTrue(json.contains("\"type\":\"sse\"")); assertTrue(json.contains("\"protocol\":\"http\"")); assertTrue(json.contains("\"endpointType\":\"DIRECT\"")); assertTrue(json.contains("\"endpointData\":\"127.0.0.1:8080\"")); assertTrue(json.contains("\"path\":\"/front\"")); assertTrue(json.contains("\"headers\":[")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"serviceRef\":{\"namespaceId\":\"public\",\"groupName\":\"DEFAULT_GROUP\"," + "\"serviceName\":\"mcp-service\"},\"exportPath\":\"/mcp/export\"," + "\"frontEndpointConfigList\":[{\"type\":\"sse\",\"protocol\":\"http\"," + "\"endpointType\":\"DIRECT\",\"endpointData\":\"127.0.0.1:8080\",\"path\":\"/front\"," + "\"headers\":[{\"name\":\"Authorization\",\"value\":\"Bearer token\"}]}]}"; McpServerRemoteServiceConfig result = mapper.readValue(json, McpServerRemoteServiceConfig.class); assertNotNull(result); assertEquals("/mcp/export", result.getExportPath()); assertNotNull(result.getServiceRef()); assertEquals("public", result.getServiceRef().getNamespaceId()); assertEquals("DEFAULT_GROUP", result.getServiceRef().getGroupName()); assertEquals("mcp-service", result.getServiceRef().getServiceName()); assertNotNull(result.getFrontEndpointConfigList()); assertEquals(1, result.getFrontEndpointConfigList().size()); FrontEndpointConfig frontEndpointConfig = result.getFrontEndpointConfigList().get(0); assertEquals("sse", frontEndpointConfig.getType()); assertEquals("http", frontEndpointConfig.getProtocol()); assertEquals("DIRECT", frontEndpointConfig.getEndpointType()); assertEquals("127.0.0.1:8080", frontEndpointConfig.getEndpointData()); assertEquals("/front", frontEndpointConfig.getPath()); assertNotNull(frontEndpointConfig.getHeaders()); assertEquals(1, frontEndpointConfig.getHeaders().size()); assertEquals("Authorization", frontEndpointConfig.getHeaders().get(0).getName()); assertEquals("Bearer token", frontEndpointConfig.getHeaders().get(0).getValue()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerValidationItemTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; 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; class McpServerValidationItemTest extends BasicRequestTest { @Test void testSerializeValidItem() throws JsonProcessingException { mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); McpServerValidationItem item = new McpServerValidationItem(); item.setServerName("test-server"); item.setServerId("server-123"); item.setStatus("valid"); item.setExists(false); item.setSelected(true); McpServerDetailInfo server = new McpServerDetailInfo(); server.setName("test-server"); server.setId("server-123"); server.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); server.setDescription("Test server"); item.setServer(server); String json = mapper.writeValueAsString(item); assertTrue(json.contains("\"serverName\":\"test-server\"")); assertTrue(json.contains("\"serverId\":\"server-123\"")); assertTrue(json.contains("\"status\":\"valid\"")); assertTrue(json.contains("\"exists\":false")); assertTrue(json.contains("\"selected\":true")); assertTrue(json.contains("\"server\":{")); assertTrue(json.contains("\"name\":\"test-server\"")); assertTrue(json.contains("\"protocol\":\"stdio\"")); } @Test void testSerializeInvalidItem() throws JsonProcessingException { McpServerValidationItem item = new McpServerValidationItem(); item.setServerName("invalid-server"); item.setStatus("invalid"); item.setErrors(Arrays.asList("Missing protocol", "Invalid port", "Empty name")); item.setExists(false); item.setSelected(false); String json = mapper.writeValueAsString(item); assertTrue(json.contains("\"serverName\":\"invalid-server\"")); assertTrue(json.contains("\"status\":\"invalid\"")); assertTrue(json.contains("\"errors\":[\"Missing protocol\",\"Invalid port\",\"Empty name\"]")); assertTrue(json.contains("\"exists\":false")); assertTrue(json.contains("\"selected\":false")); } @Test void testSerializeDuplicateItem() throws JsonProcessingException { McpServerValidationItem item = new McpServerValidationItem(); item.setServerName("existing-server"); item.setServerId("existing-id"); item.setStatus("duplicate"); item.setExists(true); item.setSelected(false); item.setErrors(Collections.singletonList("Server already exists")); String json = mapper.writeValueAsString(item); assertTrue(json.contains("\"serverName\":\"existing-server\"")); assertTrue(json.contains("\"serverId\":\"existing-id\"")); assertTrue(json.contains("\"status\":\"duplicate\"")); assertTrue(json.contains("\"exists\":true")); assertTrue(json.contains("\"selected\":false")); assertTrue(json.contains("\"errors\":[\"Server already exists\"]")); } @Test void testSerializeDefaultSelectedValue() throws JsonProcessingException { McpServerValidationItem item = new McpServerValidationItem(); item.setServerName("default-server"); item.setStatus("valid"); String json = mapper.writeValueAsString(item); assertTrue(json.contains("\"serverName\":\"default-server\"")); assertTrue(json.contains("\"status\":\"valid\"")); assertTrue(json.contains("\"selected\":true")); } @Test void testDeserializeValidItem() throws JsonProcessingException { String json = "{\"serverName\":\"test-server\",\"serverId\":\"server-123\",\"status\":\"valid\"," + "\"exists\":false,\"selected\":true,\"server\":{\"name\":\"test-server\",\"id\":\"server-123\"," + "\"protocol\":\"stdio\",\"description\":\"Test server\"}}"; McpServerValidationItem result = mapper.readValue(json, McpServerValidationItem.class); assertNotNull(result); assertEquals("test-server", result.getServerName()); assertEquals("server-123", result.getServerId()); assertEquals("valid", result.getStatus()); assertNull(result.getErrors()); assertFalse(result.isExists()); assertTrue(result.isSelected()); assertNotNull(result.getServer()); assertEquals("test-server", result.getServer().getName()); assertEquals("server-123", result.getServer().getId()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, result.getServer().getProtocol()); assertEquals("Test server", result.getServer().getDescription()); } @Test void testDeserializeInvalidItem() throws JsonProcessingException { String json = "{\"serverName\":\"invalid-server\",\"status\":\"invalid\"," + "\"errors\":[\"Missing protocol\",\"Invalid port\",\"Empty name\"],\"exists\":false,\"selected\":false}"; McpServerValidationItem result = mapper.readValue(json, McpServerValidationItem.class); assertNotNull(result); assertEquals("invalid-server", result.getServerName()); assertNull(result.getServerId()); assertEquals("invalid", result.getStatus()); assertNotNull(result.getErrors()); assertEquals(3, result.getErrors().size()); assertEquals("Missing protocol", result.getErrors().get(0)); assertEquals("Invalid port", result.getErrors().get(1)); assertEquals("Empty name", result.getErrors().get(2)); assertFalse(result.isExists()); assertFalse(result.isSelected()); assertNull(result.getServer()); } @Test void testDeserializeDuplicateItem() throws JsonProcessingException { String json = "{\"serverName\":\"existing-server\",\"serverId\":\"existing-id\",\"status\":\"duplicate\"," + "\"exists\":true,\"selected\":false,\"errors\":[\"Server already exists\"]}"; McpServerValidationItem result = mapper.readValue(json, McpServerValidationItem.class); assertNotNull(result); assertEquals("existing-server", result.getServerName()); assertEquals("existing-id", result.getServerId()); assertEquals("duplicate", result.getStatus()); assertNotNull(result.getErrors()); assertEquals(1, result.getErrors().size()); assertEquals("Server already exists", result.getErrors().get(0)); assertTrue(result.isExists()); assertFalse(result.isSelected()); assertNull(result.getServer()); } @Test void testDeserializeDefaultSelectedValue() throws JsonProcessingException { String json = "{\"serverName\":\"default-server\",\"status\":\"valid\"}"; McpServerValidationItem result = mapper.readValue(json, McpServerValidationItem.class); assertNotNull(result); assertEquals("default-server", result.getServerName()); assertEquals("valid", result.getStatus()); assertTrue(result.isSelected()); assertFalse(result.isExists()); } @Test void testDeserializeMinimalItem() throws JsonProcessingException { String json = "{\"serverName\":\"minimal-server\",\"status\":\"unknown\",\"exists\":false,\"selected\":true}"; McpServerValidationItem result = mapper.readValue(json, McpServerValidationItem.class); assertNotNull(result); assertEquals("minimal-server", result.getServerName()); assertNull(result.getServerId()); assertEquals("unknown", result.getStatus()); assertNull(result.getErrors()); assertFalse(result.isExists()); assertTrue(result.isSelected()); assertNull(result.getServer()); } @Test void testDeserializeWithEmptyErrors() throws JsonProcessingException { String json = "{\"serverName\":\"server-with-empty-errors\",\"status\":\"valid\",\"errors\":[]}"; McpServerValidationItem result = mapper.readValue(json, McpServerValidationItem.class); assertNotNull(result); assertEquals("server-with-empty-errors", result.getServerName()); assertEquals("valid", result.getStatus()); assertNotNull(result.getErrors()); assertEquals(0, result.getErrors().size()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServerVersionInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.UUID; 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 McpServerVersionInfoTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpServerVersionInfo mcpServerVersionInfo = new McpServerVersionInfo(); mcpServerVersionInfo.setId(UUID.randomUUID().toString()); mcpServerVersionInfo.setName("testVersionInfo"); mcpServerVersionInfo.setLatestPublishedVersion("1.0.0"); mcpServerVersionInfo.setVersionDetail(new ServerVersionDetail()); mcpServerVersionInfo.getVersionDetail().setVersion("1.0.0"); mcpServerVersionInfo.getVersionDetail().setRelease_date("2023-07-01T00:00:00Z"); mcpServerVersionInfo.getVersionDetail().setIs_latest(true); mcpServerVersionInfo.setVersions(Collections.singletonList(mcpServerVersionInfo.getVersionDetail())); String json = mapper.writeValueAsString(mcpServerVersionInfo); assertNotNull(json); assertTrue(json.contains(String.format("\"id\":\"%s\"", mcpServerVersionInfo.getId()))); assertTrue(json.contains("\"name\":\"testVersionInfo\"")); assertTrue(json.contains("\"versionDetail\":{")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"release_date\":\"2023-07-01T00:00:00Z\"")); assertTrue(json.contains("\"is_latest\":true")); assertTrue(json.contains("\"latestPublishedVersion\":\"1.0.0\"")); assertTrue(json.contains("\"versionDetails\":[{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"id\":\"b646506e-901b-41a1-8790-a4378d11055e\",\"name\":\"testVersionInfo\",\"versionDetail\":" + "{\"version\":\"1.0.0\",\"release_date\":\"2023-07-01T00:00:00Z\",\"is_latest\":true},\"enabled\":true," + "\"latestPublishedVersion\":\"1.0.0\",\"versionDetails\":[{\"version\":\"1.0.0\",\"release_date\":" + "\"2023-07-01T00:00:00Z\",\"is_latest\":true}]}"; McpServerVersionInfo mcpServerVersionInfo = mapper.readValue(json, McpServerVersionInfo.class); assertNotNull(mcpServerVersionInfo); assertEquals("b646506e-901b-41a1-8790-a4378d11055e", mcpServerVersionInfo.getId()); assertEquals("testVersionInfo", mcpServerVersionInfo.getName()); assertNotNull(mcpServerVersionInfo.getVersionDetail()); assertEquals("1.0.0", mcpServerVersionInfo.getVersionDetail().getVersion()); assertEquals("2023-07-01T00:00:00Z", mcpServerVersionInfo.getVersionDetail().getRelease_date()); assertTrue(mcpServerVersionInfo.getVersionDetail().getIs_latest()); assertTrue(mcpServerVersionInfo.isEnabled()); assertEquals("1.0.0", mcpServerVersionInfo.getLatestPublishedVersion()); assertNotNull(mcpServerVersionInfo.getVersionDetails()); assertEquals(1, mcpServerVersionInfo.getVersionDetails().size()); ServerVersionDetail versionDetail = mcpServerVersionInfo.getVersionDetails().get(0); assertEquals("1.0.0", versionDetail.getVersion()); assertEquals("2023-07-01T00:00:00Z", versionDetail.getRelease_date()); assertTrue(versionDetail.getIs_latest()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpServiceRefTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class McpServiceRefTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpServiceRef serviceRef = new McpServiceRef(); serviceRef.setNamespaceId("public"); serviceRef.setGroupName("DEFAULT_GROUP"); serviceRef.setServiceName("mcp-service"); serviceRef.setTransportProtocol("http"); String json = mapper.writeValueAsString(serviceRef); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"groupName\":\"DEFAULT_GROUP\"")); assertTrue(json.contains("\"serviceName\":\"mcp-service\"")); assertTrue(json.contains("\"transportProtocol\":\"http\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"namespaceId\":\"public\",\"groupName\":\"DEFAULT_GROUP\"," + "\"serviceName\":\"mcp-service\",\"transportProtocol\":\"http\"}"; McpServiceRef result = mapper.readValue(json, McpServiceRef.class); assertNotNull(result); assertEquals("public", result.getNamespaceId()); assertEquals("DEFAULT_GROUP", result.getGroupName()); assertEquals("mcp-service", result.getServiceName()); assertEquals("http", result.getTransportProtocol()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpToolMetaTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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 McpToolMetaTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpToolMeta toolMeta = new McpToolMeta(); toolMeta.setEnabled(true); Map invokeContext = new HashMap<>(); invokeContext.put("path", "/api/tool"); invokeContext.put("method", "POST"); toolMeta.setInvokeContext(invokeContext); Map templates = new HashMap<>(); Map template = new HashMap<>(); template.put("templateType", "json"); templates.put("default", template); toolMeta.setTemplates(templates); String json = mapper.writeValueAsString(toolMeta); assertTrue(json.contains("\"enabled\":true")); assertTrue(json.contains("\"invokeContext\":{")); assertTrue(json.contains("\"path\":\"/api/tool\"")); assertTrue(json.contains("\"method\":\"POST\"")); assertTrue(json.contains("\"templates\":{")); assertTrue(json.contains("\"default\":{")); assertTrue(json.contains("\"templateType\":\"json\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"invokeContext\":{\"path\":\"/api/tool\",\"method\":\"POST\"}," + "\"enabled\":true,\"templates\":{\"default\":{\"templateType\":\"json\"}}}"; McpToolMeta result = mapper.readValue(json, McpToolMeta.class); assertNotNull(result); assertTrue(result.isEnabled()); assertNotNull(result.getInvokeContext()); assertEquals("/api/tool", result.getInvokeContext().get("path")); assertEquals("POST", result.getInvokeContext().get("method")); assertNotNull(result.getTemplates()); assertNotNull(result.getTemplates().get("default")); Map template = (Map) result.getTemplates().get("default"); assertEquals("json", template.get("templateType")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpToolSpecificationTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; import java.util.Map; 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 McpToolSpecificationTest extends BasicRequestTest { private static final String MCP_TOOL_SPEC = "{\"tools\":[{\"name\":\"testTool\",\"description\":\"test tool description\",\"inputSchema\":{\"type\":\"object\"," + "\"properties\":{\"a\":{\"description\":\"aaa\",\"type\":\"string\"}}}}],\"toolsMeta\":{\"testTool\":" + "{\"invokeContext\":{\"path\":\"/xxx\",\"method\":\"GET\"},\"enabled\":true,\"templates\":" + "{\"json-go-tamplate\":{\"templateType\":\"string\",\"responseTemplate\":{\"body\":\"string\"}," + "\"requestTemplate\":{\"headers\":[],\"method\":\"GET\",\"argsToFormBody\":true,\"argsToJsonBody\":false," + "\"body\":\"string\",\"url\":\"\",\"argsToUrlParam\":true}}}}},\"securitySchemes\":[{\"id\":\"1\"," + "\"type\":\"apiKey\",\"scheme\":\"\",\"in\":\"header\",\"name\":\"testSecurity\"," + "\"defaultCredential\":\"publicKey\"}]}"; @Test void testSerialize() throws JsonProcessingException { McpToolSpecification toolSpecification = new McpToolSpecification(); toolSpecification.setSpecificationType("encrypted"); // 添加 EncryptObject 测试 EncryptObject encryptObject = new EncryptObject(); encryptObject.setData("encryptedData"); Map encryptInfo = new HashMap<>(); encryptInfo.put("alg", "AES"); encryptInfo.put("iv", "initialVector"); encryptObject.setEncryptInfo(encryptInfo); toolSpecification.setEncryptData(encryptObject); McpTool mcpTool = new McpTool(); toolSpecification.setTools(Collections.singletonList(mcpTool)); mcpTool.setName("testTool"); mcpTool.setDescription("test tool description"); Map inputSchema = new HashMap<>(); inputSchema.put("type", "object"); Map properties = new HashMap<>(); inputSchema.put("properties", properties); Map aSchema = new HashMap<>(); properties.put("a", aSchema); aSchema.put("type", "string"); aSchema.put("description", "aaa"); mcpTool.setInputSchema(inputSchema); Map outputSchema = new HashMap<>(); outputSchema.put("type", "object"); Map outProperties = new HashMap<>(); Map resultSchema = new HashMap<>(); resultSchema.put("type", "string"); resultSchema.put("description", "result"); outProperties.put("result", resultSchema); outputSchema.put("properties", outProperties); mcpTool.setOutputSchema(outputSchema); McpToolMeta mcpToolMeta = new McpToolMeta(); Map templates = new HashMap<>(); mcpToolMeta.setTemplates(templates); Map jsonGoTemplate = new HashMap<>(); templates.put("json-go-tamplate", jsonGoTemplate); jsonGoTemplate.put("templateType", "string"); Map requestTemplate = new HashMap<>(); jsonGoTemplate.put("requestTemplate", requestTemplate); requestTemplate.put("url", ""); requestTemplate.put("method", "GET"); requestTemplate.put("headers", Collections.emptyList()); requestTemplate.put("argsToJsonBody", false); requestTemplate.put("argsToUrlParam", true); requestTemplate.put("argsToFormBody", true); requestTemplate.put("body", "string"); Map responseTemplate = new HashMap<>(); jsonGoTemplate.put("responseTemplate", responseTemplate); responseTemplate.put("body", "string"); Map invokeContext = new HashMap<>(); invokeContext.put("path", "/xxx"); invokeContext.put("method", "GET"); mcpToolMeta.setInvokeContext(invokeContext); toolSpecification.setToolsMeta(Collections.singletonMap("testTool", mcpToolMeta)); SecurityScheme securityScheme = new SecurityScheme(); securityScheme.setId("1"); securityScheme.setType("apiKey"); securityScheme.setName("testSecurity"); securityScheme.setScheme(""); securityScheme.setIn("header"); securityScheme.setDefaultCredential("publicKey"); toolSpecification.setSecuritySchemes(Collections.singletonList(securityScheme)); String json = mapper.writeValueAsString(toolSpecification); assertNotNull(json); assertTrue(json.contains("\"specificationType\":\"encrypted\"")); assertTrue(json.contains("\"encryptData\":{")); assertTrue(json.contains("\"data\":\"encryptedData\"")); assertTrue(json.contains("\"encryptInfo\":{")); assertTrue(json.contains("\"tools\":[{")); assertTrue(json.contains("\"name\":\"testTool\"")); assertTrue(json.contains("\"description\":\"test tool description\"")); assertTrue(json.contains("\"inputSchema\":{")); assertTrue(json.contains("{\"type\":\"object\"")); assertTrue(json.contains("\"properties\":{\"a\":{")); assertTrue(json.contains("\"outputSchema\":{")); assertTrue(json.contains("\"toolsMeta\":{\"testTool\":{")); assertTrue(json.contains("\"invokeContext\":{")); assertTrue(json.contains("\"templates\":{")); assertTrue(json.contains("\"securitySchemes\":[{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"specificationType\":\"encrypted\",\"encryptData\":{\"data\":\"encryptedData\"," + "\"encryptInfo\":{\"alg\":\"AES\",\"iv\":\"initialVector\"}}," + "\"tools\":[{\"name\":\"testTool\",\"description\":\"test tool description\"," + "\"inputSchema\":{\"type\":\"object\"," + "\"properties\":{\"a\":{\"description\":\"aaa\",\"type\":\"string\"}}}," + "\"outputSchema\":{\"type\":\"object\"," + "\"properties\":{\"result\":{\"type\":\"string\"," + "\"description\":\"result\"}}}}]," + "\"toolsMeta\":{\"testTool\":" + "{\"invokeContext\":{\"path\":\"/xxx\",\"method\":\"GET\"},\"enabled\":true,\"templates\":" + "{\"json-go-tamplate\":{\"templateType\":\"string\",\"responseTemplate\":{\"body\":\"string\"}," + "\"requestTemplate\":{\"headers\":[],\"method\":\"GET\",\"argsToFormBody\":true," + "\"argsToJsonBody\":false," + "\"body\":\"string\",\"url\":\"\",\"argsToUrlParam\":true}}}}}," + "\"securitySchemes\":[{\"id\":\"1\"," + "\"type\":\"apiKey\",\"scheme\":\"\",\"in\":\"header\",\"name\":\"testSecurity\"," + "\"defaultCredential\":\"publicKey\"}]}"; McpToolSpecification result = mapper.readValue(json, McpToolSpecification.class); assertEquals("encrypted", result.getSpecificationType()); assertNotNull(result.getEncryptData()); assertEquals("encryptedData", result.getEncryptData().getData()); assertNotNull(result.getEncryptData().getEncryptInfo()); assertEquals("AES", result.getEncryptData().getEncryptInfo().get("alg")); assertEquals("initialVector", result.getEncryptData().getEncryptInfo().get("iv")); assertEquals(1, result.getTools().size()); assertEquals("testTool", result.getTools().get(0).getName()); assertEquals("test tool description", result.getTools().get(0).getDescription()); assertEquals("object", result.getTools().get(0).getInputSchema().get("type")); assertNotNull(result.getTools().get(0).getInputSchema().get("properties")); assertEquals("object", result.getTools().get(0).getOutputSchema().get("type")); assertNotNull(result.getTools().get(0).getOutputSchema().get("properties")); assertEquals(1, result.getToolsMeta().size()); assertNotNull(result.getToolsMeta().get("testTool")); assertNotNull(result.getToolsMeta().get("testTool").getInvokeContext()); assertNotNull(result.getToolsMeta().get("testTool").getTemplates()); assertNotNull(result.getSecuritySchemes()); assertEquals(1, result.getSecuritySchemes().size()); } @Test void testDeserializeOriginal() throws JsonProcessingException { McpToolSpecification result = mapper.readValue(MCP_TOOL_SPEC, McpToolSpecification.class); assertEquals(1, result.getTools().size()); assertEquals("testTool", result.getTools().get(0).getName()); assertEquals("test tool description", result.getTools().get(0).getDescription()); assertEquals("object", result.getTools().get(0).getInputSchema().get("type")); assertNotNull(result.getTools().get(0).getInputSchema().get("properties")); assertEquals(1, result.getToolsMeta().size()); assertNotNull(result.getToolsMeta().get("testTool")); assertNotNull(result.getToolsMeta().get("testTool").getInvokeContext()); assertNotNull(result.getToolsMeta().get("testTool").getTemplates()); assertNotNull(result.getSecuritySchemes()); assertEquals(1, result.getSecuritySchemes().size()); // 默认值测试 assertNull(result.getSpecificationType()); assertNull(result.getEncryptData()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/McpToolTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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 McpToolTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpTool mcpTool = new McpTool(); mcpTool.setName("testTool"); mcpTool.setDescription("A test tool for MCP"); Map inputSchema = new HashMap<>(); inputSchema.put("type", "object"); Map properties = new HashMap<>(); Map paramA = new HashMap<>(); paramA.put("type", "string"); paramA.put("description", "Parameter A"); properties.put("a", paramA); inputSchema.put("properties", properties); mcpTool.setInputSchema(inputSchema); Map outputSchema = new HashMap<>(); outputSchema.put("type", "object"); Map outputProperties = new HashMap<>(); Map resultSchema = new HashMap<>(); resultSchema.put("type", "string"); resultSchema.put("description", "Result"); outputProperties.put("result", resultSchema); outputSchema.put("properties", outputProperties); mcpTool.setOutputSchema(outputSchema); // Set meta field (_meta in JSON) Map meta = new HashMap<>(); meta.put("hint", "This is a test tool"); meta.put("version", 1); mcpTool.setMeta(meta); // Set annotations McpToolAnnotations annotations = new McpToolAnnotations(); annotations.setTitle("Test Tool Title"); annotations.setReadOnlyHint(true); annotations.setDestructiveHint(false); annotations.setIdempotentHint(true); annotations.setOpenWorldHint(false); mcpTool.setAnnotations(annotations); String json = mapper.writeValueAsString(mcpTool); assertTrue(json.contains("\"name\":\"testTool\"")); assertTrue(json.contains("\"description\":\"A test tool for MCP\"")); assertTrue(json.contains("\"inputSchema\":{")); assertTrue(json.contains("\"type\":\"object\"")); assertTrue(json.contains("\"properties\":{")); assertTrue(json.contains("\"a\":{")); assertTrue(json.contains("\"type\":\"string\"")); assertTrue(json.contains("\"description\":\"Parameter A\"")); assertTrue(json.contains("\"outputSchema\":{")); assertTrue(json.contains("\"result\":{")); assertTrue(json.contains("\"description\":\"Result\"")); // Verify _meta field serialization assertTrue(json.contains("\"_meta\":{")); assertTrue(json.contains("\"hint\":\"This is a test tool\"")); assertTrue(json.contains("\"version\":1")); // Verify annotations field serialization assertTrue(json.contains("\"annotations\":{")); assertTrue(json.contains("\"title\":\"Test Tool Title\"")); assertTrue(json.contains("\"readOnlyHint\":true")); assertTrue(json.contains("\"destructiveHint\":false")); assertTrue(json.contains("\"idempotentHint\":true")); assertTrue(json.contains("\"openWorldHint\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"name\":\"testTool\",\"description\":\"A test tool for MCP\"," + "\"inputSchema\":{\"type\":\"object\",\"properties\":{\"a\":{\"type\":\"string\"," + "\"description\":\"Parameter A\"}}}," + "\"outputSchema\":{\"type\":\"object\",\"properties\":{\"result\":{\"type\":\"string\"," + "\"description\":\"Result\"}}}," + "\"_meta\":{\"hint\":\"This is a test tool\",\"version\":1}," + "\"annotations\":{\"title\":\"Test Tool Title\",\"readOnlyHint\":true," + "\"destructiveHint\":false,\"idempotentHint\":true,\"openWorldHint\":false}}"; McpTool result = mapper.readValue(json, McpTool.class); assertNotNull(result); assertEquals("testTool", result.getName()); assertEquals("A test tool for MCP", result.getDescription()); assertNotNull(result.getInputSchema()); assertEquals("object", result.getInputSchema().get("type")); assertNotNull(result.getInputSchema().get("properties")); Map properties = (Map) result.getInputSchema().get("properties"); assertNotNull(properties.get("a")); Map paramA = (Map) properties.get("a"); assertEquals("string", paramA.get("type")); assertEquals("Parameter A", paramA.get("description")); assertNotNull(result.getOutputSchema()); assertEquals("object", result.getOutputSchema().get("type")); Map outProps = (Map) result.getOutputSchema().get("properties"); assertNotNull(outProps.get("result")); // Verify _meta field deserialization assertNotNull(result.getMeta()); assertEquals("This is a test tool", result.getMeta().get("hint")); assertEquals(1, result.getMeta().get("version")); // Verify annotations field deserialization assertNotNull(result.getAnnotations()); assertEquals("Test Tool Title", result.getAnnotations().getTitle()); assertEquals(true, result.getAnnotations().getReadOnlyHint()); assertEquals(false, result.getAnnotations().getDestructiveHint()); assertEquals(true, result.getAnnotations().getIdempotentHint()); assertEquals(false, result.getAnnotations().getOpenWorldHint()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/SecuritySchemeTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class SecuritySchemeTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { SecurityScheme securityScheme = new SecurityScheme(); securityScheme.setId("sec-1"); securityScheme.setType("apiKey"); securityScheme.setScheme("bearer"); securityScheme.setIn("header"); securityScheme.setName("Authorization"); securityScheme.setDefaultCredential("default-token"); String json = mapper.writeValueAsString(securityScheme); assertTrue(json.contains("\"id\":\"sec-1\"")); assertTrue(json.contains("\"type\":\"apiKey\"")); assertTrue(json.contains("\"scheme\":\"bearer\"")); assertTrue(json.contains("\"in\":\"header\"")); assertTrue(json.contains("\"name\":\"Authorization\"")); assertTrue(json.contains("\"defaultCredential\":\"default-token\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"id\":\"sec-1\",\"type\":\"apiKey\",\"scheme\":\"bearer\",\"in\":\"header\"," + "\"name\":\"Authorization\",\"defaultCredential\":\"default-token\"}"; SecurityScheme result = mapper.readValue(json, SecurityScheme.class); assertNotNull(result); assertEquals("sec-1", result.getId()); assertEquals("apiKey", result.getType()); assertEquals("bearer", result.getScheme()); assertEquals("header", result.getIn()); assertEquals("Authorization", result.getName()); assertEquals("default-token", result.getDefaultCredential()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/IconTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; 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 IconTest extends BasicRequestTest { @Test void testSerializePngIcon() throws JsonProcessingException { Icon icon = new Icon(); icon.setSrc("https://example.com/icon.png"); icon.setMimeType(Icon.MimeType.IMAGE_PNG); icon.setSizes(Arrays.asList("16x16", "32x32", "64x64")); icon.setTheme(Icon.Theme.LIGHT); String json = mapper.writeValueAsString(icon); assertNotNull(json); assertTrue(json.contains("\"src\":\"https://example.com/icon.png\"")); assertTrue(json.contains("\"mimeType\":\"image/png\"")); assertTrue(json.contains("\"sizes\":[\"16x16\",\"32x32\",\"64x64\"]")); assertTrue(json.contains("\"theme\":\"light\"")); } @Test void testSerializeSvgIcon() throws JsonProcessingException { Icon icon = new Icon(); icon.setSrc("https://example.com/icon.svg"); icon.setMimeType(Icon.MimeType.IMAGE_SVG_XML); icon.setTheme(Icon.Theme.DARK); String json = mapper.writeValueAsString(icon); assertTrue(json.contains("\"mimeType\":\"image/svg+xml\"")); assertTrue(json.contains("\"theme\":\"dark\"")); } @Test void testDeserializeIcon() throws JsonProcessingException { String json = "{\"src\":\"https://example.com/icon.png\",\"mimeType\":\"image/png\"," + "\"sizes\":[\"16x16\",\"32x32\"],\"theme\":\"light\"}"; Icon icon = mapper.readValue(json, Icon.class); assertNotNull(icon); assertEquals("https://example.com/icon.png", icon.getSrc()); assertEquals(Icon.MimeType.IMAGE_PNG, icon.getMimeType()); assertEquals(2, icon.getSizes().size()); assertEquals("16x16", icon.getSizes().get(0)); assertEquals(Icon.Theme.LIGHT, icon.getTheme()); } @Test void testMimeTypeEnumValues() { assertEquals("image/png", Icon.MimeType.IMAGE_PNG.getValue()); assertEquals("image/jpeg", Icon.MimeType.IMAGE_JPEG.getValue()); assertEquals("image/jpg", Icon.MimeType.IMAGE_JPG.getValue()); assertEquals("image/svg+xml", Icon.MimeType.IMAGE_SVG_XML.getValue()); assertEquals("image/webp", Icon.MimeType.IMAGE_WEBP.getValue()); } @Test void testThemeEnumValues() { assertEquals("light", Icon.Theme.LIGHT.getValue()); assertEquals("dark", Icon.Theme.DARK.getValue()); } @Test void testMimeTypeFromValue() { assertEquals(Icon.MimeType.IMAGE_PNG, Icon.MimeType.fromValue("image/png")); assertEquals(Icon.MimeType.IMAGE_SVG_XML, Icon.MimeType.fromValue("image/svg+xml")); assertEquals(Icon.MimeType.IMAGE_WEBP, Icon.MimeType.fromValue("image/webp")); } @Test void testThemeFromValue() { assertEquals(Icon.Theme.LIGHT, Icon.Theme.fromValue("light")); assertEquals(Icon.Theme.DARK, Icon.Theme.fromValue("dark")); } @Test void testMimeTypeFromValueCaseInsensitive() { assertEquals(Icon.MimeType.IMAGE_PNG, Icon.MimeType.fromValue("IMAGE/PNG")); assertEquals(Icon.MimeType.IMAGE_SVG_XML, Icon.MimeType.fromValue("IMAGE/SVG+XML")); } @Test void testIconMinimalRequired() throws JsonProcessingException { Icon icon = new Icon(); icon.setSrc("https://example.com/required.png"); String json = mapper.writeValueAsString(icon); assertTrue(json.contains("\"src\":\"https://example.com/required.png\"")); assertTrue(json.contains("\"mimeType\":null") || !json.contains("mimeType")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/InputTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; 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 InputTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { Input input = new Input(); input.setDescription("test description"); input.setIsRequired(true); input.setFormat("string"); input.setValue("test value"); input.setIsSecret(false); input.setDefaultValue("default value"); input.setChoices(Arrays.asList("choice1", "choice2")); String json = mapper.writeValueAsString(input); assertNotNull(json); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"isRequired\":true")); assertTrue(json.contains("\"format\":\"string\"")); assertTrue(json.contains("\"value\":\"test value\"")); assertTrue(json.contains("\"isSecret\":false")); assertTrue(json.contains("\"defaultValue\":\"default value\"")); assertTrue(json.contains("\"choices\":[\"choice1\",\"choice2\"]")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"description\":\"test description\",\"isRequired\":true,\"format\":\"string\"," + "\"value\":\"test value\",\"isSecret\":false,\"defaultValue\":\"default value\"," + "\"choices\":[\"choice1\",\"choice2\"]}"; Input input = mapper.readValue(json, Input.class); assertNotNull(input); assertEquals("test description", input.getDescription()); assertEquals(true, input.getIsRequired()); assertEquals("string", input.getFormat()); assertEquals("test value", input.getValue()); assertEquals(false, input.getIsSecret()); assertEquals("default value", input.getDefaultValue()); assertEquals(2, input.getChoices().size()); assertEquals("choice1", input.getChoices().get(0)); assertEquals("choice2", input.getChoices().get(1)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/InputWithVariablesTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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 InputWithVariablesTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { InputWithVariables inputWithVariables = new InputWithVariables(); inputWithVariables.setDescription("test description"); Map variables = new HashMap<>(); Input varInput = new Input(); varInput.setDescription("variable description"); variables.put("var1", varInput); inputWithVariables.setVariables(variables); String json = mapper.writeValueAsString(inputWithVariables); assertNotNull(json); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"variables\":")); assertTrue(json.contains("\"var1\":")); assertTrue(json.contains("\"variable description\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"description\":\"test description\",\"variables\":{\"var1\":{\"description\":\"variable description\"}}}"; InputWithVariables inputWithVariables = mapper.readValue(json, InputWithVariables.class); assertNotNull(inputWithVariables); assertEquals("test description", inputWithVariables.getDescription()); assertEquals(1, inputWithVariables.getVariables().size()); assertEquals("variable description", inputWithVariables.getVariables().get("var1").getDescription()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/KeyValueInputTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class KeyValueInputTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { KeyValueInput keyValueInput = new KeyValueInput(); keyValueInput.setName("testKey"); keyValueInput.setDescription("test description"); keyValueInput.setValue("test value"); String json = mapper.writeValueAsString(keyValueInput); assertNotNull(json); assertTrue(json.contains("\"name\":\"testKey\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"value\":\"test value\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"name\":\"testKey\",\"description\":\"test description\",\"value\":\"test value\"}"; KeyValueInput keyValueInput = mapper.readValue(json, KeyValueInput.class); assertNotNull(keyValueInput); assertEquals("testKey", keyValueInput.getName()); assertEquals("test description", keyValueInput.getDescription()); assertEquals("test value", keyValueInput.getValue()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/McpErrorResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class McpErrorResponseTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpErrorResponse errorResponse = new McpErrorResponse(); errorResponse.setError("test error"); String json = mapper.writeValueAsString(errorResponse); assertTrue(json.contains("\"error\":\"test error\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"error\":\"test error\"}"; McpErrorResponse errorResponse = mapper.readValue(json, McpErrorResponse.class); assertEquals("test error", errorResponse.getError()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/McpRegistryServerListTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertNull; class McpRegistryServerListTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpRegistryServerList mcpRegistryServerList = new McpRegistryServerList(); // Use detail type to match List in production code mcpRegistryServerList.setServers(Collections.singletonList(new ServerResponse())); // Set metadata with count and nextCursor (now camelCase) mcpRegistryServerList.setMetadata(new McpRegistryServerList.Metadata("next", 1)); String json = mapper.writeValueAsString(mcpRegistryServerList); assertTrue(json.contains("\"servers\":[")); assertTrue(json.contains("\"metadata\":")); // Primary format: camelCase assertTrue(json.contains("\"nextCursor\":\"next\"")); assertTrue(json.contains("\"count\":1")); } @Test void testDeserialize() throws JsonProcessingException { // Test with new camelCase format (primary) String jsonCamelCase = "{\"servers\":[],\"metadata\":{\"nextCursor\":\"next\",\"count\":1}}"; McpRegistryServerList result1 = mapper.readValue(jsonCamelCase, McpRegistryServerList.class); assertEquals(0, result1.getServers().size()); assertEquals(1, result1.getMetadata().getCount()); assertEquals("next", result1.getMetadata().getNextCursor()); // Test with old snake_case format (backward compatibility) String jsonSnakeCase = "{\"servers\":[],\"metadata\":{\"next_cursor\":\"next\",\"count\":1}}"; McpRegistryServerList result2 = mapper.readValue(jsonSnakeCase, McpRegistryServerList.class); assertEquals(0, result2.getServers().size()); assertEquals(1, result2.getMetadata().getCount()); assertEquals("next", result2.getMetadata().getNextCursor()); } @Test void testSerializeWithMultipleServers() throws JsonProcessingException { McpRegistryServerList list = new McpRegistryServerList(); ServerResponse sr1 = new ServerResponse(); McpRegistryServerDetail server1 = new McpRegistryServerDetail(); server1.setName("Server1"); sr1.setServer(server1); ServerResponse sr2 = new ServerResponse(); McpRegistryServerDetail server2 = new McpRegistryServerDetail(); server2.setName("Server2"); sr2.setServer(server2); list.setServers(Arrays.asList(sr1, sr2)); list.setMetadata(new McpRegistryServerList.Metadata("cursor2", 2)); String json = mapper.writeValueAsString(list); assertTrue(json.contains("\"servers\":[")); assertTrue(json.contains("\"name\":\"Server1\"")); assertTrue(json.contains("\"name\":\"Server2\"")); assertTrue(json.contains("\"count\":2")); } @Test void testDeserializeWithMultipleServers() throws JsonProcessingException { String json = "{\"servers\":[" + "{\"server\":{\"name\":\"Server1\",\"version\":\"1.0.0\"}}," + "{\"server\":{\"name\":\"Server2\",\"version\":\"2.0.0\"}}" + "],\"metadata\":{\"nextCursor\":\"cursor2\",\"count\":2}}"; McpRegistryServerList list = mapper.readValue(json, McpRegistryServerList.class); assertEquals(2, list.getServers().size()); assertEquals("Server1", list.getServers().get(0).getServer().getName()); assertEquals("Server2", list.getServers().get(1).getServer().getName()); assertEquals("cursor2", list.getMetadata().getNextCursor()); assertEquals(2, list.getMetadata().getCount()); } @Test void testMetadataConstructor() throws JsonProcessingException { McpRegistryServerList.Metadata metadata = new McpRegistryServerList.Metadata("test_cursor", 5); assertEquals("test_cursor", metadata.getNextCursor()); assertEquals(5, metadata.getCount()); String json = mapper.writeValueAsString(metadata); assertTrue(json.contains("\"nextCursor\":\"test_cursor\"")); assertTrue(json.contains("\"count\":5")); } @Test void testEmptyServerList() throws JsonProcessingException { McpRegistryServerList list = new McpRegistryServerList(); list.setServers(Collections.emptyList()); list.setMetadata(new McpRegistryServerList.Metadata(null, 0)); String json = mapper.writeValueAsString(list); assertTrue(json.contains("\"servers\":[]")); assertTrue(json.contains("\"metadata\":{")); } @Test void testNullNextCursorHandling() throws JsonProcessingException { McpRegistryServerList list = new McpRegistryServerList(); list.setServers(Collections.emptyList()); list.setMetadata(new McpRegistryServerList.Metadata(null, 0)); String json = mapper.writeValueAsString(list); McpRegistryServerList parsed = mapper.readValue(json, McpRegistryServerList.class); assertNull(parsed.getMetadata().getNextCursor()); assertEquals(0, parsed.getMetadata().getCount()); } @Test void testBackwardCompatibilitySnakeCaseAlias() throws JsonProcessingException { // Ensure @JsonAlias works for next_cursor -> nextCursor String jsonSnakeCase = "{\"servers\":[],\"metadata\":{\"next_cursor\":\"pagination_cursor\",\"count\":10}}"; McpRegistryServerList list = mapper.readValue(jsonSnakeCase, McpRegistryServerList.class); assertEquals("pagination_cursor", list.getMetadata().getNextCursor()); assertEquals(10, list.getMetadata().getCount()); } @Test void testPrimaryFormatCamelCase() throws JsonProcessingException { // Ensure camelCase is the primary serialization format McpRegistryServerList list = new McpRegistryServerList(); list.setServers(Collections.emptyList()); list.setMetadata(new McpRegistryServerList.Metadata("cursor", 5)); String json = mapper.writeValueAsString(list); // Primary format should be camelCase assertTrue(json.contains("\"nextCursor\":\"cursor\"")); // Should NOT use snake_case in serialization assertTrue(!json.contains("\"next_cursor\"")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/McpServerStatusEnumTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class McpServerStatusEnumTest { @Test void testStatusValues() { assertEquals("active", McpServerStatusEnum.ACTIVE.getName()); assertEquals("deleted", McpServerStatusEnum.DELETED.getName()); assertEquals("deprecated", McpServerStatusEnum.DEPRECATED.getName()); } @Test void testParseStatusActive() { McpServerStatusEnum status = McpServerStatusEnum.parseStatus("active"); assertEquals(McpServerStatusEnum.ACTIVE, status); } @Test void testParseStatusDeleted() { McpServerStatusEnum status = McpServerStatusEnum.parseStatus("deleted"); assertEquals(McpServerStatusEnum.DELETED, status); } @Test void testParseStatusDeprecated() { McpServerStatusEnum status = McpServerStatusEnum.parseStatus("deprecated"); assertEquals(McpServerStatusEnum.DEPRECATED, status); } @Test void testParseStatusInvalid() { McpServerStatusEnum status = McpServerStatusEnum.parseStatus("invalid"); assertNull(status); } @Test void testParseStatusNull() { McpServerStatusEnum status = McpServerStatusEnum.parseStatus(null); assertNull(status); } @Test void testParseStatusCaseSensitive() { // The parsing should be case-sensitive McpServerStatusEnum status = McpServerStatusEnum.parseStatus("ACTIVE"); assertNull(status); } @Test void testAllEnumValues() { McpServerStatusEnum[] values = McpServerStatusEnum.values(); assertEquals(3, values.length); assertEquals(McpServerStatusEnum.ACTIVE, values[0]); assertEquals(McpServerStatusEnum.DELETED, values[1]); assertEquals(McpServerStatusEnum.DEPRECATED, values[2]); } @Test void testEnumOrdinal() { assertEquals(0, McpServerStatusEnum.ACTIVE.ordinal()); assertEquals(1, McpServerStatusEnum.DELETED.ordinal()); assertEquals(2, McpServerStatusEnum.DEPRECATED.ordinal()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/MetaTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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 MetaTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { McpRegistryServerDetail.Meta meta = new McpRegistryServerDetail.Meta(); Map publisherProvided = new HashMap<>(); publisherProvided.put("key1", "value1"); meta.setPublisherMeta(publisherProvided); String json = mapper.writeValueAsString(meta); assertNotNull(json); assertTrue(json.contains("\"io.modelcontextprotocol.registry/publisher-provided\":")); assertTrue(json.contains("\"key1\":\"value1\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"io.modelcontextprotocol.registry/publisher-provided\":{\"key1\":\"value1\"}}"; McpRegistryServerDetail.Meta meta = mapper.readValue(json, McpRegistryServerDetail.Meta.class); assertNotNull(meta); assertEquals("value1", meta.getPublisherMeta().get("key1")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/NacosMcpRegistryServerDetailTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.SerializationFeature; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; 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 NacosMcpRegistryServerDetailTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { // Repository is empty object mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); McpRegistryServerDetail mcpRegistryServerDetail = new McpRegistryServerDetail(); mcpRegistryServerDetail.setName("testRegistryServer"); mcpRegistryServerDetail.setDescription("test mcp registry server object"); mcpRegistryServerDetail.setRepository(new Repository()); mcpRegistryServerDetail.setVersion("1.0.0"); mcpRegistryServerDetail.setSchema("http://example.com/schema"); // Create test packages Package pkg = new Package(); pkg.setIdentifier("test-package"); pkg.setVersion("1.0.0"); mcpRegistryServerDetail.setPackages(Arrays.asList(pkg)); McpRegistryServerDetail.Meta meta = new McpRegistryServerDetail.Meta(); mcpRegistryServerDetail.setMeta(meta); mcpRegistryServerDetail.setRemotes(Collections.singletonList(new Remote())); mcpRegistryServerDetail.getRemotes().get(0).setUrl("127.0.0.1:8848/sse"); mcpRegistryServerDetail.getRemotes().get(0).setType("https"); String json = mapper.writeValueAsString(mcpRegistryServerDetail); assertNotNull(json); assertTrue(json.contains("\"name\":\"testRegistryServer\"")); assertTrue(json.contains("\"description\":\"test mcp registry server object\"")); assertTrue(json.contains("\"repository\":{}")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"$schema\":\"http://example.com/schema\"")); assertTrue(json.contains("\"packages\":[{")); assertTrue(json.contains("\"identifier\":\"test-package\"")); assertTrue(json.contains("\"remotes\":[{")); assertTrue(json.contains("\"url\":\"127.0.0.1:8848/sse\"")); assertTrue(json.contains("\"type\":\"https\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"name\":\"testRegistryServer\",\"description\":\"test mcp registry server object\",\"$schema\":\"http://example.com/schema\",\"packages\":[{\"identifier\":\"test-package\",\"version\":\"1.0.0\"}]," + "\"repository\":{},\"version\":\"1.0.0\",\"remotes\":[{\"type\":\"https\"," + "\"url\":\"127.0.0.1:8848/sse\"}],\"_meta\":{\"io.modelcontextprotocol.registry/official\":" + "{\"publishedAt\":\"2022-01-01T00:00:00Z\"}}}"; McpRegistryServerDetail mcpRegistryServerDetail = mapper.readValue(json, McpRegistryServerDetail.class); assertNotNull(mcpRegistryServerDetail); assertEquals("testRegistryServer", mcpRegistryServerDetail.getName()); assertEquals("test mcp registry server object", mcpRegistryServerDetail.getDescription()); assertNotNull(mcpRegistryServerDetail.getRepository()); assertEquals("1.0.0", mcpRegistryServerDetail.getVersion()); assertEquals("http://example.com/schema", mcpRegistryServerDetail.getSchema()); assertNotNull(mcpRegistryServerDetail.getPackages()); assertEquals(1, mcpRegistryServerDetail.getPackages().size()); assertEquals("test-package", mcpRegistryServerDetail.getPackages().get(0).getIdentifier()); assertEquals("1.0.0", mcpRegistryServerDetail.getPackages().get(0).getVersion()); assertNotNull(mcpRegistryServerDetail.getRemotes()); assertEquals(1, mcpRegistryServerDetail.getRemotes().size()); assertEquals("https", mcpRegistryServerDetail.getRemotes().get(0).getType()); assertEquals("127.0.0.1:8848/sse", mcpRegistryServerDetail.getRemotes().get(0).getUrl()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/NamedArgumentTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class NamedArgumentTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { NamedArgument namedArgument = new NamedArgument(); namedArgument.setType("named"); namedArgument.setName("testArg"); namedArgument.setValue("testValue"); namedArgument.setDescription("test description"); namedArgument.setIsRequired(true); namedArgument.setIsRepeated(false); String json = mapper.writeValueAsString(namedArgument); assertNotNull(json); assertTrue(json.contains("\"type\":\"named\"")); assertTrue(json.contains("\"name\":\"testArg\"")); assertTrue(json.contains("\"value\":\"testValue\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"isRequired\":true")); assertTrue(json.contains("\"isRepeated\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"type\":\"named\",\"name\":\"testArg\",\"value\":\"testValue\"," + "\"description\":\"test description\",\"isRequired\":true,\"isRepeated\":false}"; NamedArgument namedArgument = mapper.readValue(json, NamedArgument.class); assertNotNull(namedArgument); assertEquals("named", namedArgument.getType()); assertEquals("testArg", namedArgument.getName()); assertEquals("testValue", namedArgument.getValue()); assertEquals("test description", namedArgument.getDescription()); assertEquals(true, namedArgument.getIsRequired()); assertEquals(false, namedArgument.getIsRepeated()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/OfficialMetaTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class OfficialMetaTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { OfficialMeta officialMeta = new OfficialMeta(); officialMeta.setPublishedAt("2022-01-01T00:00:00Z"); officialMeta.setUpdatedAt("2022-01-02T00:00:00Z"); officialMeta.setIsLatest(true); String json = mapper.writeValueAsString(officialMeta); assertNotNull(json); assertTrue(json.contains("\"publishedAt\":\"2022-01-01T00:00:00Z\"")); assertTrue(json.contains("\"updatedAt\":\"2022-01-02T00:00:00Z\"")); assertTrue(json.contains("\"isLatest\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"serverId\":\"server1\",\"versionId\":\"version1\"," + "\"publishedAt\":\"2022-01-01T00:00:00Z\",\"updatedAt\":\"2022-01-02T00:00:00Z\"," + "\"isLatest\":true}"; OfficialMeta officialMeta = mapper.readValue(json, OfficialMeta.class); assertNotNull(officialMeta); assertEquals("2022-01-01T00:00:00Z", officialMeta.getPublishedAt()); assertEquals("2022-01-02T00:00:00Z", officialMeta.getUpdatedAt()); assertEquals(true, officialMeta.getIsLatest()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/PackageTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; 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 PackageTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { Package pkg = new Package(); pkg.setRegistryType("maven"); pkg.setRegistryBaseUrl("https://repo.maven.apache.org/maven2/"); pkg.setIdentifier("com.alibaba.nacos:test-package"); pkg.setVersion("1.0.0"); pkg.setFileSha256("abc123"); pkg.setRuntimeHint("java11"); // Create test arguments NamedArgument namedArgument = new NamedArgument(); namedArgument.setName("arg1"); namedArgument.setValue("value1"); PositionalArgument positionalArgument = new PositionalArgument(); positionalArgument.setValueHint("posValue"); pkg.setRuntimeArguments(Collections.singletonList(namedArgument)); pkg.setPackageArguments(Arrays.asList(namedArgument, positionalArgument)); KeyValueInput envVar = new KeyValueInput(); envVar.setName("ENV_VAR"); envVar.setValue("env_value"); pkg.setEnvironmentVariables(Collections.singletonList(envVar)); String json = mapper.writeValueAsString(pkg); assertNotNull(json); assertTrue(json.contains("\"registryType\":\"maven\"")); assertTrue(json.contains("\"registryBaseUrl\":\"https://repo.maven.apache.org/maven2/\"")); assertTrue(json.contains("\"identifier\":\"com.alibaba.nacos:test-package\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"fileSha256\":\"abc123\"")); assertTrue(json.contains("\"runtimeHint\":\"java11\"")); assertTrue(json.contains("\"runtimeArguments\":[")); assertTrue(json.contains("\"packageArguments\":[")); assertTrue(json.contains("\"environmentVariables\":[")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{" + "\"registryType\":\"maven\"," + "\"registryBaseUrl\":\"https://repo.maven.apache.org/maven2/\"," + "\"identifier\":\"com.alibaba.nacos:test-package\"," + "\"version\":\"1.0.0\"," + "\"fileSha256\":\"abc123\"," + "\"runtimeHint\":\"java11\"," + "\"runtimeArguments\":[{\"type\":\"named\",\"name\":\"arg1\",\"value\":\"value1\"}]," + "\"packageArguments\":[" + " {\"type\":\"named\",\"name\":\"arg1\",\"value\":\"value1\"}," + " {\"type\":\"positional\",\"valueHint\":\"posValue\"}" + "]," + "\"environmentVariables\":[{\"name\":\"ENV_VAR\",\"value\":\"env_value\"}]" + "}"; Package pkg = mapper.readValue(json, Package.class); assertNotNull(pkg); assertEquals("maven", pkg.getRegistryType()); assertEquals("https://repo.maven.apache.org/maven2/", pkg.getRegistryBaseUrl()); assertEquals("com.alibaba.nacos:test-package", pkg.getIdentifier()); assertEquals("1.0.0", pkg.getVersion()); assertEquals("abc123", pkg.getFileSha256()); assertEquals("java11", pkg.getRuntimeHint()); assertEquals(1, pkg.getRuntimeArguments().size()); assertEquals("named", ((NamedArgument) pkg.getRuntimeArguments().get(0)).getType()); assertEquals(2, pkg.getPackageArguments().size()); assertEquals("named", ((NamedArgument) pkg.getPackageArguments().get(0)).getType()); assertEquals("positional", ((PositionalArgument) pkg.getPackageArguments().get(1)).getType()); assertEquals(1, pkg.getEnvironmentVariables().size()); assertEquals("ENV_VAR", pkg.getEnvironmentVariables().get(0).getName()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/PackageTransportTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; 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 PackageTransportTest extends BasicRequestTest { @Test void testPackageWithStdioTransport() throws JsonProcessingException { Package pkg = new Package(); pkg.setIdentifier("test-package"); pkg.setVersion("1.0.0"); StdioTransport transport = new StdioTransport(); pkg.setTransport(transport); String json = mapper.writeValueAsString(pkg); assertTrue(json.contains("\"identifier\":\"test-package\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"transport\":{")); assertTrue(json.contains("\"type\":\"stdio\"")); } @Test void testPackageWithStreamableHttpTransport() throws JsonProcessingException { Package pkg = new Package(); pkg.setIdentifier("test-package"); pkg.setVersion("1.0.0"); StreamableHttpTransport transport = new StreamableHttpTransport(); transport.setUrl("http://localhost:8080/api"); KeyValueInput header = new KeyValueInput(); header.setName("Authorization"); header.setValue("Bearer token"); transport.setHeaders(Arrays.asList(header)); pkg.setTransport(transport); String json = mapper.writeValueAsString(pkg); assertTrue(json.contains("\"type\":\"streamable-http\"")); assertTrue(json.contains("\"url\":\"http://localhost:8080/api\"")); } @Test void testPackageWithSseTransport() throws JsonProcessingException { Package pkg = new Package(); pkg.setIdentifier("test-package"); pkg.setVersion("1.0.0"); SseTransport transport = new SseTransport(); transport.setUrl("https://example.com/sse"); pkg.setTransport(transport); String json = mapper.writeValueAsString(pkg); assertTrue(json.contains("\"type\":\"sse\"")); assertTrue(json.contains("\"url\":\"https://example.com/sse\"")); } @Test void testDeserializePackageWithStdioTransport() throws JsonProcessingException { String json = "{\"identifier\":\"test-package\",\"version\":\"1.0.0\"," + "\"transport\":{\"type\":\"stdio\"}}"; Package pkg = mapper.readValue(json, Package.class); assertEquals("test-package", pkg.getIdentifier()); assertEquals("1.0.0", pkg.getVersion()); assertNotNull(pkg.getTransport()); assertTrue(pkg.getTransport() instanceof StdioTransport); } @Test void testDeserializePackageWithStreamableHttpTransport() throws JsonProcessingException { String json = "{\"identifier\":\"test-package\",\"version\":\"1.0.0\"," + "\"transport\":{\"type\":\"streamable-http\",\"url\":\"http://localhost:8080/api\"," + "\"headers\":[{\"name\":\"Authorization\",\"value\":\"Bearer token\"}]}}"; Package pkg = mapper.readValue(json, Package.class); assertEquals("test-package", pkg.getIdentifier()); assertNotNull(pkg.getTransport()); assertTrue(pkg.getTransport() instanceof StreamableHttpTransport); } @Test void testDeserializePackageWithSseTransport() throws JsonProcessingException { String json = "{\"identifier\":\"test-package\",\"version\":\"1.0.0\"," + "\"transport\":{\"type\":\"sse\",\"url\":\"https://example.com/sse\"}}"; Package pkg = mapper.readValue(json, Package.class); assertEquals("test-package", pkg.getIdentifier()); assertNotNull(pkg.getTransport()); assertTrue(pkg.getTransport() instanceof SseTransport); } @Test void testPackageWithCompleteFields() throws JsonProcessingException { Package pkg = new Package(); pkg.setRegistryType("npm"); pkg.setRegistryBaseUrl("https://registry.npmjs.org"); pkg.setIdentifier("test-package"); pkg.setVersion("1.0.0"); pkg.setFileSha256("abc123def456"); pkg.setRuntimeHint("node runtime"); StdioTransport transport = new StdioTransport(); pkg.setTransport(transport); String json = mapper.writeValueAsString(pkg); assertNotNull(json); assertTrue(json.contains("\"registryType\":\"npm\"")); assertTrue(json.contains("\"identifier\":\"test-package\"")); assertTrue(json.contains("\"transport\":{")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/PositionalArgumentTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class PositionalArgumentTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { PositionalArgument positionalArgument = new PositionalArgument(); positionalArgument.setType("positional"); positionalArgument.setValueHint("testHint"); positionalArgument.setValue("testValue"); positionalArgument.setDescription("test description"); positionalArgument.setIsRequired(true); positionalArgument.setIsRepeated(false); String json = mapper.writeValueAsString(positionalArgument); assertNotNull(json); assertTrue(json.contains("\"type\":\"positional\"")); assertTrue(json.contains("\"valueHint\":\"testHint\"")); assertTrue(json.contains("\"value\":\"testValue\"")); assertTrue(json.contains("\"description\":\"test description\"")); assertTrue(json.contains("\"isRequired\":true")); assertTrue(json.contains("\"isRepeated\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"type\":\"positional\",\"valueHint\":\"testHint\",\"value\":\"testValue\"," + "\"description\":\"test description\",\"isRequired\":true,\"isRepeated\":false}"; PositionalArgument positionalArgument = mapper.readValue(json, PositionalArgument.class); assertNotNull(positionalArgument); assertEquals("positional", positionalArgument.getType()); assertEquals("testHint", positionalArgument.getValueHint()); assertEquals("testValue", positionalArgument.getValue()); assertEquals("test description", positionalArgument.getDescription()); assertEquals(true, positionalArgument.getIsRequired()); assertEquals(false, positionalArgument.getIsRepeated()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/RemoteTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Collections; 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 RemoteTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { Remote remote = new Remote(); remote.setType("https"); remote.setUrl("https://test.server.com/api"); KeyValueInput header = new KeyValueInput(); header.setName("Authorization"); header.setValue("Bearer token"); remote.setHeaders(Collections.singletonList(header)); String json = mapper.writeValueAsString(remote); assertNotNull(json); assertTrue(json.contains("\"type\":\"https\"")); assertTrue(json.contains("\"url\":\"https://test.server.com/api\"")); assertTrue(json.contains("\"headers\":[")); assertTrue(json.contains("\"name\":\"Authorization\"")); assertTrue(json.contains("\"value\":\"Bearer token\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"type\":\"https\",\"url\":\"https://test.server.com/api\"," + "\"headers\":[{\"name\":\"Authorization\",\"value\":\"Bearer token\"}]}"; Remote remote = mapper.readValue(json, Remote.class); assertNotNull(remote); assertEquals("https", remote.getType()); assertEquals("https://test.server.com/api", remote.getUrl()); assertEquals(1, remote.getHeaders().size()); assertEquals("Authorization", remote.getHeaders().get(0).getName()); assertEquals("Bearer token", remote.getHeaders().get(0).getValue()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/RepositoryTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class RepositoryTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { Repository repository = new Repository(); repository.setUrl("https://github.com/test/repo"); repository.setSource("github"); repository.setId("test-repo-id"); repository.setSubfolder("sub/folder"); String json = mapper.writeValueAsString(repository); assertNotNull(json); assertTrue(json.contains("\"url\":\"https://github.com/test/repo\"")); assertTrue(json.contains("\"source\":\"github\"")); assertTrue(json.contains("\"id\":\"test-repo-id\"")); assertTrue(json.contains("\"subfolder\":\"sub/folder\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"url\":\"https://github.com/test/repo\",\"source\":\"github\"," + "\"id\":\"test-repo-id\",\"subfolder\":\"sub/folder\"}"; Repository repository = mapper.readValue(json, Repository.class); assertNotNull(repository); assertEquals("https://github.com/test/repo", repository.getUrl()); assertEquals("github", repository.getSource()); assertEquals("test-repo-id", repository.getId()); assertEquals("sub/folder", repository.getSubfolder()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/ServerResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; 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 ServerResponseTest extends BasicRequestTest { @Test void testSerializeServerResponseBasic() throws JsonProcessingException { ServerResponse response = new ServerResponse(); McpRegistryServerDetail server = new McpRegistryServerDetail(); server.setName("TestServer"); server.setDescription("Test Server Description"); server.setVersion("1.0.0"); response.setServer(server); OfficialMeta official = new OfficialMeta(); official.setPublishedAt("2025-01-01T00:00:00Z"); official.setUpdatedAt("2025-01-15T00:00:00Z"); official.setIsLatest(true); official.setStatus("active"); ServerResponse.Meta meta = new ServerResponse.Meta(); meta.setOfficial(official); response.setMeta(meta); String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"server\":{")); assertTrue(json.contains("\"name\":\"TestServer\"")); assertTrue(json.contains("\"_meta\":{")); assertTrue(json.contains("\"io.modelcontextprotocol.registry/official\":{")); assertTrue(json.contains("\"publishedAt\":\"2025-01-01T00:00:00Z\"")); assertTrue(json.contains("\"status\":\"active\"")); } @Test void testDeserializeServerResponseBasic() throws JsonProcessingException { String json = "{\"server\":{\"name\":\"TestServer\",\"version\":\"1.0.0\"}," + "\"_meta\":{\"io.modelcontextprotocol.registry/official\":" + "{\"publishedAt\":\"2025-01-01T00:00:00Z\",\"isLatest\":true}}}"; ServerResponse response = mapper.readValue(json, ServerResponse.class); assertNotNull(response); assertNotNull(response.getServer()); assertEquals("TestServer", response.getServer().getName()); assertEquals("1.0.0", response.getServer().getVersion()); assertNotNull(response.getMeta()); assertNotNull(response.getMeta().getOfficial()); assertEquals("2025-01-01T00:00:00Z", response.getMeta().getOfficial().getPublishedAt()); assertEquals(true, response.getMeta().getOfficial().getIsLatest()); } @Test void testServerResponseWithMetadataExtensions() throws JsonProcessingException { Map extensionData = new HashMap<>(); extensionData.put("customField", "customValue"); extensionData.put("metadata", new HashMap() { { put("key1", "value1"); } }); String json = "{\"server\":{\"name\":\"ExtendedServer\"}," + "\"_meta\":{" + "\"io.modelcontextprotocol.registry/official\":{\"publishedAt\":\"2025-01-01T00:00:00Z\"}," + "\"customExtension\":\"extensionValue\"}}"; ServerResponse response = mapper.readValue(json, ServerResponse.class); assertNotNull(response); assertNotNull(response.getServer()); assertEquals("ExtendedServer", response.getServer().getName()); assertNotNull(response.getMeta()); } @Test void testServerResponseMinimal() throws JsonProcessingException { ServerResponse response = new ServerResponse(); McpRegistryServerDetail server = new McpRegistryServerDetail(); server.setName("MinimalServer"); response.setServer(server); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"server\":{")); assertTrue(json.contains("\"name\":\"MinimalServer\"")); } @Test void testServerResponseMetaNullSafe() throws JsonProcessingException { ServerResponse response = new ServerResponse(); McpRegistryServerDetail server = new McpRegistryServerDetail(); server.setName("TestServer"); response.setServer(server); // Meta is not set - should handle null gracefully String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"server\":{")); } @Test void testServerResponseMetaOfficialNested() throws JsonProcessingException { String json = "{" + "\"server\":{\"name\":\"NestedServer\"}," + "\"_meta\":{" + "\"io.modelcontextprotocol.registry/official\":{" + "\"publishedAt\":\"2025-01-01T00:00:00Z\"," + "\"updatedAt\":\"2025-01-15T00:00:00Z\"," + "\"isLatest\":true," + "\"status\":\"active\"}}}"; ServerResponse response = mapper.readValue(json, ServerResponse.class); assertEquals("NestedServer", response.getServer().getName()); assertEquals("2025-01-01T00:00:00Z", response.getMeta().getOfficial().getPublishedAt()); assertEquals("2025-01-15T00:00:00Z", response.getMeta().getOfficial().getUpdatedAt()); assertEquals(true, response.getMeta().getOfficial().getIsLatest()); assertEquals("active", response.getMeta().getOfficial().getStatus()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/ServerVersionDetailTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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.assertTrue; class ServerVersionDetailTest extends BasicRequestTest { @Test void testSerialize() throws JsonProcessingException { ServerVersionDetail serverVersionDetail = new ServerVersionDetail(); serverVersionDetail.setVersion("1.0.0"); serverVersionDetail.setRelease_date("2022-01-01T00:00:00Z"); serverVersionDetail.setIs_latest(true); String json = mapper.writeValueAsString(serverVersionDetail); assertNotNull(json); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"release_date\":\"2022-01-01T00:00:00Z\"")); assertTrue(json.contains("\"is_latest\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"version\":\"1.0.0\",\"release_date\":\"2022-01-01T00:00:00Z\",\"is_latest\":true}"; ServerVersionDetail serverVersionDetail = mapper.readValue(json, ServerVersionDetail.class); assertNotNull(serverVersionDetail); assertEquals("1.0.0", serverVersionDetail.getVersion()); assertEquals("2022-01-01T00:00:00Z", serverVersionDetail.getRelease_date()); assertEquals(true, serverVersionDetail.getIs_latest()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/mcp/registry/TransportTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.mcp.registry; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Arrays; 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 TransportTest extends BasicRequestTest { @Test void testStdioTransportSerialize() throws JsonProcessingException { StdioTransport transport = new StdioTransport(); String json = mapper.writeValueAsString(transport); assertNotNull(json); assertTrue(json.contains("\"type\":\"stdio\"")); } @Test void testStdioTransportDeserialize() throws JsonProcessingException { String json = "{\"type\":\"stdio\"}"; StdioTransport transport = mapper.readValue(json, StdioTransport.class); assertNotNull(transport); assertEquals("stdio", transport.getType()); } @Test void testStreamableHttpTransportSerialize() throws JsonProcessingException { StreamableHttpTransport transport = new StreamableHttpTransport(); transport.setUrl("http://localhost:8080/api"); KeyValueInput header1 = new KeyValueInput(); header1.setName("Authorization"); header1.setValue("Bearer token123"); KeyValueInput header2 = new KeyValueInput(); header2.setName("Content-Type"); header2.setValue("application/json"); transport.setHeaders(Arrays.asList(header1, header2)); String json = mapper.writeValueAsString(transport); assertNotNull(json); assertTrue(json.contains("\"type\":\"streamable-http\"")); assertTrue(json.contains("\"url\":\"http://localhost:8080/api\"")); assertTrue(json.contains("\"headers\":[")); assertTrue(json.contains("\"Authorization\"")); } @Test void testStreamableHttpTransportDeserialize() throws JsonProcessingException { String json = "{\"type\":\"streamable-http\",\"url\":\"http://localhost:8080/api\"," + "\"headers\":[{\"name\":\"Authorization\",\"value\":\"Bearer token123\"}]}"; StreamableHttpTransport transport = mapper.readValue(json, StreamableHttpTransport.class); assertNotNull(transport); assertEquals("streamable-http", transport.getType()); assertEquals("http://localhost:8080/api", transport.getUrl()); assertNotNull(transport.getHeaders()); assertEquals(1, transport.getHeaders().size()); assertEquals("Authorization", transport.getHeaders().get(0).getName()); } @Test void testSseTransportSerialize() throws JsonProcessingException { SseTransport transport = new SseTransport(); transport.setUrl("https://example.com/sse"); KeyValueInput header = new KeyValueInput(); header.setName("Accept"); header.setValue("text/event-stream"); transport.setHeaders(Arrays.asList(header)); String json = mapper.writeValueAsString(transport); assertNotNull(json); assertTrue(json.contains("\"type\":\"sse\"")); assertTrue(json.contains("\"url\":\"https://example.com/sse\"")); } @Test void testSseTransportDeserialize() throws JsonProcessingException { String json = "{\"type\":\"sse\",\"url\":\"https://example.com/sse\"}"; SseTransport transport = mapper.readValue(json, SseTransport.class); assertNotNull(transport); assertEquals("sse", transport.getType()); assertEquals("https://example.com/sse", transport.getUrl()); } @Test void testTransportTypeDetection() throws JsonProcessingException { // Test polymorphic deserialization with @JsonTypeInfo String stdioJson = "{\"type\":\"stdio\"}"; String httpJson = "{\"type\":\"streamable-http\",\"url\":\"http://localhost:8080\"}"; String sseJson = "{\"type\":\"sse\",\"url\":\"https://example.com\"}"; StdioTransport stdio = mapper.readValue(stdioJson, StdioTransport.class); StreamableHttpTransport http = mapper.readValue(httpJson, StreamableHttpTransport.class); SseTransport sse = mapper.readValue(sseJson, SseTransport.class); assertEquals("stdio", stdio.getType()); assertEquals("streamable-http", http.getType()); assertEquals("sse", sse.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/model/skills/SkillUtilsTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.model.skills; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashMap; import java.util.Map; 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.junit.jupiter.api.Assertions.assertTrue; /** * Test for SkillUtils. * * @author nacos */ class SkillUtilsTest { @TempDir Path tempDir; @Test void testToMarkdownWithValidSkill() { // Given Skill skill = createValidSkill(); // When String markdown = SkillUtils.toMarkdown(skill); // Then assertNotNull(markdown); assertTrue(markdown.contains("name: test-skill")); assertTrue(markdown.contains("description: Test description")); assertTrue(markdown.contains("Test instruction")); } @Test void testToMarkdownWithNullSkill() { // When String markdown = SkillUtils.toMarkdown(null); // Then assertEquals("", markdown); } @Test void testToMarkdownWithSpecialCharacters() { // Given Skill skill = createValidSkill(); skill.setDescription("Description with: colon and \"quotes\""); // When String markdown = SkillUtils.toMarkdown(skill); // Then assertNotNull(markdown); assertTrue(markdown.contains("\"Description with: colon")); } @Test void testSyncToLocalWithOverwriteStrategy() throws IOException { // Given Skill skill = createValidSkill(); Path baseDir = tempDir.resolve("skills"); Files.createDirectories(baseDir); // When SkillUtils.syncToLocal(skill, baseDir.toString()); // Then Path skillDir = baseDir.resolve(skill.getName()); assertTrue(Files.exists(skillDir)); assertTrue(Files.exists(skillDir.resolve("SKILL.md"))); } @Test void testSyncToLocalWithBackupStrategy() throws IOException { // Given Skill skill = createValidSkill(); Path baseDir = tempDir.resolve("skills"); Files.createDirectories(baseDir); Path skillDir = baseDir.resolve(skill.getName()); Files.createDirectories(skillDir); // When SkillUtils.syncToLocal(skill, baseDir.toString(), SkillUtils.ExistingDirectoryStrategy.BACKUP); // Then assertTrue(Files.exists(skillDir)); assertTrue(Files.exists(skillDir.resolve("SKILL.md"))); } @Test void testSyncToLocalWithFailStrategy() throws IOException { // Given Skill skill = createValidSkill(); Path baseDir = tempDir.resolve("skills"); Files.createDirectories(baseDir); Path skillDir = baseDir.resolve(skill.getName()); Files.createDirectories(skillDir); // When & Then assertThrows(java.nio.file.FileAlreadyExistsException.class, () -> SkillUtils.syncToLocal(skill, baseDir.toString(), SkillUtils.ExistingDirectoryStrategy.FAIL)); } @Test void testSyncToLocalWithResources() throws IOException { // Given Skill skill = createValidSkillWithResources(); Path baseDir = tempDir.resolve("skills"); Files.createDirectories(baseDir); // When SkillUtils.syncToLocal(skill, baseDir.toString()); // Then Path skillDir = baseDir.resolve(skill.getName()); assertTrue(Files.exists(skillDir)); assertTrue(Files.exists(skillDir.resolve("SKILL.md"))); assertTrue(Files.exists(skillDir.resolve("script").resolve("test.sh"))); } @Test void testSyncToLocalWithCustomDirName() throws IOException { // Given Skill skill = createValidSkill(); Path baseDir = tempDir.resolve("skills"); Files.createDirectories(baseDir); // When SkillUtils.syncToLocal(skill, baseDir.toString(), "custom-dir"); // Then Path skillDir = baseDir.resolve("custom-dir"); assertTrue(Files.exists(skillDir)); assertTrue(Files.exists(skillDir.resolve("SKILL.md"))); } @Test void testSyncToLocalWithNullSkill() { // When & Then assertThrows(IllegalArgumentException.class, () -> SkillUtils.syncToLocal(null, tempDir.toString())); } @Test void testSyncToLocalWithBlankSkillName() { // Given Skill skill = new Skill(); skill.setName(""); // When & Then assertThrows(IllegalArgumentException.class, () -> SkillUtils.syncToLocal(skill, tempDir.toString())); } @Test void testSyncToLocalWithBlankBaseDir() { // Given Skill skill = createValidSkill(); // When & Then assertThrows(IllegalArgumentException.class, () -> SkillUtils.syncToLocal(skill, "")); } /** * Create a valid skill for testing. */ private Skill createValidSkill() { Skill skill = new Skill(); skill.setName("test-skill"); skill.setDescription("Test description"); skill.setInstruction("Test instruction"); return skill; } /** * Create a skill with resources for testing. */ private Skill createValidSkillWithResources() { Skill skill = createValidSkill(); Map resources = new HashMap<>(); SkillResource resource = new SkillResource(); resource.setName("test.sh"); resource.setType("script"); resource.setContent("#!/bin/bash\necho 'test'"); resources.put("test", resource); skill.setResource(resources); return skill; } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/AbstractAgentRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 AbstractAgentRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { TestAbstractAgentRequest request = new TestAbstractAgentRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setAgentName("testAgent"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"agentName\":\"testAgent\"")); assertTrue(json.contains("\"module\":\"ai\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"agentName\":\"testAgent\",\"module\":\"ai\"}"; TestAbstractAgentRequest result = mapper.readValue(json, TestAbstractAgentRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testAgent", result.getAgentName()); assertEquals(Constants.AI.AI_MODULE, result.getModule()); } /** * Test implementation of AbstractAgentRequest. */ public static class TestAbstractAgentRequest extends AbstractAgentRequest { } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/AgentEndpointRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 AgentEndpointRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { AgentEndpointRequest request = new AgentEndpointRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setAgentName("testAgent"); AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("127.0.0.1"); endpoint.setPort(8848); endpoint.setVersion("1.0.0"); request.setEndpoint(endpoint); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"agentName\":\"testAgent\"")); assertTrue(json.contains("\"type\":\"registerEndpoint\"")); assertTrue(json.contains("\"address\":\"127.0.0.1\"")); assertTrue(json.contains("\"port\":8848")); assertTrue(json.contains("\"version\":\"1.0.0\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"agentName\":\"testAgent\"," + "\"endpoint\":{\"transport\":\"JSONRPC\",\"address\":\"127.0.0.1\",\"port\":8848,\"path\":\"\"," + "\"supportTls\":false,\"version\":\"1.0.0\"},\"type\":\"registerEndpoint\",\"module\":\"ai\"}"; AgentEndpointRequest result = mapper.readValue(json, AgentEndpointRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testAgent", result.getAgentName()); assertEquals(AiRemoteConstants.REGISTER_ENDPOINT, result.getType()); AgentEndpoint endpoint = result.getEndpoint(); assertNotNull(endpoint); assertEquals("127.0.0.1", endpoint.getAddress()); assertEquals(8848, endpoint.getPort()); assertEquals("1.0.0", endpoint.getVersion()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/BatchAgentEndpointRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collection; import java.util.UUID; 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 BatchAgentEndpointRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setAgentName("testAgent"); Collection endpoints = Arrays.asList(createTestEndpoint1(), createTestEndpoint2()); request.setEndpoints(endpoints); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"agentName\":\"testAgent\"")); assertTrue(json.contains("\"type\":\"batchRegisterEndpoint\"")); assertTrue(json.contains("\"address\":\"127.0.0.1\"")); assertTrue(json.contains("\"port\":8848")); assertTrue(json.contains("\"address\":\"192.168.1.100\"")); assertTrue(json.contains("\"port\":9090")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"agentName\":\"testAgent\"," + "\"endpoints\":[{\"transport\":\"JSONRPC\",\"address\":\"127.0.0.1\",\"port\":8848,\"path\":\"\"," + "\"supportTls\":false,\"version\":\"1.0.0\"},{\"transport\":\"GRPC\",\"address\":\"192.168.1.100\"," + "\"port\":9090,\"path\":\"\",\"supportTls\":true,\"version\":\"2.0.0\"}]," + "\"type\":\"batchRegisterEndpoint\",\"module\":\"ai\"}"; BatchAgentEndpointRequest result = mapper.readValue(json, BatchAgentEndpointRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testAgent", result.getAgentName()); assertEquals(AiRemoteConstants.BATCH_REGISTER_ENDPOINT, result.getType()); Collection endpoints = result.getEndpoints(); assertNotNull(endpoints); assertEquals(2, endpoints.size()); Object[] endpointArray = endpoints.toArray(); AgentEndpoint endpoint1 = (AgentEndpoint) endpointArray[0]; assertEquals("127.0.0.1", endpoint1.getAddress()); assertEquals(8848, endpoint1.getPort()); assertEquals("1.0.0", endpoint1.getVersion()); AgentEndpoint endpoint2 = (AgentEndpoint) endpointArray[1]; assertEquals("192.168.1.100", endpoint2.getAddress()); assertEquals(9090, endpoint2.getPort()); assertEquals("2.0.0", endpoint2.getVersion()); } @Test void testGetType() { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); assertEquals(AiRemoteConstants.BATCH_REGISTER_ENDPOINT, request.getType()); } private AgentEndpoint createTestEndpoint1() { AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setTransport("JSONRPC"); endpoint.setAddress("127.0.0.1"); endpoint.setPort(8848); endpoint.setVersion("1.0.0"); return endpoint; } private AgentEndpoint createTestEndpoint2() { AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setTransport("GRPC"); endpoint.setAddress("192.168.1.100"); endpoint.setPort(9090); endpoint.setSupportTls(true); endpoint.setVersion("2.0.0"); return endpoint; } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/McpServerEndpointRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 McpServerEndpointRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { McpServerEndpointRequest request = new McpServerEndpointRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setMcpName("testMcpName"); request.setMcpId(id); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); request.setAddress("1.1.1.1"); request.setPort(3306); request.setVersion("1.0.0"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"mcpName\":\"testMcpName\"")); assertTrue(json.contains(String.format("\"mcpId\":\"%s\"", id))); assertTrue(json.contains(String.format("\"type\":\"%s\"", AiRemoteConstants.REGISTER_ENDPOINT))); assertTrue(json.contains("\"address\":\"1.1.1.1\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"port\":3306")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"mcpId\":\"2aaebf2d-4b7b-4ab9-9ad2-1e60355ae041\"," + "\"mcpName\":\"testMcpName\",\"address\":\"1.1.1.1\",\"port\":3306,\"version\":\"1.0.0\"," + "\"type\":\"registerEndpoint\",\"module\":\"ai\"}"; McpServerEndpointRequest result = mapper.readValue(json, McpServerEndpointRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testMcpName", result.getMcpName()); assertEquals("2aaebf2d-4b7b-4ab9-9ad2-1e60355ae041", result.getMcpId()); assertEquals("1.1.1.1", result.getAddress()); assertEquals(3306, result.getPort()); assertEquals("1.0.0", result.getVersion()); assertEquals(AiRemoteConstants.REGISTER_ENDPOINT, result.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/QueryAgentCardRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 QueryAgentCardRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { QueryAgentCardRequest request = new QueryAgentCardRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setAgentName("testAgent"); request.setVersion("1.0.0"); request.setRegistrationType(AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"agentName\":\"testAgent\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"registrationType\":\"SERVICE\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"agentName\":\"testAgent\"," + "\"version\":\"1.0.0\",\"registrationType\":\"SERVICE\",\"module\":\"ai\"}"; QueryAgentCardRequest result = mapper.readValue(json, QueryAgentCardRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testAgent", result.getAgentName()); assertEquals("1.0.0", result.getVersion()); assertEquals(AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE, result.getRegistrationType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/QueryMcpServerRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 QueryMcpServerRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { QueryMcpServerRequest request = new QueryMcpServerRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setMcpName("testMcpName"); request.setMcpId(id); request.setVersion("1.0.0"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"mcpName\":\"testMcpName\"")); assertTrue(json.contains(String.format("\"mcpId\":\"%s\"", id))); assertTrue(json.contains("\"version\":\"1.0.0\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"mcpId\":\"2aaebf2d-4b7b-4ab9-9ad2-1e60355ae041\"," + "\"mcpName\":\"testMcpName\",\"version\":\"1.0.0\",\"module\":\"ai\"}"; QueryMcpServerRequest result = mapper.readValue(json, QueryMcpServerRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testMcpName", result.getMcpName()); assertEquals("2aaebf2d-4b7b-4ab9-9ad2-1e60355ae041", result.getMcpId()); assertEquals("1.0.0", result.getVersion()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/ReleaseAgentCardRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 ReleaseAgentCardRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { ReleaseAgentCardRequest request = new ReleaseAgentCardRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setAgentName("testAgent"); request.setRegistrationType(AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE); request.setSetAsLatest(true); AgentCard agentCard = new AgentCard(); agentCard.setName("testAgentCard"); agentCard.setVersion("1.0.0"); request.setAgentCard(agentCard); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"agentName\":\"testAgent\"")); assertTrue(json.contains("\"registrationType\":\"SERVICE\"")); assertTrue(json.contains("\"setAsLatest\":true")); assertTrue(json.contains("\"name\":\"testAgentCard\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"agentName\":\"testAgent\"," + "\"agentCard\":{\"protocolVersion\":null,\"name\":\"testAgentCard\",\"description\":null,\"version\":\"1.0.0\"," + "\"iconUrl\":null,\"capabilities\":null,\"skills\":null,\"url\":null,\"preferredTransport\":null," + "\"additionalInterfaces\":null,\"provider\":null,\"documentationUrl\":null,\"securitySchemes\":null," + "\"security\":null,\"defaultInputModes\":null,\"defaultOutputModes\":null," + "\"supportsAuthenticatedExtendedCard\":null}," + "\"registrationType\":\"SERVICE\",\"setAsLatest\":true,\"module\":\"ai\"}"; ReleaseAgentCardRequest result = mapper.readValue(json, ReleaseAgentCardRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testAgent", result.getAgentName()); assertEquals(AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE, result.getRegistrationType()); assertEquals(true, result.isSetAsLatest()); AgentCard agentCard = result.getAgentCard(); assertNotNull(agentCard); assertEquals("testAgentCard", agentCard.getName()); assertEquals("1.0.0", agentCard.getVersion()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/request/ReleaseMcpServerRequestTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.request; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.UUID; 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 ReleaseMcpServerRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); String id = UUID.randomUUID().toString(); request.setRequestId("1"); request.setNamespaceId(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE); request.setMcpName("testMcpName"); request.setMcpId(id); McpServerBasicInfo serverSpecification = new McpServerBasicInfo(); serverSpecification.setName("testServerName"); serverSpecification.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); serverSpecification.setFrontProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); request.setServerSpecification(serverSpecification); McpToolSpecification toolSpecification = new McpToolSpecification(); request.setToolSpecification(toolSpecification); request.setEndpointSpecification(new McpEndpointSpec()); request.getEndpointSpecification().setType(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT); request.getEndpointSpecification().setData(new HashMap<>()); request.getEndpointSpecification().getData().put("address", "127.0.0.1"); request.getEndpointSpecification().getData().put("port", "8848"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"public\"")); assertTrue(json.contains("\"mcpName\":\"testMcpName\"")); assertTrue(json.contains("\"serverSpecification\":{")); assertTrue(json.contains("\"toolSpecification\":{")); assertTrue(json.contains("\"tools\":[]")); assertTrue(json.contains("\"toolsMeta\":{}")); assertTrue(json.contains("\"endpointSpecification\":{")); assertTrue(json.contains("\"type\":\"DIRECT\"")); assertTrue(json.contains(String.format("\"mcpId\":\"%s\"", id))); assertTrue(json.contains(String.format("\"protocol\":\"%s\"", AiConstants.Mcp.MCP_PROTOCOL_STDIO))); assertTrue(json.contains(String.format("\"frontProtocol\":\"%s\"", AiConstants.Mcp.MCP_PROTOCOL_STDIO))); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"namespaceId\":\"public\",\"mcpId\":\"bbd8036e-4f17-4ed8-befc-a08b6fd5978d\"," + "\"mcpName\":\"testMcpName\",\"serverSpecification\":{\"name\":\"testServerName\",\"protocol\":\"stdio\"," + "\"frontProtocol\":\"stdio\",\"enabled\":true},\"toolSpecification\":{\"tools\":[],\"toolsMeta\":{}," + "\"securitySchemes\":[]},\"endpointSpecification\":{\"type\":\"DIRECT\",\"data\":{\"address\":\"127.0.0.1\"," + "\"port\":\"8848\"}},\"module\":\"ai\"}"; ReleaseMcpServerRequest result = mapper.readValue(json, ReleaseMcpServerRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, result.getNamespaceId()); assertEquals("testMcpName", result.getMcpName()); assertEquals("bbd8036e-4f17-4ed8-befc-a08b6fd5978d", result.getMcpId()); McpServerBasicInfo serverSpecification = result.getServerSpecification(); assertEquals("testServerName", serverSpecification.getName()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, serverSpecification.getProtocol()); assertEquals(AiConstants.Mcp.MCP_PROTOCOL_STDIO, serverSpecification.getFrontProtocol()); McpToolSpecification toolSpec = result.getToolSpecification(); assertNotNull(toolSpec); McpEndpointSpec endpointSpec = result.getEndpointSpecification(); assertNotNull(endpointSpec); assertEquals(AiConstants.Mcp.MCP_ENDPOINT_TYPE_DIRECT, endpointSpec.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/response/AgentEndpointResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; 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.assertTrue; class AgentEndpointResponseTest extends BasicRequestTest { @Test void testSerialize() throws Exception { AgentEndpointResponse response = new AgentEndpointResponse(); response.setRequestId("1"); response.setType(AiRemoteConstants.REGISTER_ENDPOINT); String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains(String.format("\"type\":\"%s\"", AiRemoteConstants.REGISTER_ENDPOINT))); } @Test void testDeserialize() throws Exception { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"type\":\"registerEndpoint\",\"success\":true}"; AgentEndpointResponse result = mapper.readValue(json, AgentEndpointResponse.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiRemoteConstants.REGISTER_ENDPOINT, result.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/response/McpServerEndpointResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.remote.request.BasicRequestTest; 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.assertTrue; class McpServerEndpointResponseTest extends BasicRequestTest { @Test void testSerialize() throws Exception { McpServerEndpointResponse response = new McpServerEndpointResponse(); response.setRequestId("1"); response.setType(AiRemoteConstants.REGISTER_ENDPOINT); String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains(String.format("\"type\":\"%s\"", AiRemoteConstants.REGISTER_ENDPOINT))); } @Test void testDeserialize() throws Exception { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"type\":\"registerEndpoint\",\"success\":true}"; McpServerEndpointResponse result = mapper.readValue(json, McpServerEndpointResponse.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals(AiRemoteConstants.REGISTER_ENDPOINT, result.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/response/QueryAgentCardResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.remote.request.BasicRequestTest; 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.assertTrue; class QueryAgentCardResponseTest extends BasicRequestTest { @Test void testSerialize() throws Exception { final QueryAgentCardResponse response = new QueryAgentCardResponse(); AgentCardDetailInfo agentCardDetailInfo = new AgentCardDetailInfo(); agentCardDetailInfo.setName("testAgent"); agentCardDetailInfo.setVersion("1.0.0"); agentCardDetailInfo.setDescription("Test Agent Description"); response.setAgentCardDetailInfo(agentCardDetailInfo); response.setRequestId("1"); String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"agentCardDetailInfo\":{")); assertTrue(json.contains("\"name\":\"testAgent\"")); assertTrue(json.contains("\"version\":\"1.0.0\"")); assertTrue(json.contains("\"description\":\"Test Agent Description\"")); } @Test void testDeserialize() throws Exception { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"agentCardDetailInfo\":" + "{\"name\":\"testAgent\",\"version\":\"1.0.0\",\"description\":\"Test Agent Description\"},\"success\":true}"; QueryAgentCardResponse result = mapper.readValue(json, QueryAgentCardResponse.class); assertNotNull(result); assertEquals("1", result.getRequestId()); AgentCardDetailInfo agentCardDetailInfo = result.getAgentCardDetailInfo(); assertNotNull(agentCardDetailInfo); assertEquals("testAgent", agentCardDetailInfo.getName()); assertEquals("1.0.0", agentCardDetailInfo.getVersion()); assertEquals("Test Agent Description", agentCardDetailInfo.getDescription()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/response/QueryMcpServerResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 QueryMcpServerResponseTest extends BasicRequestTest { @Test void testSerialize() throws Exception { QueryMcpServerResponse response = new QueryMcpServerResponse(); String id = UUID.randomUUID().toString(); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setId(id); mcpServerDetailInfo.setName("testMcpName"); response.setMcpServerDetailInfo(mcpServerDetailInfo); response.setRequestId("1"); String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"mcpServerDetailInfo\":{")); assertTrue(json.contains(String.format("\"id\":\"%s\"", id))); assertTrue(json.contains("\"name\":\"testMcpName\"")); assertTrue(json.contains("\"enabled\":true")); } @Test void testDeserialize() throws Exception { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"mcpServerDetailInfo\":" + "{\"id\":\"27dde181-cc8f-442f-a63d-2d2dc64735d8\",\"name\":\"testMcpName\",\"enabled\":true},\"success\":true}"; QueryMcpServerResponse result = mapper.readValue(json, QueryMcpServerResponse.class); assertNotNull(result); assertEquals("1", result.getRequestId()); McpServerDetailInfo mcpServerDetailInfo = result.getMcpServerDetailInfo(); assertNotNull(mcpServerDetailInfo); assertEquals("27dde181-cc8f-442f-a63d-2d2dc64735d8", mcpServerDetailInfo.getId()); assertEquals("testMcpName", mcpServerDetailInfo.getName()); assertTrue(mcpServerDetailInfo.isEnabled()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/response/ReleaseAgentCardResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; class ReleaseAgentCardResponseTest extends BasicRequestTest { @Test void testSerialize() throws Exception { ReleaseAgentCardResponse response = new ReleaseAgentCardResponse(); response.setRequestId("1"); String json = mapper.writeValueAsString(response); assertNotNull(json); // ReleaseAgentCardResponse has no additional fields, just test basic serialization } @Test void testDeserialize() throws Exception { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"success\":true}"; ReleaseAgentCardResponse result = mapper.readValue(json, ReleaseAgentCardResponse.class); assertNotNull(result); // ReleaseAgentCardResponse has no additional fields, just test basic deserialization } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/ai/remote/response/ReleaseMcpServerResponseTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.ai.remote.response; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import org.junit.jupiter.api.Test; import java.util.UUID; 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 ReleaseMcpServerResponseTest extends BasicRequestTest { @Test void testSerialize() throws Exception { ReleaseMcpServerResponse response = new ReleaseMcpServerResponse(); String id = UUID.randomUUID().toString(); response.setRequestId("1"); response.setMcpId(id); String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains(String.format("\"mcpId\":\"%s\"", id))); } @Test void testDeserialize() throws Exception { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"mcpId\":\"12f418d5-22c0-4ed5-a37f-0286c5fe31c9\",\"success\":true}"; ReleaseMcpServerResponse result = mapper.readValue(json, ReleaseMcpServerResponse.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertEquals("12f418d5-22c0-4ed5-a37f-0286c5fe31c9", result.getMcpId()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/annotation/NacosPropertiesTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.annotation; import org.junit.jupiter.api.Test; import org.springframework.mock.env.MockEnvironment; import static com.alibaba.nacos.api.annotation.NacosProperties.ACCESS_KEY_PLACEHOLDER; import static com.alibaba.nacos.api.annotation.NacosProperties.CLUSTER_NAME_PLACEHOLDER; import static com.alibaba.nacos.api.annotation.NacosProperties.CONTEXT_PATH_PLACEHOLDER; import static com.alibaba.nacos.api.annotation.NacosProperties.ENCODE_PLACEHOLDER; import static com.alibaba.nacos.api.annotation.NacosProperties.ENDPOINT_PLACEHOLDER; import static com.alibaba.nacos.api.annotation.NacosProperties.NAMESPACE_PLACEHOLDER; import static com.alibaba.nacos.api.annotation.NacosProperties.SECRET_KEY_PLACEHOLDER; import static com.alibaba.nacos.api.annotation.NacosProperties.SERVER_ADDR_PLACEHOLDER; import static org.junit.jupiter.api.Assertions.assertEquals; class NacosPropertiesTest { @Test void testPlaceholders() { assertEquals("${nacos.endpoint:}", ENDPOINT_PLACEHOLDER); assertEquals("${nacos.namespace:}", NAMESPACE_PLACEHOLDER); assertEquals("${nacos.access-key:}", ACCESS_KEY_PLACEHOLDER); assertEquals("${nacos.secret-key:}", SECRET_KEY_PLACEHOLDER); assertEquals("${nacos.server-addr:}", SERVER_ADDR_PLACEHOLDER); assertEquals("${nacos.context-path:}", CONTEXT_PATH_PLACEHOLDER); assertEquals("${nacos.cluster-name:}", CLUSTER_NAME_PLACEHOLDER); assertEquals("${nacos.encode:UTF-8}", ENCODE_PLACEHOLDER); } @Test void testResolvePlaceholders() { testResolvePlaceholder(ENDPOINT_PLACEHOLDER, "nacos.endpoint", "test-value", "test-value"); testResolvePlaceholder(ENDPOINT_PLACEHOLDER, "", "test-value", ""); testResolvePlaceholder(NAMESPACE_PLACEHOLDER, "nacos.namespace", "test-value", "test-value"); testResolvePlaceholder(NAMESPACE_PLACEHOLDER, "", "test-value", ""); testResolvePlaceholder(ACCESS_KEY_PLACEHOLDER, "nacos.access-key", "test-value", "test-value"); testResolvePlaceholder(ACCESS_KEY_PLACEHOLDER, "", "test-value", ""); testResolvePlaceholder(SECRET_KEY_PLACEHOLDER, "nacos.secret-key", "test-value", "test-value"); testResolvePlaceholder(SECRET_KEY_PLACEHOLDER, "", "test-value", ""); testResolvePlaceholder(SERVER_ADDR_PLACEHOLDER, "nacos.server-addr", "test-value", "test-value"); testResolvePlaceholder(SERVER_ADDR_PLACEHOLDER, "", "test-value", ""); testResolvePlaceholder(CONTEXT_PATH_PLACEHOLDER, "nacos.context-path", "test-value", "test-value"); testResolvePlaceholder(CONTEXT_PATH_PLACEHOLDER, "", "test-value", ""); testResolvePlaceholder(CLUSTER_NAME_PLACEHOLDER, "nacos.cluster-name", "test-value", "test-value"); testResolvePlaceholder(CLUSTER_NAME_PLACEHOLDER, "", "test-value", ""); testResolvePlaceholder(ENCODE_PLACEHOLDER, "nacos.encode", "test-value", "test-value"); testResolvePlaceholder(ENCODE_PLACEHOLDER, "", "test-value", "UTF-8"); } private void testResolvePlaceholder(String placeholder, String propertyName, String propertyValue, String expectValue) { MockEnvironment environment = new MockEnvironment(); environment.setProperty(propertyName, propertyValue); String resolvedValue = environment.resolvePlaceholders(placeholder); assertEquals(expectValue, resolvedValue); } @Test void testSort() { } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/cmdb/pojo/EntityEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class EntityEventTest { ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialization() throws JsonProcessingException { EntityEvent entity = new EntityEvent(); entity.setEntityName("test-entity"); entity.setEntityType("CMDB"); entity.setType(EntityEventType.ENTITY_ADD_OR_UPDATE); String actual = mapper.writeValueAsString(entity); System.out.println(actual); assertTrue(actual.contains("\"entityName\":\"test-entity\"")); assertTrue(actual.contains("\"entityType\":\"CMDB\"")); assertTrue(actual.contains("\"type\":\"ENTITY_ADD_OR_UPDATE\"")); } @Test void testDeserialization() throws JsonProcessingException { String json = "{\"type\":\"ENTITY_REMOVE\",\"entityName\":\"test-entity\",\"entityType\":\"CMDB\"}"; EntityEvent entity = mapper.readValue(json, EntityEvent.class); assertEquals("test-entity", entity.getEntityName()); assertEquals("CMDB", entity.getEntityType()); assertEquals(EntityEventType.ENTITY_REMOVE, entity.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/cmdb/pojo/EntityTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class EntityTest { ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialization() throws JsonProcessingException { Entity entity = new Entity(); entity.setName("test-entity"); entity.setType(PreservedEntityTypes.ip.name()); entity.setLabels(Collections.singletonMap("test-label-key", "test-label-value")); String actual = mapper.writeValueAsString(entity); assertTrue(actual.contains("\"type\":\"ip\"")); assertTrue(actual.contains("\"name\":\"test-entity\"")); assertTrue(actual.contains("\"labels\":{\"test-label-key\":\"test-label-value\"}")); } @Test void testDeserialization() throws JsonProcessingException { String json = "{\"type\":\"service\",\"name\":\"test-entity\",\"labels\":{\"test-label-key\":\"test-label-value\"}}"; Entity entity = mapper.readValue(json, Entity.class); assertEquals("test-entity", entity.getName()); assertEquals(PreservedEntityTypes.service.name(), entity.getType()); assertEquals(1, entity.getLabels().size()); assertTrue(entity.getLabels().containsKey("test-label-key")); assertEquals("test-label-value", entity.getLabels().get("test-label-key")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/cmdb/pojo/LabelTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.cmdb.pojo; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class LabelTest { ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialization() throws JsonProcessingException { Label label = new Label(); label.setName("test-label"); label.setDescription("CMDB description"); label.setValues(Collections.singletonMap("test-value", "test-value").keySet()); String actual = mapper.writeValueAsString(label); System.out.println(actual); assertTrue(actual.contains("\"name\":\"test-label\"")); assertTrue(actual.contains("\"description\":\"CMDB description\"")); assertTrue(actual.contains("\"values\":[\"test-value\"]")); } @Test void testDeserialization() throws JsonProcessingException { String json = "{\"values\":[\"test-value\"],\"name\":\"test-label\",\"description\":\"CMDB description\"}"; Label label = mapper.readValue(json, Label.class); assertEquals("test-label", label.getName()); assertEquals("CMDB description", label.getDescription()); assertEquals(1, label.getValues().size()); assertEquals("test-value", label.getValues().iterator().next()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/ConfigChangeEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class ConfigChangeEventTest { @Test void testConstructor() { Map mockData = new HashMap<>(); mockData.put("test", new ConfigChangeItem("testKey", null, "testValue")); ConfigChangeEvent event = new ConfigChangeEvent(mockData); assertEquals(1, event.getChangeItems().size()); assertEquals("testKey", event.getChangeItem("test").getKey()); assertNull(event.getChangeItem("test").getOldValue()); assertEquals("testValue", event.getChangeItem("test").getNewValue()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/ConfigChangeItemTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class ConfigChangeItemTest { @Test void testSetNewValue() { ConfigChangeItem item = new ConfigChangeItem("testKey", null, "testValue"); item.setType(PropertyChangeType.ADDED); assertEquals("testKey", item.getKey()); assertNull(item.getOldValue()); assertEquals("testValue", item.getNewValue()); assertEquals(PropertyChangeType.ADDED, item.getType()); item.setOldValue("testValue"); item.setNewValue("testValue2"); item.setType(PropertyChangeType.MODIFIED); assertEquals("testKey", item.getKey()); assertEquals("testValue", item.getOldValue()); assertEquals("testValue2", item.getNewValue()); assertEquals(PropertyChangeType.MODIFIED, item.getType()); item.setKey("deletedKey"); item.setType(PropertyChangeType.DELETED); assertEquals("deletedKey", item.getKey()); assertEquals(PropertyChangeType.DELETED, item.getType()); } @Test void testToString() { ConfigChangeItem item = new ConfigChangeItem("testKey", null, "testValue"); item.setType(PropertyChangeType.ADDED); assertEquals("ConfigChangeItem{key='testKey', oldValue='null', newValue='testValue', type=ADDED}", item.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/ConfigFactoryTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.NacosConfigService; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class ConfigFactoryTest { @Test void testCreateConfigServiceByPropertiesSuccess() throws NacosException { NacosConfigService.IS_THROW_EXCEPTION.set(false); assertNotNull(ConfigFactory.createConfigService(new Properties())); } @Test void testCreateConfigServiceByPropertiesFailure() { NacosConfigService.IS_THROW_EXCEPTION.set(true); assertThrows(NacosException.class, () -> ConfigFactory.createConfigService(new Properties())); } @Test void testCreateConfigServiceByServerAddrSuccess() throws NacosException { NacosConfigService.IS_THROW_EXCEPTION.set(false); assertNotNull(ConfigFactory.createConfigService("localhost:8848")); } @Test void testCreateConfigServiceByServerAddrFailure() { NacosConfigService.IS_THROW_EXCEPTION.set(true); assertThrows(NacosException.class, () -> ConfigFactory.createConfigService("localhost:8848")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/ConfigTypeTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config; 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 ConfigTypeTest { @Test void isValidType() { assertTrue(ConfigType.isValidType("xml")); assertTrue(ConfigType.isValidType("properties")); assertTrue(ConfigType.isValidType("json")); assertTrue(ConfigType.isValidType("text")); assertTrue(ConfigType.isValidType("html")); assertTrue(ConfigType.isValidType("yaml")); assertTrue(ConfigType.isValidType("unset")); assertFalse(ConfigType.isValidType("")); assertFalse(ConfigType.isValidType("yml")); } @Test void testGetDefaultType() { assertEquals("text", ConfigType.getDefaultType().getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/ability/ClientRemoteAbilityTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.ability; import com.alibaba.nacos.api.remote.ability.ClientRemoteAbility; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientRemoteAbilityTest { private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { ClientRemoteAbility abilities = new ClientRemoteAbility(); String json = mapper.writeValueAsString(abilities); assertEquals("{\"supportRemoteConnection\":false}", json); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"supportRemoteConnection\":true}"; ClientRemoteAbility abilities = mapper.readValue(json, ClientRemoteAbility.class); assertTrue(abilities.isSupportRemoteConnection()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/ability/ServerConfigAbilityTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.ability; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class ServerConfigAbilityTest { @Test void testEquals() { ServerConfigAbility ability = new ServerConfigAbility(); ability.setSupportRemoteMetrics(true); assertEquals(ability, ability); assertNotEquals(null, ability); assertNotEquals(ability, new ClientConfigAbility()); ServerConfigAbility newOne = new ServerConfigAbility(); assertNotEquals(ability, newOne); newOne.setSupportRemoteMetrics(true); assertEquals(ability, newOne); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/ability/ServerRemoteAbilityTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.ability; import com.alibaba.nacos.api.ability.ClientAbilities; import com.alibaba.nacos.api.remote.ability.ServerRemoteAbility; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServerRemoteAbilityTest { private static ObjectMapper mapper; private ServerRemoteAbility serverAbilities; @BeforeAll static void setUpBeforeClass() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); } @BeforeEach void setUp() throws Exception { serverAbilities = new ServerRemoteAbility(); } @Test void testSerialize() throws JsonProcessingException { serverAbilities = new ServerRemoteAbility(); String json = mapper.writeValueAsString(serverAbilities); assertTrue(json.contains("\"supportRemoteConnection\":false")); assertTrue(json.contains("\"grpcReportEnabled\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"supportRemoteConnection\":true,\"grpcReportEnabled\":true}"; ServerRemoteAbility abilities = mapper.readValue(json, ServerRemoteAbility.class); assertTrue(abilities.isSupportRemoteConnection()); assertTrue(abilities.isGrpcReportEnabled()); } @Test void testEqualsAndHashCode() { assertEquals(serverAbilities, serverAbilities); assertEquals(serverAbilities.hashCode(), serverAbilities.hashCode()); assertNotEquals(null, serverAbilities); assertNotEquals(serverAbilities, new ClientAbilities()); ServerRemoteAbility test = new ServerRemoteAbility(); assertEquals(serverAbilities, test); assertEquals(serverAbilities.hashCode(), test.hashCode()); test.setSupportRemoteConnection(true); assertNotEquals(serverAbilities, test); assertNotEquals(serverAbilities.hashCode(), test.hashCode()); test.setSupportRemoteConnection(false); test.setGrpcReportEnabled(false); assertNotEquals(serverAbilities, test); assertNotEquals(serverAbilities.hashCode(), test.hashCode()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/listener/AbstractFuzzyWatchEventWatcherTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNull; class AbstractFuzzyWatchEventWatcherTest { private AbstractFuzzyWatchEventWatcher fuzzyWatchEventWatcher; @BeforeEach void setUp() { fuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { // Empty implementation for testing } }; } @Test void testGetExecutor() { assertNull(fuzzyWatchEventWatcher.getExecutor()); } @Test void testOnPatternOverLimit() { assertDoesNotThrow(() -> fuzzyWatchEventWatcher.onPatternOverLimit()); } @Test void testOnConfigReachUpLimit() { assertDoesNotThrow(() -> fuzzyWatchEventWatcher.onConfigReachUpLimit()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/listener/AbstractListenerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNull; class AbstractListenerTest { @Test void testGetExecutor() { // Default listener executor is null. assertNull(new AbstractListener() { @Override public void receiveConfigInfo(String configInfo) { } }.getExecutor()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/listener/AbstractSharedListenerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class AbstractSharedListenerTest { private static final String CONFIG_CONTENT = "test"; private static Map receivedMap; @BeforeEach void setUp() { receivedMap = new HashMap<>(); } @Test void testFillContext() { assertEquals(0, receivedMap.size()); MockShardListener listener = new MockShardListener(); listener.receiveConfigInfo(CONFIG_CONTENT); assertEquals(2, receivedMap.size()); assertNull(receivedMap.get("group")); assertNull(receivedMap.get("dataId")); listener.fillContext("aaa", "ggg"); listener.receiveConfigInfo(CONFIG_CONTENT); assertEquals(2, receivedMap.size()); assertEquals("ggg", receivedMap.get("group")); assertEquals("aaa", receivedMap.get("dataId")); } @Test void getExecutor() { // Default listener executor is null. assertNull(new MockShardListener().getExecutor()); } private static class MockShardListener extends AbstractSharedListener { @Override public void innerReceive(String dataId, String group, String configInfo) { assertEquals(CONFIG_CONTENT, configInfo); receivedMap.put("group", group); receivedMap.put("dataId", dataId); } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/listener/ConfigFuzzyWatchChangeEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.listener; 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; class ConfigFuzzyWatchChangeEventTest { @Test void testBuildWithValidParameters() { ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build( "test-namespace", "test-group", "test-dataId", "ADD_CONFIG", "FUZZY_WATCH_INIT_NOTIFY"); assertNotNull(event); assertEquals("test-namespace", event.getNamespace()); assertEquals("test-group", event.getGroup()); assertEquals("test-dataId", event.getDataId()); assertEquals("ADD_CONFIG", event.getChangedType()); assertEquals("FUZZY_WATCH_INIT_NOTIFY", event.getSyncType()); } @Test void testBuildWithNullParameters() { ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build( null, null, null, null, null); assertNotNull(event); assertNull(event.getNamespace()); assertNull(event.getGroup()); assertNull(event.getDataId()); assertNull(event.getChangedType()); assertNull(event.getSyncType()); } @Test void testToString() { ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build( "test-namespace", "test-group", "test-dataId", "ADD_CONFIG", "FUZZY_WATCH_INIT_NOTIFY"); String expected = "ConfigFuzzyWatchChangeEvent{group='test-group', dataId='test-dataId', " + "namespace='test-namespace', changedType='ADD_CONFIG', syncType='FUZZY_WATCH_INIT_NOTIFY'}"; assertEquals(expected, event.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/model/ConfigCloneInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class ConfigCloneInfoTest { private ObjectMapper mapper; ConfigCloneInfo configCloneInfo; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); configCloneInfo = new ConfigCloneInfo(); configCloneInfo.setConfigId(1L); configCloneInfo.setTargetDataId("newDataId"); configCloneInfo.setTargetGroupName("newGroup"); } @Test public void testSerialize() throws Exception { String json = mapper.writeValueAsString(configCloneInfo); assertTrue(json.contains("\"configId\":1")); assertTrue(json.contains("\"targetGroupName\":\"newGroup\"")); assertTrue(json.contains("\"targetDataId\":\"newDataId\"")); } @Test public void testDeserialize() throws Exception { String json = "{\"configId\":1,\"targetGroupName\":\"newGroup\",\"targetDataId\":\"newDataId\"}"; ConfigCloneInfo actual = mapper.readValue(json, ConfigCloneInfo.class); assertEquals(configCloneInfo.getConfigId(), actual.getConfigId()); assertEquals(configCloneInfo.getTargetGroupName(), actual.getTargetGroupName()); assertEquals(configCloneInfo.getTargetDataId(), actual.getTargetDataId()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/model/ConfigHistoryInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class ConfigHistoryInfoTest { private ObjectMapper mapper; private ConfigHistoryBasicInfo basicInfo; private ConfigHistoryDetailInfo detailInfo; private long createTime; private long modifyTime; @BeforeEach void setUp() { createTime = System.currentTimeMillis(); mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); basicInfo = new ConfigHistoryBasicInfo(); detailInfo = new ConfigHistoryDetailInfo(); modifyTime = System.currentTimeMillis(); mockBasicInfo(basicInfo, createTime, modifyTime); mockBasicInfo(detailInfo, createTime, modifyTime); mockDetailInfo(detailInfo); } private void mockBasicInfo(ConfigHistoryBasicInfo basicInfo, long createTime, long modifyTime) { basicInfo.setId(1L); basicInfo.setNamespaceId("testNs"); basicInfo.setGroupName("testGroup"); basicInfo.setDataId("testDataId"); basicInfo.setMd5("testMd5"); basicInfo.setType("text"); basicInfo.setAppName("testApp"); basicInfo.setCreateTime(createTime); basicInfo.setModifyTime(modifyTime); basicInfo.setSrcIp("1.1.1.1"); basicInfo.setSrcUser("testCreateUser"); basicInfo.setOpType("I"); basicInfo.setPublishType("formal"); } private void mockDetailInfo(ConfigHistoryDetailInfo detailInfo) { detailInfo.setContent("testContent"); detailInfo.setEncryptedDataKey("testEncryptedDataKey"); detailInfo.setGrayName("testGrayName"); detailInfo.setExtInfo("{\"type\":\"text\"}"); } @Test public void testBasicInfoSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(basicInfo); assertJsonContainBasicInfos(json); } @Test public void testBasicInfoDeserialize() throws JsonProcessingException { String json = "{\"id\":\"1\",\"namespaceId\":\"testNs\",\"groupName\":\"testGroup\",\"dataId\":\"testDataId\"," + "\"md5\":\"testMd5\",\"type\":\"text\",\"appName\":\"testApp\",\"createTime\":%s,\"modifyTime\":%s," + "\"srcIp\":\"1.1.1.1\",\"srcUser\":\"testCreateUser\",\"opType\":\"I\",\"publishType\":\"formal\"}"; json = String.format(json, createTime, modifyTime); assertBasicInfo(mapper.readValue(json, ConfigHistoryBasicInfo.class)); } @Test public void testDetailInfoSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(detailInfo); assertJsonContainBasicInfos(json); asserJsonContainDetailInfos(json); } @Test public void testDetailInfoDeserialize() throws JsonProcessingException { String json = "{\"id\":\"1\",\"namespaceId\":\"testNs\",\"groupName\":\"testGroup\",\"dataId\":\"testDataId\"," + "\"md5\":\"testMd5\",\"type\":\"text\",\"appName\":\"testApp\",\"createTime\":%s,\"modifyTime\":%s," + "\"srcIp\":\"1.1.1.1\",\"srcUser\":\"testCreateUser\",\"opType\":\"I\",\"publishType\":\"formal\"," + "\"content\":\"testContent\",\"encryptedDataKey\":\"testEncryptedDataKey\",\"grayName\":\"testGrayName\"," + "\"extInfo\":\"{\\\"type\\\":\\\"text\\\"}\"}"; json = String.format(json, createTime, modifyTime); ConfigHistoryDetailInfo detailInfo = mapper.readValue(json, ConfigHistoryDetailInfo.class); assertBasicInfo(detailInfo); assertDetailInfo(detailInfo); } private void assertJsonContainBasicInfos(String json) { assertTrue(json.contains("\"id\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"testNs\"")); assertTrue(json.contains("\"groupName\":\"testGroup\"")); assertTrue(json.contains("\"dataId\":\"testDataId\"")); assertTrue(json.contains("\"md5\":\"testMd5\"")); assertTrue(json.contains("\"type\":\"text\"")); assertTrue(json.contains("\"appName\":\"testApp\"")); assertTrue(json.contains("\"createTime\":" + createTime)); assertTrue(json.contains("\"modifyTime\":" + modifyTime)); assertTrue(json.contains("\"srcIp\":\"1.1.1.1\"")); assertTrue(json.contains("\"srcUser\":\"testCreateUser\"")); assertTrue(json.contains("\"opType\":\"I\"")); assertTrue(json.contains("\"publishType\":\"formal\"")); } private void asserJsonContainDetailInfos(String json) { assertTrue(json.contains("\"content\":\"testContent\"")); assertTrue(json.contains("\"encryptedDataKey\":\"testEncryptedDataKey\"")); assertTrue(json.contains("\"grayName\":\"testGrayName\"")); assertTrue(json.contains("\"extInfo\":\"{\\\"type\\\":\\\"text\\\"}\"")); } private void assertBasicInfo(ConfigHistoryBasicInfo actual) { assertEquals(basicInfo.getId(), actual.getId()); assertEquals(basicInfo.getNamespaceId(), actual.getNamespaceId()); assertEquals(basicInfo.getGroupName(), actual.getGroupName()); assertEquals(basicInfo.getDataId(), actual.getDataId()); assertEquals(basicInfo.getMd5(), actual.getMd5()); assertEquals(basicInfo.getType(), actual.getType()); assertEquals(basicInfo.getAppName(), actual.getAppName()); assertEquals(basicInfo.getCreateTime(), actual.getCreateTime()); assertEquals(basicInfo.getModifyTime(), actual.getModifyTime()); assertEquals(basicInfo.getSrcIp(), actual.getSrcIp()); assertEquals(basicInfo.getSrcUser(), actual.getSrcUser()); assertEquals(basicInfo.getPublishType(), actual.getPublishType()); assertEquals(basicInfo.getOpType(), actual.getOpType()); } private void assertDetailInfo(ConfigHistoryDetailInfo actual) { assertEquals(detailInfo.getContent(), actual.getContent()); assertEquals(detailInfo.getEncryptedDataKey(), actual.getEncryptedDataKey()); assertEquals(detailInfo.getGrayName(), actual.getGrayName()); assertEquals(detailInfo.getExtInfo(), actual.getExtInfo()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/model/ConfigInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class ConfigInfoTest { private ObjectMapper mapper; private ConfigBasicInfo basicInfo; private ConfigDetailInfo detailInfo; private ConfigGrayInfo grayInfo; private long createTime; private long modifyTime; @BeforeEach void setUp() { createTime = System.currentTimeMillis(); mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); basicInfo = new ConfigBasicInfo(); detailInfo = new ConfigDetailInfo(); grayInfo = new ConfigGrayInfo(); modifyTime = System.currentTimeMillis(); mockBasicInfo(basicInfo, createTime, modifyTime); mockBasicInfo(detailInfo, createTime, modifyTime); mockBasicInfo(grayInfo, createTime, modifyTime); mockDetailInfo(detailInfo); mockDetailInfo(grayInfo); grayInfo.setGrayName("testGrayName"); grayInfo.setGrayRule( "{\"type\":\"beta\",\"version\":\"1.0.0\",\"expr\":\"127.0.0.1,127.0.0.2\",\"priority\":-1000}"); } private void mockBasicInfo(ConfigBasicInfo basicInfo, long createTime, long modifyTime) { basicInfo.setId(1L); basicInfo.setNamespaceId("testNs"); basicInfo.setGroupName("testGroup"); basicInfo.setDataId("testDataId"); basicInfo.setMd5("testMd5"); basicInfo.setType("text"); basicInfo.setAppName("testApp"); basicInfo.setCreateTime(createTime); basicInfo.setModifyTime(modifyTime); basicInfo.setDesc("testDesc"); basicInfo.setConfigTags("testConfigTag1,testConfigTag2"); } private void mockDetailInfo(ConfigDetailInfo detailInfo) { detailInfo.setContent("testContent"); detailInfo.setEncryptedDataKey("testEncryptedDataKey"); detailInfo.setCreateUser("testCreateUser"); detailInfo.setCreateIp("1.1.1.1"); } @Test public void testBasicInfoSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(basicInfo); assertJsonContainBasicInfos(json); } @Test public void testBasicInfoDeserialize() throws JsonProcessingException { String json = "{\"id\":\"1\",\"namespaceId\":\"testNs\",\"groupName\":\"testGroup\",\"dataId\":\"testDataId\"," + "\"md5\":\"testMd5\",\"type\":\"text\",\"appName\":\"testApp\",\"createTime\":%s,\"modifyTime\":%s}"; json = String.format(json, createTime, modifyTime); assertBasicInfo(mapper.readValue(json, ConfigBasicInfo.class)); } @Test public void testDetailInfoSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(detailInfo); assertJsonContainBasicInfos(json); asserJsonContainDetailInfos(json); } @Test public void testDetailInfoDeserialize() throws JsonProcessingException { String json = "{\"id\":\"1\",\"namespaceId\":\"testNs\",\"groupName\":\"testGroup\",\"dataId\":\"testDataId\"," + "\"md5\":\"testMd5\",\"type\":\"text\",\"appName\":\"testApp\",\"createTime\":%s," + "\"modifyTime\":%s,\"content\":\"testContent\",\"desc\":\"testDesc\"," + "\"encryptedDataKey\":\"testEncryptedDataKey\",\"createUser\":\"testCreateUser\"," + "\"createIp\":\"1.1.1.1\",\"configTags\":\"testConfigTag1,testConfigTag2\"}"; json = String.format(json, createTime, modifyTime); ConfigDetailInfo detailInfo = mapper.readValue(json, ConfigDetailInfo.class); assertBasicInfo(detailInfo); assertDetailInfo(detailInfo); } @Test public void testGrayInfoSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(grayInfo); assertJsonContainBasicInfos(json); asserJsonContainDetailInfos(json); assertTrue(json.contains("\"grayName\":\"testGrayName\"")); assertTrue(json.contains("\"grayRule\":\"{")); assertTrue(json.contains("\\\"type\\\":\\\"beta\\\"")); assertTrue(json.contains("\\\"version\\\":\\\"1.0.0\\\"")); assertTrue(json.contains("\\\"expr\\\":\\\"127.0.0.1,127.0.0.2\\\"")); assertTrue(json.contains("\\\"priority\\\":-1000")); } @Test public void testGrayInfoDeserialize() throws JsonProcessingException { String json = "{\"id\":\"1\",\"namespaceId\":\"testNs\",\"groupName\":\"testGroup\",\"dataId\":\"testDataId\"," + "\"md5\":\"testMd5\",\"type\":\"text\",\"appName\":\"testApp\",\"createTime\":%s,\"modifyTime\":%s," + "\"content\":\"testContent\",\"desc\":\"testDesc\",\"encryptedDataKey\":\"testEncryptedDataKey\"," + "\"createUser\":\"testCreateUser\",\"createIp\":\"1.1.1.1\",\"configTags\":\"testConfigTag1,testConfigTag2\"," + "\"grayName\":\"testGrayName\",\"grayRule\":" + "\"{\\\"type\\\":\\\"beta\\\",\\\"version\\\":\\\"1.0.0\\\",\\\"expr\\\":\\\"127.0.0.1,127.0.0.2\\\",\\\"priority\\\":-1000}\"}"; json = String.format(json, createTime, modifyTime); ConfigGrayInfo actualGrayInfo = mapper.readValue(json, ConfigGrayInfo.class); assertBasicInfo(actualGrayInfo); assertDetailInfo(actualGrayInfo); assertEquals(grayInfo.getGrayName(), actualGrayInfo.getGrayName()); assertEquals(grayInfo.getGrayRule(), actualGrayInfo.getGrayRule()); } private void assertJsonContainBasicInfos(String json) { assertTrue(json.contains("\"id\":\"1\"")); assertTrue(json.contains("\"namespaceId\":\"testNs\"")); assertTrue(json.contains("\"groupName\":\"testGroup\"")); assertTrue(json.contains("\"dataId\":\"testDataId\"")); assertTrue(json.contains("\"md5\":\"testMd5\"")); assertTrue(json.contains("\"type\":\"text\"")); assertTrue(json.contains("\"appName\":\"testApp\"")); assertTrue(json.contains("\"createTime\":" + createTime)); assertTrue(json.contains("\"modifyTime\":" + modifyTime)); } private void asserJsonContainDetailInfos(String json) { assertTrue(json.contains("\"content\":\"testContent\"")); assertTrue(json.contains("\"desc\":\"testDesc\"")); assertTrue(json.contains("\"encryptedDataKey\":\"testEncryptedDataKey\"")); assertTrue(json.contains("\"createUser\":\"testCreateUser\"")); assertTrue(json.contains("\"createIp\":\"1.1.1.1\"")); assertTrue(json.contains("\"configTags\":\"testConfigTag1,testConfigTag2\"")); } private void assertBasicInfo(ConfigBasicInfo actual) { assertEquals(basicInfo.getId(), actual.getId()); assertEquals(basicInfo.getNamespaceId(), actual.getNamespaceId()); assertEquals(basicInfo.getGroupName(), actual.getGroupName()); assertEquals(basicInfo.getDataId(), actual.getDataId()); assertEquals(basicInfo.getMd5(), actual.getMd5()); assertEquals(basicInfo.getType(), actual.getType()); assertEquals(basicInfo.getAppName(), actual.getAppName()); assertEquals(basicInfo.getCreateTime(), actual.getCreateTime()); assertEquals(basicInfo.getModifyTime(), actual.getModifyTime()); } private void assertDetailInfo(ConfigDetailInfo actual) { assertEquals(detailInfo.getContent(), actual.getContent()); assertEquals(detailInfo.getDesc(), actual.getDesc()); assertEquals(detailInfo.getEncryptedDataKey(), actual.getEncryptedDataKey()); assertEquals(detailInfo.getCreateUser(), actual.getCreateUser()); assertEquals(detailInfo.getCreateIp(), actual.getCreateIp()); assertEquals(detailInfo.getConfigTags(), actual.getConfigTags()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/model/ConfigListenerInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigListenerInfoTest { private ObjectMapper mapper; private ConfigListenerInfo configListenerInfo; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); configListenerInfo = new ConfigListenerInfo(); configListenerInfo.setQueryType(ConfigListenerInfo.QUERY_TYPE_CONFIG); configListenerInfo.setListenersStatus(Collections.singletonMap("1.1.1.1", "testMd5")); } @Test public void testSerialize() throws Exception { String json = mapper.writeValueAsString(configListenerInfo); assertTrue(json.contains("\"queryType\":\"config\"")); assertTrue(json.contains("\"listenersStatus\":{\"1.1.1.1\":\"testMd5\"}")); } @Test public void testDeserialize() throws Exception { String json = "{\"queryType\":\"config\",\"listenersStatus\":{\"1.1.1.1\":\"testMd5\"}}"; ConfigListenerInfo configListenerInfo = mapper.readValue(json, ConfigListenerInfo.class); assertEquals(ConfigListenerInfo.QUERY_TYPE_CONFIG, configListenerInfo.getQueryType()); assertEquals("testMd5", configListenerInfo.getListenersStatus().get("1.1.1.1")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/model/SameConfigPolicyTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.model; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class SameConfigPolicyTest { private ObjectMapper mapper; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } @Test public void testSerialize() throws Exception { String abortJson = mapper.writeValueAsString(SameConfigPolicy.ABORT); String skipJson = mapper.writeValueAsString(SameConfigPolicy.SKIP); String overwriteJson = mapper.writeValueAsString(SameConfigPolicy.OVERWRITE); assertTrue(abortJson.contains("\"ABORT\"")); assertTrue(skipJson.contains("\"SKIP\"")); assertTrue(overwriteJson.contains("\"OVERWRITE\"")); } @Test public void testDeserialize() throws Exception { assertEquals(SameConfigPolicy.ABORT, mapper.readValue("\"ABORT\"", SameConfigPolicy.class)); assertEquals(SameConfigPolicy.SKIP, mapper.readValue("\"SKIP\"", SameConfigPolicy.class)); assertEquals(SameConfigPolicy.OVERWRITE, mapper.readValue("\"OVERWRITE\"", SameConfigPolicy.class)); } @Test public void testValues() { SameConfigPolicy[] values = SameConfigPolicy.values(); assertEquals(3, values.length); assertEquals(SameConfigPolicy.ABORT, values[0]); assertEquals(SameConfigPolicy.SKIP, values[1]); assertEquals(SameConfigPolicy.OVERWRITE, values[2]); } @Test public void testValueOf() { assertEquals(SameConfigPolicy.ABORT, SameConfigPolicy.valueOf("ABORT")); assertEquals(SameConfigPolicy.SKIP, SameConfigPolicy.valueOf("SKIP")); assertEquals(SameConfigPolicy.OVERWRITE, SameConfigPolicy.valueOf("OVERWRITE")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/BasedConfigRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.remote.request.Request; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import java.util.HashMap; import java.util.Map; import java.util.UUID; public abstract class BasedConfigRequestTest { protected static final String DATA_ID = "test_data"; protected static final String GROUP = "group"; protected static final String TENANT = "test_tenant"; protected static final String MD5 = "test_MD5"; protected static final String TAG = "tag"; protected static final String[] KEY = new String[] {DATA_ID, GROUP, TENANT}; protected static final Map HEADERS = new HashMap<>(); protected static final String HEADER_KEY = "header1"; protected static final String HEADER_VALUE = "test_header1"; protected static final String CONTENT = "content"; protected static ObjectMapper mapper; static { HEADERS.put(HEADER_KEY, HEADER_VALUE); } @BeforeAll public static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); } public abstract void testSerialize() throws JsonProcessingException; public abstract void testDeserialize() throws JsonProcessingException; protected String injectRequestUuId(Request request) { String uuid = UUID.randomUUID().toString(); request.setRequestId(uuid); return uuid; } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ClientConfigMetricRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Objects; import static com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest.MetricsKey.CACHE_DATA; import static com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest.MetricsKey.SNAPSHOT_DATA; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientConfigMetricRequestTest extends BasedConfigRequestTest { @Override @Test public void testSerialize() throws JsonProcessingException { ClientConfigMetricRequest clientMetrics = new ClientConfigMetricRequest(); clientMetrics.putAllHeader(HEADERS); clientMetrics.getMetricsKeys() .add(ClientConfigMetricRequest.MetricsKey.build(CACHE_DATA, String.join("+", KEY))); clientMetrics.getMetricsKeys() .add(ClientConfigMetricRequest.MetricsKey.build(SNAPSHOT_DATA, String.join("+", KEY))); final String requestId = injectRequestUuId(clientMetrics); String json = mapper.writeValueAsString(clientMetrics); assertTrue(json.contains("\"type\":\"" + "cacheData" + "\"")); assertTrue(json.contains("\"type\":\"" + "snapshotData" + "\"")); assertTrue(json.contains("\"key\":\"" + String.join("+", KEY) + "\"")); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"}," + "\"metricsKeys\":[{\"type\":\"cacheData\",\"key\":" + "\"test_data+group+test_tenant\"},{\"type\":\"snapshotData\"," + "\"key\":\"test_data+group+test_tenant\"}],\"module\":\"config\"}"; ClientConfigMetricRequest actual = mapper.readValue(json, ClientConfigMetricRequest.class); assertEquals(2, actual.getMetricsKeys().size()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(HEADER_VALUE, actual.getHeader(HEADER_KEY)); } @Test void testMetricsKeysEquals() { String dataKey = String.join("+", KEY); ClientConfigMetricRequest.MetricsKey key = ClientConfigMetricRequest.MetricsKey.build(CACHE_DATA, dataKey); assertEquals(key, key); assertNotEquals(null, key); assertNotEquals(key, new ClientConfigMetricRequest()); ClientConfigMetricRequest.MetricsKey newOne = ClientConfigMetricRequest.MetricsKey.build(SNAPSHOT_DATA, dataKey); assertNotEquals(key, newOne); newOne.setType(CACHE_DATA); assertEquals(key, newOne); } @Test void testMetricsHashCode() { String dataKey = String.join("+", KEY); ClientConfigMetricRequest.MetricsKey key = ClientConfigMetricRequest.MetricsKey.build(CACHE_DATA, dataKey); assertEquals(Objects.hash(CACHE_DATA, dataKey), key.hashCode()); } @Test void testMetricsToString() { ClientConfigMetricRequest.MetricsKey key = ClientConfigMetricRequest.MetricsKey.build(CACHE_DATA, String.join("+", KEY)); assertEquals("MetricsKey{type='cacheData', key='test_data+group+test_tenant'}", key.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigBatchListenRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigBatchListenRequestTest extends BasedConfigRequestTest { @Override @Test public void testSerialize() throws JsonProcessingException { ConfigBatchListenRequest configBatchListenRequest = new ConfigBatchListenRequest(); configBatchListenRequest.putAllHeader(HEADERS); configBatchListenRequest.addConfigListenContext(GROUP, DATA_ID, TENANT, MD5); final String requestId = injectRequestUuId(configBatchListenRequest); String json = mapper.writeValueAsString(configBatchListenRequest); assertTrue(json.contains("\"listen\":" + "true")); assertTrue(json.contains( "\"configListenContexts\":[{\"dataId\":\"test_data\",\"group\":\"group\",\"md5\":\"test_MD5\",\"tenant\":\"test_tenant\"}]")); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"},\"listen\":true," + "\"configListenContexts\":[{\"group\":\"group\",\"md5\":\"test_MD5\"," + "\"dataId\":\"test_data\",\"tenant\":\"test_tenant\"}],\"module\":\"config\"}"; ConfigBatchListenRequest actual = mapper.readValue(json, ConfigBatchListenRequest.class); assertEquals(true, actual.isListen()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(HEADER_VALUE, actual.getHeader(HEADER_KEY)); assertEquals(1, actual.getConfigListenContexts().size()); } @Test void testConfigListenContextToString() { ConfigBatchListenRequest configBatchListenRequest = new ConfigBatchListenRequest(); configBatchListenRequest.addConfigListenContext(GROUP, DATA_ID, TENANT, MD5); assertEquals("ConfigListenContext{group='group', md5='test_MD5', dataId='test_data', tenant='test_tenant'}", configBatchListenRequest.getConfigListenContexts().get(0).toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigChangeNotifyRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; 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; class ConfigChangeNotifyRequestTest extends BasedConfigRequestTest { ConfigChangeNotifyRequest configChangeNotifyRequest; String requestId; @BeforeEach void before() { configChangeNotifyRequest = ConfigChangeNotifyRequest.build(DATA_ID, GROUP, TENANT); configChangeNotifyRequest.putAllHeader(HEADERS); requestId = injectRequestUuId(configChangeNotifyRequest); } @Override @Test public void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(configChangeNotifyRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"dataId\":\"" + DATA_ID)); assertTrue(json.contains("\"group\":\"" + GROUP)); assertTrue(json.contains("\"tenant\":\"" + TENANT)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"},\"dataId\":\"test_data\",\"group\":" + "\"group\",\"tenant\":\"test_tenant\",\"module\":\"config\"}"; ConfigChangeNotifyRequest actual = mapper.readValue(json, ConfigChangeNotifyRequest.class); assertEquals(DATA_ID, actual.getDataId()); assertEquals(GROUP, actual.getGroup()); assertEquals(TENANT, actual.getTenant()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(HEADER_VALUE, actual.getHeader(HEADER_KEY)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchChangeNotifyRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; 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; class ConfigFuzzyWatchChangeNotifyRequestTest extends BasedConfigRequestTest { private static final String GROUP_KEY = "test-group-key"; private static final String CHANGE_TYPE = "ADD"; ConfigFuzzyWatchChangeNotifyRequest configFuzzyWatchChangeNotifyRequest; String requestId; @BeforeEach void before() { configFuzzyWatchChangeNotifyRequest = new ConfigFuzzyWatchChangeNotifyRequest(GROUP_KEY, CHANGE_TYPE); configFuzzyWatchChangeNotifyRequest.putAllHeader(HEADERS); requestId = injectRequestUuId(configFuzzyWatchChangeNotifyRequest); } @Override @Test public void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(configFuzzyWatchChangeNotifyRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"groupKey\":\"" + GROUP_KEY)); assertTrue(json.contains("\"changeType\":\"" + CHANGE_TYPE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"},\"groupKey\":\"test-group-key\"," + "\"changeType\":\"ADD\",\"module\":\"config\"}"; ConfigFuzzyWatchChangeNotifyRequest actual = mapper.readValue(json, ConfigFuzzyWatchChangeNotifyRequest.class); assertEquals(GROUP_KEY, actual.getGroupKey()); assertEquals(CHANGE_TYPE, actual.getChangeType()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(HEADER_VALUE, actual.getHeader(HEADER_KEY)); } @Test void testToString() { ConfigFuzzyWatchChangeNotifyRequest request = new ConfigFuzzyWatchChangeNotifyRequest(GROUP_KEY, CHANGE_TYPE); assertEquals("FuzzyListenNotifyChangeRequest{', groupKey='" + GROUP_KEY + "', changeType=" + CHANGE_TYPE + "}", request.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigFuzzyWatchRequestTest extends BasedConfigRequestTest { private static final String GROUP_KEY_PATTERN = "test.*"; private static final String WATCH_TYPE = "FUZZY"; @Override @Test public void testSerialize() throws JsonProcessingException { ConfigFuzzyWatchRequest configFuzzyWatchRequest = new ConfigFuzzyWatchRequest(); configFuzzyWatchRequest.putAllHeader(HEADERS); configFuzzyWatchRequest.setGroupKeyPattern(GROUP_KEY_PATTERN); configFuzzyWatchRequest.setWatchType(WATCH_TYPE); configFuzzyWatchRequest.setInitializing(true); Set receivedGroupKeys = new HashSet<>(); receivedGroupKeys.add("test-group-key-1"); receivedGroupKeys.add("test-group-key-2"); configFuzzyWatchRequest.setReceivedGroupKeys(receivedGroupKeys); final String requestId = injectRequestUuId(configFuzzyWatchRequest); String json = mapper.writeValueAsString(configFuzzyWatchRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"groupKeyPattern\":\"" + GROUP_KEY_PATTERN)); assertTrue(json.contains("\"watchType\":\"" + WATCH_TYPE)); assertTrue(json.contains("\"initializing\":" + true)); assertTrue(json.contains("\"receivedGroupKeys\":[")); assertTrue(json.contains("\"test-group-key-1\"")); assertTrue(json.contains("\"test-group-key-2\"")); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"},\"groupKeyPattern\":\"test.*\"," + "\"watchType\":\"FUZZY\",\"initializing\":true," + "\"receivedGroupKeys\":[\"test-group-key-1\",\"test-group-key-2\"],\"module\":\"config\"}"; ConfigFuzzyWatchRequest actual = mapper.readValue(json, ConfigFuzzyWatchRequest.class); assertEquals(GROUP_KEY_PATTERN, actual.getGroupKeyPattern()); assertEquals(WATCH_TYPE, actual.getWatchType()); assertEquals(true, actual.isInitializing()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(HEADER_VALUE, actual.getHeader(HEADER_KEY)); assertEquals(2, actual.getReceivedGroupKeys().size()); assertTrue(actual.getReceivedGroupKeys().contains("test-group-key-1")); assertTrue(actual.getReceivedGroupKeys().contains("test-group-key-2")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigFuzzyWatchSyncRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest.Context; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigFuzzyWatchSyncRequestTest extends BasedConfigRequestTest { private static final String GROUP_KEY_PATTERN = "test.*"; private static final String SYNC_TYPE = Constants.FUZZY_WATCH_INIT_NOTIFY; private static final String GROUP_KEY = "test-group-key"; private static final String CHANGED_TYPE = "ADD"; @Override @Test public void testSerialize() throws JsonProcessingException { Set contexts = new HashSet<>(); Context context = Context.build(GROUP_KEY, CHANGED_TYPE); contexts.add(context); ConfigFuzzyWatchSyncRequest configFuzzyWatchSyncRequest = ConfigFuzzyWatchSyncRequest.buildSyncRequest( SYNC_TYPE, contexts, GROUP_KEY_PATTERN, 2, 1); configFuzzyWatchSyncRequest.putAllHeader(HEADERS); final String requestId = injectRequestUuId(configFuzzyWatchSyncRequest); String json = mapper.writeValueAsString(configFuzzyWatchSyncRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"groupKeyPattern\":\"" + GROUP_KEY_PATTERN)); assertTrue(json.contains("\"syncType\":\"" + SYNC_TYPE)); assertTrue(json.contains("\"totalBatch\":" + 2)); assertTrue(json.contains("\"currentBatch\":" + 1)); assertTrue(json.contains("\"contexts\":[")); assertTrue(json.contains("\"groupKey\":\"" + GROUP_KEY)); assertTrue(json.contains("\"changedType\":\"" + CHANGED_TYPE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"},\"groupKeyPattern\":\"test.*\"," + "\"syncType\":\"" + Constants.FUZZY_WATCH_INIT_NOTIFY + "\",\"totalBatch\":2,\"currentBatch\":1," + "\"contexts\":[{\"groupKey\":\"test-group-key\",\"changedType\":\"ADD\"}],\"module\":\"config\"}"; ConfigFuzzyWatchSyncRequest actual = mapper.readValue(json, ConfigFuzzyWatchSyncRequest.class); assertEquals(GROUP_KEY_PATTERN, actual.getGroupKeyPattern()); assertEquals(SYNC_TYPE, actual.getSyncType()); assertEquals(2, actual.getTotalBatch()); assertEquals(1, actual.getCurrentBatch()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(HEADER_VALUE, actual.getHeader(HEADER_KEY)); assertEquals(1, actual.getContexts().size()); Context context = actual.getContexts().iterator().next(); assertEquals(GROUP_KEY, context.getGroupKey()); assertEquals(CHANGED_TYPE, context.getChangedType()); } @Test void testBuildInitFinishRequest() { ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest(GROUP_KEY_PATTERN); assertEquals(GROUP_KEY_PATTERN, request.getGroupKeyPattern()); assertEquals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY, request.getSyncType()); } @Test void testContextBuild() { Context context = Context.build(GROUP_KEY, CHANGED_TYPE); assertEquals(GROUP_KEY, context.getGroupKey()); assertEquals(CHANGED_TYPE, context.getChangedType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigPublishRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; 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; class ConfigPublishRequestTest extends BasedConfigRequestTest { private static final String TAG_PARAM = "tag"; private static final String APP_NAME_PARAM = "appName"; ConfigPublishRequest configPublishRequest; String requestId; @BeforeEach void before() { configPublishRequest = new ConfigPublishRequest(DATA_ID, GROUP, TENANT, CONTENT); configPublishRequest.putAdditionalParam(TAG_PARAM, TAG_PARAM); configPublishRequest.putAdditionalParam(APP_NAME_PARAM, APP_NAME_PARAM); configPublishRequest.setCasMd5(MD5); configPublishRequest.putAllHeader(HEADERS); requestId = injectRequestUuId(configPublishRequest); } @Override @Test public void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(configPublishRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"dataId\":\"" + DATA_ID)); assertTrue(json.contains("\"group\":\"" + GROUP)); assertTrue(json.contains("\"tenant\":\"" + TENANT)); assertTrue(json.contains("\"content\":\"" + CONTENT)); assertTrue(json.contains("\"casMd5\":\"" + MD5)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"},\"dataId\":\"test_data\",\"group\":\"group\"," + "\"tenant\":\"test_tenant\",\"content\":\"content\",\"casMd5\":\"test_MD5\"," + "\"additionMap\":{\"appName\":\"appName\",\"tag\":\"tag\"},\"module\":\"config\"}"; ConfigPublishRequest actual = mapper.readValue(json, ConfigPublishRequest.class); assertEquals(DATA_ID, actual.getDataId()); assertEquals(GROUP, actual.getGroup()); assertEquals(TENANT, actual.getTenant()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(CONTENT, actual.getContent()); assertEquals(MD5, actual.getCasMd5()); assertEquals(TAG_PARAM, actual.getAdditionParam(TAG_PARAM)); assertEquals(APP_NAME_PARAM, actual.getAdditionParam(APP_NAME_PARAM)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigQueryRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigQueryRequestTest extends BasedConfigRequestTest { ConfigQueryRequest configQueryRequest; Map headers = new HashMap<>(); String requestId; @BeforeEach void before() { headers.put(Constants.Config.NOTIFY_HEADER, Boolean.TRUE.toString()); configQueryRequest = ConfigQueryRequest.build(DATA_ID, GROUP, TENANT); configQueryRequest.putAllHeader(headers); configQueryRequest.setTag(TAG); requestId = injectRequestUuId(configQueryRequest); } @Test void testIsNotify() { assertTrue(configQueryRequest.isNotify()); } @Override @Test public void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(configQueryRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"dataId\":\"" + DATA_ID)); assertTrue(json.contains("\"group\":\"" + GROUP)); assertTrue(json.contains("\"tenant\":\"" + TENANT)); assertTrue(json.contains("\"tag\":\"" + TAG)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"notify\":\"true\"},\"dataId\":\"test_data\",\"group\":\"group\"," + "\"tenant\":\"test_tenant\",\"notify\":true,\"module\":\"config\",\"tag\":\"tag\"}"; ConfigQueryRequest actual = mapper.readValue(json, ConfigQueryRequest.class); assertEquals(DATA_ID, actual.getDataId()); assertEquals(GROUP, actual.getGroup()); assertEquals(TENANT, actual.getTenant()); assertEquals(TAG, actual.getTag()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/ConfigRemoveRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; 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; class ConfigRemoveRequestTest extends BasedConfigRequestTest { ConfigRemoveRequest configRemoveRequest; String requestId; @BeforeEach void before() { configRemoveRequest = new ConfigRemoveRequest(DATA_ID, GROUP, TENANT, TAG); requestId = injectRequestUuId(configRemoveRequest); } @Override @Test public void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(configRemoveRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"dataId\":\"" + DATA_ID)); assertTrue(json.contains("\"group\":\"" + GROUP)); assertTrue(json.contains("\"tenant\":\"" + TENANT)); assertTrue(json.contains("\"tag\":\"" + TAG)); assertTrue(json.contains("\"requestId\":\"" + requestId)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"dataId\":\"test_data\",\"group\":\"group\",\"tenant\":\"test_tenant\"" + ",\"tag\":\"tag\",\"module\":\"config\"}"; ConfigRemoveRequest actual = mapper.readValue(json, ConfigRemoveRequest.class); assertEquals(DATA_ID, actual.getDataId()); assertEquals(GROUP, actual.getGroup()); assertEquals(TENANT, actual.getTenant()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(TAG, actual.getTag()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/request/cluster/ConfigChangeClusterSyncRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.request.cluster; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.BasedConfigRequestTest; import com.fasterxml.jackson.core.JsonProcessingException; 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; class ConfigChangeClusterSyncRequestTest extends BasedConfigRequestTest { private static final String GRAY_NAME = "test-gray-name"; ConfigChangeClusterSyncRequest configChangeClusterSyncRequest; String requestId; @BeforeEach void before() { configChangeClusterSyncRequest = new ConfigChangeClusterSyncRequest(); configChangeClusterSyncRequest.setDataId(DATA_ID); configChangeClusterSyncRequest.setGroup(GROUP); configChangeClusterSyncRequest.setTenant(TENANT); configChangeClusterSyncRequest.setTag(TAG); configChangeClusterSyncRequest.setBeta(Boolean.TRUE); configChangeClusterSyncRequest.setLastModified(0L); configChangeClusterSyncRequest.setGrayName(GRAY_NAME); configChangeClusterSyncRequest.putAllHeader(HEADERS); requestId = injectRequestUuId(configChangeClusterSyncRequest); } @Override @Test public void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(configChangeClusterSyncRequest); assertTrue(json.contains("\"module\":\"" + Constants.Config.CONFIG_MODULE)); assertTrue(json.contains("\"dataId\":\"" + DATA_ID)); assertTrue(json.contains("\"group\":\"" + GROUP)); assertTrue(json.contains("\"tenant\":\"" + TENANT)); assertTrue(json.contains("\"tag\":\"" + TAG)); assertTrue(json.contains("\"beta\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"lastModified\":" + 0)); assertTrue(json.contains("\"grayName\":\"" + GRAY_NAME)); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{\"header1\":\"test_header1\"},\"requestId\":\"ece89111-3c42-4055-aca4-c95e16ec564b\",\"dataId\":\"test_data\"," + "\"group\":\"group\",\"tenant\":\"test_tenant\"," + "\"tag\":\"tag\",\"lastModified\":0,\"beta\":true,\"grayName\":\"test-gray-name\",\"module\":\"config\"}"; ConfigChangeClusterSyncRequest actual = mapper.readValue(json, ConfigChangeClusterSyncRequest.class); assertEquals(DATA_ID, actual.getDataId()); assertEquals(GROUP, actual.getGroup()); assertEquals(TENANT, actual.getTenant()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getModule()); assertEquals(0L, actual.getLastModified()); assertEquals(GRAY_NAME, actual.getGrayName()); assertTrue(actual.isBeta()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/BasedConfigResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.config.remote.request.BasedConfigRequestTest; import com.alibaba.nacos.api.remote.response.Response; import com.fasterxml.jackson.core.JsonProcessingException; import java.util.UUID; public abstract class BasedConfigResponseTest extends BasedConfigRequestTest { protected String requestId; @Override public void testSerialize() throws JsonProcessingException { } @Override public void testDeserialize() throws JsonProcessingException { } public abstract void testSerializeSuccessResponse() throws JsonProcessingException; public abstract void testSerializeFailResponse() throws JsonProcessingException; protected String injectResponseUuId(Response response) { String uuid = UUID.randomUUID().toString(); response.setRequestId(uuid); return uuid; } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/ClientConfigMetricResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class ClientConfigMetricResponseTest extends BasedConfigResponseTest { ClientConfigMetricResponse clientConfigMetricResponse; Map metric = new HashMap<>(16); @BeforeEach void before() { metric.put("m1", "v1"); clientConfigMetricResponse = new ClientConfigMetricResponse(); clientConfigMetricResponse.setMetrics(metric); clientConfigMetricResponse.putMetric("m2", "v2"); requestId = injectResponseUuId(clientConfigMetricResponse); } @Override @Test public void testSerializeSuccessResponse() throws JsonProcessingException { String json = mapper.writeValueAsString(clientConfigMetricResponse); assertTrue(json.contains("\"success\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"resultCode\":" + ResponseCode.SUCCESS.getCode())); } @Override public void testSerializeFailResponse() throws JsonProcessingException { } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"6ef9237b-24f3-448a-87fc-713f18ee06a1\"," + "\"metrics\":{\"m1\":\"v1\",\"m2\":\"v2\"},\"success\":true}"; ClientConfigMetricResponse actual = mapper.readValue(json, ClientConfigMetricResponse.class); assertTrue(actual.isSuccess()); assertEquals(actual.getResultCode(), ResponseCode.SUCCESS.getCode()); assertEquals(actual.getMetrics(), metric); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/ConfigChangeBatchListenResponseTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.fasterxml.jackson.core.JsonProcessingException; 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; class ConfigChangeBatchListenResponseTest extends BasedConfigResponseTest { ConfigChangeBatchListenResponse configChangeBatchListenResponse; @BeforeEach void before() { configChangeBatchListenResponse = new ConfigChangeBatchListenResponse(); requestId = injectResponseUuId(configChangeBatchListenResponse); configChangeBatchListenResponse.addChangeConfig(DATA_ID, GROUP, TENANT); } @Override @Test public void testSerializeSuccessResponse() throws JsonProcessingException { String json = mapper.writeValueAsString(configChangeBatchListenResponse); assertTrue(json.contains("\"success\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"resultCode\":" + ResponseCode.SUCCESS.getCode())); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains( "\"changedConfigs\":[{\"dataId\":\"test_data\",\"group\":\"group\",\"tenant\":\"test_tenant\"}]")); } @Override @Test public void testSerializeFailResponse() throws JsonProcessingException { ConfigChangeBatchListenResponse configChangeBatchListenResponse = ConfigChangeBatchListenResponse.buildFailResponse( "Fail"); String json = mapper.writeValueAsString(configChangeBatchListenResponse); assertTrue(json.contains("\"resultCode\":" + ResponseCode.FAIL.getCode())); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"message\":\"Fail\"")); assertTrue(json.contains("\"success\":false")); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"061e36b0-c7bd-4fd0-950c-73b13ca1cb2f\"," + "\"changedConfigs\":[{\"group\":\"group\",\"dataId\":\"test_data\",\"tenant\":\"test_tenant\"}],\"success\":true}"; ConfigChangeBatchListenResponse actual = mapper.readValue(json, ConfigChangeBatchListenResponse.class); assertTrue(actual.isSuccess()); assertEquals(ResponseCode.SUCCESS.getCode(), actual.getResultCode()); assertEquals("061e36b0-c7bd-4fd0-950c-73b13ca1cb2f", actual.getRequestId()); assertEquals(TENANT, actual.getChangedConfigs().get(0).getTenant()); assertEquals(GROUP, actual.getChangedConfigs().get(0).getGroup()); assertEquals(DATA_ID, actual.getChangedConfigs().get(0).getDataId()); assertEquals("ConfigContext{group='group', dataId='test_data', tenant='test_tenant'}", actual.getChangedConfigs().get(0).toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/ConfigChangeNotifyResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; public class ConfigChangeNotifyResponseTest extends BasedConfigResponseTest { ConfigChangeNotifyResponse configChangeNotifyResponse; @BeforeEach void before() { configChangeNotifyResponse = new ConfigChangeNotifyResponse(); requestId = injectResponseUuId(configChangeNotifyResponse); } @Override @Test public void testSerializeSuccessResponse() throws JsonProcessingException { String json = mapper.writeValueAsString(configChangeNotifyResponse); assertTrue(json.contains("\"success\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"resultCode\":" + ResponseCode.SUCCESS.getCode())); assertTrue(json.contains("\"errorCode\":0")); } @Override public void testSerializeFailResponse() throws JsonProcessingException { } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/ConfigPublishResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigPublishResponseTest extends BasedConfigResponseTest { ConfigPublishResponse configPublishResponse; @BeforeEach void before() { configPublishResponse = ConfigPublishResponse.buildSuccessResponse(); requestId = injectResponseUuId(configPublishResponse); } @Override @Test public void testSerializeSuccessResponse() throws JsonProcessingException { String json = mapper.writeValueAsString(configPublishResponse); assertTrue(json.contains("\"success\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"resultCode\":" + ResponseCode.SUCCESS.getCode())); assertTrue(json.contains("\"errorCode\":0")); } @Override @Test public void testSerializeFailResponse() throws JsonProcessingException { ConfigPublishResponse configPublishResponse = ConfigPublishResponse.buildFailResponse(500, "Fail"); String json = mapper.writeValueAsString(configPublishResponse); assertTrue(json.contains("\"resultCode\":" + ResponseCode.FAIL.getCode())); assertTrue(json.contains("\"errorCode\":500")); assertTrue(json.contains("\"message\":\"Fail\"")); assertTrue(json.contains("\"success\":false")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/ConfigQueryResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.fasterxml.jackson.core.JsonProcessingException; 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; class ConfigQueryResponseTest extends BasedConfigResponseTest { ConfigQueryResponse configQueryResponse; @BeforeEach void before() { configQueryResponse = ConfigQueryResponse.buildSuccessResponse("success"); configQueryResponse.setContentType("text"); configQueryResponse.setEncryptedDataKey("encryptedKey"); configQueryResponse.setLastModified(1111111L); configQueryResponse.setMd5(MD5); configQueryResponse.setTag(TAG); requestId = injectResponseUuId(configQueryResponse); } @Override @Test public void testSerializeSuccessResponse() throws JsonProcessingException { String json = mapper.writeValueAsString(configQueryResponse); assertTrue(json.contains("\"success\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"resultCode\":" + ResponseCode.SUCCESS.getCode())); assertTrue(json.contains("\"md5\":\"" + MD5 + "\"")); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"content\":\"success\"")); assertTrue(json.contains("\"contentType\":\"text\"")); assertTrue(json.contains("\"lastModified\":1111111")); } @Override @Test public void testSerializeFailResponse() throws JsonProcessingException { ConfigQueryResponse configQueryResponse = ConfigQueryResponse.buildFailResponse(500, "Fail"); String json = mapper.writeValueAsString(configQueryResponse); assertTrue(json.contains("\"resultCode\":" + ResponseCode.FAIL.getCode())); assertTrue(json.contains("\"errorCode\":500")); assertTrue(json.contains("\"message\":\"Fail\"")); assertTrue(json.contains("\"success\":false")); } @Override @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"2239753e-e682-441c-83cf-fb8129ca68a4\"," + "\"content\":\"success\",\"encryptedDataKey\":\"encryptedKey\",\"contentType\":\"text\",\"md5\":\"test_MD5\"," + "\"lastModified\":1111111,\"tag\":\"tag\",\"beta\":false,\"success\":true}\n"; ConfigQueryResponse actual = mapper.readValue(json, ConfigQueryResponse.class); assertTrue(actual.isSuccess()); assertEquals(ResponseCode.SUCCESS.getCode(), actual.getResultCode()); assertEquals("success", actual.getContent()); assertEquals("text", actual.getContentType()); assertEquals("2239753e-e682-441c-83cf-fb8129ca68a4", actual.getRequestId()); assertEquals(MD5, actual.getMd5()); assertEquals(TAG, actual.getTag()); assertEquals("text", actual.getContentType()); assertEquals(1111111L, actual.getLastModified()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/ConfigRemoveResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigRemoveResponseTest extends BasedConfigResponseTest { ConfigRemoveResponse configRemoveResponse; @BeforeEach void before() { configRemoveResponse = ConfigRemoveResponse.buildSuccessResponse(); requestId = injectResponseUuId(configRemoveResponse); } @Override @Test public void testSerializeSuccessResponse() throws JsonProcessingException { String json = mapper.writeValueAsString(configRemoveResponse); assertTrue(json.contains("\"success\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"resultCode\":" + ResponseCode.SUCCESS.getCode())); assertTrue(json.contains("\"errorCode\":0")); } @Override @Test public void testSerializeFailResponse() throws JsonProcessingException { ConfigRemoveResponse configRemoveResponse = ConfigRemoveResponse.buildFailResponse("Fail"); String json = mapper.writeValueAsString(configRemoveResponse); assertTrue(json.contains("\"resultCode\":" + ResponseCode.FAIL.getCode())); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"message\":\"Fail\"")); assertTrue(json.contains("\"success\":false")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/config/remote/response/cluster/ConfigChangeClusterSyncResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.config.remote.response.cluster; import com.alibaba.nacos.api.config.remote.response.BasedConfigResponseTest; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; public class ConfigChangeClusterSyncResponseTest extends BasedConfigResponseTest { ConfigChangeClusterSyncResponse configChangeClusterSyncResponse; @BeforeEach void before() { configChangeClusterSyncResponse = new ConfigChangeClusterSyncResponse(); requestId = injectResponseUuId(configChangeClusterSyncResponse); } @Override @Test public void testSerializeSuccessResponse() throws JsonProcessingException { String json = mapper.writeValueAsString(configChangeClusterSyncResponse); assertTrue(json.contains("\"success\":" + Boolean.TRUE)); assertTrue(json.contains("\"requestId\":\"" + requestId)); assertTrue(json.contains("\"resultCode\":" + ResponseCode.SUCCESS.getCode())); assertTrue(json.contains("\"errorCode\":0")); } @Override public void testSerializeFailResponse() throws JsonProcessingException { } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/exception/NacosExceptionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception; import com.alibaba.nacos.api.common.Constants; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class NacosExceptionTest { @Test void testEmptyConstructor() { NacosException exception = new NacosException(); assertEquals(0, exception.getErrCode()); assertEquals(Constants.NULL, exception.getErrMsg()); assertEquals("ErrCode:0, ErrMsg:", exception.toString()); exception.setErrCode(NacosException.INVALID_PARAM); exception.setErrMsg("test"); assertEquals("ErrCode:400, ErrMsg:test", exception.toString()); } @Test void testConstructorWithErrMsg() { NacosException exception = new NacosException(NacosException.SERVER_ERROR, "test"); assertEquals(NacosException.SERVER_ERROR, exception.getErrCode()); assertEquals("test", exception.getErrMsg()); assertEquals("ErrCode:500, ErrMsg:test", exception.toString()); } @Test void testConstructorWithCause() { NacosException exception = new NacosException(NacosException.SERVER_ERROR, new RuntimeException("cause test")); assertEquals(NacosException.SERVER_ERROR, exception.getErrCode()); assertEquals("cause test", exception.getErrMsg()); assertEquals("ErrCode:500, ErrMsg:cause test", exception.toString()); } @Test void testConstructorWithMultiCauses() { NacosException exception = new NacosException(NacosException.SERVER_ERROR, new RuntimeException("cause test", new RuntimeException("multi"))); assertEquals(NacosException.SERVER_ERROR, exception.getErrCode()); assertEquals("multi", exception.getErrMsg()); assertEquals("ErrCode:500, ErrMsg:multi", exception.toString()); } @Test void testConstructorWithFull() { NacosException exception = new NacosException(NacosException.SERVER_ERROR, "test", new RuntimeException("cause test")); assertEquals(NacosException.SERVER_ERROR, exception.getErrCode()); assertEquals("test", exception.getErrMsg()); assertEquals("ErrCode:500, ErrMsg:test", exception.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/exception/api/NacosApiExceptionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.api; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.model.v2.ErrorCode; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class NacosApiExceptionTest { @Test void testEmptyConstructor() { NacosApiException exception = new NacosApiException(); assertEquals(0, exception.getErrCode()); assertEquals(0, exception.getDetailErrCode()); assertEquals(Constants.NULL, exception.getErrMsg()); assertEquals(Constants.NULL, exception.getErrAbstract()); } @Test void testConstructorWithoutCause() { NacosApiException exception = new NacosApiException(500, ErrorCode.SERVER_ERROR, "test"); assertEquals(500, exception.getErrCode()); assertEquals(ErrorCode.SERVER_ERROR.getCode().intValue(), exception.getDetailErrCode()); assertEquals("test", exception.getErrMsg()); assertEquals(ErrorCode.SERVER_ERROR.getMsg(), exception.getErrAbstract()); } @Test void testConstructorWithCause() { NacosApiException exception = new NacosApiException(500, ErrorCode.SERVER_ERROR, new RuntimeException("cause test"), "test"); assertEquals(500, exception.getErrCode()); assertEquals(ErrorCode.SERVER_ERROR.getCode().intValue(), exception.getDetailErrCode()); assertEquals("test", exception.getErrMsg()); assertEquals(ErrorCode.SERVER_ERROR.getMsg(), exception.getErrAbstract()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/exception/runtime/NacosDeserializationExceptionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.databind.type.SimpleType; import org.junit.jupiter.api.Test; import java.lang.reflect.Type; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class NacosDeserializationExceptionTest { @Test void testEmptyConstructor() { NacosDeserializationException exception = new NacosDeserializationException(); assertEquals(Constants.Exception.DESERIALIZE_ERROR_CODE, exception.getErrCode()); assertNull(exception.getMessage()); assertNull(exception.getTargetClass()); } @Test void testConstructorWithTargetClass() { NacosDeserializationException exception = new NacosDeserializationException( NacosDeserializationExceptionTest.class); assertEquals(Constants.Exception.DESERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals(String.format("errCode: 101, errMsg: Nacos deserialize for class [%s] failed. ", NacosDeserializationExceptionTest.class.getName()), exception.getMessage()); assertEquals(NacosDeserializationExceptionTest.class, exception.getTargetClass()); } @Test void testConstructorWithTargetType() { Type type = SimpleType.constructUnsafe(NacosDeserializationExceptionTest.class); NacosDeserializationException exception = new NacosDeserializationException(type); assertEquals(Constants.Exception.DESERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals( String.format("errCode: 101, errMsg: Nacos deserialize for class [%s] failed. ", type.getTypeName()), exception.getMessage()); assertNull(exception.getTargetClass()); } @Test void testConstructorWithCause() { NacosDeserializationException exception = new NacosDeserializationException(new RuntimeException("test")); assertEquals(Constants.Exception.DESERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals("errCode: 101, errMsg: Nacos deserialize failed. ", exception.getMessage()); assertNull(exception.getTargetClass()); } @Test void testConstructorWithTargetClassAndCause() { NacosDeserializationException exception = new NacosDeserializationException( NacosDeserializationExceptionTest.class, new RuntimeException("test")); assertEquals(Constants.Exception.DESERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals(String.format("errCode: 101, errMsg: Nacos deserialize for class [%s] failed, cause error[%s]. ", NacosDeserializationExceptionTest.class.getName(), "test"), exception.getMessage()); assertEquals(NacosDeserializationExceptionTest.class, exception.getTargetClass()); } @Test void testConstructorWithTargetTypeAndCause() { Type type = SimpleType.constructUnsafe(NacosDeserializationExceptionTest.class); NacosDeserializationException exception = new NacosDeserializationException(type, new RuntimeException("test")); assertEquals(Constants.Exception.DESERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals(String.format("errCode: 101, errMsg: Nacos deserialize for class [%s] failed, cause error[%s]. ", type.getTypeName(), "test"), exception.getMessage()); assertNull(exception.getTargetClass()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/exception/runtime/NacosLoadExceptionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class NacosLoadExceptionTest { @Test void testConstructor() { NacosLoadException exception = new NacosLoadException("test"); assertEquals("test", exception.getMessage()); assertNull(exception.getCause()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/exception/runtime/NacosRuntimeExceptionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; import com.alibaba.nacos.api.exception.NacosException; 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.assertTrue; class NacosRuntimeExceptionTest { @Test void testConstructorWithErrorCode() { NacosRuntimeException exception = new NacosRuntimeException(NacosException.INVALID_PARAM); assertEquals(NacosException.INVALID_PARAM, exception.getErrCode()); assertNull(exception.getMessage()); assertNull(exception.getCause()); } @Test void testConstructorWithErrorCodeAndMsg() { NacosRuntimeException exception = new NacosRuntimeException(NacosException.INVALID_PARAM, "test"); assertEquals(NacosException.INVALID_PARAM, exception.getErrCode()); assertEquals("errCode: 400, errMsg: test ", exception.getMessage()); assertNull(exception.getCause()); } @Test void testConstructorWithErrorCodeAndCause() { NacosRuntimeException exception = new NacosRuntimeException(NacosException.INVALID_PARAM, new RuntimeException("test")); assertEquals(NacosException.INVALID_PARAM, exception.getErrCode()); assertEquals("java.lang.RuntimeException: test", exception.getMessage()); assertTrue(exception.getCause() instanceof RuntimeException); } @Test void testConstructorWithFull() { NacosRuntimeException exception = new NacosRuntimeException(NacosException.INVALID_PARAM, "test", new RuntimeException("cause test")); assertEquals(NacosException.INVALID_PARAM, exception.getErrCode()); assertEquals("errCode: 400, errMsg: test ", exception.getMessage()); assertTrue(exception.getCause() instanceof RuntimeException); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/exception/runtime/NacosSerializationExceptionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.exception.runtime; import com.alibaba.nacos.api.common.Constants; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class NacosSerializationExceptionTest { @Test void testEmptyConstructor() { NacosSerializationException exception = new NacosSerializationException(); assertEquals(Constants.Exception.SERIALIZE_ERROR_CODE, exception.getErrCode()); assertNull(exception.getMessage()); assertNull(exception.getSerializedClass()); } @Test void testConstructorWithSerializedClass() { NacosSerializationException exception = new NacosSerializationException(NacosSerializationExceptionTest.class); assertEquals(Constants.Exception.SERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals(String.format("errCode: 100, errMsg: Nacos serialize for class [%s] failed. ", NacosSerializationExceptionTest.class.getName()), exception.getMessage()); assertEquals(NacosSerializationExceptionTest.class, exception.getSerializedClass()); } @Test void testConstructorWithCause() { NacosSerializationException exception = new NacosSerializationException(new RuntimeException("test")); assertEquals(Constants.Exception.SERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals("errCode: 100, errMsg: Nacos serialize failed. ", exception.getMessage()); assertNull(exception.getSerializedClass()); } @Test void testConstructorWithSerializedClassAndCause() { NacosSerializationException exception = new NacosSerializationException(NacosSerializationExceptionTest.class, new RuntimeException("test")); assertEquals(Constants.Exception.SERIALIZE_ERROR_CODE, exception.getErrCode()); assertEquals(String.format("errCode: 100, errMsg: Nacos serialize for class [%s] failed. ", NacosSerializationExceptionTest.class.getName(), "test"), exception.getMessage()); assertEquals(NacosSerializationExceptionTest.class, exception.getSerializedClass()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/lock/NacosLockFactoryTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.lock.NacosLockService; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class NacosLockFactoryTest { @Test void createAiServiceWithException() { NacosLockService.IS_THROW_EXCEPTION.set(true); assertThrows(NacosException.class, () -> NacosLockFactory.createLockService(new Properties())); } @Test void createAiServiceSuccess() throws NacosException { NacosLockService.IS_THROW_EXCEPTION.set(false); assertNotNull(NacosLockFactory.createLockService(new Properties())); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/lock/model/LockInstanceTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.model; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.LockService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class LockInstanceTest { private LockInstance lockInstance; @BeforeEach void setUp() { lockInstance = new LockInstance(); } @Test void testConstructorWithParameters() { String key = "testKey"; Long expiredTime = 1000L; String lockType = "testType"; LockInstance instance = new LockInstance(key, expiredTime, lockType); assertEquals(key, instance.getKey()); assertEquals(expiredTime, instance.getExpiredTime()); assertEquals(lockType, instance.getLockType()); } @Test void testDefaultConstructor() { LockInstance instance = new LockInstance(); assertNull(instance.getKey()); assertNull(instance.getExpiredTime()); assertNull(instance.getLockType()); assertNull(instance.getParams()); } @Test void testGetAndSetKey() { String key = "testKey"; lockInstance.setKey(key); assertEquals(key, lockInstance.getKey()); } @Test void testGetAndSetExpiredTime() { Long expiredTime = 1000L; lockInstance.setExpiredTime(expiredTime); assertEquals(expiredTime, lockInstance.getExpiredTime()); } @Test void testGetAndSetLockType() { String lockType = "testType"; lockInstance.setLockType(lockType); assertEquals(lockType, lockInstance.getLockType()); } @Test void testGetAndSetParams() { Map params = new HashMap<>(); params.put("param1", "value1"); params.put("param2", "value2"); lockInstance.setParams(params); assertEquals(params, lockInstance.getParams()); } @Test void testLockMethod() throws NacosException { LockService lockService = mock(LockService.class); Boolean expectedResult = true; when(lockService.remoteTryLock(lockInstance)).thenReturn(expectedResult); lockInstance.setKey("testKey"); lockInstance.setLockType("testType"); lockInstance.setExpiredTime(1000L); Boolean result = lockInstance.lock(lockService); assertEquals(expectedResult, result); } @Test void testUnlockMethod() throws NacosException { LockService lockService = mock(LockService.class); Boolean expectedResult = true; when(lockService.remoteReleaseLock(lockInstance)).thenReturn(expectedResult); lockInstance.setKey("testKey"); lockInstance.setLockType("testType"); lockInstance.setExpiredTime(1000L); Boolean result = lockInstance.unLock(lockService); assertEquals(expectedResult, result); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/lock/remote/request/LockOperationRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.remote.request; import com.alibaba.nacos.api.lock.model.LockInstance; import com.alibaba.nacos.api.lock.remote.LockOperationEnum; import com.alibaba.nacos.api.remote.request.BasicRequestTest; 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.assertTrue; class LockOperationRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { LockOperationRequest request = new LockOperationRequest(); request.setRequestId("1"); LockInstance lockInstance = new LockInstance(); lockInstance.setKey("testKey"); lockInstance.setLockType("testType"); lockInstance.setExpiredTime(1000L); request.setLockInstance(lockInstance); request.setLockOperationEnum(LockOperationEnum.ACQUIRE); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"lockInstance\":{")); assertTrue(json.contains("\"key\":\"testKey\"")); assertTrue(json.contains("\"lockType\":\"testType\"")); assertTrue(json.contains("\"expiredTime\":1000")); assertTrue(json.contains("\"lockOperationEnum\":\"ACQUIRE\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"lockInstance\":{\"key\":\"testKey\",\"expiredTime\":1000," + "\"lockType\":\"testType\"},\"lockOperationEnum\":\"ACQUIRE\",\"module\":\"lock\"}"; LockOperationRequest result = mapper.readValue(json, LockOperationRequest.class); assertNotNull(result); assertEquals("1", result.getRequestId()); LockInstance lockInstance = result.getLockInstance(); assertNotNull(lockInstance); assertEquals("testKey", lockInstance.getKey()); assertEquals("testType", lockInstance.getLockType()); assertEquals(Long.valueOf(1000L), lockInstance.getExpiredTime()); assertEquals(LockOperationEnum.ACQUIRE, result.getLockOperationEnum()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/lock/remote/response/LockOperationResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.lock.remote.response; import com.alibaba.nacos.api.remote.request.BasicRequestTest; import com.alibaba.nacos.api.remote.response.ResponseCode; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class LockOperationResponseTest extends BasicRequestTest { @Test void testConstructor() { LockOperationResponse response = new LockOperationResponse(true); assertTrue((Boolean) response.getResult()); response = new LockOperationResponse(false); assertFalse((Boolean) response.getResult()); } @Test void testSuccess() { LockOperationResponse response = LockOperationResponse.success(true); assertTrue((Boolean) response.getResult()); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); } @Test void testFail() { String errorMessage = "test error"; LockOperationResponse response = LockOperationResponse.fail(errorMessage); assertFalse((Boolean) response.getResult()); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(errorMessage, response.getMessage()); } @Test void testSerialize() throws Exception { LockOperationResponse response = new LockOperationResponse(); response.setRequestId("1"); response.setResult(true); String json = mapper.writeValueAsString(response); assertNotNull(json); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"result\":true")); } @Test void testDeserialize() throws Exception { String json = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"result\":true,\"success\":true}"; LockOperationResponse result = mapper.readValue(json, LockOperationResponse.class); assertNotNull(result); assertEquals("1", result.getRequestId()); assertTrue((Boolean) result.getResult()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/PageTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class PageTest { private ObjectMapper mapper; private Page page; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); page = new Page<>(); page.setPagesAvailable(10); page.setPageNumber(1); page.setTotalCount(10); page.setPageItems(Collections.singletonList("test")); } @Test void setPageItems() { Page page = new Page<>(); assertEquals(0, page.getPageItems().size()); page.setPageItems(Collections.singletonList(new Object())); assertEquals(1, page.getPageItems().size()); } @Test void testSerialize() throws Exception { String json = mapper.writeValueAsString(page); assertTrue(json.contains("\"totalCount\":10")); assertTrue(json.contains("\"pageNumber\":1")); assertTrue(json.contains("\"pagesAvailable\":10")); assertTrue(json.contains("\"pageItems\":[\"test\"]")); } @Test void testDeserialize() throws Exception { String json = "{\"totalCount\":10,\"pageNumber\":1,\"pagesAvailable\":10,\"pageItems\":[\"test\"]}"; Page page = mapper.readValue(json, new TypeReference>() { }); assertEquals(10, page.getPagesAvailable()); assertEquals(1, page.getPageNumber()); assertEquals(10, page.getTotalCount()); assertEquals(1, page.getPageItems().size()); assertEquals("test", page.getPageItems().get(0)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/response/ConnectionInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.Date; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConnectionInfoTest { private ObjectMapper mapper; ConnectionInfo connectionInfo; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); ConnectionMetaInfo metaInfo = new ConnectionMetaInfo(); metaInfo.setConnectType("grpc"); metaInfo.setClientIp("127.0.0.1"); metaInfo.setRemoteIp("127.0.0.1"); metaInfo.setRemotePort(8080); metaInfo.setLocalPort(18080); metaInfo.setVersion("3.0.0"); metaInfo.setConnectionId("1739168690942_127.0.0.1_18080"); Date now = new Date(); metaInfo.setCreateTime(now); metaInfo.setLastActiveTime(now.getTime()); metaInfo.setLabels(Collections.singletonMap(Constants.APPNAME, "test")); metaInfo.setAppName("test"); metaInfo.setNamespaceId("public"); connectionInfo = new ConnectionInfo(); connectionInfo.setMetaInfo(metaInfo); connectionInfo.setAbilityTable(Collections.emptyMap()); } @AfterEach void tearDown() { } @Test void testSerialize() throws Exception { String json = mapper.writeValueAsString(connectionInfo); assertTrue(json.contains("\"traced\":false")); assertTrue(json.contains("\"metaInfo\":{")); assertTrue(json.contains("\"connectType\":\"grpc\"")); assertTrue(json.contains("\"clientIp\":\"127.0.0.1\"")); assertTrue(json.contains("\"remoteIp\":\"127.0.0.1\"")); assertTrue(json.contains("\"remotePort\":8080")); assertTrue(json.contains("\"localPort\":18080")); assertTrue(json.contains("\"version\":\"3.0.0\"")); assertTrue(json.contains("\"connectionId\":\"1739168690942_127.0.0.1_18080\"")); assertTrue(json.contains("\"createTime\":" + connectionInfo.getMetaInfo().getCreateTime().getTime())); assertTrue(json.contains("\"lastActiveTime\":" + connectionInfo.getMetaInfo().getLastActiveTime())); assertTrue(json.contains("\"appName\":\"test\"")); assertTrue(json.contains("\"labels\":{")); assertTrue(json.contains("\"AppName\":\"test\"")); assertTrue(json.contains("\"abilityTable\":{}")); assertTrue(json.contains("\"namespaceId\":\"public\"")); } @Test void testDeserialize() throws Exception { // full connection information from server. String json = "{\"traced\":false,\"abilityTable\":{},\"metaInfo\":{\"namespaceId\":\"public\",\"connectType\":\"grpc\"," + "\"clientIp\":\"127.0.0.1\",\"remoteIp\":\"127.0.0.1\",\"remotePort\":8080," + "\"localPort\":18080,\"version\":\"3.0.0\",\"connectionId\":\"1739168690942_127.0.0.1_18080\"," + "\"createTime\":1739170615198,\"lastActiveTime\":1739170615198,\"appName\":\"test\"," + "\"labels\":{\"AppName\":\"test\"},\"appLabels\":{\"ClientVersion\":\"3.0.0\",\"AppName\":\"test\"}," + "\"sdkSource\":false,\"clusterSource\":false},\"connected\":false,\"labels\":{\"AppName\":\"test\"}," + "\"appLabels\":{\"ClientVersion\":\"3.0.0\",\"AppName\":\"test\"}}"; ConnectionInfo actualConnectionInfo = mapper.readValue(json, ConnectionInfo.class); assertEquals(connectionInfo.isTraced(), actualConnectionInfo.isTraced()); assertEquals(connectionInfo.getAbilityTable(), actualConnectionInfo.getAbilityTable()); assertEquals(connectionInfo.getMetaInfo().getConnectType(), actualConnectionInfo.getMetaInfo().getConnectType()); assertEquals(connectionInfo.getMetaInfo().getClientIp(), actualConnectionInfo.getMetaInfo().getClientIp()); assertEquals(connectionInfo.getMetaInfo().getRemoteIp(), actualConnectionInfo.getMetaInfo().getRemoteIp()); assertEquals(connectionInfo.getMetaInfo().getRemotePort(), actualConnectionInfo.getMetaInfo().getRemotePort()); assertEquals(connectionInfo.getMetaInfo().getLocalPort(), actualConnectionInfo.getMetaInfo().getLocalPort()); assertEquals(connectionInfo.getMetaInfo().getVersion(), actualConnectionInfo.getMetaInfo().getVersion()); assertEquals(connectionInfo.getMetaInfo().getConnectionId(), actualConnectionInfo.getMetaInfo().getConnectionId()); assertEquals(1739170615198L, actualConnectionInfo.getMetaInfo().getCreateTime().getTime()); assertEquals(1739170615198L, actualConnectionInfo.getMetaInfo().getLastActiveTime()); assertEquals(connectionInfo.getMetaInfo().getAppName(), actualConnectionInfo.getMetaInfo().getAppName()); assertEquals(connectionInfo.getMetaInfo().getLabels(), actualConnectionInfo.getMetaInfo().getLabels()); assertEquals(connectionInfo.getMetaInfo().getNamespaceId(), actualConnectionInfo.getMetaInfo().getNamespaceId()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/response/InstanceIdGeneratorInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class InstanceIdGeneratorInfoTest { @Test void test() { IdGeneratorInfo vo = new IdGeneratorInfo(); IdGeneratorInfo.IdInfo info = new IdGeneratorInfo.IdInfo(); info.setWorkerId(1L); info.setCurrentId(2L); vo.setResource("test"); vo.setInfo(info); assertEquals(vo.getInfo(), info); assertEquals("test", vo.getResource()); assertEquals(1L, vo.getInfo().getWorkerId().longValue()); assertEquals(2L, vo.getInfo().getCurrentId().longValue()); assertEquals("IdGeneratorVO{resource='test', info=IdInfo{currentId=2, workerId=1}}", vo.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/response/NacosMemberTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import com.alibaba.nacos.api.common.NodeState; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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.assertTrue; class NacosMemberTest { private ObjectMapper mapper; NacosMember member; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); member = new NacosMember(); member.setIp("127.0.0.1"); member.setPort(8080); member.setState(NodeState.UP); member.getExtendInfo().put("testK", "testV"); } @AfterEach void tearDown() { } @Test public void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(member); assertTrue(json.contains("\"ip\":\"127.0.0.1\"")); assertTrue(json.contains("\"port\":8080")); assertTrue(json.contains("\"state\":\"UP\"")); assertTrue(json.contains("\"extendInfo\":{")); assertTrue(json.contains("\"testK\":\"testV\"")); assertTrue(json.contains("\"address\":\"127.0.0.1:8080\"")); } @Test public void testDeserialize() throws JsonProcessingException { String json = "{\"ip\":\"127.0.0.1\",\"port\":8080,\"state\":\"UP\",\"extendInfo\":{\"testK\":\"testV\"},\"address\":\"127.0.0.1:8080\"," + "\"abilities\":{\"remoteAbility\":{\"supportRemoteConnection\":false,\"grpcReportEnabled\":true}," + "\"configAbility\":{\"supportRemoteMetrics\":false},\"namingAbility\":{\"supportJraft\":false}}}"; NacosMember actualMember = mapper.readValue(json, NacosMember.class); assertEquals(member, actualMember); assertEquals(member.getExtendInfo(), actualMember.getExtendInfo()); assertEquals(member.getAbilities(), actualMember.getAbilities()); assertEquals(member.hashCode(), actualMember.hashCode()); assertEquals(member.toString(), actualMember.toString()); } @Test public void testEquals() { assertEquals(member, member); assertNotEquals(member, new Object()); NacosMember member1 = new NacosMember(); member1.setIp(member.getIp()); member1.setPort(member.getPort()); member1.setState(NodeState.DOWN); assertEquals(member, member1); member1.setIp("127.0.0.2"); assertNotEquals(member, member1); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/response/NamespaceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class NamespaceTest { private ObjectMapper mapper; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); } @Test void testNoArgsConstructor() { Namespace namespace = new Namespace(); assertNull(namespace.getNamespace()); assertNull(namespace.getNamespaceShowName()); assertNull(namespace.getNamespaceDesc()); assertEquals(0, namespace.getQuota()); assertEquals(0, namespace.getConfigCount()); assertEquals(0, namespace.getType()); } @Test void testConstructorWithNamespaceAndShowName() { Namespace namespace = new Namespace("testNamespace", "testShowName"); assertEquals("testNamespace", namespace.getNamespace()); assertEquals("testShowName", namespace.getNamespaceShowName()); assertNull(namespace.getNamespaceDesc()); assertEquals(0, namespace.getQuota()); assertEquals(0, namespace.getConfigCount()); assertEquals(0, namespace.getType()); } @Test void testConstructorWithAllParamsExceptDesc() { Namespace namespace = new Namespace("testNamespace", "testShowName", 100, 50, 1); assertEquals("testNamespace", namespace.getNamespace()); assertEquals("testShowName", namespace.getNamespaceShowName()); assertNull(namespace.getNamespaceDesc()); assertEquals(100, namespace.getQuota()); assertEquals(50, namespace.getConfigCount()); assertEquals(1, namespace.getType()); } @Test void testConstructorWithAllParams() { Namespace namespace = new Namespace("testNamespace", "testShowName", "testDesc", 100, 50, 1); assertEquals("testNamespace", namespace.getNamespace()); assertEquals("testShowName", namespace.getNamespaceShowName()); assertEquals("testDesc", namespace.getNamespaceDesc()); assertEquals(100, namespace.getQuota()); assertEquals(50, namespace.getConfigCount()); assertEquals(1, namespace.getType()); } @Test void testGetterSetter() { Namespace namespace = new Namespace(); namespace.setNamespace("testNamespace"); namespace.setNamespaceShowName("testShowName"); namespace.setNamespaceDesc("testDesc"); namespace.setQuota(100); namespace.setConfigCount(50); namespace.setType(1); assertEquals("testNamespace", namespace.getNamespace()); assertEquals("testShowName", namespace.getNamespaceShowName()); assertEquals("testDesc", namespace.getNamespaceDesc()); assertEquals(100, namespace.getQuota()); assertEquals(50, namespace.getConfigCount()); assertEquals(1, namespace.getType()); } @Test void testSerialize() throws Exception { Namespace namespace = new Namespace("testNamespace", "testShowName", "testDesc", 100, 50, 1); String json = mapper.writeValueAsString(namespace); Namespace deserializedNamespace = mapper.readValue(json, Namespace.class); assertEquals(namespace.getNamespace(), deserializedNamespace.getNamespace()); assertEquals(namespace.getNamespaceShowName(), deserializedNamespace.getNamespaceShowName()); assertEquals(namespace.getNamespaceDesc(), deserializedNamespace.getNamespaceDesc()); assertEquals(namespace.getQuota(), deserializedNamespace.getQuota()); assertEquals(namespace.getConfigCount(), deserializedNamespace.getConfigCount()); assertEquals(namespace.getType(), deserializedNamespace.getType()); } @Test void testDeserialize() throws Exception { String json = "{\"namespace\":\"testNamespace\",\"namespaceShowName\":\"testShowName\"," + "\"namespaceDesc\":\"testDesc\",\"quota\":100,\"configCount\":50,\"type\":1}"; Namespace namespace = mapper.readValue(json, Namespace.class); assertEquals("testNamespace", namespace.getNamespace()); assertEquals("testShowName", namespace.getNamespaceShowName()); assertEquals("testDesc", namespace.getNamespaceDesc()); assertEquals(100, namespace.getQuota()); assertEquals(50, namespace.getConfigCount()); assertEquals(1, namespace.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/response/ServerLoaderMetricsTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.response; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServerLoaderMetricsTest { private ObjectMapper mapper; ServerLoaderMetrics serverLoaderMetrics; @BeforeEach void setUp() { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); serverLoaderMetrics = new ServerLoaderMetrics(); Map metrics = new HashMap<>(); metrics.put("conCount", "100"); metrics.put("cpu", "100"); metrics.put("load", "1"); ServerLoaderMetric metric = ServerLoaderMetric.Builder.newBuilder().withAddress("127.0.0.1:8848") .convertFromMap(metrics).build(); serverLoaderMetrics.setDetail(Collections.singletonList(metric)); serverLoaderMetrics.setMemberCount(1); serverLoaderMetrics.setMetricsCount(1); serverLoaderMetrics.setCompleted(true); serverLoaderMetrics.setMax(100); serverLoaderMetrics.setMin(100); serverLoaderMetrics.setAvg(100); serverLoaderMetrics.setThreshold(String.valueOf(serverLoaderMetrics.getAvg() * 1.1d)); serverLoaderMetrics.setTotal(100); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(serverLoaderMetrics); assertTrue(json.contains("\"memberCount\":1")); assertTrue(json.contains("\"metricsCount\":1")); assertTrue(json.contains("\"completed\":true")); assertTrue(json.contains("\"max\":100")); assertTrue(json.contains("\"min\":100")); assertTrue(json.contains("\"avg\":100")); assertTrue(json.contains("\"threshold\":\"110.0")); assertTrue(json.contains("\"total\":100")); assertTrue(json.contains("\"detail\":[")); assertTrue(json.contains("\"address\":\"127.0.0.1:8848\"")); assertTrue(json.contains("\"sdkConCount\":0")); assertTrue(json.contains("\"conCount\":100")); assertTrue(json.contains("\"load\":\"1\"")); assertTrue(json.contains("\"cpu\":\"100\"")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"detail\":[{\"address\":\"127.0.0.1:8848\",\"sdkConCount\":0,\"conCount\":100," + "\"load\":\"1\",\"cpu\":\"100\"}],\"memberCount\":1,\"metricsCount\":1,\"completed\":true," + "\"max\":100,\"min\":100,\"avg\":100,\"threshold\":\"110.00000000000001\",\"total\":100}"; ServerLoaderMetrics metricsInfo1 = mapper.readValue(jsonString, ServerLoaderMetrics.class); assertEquals(serverLoaderMetrics.getMemberCount(), metricsInfo1.getMemberCount()); assertEquals(serverLoaderMetrics.getMetricsCount(), metricsInfo1.getMetricsCount()); assertEquals(serverLoaderMetrics.isCompleted(), metricsInfo1.isCompleted()); assertEquals(serverLoaderMetrics.getMax(), metricsInfo1.getMax()); assertEquals(serverLoaderMetrics.getMin(), metricsInfo1.getMin()); assertEquals(serverLoaderMetrics.getAvg(), metricsInfo1.getAvg()); assertEquals(serverLoaderMetrics.getThreshold(), metricsInfo1.getThreshold()); assertEquals(serverLoaderMetrics.getTotal(), metricsInfo1.getTotal()); assertEquals(serverLoaderMetrics.getDetail().get(0).getAddress(), metricsInfo1.getDetail().get(0).getAddress()); assertEquals(serverLoaderMetrics.getDetail().get(0).getConCount(), metricsInfo1.getDetail().get(0).getConCount()); assertEquals(serverLoaderMetrics.getDetail().get(0).getSdkConCount(), metricsInfo1.getDetail().get(0).getSdkConCount()); assertEquals(serverLoaderMetrics.getDetail().get(0).getCpu(), metricsInfo1.getDetail().get(0).getCpu()); assertEquals(serverLoaderMetrics.getDetail().get(0).getLoad(), metricsInfo1.getDetail().get(0).getLoad()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/v2/ErrorCodeTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.v2; import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class ErrorCodeTest { @Test void testCodeNotSame() { Class errorCodeClass = ErrorCode.class; ErrorCode[] errorCodes = errorCodeClass.getEnumConstants(); Set codeSet = new HashSet(errorCodes.length); for (ErrorCode errorCode : errorCodes) { codeSet.add(errorCode.getCode()); } assertEquals(errorCodes.length, codeSet.size()); } @Test void testGetErrorCode() { // 测试存在的错误码 assertEquals(ErrorCode.SUCCESS, ErrorCode.getErrorCode("SUCCESS")); assertEquals(ErrorCode.PARAMETER_MISSING, ErrorCode.getErrorCode("PARAMETER_MISSING")); assertEquals(ErrorCode.SERVER_ERROR, ErrorCode.getErrorCode("SERVER_ERROR")); // 测试不存在的错误码 assertNull(ErrorCode.getErrorCode("NON_EXISTENT_ERROR")); assertNull(ErrorCode.getErrorCode(null)); assertNull(ErrorCode.getErrorCode("")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/v2/ResultTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.v2; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class ResultTest { @Test void testSuccessEmptyResult() { Result result = Result.success(); assertNull(result.getData()); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals(ErrorCode.SUCCESS.getMsg(), result.getMessage()); } @Test void testSuccessWithData() { Result result = Result.success("test"); assertEquals("test", result.getData()); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals(ErrorCode.SUCCESS.getMsg(), result.getMessage()); } @Test void testFailureMessageResult() { Result result = Result.failure("test"); assertNull(result.getData()); assertEquals(ErrorCode.SERVER_ERROR.getCode(), result.getCode()); assertEquals("test", result.getMessage()); } @Test void testFailureWithoutData() { Result result = Result.failure(ErrorCode.DATA_ACCESS_ERROR); assertNull(result.getData()); assertEquals(ErrorCode.DATA_ACCESS_ERROR.getCode(), result.getCode()); assertEquals(ErrorCode.DATA_ACCESS_ERROR.getMsg(), result.getMessage()); } @Test void testFailureWithData() { Result result = Result.failure(ErrorCode.DATA_ACCESS_ERROR, "error"); assertEquals("error", result.getData()); assertEquals(ErrorCode.DATA_ACCESS_ERROR.getCode(), result.getCode()); assertEquals(ErrorCode.DATA_ACCESS_ERROR.getMsg(), result.getMessage()); } @Test void testFailureWithCodeMessageAndData() { Result result = Result.failure(10001, "custom error", "errorData"); assertEquals("errorData", result.getData()); assertEquals(Integer.valueOf(10001), result.getCode()); assertEquals("custom error", result.getMessage()); } @Test void testToString() { Result result = Result.success("test"); assertEquals("Result{errorCode=0, message='success', data=test}", result.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/model/v2/SupportedLanguageTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.model.v2; 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 SupportedLanguageTest { @Test void testGetLanguage() { assertEquals("zh-CN", SupportedLanguage.ZH_CN.getLanguage()); assertEquals("en-US", SupportedLanguage.EN_US.getLanguage()); } @Test void testIsSupported() { // 测试支持的语言 assertTrue(SupportedLanguage.isSupported("zh-CN")); assertTrue(SupportedLanguage.isSupported("en-US")); // 测试不支持的语言 assertFalse(SupportedLanguage.isSupported("fr-FR")); assertFalse(SupportedLanguage.isSupported("ja-JP")); assertFalse(SupportedLanguage.isSupported(null)); assertFalse(SupportedLanguage.isSupported("")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/NamingFactoryTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.naming.NacosNamingService; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class NamingFactoryTest { @Test void testCreateNamingServiceByPropertiesSuccess() throws NacosException { NacosNamingService.IS_THROW_EXCEPTION.set(false); assertNotNull(NamingFactory.createNamingService(new Properties())); } @Test void testCreateNamingServiceByPropertiesFailure() { NacosNamingService.IS_THROW_EXCEPTION.set(true); assertThrows(NacosException.class, () -> NamingFactory.createNamingService(new Properties())); } @Test void testCreateNamingServiceByServerAddrSuccess() throws NacosException { NacosNamingService.IS_THROW_EXCEPTION.set(false); assertNotNull(NamingFactory.createNamingService("localhost:8848")); } @Test void testCreateNamingServiceByServerAddrFailure() { NacosNamingService.IS_THROW_EXCEPTION.set(true); assertThrows(NacosException.class, () -> NamingFactory.createNamingService("localhost:8848")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/ability/ClientNamingAbilityTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.ability; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientNamingAbilityTest { @Test void testGetAndSet() { ClientNamingAbility ability = new ClientNamingAbility(); assertFalse(ability.isSupportDeltaPush()); assertFalse(ability.isSupportRemoteMetric()); ability.setSupportDeltaPush(true); ability.setSupportRemoteMetric(true); assertTrue(ability.isSupportDeltaPush()); assertTrue(ability.isSupportRemoteMetric()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/ability/ServerNamingAbilityTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.ability; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; 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; class ServerNamingAbilityTest { private static ObjectMapper jacksonMapper; @BeforeAll static void setUpClass() throws Exception { jacksonMapper = new ObjectMapper(); jacksonMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); jacksonMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testDeserializeServerNamingAbilityForNonExistItem() throws JsonProcessingException { String nonExistItemJson = "{\"exampleAbility\":false}"; ServerNamingAbility actual = jacksonMapper.readValue(nonExistItemJson, ServerNamingAbility.class); assertFalse(actual.isSupportJraft()); } @Test void testEquals() throws JsonProcessingException { ServerNamingAbility expected = new ServerNamingAbility(); expected.setSupportJraft(true); String serializeJson = jacksonMapper.writeValueAsString(expected); ServerNamingAbility actual = jacksonMapper.readValue(serializeJson, ServerNamingAbility.class); assertEquals(expected, actual); actual = new ServerNamingAbility(); assertNotEquals(expected, actual); actual.setSupportJraft(true); assertEquals(expected, actual); } @Test void testEqualsForOneObject() { ServerNamingAbility ability = new ServerNamingAbility(); assertEquals(ability, ability); } @Test void testEqualsForOtherAbility() { ServerNamingAbility ability = new ServerNamingAbility(); assertNotEquals(ability, new ClientNamingAbility()); } @Test void testHashCode() throws JsonProcessingException { ServerNamingAbility expected = new ServerNamingAbility(); expected.setSupportJraft(true); String serializeJson = jacksonMapper.writeValueAsString(expected); ServerNamingAbility actual = jacksonMapper.readValue(serializeJson, ServerNamingAbility.class); assertEquals(expected, actual); actual = new ServerNamingAbility(); assertNotEquals(expected, actual); actual.setSupportJraft(true); assertEquals(expected.hashCode(), actual.hashCode()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/listener/AbstractFuzzyWatchEventWatcherTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertNull; class AbstractFuzzyWatchEventWatcherTest { AbstractFuzzyWatchEventWatcher fuzzyWatchEventWatcher; @BeforeEach void setUp() { fuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { } }; } @Test void getExecutor() { assertNull(fuzzyWatchEventWatcher.getExecutor()); } @Test void onPatternOverLimit() { assertDoesNotThrow(fuzzyWatchEventWatcher::onPatternOverLimit); } @Test void onServiceReachUpLimit() { assertDoesNotThrow(fuzzyWatchEventWatcher::onServiceReachUpLimit); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/listener/FuzzyWatchChangeEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; 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; class FuzzyWatchChangeEventTest { @BeforeEach void setUp() throws Exception { } @Test void testFuzzyWatchChangeEventWithEmptyConstructor() { FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(); assertNull(event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getNamespace()); assertNull(event.getChangeType()); assertNull(event.getSyncType()); } @Test void testFuzzyWatchChangeEventWithFullConstructor() { FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent("service", "group", "namespace", "ADD_SERVICE", "FUZZY_WATCH_INIT_NOTIFY"); assertEquals("service", event.getServiceName()); assertEquals("group", event.getGroupName()); assertEquals("namespace", event.getNamespace()); assertEquals("ADD_SERVICE", event.getChangeType()); assertEquals("FUZZY_WATCH_INIT_NOTIFY", event.getSyncType()); } @Test void testToString() { FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent("service", "group", "namespace", "ADD_SERVICE", "FUZZY_WATCH_INIT_NOTIFY"); String expected = "FuzzyWatchChangeEvent{serviceName='service', groupName='group', namespace='namespace'," + " changeType='ADD_SERVICE', syncType='FUZZY_WATCH_INIT_NOTIFY'}"; assertEquals(expected, event.toString()); } @Test void testFuzzyWatchChangeEventWithNullValues() { FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(null, null, null, null, null); assertNull(event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getNamespace()); assertNull(event.getChangeType()); assertNull(event.getSyncType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/listener/NamingEventTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.listener; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingEventTest { private MockNamingEventListener eventListener; @BeforeEach void setUp() throws Exception { eventListener = new MockNamingEventListener(); } @Test void testNamingEventWithSimpleConstructor() { NamingEvent event = new NamingEvent("serviceName", Collections.EMPTY_LIST); assertEquals("serviceName", event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getClusters()); assertTrue(event.getInstances().isEmpty()); eventListener.onEvent(event); assertNull(event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getClusters()); assertNull(event.getInstances()); } @Test void testNamingEventWithFullConstructor() { NamingEvent event = new NamingEvent("serviceName", "group", "clusters", Collections.EMPTY_LIST); assertEquals("serviceName", event.getServiceName()); assertEquals("group", event.getGroupName()); assertEquals("clusters", event.getClusters()); assertTrue(event.getInstances().isEmpty()); eventListener.onEvent(event); assertNull(event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getClusters()); assertNull(event.getInstances()); } private static class MockNamingEventListener extends AbstractEventListener { @Override public void onEvent(Event event) { assertNull(getExecutor()); NamingEvent namingEvent = (NamingEvent) event; namingEvent.setServiceName(null); namingEvent.setGroupName(null); namingEvent.setClusters(null); namingEvent.setInstances(null); } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/ClusterTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.Collections; 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 ClusterTest { private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSetAndGet() { Cluster actual = new Cluster(); assertNull(actual.getName()); assertNull(actual.getServiceName()); assertEquals(Tcp.TYPE, actual.getHealthChecker().getType()); assertEquals(80, actual.getDefaultPort()); assertEquals(80, actual.getDefaultCheckPort()); assertTrue(actual.isUseIpPort4Check()); assertTrue(actual.getMetadata().isEmpty()); actual.setName("cluster"); actual.setServiceName("group@@service"); actual.setHealthChecker(new Http()); actual.setDefaultPort(81); actual.setDefaultCheckPort(82); actual.setUseIpPort4Check(false); actual.setMetadata(Collections.singletonMap("a", "a")); assertEquals("cluster", actual.getName()); assertEquals("group@@service", actual.getServiceName()); assertEquals(Http.TYPE, actual.getHealthChecker().getType()); assertEquals(81, actual.getDefaultPort()); assertEquals(82, actual.getDefaultCheckPort()); assertFalse(actual.isUseIpPort4Check()); assertFalse(actual.getMetadata().isEmpty()); assertTrue(actual.getMetadata().containsKey("a")); assertEquals("a", actual.getMetadata().get("a")); } @Test void testJsonSerialize() throws JsonProcessingException { Cluster actual = new Cluster("cluster"); actual.setServiceName("group@@service"); actual.setHealthChecker(new Http()); actual.setDefaultPort(81); actual.setDefaultCheckPort(82); actual.setUseIpPort4Check(false); actual.setMetadata(Collections.singletonMap("a", "a")); String json = mapper.writeValueAsString(actual); assertTrue(json.contains("\"serviceName\":\"group@@service\"")); assertTrue(json.contains("\"name\":\"cluster\"")); assertTrue(json.contains("\"type\":\"HTTP\"")); assertTrue(json.contains("\"defaultPort\":81")); assertTrue(json.contains("\"defaultCheckPort\":82")); assertTrue(json.contains("\"useIpPort4Check\":false")); assertTrue(json.contains("\"metadata\":{\"a\":\"a\"}")); } @Test void testJsonDeserialize() throws JsonProcessingException { String json = "{\"serviceName\":\"group@@service\",\"name\":\"cluster\"," + "\"healthChecker\":{\"type\":\"HTTP\",\"path\":\"\",\"headers\":\"\",\"expectedResponseCode\":200}," + "\"defaultPort\":81,\"defaultCheckPort\":82,\"useIpPort4Check\":false,\"metadata\":{\"a\":\"a\"}}"; Cluster actual = mapper.readValue(json, Cluster.class); assertEquals("cluster", actual.getName()); assertEquals("group@@service", actual.getServiceName()); assertEquals(Http.TYPE, actual.getHealthChecker().getType()); assertEquals(81, actual.getDefaultPort()); assertEquals(82, actual.getDefaultCheckPort()); assertFalse(actual.isUseIpPort4Check()); assertFalse(actual.getMetadata().isEmpty()); assertTrue(actual.getMetadata().containsKey("a")); assertEquals("a", actual.getMetadata().get("a")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/InstanceTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class InstanceTest { private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSetAndGet() { Instance instance = new Instance(); assertNull(instance.getInstanceId()); assertNull(instance.getIp()); assertEquals(0, instance.getPort()); assertEquals(1.0D, instance.getWeight(), 0.1); assertTrue(instance.isHealthy()); assertTrue(instance.isEnabled()); assertTrue(instance.isEphemeral()); assertNull(instance.getClusterName()); assertNull(instance.getServiceName()); assertTrue(instance.getMetadata().isEmpty()); setInstance(instance); checkInstance(instance); } @Test void testJsonSerialize() throws JsonProcessingException { Instance instance = new Instance(); setInstance(instance); String actual = mapper.writeValueAsString(instance); assertTrue(actual.contains("\"instanceId\":\"id\"")); assertTrue(actual.contains("\"ip\":\"1.1.1.1\"")); assertTrue(actual.contains("\"port\":1000")); assertTrue(actual.contains("\"weight\":100.0")); assertTrue(actual.contains("\"healthy\":false")); assertTrue(actual.contains("\"enabled\":false")); assertTrue(actual.contains("\"ephemeral\":false")); assertTrue(actual.contains("\"clusterName\":\"cluster\"")); assertTrue(actual.contains("\"serviceName\":\"group@@serviceName\"")); assertTrue(actual.contains("\"metadata\":{\"a\":\"b\"}")); assertTrue(actual.contains("\"instanceHeartBeatInterval\":5000")); assertTrue(actual.contains("\"instanceHeartBeatTimeOut\":15000")); assertTrue(actual.contains("\"ipDeleteTimeout\":30000")); } @Test void testJsonDeserialize() throws JsonProcessingException { String json = "{\"instanceId\":\"id\",\"ip\":\"1.1.1.1\",\"port\":1000,\"weight\":100.0,\"healthy\":false," + "\"enabled\":false,\"ephemeral\":false,\"clusterName\":\"cluster\"," + "\"serviceName\":\"group@@serviceName\",\"metadata\":{\"a\":\"b\"},\"instanceHeartBeatInterval\":5000," + "\"instanceHeartBeatTimeOut\":15000,\"ipDeleteTimeout\":30000}"; Instance instance = mapper.readValue(json, Instance.class); checkInstance(instance); } @Test void testCheckClusterNameFormat() { Instance instance = new Instance(); instance.setClusterName("demo"); assertEquals("demo", instance.getClusterName()); } @Test void testToInetAddr() { Instance instance = new Instance(); setInstance(instance); assertEquals("1.1.1.1:1000", instance.toInetAddr()); } @Test void testContainsMetadata() { Instance instance = new Instance(); assertFalse(instance.containsMetadata("a")); instance.setMetadata(null); assertFalse(instance.containsMetadata("a")); instance.addMetadata("a", "b"); assertTrue(instance.containsMetadata("a")); } @Test void testGetInstanceIdGenerator() { Instance instance = new Instance(); assertEquals(Constants.DEFAULT_INSTANCE_ID_GENERATOR, instance.getInstanceIdGenerator()); instance.addMetadata(PreservedMetadataKeys.INSTANCE_ID_GENERATOR, "test"); assertEquals("test", instance.getInstanceIdGenerator()); } @Test void testEquals() { Instance actual = new Instance(); setInstance(actual); actual.setMetadata(new HashMap<>()); actual.addMetadata("a", "b"); assertNotEquals(actual, new Object()); Instance expected = new Instance(); setInstance(expected); expected.setMetadata(new HashMap<>()); expected.addMetadata("a", "b"); assertEquals(actual, expected); expected.addMetadata("a", "c"); assertNotEquals(actual, expected); } @Test void testValidateSuccess() throws Exception { Instance instance = new Instance(); instance.setIp("1.1.1.1"); instance.setPort(8080); // 验证应该成功,不会抛出异常 instance.validate(); } @Test void testValidateFailureWithEmptyIp() { Instance instance = new Instance(); instance.setPort(8080); // IP为空,验证应该失败 assertThrows(NacosApiException.class, instance::validate); } @Test void testValidateFailureWithInvalidPort() { Instance instance = new Instance(); instance.setIp("1.1.1.1"); instance.setPort(99999); // 无效端口 > 65535 // 端口无效,验证应该失败 assertThrows(NacosApiException.class, instance::validate); Instance instance2 = new Instance(); instance2.setIp("1.1.1.1"); instance2.setPort(-1); // 无效端口 < 0 // 端口无效,验证应该失败 assertThrows(NacosApiException.class, instance2::validate); } @Test void testFillDefaultValue() throws NacosApiException { Instance instance = new Instance(); // 初始时 clusterName 为 null assertNull(instance.getClusterName()); // 调用私有方法 fillDefaultValue 是通过 validate 方法间接测试 instance.setIp("1.1.1.1"); instance.setPort(8080); instance.validate(); // clusterName 应该被设置为默认值 assertEquals(Constants.DEFAULT_CLUSTER_NAME, instance.getClusterName()); } @Test void testFillDefaultValueWithExistingClusterName() throws NacosApiException { Instance instance = new Instance(); instance.setClusterName("test-cluster"); instance.setIp("1.1.1.1"); instance.setPort(8080); // 调用 validate 会触发 fillDefaultValue instance.validate(); // clusterName 应该保持原值 assertEquals("test-cluster", instance.getClusterName()); } private void setInstance(Instance instance) { instance.setInstanceId("id"); instance.setIp("1.1.1.1"); instance.setPort(1000); instance.setWeight(100); instance.setHealthy(false); instance.setEnabled(false); instance.setEphemeral(false); instance.setClusterName("cluster"); instance.setServiceName("group@@serviceName"); instance.setMetadata(Collections.singletonMap("a", "b")); } private void checkInstance(Instance instance) { assertEquals("id", instance.getInstanceId()); assertEquals("1.1.1.1", instance.getIp()); assertEquals(1000, instance.getPort()); assertEquals(100D, instance.getWeight(), 0.1); assertFalse(instance.isHealthy()); assertFalse(instance.isEnabled()); assertFalse(instance.isEphemeral()); assertEquals("cluster", instance.getClusterName()); assertEquals("group@@serviceName", instance.getServiceName()); assertFalse(instance.getMetadata().isEmpty()); assertTrue(instance.containsMetadata("a")); assertEquals("b", instance.getMetadata().get("a")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/ListViewTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.LinkedList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class ListViewTest { @Test void testToString() { List data = new LinkedList<>(); data.add("1"); data.add("2"); data.add("3"); ListView listView = new ListView<>(); listView.setData(data); listView.setCount(data.size()); String actual = listView.toString(); assertEquals("ListView{data=[1, 2, 3], count=3}", actual); } @Test void testSetAndGet() { ListView listView = new ListView<>(); assertEquals(0, listView.getCount()); assertNull(listView.getData()); listView.setCount(1); listView.setData(Collections.singletonList("1")); assertEquals(1, listView.getCount()); assertEquals(1, listView.getData().size()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/ServiceInfoTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.utils.StringUtils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class ServiceInfoTest { private ObjectMapper mapper; private ServiceInfo serviceInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); serviceInfo = new ServiceInfo("G@@testName", "testClusters"); } @Test void testSerialize() throws JsonProcessingException { String actual = mapper.writeValueAsString(serviceInfo); assertTrue(actual.contains("\"name\":\"G@@testName\"")); assertTrue(actual.contains("\"clusters\":\"testClusters\"")); assertTrue(actual.contains("\"cacheMillis\":1000")); assertTrue(actual.contains("\"hosts\":[]")); assertTrue(actual.contains("\"lastRefTime\":0")); assertTrue(actual.contains("\"checksum\":\"\"")); assertTrue(actual.contains("\"valid\":true")); assertTrue(actual.contains("\"allIps\":false")); assertFalse(actual.contains("jsonFromServer")); assertFalse(actual.contains("key")); assertFalse(actual.contains("keyEncoded")); } @Test void testDeserialize() throws IOException { String example = "{\"name\":\"G@@testName\",\"clusters\":\"testClusters\",\"cacheMillis\":1000,\"hosts\":[]," + "\"lastRefTime\":0,\"checksum\":\"\",\"allIps\":false,\"valid\":true,\"groupName\":\"\"}"; ServiceInfo actual = mapper.readValue(example, ServiceInfo.class); assertEquals("G@@testName", actual.getName()); assertEquals(0, actual.ipCount()); assertEquals("testClusters", actual.getClusters()); assertEquals("", actual.getChecksum()); assertEquals("", actual.getGroupName()); assertEquals(1000, actual.getCacheMillis()); assertEquals(0, actual.getLastRefTime()); assertTrue(actual.expired()); assertTrue(actual.getHosts().isEmpty()); assertTrue(actual.isValid()); assertFalse(actual.isAllIps()); } @Test void testGetKey() { String key = serviceInfo.getKey(); assertEquals("G@@testName@@testClusters", key); assertEquals("G@@testName@@testClusters", serviceInfo.toString()); } @Test void testGetKeyEncode() { String key = serviceInfo.getKeyEncoded(); String encodeName = null; try { encodeName = URLEncoder.encode("G@@testName", "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } assertEquals(key, ServiceInfo.getKey(encodeName, "testClusters")); } @Test void testGetKeyWithException() { try (MockedStatic mockedStatic = Mockito.mockStatic(URLEncoder.class)) { mockedStatic.when(() -> URLEncoder.encode(Mockito.anyString(), Mockito.anyString())) .thenThrow(new UnsupportedEncodingException()); String key = serviceInfo.getKeyEncoded(); assertEquals(key, ServiceInfo.getKey("G@@testName", "testClusters")); } } @Test void testServiceInfoConstructor() { String key1 = "group@@name"; String key2 = "group@@name@@c2"; ServiceInfo s1 = new ServiceInfo(key1); ServiceInfo s2 = new ServiceInfo(key2); assertEquals(key1, s1.getKey()); assertEquals(key2, s2.getKey()); } @Test void testServiceInfoConstructorWithError() { assertThrows(IllegalArgumentException.class, () -> { String key1 = "name"; ServiceInfo s1 = new ServiceInfo(key1); }); } @Test void testValidateForAllIps() { serviceInfo.setAllIps(true); assertTrue(serviceInfo.validate()); } @Test void testValidateForNullHosts() { serviceInfo.setHosts(null); assertFalse(serviceInfo.validate()); } @Test void testValidateForEmptyHosts() { serviceInfo.setHosts(Collections.EMPTY_LIST); assertFalse(serviceInfo.validate()); } @Test void testValidateForUnhealthyHosts() { Instance instance = new Instance(); instance.setHealthy(false); serviceInfo.addHost(instance); assertFalse(serviceInfo.validate()); } @Test void testValidateForBothUnhealthyAndHealthyHosts() { List instanceList = new LinkedList<>(); Instance instance = new Instance(); instanceList.add(instance); instance = new Instance(); instance.setHealthy(false); instanceList.add(instance); serviceInfo.addAllHosts(instanceList); assertTrue(serviceInfo.validate()); } @Test void testFromKey() { String key1 = "group@@name"; String key2 = "group@@name@@c2"; ServiceInfo s1 = ServiceInfo.fromKey(key1); ServiceInfo s2 = ServiceInfo.fromKey(key2); assertEquals(key1, s1.getKey()); assertEquals(key2, s2.getKey()); } @Test void testSetAndGet() throws JsonProcessingException { serviceInfo.setReachProtectionThreshold(true); serviceInfo.setJsonFromServer(mapper.writeValueAsString(serviceInfo)); ServiceInfo actual = mapper.readValue(serviceInfo.getJsonFromServer(), ServiceInfo.class); assertEquals(StringUtils.EMPTY, actual.getJsonFromServer()); assertTrue(actual.isReachProtectionThreshold()); } @Test void testGetKeyWithoutClusters() { // 测试带groupName的情况 ServiceInfo serviceInfo1 = new ServiceInfo("group@@name", "cluster"); assertEquals("group@@name", serviceInfo1.getKeyWithoutClusters()); // 测试不带groupName的情况 ServiceInfo serviceInfo2 = new ServiceInfo("name", "cluster"); assertEquals("name", serviceInfo2.getKeyWithoutClusters()); // 测试name中已经包含@@的情况 ServiceInfo serviceInfo3 = new ServiceInfo("group@@name@@cluster", ""); assertEquals("group@@name@@cluster", serviceInfo3.getKeyWithoutClusters()); } @Test void testCloneBasicFields() { // Setup original ServiceInfo with all fields ServiceInfo original = new ServiceInfo("testGroup@@testName", "testClusters"); original.setCacheMillis(2000L); original.setLastRefTime(1234567890L); original.setChecksum("testChecksum"); original.setAllIps(true); original.setReachProtectionThreshold(true); original.setJsonFromServer("testJson"); // Clone ServiceInfo cloned = original.clone(); // Verify it's a different object assertNotSame(original, cloned); // Verify all basic fields are copied assertEquals(original.getName(), cloned.getName()); assertEquals(original.getGroupName(), cloned.getGroupName()); assertEquals(original.getClusters(), cloned.getClusters()); assertEquals(original.getCacheMillis(), cloned.getCacheMillis()); assertEquals(original.getLastRefTime(), cloned.getLastRefTime()); assertEquals(original.getChecksum(), cloned.getChecksum()); assertEquals(original.isAllIps(), cloned.isAllIps()); assertEquals(original.isReachProtectionThreshold(), cloned.isReachProtectionThreshold()); assertEquals(original.getJsonFromServer(), cloned.getJsonFromServer()); } @Test void testCloneWithNullHosts() { final ServiceInfo original = new ServiceInfo("testGroup@@testName", "testClusters"); original.setHosts(null); ServiceInfo cloned = original.clone(); assertNotSame(original, cloned); // Clone method initializes hosts to empty list even if original is null assertTrue(cloned.getHosts().isEmpty()); } @Test void testCloneWithEmptyHosts() { ServiceInfo original = new ServiceInfo("testGroup@@testName", "testClusters"); original.setHosts(new LinkedList<>()); ServiceInfo cloned = original.clone(); assertNotSame(original, cloned); assertNotSame(original.getHosts(), cloned.getHosts()); assertTrue(cloned.getHosts().isEmpty()); } @Test void testCloneWithHosts() { // Setup original ServiceInfo with hosts final ServiceInfo original = new ServiceInfo("testGroup@@testName", "testClusters"); Instance instance1 = new Instance(); instance1.setInstanceId("instance1"); instance1.setIp("192.168.1.1"); instance1.setPort(8080); instance1.setWeight(1.0); instance1.setHealthy(true); instance1.setEnabled(true); instance1.setEphemeral(true); instance1.setClusterName("cluster1"); instance1.setServiceName("service1"); Instance instance2 = new Instance(); instance2.setInstanceId("instance2"); instance2.setIp("192.168.1.2"); instance2.setPort(8081); instance2.setWeight(2.0); instance2.setHealthy(false); instance2.setEnabled(false); instance2.setEphemeral(false); instance2.setClusterName("cluster2"); instance2.setServiceName("service2"); original.addHost(instance1); original.addHost(instance2); // Clone ServiceInfo cloned = original.clone(); // Verify it's a different object assertNotSame(original, cloned); // Verify hosts list is different assertNotSame(original.getHosts(), cloned.getHosts()); assertEquals(original.getHosts().size(), cloned.getHosts().size()); // Verify each host is a different object but with same values for (int i = 0; i < original.getHosts().size(); i++) { Instance originalHost = original.getHosts().get(i); Instance clonedHost = cloned.getHosts().get(i); assertNotSame(originalHost, clonedHost); assertEquals(originalHost.getInstanceId(), clonedHost.getInstanceId()); assertEquals(originalHost.getIp(), clonedHost.getIp()); assertEquals(originalHost.getPort(), clonedHost.getPort()); assertEquals(originalHost.getWeight(), clonedHost.getWeight()); assertEquals(originalHost.isHealthy(), clonedHost.isHealthy()); assertEquals(originalHost.isEnabled(), clonedHost.isEnabled()); assertEquals(originalHost.isEphemeral(), clonedHost.isEphemeral()); assertEquals(originalHost.getClusterName(), clonedHost.getClusterName()); assertEquals(originalHost.getServiceName(), clonedHost.getServiceName()); } } @Test void testCloneWithHostsMetadata() { final ServiceInfo original = new ServiceInfo("testGroup@@testName", "testClusters"); Instance instance = new Instance(); instance.setIp("192.168.1.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put("key1", "value1"); metadata.put("key2", "value2"); instance.setMetadata(metadata); original.addHost(instance); // Clone ServiceInfo cloned = original.clone(); // Verify metadata is deep copied Instance originalHost = original.getHosts().get(0); Instance clonedHost = cloned.getHosts().get(0); assertNotSame(originalHost.getMetadata(), clonedHost.getMetadata()); assertEquals(originalHost.getMetadata(), clonedHost.getMetadata()); assertEquals(originalHost.getMetadata().size(), clonedHost.getMetadata().size()); assertEquals("value1", clonedHost.getMetadata().get("key1")); assertEquals("value2", clonedHost.getMetadata().get("key2")); } @Test void testCloneWithHostsNullMetadata() { final ServiceInfo original = new ServiceInfo("testGroup@@testName", "testClusters"); Instance instance = new Instance(); instance.setIp("192.168.1.1"); instance.setPort(8080); instance.setMetadata(null); original.addHost(instance); ServiceInfo cloned = original.clone(); Instance clonedHost = cloned.getHosts().get(0); // Instance metadata is initialized to empty HashMap by default // When clone method doesn't set metadata (because original is null), // the cloned Instance keeps its default empty HashMap assertTrue(clonedHost.getMetadata() != null && clonedHost.getMetadata().isEmpty()); } @Test void testCloneModificationDoesNotAffectOriginal() { ServiceInfo original = new ServiceInfo("testGroup@@testName", "testClusters"); original.setCacheMillis(1000L); original.setAllIps(false); Instance instance = new Instance(); instance.setIp("192.168.1.1"); instance.setPort(8080); Map metadata = new HashMap<>(); metadata.put("key1", "value1"); instance.setMetadata(metadata); original.addHost(instance); // Clone ServiceInfo cloned = original.clone(); // Modify cloned object cloned.setCacheMillis(2000L); cloned.setAllIps(true); cloned.setName("modifiedName"); cloned.getHosts().get(0).setIp("10.0.0.1"); cloned.getHosts().get(0).getMetadata().put("key2", "value2"); // Verify original is not affected assertEquals(1000L, original.getCacheMillis()); assertFalse(original.isAllIps()); assertEquals("testGroup@@testName", original.getName()); assertEquals("192.168.1.1", original.getHosts().get(0).getIp()); assertEquals(1, original.getHosts().get(0).getMetadata().size()); assertFalse(original.getHosts().get(0).getMetadata().containsKey("key2")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/ServiceTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.api.selector.Selector; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class ServiceTest { @Test void testSetAndGet() { Service service = new Service(); assertNull(service.getName()); assertNull(service.getGroupName()); assertEquals(0.0f, service.getProtectThreshold(), 0.1); assertTrue(service.getMetadata().isEmpty()); service.setName("service"); service.setGroupName("group"); service.setProtectThreshold(1.0f); HashMap metadata = new HashMap<>(); service.setMetadata(metadata); service.addMetadata("a", "b"); assertEquals("service", service.getName()); assertEquals("group", service.getGroupName()); assertEquals(1.0f, service.getProtectThreshold(), 0.1); assertEquals(1, service.getMetadata().size()); assertEquals("b", service.getMetadata().get("a")); } @Test void testToString() { Service service = new Service(); service.setName("service"); service.setGroupName("group"); service.setProtectThreshold(1.0f); service.setMetadata(Collections.singletonMap("a", "b")); } @Test void testNamespaceId() { Service service = new Service(); assertNull(service.getNamespaceId()); service.setNamespaceId("namespace"); assertEquals("namespace", service.getNamespaceId()); } @Test void testEphemeral() { Service service = new Service(); // 默认值应为 false assertEquals(false, service.isEphemeral()); service.setEphemeral(true); assertEquals(true, service.isEphemeral()); } @Test void testSelector() { Service service = new Service(); // 默认选择器应该是 NoneSelector assertNotNull(service.getSelector()); assertTrue(service.getSelector() instanceof NoneSelector); Selector selector = new NoneSelector(); service.setSelector(selector); assertEquals(selector, service.getSelector()); } @Test void testFillDefaultValue() { Service service = new Service(); // 初始时 namespaceId 和 groupName 都是 null assertNull(service.getNamespaceId()); assertNull(service.getGroupName()); // 调用 fillDefaultValue 方法 service.fillDefaultValue(); // 应该被设置为默认值 assertEquals(Constants.DEFAULT_NAMESPACE_ID, service.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, service.getGroupName()); } @Test void testValidateSuccess() throws NacosApiException { Service service = new Service(); service.setName("service"); // 验证应该成功,不会抛出异常 service.validate(); } @Test void testValidateFailure() { Service service = new Service(); // 没有设置 name,验证应该失败 assertThrows(NacosApiException.class, () -> { service.validate(); }); } @Test void testValidateWithFillDefaultValue() throws NacosApiException { Service service = new Service(); service.setName("service"); // namespaceId 和 groupName 未设置 // 验证时会调用 fillDefaultValue service.validate(); // 验证后应该填充默认值 assertEquals(Constants.DEFAULT_NAMESPACE_ID, service.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, service.getGroupName()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/builder/InstanceBuilderTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.builder; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class InstanceBuilderTest { private static final String SERVICE_NAME = "testService"; private static final String CLUSTER_NAME = "testCluster"; private static final String INSTANCE_ID = "ID"; private static final String IP = "127.0.0.1"; private static final int PORT = 8848; private static final double WEIGHT = 2.0; private static final boolean HEALTHY = false; private static final boolean ENABLED = false; private static final boolean EPHEMERAL = false; private static final String META_KEY = "key"; private static final String META_VALUE = "value"; @Test void testBuildFullInstance() { InstanceBuilder builder = InstanceBuilder.newBuilder(); Instance actual = builder.setServiceName(SERVICE_NAME).setClusterName(CLUSTER_NAME).setInstanceId(INSTANCE_ID).setIp(IP) .setPort(PORT).setWeight(WEIGHT).setHealthy(HEALTHY).setEnabled(ENABLED).setEphemeral(EPHEMERAL) .addMetadata(META_KEY, META_VALUE).build(); assertEquals(actual.getServiceName(), SERVICE_NAME); assertEquals(actual.getClusterName(), CLUSTER_NAME); assertEquals(actual.getInstanceId(), INSTANCE_ID); assertEquals(actual.getIp(), IP); assertEquals(actual.getPort(), PORT); assertEquals(actual.getWeight(), WEIGHT); assertEquals(actual.isHealthy(), HEALTHY); assertEquals(actual.isEnabled(), ENABLED); assertEquals(actual.isEphemeral(), EPHEMERAL); assertEquals(actual.getMetadata().size(), 1); assertEquals(actual.getMetadata().get(META_KEY), META_VALUE); } @Test void testBuildInstanceWithoutNewMetadata() { InstanceBuilder builder = InstanceBuilder.newBuilder(); Map metadata = new HashMap<>(); metadata.put("test", "test"); Instance actual = builder.setMetadata(metadata).build(); assertNull(actual.getServiceName()); assertNull(actual.getClusterName()); assertNull(actual.getInstanceId()); assertNull(actual.getIp()); assertEquals(actual.getPort(), 0); assertEquals(actual.getWeight(), 1.0); assertTrue(actual.isHealthy()); assertTrue(actual.isEnabled()); assertTrue(actual.isEphemeral()); assertEquals(1, actual.getMetadata().size()); } @Test void testBuildEmptyInstance() { InstanceBuilder builder = InstanceBuilder.newBuilder(); Instance actual = builder.build(); assertNull(actual.getServiceName()); assertNull(actual.getClusterName()); assertNull(actual.getInstanceId()); assertNull(actual.getIp()); assertEquals(actual.getPort(), 0); assertEquals(actual.getWeight(), 1.0); assertTrue(actual.isHealthy()); assertTrue(actual.isEnabled()); assertTrue(actual.isEphemeral()); assertTrue(actual.getMetadata().isEmpty()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/healthcheck/AbstractHealthCheckerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class AbstractHealthCheckerTest { private final ObjectMapper objectMapper = new ObjectMapper(); @BeforeEach void setUp() { objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); objectMapper.registerSubtypes(new NamedType(TestChecker.class, TestChecker.TYPE)); } @Test void testSerialize() throws JsonProcessingException { TestChecker testChecker = new TestChecker(); testChecker.setTestValue(""); String actual = objectMapper.writeValueAsString(testChecker); assertTrue(actual.contains("\"testValue\":\"\"")); assertTrue(actual.contains("\"type\":\"TEST\"")); } @Test void testDeserialize() throws IOException { String testChecker = "{\"type\":\"TEST\",\"testValue\":\"\"}"; TestChecker actual = objectMapper.readValue(testChecker, TestChecker.class); assertEquals("", actual.getTestValue()); assertEquals(TestChecker.TYPE, actual.getType()); } @Test void testClone() throws CloneNotSupportedException { AbstractHealthChecker none = new AbstractHealthChecker.None().clone(); assertEquals(AbstractHealthChecker.None.class, none.getClass()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/healthcheck/HealthCheckTypeTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Mysql; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp; import org.junit.jupiter.api.Test; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class HealthCheckTypeTest { @Test void testOfHealthCheckerClassForBuildInType() { assertEquals(Tcp.class, HealthCheckType.ofHealthCheckerClass("TCP")); assertEquals(Http.class, HealthCheckType.ofHealthCheckerClass("HTTP")); assertEquals(Mysql.class, HealthCheckType.ofHealthCheckerClass("MYSQL")); assertEquals(AbstractHealthChecker.None.class, HealthCheckType.ofHealthCheckerClass("NONE")); } @Test void testOfHealthCheckerClassForExtendType() { HealthCheckType.registerHealthChecker(TestChecker.TYPE, TestChecker.class); assertEquals(TestChecker.class, HealthCheckType.ofHealthCheckerClass(TestChecker.TYPE)); } @Test void testOfHealthCheckerClassForNonExistType() { assertNull(HealthCheckType.ofHealthCheckerClass("non-exist")); } @Test void testGetLoadedHealthCheckerClasses() { HealthCheckType.registerHealthChecker(TestChecker.TYPE, TestChecker.class); List> actual = HealthCheckType.getLoadedHealthCheckerClasses(); assertEquals(5, actual.size()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/healthcheck/HealthCheckerFactoryTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.api.exception.runtime.NacosSerializationException; import com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp; import org.junit.jupiter.api.BeforeAll; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class HealthCheckerFactoryTest { @BeforeAll static void beforeClass() { HealthCheckerFactory.registerSubType(new TestChecker()); } @Test void testSerialize() { Tcp tcp = new Tcp(); String actual = HealthCheckerFactory.serialize(tcp); assertTrue(actual.contains("\"type\":\"TCP\"")); } @Test void testSerializeExtend() { TestChecker testChecker = new TestChecker(); String actual = HealthCheckerFactory.serialize(testChecker); assertTrue(actual.contains("\"type\":\"TEST\"")); } @Test void testDeserialize() { String tcpString = "{\"type\":\"TCP\"}"; AbstractHealthChecker actual = HealthCheckerFactory.deserialize(tcpString); assertEquals(Tcp.class, actual.getClass()); } @Test void testDeserializeExtend() { String tcpString = "{\"type\":\"TEST\",\"testValue\":null}"; AbstractHealthChecker actual = HealthCheckerFactory.deserialize(tcpString); assertEquals(TestChecker.class, actual.getClass()); } @Test void testSerializeNoRegister() { NoRegisterHealthChecker noRegister = new NoRegisterHealthChecker(); assertFalse(HealthCheckerFactory.serialize(noRegister).contains("no register")); } @Test void testDeserializeNoRegister() { String tcpString = "{\"type\":\"no register\",\"testValue\":null}"; AbstractHealthChecker actual = HealthCheckerFactory.deserialize(tcpString); assertEquals(AbstractHealthChecker.None.class, actual.getClass()); } @Test void testSerializeFailure() { assertThrows(NacosSerializationException.class, () -> { SelfDependHealthChecker selfDependHealthChecker = new SelfDependHealthChecker(); System.out.println(HealthCheckerFactory.serialize(selfDependHealthChecker)); }); } @Test void testDeserializeFailure() { assertThrows(NacosDeserializationException.class, () -> { String errorString = "{\"type\"=\"TCP\"}"; System.out.println(HealthCheckerFactory.deserialize(errorString)); }); } @Test void testCreateNoneHealthChecker() { assertEquals(AbstractHealthChecker.None.class, HealthCheckerFactory.createNoneHealthChecker().getClass()); } private static class NoRegisterHealthChecker extends AbstractHealthChecker { private static final long serialVersionUID = 9020783491111797559L; private String testValue; protected NoRegisterHealthChecker() { super("no register"); } public String getTestValue() { return testValue; } public void setTestValue(String testValue) { this.testValue = testValue; } @Override public AbstractHealthChecker clone() throws CloneNotSupportedException { return null; } } private static class SelfDependHealthChecker extends AbstractHealthChecker { private static final long serialVersionUID = 876677992848225965L; public SelfDependHealthChecker self = this; protected SelfDependHealthChecker() { super("self depend"); } @Override public AbstractHealthChecker clone() throws CloneNotSupportedException { return null; } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/healthcheck/TestChecker.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.annotation.JsonTypeInfo.Id; public class TestChecker extends AbstractHealthChecker { @JsonTypeInfo(use = Id.NAME, property = "type") public static final String TYPE = "TEST"; private static final long serialVersionUID = 2472091207760970225L; private String testValue; public TestChecker() { super(TYPE); } public String getTestValue() { return testValue; } public void setTestValue(String testValue) { this.testValue = testValue; } @Override public AbstractHealthChecker clone() throws CloneNotSupportedException { return null; } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/healthcheck/impl/HttpTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Map; 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; class HttpTest { private ObjectMapper objectMapper; private Http http; @BeforeEach void setUp() { objectMapper = new ObjectMapper(); http = new Http(); } @Test void testGetExpectedResponseCodeWithEmpty() { http.setHeaders(""); assertTrue(http.getCustomHeaders().isEmpty()); } @Test void testGetExpectedResponseCodeWithoutEmpty() { http.setHeaders("x:a|y:"); Map actual = http.getCustomHeaders(); assertFalse(actual.isEmpty()); assertEquals(1, actual.size()); assertEquals("a", actual.get("x")); } @Test void testSerialize() throws JsonProcessingException { http.setHeaders("x:a|y:"); http.setPath("/x"); String actual = objectMapper.writeValueAsString(http); assertTrue(actual.contains("\"path\":\"/x\"")); assertTrue(actual.contains("\"type\":\"HTTP\"")); assertTrue(actual.contains("\"headers\":\"x:a|y:\"")); assertTrue(actual.contains("\"expectedResponseCode\":200")); } @Test void testDeserialize() throws IOException { String testChecker = "{\"type\":\"HTTP\",\"path\":\"/x\",\"headers\":\"x:a|y:\",\"expectedResponseCode\":200}"; Http actual = objectMapper.readValue(testChecker, Http.class); assertEquals("x:a|y:", actual.getHeaders()); assertEquals("/x", actual.getPath()); assertEquals(200, actual.getExpectedResponseCode()); assertEquals("x:a|y:", actual.getHeaders()); assertEquals(Http.TYPE, actual.getType()); } @Test void testClone() throws CloneNotSupportedException { Http cloned = http.clone(); assertEquals(http.hashCode(), cloned.hashCode()); assertEquals(http, cloned); } @Test void testNotEquals() throws CloneNotSupportedException { assertNotEquals(http, new Tcp()); Http cloned = http.clone(); cloned.setPath("aaa"); assertNotEquals(http, cloned); cloned = http.clone(); cloned.setHeaders("aaa"); assertNotEquals(http, cloned); cloned = http.clone(); cloned.setExpectedResponseCode(500); assertNotEquals(http, cloned); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/healthcheck/impl/MysqlTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck.impl; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class MysqlTest { private ObjectMapper objectMapper; private Mysql mysql; @BeforeEach void setUp() throws Exception { mysql = new Mysql(); mysql.setUser("user"); mysql.setPwd("pwd"); mysql.setCmd("cmd"); objectMapper = new ObjectMapper(); } @Test void testSerialize() throws JsonProcessingException { String actual = objectMapper.writeValueAsString(mysql); assertTrue(actual.contains("\"user\":\"user\"")); assertTrue(actual.contains("\"type\":\"MYSQL\"")); assertTrue(actual.contains("\"pwd\":\"pwd\"")); assertTrue(actual.contains("\"cmd\":\"cmd\"")); } @Test void testDeserialize() throws IOException { String testChecker = "{\"type\":\"MYSQL\",\"user\":\"user\",\"pwd\":\"pwd\",\"cmd\":\"cmd\"}"; Mysql actual = objectMapper.readValue(testChecker, Mysql.class); assertEquals("cmd", actual.getCmd()); assertEquals("pwd", actual.getPwd()); assertEquals("user", actual.getUser()); assertEquals(Mysql.TYPE, actual.getType()); } @Test void testClone() throws CloneNotSupportedException { Mysql cloned = mysql.clone(); assertEquals(mysql.hashCode(), cloned.hashCode()); assertEquals(mysql, cloned); } @Test void testNotEquals() throws CloneNotSupportedException { assertNotEquals(mysql, new Tcp()); Mysql cloned = mysql.clone(); cloned.setUser("aaa"); assertNotEquals(mysql, cloned); cloned = mysql.clone(); cloned.setPwd("aaa"); assertNotEquals(mysql, cloned); cloned = mysql.clone(); cloned.setCmd("aaa"); assertNotEquals(mysql, cloned); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/healthcheck/impl/TcpTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.healthcheck.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class TcpTest { @Test void testClone() throws CloneNotSupportedException { Tcp original = new Tcp(); Tcp cloned = original.clone(); assertEquals(original.hashCode(), cloned.hashCode()); assertEquals(original, cloned); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientPublisherInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientPublisherInfoTest { private ObjectMapper mapper; private ClientPublisherInfo clientPublisherInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); clientPublisherInfo = new ClientPublisherInfo(); clientPublisherInfo.setClientId("clientId"); clientPublisherInfo.setIp("1.1.1.1"); clientPublisherInfo.setPort(8080); clientPublisherInfo.setClusterName("clusterName"); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(clientPublisherInfo); assertTrue(json.contains("\"clientId\":\"clientId\"")); assertTrue(json.contains("\"ip\":\"1.1.1.1\"")); assertTrue(json.contains("\"port\":8080")); assertTrue(json.contains("\"clusterName\":\"clusterName\"")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"clientId\":\"clientId\",\"ip\":\"1.1.1.1\",\"port\":8080,\"clusterName\":\"clusterName\"}"; ClientPublisherInfo clientPublisherInfo1 = mapper.readValue(jsonString, ClientPublisherInfo.class); assertEquals(clientPublisherInfo.getClientId(), clientPublisherInfo1.getClientId()); assertEquals(clientPublisherInfo.getIp(), clientPublisherInfo1.getIp()); assertEquals(clientPublisherInfo.getPort(), clientPublisherInfo1.getPort()); assertEquals(clientPublisherInfo.getClusterName(), clientPublisherInfo1.getClusterName()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientServiceInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientServiceInfoTest { private ObjectMapper mapper; private ClientServiceInfo clientServiceInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); clientServiceInfo = new ClientServiceInfo(); clientServiceInfo.setNamespaceId("namespaceId"); clientServiceInfo.setGroupName("groupName"); clientServiceInfo.setServiceName("serviceName"); ClientPublisherInfo publisherInfo = new ClientPublisherInfo(); publisherInfo.setClientId("publisherId"); publisherInfo.setIp("1.1.1.1"); publisherInfo.setPort(8080); publisherInfo.setClusterName("publisherCluster"); clientServiceInfo.setPublisherInfo(publisherInfo); ClientSubscriberInfo subscriberInfo = new ClientSubscriberInfo(); subscriberInfo.setClientId("subscriberId"); subscriberInfo.setAppName("subscriberApp"); subscriberInfo.setAgent("subscriberAgent"); subscriberInfo.setAddress("1.1.1.1:8080"); clientServiceInfo.setSubscriberInfo(subscriberInfo); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(clientServiceInfo); assertTrue(json.contains("\"namespaceId\":\"namespaceId\"")); assertTrue(json.contains("\"groupName\":\"groupName\"")); assertTrue(json.contains("\"serviceName\":\"serviceName\"")); assertTrue(json.contains("\"publisherInfo\":{")); assertTrue(json.contains("\"subscriberInfo\":{")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"namespaceId\":\"namespaceId\",\"groupName\":\"groupName\",\"serviceName\":\"serviceName\"," + "\"publisherInfo\":{\"clientId\":\"publisherId\",\"ip\":\"1.1.1.1\",\"port\":8080,\"clusterName\":\"publisherCluster\"}," + "\"subscriberInfo\":{\"clientId\":\"subscriberId\",\"appName\":\"subscriberApp\",\"agent\":\"subscriberAgent\"" + ",\"address\":\"1.1.1.1:8080\"}}"; ClientServiceInfo clientServiceInfo1 = mapper.readValue(jsonString, ClientServiceInfo.class); assertEquals(clientServiceInfo.getNamespaceId(), clientServiceInfo1.getNamespaceId()); assertEquals(clientServiceInfo.getGroupName(), clientServiceInfo1.getGroupName()); assertEquals(clientServiceInfo.getServiceName(), clientServiceInfo1.getServiceName()); assertEquals(clientServiceInfo.getPublisherInfo().getClientId(), clientServiceInfo1.getPublisherInfo().getClientId()); assertEquals(clientServiceInfo.getPublisherInfo().getIp(), clientServiceInfo1.getPublisherInfo().getIp()); assertEquals(clientServiceInfo.getPublisherInfo().getPort(), clientServiceInfo1.getPublisherInfo().getPort()); assertEquals(clientServiceInfo.getPublisherInfo().getClusterName(), clientServiceInfo1.getPublisherInfo().getClusterName()); assertEquals(clientServiceInfo.getSubscriberInfo().getClientId(), clientServiceInfo1.getSubscriberInfo().getClientId()); assertEquals(clientServiceInfo.getSubscriberInfo().getAppName(), clientServiceInfo1.getSubscriberInfo().getAppName()); assertEquals(clientServiceInfo.getSubscriberInfo().getAgent(), clientServiceInfo1.getSubscriberInfo().getAgent()); assertEquals(clientServiceInfo.getSubscriberInfo().getAddress(), clientServiceInfo1.getSubscriberInfo().getAddress()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientSubscriberInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientSubscriberInfoTest { private ObjectMapper mapper; private ClientSubscriberInfo clientSubscriberInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); clientSubscriberInfo = new ClientSubscriberInfo(); clientSubscriberInfo.setClientId("clientId"); clientSubscriberInfo.setAppName("appName"); clientSubscriberInfo.setAgent("agent"); clientSubscriberInfo.setAddress("1.1.1.1:8080"); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(clientSubscriberInfo); assertTrue(json.contains("\"clientId\":\"clientId\"")); assertTrue(json.contains("\"appName\":\"appName\"")); assertTrue(json.contains("\"agent\":\"agent\"")); assertTrue(json.contains("\"address\":\"1.1.1.1:8080\"")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"clientId\":\"clientId\",\"appName\":\"appName\",\"agent\":\"agent\",\"address\":\"1.1.1.1:8080\"}"; ClientSubscriberInfo clientSubscriberInfo1 = mapper.readValue(jsonString, ClientSubscriberInfo.class); assertEquals(clientSubscriberInfo.getClientId(), clientSubscriberInfo1.getClientId()); assertEquals(clientSubscriberInfo.getAppName(), clientSubscriberInfo1.getAppName()); assertEquals(clientSubscriberInfo.getAgent(), clientSubscriberInfo1.getAgent()); assertEquals(clientSubscriberInfo.getAddress(), clientSubscriberInfo1.getAddress()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClientSummaryInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientSummaryInfoTest { private ObjectMapper mapper; private ClientSummaryInfo clientSummaryInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); clientSummaryInfo = new ClientSummaryInfo(); clientSummaryInfo.setClientId("clientId"); clientSummaryInfo.setEphemeral(true); clientSummaryInfo.setLastUpdatedTime(1000L); clientSummaryInfo.setClientType("connection"); clientSummaryInfo.setConnectType("grpc"); clientSummaryInfo.setAppName("appName"); clientSummaryInfo.setVersion("version"); clientSummaryInfo.setClientIp("1.1.1.1"); clientSummaryInfo.setClientPort(8080); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(clientSummaryInfo); assertTrue(json.contains("\"clientId\":\"clientId\"")); assertTrue(json.contains("\"ephemeral\":true")); assertTrue(json.contains("\"lastUpdatedTime\":1000")); assertTrue(json.contains("\"clientType\":\"connection\"")); assertTrue(json.contains("\"connectType\":\"grpc\"")); assertTrue(json.contains("\"appName\":\"appName\"")); assertTrue(json.contains("\"version\":\"version\"")); assertTrue(json.contains("\"clientIp\":\"1.1.1.1\"")); assertTrue(json.contains("\"clientPort\":8080")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"clientId\":\"clientId\",\"ephemeral\":true,\"lastUpdatedTime\":1000," + "\"clientType\":\"connection\",\"connectType\":\"grpc\",\"appName\":\"appName\"," + "\"version\":\"version\",\"clientIp\":\"1.1.1.1\",\"clientPort\":8080}"; ClientSummaryInfo clientSummaryInfo1 = mapper.readValue(jsonString, ClientSummaryInfo.class); assertEquals(clientSummaryInfo.getClientId(), clientSummaryInfo1.getClientId()); assertEquals(clientSummaryInfo.isEphemeral(), clientSummaryInfo1.isEphemeral()); assertEquals(clientSummaryInfo.getLastUpdatedTime(), clientSummaryInfo1.getLastUpdatedTime()); assertEquals(clientSummaryInfo.getClientType(), clientSummaryInfo1.getClientType()); assertEquals(clientSummaryInfo.getConnectType(), clientSummaryInfo1.getConnectType()); assertEquals(clientSummaryInfo.getAppName(), clientSummaryInfo1.getAppName()); assertEquals(clientSummaryInfo.getVersion(), clientSummaryInfo1.getVersion()); assertEquals(clientSummaryInfo.getClientIp(), clientSummaryInfo1.getClientIp()); assertEquals(clientSummaryInfo.getClientPort(), clientSummaryInfo1.getClientPort()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/ClusterInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ClusterInfoTest { @Test void testValidateWithEmptyClusterName() throws NacosApiException { ClusterInfo clusterInfo = new ClusterInfo(); clusterInfo.setClusterName(""); clusterInfo.setHealthChecker(new AbstractHealthChecker.None()); clusterInfo.validate(); assertEquals(Constants.DEFAULT_CLUSTER_NAME, clusterInfo.getClusterName()); } @Test void testValidateWithNullClusterName() throws NacosApiException { ClusterInfo clusterInfo = new ClusterInfo(); clusterInfo.setClusterName(null); clusterInfo.setHealthChecker(new AbstractHealthChecker.None()); clusterInfo.validate(); assertEquals(Constants.DEFAULT_CLUSTER_NAME, clusterInfo.getClusterName()); } @Test void testValidateWithValidClusterName() throws NacosApiException { ClusterInfo clusterInfo = new ClusterInfo(); clusterInfo.setClusterName("test-cluster"); clusterInfo.setHealthChecker(new AbstractHealthChecker.None()); clusterInfo.validate(); assertEquals("test-cluster", clusterInfo.getClusterName()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/InstanceMetadataBatchResultTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class InstanceMetadataBatchResultTest { private ObjectMapper mapper; private InstanceMetadataBatchResult instanceMetadataBatchResult; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); instanceMetadataBatchResult = new InstanceMetadataBatchResult(); instanceMetadataBatchResult.setUpdated(Collections.singletonList("1.1.1.1")); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(instanceMetadataBatchResult); assertTrue(json.contains("\"updated\":[\"1.1.1.1\"]")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"updated\":[\"1.1.1.1\"]}"; InstanceMetadataBatchResult metricsInfo1 = mapper.readValue(jsonString, InstanceMetadataBatchResult.class); assertEquals(instanceMetadataBatchResult.getUpdated(), metricsInfo1.getUpdated()); } @Test void testConstructorWithParameter() { InstanceMetadataBatchResult result = new InstanceMetadataBatchResult(Collections.singletonList("2.2.2.2")); assertEquals(1, result.getUpdated().size()); assertEquals("2.2.2.2", result.getUpdated().get(0)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/MetricsInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class MetricsInfoTest { private ObjectMapper mapper; private MetricsInfo metricsInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); metricsInfo = new MetricsInfo(); metricsInfo.setStatus("UP"); metricsInfo.setClientCount(10); metricsInfo.setInstanceCount(100); metricsInfo.setServiceCount(20); metricsInfo.setSubscribeCount(200); metricsInfo.setConnectionBasedClientCount(8); metricsInfo.setResponsibleClientCount(8); metricsInfo.setEphemeralIpPortClientCount(2); metricsInfo.setPersistentIpPortClientCount(0); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(metricsInfo); assertTrue(json.contains("\"status\":\"UP\"")); assertTrue(json.contains("\"serviceCount\":20")); assertTrue(json.contains("\"instanceCount\":100")); assertTrue(json.contains("\"subscribeCount\":200")); assertTrue(json.contains("\"clientCount\":10")); assertTrue(json.contains("\"connectionBasedClientCount\":8")); assertTrue(json.contains("\"ephemeralIpPortClientCount\":2")); assertTrue(json.contains("\"persistentIpPortClientCount\":0")); assertTrue(json.contains("\"responsibleClientCount\":8")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"status\":\"UP\",\"serviceCount\":20,\"instanceCount\":100,\"subscribeCount\":200," + "\"clientCount\":10,\"connectionBasedClientCount\":8,\"ephemeralIpPortClientCount\":2," + "\"persistentIpPortClientCount\":0,\"responsibleClientCount\":8}\n"; MetricsInfo metricsInfo1 = mapper.readValue(jsonString, MetricsInfo.class); assertEquals(metricsInfo.getStatus(), metricsInfo1.getStatus()); assertEquals(metricsInfo.getClientCount(), metricsInfo1.getClientCount()); assertEquals(metricsInfo.getInstanceCount(), metricsInfo1.getInstanceCount()); assertEquals(metricsInfo.getServiceCount(), metricsInfo1.getServiceCount()); assertEquals(metricsInfo.getSubscribeCount(), metricsInfo1.getSubscribeCount()); assertEquals(metricsInfo.getConnectionBasedClientCount(), metricsInfo1.getConnectionBasedClientCount()); assertEquals(metricsInfo.getResponsibleClientCount(), metricsInfo1.getResponsibleClientCount()); assertEquals(metricsInfo.getEphemeralIpPortClientCount(), metricsInfo1.getEphemeralIpPortClientCount()); assertEquals(metricsInfo.getPersistentIpPortClientCount(), metricsInfo1.getPersistentIpPortClientCount()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/ServiceDetailInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServiceDetailInfoTest { private ObjectMapper mapper; private ServiceDetailInfo serviceDetailInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); serviceDetailInfo = new ServiceDetailInfo(); serviceDetailInfo.setNamespaceId("testNs"); serviceDetailInfo.setGroupName("testG"); serviceDetailInfo.setServiceName("testS"); serviceDetailInfo.setEphemeral(false); serviceDetailInfo.setProtectThreshold(0.5f); Map metadata = new HashMap<>(); metadata.put("testKey", "testValue"); serviceDetailInfo.setMetadata(metadata); ClusterInfo clusterInfo = new ClusterInfo(); clusterInfo.setClusterName("testC"); clusterInfo.setHealthChecker(new AbstractHealthChecker.None()); clusterInfo.setMetadata(metadata); clusterInfo.setHosts(Collections.emptyList()); clusterInfo.setHealthyCheckPort(8080); clusterInfo.setUseInstancePortForCheck(false); Map clusterMap = new HashMap<>(); clusterMap.put("testC", clusterInfo); serviceDetailInfo.setClusterMap(clusterMap); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(serviceDetailInfo); assertTrue(json.contains("\"namespaceId\":\"testNs\"")); assertTrue(json.contains("\"groupName\":\"testG\"")); assertTrue(json.contains("\"serviceName\":\"testS\"")); assertTrue(json.contains("\"ephemeral\":false")); assertTrue(json.contains("\"protectThreshold\":0.5")); assertTrue(json.contains("\"metadata\":{\"testKey\":\"testValue\"}")); assertTrue(json.contains("\"selector\":null")); assertTrue(json.contains("\"clusterMap\":{\"testC\":{")); assertTrue(json.contains("\"clusterName\":\"testC\"")); assertTrue(json.contains("\"healthChecker\":{\"type\":\"NONE\"}")); assertTrue(json.contains("\"hosts\":[]")); assertTrue(json.contains("\"healthyCheckPort\":8080")); assertTrue(json.contains("\"useInstancePortForCheck\":false")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"namespaceId\":\"testNs\",\"serviceName\":\"testS\",\"groupName\":\"testG\"," + "\"clusterMap\":{\"testC\":{\"clusterName\":\"testC\",\"healthChecker\":{\"type\":\"NONE\"}," + "\"metadata\":{\"testKey\":\"testValue\"},\"hosts\":[],\"healthyCheckPort\":8080,\"useInstancePortForCheck\":false}}," + "\"metadata\":{\"testKey\":\"testValue\"}," + "\"protectThreshold\":0.5,\"selector\":null,\"ephemeral\":false}"; ServiceDetailInfo serviceDetailInfo1 = mapper.readValue(jsonString, ServiceDetailInfo.class); assertEquals(serviceDetailInfo.getNamespaceId(), serviceDetailInfo1.getNamespaceId()); assertEquals(serviceDetailInfo.getGroupName(), serviceDetailInfo1.getGroupName()); assertEquals(serviceDetailInfo.getServiceName(), serviceDetailInfo1.getServiceName()); assertEquals(serviceDetailInfo.isEphemeral(), serviceDetailInfo1.isEphemeral()); assertEquals(serviceDetailInfo.getProtectThreshold(), serviceDetailInfo1.getProtectThreshold()); assertEquals(serviceDetailInfo.getMetadata(), serviceDetailInfo1.getMetadata()); assertEquals(serviceDetailInfo.getSelector(), serviceDetailInfo1.getSelector()); assertEquals(serviceDetailInfo.getClusterMap().size(), serviceDetailInfo1.getClusterMap().size()); assertEquals(serviceDetailInfo.getClusterMap().keySet(), serviceDetailInfo1.getClusterMap().keySet()); for (Map.Entry entry : serviceDetailInfo.getClusterMap().entrySet()) { ClusterInfo clusterInfo = entry.getValue(); ClusterInfo clusterInfo1 = serviceDetailInfo1.getClusterMap().get(entry.getKey()); assertEquals(clusterInfo.getClusterName(), clusterInfo1.getClusterName()); assertEquals(clusterInfo.getHealthChecker().getType(), clusterInfo1.getHealthChecker().getType()); assertEquals(clusterInfo.getMetadata(), clusterInfo1.getMetadata()); assertEquals(clusterInfo.getHosts(), clusterInfo1.getHosts()); assertEquals(clusterInfo.getHealthyCheckPort(), clusterInfo1.getHealthyCheckPort()); assertEquals(clusterInfo.isUseInstancePortForCheck(), clusterInfo1.isUseInstancePortForCheck()); } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/ServiceViewTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServiceViewTest { private ObjectMapper mapper; private ServiceView serviceView; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); serviceView = new ServiceView(); serviceView.setName("service"); serviceView.setGroupName("group"); serviceView.setClusterCount(2); serviceView.setIpCount(10); serviceView.setHealthyInstanceCount(8); serviceView.setTriggerFlag("flag"); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(serviceView); assertTrue(json.contains("\"name\":\"service\"")); assertTrue(json.contains("\"groupName\":\"group\"")); assertTrue(json.contains("\"clusterCount\":2")); assertTrue(json.contains("\"ipCount\":10")); assertTrue(json.contains("\"healthyInstanceCount\":8")); assertTrue(json.contains("\"triggerFlag\":\"flag\"")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"name\":\"service\",\"groupName\":\"group\",\"clusterCount\":2," + "\"ipCount\":10,\"healthyInstanceCount\":8,\"triggerFlag\":\"flag\"}"; ServiceView serviceView1 = mapper.readValue(jsonString, ServiceView.class); assertEquals(serviceView.getName(), serviceView1.getName()); assertEquals(serviceView.getGroupName(), serviceView1.getGroupName()); assertEquals(serviceView.getClusterCount(), serviceView1.getClusterCount()); assertEquals(serviceView.getIpCount(), serviceView1.getIpCount()); assertEquals(serviceView.getHealthyInstanceCount(), serviceView1.getHealthyInstanceCount()); assertEquals(serviceView.getTriggerFlag(), serviceView1.getTriggerFlag()); } @Test void testToString() { String expected = "ServiceView{name='service', groupName='group', clusterCount=2, ipCount=10, healthyInstanceCount=8, triggerFlag='flag'}"; assertEquals(expected, serviceView.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/pojo/maintainer/SubscriberInfoTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.pojo.maintainer; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class SubscriberInfoTest { private ObjectMapper mapper; private SubscriberInfo subscriberInfo; @BeforeEach void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); subscriberInfo = new SubscriberInfo(); subscriberInfo.setNamespaceId("namespaceId"); subscriberInfo.setGroupName("groupName"); subscriberInfo.setServiceName("serviceName"); subscriberInfo.setIp("1.1.1.1"); subscriberInfo.setPort(8080); subscriberInfo.setAgent("agent"); subscriberInfo.setAppName("appName"); } @Test void testSerialize() throws JsonProcessingException { String json = mapper.writeValueAsString(subscriberInfo); assertTrue(json.contains("\"namespaceId\":\"namespaceId\"")); assertTrue(json.contains("\"groupName\":\"groupName\"")); assertTrue(json.contains("\"serviceName\":\"serviceName\"")); assertTrue(json.contains("\"ip\":\"1.1.1.1\"")); assertTrue(json.contains("\"port\":8080")); assertTrue(json.contains("\"agent\":\"agent\"")); assertTrue(json.contains("\"appName\":\"appName\"")); } @Test void testDeserialize() throws IOException { String jsonString = "{\"namespaceId\":\"namespaceId\",\"groupName\":\"groupName\",\"serviceName\":\"serviceName\"," + "\"ip\":\"1.1.1.1\",\"port\":8080,\"agent\":\"agent\",\"appName\":\"appName\"}"; SubscriberInfo subscriberInfo1 = mapper.readValue(jsonString, SubscriberInfo.class); assertEquals(subscriberInfo.getNamespaceId(), subscriberInfo1.getNamespaceId()); assertEquals(subscriberInfo.getGroupName(), subscriberInfo1.getGroupName()); assertEquals(subscriberInfo.getServiceName(), subscriberInfo1.getServiceName()); assertEquals(subscriberInfo.getIp(), subscriberInfo1.getIp()); assertEquals(subscriberInfo.getPort(), subscriberInfo1.getPort()); assertEquals(subscriberInfo.getAgent(), subscriberInfo1.getAgent()); assertEquals(subscriberInfo.getAppName(), subscriberInfo1.getAppName()); assertEquals(subscriberInfo.getAddress(), subscriberInfo1.getAddress()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/BasedNamingRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public abstract class BasedNamingRequestTest { protected static final String SERVICE = "service"; protected static final String GROUP = "group"; protected static final String NAMESPACE = "namespace"; protected static ObjectMapper mapper; @BeforeAll public static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } protected void injectNamingRequestBasedInfo(AbstractNamingRequest request) { request.setServiceName(SERVICE); request.setGroupName(GROUP); request.setNamespace(NAMESPACE); } protected void checkNamingRequestBasedInfo(AbstractNamingRequest request) { assertEquals(SERVICE, request.getServiceName()); assertEquals(GROUP, request.getGroupName()); assertEquals(NAMESPACE, request.getNamespace()); assertEquals(NAMING_MODULE, request.getModule()); } protected void checkSerializeBasedInfo(String json) { assertTrue(json.contains("\"serviceName\":\"" + SERVICE + "\"")); assertTrue(json.contains("\"groupName\":\"" + GROUP + "\"")); assertTrue(json.contains("\"namespace\":\"" + NAMESPACE + "\"")); assertTrue(json.contains("\"module\":\"" + NAMING_MODULE + "\"")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/BatchInstanceRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class BatchInstanceRequestTest extends BasedNamingRequestTest { @Test void testSerialize() throws JsonProcessingException { BatchInstanceRequest request = new BatchInstanceRequest(NAMESPACE, SERVICE, GROUP, NamingRemoteConstants.BATCH_REGISTER_INSTANCE, Collections.singletonList(new Instance())); String json = mapper.writeValueAsString(request); checkSerializeBasedInfo(json); assertTrue(json.contains("\"type\":\"" + NamingRemoteConstants.BATCH_REGISTER_INSTANCE + "\"")); assertTrue(json.contains("\"instances\":[{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"namespace\":\"namespace\",\"serviceName\":\"service\",\"groupName\":\"group\"," + "\"type\":\"batchRegisterInstance\",\"instances\":[{\"port\":0,\"weight\":1.0,\"healthy\":true," + "\"enabled\":true,\"ephemeral\":true,\"metadata\":{},\"instanceIdGenerator\":\"simple\"," + "\"instanceHeartBeatInterval\":5000,\"instanceHeartBeatTimeOut\":15000,\"ipDeleteTimeout\":30000}]," + "\"module\":\"naming\"}"; BatchInstanceRequest actual = mapper.readValue(json, BatchInstanceRequest.class); checkNamingRequestBasedInfo(actual); assertEquals(NamingRemoteConstants.BATCH_REGISTER_INSTANCE, actual.getType()); assertEquals(1, actual.getInstances().size()); assertEquals(new Instance(), actual.getInstances().get(0)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/InstanceRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class InstanceRequestTest extends BasedNamingRequestTest { @Test void testSerialize() throws JsonProcessingException { InstanceRequest request = new InstanceRequest(NAMESPACE, SERVICE, GROUP, NamingRemoteConstants.REGISTER_INSTANCE, new Instance()); String json = mapper.writeValueAsString(request); checkSerializeBasedInfo(json); assertTrue(json.contains("\"type\":\"" + NamingRemoteConstants.REGISTER_INSTANCE + "\"")); assertTrue(json.contains("\"instance\":{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"namespace\":\"namespace\",\"serviceName\":\"service\",\"groupName\":\"group\"," + "\"type\":\"deregisterInstance\",\"instance\":{\"port\":0,\"weight\":1.0,\"healthy\":true," + "\"enabled\":true,\"ephemeral\":true,\"metadata\":{},\"instanceIdGenerator\":\"simple\"," + "\"instanceHeartBeatInterval\":5000,\"instanceHeartBeatTimeOut\":15000,\"ipDeleteTimeout\":30000}," + "\"module\":\"naming\"}"; InstanceRequest actual = mapper.readValue(json, InstanceRequest.class); checkNamingRequestBasedInfo(actual); assertEquals(NamingRemoteConstants.DE_REGISTER_INSTANCE, actual.getType()); assertEquals(new Instance(), actual.getInstance()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchChangeNotifyRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingFuzzyWatchChangeNotifyRequestTest { private static final String SERVICE_KEY = "serviceKey"; private static final String CHANGED_TYPE = "changedType"; private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { NamingFuzzyWatchChangeNotifyRequest request = new NamingFuzzyWatchChangeNotifyRequest(SERVICE_KEY, CHANGED_TYPE); String json = mapper.writeValueAsString(request); assertTrue(json.contains("\"serviceKey\":\"" + SERVICE_KEY + "\"")); assertTrue(json.contains("\"changedType\":\"" + CHANGED_TYPE + "\"")); assertTrue(json.contains("\"syncType\":\"" + FUZZY_WATCH_RESOURCE_CHANGED + "\"")); assertTrue(json.contains("\"module\":\"" + NAMING_MODULE + "\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"serviceKey\":\"serviceKey\",\"changedType\":\"changedType\"," + "\"syncType\":\"FUZZY_WATCH_RESOURCE_CHANGED\",\"module\":\"naming\"}"; NamingFuzzyWatchChangeNotifyRequest actual = mapper.readValue(json, NamingFuzzyWatchChangeNotifyRequest.class); assertEquals(SERVICE_KEY, actual.getServiceKey()); assertEquals(CHANGED_TYPE, actual.getChangedType()); assertEquals(FUZZY_WATCH_RESOURCE_CHANGED, actual.getSyncType()); assertEquals(NAMING_MODULE, actual.getModule()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingFuzzyWatchRequestTest { private static final String NAMESPACE = "namespace"; private static final String GROUP_KEY_PATTERN = "groupKeyPattern"; private static final String WATCH_TYPE = "watchType"; private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { NamingFuzzyWatchRequest request = new NamingFuzzyWatchRequest(GROUP_KEY_PATTERN, WATCH_TYPE); request.setNamespace(NAMESPACE); Set receivedGroupKeys = new HashSet<>(); receivedGroupKeys.add("key1"); receivedGroupKeys.add("key2"); request.setReceivedGroupKeys(receivedGroupKeys); request.setInitializing(true); String json = mapper.writeValueAsString(request); assertTrue(json.contains("\"namespace\":\"" + NAMESPACE + "\"")); assertTrue(json.contains("\"groupKeyPattern\":\"" + GROUP_KEY_PATTERN + "\"")); assertTrue(json.contains("\"watchType\":\"" + WATCH_TYPE + "\"")); assertTrue(json.contains("\"receivedGroupKeys\":[")); assertTrue(json.contains("\"initializing\":true")); assertTrue(json.contains("\"module\":\"" + NAMING_MODULE + "\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"initializing\":true,\"namespace\":\"namespace\",\"groupKeyPattern\":\"groupKeyPattern\"," + "\"receivedGroupKeys\":[\"key1\",\"key2\"],\"watchType\":\"watchType\",\"module\":\"naming\"}"; NamingFuzzyWatchRequest actual = mapper.readValue(json, NamingFuzzyWatchRequest.class); assertEquals(NAMESPACE, actual.getNamespace()); assertEquals(GROUP_KEY_PATTERN, actual.getGroupKeyPattern()); assertEquals(WATCH_TYPE, actual.getWatchType()); assertEquals(true, actual.isInitializing()); assertEquals(NAMING_MODULE, actual.getModule()); assertEquals(2, actual.getReceivedGroupKeys().size()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/NamingFuzzyWatchSyncRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest.Context; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.HashSet; import java.util.Set; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingFuzzyWatchSyncRequestTest { private static final String GROUP_KEY_PATTERN = "groupKeyPattern"; private static final String SYNC_TYPE = "syncType"; private static final String SERVICE_KEY = "serviceKey"; private static final String CHANGED_TYPE = "changedType"; private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { Set contexts = new HashSet<>(); Context context = Context.build(SERVICE_KEY, CHANGED_TYPE); contexts.add(context); NamingFuzzyWatchSyncRequest request = new NamingFuzzyWatchSyncRequest(GROUP_KEY_PATTERN, SYNC_TYPE, contexts); request.setTotalBatch(2); request.setCurrentBatch(1); String json = mapper.writeValueAsString(request); assertTrue(json.contains("\"groupKeyPattern\":\"" + GROUP_KEY_PATTERN + "\"")); assertTrue(json.contains("\"syncType\":\"" + SYNC_TYPE + "\"")); assertTrue(json.contains("\"contexts\":[{")); assertTrue(json.contains("\"serviceKey\":\"" + SERVICE_KEY + "\"")); assertTrue(json.contains("\"changedType\":\"" + CHANGED_TYPE + "\"")); assertTrue(json.contains("\"totalBatch\":2")); assertTrue(json.contains("\"currentBatch\":1")); assertTrue(json.contains("\"module\":\"" + NAMING_MODULE + "\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"groupKeyPattern\":\"groupKeyPattern\",\"contexts\":[{\"serviceKey\":\"serviceKey\"," + "\"changedType\":\"changedType\"}],\"totalBatch\":2,\"currentBatch\":1,\"syncType\":\"syncType\",\"module\":\"naming\"}"; NamingFuzzyWatchSyncRequest actual = mapper.readValue(json, NamingFuzzyWatchSyncRequest.class); assertEquals(GROUP_KEY_PATTERN, actual.getGroupKeyPattern()); assertEquals(SYNC_TYPE, actual.getSyncType()); assertEquals(2, actual.getTotalBatch()); assertEquals(1, actual.getCurrentBatch()); assertEquals(NAMING_MODULE, actual.getModule()); assertEquals(1, actual.getContexts().size()); Context context = actual.getContexts().iterator().next(); assertEquals(SERVICE_KEY, context.getServiceKey()); assertEquals(CHANGED_TYPE, context.getChangedType()); } @Test void testBuildSyncNotifyRequest() { Set contexts = new HashSet<>(); Context context = Context.build(SERVICE_KEY, CHANGED_TYPE); contexts.add(context); NamingFuzzyWatchSyncRequest request = NamingFuzzyWatchSyncRequest.buildSyncNotifyRequest( GROUP_KEY_PATTERN, SYNC_TYPE, contexts, 3, 2); assertEquals(GROUP_KEY_PATTERN, request.getGroupKeyPattern()); assertEquals(SYNC_TYPE, request.getSyncType()); assertEquals(3, request.getTotalBatch()); assertEquals(2, request.getCurrentBatch()); assertEquals(1, request.getContexts().size()); Context actualContext = request.getContexts().iterator().next(); assertEquals(SERVICE_KEY, actualContext.getServiceKey()); assertEquals(CHANGED_TYPE, actualContext.getChangedType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/NotifySubscriberRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class NotifySubscriberRequestTest { private static final String SERVICE = "service"; private static final String GROUP = "group"; private static final String NAMESPACE = "namespace"; private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { ServiceInfo serviceInfo = new ServiceInfo(GROUP + "@@" + SERVICE); NotifySubscriberRequest request = NotifySubscriberRequest.buildNotifySubscriberRequest(serviceInfo); request.setServiceName(SERVICE); request.setGroupName(GROUP); request.setNamespace(NAMESPACE); String json = mapper.writeValueAsString(request); checkSerializeBasedInfo(json); assertTrue(json.contains("\"serviceInfo\":{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"namespace\":\"namespace\",\"serviceName\":\"service\",\"groupName\":\"group\"," + "\"serviceInfo\":{\"name\":\"service\",\"groupName\":\"group\",\"cacheMillis\":1000,\"hosts\":[]," + "\"lastRefTime\":0,\"checksum\":\"\",\"allIPs\":false,\"reachProtectionThreshold\":false," + "\"valid\":true},\"module\":\"naming\"}"; NotifySubscriberRequest actual = mapper.readValue(json, NotifySubscriberRequest.class); checkRequestBasedInfo(actual); assertEquals(GROUP + "@@" + SERVICE, actual.getServiceInfo().getKey()); } private void checkRequestBasedInfo(NotifySubscriberRequest request) { assertEquals(SERVICE, request.getServiceName()); assertEquals(GROUP, request.getGroupName()); assertEquals(NAMESPACE, request.getNamespace()); assertEquals(NAMING_MODULE, request.getModule()); } private void checkSerializeBasedInfo(String json) { assertTrue(json.contains("\"serviceName\":\"" + SERVICE + "\"")); assertTrue(json.contains("\"groupName\":\"" + GROUP + "\"")); assertTrue(json.contains("\"namespace\":\"" + NAMESPACE + "\"")); assertTrue(json.contains("\"module\":\"" + NAMING_MODULE + "\"")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/PersistentInstanceRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class PersistentInstanceRequestTest extends BasedNamingRequestTest { @Test void testSerialize() throws JsonProcessingException { PersistentInstanceRequest request = new PersistentInstanceRequest(NAMESPACE, SERVICE, GROUP, NamingRemoteConstants.REGISTER_INSTANCE, new Instance()); String json = mapper.writeValueAsString(request); checkSerializeBasedInfo(json); assertTrue(json.contains("\"type\":\"" + NamingRemoteConstants.REGISTER_INSTANCE + "\"")); assertTrue(json.contains("\"instance\":{")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"namespace\":\"namespace\",\"serviceName\":\"service\",\"groupName\":\"group\"," + "\"type\":\"deregisterInstance\",\"instance\":{\"port\":0,\"weight\":1.0,\"healthy\":true," + "\"enabled\":true,\"ephemeral\":true,\"metadata\":{},\"instanceIdGenerator\":\"simple\"," + "\"instanceHeartBeatInterval\":5000,\"instanceHeartBeatTimeOut\":15000,\"ipDeleteTimeout\":30000}," + "\"module\":\"naming\"}"; PersistentInstanceRequest actual = mapper.readValue(json, PersistentInstanceRequest.class); checkNamingRequestBasedInfo(actual); assertEquals(NamingRemoteConstants.DE_REGISTER_INSTANCE, actual.getType()); assertEquals(new Instance(), actual.getInstance()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/ServiceListRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static com.alibaba.nacos.api.common.Constants.Naming.NAMING_MODULE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServiceListRequestTest extends BasedNamingRequestTest { @Test void testSerialize() throws JsonProcessingException { ServiceListRequest request = new ServiceListRequest(NAMESPACE, GROUP, 1, 10); request.setSelector("label"); String json = mapper.writeValueAsString(request); assertTrue(json.contains("\"groupName\":\"" + GROUP + "\"")); assertTrue(json.contains("\"namespace\":\"" + NAMESPACE + "\"")); assertTrue(json.contains("\"module\":\"" + NAMING_MODULE + "\"")); assertTrue(json.contains("\"selector\":\"label\"")); assertTrue(json.contains("\"pageNo\":1")); assertTrue(json.contains("\"pageSize\":10")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"namespace\":\"namespace\",\"serviceName\":\"\",\"groupName\":\"group\"," + "\"pageNo\":1,\"pageSize\":10,\"selector\":\"label\",\"module\":\"naming\"}"; ServiceListRequest actual = mapper.readValue(json, ServiceListRequest.class); assertEquals(GROUP, actual.getGroupName()); assertEquals(NAMESPACE, actual.getNamespace()); assertEquals(NAMING_MODULE, actual.getModule()); assertEquals(1, actual.getPageNo()); assertEquals(10, actual.getPageSize()); assertEquals("label", actual.getSelector()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/ServiceQueryRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.alibaba.nacos.api.common.Constants; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServiceQueryRequestTest extends BasedNamingRequestTest { @Test void testSerialize() throws JsonProcessingException { ServiceQueryRequest request = new ServiceQueryRequest(NAMESPACE, SERVICE, GROUP); request.setCluster(Constants.DEFAULT_CLUSTER_NAME); String json = mapper.writeValueAsString(request); checkSerializeBasedInfo(json); assertTrue(json.contains("\"cluster\":\"" + Constants.DEFAULT_CLUSTER_NAME + "\"")); assertTrue(json.contains("\"healthyOnly\":false")); assertTrue(json.contains("\"udpPort\":0")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"namespace\":\"namespace\",\"serviceName\":\"service\",\"groupName\":\"group\"," + "\"cluster\":\"DEFAULT\",\"healthyOnly\":true,\"udpPort\":0,\"module\":\"naming\"}"; ServiceQueryRequest actual = mapper.readValue(json, ServiceQueryRequest.class); checkNamingRequestBasedInfo(actual); assertEquals(Constants.DEFAULT_CLUSTER_NAME, actual.getCluster()); assertTrue(actual.isHealthyOnly()); assertEquals(0, actual.getUdpPort()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/request/SubscribeServiceRequestTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.request; import com.fasterxml.jackson.core.JsonProcessingException; 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 SubscribeServiceRequestTest extends BasedNamingRequestTest { @Test void testSerialize() throws JsonProcessingException { SubscribeServiceRequest request = new SubscribeServiceRequest(NAMESPACE, GROUP, SERVICE, "", true); String json = mapper.writeValueAsString(request); checkSerializeBasedInfo(json); assertTrue(json.contains("\"clusters\":\"\"")); assertTrue(json.contains("\"subscribe\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"headers\":{},\"namespace\":\"namespace\",\"serviceName\":\"service\",\"groupName\":\"group\"," + "\"subscribe\":false,\"clusters\":\"aa,bb\",\"module\":\"naming\"}"; SubscribeServiceRequest actual = mapper.readValue(json, SubscribeServiceRequest.class); checkNamingRequestBasedInfo(actual); assertEquals("aa,bb", actual.getClusters()); assertFalse(actual.isSubscribe()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/BatchInstanceResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class BatchInstanceResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { BatchInstanceResponse response = new BatchInstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"type\":\"" + NamingRemoteConstants.REGISTER_INSTANCE + "\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"type\":\"registerInstance\",\"success\":true}"; BatchInstanceResponse response = mapper.readValue(json, BatchInstanceResponse.class); assertEquals(NamingRemoteConstants.REGISTER_INSTANCE, response.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/InstanceResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class InstanceResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { InstanceResponse response = new InstanceResponse(NamingRemoteConstants.REGISTER_INSTANCE); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"type\":\"" + NamingRemoteConstants.REGISTER_INSTANCE + "\"")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"type\":\"deregisterInstance\",\"success\":true}"; InstanceResponse response = mapper.readValue(json, InstanceResponse.class); assertEquals(NamingRemoteConstants.DE_REGISTER_INSTANCE, response.getType()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchChangeNotifyResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingFuzzyWatchChangeNotifyResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { NamingFuzzyWatchChangeNotifyResponse response = new NamingFuzzyWatchChangeNotifyResponse(); response.setResultCode(200); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":200")); assertTrue(json.contains("\"success\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"success\":true}"; NamingFuzzyWatchChangeNotifyResponse response = mapper.readValue(json, NamingFuzzyWatchChangeNotifyResponse.class); assertTrue(response.isSuccess()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingFuzzyWatchResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerializeSuccessResponse() throws JsonProcessingException { NamingFuzzyWatchResponse response = NamingFuzzyWatchResponse.buildSuccessResponse(); response.setResultCode(200); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":200")); assertTrue(json.contains("\"success\":true")); } @Test void testSerializeFailResponse() throws JsonProcessingException { NamingFuzzyWatchResponse response = NamingFuzzyWatchResponse.buildFailResponse("test"); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":500")); assertTrue(json.contains("\"errorCode\":500")); assertTrue(json.contains("\"message\":\"test\"")); assertTrue(json.contains("\"success\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"success\":true}"; NamingFuzzyWatchResponse response = mapper.readValue(json, NamingFuzzyWatchResponse.class); assertTrue(response.isSuccess()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/NamingFuzzyWatchSyncResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingFuzzyWatchSyncResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerializeSuccessResponse() throws JsonProcessingException { NamingFuzzyWatchSyncResponse response = NamingFuzzyWatchSyncResponse.buildSuccessResponse(); response.setResultCode(200); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":200")); assertTrue(json.contains("\"success\":true")); } @Test void testSerializeFailResponse() throws JsonProcessingException { NamingFuzzyWatchSyncResponse response = NamingFuzzyWatchSyncResponse.buildFailResponse("test"); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":500")); assertTrue(json.contains("\"errorCode\":500")); assertTrue(json.contains("\"message\":\"test\"")); assertTrue(json.contains("\"success\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"success\":true}"; NamingFuzzyWatchSyncResponse response = mapper.readValue(json, NamingFuzzyWatchSyncResponse.class); assertTrue(response.isSuccess()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/NotifySubscriberResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class NotifySubscriberResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { NotifySubscriberResponse response = new NotifySubscriberResponse(); response.setResultCode(200); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":200")); assertTrue(json.contains("\"success\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"success\":true}"; NotifySubscriberResponse response = mapper.readValue(json, NotifySubscriberResponse.class); assertTrue(response.isSuccess()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/QueryServiceResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class QueryServiceResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerializeSuccessResponse() throws JsonProcessingException { QueryServiceResponse response = QueryServiceResponse.buildSuccessResponse(new ServiceInfo()); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"serviceInfo\":{")); assertTrue(json.contains("\"resultCode\":200")); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"success\":true")); } @Test void testSerializeFailResponse() throws JsonProcessingException { QueryServiceResponse response = QueryServiceResponse.buildFailResponse("test"); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":500")); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"message\":\"test\"")); assertTrue(json.contains("\"success\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"serviceInfo\":{\"cacheMillis\":1000,\"hosts\":[]," + "\"lastRefTime\":0,\"checksum\":\"\",\"allIPs\":false,\"reachProtectionThreshold\":false," + "\"valid\":true},\"success\":true}"; QueryServiceResponse response = mapper.readValue(json, QueryServiceResponse.class); assertNotNull(response.getServiceInfo()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/ServiceListResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServiceListResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerializeSuccessResponse() throws JsonProcessingException { ServiceListResponse response = ServiceListResponse.buildSuccessResponse(10, Collections.singletonList("a")); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"count\":10")); assertTrue(json.contains("\"serviceNames\":[\"a\"]")); assertTrue(json.contains("\"resultCode\":200")); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"success\":true")); } @Test void testSerializeFailResponse() throws JsonProcessingException { ServiceListResponse response = ServiceListResponse.buildFailResponse("test"); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":500")); assertTrue(json.contains("\"errorCode\":500")); assertTrue(json.contains("\"message\":\"test\"")); assertTrue(json.contains("\"success\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"count\":10,\"serviceNames\":[\"a\"],\"success\":true}"; ServiceListResponse response = mapper.readValue(json, ServiceListResponse.class); assertEquals(10, response.getCount()); assertEquals(1, response.getServiceNames().size()); assertEquals("a", response.getServiceNames().get(0)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/remote/response/SubscribeServiceResponseTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.remote.response; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class SubscribeServiceResponseTest { protected static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerializeSuccessResponse() throws JsonProcessingException { SubscribeServiceResponse response = new SubscribeServiceResponse(200, null, new ServiceInfo()); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"serviceInfo\":{")); assertTrue(json.contains("\"resultCode\":200")); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"success\":true")); } @Test void testSerializeFailResponse() throws JsonProcessingException { SubscribeServiceResponse response = new SubscribeServiceResponse(500, "test", null); String json = mapper.writeValueAsString(response); assertTrue(json.contains("\"resultCode\":500")); assertTrue(json.contains("\"errorCode\":0")); assertTrue(json.contains("\"message\":\"test\"")); assertTrue(json.contains("\"success\":false")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"serviceInfo\":{\"cacheMillis\":1000,\"hosts\":[]," + "\"lastRefTime\":0,\"checksum\":\"\",\"allIPs\":false,\"reachProtectionThreshold\":false," + "\"valid\":true},\"success\":true}"; SubscribeServiceResponse response = mapper.readValue(json, SubscribeServiceResponse.class); assertNotNull(response.getServiceInfo()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/naming/utils/NamingUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.naming.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.PreservedMetadataKeys; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.utils.StringUtils; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; 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; class NamingUtilsTest { @Test void testGetGroupedName() { assertEquals("group@@serviceName", NamingUtils.getGroupedName("serviceName", "group")); } @Test void testGetGroupedNameWithoutGroup() { assertThrows(IllegalArgumentException.class, () -> { NamingUtils.getGroupedName("serviceName", ""); }); } @Test void testGetGroupedNameWithoutServiceName() { assertThrows(IllegalArgumentException.class, () -> { NamingUtils.getGroupedName("", "group"); }); } @Test void testGetServiceName() { String validServiceName = "group@@serviceName"; assertEquals("serviceName", NamingUtils.getServiceName(validServiceName)); } @Test void testGetServiceNameWithoutGroup() { String serviceName = "serviceName"; assertEquals(serviceName, NamingUtils.getServiceName(serviceName)); } @Test void testGetServiceNameWithEmpty() { assertEquals(StringUtils.EMPTY, NamingUtils.getServiceName(null)); } @Test void testGetGroupName() { String validServiceName = "group@@serviceName"; assertEquals("group", NamingUtils.getGroupName(validServiceName)); } @Test void testGetGroupNameWithoutGroup() { String serviceName = "serviceName"; assertEquals(Constants.DEFAULT_GROUP, NamingUtils.getGroupName(serviceName)); } @Test void testGetGroupNameWithEmpty() { assertEquals(StringUtils.EMPTY, NamingUtils.getGroupName(null)); } @Test void testIsServiceNameCompatibilityMode() { String serviceName1 = "group@@serviceName"; assertTrue(NamingUtils.isServiceNameCompatibilityMode(serviceName1)); String serviceName2 = "serviceName"; assertFalse(NamingUtils.isServiceNameCompatibilityMode(serviceName2)); String serviceName3 = null; assertFalse(NamingUtils.isServiceNameCompatibilityMode(serviceName3)); } @Test void testCheckServiceNameFormat() { String validServiceName = "group@@serviceName"; NamingUtils.checkServiceNameFormat(validServiceName); } @Test void testCheckServiceNameFormatWithoutGroupAndService() { assertThrows(IllegalArgumentException.class, () -> { String validServiceName = "@@"; NamingUtils.checkServiceNameFormat(validServiceName); }); } @Test void testCheckServiceNameFormatWithoutGroup() { assertThrows(IllegalArgumentException.class, () -> { String validServiceName = "@@service"; NamingUtils.checkServiceNameFormat(validServiceName); }); } @Test void testCheckServiceNameFormatWithoutService() { assertThrows(IllegalArgumentException.class, () -> { String validServiceName = "group@@"; NamingUtils.checkServiceNameFormat(validServiceName); }); } @Test void testGetGroupedNameOptional() { String onlyGroupName = NamingUtils.getGroupedNameOptional(StringUtils.EMPTY, "groupA"); assertEquals("groupA@@", onlyGroupName); String onlyServiceName = NamingUtils.getGroupedNameOptional("serviceA", StringUtils.EMPTY); assertEquals("@@serviceA", onlyServiceName); String groupNameAndServiceName = NamingUtils.getGroupedNameOptional("serviceA", "groupA"); assertEquals("groupA@@serviceA", groupNameAndServiceName); } @Test void testGetServiceKey() { // 测试正常情况 String serviceKey = NamingUtils.getServiceKey("namespace", "group", "serviceName"); assertEquals("namespace@@group@@serviceName", serviceKey); // 测试namespace为空的情况 String serviceKeyWithEmptyNamespace = NamingUtils.getServiceKey("", "group", "serviceName"); assertEquals("public@@group@@serviceName", serviceKeyWithEmptyNamespace); // 测试namespace为null的情况 String serviceKeyWithNullNamespace = NamingUtils.getServiceKey(null, "group", "serviceName"); assertEquals("public@@group@@serviceName", serviceKeyWithNullNamespace); } @Test void testParseServiceKey() { // 测试正常情况 String serviceKey = "namespace@@group@@serviceName"; String[] parts = NamingUtils.parseServiceKey(serviceKey); assertEquals(3, parts.length); assertEquals("namespace", parts[0]); assertEquals("group", parts[1]); assertEquals("serviceName", parts[2]); // 测试只有两个部分 String serviceKeyWithTwoParts = "namespace@@group"; String[] twoParts = NamingUtils.parseServiceKey(serviceKeyWithTwoParts); assertEquals(2, twoParts.length); assertEquals("namespace", twoParts[0]); assertEquals("group", twoParts[1]); // 测试只有一个部分 String serviceKeyWithOnePart = "namespace"; String[] onePart = NamingUtils.parseServiceKey(serviceKeyWithOnePart); assertEquals(1, onePart.length); assertEquals("namespace", onePart[0]); // 测试空字符串 String[] emptyPart = NamingUtils.parseServiceKey(""); assertEquals(1, emptyPart.length); assertEquals("", emptyPart[0]); } @Test void testCheckInstanceIsLegal() throws NacosException { // check invalid clusterName Instance instance = new Instance(); instance.setClusterName("cluster1,cluster2"); try { NamingUtils.checkInstanceIsLegal(instance); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof NacosException); assertEquals( "Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: cluster1,cluster2)", e.getMessage()); } // valid clusterName instance.setClusterName("cluster1"); NamingUtils.checkInstanceIsLegal(instance); assertTrue(true); // check heartBeatTimeout, heartBeatInterval, ipDeleteTimeout Map meta = new HashMap<>(); meta.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "1"); meta.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL, "2"); meta.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "1"); instance.setMetadata(meta); try { NamingUtils.checkInstanceIsLegal(instance); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof NacosException); assertEquals("Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.", e.getMessage()); } meta.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "3"); meta.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL, "2"); meta.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "3"); NamingUtils.checkInstanceIsLegal(instance); assertTrue(true); } @Test void testBatchCheckInstanceIsLegal() throws NacosException { // check invalid clusterName Instance instance = new Instance(); instance.setClusterName("cluster1,cluster2"); List instanceList = new ArrayList<>(); instanceList.add(instance); try { NamingUtils.batchCheckInstanceIsLegal(instanceList); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof NacosException); assertEquals( "Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: cluster1,cluster2)", e.getMessage()); } instanceList.remove(instance); // TODO valid clusterName instance.setClusterName("cluster1"); instanceList.add(instance); NamingUtils.batchCheckInstanceIsLegal(instanceList); assertTrue(true); instanceList.remove(instance); // check heartBeatTimeout, heartBeatInterval, ipDeleteTimeout Map meta = new HashMap<>(); meta.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "1"); meta.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL, "2"); meta.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "1"); instance.setMetadata(meta); instanceList.add(instance); try { NamingUtils.batchCheckInstanceIsLegal(instanceList); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof NacosException); assertEquals("Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.", e.getMessage()); } instanceList.remove(instance); meta.put(PreservedMetadataKeys.HEART_BEAT_TIMEOUT, "3"); meta.put(PreservedMetadataKeys.HEART_BEAT_INTERVAL, "2"); meta.put(PreservedMetadataKeys.IP_DELETE_TIMEOUT, "3"); instance.setMetadata(meta); instanceList.add(instance); NamingUtils.batchCheckInstanceIsLegal(instanceList); assertTrue(true); } @Test void testCheckInstanceIsEphemeral() throws NacosException { Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(9089); instance.setEphemeral(true); NamingUtils.checkInstanceIsEphemeral(instance); try { instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(9089); instance.setEphemeral(false); NamingUtils.checkInstanceIsEphemeral(instance); } catch (NacosException e) { assertEquals(NacosException.INVALID_PARAM, e.getErrCode()); } } @Test void testCheckInstanceIsNull() throws NacosException { Instance instance = new Instance(); instance.setIp("127.0.0.1"); instance.setPort(9089); NamingUtils.checkInstanceIsLegal(instance); try { NamingUtils.checkInstanceIsLegal(null); } catch (NacosException e) { assertEquals(NacosException.INVALID_PARAM, e.getErrCode()); } } @Test void testIsNumber() { String str1 = "abc"; assertFalse(NamingUtils.isNumber(str1)); String str2 = "123456"; assertTrue(NamingUtils.isNumber(str2)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/AbstractPushCallBackTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; 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 AbstractPushCallBackTest { boolean testValue; @Test void testAbstractPushCallBack() { AbstractPushCallBack callBack = new AbstractPushCallBack(2000) { @Override public void onSuccess() { testValue = true; } @Override public void onFail(Throwable e) { testValue = false; } }; assertEquals(2000, callBack.getTimeout()); assertFalse(testValue); callBack.onSuccess(); assertTrue(testValue); callBack.onFail(new RuntimeException("test")); assertFalse(testValue); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/AbstractRequestCallBackTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.Response; import org.junit.jupiter.api.Test; import java.util.concurrent.Executor; 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 AbstractRequestCallBackTest { boolean testValue; @Test void testAbstractPushCallBack() { AbstractRequestCallBack callBack = new AbstractRequestCallBack() { @Override public Executor getExecutor() { return null; } @Override public void onResponse(Response response) { testValue = true; } @Override public void onException(Throwable e) { testValue = false; } }; assertEquals(3000L, callBack.getTimeout()); assertFalse(testValue); callBack.onResponse(new ErrorResponse()); assertTrue(testValue); callBack.onException(new RuntimeException("test")); assertFalse(testValue); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/DefaultRequestFutureTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import com.alibaba.nacos.api.remote.response.Response; 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.junit.jupiter.MockitoExtension; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class DefaultRequestFutureTest { private static final String CONNECTION_ID = "1233_1.1.1.1_3306"; private static final String REQUEST_ID = "10000"; @Mock private Response response; @Mock private ExecutorService executor; private long timestamp; @BeforeEach void setUp() throws Exception { timestamp = System.currentTimeMillis(); } @AfterEach void tearDown() throws Exception { } @Test void testSyncGetResponseSuccessWithoutTimeout() throws InterruptedException { DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setResponse(response); } catch (Exception ignored) { } }).start(); Response actual = requestFuture.get(); assertEquals(response, actual); assertEquals(CONNECTION_ID, requestFuture.getConnectionId()); assertEquals(REQUEST_ID, requestFuture.getRequestId()); assertTrue(requestFuture.isDone()); assertTrue(requestFuture.getTimeStamp() >= timestamp); } @Test void testSyncGetResponseFailureWithoutTimeout() throws InterruptedException { DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setFailResult(new RuntimeException("test")); } catch (Exception ignored) { } }).start(); Response actual = requestFuture.get(); assertNull(actual); assertEquals(CONNECTION_ID, requestFuture.getConnectionId()); assertEquals(REQUEST_ID, requestFuture.getRequestId()); assertTrue(requestFuture.isDone()); assertTrue(requestFuture.getTimeStamp() >= timestamp); } @Test void testSyncGetResponseSuccessWithTimeout() throws InterruptedException, TimeoutException { DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setResponse(response); } catch (Exception ignored) { } }).start(); Response actual = requestFuture.get(1000L); assertEquals(response, actual); assertEquals(CONNECTION_ID, requestFuture.getConnectionId()); assertEquals(REQUEST_ID, requestFuture.getRequestId()); assertTrue(requestFuture.isDone()); assertTrue(requestFuture.getTimeStamp() >= timestamp); } @Test void testSyncGetResponseSuccessWithInvalidTimeout() throws InterruptedException, TimeoutException { DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setResponse(response); } catch (Exception ignored) { } }).start(); Response actual = requestFuture.get(-1L); assertEquals(response, actual); assertEquals(CONNECTION_ID, requestFuture.getConnectionId()); assertEquals(REQUEST_ID, requestFuture.getRequestId()); assertTrue(requestFuture.isDone()); assertTrue(requestFuture.getTimeStamp() >= timestamp); } @Test void testSyncGetResponseFailureWithTimeout() throws InterruptedException, TimeoutException { assertThrows(TimeoutException.class, () -> { DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID); requestFuture.get(100L); }); } @Test void testSyncGetResponseSuccessByTriggerWithoutTimeout() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, null, trigger); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setResponse(response); } catch (Exception ignored) { } }).start(); Response actual = requestFuture.get(); assertEquals(response, actual); assertEquals(CONNECTION_ID, requestFuture.getConnectionId()); assertEquals(REQUEST_ID, requestFuture.getRequestId()); assertTrue(requestFuture.isDone()); assertTrue(requestFuture.getTimeStamp() >= timestamp); assertFalse(trigger.isTimeout); } @Test void testSyncGetResponseFailureByTriggerWithoutTimeout() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, null, trigger); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setFailResult(new RuntimeException("test")); } catch (Exception ignored) { } }).start(); Response actual = requestFuture.get(); assertNull(actual); assertEquals(CONNECTION_ID, requestFuture.getConnectionId()); assertEquals(REQUEST_ID, requestFuture.getRequestId()); assertTrue(requestFuture.isDone()); assertTrue(requestFuture.getTimeStamp() >= timestamp); assertFalse(trigger.isTimeout); } @Test void testSyncGetResponseSuccessByTriggerWithTimeout() throws InterruptedException, TimeoutException { MockFutureTrigger trigger = new MockFutureTrigger(); DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, null, trigger); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setResponse(response); } catch (Exception ignored) { } }).start(); Response actual = requestFuture.get(1000L); assertEquals(response, actual); assertEquals(CONNECTION_ID, requestFuture.getConnectionId()); assertEquals(REQUEST_ID, requestFuture.getRequestId()); assertTrue(requestFuture.isDone()); assertTrue(requestFuture.getTimeStamp() >= timestamp); assertFalse(trigger.isTimeout); assertFalse(trigger.isCancel); } @Test void testSyncGetResponseFailureByTriggerWithTimeout() throws InterruptedException, TimeoutException { assertThrows(TimeoutException.class, () -> { MockFutureTrigger trigger = new MockFutureTrigger(); DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, null, trigger); try { requestFuture.get(100L); } finally { assertTrue(trigger.isTimeout); } }); } @Test void testASyncGetResponseSuccessWithoutTimeout() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); MockRequestCallback callback = new MockRequestCallback(200L); DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, callback, trigger); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setResponse(response); } catch (Exception ignored) { } }).start(); TimeUnit.MILLISECONDS.sleep(250); assertEquals(response, callback.response); assertNull(callback.exception); assertFalse(trigger.isTimeout); assertFalse(trigger.isTimeout); assertEquals(callback, requestFuture.getRequestCallBack()); } @Test void testASyncGetResponseSuccessWithoutTimeoutByExecutor() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); MockRequestCallback callback = new MockRequestCallback(executor, 200L); DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, callback, trigger); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setResponse(response); } catch (Exception ignored) { } }).start(); TimeUnit.MILLISECONDS.sleep(250); assertEquals(callback, requestFuture.getRequestCallBack()); verify(executor).execute(any(DefaultRequestFuture.CallBackHandler.class)); } @Test void testASyncGetResponseFailureWithoutTimeout() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); MockRequestCallback callback = new MockRequestCallback(1000L); DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, callback, trigger); new Thread(() -> { try { TimeUnit.MILLISECONDS.sleep(100); requestFuture.setFailResult(new RuntimeException("test")); } catch (Exception ignored) { } }).start(); TimeUnit.MILLISECONDS.sleep(250); assertNull(callback.response); assertTrue(callback.exception instanceof RuntimeException); assertFalse(trigger.isTimeout); assertEquals(callback, requestFuture.getRequestCallBack()); } @Test void testASyncGetResponseFailureWithTimeout() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); MockRequestCallback callback = new MockRequestCallback(100L); final DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, callback, trigger); TimeUnit.MILLISECONDS.sleep(500); assertNull(callback.response); assertTrue(callback.exception instanceof TimeoutException); assertTrue(trigger.isTimeout); assertEquals(callback, requestFuture.getRequestCallBack()); } @Test void testSyncRequestFutureCancelFailedWithTimeout() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); final DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, null, trigger); assertThrows(TimeoutException.class, () -> requestFuture.get(100L)); requestFuture.cancel(true); assertTrue(trigger.isTimeout); assertFalse(trigger.isCancel); } @Test void testSyncRequestFutureCancelFailed() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); final DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, null, trigger); requestFuture.cancel(true); assertFalse(trigger.isTimeout); assertFalse(trigger.isCancel); } @Test void testASyncRequestFutureCancelFailedWithTrigger() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); MockRequestCallback callback = new MockRequestCallback(100L); final DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, callback, trigger); TimeUnit.MILLISECONDS.sleep(500L); requestFuture.cancel(true); assertNull(callback.response); assertTrue(callback.exception instanceof TimeoutException); assertTrue(trigger.isTimeout); assertFalse(trigger.isCancel); assertEquals(callback, requestFuture.getRequestCallBack()); } @Test void testASyncRequestFutureCancelSuccessWithTrigger() throws InterruptedException { MockFutureTrigger trigger = new MockFutureTrigger(); MockRequestCallback callback = new MockRequestCallback(500L); final DefaultRequestFuture requestFuture = new DefaultRequestFuture(CONNECTION_ID, REQUEST_ID, callback, trigger); TimeUnit.MILLISECONDS.sleep(100L); requestFuture.cancel(true); assertNull(callback.response); assertNull(callback.exception); assertFalse(trigger.isTimeout); assertTrue(trigger.isCancel); assertEquals(callback, requestFuture.getRequestCallBack()); } @Test void testFutureTriggerDefaultMethod() { final AtomicInteger callCount = new AtomicInteger(0); DefaultRequestFuture.FutureTrigger mockTrigger = callCount::incrementAndGet; assertEquals(0, callCount.get()); mockTrigger.triggerOnTimeout(); assertEquals(1, callCount.get()); mockTrigger.triggerOnCancel(); assertEquals(2, callCount.get()); } private class MockFutureTrigger implements DefaultRequestFuture.FutureTrigger { boolean isTimeout; boolean isCancel; @Override public void defaultTrigger() { // do nothing } @Override public void triggerOnTimeout() { isTimeout = true; } @Override public void triggerOnCancel() { isCancel = true; } } private class MockRequestCallback implements RequestCallBack { private final Executor executor; private final long timeout; private Response response; private Throwable exception; public MockRequestCallback(long timeout) { this(null, timeout); } public MockRequestCallback(Executor executor, long timeout) { this.executor = executor; this.timeout = timeout; } @Override public Executor getExecutor() { return executor; } @Override public long getTimeout() { return timeout; } @Override public void onResponse(Response response) { this.response = response; } @Override public void onException(Throwable e) { exception = e; } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/RpcScheduledExecutorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote; import org.junit.jupiter.api.Test; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class RpcScheduledExecutorTest { private static final String NAME = "test.rpc.thread"; Map threadNameMap = new ConcurrentHashMap<>(); @Test void testRpcScheduledExecutor() throws InterruptedException { RpcScheduledExecutor executor = new RpcScheduledExecutor(2, NAME); CountDownLatch latch = new CountDownLatch(2); executor.submit(new TestRunner(1, latch)); executor.submit(new TestRunner(2, latch)); boolean await = latch.await(1, TimeUnit.SECONDS); assertTrue(await); assertEquals(2, threadNameMap.size()); } private class TestRunner implements Runnable { int id; CountDownLatch latch; public TestRunner(int id, CountDownLatch latch) { this.id = id; this.latch = latch; } @Override public void run() { threadNameMap.put(String.valueOf(id), Thread.currentThread().getName()); latch.countDown(); } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/ability/ClientRemoteAbilityTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.ability; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientRemoteAbilityTest { private static ObjectMapper mapper; @BeforeAll static void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialize() throws JsonProcessingException { ClientRemoteAbility abilities = new ClientRemoteAbility(); String json = mapper.writeValueAsString(abilities); assertEquals("{\"supportRemoteConnection\":false}", json); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"supportRemoteConnection\":true}"; ClientRemoteAbility abilities = mapper.readValue(json, ClientRemoteAbility.class); assertTrue(abilities.isSupportRemoteConnection()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/ability/ServerRemoteAbilityTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.ability; import com.alibaba.nacos.api.ability.ClientAbilities; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ServerRemoteAbilityTest { private static ObjectMapper mapper; private ServerRemoteAbility serverAbilities; @BeforeAll static void setUpBeforeClass() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.enable(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY); } @BeforeEach void setUp() throws Exception { serverAbilities = new ServerRemoteAbility(); } @Test void testSerialize() throws JsonProcessingException { serverAbilities = new ServerRemoteAbility(); String json = mapper.writeValueAsString(serverAbilities); assertTrue(json.contains("\"supportRemoteConnection\":false")); assertTrue(json.contains("\"grpcReportEnabled\":true")); } @Test void testDeserialize() throws JsonProcessingException { String json = "{\"supportRemoteConnection\":true,\"grpcReportEnabled\":true}"; ServerRemoteAbility abilities = mapper.readValue(json, ServerRemoteAbility.class); assertTrue(abilities.isSupportRemoteConnection()); assertTrue(abilities.isGrpcReportEnabled()); } @Test void testEqualsAndHashCode() { assertEquals(serverAbilities, serverAbilities); assertEquals(serverAbilities.hashCode(), serverAbilities.hashCode()); assertNotEquals(null, serverAbilities); assertNotEquals(serverAbilities, new ClientAbilities()); ServerRemoteAbility test = new ServerRemoteAbility(); assertEquals(serverAbilities, test); assertEquals(serverAbilities.hashCode(), test.hashCode()); test.setSupportRemoteConnection(true); assertNotEquals(serverAbilities, test); assertNotEquals(serverAbilities.hashCode(), test.hashCode()); test.setSupportRemoteConnection(false); test.setGrpcReportEnabled(false); assertNotEquals(serverAbilities, test); assertNotEquals(serverAbilities.hashCode(), test.hashCode()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/BasicRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; public abstract class BasicRequestTest { protected ObjectMapper mapper; @BeforeEach public void setUp() throws Exception { mapper = new ObjectMapper(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/ConnectResetRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; 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.assertTrue; class ConnectResetRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { ConnectResetRequest request = new ConnectResetRequest(); request.setServerIp("127.0.0.1"); request.setServerPort("8888"); request.setRequestId("1"); request.setConnectionId("11111_127.0.0.1_8888"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"serverIp\":\"127.0.0.1\"")); assertTrue(json.contains("\"serverPort\":\"8888\"")); assertTrue(json.contains("\"module\":\"internal\"")); assertTrue(json.contains("\"requestId\":\"1\"")); assertTrue(json.contains("\"connectionId\":\"11111_127.0.0.1_8888\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"serverIp\":\"127.0.0.1\",\"serverPort\":\"8888\"," + "\"module\":\"internal\",\"connectionId\":\"11111_127.0.0.1_8888\"}"; ConnectResetRequest result = mapper.readValue(json, ConnectResetRequest.class); assertNotNull(result); assertEquals("127.0.0.1", result.getServerIp()); assertEquals("8888", result.getServerPort()); assertEquals("11111_127.0.0.1_8888", result.getConnectionId()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/ConnectionSetupRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; 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 ConnectionSetupRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { ConnectionSetupRequest request = new ConnectionSetupRequest(); request.setClientVersion("2.2.2"); request.setAbilityTable(new HashMap<>()); request.setTenant("testNamespaceId"); request.setLabels(Collections.singletonMap("labelKey", "labelValue")); request.setRequestId("1"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"clientVersion\":\"2.2.2\"")); assertTrue(json.contains("\"tenant\":\"testNamespaceId\"")); assertTrue(json.contains("\"labels\":{\"labelKey\":\"labelValue\"}")); assertTrue(json.contains("\"abilityTable\":{")); assertTrue(json.contains("\"module\":\"internal\"")); assertTrue(json.contains("\"requestId\":\"1\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"clientVersion\":\"2.2.2\",\"abilities\":{\"remoteAbility\":" + "{\"supportRemoteConnection\":false},\"configAbility\":{\"supportRemoteMetrics\":false}," + "\"namingAbility\":{\"supportDeltaPush\":false,\"supportRemoteMetric\":false}},\"tenant\":\"testNamespaceId\"," + "\"labels\":{\"labelKey\":\"labelValue\"},\"module\":\"internal\"}"; ConnectionSetupRequest result = mapper.readValue(json, ConnectionSetupRequest.class); assertNotNull(result); assertEquals("2.2.2", result.getClientVersion()); assertEquals("testNamespaceId", result.getTenant()); assertEquals(1, result.getLabels().size()); assertEquals("labelValue", result.getLabels().get("labelKey")); assertEquals("1", result.getRequestId()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/EmptyContentRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; 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; class EmptyContentRequestTest extends BasicRequestTest { private static final String COMMON_JSON = "{\"headers\":{\"clientIp\":\"1.1.1.1\"},\"requestId\":\"1\",\"module\":\"internal\"}"; private static final String TO_STRING = "%s{headers={clientIp=1.1.1.1}, requestId='1'}"; @BeforeEach public void setUp() throws Exception { super.setUp(); mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testClientDetectionRequest() throws JsonProcessingException, InstantiationException, IllegalAccessException { doTest(ClientDetectionRequest.class); } @Test void testHealthCheckRequest() throws JsonProcessingException, InstantiationException, IllegalAccessException { doTest(HealthCheckRequest.class); } @Test void testServerCheckRequest() throws JsonProcessingException, InstantiationException, IllegalAccessException { doTest(ServerCheckRequest.class); } @Test void testServerLoaderInfoRequest() throws JsonProcessingException, InstantiationException, IllegalAccessException { doTest(ServerLoaderInfoRequest.class); } private void doTest(Class clazz) throws IllegalAccessException, InstantiationException, JsonProcessingException { Request request = clazz.newInstance(); request.setRequestId("1"); request.putHeader("clientIp", "1.1.1.1"); String actual = mapper.writeValueAsString(request); assertCommonRequestJson(actual); request = mapper.readValue(COMMON_JSON, ServerLoaderInfoRequest.class); assertCommonRequest(request); } private void assertCommonRequestJson(String actualJson) { assertTrue(actualJson.contains("\"requestId\":\"1\"")); assertTrue(actualJson.contains("\"module\":\"internal\"")); assertTrue(actualJson.contains("\"headers\":{\"clientIp\":\"1.1.1.1\"}")); } private void assertCommonRequest(Request request) { assertEquals("1", request.getRequestId()); assertEquals("internal", request.getModule()); assertEquals(1, request.getHeaders().size()); assertEquals("1.1.1.1", request.getHeader("clientIp")); assertEquals(String.format(TO_STRING, request.getClass().getSimpleName()), request.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/PushAckRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; class PushAckRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { PushAckRequest request = PushAckRequest.build("1", false); request.setException(new NacosRuntimeException(500, "test")); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"success\":false")); assertTrue(json.contains("\"exception\":{")); assertTrue(json.contains("\"module\":\"internal\"")); assertTrue(json.contains("\"requestId\":\"1\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"success\":false," + "\"exception\":{\"stackTrace\":[],\"errCode\":500,\"message\":\"errCode: 500, errMsg: test \"," + "\"localizedMessage\":\"errCode: 500, errMsg: test \",\"suppressed\":[]},\"module\":\"internal\"}"; PushAckRequest result = mapper.readValue(json, PushAckRequest.class); assertNotNull(result); assertFalse(result.isSuccess()); assertEquals("1", result.getRequestId()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/RequestMetaTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.common.Constants; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; import java.util.Map; 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 RequestMetaTest { private RequestMeta requestMeta; @BeforeEach void setUp() { requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); requestMeta.setClientVersion("1.0.0"); requestMeta.setConnectionId("test-connection-id"); Map labels = new HashMap<>(); labels.put("env", "dev"); requestMeta.setLabels(labels); } @Test void testGetClientIp() { assertEquals("127.0.0.1", requestMeta.getClientIp()); } @Test void testGetClientVersion() { assertEquals("1.0.0", requestMeta.getClientVersion()); } @Test void testGetConnectionId() { assertEquals("test-connection-id", requestMeta.getConnectionId()); } @Test void testGetLabels() { Map labels = requestMeta.getLabels(); assertNotNull(labels); assertEquals(1, labels.size()); assertEquals("dev", labels.get("env")); } @Test void testToString() { String expected = "RequestMeta{connectionId='test-connection-id', clientIp='127.0.0.1', clientVersion='1.0.0', labels={env=dev}}"; assertEquals(expected, requestMeta.toString()); } @Test void testGetConnectionAbilityForNonExist() { assertEquals(AbilityStatus.UNKNOWN, requestMeta.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); requestMeta.setAbilityTable(Collections.emptyMap()); assertEquals(AbilityStatus.UNKNOWN, requestMeta.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); } @Test void testGetConnectionAbilityForExist() { requestMeta.setAbilityTable(Collections.singletonMap(AbilityKey.SERVER_FUZZY_WATCH.getName(), Boolean.FALSE)); assertEquals(AbilityStatus.NOT_SUPPORTED, requestMeta.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); requestMeta.setAbilityTable(Collections.singletonMap(AbilityKey.SERVER_FUZZY_WATCH.getName(), Boolean.TRUE)); assertEquals(AbilityStatus.SUPPORTED, requestMeta.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); } @Test void testExtractAppLabels() { Map labels = new HashMap<>(requestMeta.getLabels()); labels.put(Constants.APP_CONN_PREFIX + "testKey", "testValue"); labels.put(Constants.APP_CONN_PREFIX + "anotherKey", "anotherValue"); labels.put("no_app_prefix_key", "aaa"); requestMeta.setLabels(labels); Map appLabels = requestMeta.getAppLabels(); assertNotNull(appLabels); assertEquals(5, appLabels.size()); // appname, client_version_key, client_ip + 2 custom keys assertEquals("testValue", appLabels.get("testKey")); assertEquals("anotherValue", appLabels.get("anotherKey")); } @Test void testExtractAppLabelsEmptyAndBlankValues() { Map labels = new HashMap<>(requestMeta.getLabels()); // This should not be included - no value after prefix labels.put(Constants.APP_CONN_PREFIX, "value"); // This should not be included - blank value labels.put(Constants.APP_CONN_PREFIX + "blankValue", " "); // This should not be included - empty value labels.put(Constants.APP_CONN_PREFIX + "emptyValue", ""); // This should be included labels.put(Constants.APP_CONN_PREFIX + "validKey", "validValue"); requestMeta.setLabels(labels); Map appLabels = requestMeta.getAppLabels(); assertNotNull(appLabels); assertEquals(4, appLabels.size()); // appname, client_version_key, client_ip + 1 valid custom key assertEquals("validValue", appLabels.get("validKey")); assertTrue(appLabels.containsKey("validKey")); // Keys with blank or empty values should not be included assertFalse(appLabels.containsKey("blankValue")); assertFalse(appLabels.containsKey("emptyValue")); assertFalse(appLabels.containsKey("")); // key with just prefix } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/RequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class RequestTest { @BeforeEach void setUp() throws Exception { } @Test void testHeader() { MockRequest request = new MockRequest(); assertTrue(request.getHeaders().isEmpty()); assertNull(request.getHeader("clientIp")); assertEquals("1.1.1.1", request.getHeader("clientIp", "1.1.1.1")); request.putHeader("clientIp", "2.2.2.2"); assertEquals(1, request.getHeaders().size()); assertEquals("2.2.2.2", request.getHeader("clientIp")); assertEquals("2.2.2.2", request.getHeader("clientIp", "1.1.1.1")); request.putAllHeader(Collections.singletonMap("connectionId", "aaa")); assertEquals(2, request.getHeaders().size()); request.putAllHeader(null); assertEquals(2, request.getHeaders().size()); request.clearHeaders(); assertTrue(request.getHeaders().isEmpty()); } private static class MockRequest extends Request { @Override public String getModule() { return "mock"; } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/ServerReloadRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; 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.assertTrue; class ServerReloadRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { ServerReloadRequest request = new ServerReloadRequest(); request.setReloadCount(10); request.setReloadServer("1.1.1.1"); request.setRequestId("1"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"reloadCount\":10")); assertTrue(json.contains("\"reloadServer\":\"1.1.1.1\"")); assertTrue(json.contains("\"module\":\"internal\"")); assertTrue(json.contains("\"requestId\":\"1\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"reloadCount\":10,\"reloadServer\":\"1.1.1.1\"," + "\"module\":\"internal\"}"; ServerReloadRequest result = mapper.readValue(json, ServerReloadRequest.class); assertNotNull(result); assertEquals(10, result.getReloadCount()); assertEquals("1.1.1.1", result.getReloadServer()); assertEquals("1", result.getRequestId()); assertEquals("internal", result.getModule()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/request/SetupAckRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.request; import com.alibaba.nacos.api.ability.constant.AbilityKey; import org.junit.jupiter.api.Test; import java.util.Collections; 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 SetupAckRequestTest extends BasicRequestTest { @Test void testSerialize() throws Exception { SetupAckRequest request = new SetupAckRequest( Collections.singletonMap(AbilityKey.SERVER_FUZZY_WATCH.getName(), Boolean.TRUE)); request.setRequestId("1"); String json = mapper.writeValueAsString(request); assertNotNull(json); assertTrue(json.contains("\"abilityTable\":{\"fuzzyWatch\":true}")); assertTrue(json.contains("\"module\":\"internal\"")); assertTrue(json.contains("\"requestId\":\"1\"")); } @Test void testDeserialize() throws Exception { String json = "{\"headers\":{},\"requestId\":\"1\",\"abilityTable\":{\"fuzzyWatch\":true},\"module\":\"internal\"}"; SetupAckRequest result = mapper.readValue(json, SetupAckRequest.class); assertNotNull(result); assertTrue(result.getAbilityTable().get(AbilityKey.SDK_CLIENT_FUZZY_WATCH.getName())); assertEquals("1", result.getRequestId()); assertEquals("internal", result.getModule()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/response/EmptyContentResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class EmptyContentResponseTest { private static final String COMMON_JSON = "{\"resultCode\":200,\"errorCode\":0,\"requestId\":\"1\",\"success\":true}"; private static final String TO_STRING = "Response{resultCode=200, errorCode=0, message='null', requestId='1'}"; ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSetErrorInfo() { Response response = new Response() { }; response.setErrorInfo(ResponseCode.FAIL.getCode(), ResponseCode.FAIL.getDesc()); assertEquals(ResponseCode.FAIL.getCode(), response.getErrorCode()); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(ResponseCode.FAIL.getDesc(), response.getMessage()); } @Test void testClientDetectionResponse() throws JsonProcessingException { ClientDetectionResponse response = new ClientDetectionResponse(); response.setRequestId("1"); String actual = mapper.writeValueAsString(response); assertCommonResponseJson(actual); response = mapper.readValue(COMMON_JSON, ClientDetectionResponse.class); assertCommonResponse(response); } @Test void testConnectResetResponse() throws JsonProcessingException { ConnectResetResponse response = new ConnectResetResponse(); response.setRequestId("1"); String actual = mapper.writeValueAsString(response); assertCommonResponseJson(actual); response = mapper.readValue(COMMON_JSON, ConnectResetResponse.class); assertCommonResponse(response); } @Test void testHealthCheckResponse() throws JsonProcessingException { HealthCheckResponse response = new HealthCheckResponse(); response.setRequestId("1"); String actual = mapper.writeValueAsString(response); assertCommonResponseJson(actual); response = mapper.readValue(COMMON_JSON, HealthCheckResponse.class); assertCommonResponse(response); } @Test void testServerReloadResponse() throws JsonProcessingException { ServerReloadResponse response = new ServerReloadResponse(); response.setRequestId("1"); String actual = mapper.writeValueAsString(response); assertCommonResponseJson(actual); response = mapper.readValue(COMMON_JSON, ServerReloadResponse.class); assertCommonResponse(response); } @Test void testSetupAckResponse() throws JsonProcessingException { SetupAckResponse response = new SetupAckResponse(); response.setRequestId("1"); String actual = mapper.writeValueAsString(response); assertCommonResponseJson(actual); response = mapper.readValue(COMMON_JSON, SetupAckResponse.class); assertCommonResponse(response); } private void assertCommonResponse(Response response) { assertTrue(response.isSuccess()); assertNull(response.getMessage()); assertEquals(0, response.getErrorCode()); assertEquals(ResponseCode.SUCCESS.code, response.getResultCode()); assertEquals("1", response.getRequestId()); assertEquals(TO_STRING, response.toString()); } private void assertCommonResponseJson(String actualJson) { assertTrue(actualJson.contains("\"requestId\":\"1\"")); assertTrue(actualJson.contains("\"success\":true")); assertTrue(actualJson.contains("\"errorCode\":0")); assertTrue(actualJson.contains("\"resultCode\":200")); assertFalse(actualJson.contains("\"message\"")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/response/ErrorResponseTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ErrorResponseTest { @Test void testBuildWithErrorCode() { int errorCode = 500; String msg = "err msg"; Response response = ErrorResponse.build(errorCode, msg); assertEquals(errorCode, response.getErrorCode()); assertEquals(errorCode, response.getResultCode()); assertEquals(msg, response.getMessage()); } @Test void testBuildWithThrowable() { String errMsg = "exception msg"; RuntimeException runtimeException = new RuntimeException(errMsg); Response response = ErrorResponse.build(runtimeException); assertEquals(ResponseCode.FAIL.getCode(), response.getErrorCode()); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertEquals(errMsg, response.getMessage()); } @Test void testBuildWithNacosException() { int errCode = 500; String errMsg = "nacos exception msg"; NacosException nacosException = new NacosException(errCode, errMsg); Response response = ErrorResponse.build(nacosException); assertEquals(errCode, response.getErrorCode()); assertEquals(errCode, response.getResultCode()); assertEquals(errMsg, response.getMessage()); } @Test void testBuildWithNacosRuntimeException() { int errCode = 500; String errMsg = "nacos runtime exception msg"; NacosRuntimeException nacosRuntimeException = new NacosRuntimeException(errCode, errMsg); Response response = ErrorResponse.build(nacosRuntimeException); assertEquals(errCode, response.getErrorCode()); assertEquals(errCode, response.getResultCode()); assertEquals("errCode: " + errCode + ", errMsg: " + errMsg + " ", response.getMessage()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/response/ServerCheckResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class ServerCheckResponseTest { ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialization() throws JsonProcessingException { ServerCheckResponse response = new ServerCheckResponse("35643245_1.1.1.1_3306", false); String actual = mapper.writeValueAsString(response); assertTrue(actual.contains("\"connectionId\":\"35643245_1.1.1.1_3306\"")); assertTrue(actual.contains("\"supportAbilityNegotiation\":false")); } @Test void testDeserialization() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"connectionId\":\"35643245_1.1.1.1_3306\",\"success\":true," + "\"supportAbilityNegotiation\":true}"; ServerCheckResponse response = mapper.readValue(json, ServerCheckResponse.class); assertEquals("35643245_1.1.1.1_3306", response.getConnectionId()); assertTrue(response.isSupportAbilityNegotiation()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/remote/response/ServerLoaderInfoResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.remote.response; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; 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; class ServerLoaderInfoResponseTest { ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); } @Test void testSerialization() throws JsonProcessingException { ServerLoaderInfoResponse response = new ServerLoaderInfoResponse(); response.putMetricsValue("test", "testValue"); String actual = mapper.writeValueAsString(response); System.out.println(actual); assertTrue(actual.contains("\"loaderMetrics\":{\"test\":\"testValue\"}")); } @Test void testDeserialization() throws JsonProcessingException { String json = "{\"resultCode\":200,\"errorCode\":0,\"loaderMetrics\":{\"test\":\"testValue\"},\"success\":true}"; ServerLoaderInfoResponse response = mapper.readValue(json, ServerLoaderInfoResponse.class); assertEquals(1, response.getLoaderMetrics().size()); assertEquals("testValue", response.getMetricsValue("test")); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/selector/AbstractCmdbSelectorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; import com.alibaba.nacos.api.cmdb.pojo.Entity; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.selector.context.CmdbContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; import static com.alibaba.nacos.api.common.Constants.Naming.CMDB_CONTEXT_TYPE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class AbstractCmdbSelectorTest { private AtomicInteger counter; @BeforeEach void setUp() { counter = new AtomicInteger(); } @Test void testSetExpression() { MockCmdbSelector cmdbSelector = new MockCmdbSelector(); assertNull(cmdbSelector.getExpression()); cmdbSelector.setExpression("test"); assertEquals("test", cmdbSelector.getExpression()); } @Test void testParse() throws NacosException { MockCmdbSelector cmdbSelector = new MockCmdbSelector(); cmdbSelector.parse("test"); assertEquals("test", cmdbSelector.getExpression()); assertEquals(1, counter.get()); } @Test void testSelect() { CmdbContext context = new CmdbContext<>(); CmdbContext.CmdbInstance provider = new CmdbContext.CmdbInstance<>(); provider.setInstance(new Instance()); provider.setEntity(new Entity()); context.setProviders(Collections.singletonList(provider)); CmdbContext.CmdbInstance consumer = new CmdbContext.CmdbInstance<>(); consumer.setInstance(new Instance()); consumer.setEntity(new Entity()); context.setConsumer(consumer); List actual = new MockCmdbSelector().select(context); assertNull(actual.get(0).getIp()); assertTrue(actual.get(0).getMetadata().isEmpty()); assertEquals("true", provider.getInstance().getMetadata().get("afterSelect")); assertEquals("true", provider.getEntity().getLabels().get("afterSelect")); assertEquals("true", consumer.getInstance().getMetadata().get("afterSelect")); assertEquals("true", consumer.getEntity().getLabels().get("afterSelect")); } @Test void testGetContextType() { assertEquals(CMDB_CONTEXT_TYPE, new MockCmdbSelector().getContextType()); } @Test void testGetType() { assertEquals("mock", new MockCmdbSelector().getType()); } private class MockCmdbSelector extends AbstractCmdbSelector { @Override protected void doParse(String expression) throws NacosException { counter.incrementAndGet(); } @Override protected List doSelect(CmdbContext context) { for (CmdbContext.CmdbInstance each : context.getProviders()) { each.getInstance().getMetadata().put("afterSelect", "true"); each.getEntity().setLabels(Collections.singletonMap("afterSelect", "true")); } context.getConsumer().getInstance().getMetadata().put("afterSelect", "true"); context.getConsumer().getEntity().setLabels(Collections.singletonMap("afterSelect", "true")); return Collections.singletonList(new Instance()); } @Override public String getType() { return "mock"; } } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/selector/ExpressionSelectorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; 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; class ExpressionSelectorTest { ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.registerSubtypes(new NamedType(ExpressionSelector.class, SelectorType.label.name())); } @Test void testSerialization() throws JsonProcessingException { ExpressionSelector selector = new ExpressionSelector(); selector.setExpression("test expression"); String actual = mapper.writeValueAsString(selector); assertTrue(actual.contains("\"type\":\"" + SelectorType.label.name() + "\"")); assertTrue(actual.contains("\"expression\":\"test expression\"")); } @Test void testDeserialization() throws JsonProcessingException { String json = "{\"type\":\"label\",\"expression\":\"test expression\"}"; AbstractSelector actual = mapper.readValue(json, AbstractSelector.class); assertEquals(SelectorType.label.name(), actual.getType()); ExpressionSelector selector = (ExpressionSelector) actual; assertEquals("test expression", selector.getExpression()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/selector/NoneSelectorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class NoneSelectorTest { ObjectMapper mapper = new ObjectMapper(); @BeforeEach void setUp() throws Exception { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); mapper.registerSubtypes(new NamedType(NoneSelector.class, SelectorType.none.name())); } @Test void testSerialization() throws JsonProcessingException { NoneSelector selector = new NoneSelector(); String actual = mapper.writeValueAsString(selector); assertTrue(actual.contains("\"type\":\"" + SelectorType.none.name() + "\"")); } @Test void testDeserialization() throws JsonProcessingException { String json = "{\"type\":\"none\"}"; AbstractSelector actual = mapper.readValue(json, AbstractSelector.class); assertEquals(SelectorType.none.name(), actual.getType()); } @Test void testCommandMethod() throws NacosException { NoneSelector selector = new NoneSelector(); assertNull(selector.parse("")); List instances = new ArrayList<>(); assertEquals(instances, selector.select(instances)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/selector/context/CmdbContextTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.selector.context; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; class CmdbContextTest { @Test void testToString() { CmdbContext cmdbContext = new CmdbContext<>(); cmdbContext.setProviders(Collections.singletonList(new CmdbContext.CmdbInstance<>())); cmdbContext.setConsumer(new CmdbContext.CmdbInstance<>()); System.out.println(cmdbContext.toString()); assertEquals( "CmdbContext{consumer=CmdbInstance{entity=null, instance=null}, providers=[CmdbInstance{entity=null, instance=null}]}", cmdbContext.toString()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/utils/AbilityKeyTest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.utils; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import org.junit.jupiter.api.Test; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * . * * @author Daydreamer * @description Ability key test * @date 2022/9/8 12:27 **/ class AbilityKeyTest { @Test void testMapStr() { Map enumMap = new HashMap<>(); Map stringBooleanMap = AbilityKey.mapStr(enumMap); assertEquals(0, stringBooleanMap.size()); enumMap.put(AbilityKey.SERVER_FUZZY_WATCH, true); enumMap.put(AbilityKey.SERVER_DISTRIBUTED_LOCK, false); enumMap.put(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC, false); enumMap.put(AbilityKey.SERVER_MCP_REGISTRY, false); enumMap.put(AbilityKey.SERVER_AGENT_REGISTRY, false); stringBooleanMap = AbilityKey.mapStr(enumMap); assertEquals(5, stringBooleanMap.size()); assertTrue(stringBooleanMap.get(AbilityKey.SERVER_FUZZY_WATCH.getName())); assertFalse(stringBooleanMap.get(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName())); assertFalse(stringBooleanMap.get(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC.getName())); assertFalse(stringBooleanMap.get(AbilityKey.SERVER_MCP_REGISTRY.getName())); assertFalse(stringBooleanMap.get(AbilityKey.SERVER_AGENT_REGISTRY.getName())); enumMap.put(AbilityKey.SERVER_DISTRIBUTED_LOCK, true); enumMap.put(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC, true); enumMap.put(AbilityKey.SERVER_MCP_REGISTRY, true); enumMap.put(AbilityKey.SERVER_AGENT_REGISTRY, true); stringBooleanMap = AbilityKey.mapStr(enumMap); assertEquals(5, stringBooleanMap.size()); assertTrue(stringBooleanMap.get(AbilityKey.SERVER_FUZZY_WATCH.getName())); assertTrue(stringBooleanMap.get(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName())); assertTrue(stringBooleanMap.get(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC.getName())); assertTrue(stringBooleanMap.get(AbilityKey.SERVER_MCP_REGISTRY.getName())); assertTrue(stringBooleanMap.get(AbilityKey.SERVER_AGENT_REGISTRY.getName())); } @Test void testMapEnumForEmpty() { Map actual = AbilityKey.mapEnum(AbilityMode.SERVER, Collections.emptyMap()); assertTrue(actual.isEmpty()); } @Test void testMapEnum() { Map mapStr = new HashMap<>(); mapStr.put("test-no-existed", true); Map enumMap = AbilityKey.mapEnum(AbilityMode.SERVER, mapStr); assertEquals(0, enumMap.size()); mapStr.put(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName(), false); mapStr.put(AbilityKey.SERVER_FUZZY_WATCH.getName(), true); mapStr.put(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC.getName(), true); mapStr.put(AbilityKey.SERVER_MCP_REGISTRY.getName(), true); enumMap = AbilityKey.mapEnum(AbilityMode.SERVER, mapStr); assertFalse(enumMap.get(AbilityKey.SERVER_DISTRIBUTED_LOCK)); assertTrue(enumMap.get(AbilityKey.SERVER_FUZZY_WATCH)); assertTrue(enumMap.get(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)); assertTrue(enumMap.get(AbilityKey.SERVER_MCP_REGISTRY)); mapStr.clear(); mapStr.put(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName(), true); mapStr.put(AbilityKey.SERVER_FUZZY_WATCH.getName(), true); mapStr.put(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC.getName(), true); mapStr.put(AbilityKey.SERVER_MCP_REGISTRY.getName(), true); enumMap = AbilityKey.mapEnum(AbilityMode.SERVER, mapStr); assertTrue(enumMap.get(AbilityKey.SERVER_DISTRIBUTED_LOCK)); assertTrue(enumMap.get(AbilityKey.SERVER_FUZZY_WATCH)); assertTrue(enumMap.get(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)); assertTrue(enumMap.get(AbilityKey.SERVER_MCP_REGISTRY)); } @Test void testGetAllValues() { Collection actual = AbilityKey.getAllValues(AbilityMode.SERVER); assertEquals(5, actual.size()); actual = AbilityKey.getAllValues(AbilityMode.SDK_CLIENT); assertEquals(4, actual.size()); actual = AbilityKey.getAllValues(AbilityMode.CLUSTER_CLIENT); assertEquals(1, actual.size()); } @Test void testGetAllNames() { Collection actual = AbilityKey.getAllNames(AbilityMode.SERVER); assertEquals(5, actual.size()); actual = AbilityKey.getAllNames(AbilityMode.SDK_CLIENT); assertEquals(4, actual.size()); actual = AbilityKey.getAllNames(AbilityMode.CLUSTER_CLIENT); assertEquals(1, actual.size()); } @Test void testGetDescription() { assertEquals("Server whether support fuzzy watch service or config", AbilityKey.SERVER_FUZZY_WATCH.getDescription()); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/utils/NetUtilsTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.InetAddress; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class NetUtilsTest { @AfterEach void tearDown() throws Exception { Class clazz = Class.forName("com.alibaba.nacos.api.utils.NetUtils"); Field field = clazz.getDeclaredField("localIp"); field.setAccessible(true); field.set(null, ""); System.clearProperty("com.alibaba.nacos.client.local.ip"); System.clearProperty("com.alibaba.nacos.client.local.preferHostname"); System.clearProperty("java.net.preferIPv6Addresses"); } @Test void testLocalIpWithSpecifiedIp() { System.setProperty("com.alibaba.nacos.client.local.ip", "10.2.8.8"); assertEquals("10.2.8.8", NetUtils.localIp()); System.setProperty("com.alibaba.nacos.client.local.ip", "10.2.8.9"); assertEquals("10.2.8.8", NetUtils.localIp()); } @Test void testLocalIpWithPreferHostname() throws Exception { InetAddress inetAddress = invokeGetInetAddress(); String hostname = inetAddress.getHostName(); System.setProperty("com.alibaba.nacos.client.local.preferHostname", "true"); assertEquals(hostname, NetUtils.localIp()); } @Test void testLocalIpWithoutPreferHostname() throws Exception { InetAddress inetAddress = invokeGetInetAddress(); String ip = inetAddress.getHostAddress(); assertEquals(ip, NetUtils.localIp()); } @Test void testLocalIpWithException() throws Exception { Field field = System.class.getDeclaredField("props"); field.setAccessible(true); Properties properties = (Properties) field.get(null); Properties mockProperties = mock(Properties.class); when(mockProperties.getProperty("java.net.preferIPv6Addresses")).thenThrow(new RuntimeException("test")); field.set(null, mockProperties); try { System.setProperty("java.net.preferIPv6Addresses", "aaa"); InetAddress expect = InetAddress.getLocalHost(); assertEquals(expect.getHostAddress(), NetUtils.localIp()); } finally { field.set(null, properties); } } private InetAddress invokeGetInetAddress() throws Exception { Class clazz = Class.forName("com.alibaba.nacos.api.utils.NetUtils"); Method method = clazz.getDeclaredMethod("findFirstNonLoopbackAddress"); method.setAccessible(true); return (InetAddress) method.invoke(null); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/api/utils/StringUtilsTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.api.utils; 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.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class StringUtilsTest { @Test void testIsEmpty() { assertTrue(StringUtils.isEmpty(null)); assertTrue(StringUtils.isEmpty("")); assertFalse(StringUtils.isEmpty(" ")); assertFalse(StringUtils.isEmpty("bob")); assertFalse(StringUtils.isEmpty(" bob ")); } @Test void testIsBlank() { assertTrue(StringUtils.isBlank(null)); assertTrue(StringUtils.isBlank("")); assertTrue(StringUtils.isBlank(" ")); assertFalse(StringUtils.isBlank("bob")); assertFalse(StringUtils.isBlank(" bob ")); } @Test void testTrim() { assertNull(StringUtils.trim(null)); assertEquals(StringUtils.EMPTY, StringUtils.trim("")); assertEquals(StringUtils.EMPTY, StringUtils.trim(" ")); assertEquals("abc", StringUtils.trim("abc")); assertEquals("abc", StringUtils.trim(" abc ")); } @Test void testEquals() { assertTrue(StringUtils.equals(null, null)); assertFalse(StringUtils.equals(null, "abc")); assertFalse(StringUtils.equals("abc", null)); assertTrue(StringUtils.equals("abc", "abc")); assertFalse(StringUtils.equals("abc", "ABC")); assertTrue(StringUtils.equals(new StringBuilder("abc"), "abc")); assertFalse(StringUtils.equals(new StringBuilder("ABC"), "abc")); } @Test void testRegionMatchesEqualsCaseSensitive() { assertTrue(StringUtils.regionMatches("abc", false, 0, "xabc", 1, 3)); } @Test void testRegionMatchesEqualsCaseInsensitive() { assertTrue(StringUtils.regionMatches("abc", true, 0, "xabc", 1, 3)); assertTrue(StringUtils.regionMatches("abc", true, 0, "xAbc", 1, 3)); } @Test void testRegionMatchesNotEqualsCaseSensitive() { assertFalse(StringUtils.regionMatches("abc", false, 0, "xAbc", 1, 3)); assertFalse(StringUtils.regionMatches("abc", false, 0, "xCbc", 1, 3)); } @Test void testRegionMatchesNotEqualsCaseInsensitive() { assertFalse(StringUtils.regionMatches("abc", true, 0, "xCab", 1, 3)); } @Test void testRegionMatchesEqualsCaseSensitiveForNonString() { assertTrue(StringUtils.regionMatches(new StringBuilder("abc"), false, 0, "xabc", 1, 3)); } @Test void testRegionMatchesEqualsCaseInsensitiveForNonString() { assertTrue(StringUtils.regionMatches(new StringBuilder("abc"), true, 0, "xabc", 1, 3)); assertTrue(StringUtils.regionMatches(new StringBuilder("abc"), true, 0, "xAbc", 1, 3)); } @Test void testRegionMatchesNotEqualsCaseSensitiveForNonString() { assertFalse(StringUtils.regionMatches(new StringBuilder("abc"), false, 0, "xAbc", 1, 3)); assertFalse(StringUtils.regionMatches(new StringBuilder("abc"), false, 0, "xCbc", 1, 3)); } @Test void testRegionMatchesNotEqualsCaseInsensitiveForNonString() { assertFalse(StringUtils.regionMatches(new StringBuilder("abc"), true, 0, "xCab", 1, 3)); } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/client/ai/NacosAiService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai; import com.alibaba.nacos.api.ai.AiService; import com.alibaba.nacos.api.ai.listener.AbstractNacosAgentCardListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosMcpServerListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosPromptListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosSkillListener; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.exception.NacosException; import java.util.Collection; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; /** * Mock Naming Service for test {@link com.alibaba.nacos.api.ai.AiFactory}. * * @author xiweng.yy */ public class NacosAiService implements AiService { public static final AtomicBoolean IS_THROW_EXCEPTION = new AtomicBoolean(false); public NacosAiService(Properties properties) throws NacosException { if (IS_THROW_EXCEPTION.get()) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "mock exception"); } } @Override public McpServerDetailInfo getMcpServer(String mcpName, String version) throws NacosException { return null; } @Override public String releaseMcpServer(McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { return ""; } @Override public void registerMcpServerEndpoint(String mcpName, String address, int port, String version) throws NacosException { } @Override public void deregisterMcpServerEndpoint(String mcpName, String address, int port) throws NacosException { } @Override public McpServerDetailInfo subscribeMcpServer(String mcpName, String version, AbstractNacosMcpServerListener mcpServerListener) throws NacosException { return null; } @Override public void unsubscribeMcpServer(String mcpName, String version, AbstractNacosMcpServerListener mcpServerListener) throws NacosException { } @Override public void shutdown() throws NacosException { } @Override public AgentCardDetailInfo getAgentCard(String agentName, String version, String registrationType) throws NacosException { return null; } @Override public void releaseAgentCard(AgentCard agentCard, String registrationType, boolean setAsLatest) throws NacosException { } @Override public void registerAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { } @Override public void registerAgentEndpoint(String agentName, Collection endpoints) throws NacosException { } @Override public void deregisterAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { } @Override public AgentCardDetailInfo subscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException { return null; } @Override public void unsubscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException { } @Override public com.alibaba.nacos.api.ai.model.skills.Skill loadSkill(String skillName) throws NacosException { return null; } @Override public com.alibaba.nacos.api.ai.model.skills.Skill subscribeSkill(String skillName, AbstractNacosSkillListener skillListener) throws NacosException { return null; } @Override public void unsubscribeSkill(String skillName, AbstractNacosSkillListener skillListener) throws NacosException { } @Override public Prompt getPrompt(String promptKey) throws NacosException { return null; } @Override public Prompt getPromptByVersion(String promptKey, String version) throws NacosException { return null; } @Override public Prompt getPromptByLabel(String promptKey, String label) throws NacosException { return null; } @Override public Prompt subscribePrompt(String promptKey, String version, String label, AbstractNacosPromptListener promptListener) throws NacosException { return null; } @Override public void unsubscribePrompt(String promptKey, String version, String label, AbstractNacosPromptListener promptListener) throws NacosException { } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/client/config/NacosConfigService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; /** * Mock Naming Service for test {@link com.alibaba.nacos.api.config.ConfigFactory}. * * @author xiweng.yy */ public class NacosConfigService implements ConfigService { public static final AtomicBoolean IS_THROW_EXCEPTION = new AtomicBoolean(false); public NacosConfigService(Properties properties) throws NacosException { if (IS_THROW_EXCEPTION.get()) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "mock exception"); } } @Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return ""; } @Override public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException { return ""; } @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 ""; } @Override public void addConfigFilter(IConfigFilter configFilter) { } @Override public void shutDown() throws NacosException { } @Override public void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { } @Override public void fuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { } @Override public Future> fuzzyWatchWithGroupKeys(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { return null; } @Override public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { return null; } @Override public void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { } @Override public void cancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/client/lock/NacosLockService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.LockService; import com.alibaba.nacos.api.lock.model.LockInstance; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; /** * Mock Naming Service for test {@link com.alibaba.nacos.api.lock.NacosLockFactory}. * * @author xiweng.yy */ public class NacosLockService implements LockService { public static final AtomicBoolean IS_THROW_EXCEPTION = new AtomicBoolean(false); public NacosLockService(Properties properties) throws NacosException { if (IS_THROW_EXCEPTION.get()) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "mock exception"); } } @Override public Boolean lock(LockInstance instance) throws NacosException { return null; } @Override public Boolean unLock(LockInstance instance) throws NacosException { return null; } @Override public Boolean remoteTryLock(LockInstance instance) throws NacosException { return null; } @Override public Boolean remoteReleaseLock(LockInstance instance) throws NacosException { return null; } @Override public void shutdown() throws NacosException { } } ================================================ FILE: api/src/test/java/com/alibaba/nacos/client/naming/NacosNamingService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming; 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.listener.FuzzyWatchEventWatcher; 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; import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; /** * Mock Naming Service for test {@link com.alibaba.nacos.api.naming.NamingFactory}. * * @author xiweng.yy */ public class NacosNamingService implements NamingService { public static final AtomicBoolean IS_THROW_EXCEPTION = new AtomicBoolean(false); public NacosNamingService(String serverAddr) throws NacosException { if (IS_THROW_EXCEPTION.get()) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "mock exception"); } } public NacosNamingService(Properties properties) throws NacosException { if (IS_THROW_EXCEPTION.get()) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "mock exception"); } } @Override public void registerInstance(String serviceName, String ip, int port) throws NacosException { } @Override public void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException { } @Override public void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { } @Override public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { } @Override public void registerInstance(String serviceName, Instance instance) throws NacosException { } @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { } @Override public void batchRegisterInstance(String serviceName, String groupName, List instances) throws NacosException { } @Override public void batchDeregisterInstance(String serviceName, String groupName, List instances) throws NacosException { } @Override public void deregisterInstance(String serviceName, String ip, int port) throws NacosException { } @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException { } @Override public void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { } @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { } @Override public void deregisterInstance(String serviceName, Instance instance) throws NacosException { } @Override public void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException { } @Override public List getAllInstances(String serviceName) throws NacosException { return Collections.emptyList(); } @Override public List getAllInstances(String serviceName, String groupName) throws NacosException { return Collections.emptyList(); } @Override public List getAllInstances(String serviceName, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public List getAllInstances(String serviceName, List clusters) throws NacosException { return Collections.emptyList(); } @Override public List getAllInstances(String serviceName, String groupName, List clusters) throws NacosException { return Collections.emptyList(); } @Override public List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public List getAllInstances(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, boolean healthy) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, List clusters, boolean healthy, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException { return Collections.emptyList(); } @Override public Instance selectOneHealthyInstance(String serviceName) throws NacosException { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters) throws NacosException { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException { return null; } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException { 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 subscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException { } @Override public void subscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException { } @Override public void unsubscribe(String serviceName, EventListener listener) throws NacosException { } @Override public void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException { } @Override public void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException { } @Override public void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException { } @Override public void unsubscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException { } @Override public void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException { } @Override public void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { } @Override public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { } @Override public Future> fuzzyWatchWithServiceKeys(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { return null; } @Override public Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { return null; } @Override public void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { } @Override public void cancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { } @Override public ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException { return null; } @Override public ListView getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException { return null; } @Override public ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException { return null; } @Override public ListView getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException { return null; } @Override public List getSubscribeServices() throws NacosException { return Collections.emptyList(); } @Override public String getServerStatus() { return ""; } @Override public void shutDown() throws NacosException { } } ================================================ FILE: auth/pom.xml ================================================ com.alibaba.nacos nacos-all ${revision} ../pom.xml 4.0.0 nacos-auth jar nacos-auth ${project.version} https://nacos.io ${project.groupId} nacos-common com.alibaba.nacos nacos-auth-plugin ${project.groupId} nacos-sys org.springframework.boot spring-boot-starter true org.springframework spring-test test org.apache.tomcat.embed tomcat-embed-core ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/AbstractProtocolAuthService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.serveridentity.ServerIdentity; import com.alibaba.nacos.auth.serveridentity.ServerIdentityChecker; import com.alibaba.nacos.auth.serveridentity.ServerIdentityCheckerHolder; import com.alibaba.nacos.auth.serveridentity.ServerIdentityResult; import com.alibaba.nacos.auth.util.Loggers; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.AuthResult; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.Permission; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.Constants; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.auth.exception.AccessException; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginManager; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService; import java.util.Optional; import java.util.Properties; /** * Abstract protocol auth service. * *

    Implement #validateIdentity and #validateAuthority method template. * * @author xiweng.yy */ public abstract class AbstractProtocolAuthService implements ProtocolAuthService { protected final NacosAuthConfig authConfig; protected final ServerIdentityChecker checker; protected AbstractProtocolAuthService(NacosAuthConfig authConfig) { this.authConfig = authConfig; this.checker = ServerIdentityCheckerHolder.getInstance().newChecker(); } @Override public void initialize() { this.checker.init(authConfig); } @Override public boolean enableAuth(Secured secured) { Optional authPluginService = AuthPluginManager.getInstance() .findAuthServiceSpiImpl(authConfig.getNacosAuthSystemType()); if (authPluginService.isPresent()) { return authPluginService.get().enableAuth(secured.action(), secured.signType()); } Loggers.AUTH.warn("Can't find auth plugin for type {}, please add plugin to classpath or set {} as false", authConfig.getNacosAuthSystemType(), Constants.Auth.NACOS_CORE_AUTH_ENABLED); return false; } @Override public AuthResult validateIdentity(IdentityContext identityContext, Resource resource) throws AccessException { Optional authPluginService = AuthPluginManager.getInstance() .findAuthServiceSpiImpl(authConfig.getNacosAuthSystemType()); if (authPluginService.isPresent()) { return authPluginService.get().validateIdentity(identityContext, resource); } return AuthResult.successResult(); } @Override public AuthResult validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException { Optional authPluginService = AuthPluginManager.getInstance() .findAuthServiceSpiImpl(authConfig.getNacosAuthSystemType()); if (authPluginService.isPresent()) { return authPluginService.get().validateAuthority(identityContext, permission); } return AuthResult.successResult(); } @Override public ServerIdentityResult checkServerIdentity(R request, Secured secured) { if (isInvalidServerIdentity()) { return ServerIdentityResult.fail( "Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`" + " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`"); } ServerIdentity serverIdentity = parseServerIdentity(request); return checker.check(serverIdentity, secured); } private boolean isInvalidServerIdentity() { return StringUtils.isBlank(authConfig.getServerIdentityKey()) || StringUtils.isBlank( authConfig.getServerIdentityValue()); } /** * Parse server identity from protocol request. * * @param request protocol request * @return nacos server identity. */ protected abstract ServerIdentity parseServerIdentity(R request); /** * Get resource from secured annotation specified resource. * * @param secured secured annotation * @return resource */ protected Resource parseSpecifiedResource(Secured secured) { Properties properties = new Properties(); for (String each : secured.tags()) { properties.put(each, each); } return new Resource(null, null, secured.resource(), SignType.SPECIFIED, properties); } /** * Parse resource by specified resource parser. * * @param secured secured annotation * @param request request * @return resource */ protected Resource useSpecifiedParserToParse(Secured secured, R request) { try { return secured.parser().newInstance().parse(request, secured); } catch (Exception e) { Loggers.AUTH.error("Use specified resource parser {} parse resource failed.", secured.parser().getCanonicalName(), e); return Resource.EMPTY_RESOURCE; } } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/GrpcProtocolAuthService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.context.GrpcIdentityContextBuilder; import com.alibaba.nacos.auth.parser.grpc.AbstractGrpcResourceParser; import com.alibaba.nacos.auth.parser.grpc.AiGrpcResourceParser; import com.alibaba.nacos.auth.parser.grpc.ConfigGrpcResourceParser; import com.alibaba.nacos.auth.parser.grpc.NamingGrpcResourceParser; import com.alibaba.nacos.auth.serveridentity.ServerIdentity; import com.alibaba.nacos.auth.serveridentity.ServerIdentityResult; import com.alibaba.nacos.auth.util.Loggers; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import java.util.HashMap; import java.util.Map; /** * Auth Service for Grpc protocol. * * @author xiweng.yy */ public class GrpcProtocolAuthService extends AbstractProtocolAuthService { private final Map resourceParserMap; private final GrpcIdentityContextBuilder identityContextBuilder; public GrpcProtocolAuthService(NacosAuthConfig authConfig) { super(authConfig); resourceParserMap = new HashMap<>(2); identityContextBuilder = new GrpcIdentityContextBuilder(authConfig); } @Override public void initialize() { super.initialize(); resourceParserMap.put(SignType.NAMING, new NamingGrpcResourceParser()); resourceParserMap.put(SignType.CONFIG, new ConfigGrpcResourceParser()); resourceParserMap.put(SignType.AI, new AiGrpcResourceParser()); } @Override public Resource parseResource(Request request, Secured secured) { if (StringUtils.isNotBlank(secured.resource())) { return parseSpecifiedResource(secured); } String type = secured.signType(); AbstractGrpcResourceParser parser = resourceParserMap.get(type); if (parser == null) { Loggers.AUTH.warn("Can't find Grpc request resourceParser for type {}", type); return useSpecifiedParserToParse(secured, request); } return parser.parse(request, secured); } @Override public IdentityContext parseIdentity(Request request) { return identityContextBuilder.build(request); } @Override public ServerIdentityResult checkServerIdentity(Request request, Secured secured) { if (ApiType.INNER_API != secured.apiType()) { return ServerIdentityResult.noMatched(); } return super.checkServerIdentity(request, secured); } @Override protected ServerIdentity parseServerIdentity(Request request) { String serverIdentityKey = authConfig.getServerIdentityKey(); String serverIdentity = request.getHeader(serverIdentityKey); return new ServerIdentity(serverIdentityKey, serverIdentity); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/HttpProtocolAuthService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.context.HttpIdentityContextBuilder; import com.alibaba.nacos.auth.parser.http.AbstractHttpResourceParser; import com.alibaba.nacos.auth.parser.http.AiHttpResourceParser; import com.alibaba.nacos.auth.parser.http.ConfigHttpResourceParser; import com.alibaba.nacos.auth.parser.http.NamingHttpResourceParser; import com.alibaba.nacos.auth.serveridentity.ServerIdentity; import com.alibaba.nacos.auth.util.Loggers; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.SignType; import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; /** * Auth Service for Http protocol. * * @author xiweng.yy */ public class HttpProtocolAuthService extends AbstractProtocolAuthService { private final Map resourceParserMap; private final HttpIdentityContextBuilder identityContextBuilder; public HttpProtocolAuthService(NacosAuthConfig authConfig) { super(authConfig); resourceParserMap = new HashMap<>(2); identityContextBuilder = new HttpIdentityContextBuilder(authConfig); } @Override public void initialize() { super.initialize(); resourceParserMap.put(SignType.NAMING, new NamingHttpResourceParser()); resourceParserMap.put(SignType.CONFIG, new ConfigHttpResourceParser()); resourceParserMap.put(SignType.AI, new AiHttpResourceParser()); } @Override public Resource parseResource(HttpServletRequest request, Secured secured) { if (StringUtils.isNotBlank(secured.resource())) { return parseSpecifiedResource(secured); } String type = secured.signType(); if (!resourceParserMap.containsKey(type)) { Loggers.AUTH.warn("Can't find Http request resourceParser for type {} use specified resource parser", type); return useSpecifiedParserToParse(secured, request); } return resourceParserMap.get(type).parse(request, secured); } @Override public IdentityContext parseIdentity(HttpServletRequest request) { return identityContextBuilder.build(request); } @Override protected ServerIdentity parseServerIdentity(HttpServletRequest request) { String serverIdentityKey = authConfig.getServerIdentityKey(); String serverIdentity = request.getHeader(serverIdentityKey); return new ServerIdentity(serverIdentityKey, serverIdentity); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/ProtocolAuthService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.serveridentity.ServerIdentityResult; import com.alibaba.nacos.plugin.auth.api.AuthResult; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.Permission; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.exception.AccessException; /** * Protocol auth service. * * @author xiweng.yy */ public interface ProtocolAuthService { /** * Init protocol auth service. */ void initialize(); /** * Judgement whether enable auth feature according to secured information. *

    * configuration authEnabled in {@link NacosAuthConfig} is the main switch. * If authEnabled is {@code false}, this method and other follow methods should not be called. * * This method is only for plugin to judge whether auth this {@link Secured}. * For example, plugins can only auth for write action or only for naming type request. *

    * * @param secured secured information * @return {@code true} if enable auth, otherwise {@code false} */ boolean enableAuth(Secured secured); /** * Parse resource from protocol request and secured annotation. * * @param request protocol request * @param secured api secured annotation * @return resource */ Resource parseResource(R request, Secured secured); /** * Parse identity context from protocol request. * * @param request protocol request * @return identity context */ IdentityContext parseIdentity(R request); /** * Validate identity whether is legal. * * @param identityContext identity context * @param resource resource * @return {@link AuthResult} of validate result * @throws AccessException exception during validating */ AuthResult validateIdentity(IdentityContext identityContext, Resource resource) throws AccessException; /** * Validate identity whether had permission for the resource and action. * * @param identityContext identity context * @param permission permission include resource and action * @return {@link AuthResult} of validate result * @throws AccessException exception during validating */ AuthResult validateAuthority(IdentityContext identityContext, Permission permission) throws AccessException; /** * check server identity. * * @param request protocol request * @param secured secured api secured annotation * @return server identity result */ ServerIdentityResult checkServerIdentity(R request, Secured secured); } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/annotation/Secured.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.annotation; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.auth.parser.DefaultResourceParser; import com.alibaba.nacos.auth.parser.ResourceParser; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * Annotation indicating that the annotated request should be authorized. * * @author nkorange * @author mai.jh * @since 1.2.0 */ @Retention(RetentionPolicy.RUNTIME) public @interface Secured { /** * The action type of the request. * * @return action type, default READ */ ActionTypes action() default ActionTypes.READ; /** * The name of resource related to the request. * * @return resource name */ String resource() default StringUtils.EMPTY; /** * The module of resource related to the request. * * @return module name */ String signType() default SignType.NAMING; /** * Custom resource parser. Should have lower priority than resource() and typed parser. * * @return class type of resource parser */ Class parser() default DefaultResourceParser.class; /** * Specified tags for this secured, these tags will be injected into {@link com.alibaba.nacos.plugin.auth.api.Resource} * as the keys and values of properties. * * @return tags */ String[] tags() default {}; /** * The type of API. Distinguishing between ADMIN_API and OPEN_API. * * @return the type of the API */ ApiType apiType() default ApiType.OPEN_API; } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/config/AuthErrorCode.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.config; /** * Auth relative error codes, start with 5000X. * * @author xiweng.yy */ public enum AuthErrorCode { /** * invalid auth type. */ INVALID_TYPE(50001, "Invalid auth type, Please set `nacos.core.auth.system.type`, detail: https://nacos.io/docs/latest/manual/admin/auth/"), EMPTY_IDENTITY(50002, "Empty identity, Please set `nacos.core.auth.server.identity.key` and `nacos.core.auth.server.identity.value`, detail: https://nacos.io/docs/latest/manual/admin/auth/"); private final Integer code; private final String msg; public Integer getCode() { return code; } public String getMsg() { return msg; } AuthErrorCode(Integer code, String msg) { this.code = code; this.msg = msg; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/config/NacosAuthConfig.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.config; /** * Nacos Auth configurations. * * @author xiweng.yy */ public interface NacosAuthConfig { /** * Get auth scope like 'server', 'server admin', 'console'. * * @return auth scope */ String getAuthScope(); /** * Whether nacos server or console auth enabled. * * @return {@code true} means enabled, otherwise {@code false} */ boolean isAuthEnabled(); /** * Get current auth plugin type. * * @return auth plugin type. */ String getNacosAuthSystemType(); /** * Whether support server identity to identify request from other nacos servers. * * @return {@code true} means supported, otherwise {@code false} */ boolean isSupportServerIdentity(); /** * Get server identity key. * * @return server identity key If {@link #isSupportServerIdentity()} return {@code true}, otherwise empty string. */ String getServerIdentityKey(); /** * Get server identity value. * * @return server identity value If {@link #isSupportServerIdentity()} return {@code true}, otherwise empty string. */ String getServerIdentityValue(); } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/config/NacosAuthConfigHolder.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.config; import com.alibaba.nacos.common.spi.NacosServiceLoader; import java.util.Collection; import java.util.HashMap; import java.util.Map; /** * Nacos SPI Holder for {@link NacosAuthConfig}. * * @author xiweng.yy */ public class NacosAuthConfigHolder { private static final NacosAuthConfigHolder INSTANCE = new NacosAuthConfigHolder(); private final Map nacosAuthConfigMap; NacosAuthConfigHolder() { this.nacosAuthConfigMap = new HashMap<>(); for (NacosAuthConfig each : NacosServiceLoader.load(NacosAuthConfig.class)) { nacosAuthConfigMap.put(each.getAuthScope(), each); } } public static NacosAuthConfigHolder getInstance() { return INSTANCE; } public NacosAuthConfig getNacosAuthConfigByScope(String scope) { return nacosAuthConfigMap.get(scope); } public Collection getAllNacosAuthConfig() { return nacosAuthConfigMap.values(); } public boolean isAnyAuthEnabled() { return nacosAuthConfigMap.values().stream().anyMatch(NacosAuthConfig::isAuthEnabled); } /** * Is any auth config by input scope is enabled. * * @param scope the scopes to check whether enabled * @return {@code true} if any input scope auth is enabled, {@code false} all input scope auth is disabled. */ public boolean isAnyAuthEnabled(String... scope) { for (String each : scope) { NacosAuthConfig config = nacosAuthConfigMap.get(each); if (null != config && config.isAuthEnabled()) { return true; } } return false; } /** * Get nacos auth system type from the first {@link NacosAuthConfig}. * *

    * It should be same with for all {@link NacosAuthConfig}s in one nacos server. *

    * @return nacos auth system type */ public String getNacosAuthSystemType() { return nacosAuthConfigMap.values().stream().findFirst().map(NacosAuthConfig::getNacosAuthSystemType) .orElse(null); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/context/GrpcIdentityContextBuilder.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.context; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.constant.Constants; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginManager; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; /** * Identity context builder for Grpc. * * @author Nacos */ public class GrpcIdentityContextBuilder implements IdentityContextBuilder { private final NacosAuthConfig authConfig; public GrpcIdentityContextBuilder(NacosAuthConfig authConfig) { this.authConfig = authConfig; } /** * get identity context from grpc. * * @param request grpc request * @return IdentityContext request context */ @Override public IdentityContext build(Request request) { Optional authPluginService = AuthPluginManager.getInstance() .findAuthServiceSpiImpl(authConfig.getNacosAuthSystemType()); IdentityContext result = new IdentityContext(); getRemoteIp(request, result); if (!authPluginService.isPresent()) { return result; } Set identityNames = new HashSet<>(authPluginService.get().identityNames()); Map map = request.getHeaders(); for (Map.Entry entry : map.entrySet()) { if (identityNames.contains(entry.getKey())) { result.setParameter(entry.getKey(), entry.getValue()); } } return result; } private void getRemoteIp(Request request, IdentityContext result) { result.setParameter(Constants.Identity.REMOTE_IP, request.getHeader(Constants.Identity.X_REAL_IP)); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/context/HttpIdentityContextBuilder.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.context; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.constant.Constants; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginManager; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService; import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import java.util.Map; import java.util.Optional; import java.util.TreeMap; /** * Identity context builder for HTTP. * * @author Nacos */ public class HttpIdentityContextBuilder implements IdentityContextBuilder { private static final String X_FORWARDED_FOR = "X-Forwarded-For"; private static final String X_FORWARDED_FOR_SPLIT_SYMBOL = ","; private final NacosAuthConfig authConfig; public HttpIdentityContextBuilder(NacosAuthConfig authConfig) { this.authConfig = authConfig; } /** * get identity context from http. * * @param request user request * @return IdentityContext from request context */ @Override public IdentityContext build(HttpServletRequest request) { IdentityContext result = new IdentityContext(); getRemoteIp(request, result); Optional authPluginService = AuthPluginManager.getInstance() .findAuthServiceSpiImpl(authConfig.getNacosAuthSystemType()); if (!authPluginService.isPresent()) { return result; } // According to RFC2616, HTTP header and URI is case-insensitive, so use tree map with CASE_INSENSITIVE_ORDER // to match the identity key and save the real key in map value. Map identityNames = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (String each : authPluginService.get().identityNames()) { identityNames.put(each, each); } getIdentityFromHeader(request, result, identityNames); getIdentityFromParameter(request, result, identityNames); return result; } private void getIdentityFromHeader(HttpServletRequest request, IdentityContext result, Map identityNames) { Enumeration headerEnu = request.getHeaderNames(); while (headerEnu.hasMoreElements()) { String paraName = headerEnu.nextElement(); if (identityNames.containsKey(paraName)) { result.setParameter(identityNames.get(paraName), request.getHeader(paraName)); } } } private void getIdentityFromParameter(HttpServletRequest request, IdentityContext result, Map identityNames) { Enumeration paramEnu = request.getParameterNames(); while (paramEnu.hasMoreElements()) { String paraName = paramEnu.nextElement(); if (identityNames.containsKey(paraName)) { result.setParameter(identityNames.get(paraName), request.getParameter(paraName)); } } } private void getRemoteIp(HttpServletRequest request, IdentityContext result) { String remoteIp = StringUtils.EMPTY; String xForwardedFor = request.getHeader(X_FORWARDED_FOR); if (!StringUtils.isBlank(xForwardedFor)) { remoteIp = xForwardedFor.split(X_FORWARDED_FOR_SPLIT_SYMBOL)[0].trim(); } if (StringUtils.isBlank(remoteIp)) { String nginxHeader = request.getHeader(Constants.Identity.X_REAL_IP); remoteIp = StringUtils.isBlank(nginxHeader) ? request.getRemoteAddr() : nginxHeader; } result.setParameter(Constants.Identity.REMOTE_IP, remoteIp); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/context/IdentityContextBuilder.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.context; import com.alibaba.nacos.plugin.auth.api.IdentityContext; /** * Identify context. * * @author wuyfee */ public interface IdentityContextBuilder { /** * build identity context from request. * @param request user request * @return IdentityContext from request context */ IdentityContext build(T request); } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/AbstractResourceParser.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.Constants; import java.util.Properties; /** * Abstract Resource parser. * * @author xiweng.yy * @since 2.1.0 */ public abstract class AbstractResourceParser implements ResourceParser { @Override public Resource parse(R request, Secured secured) { String namespaceId = getNamespaceId(request, secured); String group = getGroup(request); String name = getResourceName(request); Properties properties = getProperties(request); String action = secured.action().toString(); properties.putIfAbsent(Constants.Resource.ACTION, action); injectTagsToProperties(properties, secured); return new Resource(namespaceId, group, name, secured.signType(), properties); } /** * Get namespaceId from request. * * @param request request * @return namespaceId */ protected abstract String getNamespaceId(R request); /** * Get namespaceId from request and secured. No implementation is required by default,this method can be rewrited * with special processing. * * @param request request * @param secured secured * @return namespaceId */ protected String getNamespaceId(R request, Secured secured) { return getNamespaceId(request); } /** * Get group name from request. * * @param request request * @return group name */ protected abstract String getGroup(R request); /** * Get resource name from request. * * @param request request * @return resource name */ protected abstract String getResourceName(R request); /** * Get custom properties from request. * * @param request request * @return custom properties */ protected abstract Properties getProperties(R request); /** * Inject tags defined in {@link Secured#tags()} into Resource properties, both key and value. * * @param properties properties in resource * @param secured secured */ protected void injectTagsToProperties(Properties properties, Secured secured) { for (String each : secured.tags()) { properties.put(each, each); } } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/DefaultResourceParser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.plugin.auth.api.Resource; /** * Default resource parser. * * @author nkorange * @author mai.jh * @since 1.2.0 */ public class DefaultResourceParser implements ResourceParser { @Override public Resource parse(Object request, Secured secured) { return Resource.EMPTY_RESOURCE; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/ResourceParser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.plugin.auth.api.Resource; /** * Resource parser. * * @author nkorange * @author mai.jh * @since 1.2.0 */ public interface ResourceParser { /** * Parse resource from request. * * @param request request * @param secured request secured * @return resource */ Resource parse(R request, Secured secured); } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/grpc/AbstractGrpcResourceParser.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.grpc; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.parser.AbstractResourceParser; import com.alibaba.nacos.plugin.auth.constant.Constants; import java.util.Properties; /** * Abstract Grpc Resource Parser. * * @author xiweng.yy */ public abstract class AbstractGrpcResourceParser extends AbstractResourceParser { @Override protected Properties getProperties(Request request) { Properties properties = new Properties(); properties.setProperty(Constants.Resource.REQUEST_CLASS, request.getClass().getSimpleName()); return properties; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/grpc/AiGrpcResourceParser.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.grpc; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.remote.request.AbstractAgentRequest; import com.alibaba.nacos.api.ai.remote.request.AbstractMcpRequest; import com.alibaba.nacos.api.ai.remote.request.ReleaseAgentCardRequest; import com.alibaba.nacos.api.ai.remote.request.ReleaseMcpServerRequest; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.common.utils.StringUtils; /** * AI Grpc resource parser. * * @author hongye.nhy xiweng.yy */ public class AiGrpcResourceParser extends AbstractGrpcResourceParser { @Override protected String getNamespaceId(Request request) { String namespaceId = null; if (request instanceof AbstractMcpRequest) { namespaceId = ((AbstractMcpRequest) request).getNamespaceId(); } else if (request instanceof AbstractAgentRequest) { namespaceId = ((AbstractAgentRequest) request).getNamespaceId(); } if (StringUtils.isBlank(namespaceId)) { namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; } return namespaceId; } @Override protected String getGroup(Request request) { return Constants.DEFAULT_GROUP; } @Override protected String getResourceName(Request request) { if (request instanceof AbstractMcpRequest) { return getMcpName((AbstractMcpRequest) request); } else if (request instanceof AbstractAgentRequest) { return getAgentName((AbstractAgentRequest) request); } return StringUtils.EMPTY; } private String getMcpName(AbstractMcpRequest request) { String mcpName = request.getMcpName(); if (request instanceof ReleaseMcpServerRequest) { ReleaseMcpServerRequest releaseMcpServerRequest = (ReleaseMcpServerRequest) request; if (null != releaseMcpServerRequest.getServerSpecification()) { mcpName = releaseMcpServerRequest.getServerSpecification().getName(); } } return StringUtils.isBlank(mcpName) ? StringUtils.EMPTY : mcpName; } private String getAgentName(AbstractAgentRequest request) { String agentName = request.getAgentName(); if (request instanceof ReleaseAgentCardRequest) { ReleaseAgentCardRequest releaseAgentCardRequest = (ReleaseAgentCardRequest) request; if (null != releaseAgentCardRequest.getAgentCard()) { agentName = releaseAgentCardRequest.getAgentCard().getName(); } } return StringUtils.isBlank(agentName) ? StringUtils.EMPTY : agentName; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/grpc/ConfigGrpcResourceParser.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.grpc; import com.alibaba.nacos.api.config.remote.request.AbstractConfigRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.common.utils.ReflectUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.List; /** * Config Grpc resource parser. * * @author xiweng.yy */ public class ConfigGrpcResourceParser extends AbstractGrpcResourceParser { @Override protected String getNamespaceId(Request request) { String namespaceId = StringUtils.EMPTY; if (request instanceof ConfigBatchListenRequest) { List configListenContexts = ((ConfigBatchListenRequest) request) .getConfigListenContexts(); if (!configListenContexts.isEmpty()) { namespaceId = ((ConfigBatchListenRequest) request).getConfigListenContexts().get(0).getTenant(); } } else if (request instanceof AbstractConfigRequest) { namespaceId = ((AbstractConfigRequest) request).getTenant(); } else { namespaceId = (String) ReflectUtils.getFieldValue(request, "tenant", StringUtils.EMPTY); } return StringUtils.isBlank(namespaceId) ? StringUtils.EMPTY : namespaceId; } @Override protected String getGroup(Request request) { String groupName; if (request instanceof AbstractConfigRequest) { groupName = ((AbstractConfigRequest) request).getGroup(); } else { groupName = (String) ReflectUtils .getFieldValue(request, com.alibaba.nacos.api.common.Constants.GROUP, StringUtils.EMPTY); } return StringUtils.isBlank(groupName) ? StringUtils.EMPTY : groupName; } @Override protected String getResourceName(Request request) { String dataId; if (request instanceof AbstractConfigRequest) { dataId = ((AbstractConfigRequest) request).getDataId(); } else { dataId = (String) ReflectUtils .getFieldValue(request, com.alibaba.nacos.api.common.Constants.DATA_ID, StringUtils.EMPTY); } return StringUtils.isBlank(dataId) ? StringUtils.EMPTY : dataId; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/grpc/NamingGrpcResourceParser.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.grpc; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.common.utils.ReflectUtils; import com.alibaba.nacos.common.utils.StringUtils; /** * Naming Grpc resource parser. * * @author xiweng.yy */ public class NamingGrpcResourceParser extends AbstractGrpcResourceParser { @Override protected String getNamespaceId(Request request) { if (request instanceof AbstractNamingRequest) { return ((AbstractNamingRequest) request).getNamespace(); } return (String) ReflectUtils.getFieldValue(request, PropertyKeyConst.NAMESPACE, StringUtils.EMPTY); } @Override protected String getGroup(Request request) { String groupName; if (request instanceof AbstractNamingRequest) { groupName = ((AbstractNamingRequest) request).getGroupName(); } else { groupName = (String) ReflectUtils.getFieldValue(request, CommonParams.GROUP_NAME, StringUtils.EMPTY); } return StringUtils.isBlank(groupName) ? StringUtils.EMPTY : groupName; } @Override protected String getResourceName(Request request) { String serviceName; if (request instanceof AbstractNamingRequest) { serviceName = ((AbstractNamingRequest) request).getServiceName(); } else { serviceName = (String) ReflectUtils.getFieldValue(request, CommonParams.SERVICE_NAME, ""); } return StringUtils.isBlank(serviceName) ? StringUtils.EMPTY : serviceName; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/http/AbstractHttpResourceParser.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.http; import com.alibaba.nacos.auth.parser.AbstractResourceParser; import jakarta.servlet.http.HttpServletRequest; /** * Abstract Http Resource Parser. * * @author xiweng.yy */ public abstract class AbstractHttpResourceParser extends AbstractResourceParser { } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/http/AiHttpResourceParser.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.http; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import jakarta.servlet.http.HttpServletRequest; import java.util.Properties; /** * Config Http resource parser. * * @author xiweng.yy */ public class AiHttpResourceParser extends AbstractHttpResourceParser { public static final String MCP_PATH = "/ai/mcp"; public static final String A2A_PATH = "/ai/a2a"; private static final String AGENT_CARD_PARAM = "agentCard"; @Override protected String getNamespaceId(HttpServletRequest request) { String namespaceId = request.getParameter(Constants.NAMESPACE_ID); if (StringUtils.isBlank(namespaceId)) { namespaceId = AiConstants.Mcp.MCP_DEFAULT_NAMESPACE; } return namespaceId; } @Override protected String getGroup(HttpServletRequest request) { return Constants.DEFAULT_GROUP; } @Override protected String getResourceName(HttpServletRequest request) { String url = request.getRequestURI(); if (url.contains(MCP_PATH)) { return getMcpName(request); } else if (url.contains(A2A_PATH)) { return getAgentName(request); } return StringUtils.EMPTY; } private String getMcpName(HttpServletRequest request) { String mcpName = request.getParameter("mcpName"); return StringUtils.isBlank(mcpName) ? StringUtils.EMPTY : mcpName; } private String getAgentName(HttpServletRequest request) { String agentName = request.getParameter("agentName"); if (request.getParameterMap().containsKey(AGENT_CARD_PARAM)) { agentName = deserializeAndGetAgentName(request.getParameter(AGENT_CARD_PARAM)); } return StringUtils.isBlank(agentName) ? StringUtils.EMPTY : agentName; } private String deserializeAndGetAgentName(String agentCardJson) { try { AgentCard agentCard = JacksonUtils.toObj(agentCardJson, AgentCard.class); return agentCard.getName(); } catch (Exception ignored) { return StringUtils.EMPTY; } } @Override protected Properties getProperties(HttpServletRequest request) { return new Properties(); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/http/ConfigHttpResourceParser.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.http; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.StringUtils; import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.Properties; /** * Config Http resource parser. * * @author xiweng.yy */ public class ConfigHttpResourceParser extends AbstractHttpResourceParser { @Override protected String getNamespaceId(HttpServletRequest request) { String namespaceId = request.getParameter(Constants.NAMESPACE_ID); if (StringUtils.isBlank(namespaceId)) { namespaceId = request.getParameter(Constants.TENANT); } return NamespaceUtil.processNamespaceParameter(namespaceId); } @Override protected String getNamespaceId(HttpServletRequest request, Secured secured) { return Arrays.stream(secured.tags()).filter(tag -> tag.startsWith(Constants.NAMESPACE_ID)) .map(tag -> tag.split(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.SPLITTER)) .filter(splitTags -> splitTags.length >= 2).map(splitTags -> request.getParameter(splitTags[1])) .filter(StringUtils::isNotBlank).findFirst().orElseGet(() -> getNamespaceId(request)); } @Override protected String getGroup(HttpServletRequest request) { String groupName = request.getParameter(Constants.GROUP_NAME); if (StringUtils.isBlank(groupName)) { groupName = request.getParameter(com.alibaba.nacos.api.common.Constants.GROUP); } return StringUtils.isBlank(groupName) ? StringUtils.EMPTY : groupName; } @Override protected String getResourceName(HttpServletRequest request) { String dataId = request.getParameter(com.alibaba.nacos.api.common.Constants.DATA_ID); return StringUtils.isBlank(dataId) ? StringUtils.EMPTY : dataId; } @Override protected Properties getProperties(HttpServletRequest request) { return new Properties(); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/parser/http/NamingHttpResourceParser.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.http; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.StringUtils; import jakarta.servlet.http.HttpServletRequest; import java.util.Properties; /** * Naming Http resource parser. * * @author xiweng.yy */ public class NamingHttpResourceParser extends AbstractHttpResourceParser { @Override protected String getNamespaceId(HttpServletRequest request) { return NamespaceUtil.processNamespaceParameter(request.getParameter(CommonParams.NAMESPACE_ID)); } /** * Group name from http request might be in service name with format ${group}@@${service}. So if group name is blank * or {@code null}, should try to get group name from service. * * @param request http request * @return group */ @Override protected String getGroup(HttpServletRequest request) { String groupName = request.getParameter(CommonParams.GROUP_NAME); if (StringUtils.isBlank(groupName)) { String serviceName = request.getParameter(CommonParams.SERVICE_NAME); groupName = NamingUtils.getGroupName(serviceName); } return StringUtils.isBlank(groupName) ? StringUtils.EMPTY : groupName; } @Override protected String getResourceName(HttpServletRequest request) { // See comment in #getGroup String serviceName = NamingUtils.getServiceName(request.getParameter(CommonParams.SERVICE_NAME)); return StringUtils.isBlank(serviceName) ? StringUtils.EMPTY : serviceName; } @Override protected Properties getProperties(HttpServletRequest request) { return new Properties(); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/serveridentity/DefaultChecker.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.serveridentity; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; /** * Nacos default server identity checker. * * @author xiweng.yy */ public class DefaultChecker implements ServerIdentityChecker { private NacosAuthConfig authConfig; @Override public void init(NacosAuthConfig authConfigs) { this.authConfig = authConfigs; } @Override public ServerIdentityResult check(ServerIdentity serverIdentity, Secured secured) { if (authConfig.getServerIdentityValue().equals(serverIdentity.getIdentityValue())) { return ServerIdentityResult.success(); } return ServerIdentityResult.noMatched(); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/serveridentity/ServerIdentity.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.serveridentity; /** * Nacos server identity. * * @author xiweng.yy */ public class ServerIdentity { private final String identityKey; private final String identityValue; public ServerIdentity(String identityKey, String identityValue) { this.identityKey = identityKey; this.identityValue = identityValue; } public String getIdentityKey() { return identityKey; } public String getIdentityValue() { return identityValue; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/serveridentity/ServerIdentityChecker.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.serveridentity; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; /** * Nacos server identity checker for nacos inner/admin API identity check. * * @author xiweng.yy */ public interface ServerIdentityChecker { /** * Do init checker. * * @param authConfig config for nacos auth. */ void init(NacosAuthConfig authConfig); /** * Do check nacos server identity. * * @param serverIdentity server identity * @param secured secured api secured annotation * @return result of checking server identity */ ServerIdentityResult check(ServerIdentity serverIdentity, Secured secured); } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/serveridentity/ServerIdentityCheckerHolder.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.serveridentity; import com.alibaba.nacos.common.spi.NacosServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.InvocationTargetException; import java.util.Collection; /** * Server Identity Checker SPI holder. * * @author xiweng.yy */ public class ServerIdentityCheckerHolder { private static final Logger LOGGER = LoggerFactory.getLogger(ServerIdentityCheckerHolder.class); private static final ServerIdentityCheckerHolder INSTANCE = new ServerIdentityCheckerHolder(); private Class checkerClass; private ServerIdentityCheckerHolder() { tryGetCheckerBySpi(); } public static ServerIdentityCheckerHolder getInstance() { return INSTANCE; } /** * Build a new checker. * * @return new checker instance. */ public ServerIdentityChecker newChecker() { try { return checkerClass.getDeclaredConstructor(new Class[0]).newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { return new DefaultChecker(); } } private synchronized void tryGetCheckerBySpi() { Collection checkers = NacosServiceLoader.load(ServerIdentityChecker.class); if (checkers.isEmpty()) { checkerClass = DefaultChecker.class; LOGGER.info("Not found ServerIdentityChecker implementation from SPI, use default."); return; } if (checkers.size() > 1) { checkerClass = showAllImplementations(checkers); return; } checkerClass = checkers.iterator().next().getClass(); LOGGER.info("Found ServerIdentityChecker implementation {}", checkerClass.getClass().getCanonicalName()); } private Class showAllImplementations(Collection checkers) { ServerIdentityChecker result = checkers.iterator().next(); for (ServerIdentityChecker each : checkers) { LOGGER.warn("Found ServerIdentityChecker implementation {}", each.getClass().getCanonicalName()); } LOGGER.warn("Found more than one ServerIdentityChecker implementation from SPI, use the first one {}.", result.getClass().getCanonicalName()); return result.getClass(); } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/serveridentity/ServerIdentityResult.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.serveridentity; /** * Nacos server identity check result. * * @author xiweng.yy */ public class ServerIdentityResult { private final ResultStatus status; private final String message; private ServerIdentityResult(ResultStatus status, String message) { this.status = status; this.message = message; } public ResultStatus getStatus() { return status; } public String getMessage() { return message; } public static ServerIdentityResult success() { return new ServerIdentityResult(ResultStatus.MATCHED, "Server identity matched."); } public static ServerIdentityResult noMatched() { return new ServerIdentityResult(ResultStatus.NOT_MATCHED, "Server identity not matched."); } public static ServerIdentityResult fail(String message) { return new ServerIdentityResult(ResultStatus.FAIL, message); } public enum ResultStatus { /** * Nacos server identity matched. */ MATCHED, /** * Nacos server identity not matched, need authentication. */ NOT_MATCHED, /** * Nacos server identity check failed. */ FAIL; } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/util/AuthHeaderUtil.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.util; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.utils.StringUtils; /** * Auth header util. * * @author xiweng.yy */ public class AuthHeaderUtil { /** * Add identity info to Http header. * * @param header http header * @param authConfig nacos auth config */ public static void addIdentityToHeader(Header header, NacosAuthConfig authConfig) { if (!authConfig.isSupportServerIdentity()) { return; } if (StringUtils.isNotBlank(authConfig.getServerIdentityKey())) { header.addParam(authConfig.getServerIdentityKey(), authConfig.getServerIdentityValue()); } } /** * Add identity info to Grpc request header. * * @param request grpc request * @param authConfig nacos auth config */ public static void addIdentityToHeader(Request request, NacosAuthConfig authConfig) { if (!authConfig.isSupportServerIdentity()) { return; } if (StringUtils.isNotBlank(authConfig.getServerIdentityKey())) { request.putHeader(authConfig.getServerIdentityKey(), authConfig.getServerIdentityValue()); } } } ================================================ FILE: auth/src/main/java/com/alibaba/nacos/auth/util/Loggers.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.util; import ch.qos.logback.classic.Level; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Loggers for core. * * @author nkorange * @since 1.2.0 */ public class Loggers { private static final String AUTH_LOG_NAME = "auth"; public static final Logger AUTH = LoggerFactory.getLogger("com.alibaba.nacos.auth"); public static void setLogLevel(String logName, String level) { if (AUTH_LOG_NAME.equals(logName)) { ((ch.qos.logback.classic.Logger) AUTH).setLevel(Level.valueOf(level)); } } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/GrpcProtocolAuthServiceTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth; import com.alibaba.nacos.api.ai.remote.request.AbstractAgentRequest; import com.alibaba.nacos.api.ai.remote.request.AbstractMcpRequest; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.mock.MockAuthPluginService; import com.alibaba.nacos.auth.mock.MockResourceParser; import com.alibaba.nacos.auth.serveridentity.ServerIdentityResult; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.Permission; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.auth.exception.AccessException; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Method; 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.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class GrpcProtocolAuthServiceTest { @Mock private NacosAuthConfig authConfig; private ConfigPublishRequest configRequest; private AbstractNamingRequest namingRequest; private AbstractMcpRequest mcpRequest; private AbstractAgentRequest agentRequest; private GrpcProtocolAuthService protocolAuthService; @BeforeEach void setUp() throws Exception { protocolAuthService = new GrpcProtocolAuthService(authConfig); protocolAuthService.initialize(); mockConfigRequest(); mockNamingRequest(); mockMcpRequest(); mockAgentRequest(); } private void mockConfigRequest() { configRequest = new ConfigPublishRequest(); configRequest.setTenant("testCNs"); configRequest.setGroup("testCG"); configRequest.setDataId("testD"); } private void mockNamingRequest() { namingRequest = new AbstractNamingRequest() { }; namingRequest.setNamespace("testNNs"); namingRequest.setGroupName("testNG"); namingRequest.setServiceName("testS"); } private void mockMcpRequest() { mcpRequest = new AbstractMcpRequest() { }; mcpRequest.setNamespaceId("testNNs"); mcpRequest.setMcpName("testS"); } private void mockAgentRequest() { agentRequest = new AbstractAgentRequest() { }; agentRequest.setNamespaceId("testNNs"); agentRequest.setAgentName("testS"); } @Test @Secured(resource = "testResource") void testParseResourceWithSpecifiedResource() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithSpecifiedResource"); Resource actual = protocolAuthService.parseResource(namingRequest, secured); assertEquals("testResource", actual.getName()); assertEquals(SignType.SPECIFIED, actual.getType()); assertNull(actual.getNamespaceId()); assertNull(actual.getGroup()); assertNotNull(actual.getProperties()); assertTrue(actual.getProperties().isEmpty()); } @Test @Secured(signType = "non-exist") void testParseResourceWithNonExistType() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithNonExistType"); Resource actual = protocolAuthService.parseResource(namingRequest, secured); assertEquals(Resource.EMPTY_RESOURCE, actual); } @Test @Secured(signType = "non-exist", parser = MockResourceParser.class) void testParseResourceWithNonExistTypeException() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithNonExistTypeException"); Resource actual = protocolAuthService.parseResource(namingRequest, secured); assertEquals(Resource.EMPTY_RESOURCE, actual); } @Test @Secured() void testParseResourceWithNamingType() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithNamingType"); Resource actual = protocolAuthService.parseResource(namingRequest, secured); assertEquals(SignType.NAMING, actual.getType()); assertEquals("testS", actual.getName()); assertEquals("testNNs", actual.getNamespaceId()); assertEquals("testNG", actual.getGroup()); assertNotNull(actual.getProperties()); } @Test @Secured(signType = SignType.CONFIG) void testParseResourceWithConfigType() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithConfigType"); Resource actual = protocolAuthService.parseResource(configRequest, secured); assertEquals(SignType.CONFIG, actual.getType()); assertEquals("testD", actual.getName()); assertEquals("testCNs", actual.getNamespaceId()); assertEquals("testCG", actual.getGroup()); assertNotNull(actual.getProperties()); } @Test @Secured(signType = SignType.AI) void testParseResourceWithMcpType() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithMcpType"); Resource actual = protocolAuthService.parseResource(mcpRequest, secured); assertEquals(SignType.AI, actual.getType()); assertEquals(mcpRequest.getMcpName(), actual.getName()); assertEquals(mcpRequest.getNamespaceId(), actual.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, actual.getGroup()); assertNotNull(actual.getProperties()); } @Test @Secured(signType = SignType.AI) void testParseResourceWithAgentType() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithAgentType"); Resource actual = protocolAuthService.parseResource(agentRequest, secured); assertEquals(SignType.AI, actual.getType()); assertEquals(agentRequest.getAgentName(), actual.getName()); assertEquals(agentRequest.getNamespaceId(), actual.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, actual.getGroup()); assertNotNull(actual.getProperties()); } @Test void testParseIdentity() { IdentityContext actual = protocolAuthService.parseIdentity(namingRequest); assertNotNull(actual); } @Test void testValidateIdentityWithoutPlugin() throws AccessException { IdentityContext identityContext = new IdentityContext(); assertTrue(protocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE).isSuccess()); } @Test void testValidateIdentityWithPlugin() throws AccessException { Mockito.when(authConfig.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); IdentityContext identityContext = new IdentityContext(); assertFalse(protocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE).isSuccess()); } @Test void testValidateAuthorityWithoutPlugin() throws AccessException { assertTrue(protocolAuthService.validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, "")).isSuccess()); } @Test void testValidateAuthorityWithPlugin() throws AccessException { Mockito.when(authConfig.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); assertFalse(protocolAuthService.validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, "")).isSuccess()); } @Test @Secured(signType = SignType.CONFIG) void testEnabledAuthWithPlugin() throws NoSuchMethodException { Mockito.when(authConfig.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); Secured secured = getMethodSecure("testEnabledAuthWithPlugin"); assertTrue(protocolAuthService.enableAuth(secured)); } @Test @Secured(signType = SignType.CONFIG) void testEnabledAuthWithoutPlugin() throws NoSuchMethodException { Mockito.when(authConfig.getNacosAuthSystemType()).thenReturn("non-exist-plugin"); Secured secured = getMethodSecure("testEnabledAuthWithoutPlugin"); assertFalse(protocolAuthService.enableAuth(secured)); } @Test @Secured(apiType = ApiType.INNER_API) void testCheckServerIdentityWithoutIdentityConfig() throws NoSuchMethodException { Secured secured = getMethodSecure("testCheckServerIdentityWithoutIdentityConfig"); ServerIdentityResult result = protocolAuthService.checkServerIdentity(namingRequest, secured); assertEquals(ServerIdentityResult.ResultStatus.FAIL, result.getStatus()); assertEquals("Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`" + " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`", result.getMessage()); when(authConfig.getServerIdentityKey()).thenReturn("1"); result = protocolAuthService.checkServerIdentity(namingRequest, secured); assertEquals(ServerIdentityResult.ResultStatus.FAIL, result.getStatus()); assertEquals("Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`" + " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`", result.getMessage()); } @Test @Secured(apiType = ApiType.INNER_API) void testCheckServerIdentityNotMatched() throws NoSuchMethodException { Secured secured = getMethodSecure("testCheckServerIdentityNotMatched"); when(authConfig.getServerIdentityKey()).thenReturn("1"); when(authConfig.getServerIdentityValue()).thenReturn("2"); ServerIdentityResult result = protocolAuthService.checkServerIdentity(namingRequest, secured); assertEquals(ServerIdentityResult.ResultStatus.NOT_MATCHED, result.getStatus()); namingRequest.putHeader("1", "3"); result = protocolAuthService.checkServerIdentity(namingRequest, secured); assertEquals(ServerIdentityResult.ResultStatus.NOT_MATCHED, result.getStatus()); } @Test @Secured(apiType = ApiType.INNER_API) void testCheckServerIdentityMatched() throws NoSuchMethodException { when(authConfig.getServerIdentityKey()).thenReturn("1"); when(authConfig.getServerIdentityValue()).thenReturn("2"); namingRequest.putHeader("1", "2"); Secured secured = getMethodSecure("testCheckServerIdentityMatched"); ServerIdentityResult result = protocolAuthService.checkServerIdentity(namingRequest, secured); assertEquals(ServerIdentityResult.ResultStatus.MATCHED, result.getStatus()); } @Test @Secured void testCheckServerIdentityForOtherTypeApi() throws NoSuchMethodException { namingRequest.putHeader("1", "2"); Secured secured = getMethodSecure("testCheckServerIdentityForOtherTypeApi"); ServerIdentityResult result = protocolAuthService.checkServerIdentity(namingRequest, secured); assertEquals(ServerIdentityResult.ResultStatus.NOT_MATCHED, result.getStatus()); } private Secured getMethodSecure(String methodName) throws NoSuchMethodException { Method method = GrpcProtocolAuthServiceTest.class.getDeclaredMethod(methodName); return method.getAnnotation(Secured.class); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/HttpProtocolAuthServiceTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.mock.MockAuthPluginService; import com.alibaba.nacos.auth.mock.MockResourceParser; import com.alibaba.nacos.auth.serveridentity.ServerIdentityResult; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.Permission; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.auth.exception.AccessException; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; 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.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class HttpProtocolAuthServiceTest { @Mock private NacosAuthConfig authConfig; @Mock private HttpServletRequest request; private HttpProtocolAuthService protocolAuthService; @BeforeEach void setUp() throws Exception { protocolAuthService = new HttpProtocolAuthService(authConfig); protocolAuthService.initialize(); when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNNs"); when(request.getParameter(eq(CommonParams.GROUP_NAME))).thenReturn("testNG"); when(request.getParameter(eq(CommonParams.SERVICE_NAME))).thenReturn("testS"); when(request.getParameter(eq("tenant"))).thenReturn("testCNs"); when(request.getParameter(eq(Constants.GROUP))).thenReturn("testCG"); when(request.getParameter(eq(Constants.DATA_ID))).thenReturn("testD"); } @Test @Secured(resource = "testResource", tags = {"testTag"}) void testParseResourceWithSpecifiedResource() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithSpecifiedResource"); Resource actual = protocolAuthService.parseResource(request, secured); assertEquals("testResource", actual.getName()); assertEquals(SignType.SPECIFIED, actual.getType()); assertNull(actual.getNamespaceId()); assertNull(actual.getGroup()); assertNotNull(actual.getProperties()); assertEquals(1, actual.getProperties().size()); assertEquals("testTag", actual.getProperties().get("testTag")); } @Test @Secured(signType = "non-exist") void testParseResourceWithNonExistType() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithNonExistType"); Resource actual = protocolAuthService.parseResource(request, secured); assertEquals(Resource.EMPTY_RESOURCE, actual); } @Test @Secured(signType = "non-exist", parser = MockResourceParser.class) void testParseResourceWithNonExistTypeException() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithNonExistTypeException"); Resource actual = protocolAuthService.parseResource(request, secured); assertEquals(Resource.EMPTY_RESOURCE, actual); } @Test @Secured() void testParseResourceWithNamingType() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithNamingType"); Resource actual = protocolAuthService.parseResource(request, secured); assertEquals(SignType.NAMING, actual.getType()); assertEquals("testS", actual.getName()); assertEquals("testNNs", actual.getNamespaceId()); assertEquals("testNG", actual.getGroup()); assertNotNull(actual.getProperties()); } @Test @Secured(signType = SignType.CONFIG) void testParseResourceWithConfigTypeForNewGroup() throws NoSuchMethodException { Secured secured = getMethodSecure("testParseResourceWithConfigTypeForNewGroup"); Resource actual = protocolAuthService.parseResource(request, secured); assertEquals(SignType.CONFIG, actual.getType()); assertEquals("testD", actual.getName()); assertEquals("testNNs", actual.getNamespaceId()); assertEquals("testNG", actual.getGroup()); assertNotNull(actual.getProperties()); } @Test @Secured(signType = SignType.CONFIG) void testParseResourceWithConfigTypeForOldGroup() throws NoSuchMethodException { when(request.getParameter(eq(CommonParams.GROUP_NAME))).thenReturn(""); Secured secured = getMethodSecure("testParseResourceWithConfigTypeForOldGroup"); Resource actual = protocolAuthService.parseResource(request, secured); assertEquals(SignType.CONFIG, actual.getType()); assertEquals("testD", actual.getName()); assertEquals("testNNs", actual.getNamespaceId()); assertEquals("testCG", actual.getGroup()); assertNotNull(actual.getProperties()); } @Test void testParseIdentity() { IdentityContext actual = protocolAuthService.parseIdentity(request); assertNotNull(actual); } @Test void testValidateIdentityWithoutPlugin() throws AccessException { IdentityContext identityContext = new IdentityContext(); assertTrue(protocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE).isSuccess()); } @Test void testValidateIdentityWithPlugin() throws AccessException { when(authConfig.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); IdentityContext identityContext = new IdentityContext(); assertFalse(protocolAuthService.validateIdentity(identityContext, Resource.EMPTY_RESOURCE).isSuccess()); } @Test void testValidateAuthorityWithoutPlugin() throws AccessException { assertTrue(protocolAuthService.validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, "")).isSuccess()); } @Test void testValidateAuthorityWithPlugin() throws AccessException { when(authConfig.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); assertFalse(protocolAuthService.validateAuthority(new IdentityContext(), new Permission(Resource.EMPTY_RESOURCE, "")).isSuccess()); } @Test @Secured(signType = SignType.CONFIG) void testEnabledAuthWithPlugin() throws NoSuchMethodException { when(authConfig.getNacosAuthSystemType()).thenReturn(MockAuthPluginService.TEST_PLUGIN); Secured secured = getMethodSecure("testEnabledAuthWithPlugin"); assertTrue(protocolAuthService.enableAuth(secured)); } @Test @Secured(signType = SignType.CONFIG) void testEnabledAuthWithoutPlugin() throws NoSuchMethodException { when(authConfig.getNacosAuthSystemType()).thenReturn("non-exist-plugin"); Secured secured = getMethodSecure("testEnabledAuthWithoutPlugin"); assertFalse(protocolAuthService.enableAuth(secured)); } @Test void testCheckServerIdentityWithoutIdentityConfig() throws NoSuchMethodException { Secured secured = getMethodSecure("testCheckServerIdentityWithoutIdentityConfig"); ServerIdentityResult result = protocolAuthService.checkServerIdentity(request, secured); assertEquals(ServerIdentityResult.ResultStatus.FAIL, result.getStatus()); assertEquals("Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`" + " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`", result.getMessage()); when(authConfig.getServerIdentityKey()).thenReturn("1"); result = protocolAuthService.checkServerIdentity(request, secured); assertEquals(ServerIdentityResult.ResultStatus.FAIL, result.getStatus()); assertEquals("Invalid server identity key or value, Please make sure set `nacos.core.auth.server.identity.key`" + " and `nacos.core.auth.server.identity.value`, or open `nacos.core.auth.enable.userAgentAuthWhite`", result.getMessage()); } @Test void testCheckServerIdentityNotMatched() throws NoSuchMethodException { Secured secured = getMethodSecure("testCheckServerIdentityNotMatched"); when(authConfig.getServerIdentityKey()).thenReturn("1"); when(authConfig.getServerIdentityValue()).thenReturn("2"); ServerIdentityResult result = protocolAuthService.checkServerIdentity(request, secured); assertEquals(ServerIdentityResult.ResultStatus.NOT_MATCHED, result.getStatus()); when(request.getHeader("1")).thenReturn("3"); result = protocolAuthService.checkServerIdentity(request, secured); assertEquals(ServerIdentityResult.ResultStatus.NOT_MATCHED, result.getStatus()); } @Test void testCheckServerIdentityMatched() throws NoSuchMethodException { when(authConfig.getServerIdentityKey()).thenReturn("1"); when(authConfig.getServerIdentityValue()).thenReturn("2"); when(request.getHeader("1")).thenReturn("2"); Secured secured = getMethodSecure("testCheckServerIdentityMatched"); ServerIdentityResult result = protocolAuthService.checkServerIdentity(request, secured); assertEquals(ServerIdentityResult.ResultStatus.MATCHED, result.getStatus()); } private Secured getMethodSecure(String methodName) throws NoSuchMethodException { Method method = HttpProtocolAuthServiceTest.class.getDeclaredMethod(methodName); return method.getAnnotation(Secured.class); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/config/AuthErrorCodeTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.config; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(MockitoExtension.class) class AuthErrorCodeTest { @Test void testInvalidTypeCode() { assertEquals(50001, AuthErrorCode.INVALID_TYPE.getCode()); } @Test void testInvalidTypeMsg() { assertTrue(AuthErrorCode.INVALID_TYPE.getMsg().contains("Invalid auth type")); assertTrue(AuthErrorCode.INVALID_TYPE.getMsg().contains("nacos.core.auth.system.type")); } @Test void testEmptyIdentityCode() { assertEquals(50002, AuthErrorCode.EMPTY_IDENTITY.getCode()); } @Test void testEmptyIdentityMsg() { assertTrue(AuthErrorCode.EMPTY_IDENTITY.getMsg().contains("Empty identity")); assertTrue(AuthErrorCode.EMPTY_IDENTITY.getMsg().contains("nacos.core.auth.server.identity.key")); } @Test void testValues() { assertEquals(2, AuthErrorCode.values().length); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/config/NacosAuthConfigHolderTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.config; import com.alibaba.nacos.common.spi.NacosServiceLoader; 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.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; 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; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NacosAuthConfigHolderTest { @Mock NacosAuthConfig nacosAuthConfig; private Map cachedConfigMap; @BeforeEach void setUp() { cachedConfigMap = (Map) ReflectionTestUtils.getField( NacosAuthConfigHolder.getInstance(), "nacosAuthConfigMap"); Map mockMap = Map.of("test", nacosAuthConfig); ReflectionTestUtils.setField(NacosAuthConfigHolder.getInstance(), "nacosAuthConfigMap", mockMap); } @AfterEach void tearDown() { if (cachedConfigMap != null) { ReflectionTestUtils.setField(NacosAuthConfigHolder.getInstance(), "nacosAuthConfigMap", cachedConfigMap); } } @Test void getNacosAuthConfigByScope() { assertEquals(nacosAuthConfig, NacosAuthConfigHolder.getInstance().getNacosAuthConfigByScope("test")); assertNull(NacosAuthConfigHolder.getInstance().getNacosAuthConfigByScope("test1")); } @Test void getAllNacosAuthConfig() { assertEquals(1, NacosAuthConfigHolder.getInstance().getAllNacosAuthConfig().size()); assertTrue(NacosAuthConfigHolder.getInstance().getAllNacosAuthConfig().contains(nacosAuthConfig)); } @Test void isAnyAuthEnabled() { assertFalse(NacosAuthConfigHolder.getInstance().isAnyAuthEnabled()); when(nacosAuthConfig.isAuthEnabled()).thenReturn(true); assertTrue(NacosAuthConfigHolder.getInstance().isAnyAuthEnabled()); } @Test void testIsAnyAuthEnabledWithScopeFound() { when(nacosAuthConfig.isAuthEnabled()).thenReturn(true); assertTrue(NacosAuthConfigHolder.getInstance().isAnyAuthEnabled("test")); } @Test void testIsAnyAuthEnabledWithScopeNotFound() { assertFalse(NacosAuthConfigHolder.getInstance().isAnyAuthEnabled("nonExist")); } @Test void testIsAnyAuthEnabledWithScopeDisabled() { when(nacosAuthConfig.isAuthEnabled()).thenReturn(false); assertFalse(NacosAuthConfigHolder.getInstance().isAnyAuthEnabled("test")); } @Test void testGetNacosAuthSystemType() { when(nacosAuthConfig.getNacosAuthSystemType()).thenReturn("nacos"); assertEquals("nacos", NacosAuthConfigHolder.getInstance().getNacosAuthSystemType()); } @Test void testGetNacosAuthSystemTypeWhenEmpty() { ReflectionTestUtils.setField(NacosAuthConfigHolder.getInstance(), "nacosAuthConfigMap", Map.of()); assertNull(NacosAuthConfigHolder.getInstance().getNacosAuthSystemType()); } @Test void testConstructorWithSpiProvider() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { Map, Collection>> servicesMap = (Map, Collection>>) ReflectionTestUtils.getField( NacosServiceLoader.class, "SERVICES"); List> classes = new LinkedList<>(); classes.add(MockNacosAuthConfig.class); servicesMap.put(NacosAuthConfig.class, classes); try { Constructor constructor = NacosAuthConfigHolder.class.getDeclaredConstructor(); constructor.setAccessible(true); NacosAuthConfigHolder holder = constructor.newInstance(); assertEquals(1, holder.getAllNacosAuthConfig().size()); } finally { servicesMap.remove(NacosAuthConfig.class); } } public static class MockNacosAuthConfig implements NacosAuthConfig { @Override public String getAuthScope() { return "mock"; } @Override public boolean isAuthEnabled() { return false; } @Override public String getNacosAuthSystemType() { return "nacos"; } @Override public boolean isSupportServerIdentity() { return false; } @Override public String getServerIdentityKey() { return ""; } @Override public String getServerIdentityValue() { return ""; } } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/context/GrpcIdentityContextBuilderTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.context; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.constant.Constants; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class GrpcIdentityContextBuilderTest { private static final String TEST_PLUGIN = "test"; private static final String IDENTITY_TEST_KEY = "identity-test-key"; private static final String IDENTITY_TEST_VALUE = "identity-test-value"; @Mock private NacosAuthConfig authConfig; @Mock private Request request; private GrpcIdentityContextBuilder identityContextBuilder; @BeforeEach void setUp() throws Exception { identityContextBuilder = new GrpcIdentityContextBuilder(authConfig); when(authConfig.getNacosAuthSystemType()).thenReturn(TEST_PLUGIN); Map headers = new HashMap<>(); headers.put(IDENTITY_TEST_KEY, IDENTITY_TEST_VALUE); when(request.getHeaders()).thenReturn(headers); when(request.getHeader(Constants.Identity.X_REAL_IP)).thenReturn("1.1.1.1"); } @Test void testBuildWithoutPlugin() { when(authConfig.getNacosAuthSystemType()).thenReturn("non-exist"); IdentityContext actual = identityContextBuilder.build(request); assertNull(actual.getParameter(IDENTITY_TEST_KEY)); } @Test void testBuild() { IdentityContext actual = identityContextBuilder.build(request); assertEquals(IDENTITY_TEST_VALUE, actual.getParameter(IDENTITY_TEST_KEY)); assertEquals("1.1.1.1", actual.getParameter(Constants.Identity.REMOTE_IP)); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/context/HtppIdentityContextBuilderTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.context; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.constant.Constants; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import jakarta.servlet.http.HttpServletRequest; import java.util.Enumeration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class HtppIdentityContextBuilderTest { private static final String TEST_PLUGIN = "test"; private static final String IDENTITY_TEST_KEY = "identity-test-key"; private static final String IDENTITY_TEST_VALUE = "identity-test-value"; @Mock private NacosAuthConfig authConfig; @Mock private HttpServletRequest request; @Mock private Enumeration headerNames; @Mock private Enumeration parameterNames; private HttpIdentityContextBuilder identityContextBuilder; @BeforeEach void setUp() throws Exception { identityContextBuilder = new HttpIdentityContextBuilder(authConfig); when(authConfig.getNacosAuthSystemType()).thenReturn(TEST_PLUGIN); } @Test void testBuildWithoutPlugin() { mockHeader(true); mockParameter(true); when(authConfig.getNacosAuthSystemType()).thenReturn("non-exist"); IdentityContext actual = identityContextBuilder.build(request); assertNull(actual.getParameter(IDENTITY_TEST_KEY)); } @Test void testBuildWithHeader() { mockHeader(true); mockParameter(false); IdentityContext actual = identityContextBuilder.build(request); assertEquals(IDENTITY_TEST_VALUE, actual.getParameter(IDENTITY_TEST_KEY)); assertEquals("1.1.1.1", actual.getParameter(Constants.Identity.REMOTE_IP)); } @Test void testBuildWithParameter() { mockHeader(false); mockParameter(true); IdentityContext actual = identityContextBuilder.build(request); assertEquals(IDENTITY_TEST_VALUE, actual.getParameter(IDENTITY_TEST_KEY)); } @Test void testBuildWithXForwardedFor() { when(request.getHeader("X-Forwarded-For")).thenReturn("10.0.0.1, 10.0.0.2"); mockHeader(false); mockParameter(false); when(authConfig.getNacosAuthSystemType()).thenReturn("non-exist"); IdentityContext actual = identityContextBuilder.build(request); assertEquals("10.0.0.1", actual.getParameter(Constants.Identity.REMOTE_IP)); } private void mockHeader(boolean contained) { when(request.getHeaderNames()).thenReturn(headerNames); if (contained) { when(headerNames.hasMoreElements()).thenReturn(Boolean.TRUE, Boolean.FALSE); when(headerNames.nextElement()).thenReturn(IDENTITY_TEST_KEY, (String) null); when(request.getHeader(IDENTITY_TEST_KEY)).thenReturn(IDENTITY_TEST_VALUE); when(request.getHeader(Constants.Identity.X_REAL_IP)).thenReturn("1.1.1.1"); } } private void mockParameter(boolean contained) { when(request.getParameterNames()).thenReturn(parameterNames); if (contained) { when(parameterNames.hasMoreElements()).thenReturn(Boolean.TRUE, Boolean.FALSE); when(parameterNames.nextElement()).thenReturn(IDENTITY_TEST_KEY, (String) null); when(request.getParameter(IDENTITY_TEST_KEY)).thenReturn(IDENTITY_TEST_VALUE); } } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/mock/MockAuthPluginService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.mock; import com.alibaba.nacos.plugin.auth.api.AuthResult; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import com.alibaba.nacos.plugin.auth.api.Permission; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService; import java.util.Collection; import java.util.Collections; public class MockAuthPluginService implements AuthPluginService { public static final String TEST_PLUGIN = "test"; public static final String IDENTITY_TEST_KEY = "identity-test-key"; @Override public Collection identityNames() { return Collections.singletonList(IDENTITY_TEST_KEY); } @Override public boolean enableAuth(ActionTypes action, String type) { return true; } @Override public String getAuthServiceName() { return TEST_PLUGIN; } @Override public AuthResult validateAuthority(IdentityContext identityContext, Permission permission) { return AuthResult.failureResult(401, "mock auth failed"); } @Override public AuthResult validateIdentity(IdentityContext identityContext, Resource resource) { return AuthResult.failureResult(403, "mock auth failed"); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/mock/MockResourceParser.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.mock; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.parser.ResourceParser; import com.alibaba.nacos.plugin.auth.api.Resource; public class MockResourceParser implements ResourceParser { @Override public Resource parse(Object request, Secured secured) { throw new NacosRuntimeException(500); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/parser/grpc/AiGrpcResourceParserTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.grpc; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.remote.request.AbstractAgentRequest; import com.alibaba.nacos.api.ai.remote.request.AbstractMcpRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.remote.request.ReleaseAgentCardRequest; import com.alibaba.nacos.api.ai.remote.request.ReleaseMcpServerRequest; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.plugin.auth.api.Resource; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.lang.reflect.Method; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; class AiGrpcResourceParserTest { private AiGrpcResourceParser resourceParser; private static Stream fulContextRequests() { Arguments case1 = Arguments.of(mockMcpRequest("testNs", "testName"), "testNs", "testName", MockMcpRequest.class.getSimpleName()); Arguments case2 = Arguments.of(mockAgentRequest("testNs", "testName"), "testNs", "testName", MockAgentRequest.class.getSimpleName()); Arguments case3 = Arguments.of(makeReleaseAgentCardRequest("testNs", "testName", "testCardName"), "testNs", "testCardName", ReleaseAgentCardRequest.class.getSimpleName()); Arguments case4 = Arguments.of(mockOtherRequest("testNs", "testName"), AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "", NotifySubscriberRequest.class.getSimpleName()); Arguments case5 = Arguments.of(makeReleaseMcpServerRequest("testNs", "testName", "testSpecName"), "testNs", "testSpecName", ReleaseMcpServerRequest.class.getSimpleName()); return Stream.of(case1, case2, case3, case5); } private static Stream withoutNamespaceRequests() { Arguments case1 = Arguments.of(mockMcpRequest("", "testName"), AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "testName", MockMcpRequest.class.getSimpleName()); Arguments case2 = Arguments.of(mockAgentRequest(null, "testName"), AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "testName", MockAgentRequest.class.getSimpleName()); Arguments case3 = Arguments.of(mockOtherRequest(null, "testName"), AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "", NotifySubscriberRequest.class.getSimpleName()); return Stream.of(case1, case2, case3); } private static Stream withoutNameRequests() { Arguments case1 = Arguments.of(mockMcpRequest("testNs", ""), "testNs", "", MockMcpRequest.class.getSimpleName()); Arguments case2 = Arguments.of(mockAgentRequest("testNs", null), "testNs", "", MockAgentRequest.class.getSimpleName()); Arguments case3 = Arguments.of(mockOtherRequest("testNs", ""), AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, "", NotifySubscriberRequest.class.getSimpleName()); return Stream.of(case1, case2, case3); } @BeforeEach void setUp() throws Exception { resourceParser = new AiGrpcResourceParser(); } @ParameterizedTest @MethodSource({"fulContextRequests", "withoutNamespaceRequests", "withoutNameRequests"}) @Secured(signType = SignType.AI) void testParse(Request request, String expectedNamespaceId, String expectedName, String expectedRequestClassName) throws NoSuchMethodException { Secured secured = getMethodSecure(); Resource actual = resourceParser.parse(request, secured); assertEquals(expectedNamespaceId, actual.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, actual.getGroup()); assertEquals(expectedName, actual.getName()); assertEquals(SignType.AI, actual.getType()); assertEquals(expectedRequestClassName, actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } private static AbstractMcpRequest mockMcpRequest(String testNs, String testS) { MockMcpRequest result = new MockMcpRequest(); result.setNamespaceId(testNs); result.setMcpName(testS); return result; } private static MockAgentRequest mockAgentRequest(String testNs, String testS) { MockAgentRequest result = new MockAgentRequest(); result.setNamespaceId(testNs); result.setAgentName(testS); return result; } private static ReleaseAgentCardRequest makeReleaseAgentCardRequest(String testNs, String agentName, String cardName) { ReleaseAgentCardRequest result = new ReleaseAgentCardRequest(); result.setNamespaceId(testNs); result.setAgentName(agentName); AgentCard agentCard = new AgentCard(); agentCard.setName(cardName); result.setAgentCard(agentCard); return result; } private static ReleaseMcpServerRequest makeReleaseMcpServerRequest(String testNs, String mcpName, String specName) { ReleaseMcpServerRequest result = new ReleaseMcpServerRequest(); result.setNamespaceId(testNs); result.setMcpName(mcpName); McpServerBasicInfo serverSpecification = new McpServerBasicInfo(); serverSpecification.setName(specName); result.setServerSpecification(serverSpecification); return result; } private static Request mockOtherRequest(String testNs, String testS) { NotifySubscriberRequest result = new NotifySubscriberRequest(); result.setNamespace(testNs); result.setGroupName(""); result.setServiceName(testS); return result; } @Secured(signType = SignType.AI) void forSecureAnnotationMethod() { } private Secured getMethodSecure() throws NoSuchMethodException { Method method = AiGrpcResourceParserTest.class.getDeclaredMethod("forSecureAnnotationMethod"); return method.getAnnotation(Secured.class); } private static class MockMcpRequest extends AbstractMcpRequest { } private static class MockAgentRequest extends AbstractAgentRequest { } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/parser/grpc/ConfigGrpcResourceParserTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.grpc; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.Resource; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.assertEquals; class ConfigGrpcResourceParserTest { private ConfigGrpcResourceParser resourceParser; @BeforeEach void setUp() throws Exception { resourceParser = new ConfigGrpcResourceParser(); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithFullContext() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockConfigRequest("testNs", "testG", "testD"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithoutNamespace() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockConfigRequest("", "testG", "testD"); Resource actual = resourceParser.parse(request, secured); assertEquals("", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithoutGroup() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockConfigRequest("testNs", "", "testD"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(StringUtils.EMPTY, actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithoutDataId() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockConfigRequest("testNs", "testG", ""); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithConfigBatchListenRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); ConfigBatchListenRequest request = new ConfigBatchListenRequest(); request.addConfigListenContext("testG", "testD", "testNs", "111"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(StringUtils.EMPTY, actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); request.getConfigListenContexts().clear(); actual = resourceParser.parse(request, secured); assertEquals(StringUtils.EMPTY, actual.getNamespaceId()); assertEquals(StringUtils.EMPTY, actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithReflectionRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = ConfigChangeNotifyRequest.build("rTestD", "rTestG", "rTestNs"); Resource actual = resourceParser.parse(request, secured); assertEquals("rTestNs", actual.getNamespaceId()); assertEquals("rTestG", actual.getGroup()); assertEquals("rTestD", actual.getName()); } private Request mockConfigRequest(String tenant, String group, String dataId) { ConfigPublishRequest request = new ConfigPublishRequest(); request.setTenant(tenant); request.setGroup(group); request.setDataId(dataId); return request; } private Secured getMethodSecure() throws NoSuchMethodException { StackTraceElement[] traces = new Exception().getStackTrace(); StackTraceElement callerElement = traces[1]; String methodName = callerElement.getMethodName(); Method method = this.getClass().getDeclaredMethod(methodName); return method.getAnnotation(Secured.class); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/parser/grpc/NamingGrpcResourceParserTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.grpc; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.Resource; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class NamingGrpcResourceParserTest { private NamingGrpcResourceParser resourceParser; @BeforeEach void setUp() throws Exception { resourceParser = new NamingGrpcResourceParser(); } @Test @Secured() void testParseWithFullContextForNamingRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); AbstractNamingRequest request = mockNamingRequest("testNs", "testG", "testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(MockNamingRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } @Test @Secured() void testParseWithFullContextForOtherRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockOtherRequest("testNs", "testG", "testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(NotifySubscriberRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } @Test @Secured() void testParseWithoutNamespaceForNamingRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); AbstractNamingRequest request = mockNamingRequest(null, "testG", "testS"); Resource actual = resourceParser.parse(request, secured); assertNull(actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(MockNamingRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } @Test @Secured() void testParseWithoutNamespaceForOtherRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockOtherRequest(null, "testG", "testS"); Resource actual = resourceParser.parse(request, secured); assertNull(actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(NotifySubscriberRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } @Test @Secured() void testParseWithoutGroupForNamingRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); AbstractNamingRequest request = mockNamingRequest("testNs", null, "testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(StringUtils.EMPTY, actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(MockNamingRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } @Test @Secured() void testParseWithoutGroupForOtherRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockOtherRequest("testNs", null, "testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(StringUtils.EMPTY, actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(NotifySubscriberRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } @Test @Secured() void testParseWithoutDataIdForNamingRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); AbstractNamingRequest request = mockNamingRequest("testNs", "testG", null); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(MockNamingRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } @Test @Secured() void testParseWithoutDataIdForOtherRequest() throws NoSuchMethodException { Secured secured = getMethodSecure(); Request request = mockOtherRequest("testNs", "testG", null); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertEquals(NotifySubscriberRequest.class.getSimpleName(), actual.getProperties() .getProperty(com.alibaba.nacos.plugin.auth.constant.Constants.Resource.REQUEST_CLASS)); } private AbstractNamingRequest mockNamingRequest(String testNs, String testG, String testS) { MockNamingRequest result = new MockNamingRequest(); result.setNamespace(testNs); result.setGroupName(testG); result.setServiceName(testS); return result; } private Request mockOtherRequest(String testNs, String testG, String testS) { NotifySubscriberRequest result = new NotifySubscriberRequest(); result.setNamespace(testNs); result.setGroupName(testG); result.setServiceName(testS); return result; } private Secured getMethodSecure() throws NoSuchMethodException { StackTraceElement[] traces = new Exception().getStackTrace(); StackTraceElement callerElement = traces[1]; String methodName = callerElement.getMethodName(); Method method = this.getClass().getDeclaredMethod(methodName); return method.getAnnotation(Secured.class); } private class MockNamingRequest extends AbstractNamingRequest { } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/parser/http/AiHttpResourceParserTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.http; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.Resource; import jakarta.servlet.http.HttpServletRequest; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class AiHttpResourceParserTest { @Mock private HttpServletRequest request; private AiHttpResourceParser resourceParser; @BeforeEach void setUp() { resourceParser = new AiHttpResourceParser(); } @Test @Secured(signType = "ai") void testParseWithNamespaceId() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/mcp"); when(request.getParameter(eq("mcpName"))).thenReturn("testMcp"); when(request.getParameterMap()).thenReturn(new HashMap<>()); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, actual.getGroup()); assertEquals("testMcp", actual.getName()); } @Test @Secured(signType = "ai") void testParseWithDefaultNamespace() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn(null); when(request.getRequestURI()).thenReturn("/ai/mcp"); when(request.getParameter(eq("mcpName"))).thenReturn("testMcp"); when(request.getParameterMap()).thenReturn(new HashMap<>()); Resource actual = resourceParser.parse(request, secured); assertEquals(AiConstants.Mcp.MCP_DEFAULT_NAMESPACE, actual.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, actual.getGroup()); assertEquals("testMcp", actual.getName()); } @Test @Secured(signType = "ai") void testParseWithMcpPath() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/mcp"); when(request.getParameter(eq("mcpName"))).thenReturn("testMcp"); when(request.getParameterMap()).thenReturn(new HashMap<>()); Resource actual = resourceParser.parse(request, secured); assertEquals("testMcp", actual.getName()); } @Test @Secured(signType = "ai") void testParseWithMcpPathWithoutName() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/mcp"); when(request.getParameter(eq("mcpName"))).thenReturn(null); when(request.getParameterMap()).thenReturn(new HashMap<>()); Resource actual = resourceParser.parse(request, secured); assertEquals(StringUtils.EMPTY, actual.getName()); } @Test @Secured(signType = "ai") void testParseWithA2aPathWithAgentName() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/a2a"); when(request.getParameter(eq("agentName"))).thenReturn("testAgent"); when(request.getParameterMap()).thenReturn(new HashMap<>()); Resource actual = resourceParser.parse(request, secured); assertEquals("testAgent", actual.getName()); } @Test @Secured(signType = "ai") void testParseWithA2aPathWithAgentCard() throws NoSuchMethodException { Secured secured = getMethodSecure(); Map paramMap = new HashMap<>(); paramMap.put("agentCard", new String[] {"{\"name\":\"cardAgent\"}"}); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/a2a"); when(request.getParameter(eq("agentCard"))).thenReturn("{\"name\":\"cardAgent\"}"); when(request.getParameterMap()).thenReturn(paramMap); Resource actual = resourceParser.parse(request, secured); assertEquals("cardAgent", actual.getName()); } @Test @Secured(signType = "ai") void testParseWithA2aPathWithInvalidAgentCard() throws NoSuchMethodException { Secured secured = getMethodSecure(); Map paramMap = new HashMap<>(); paramMap.put("agentCard", new String[] {"invalid-json"}); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/a2a"); when(request.getParameter(eq("agentCard"))).thenReturn("invalid-json"); when(request.getParameterMap()).thenReturn(paramMap); Resource actual = resourceParser.parse(request, secured); assertEquals(StringUtils.EMPTY, actual.getName()); } @Test @Secured(signType = "ai") void testParseWithUnknownPath() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/unknown"); when(request.getParameterMap()).thenReturn(new HashMap<>()); Resource actual = resourceParser.parse(request, secured); assertEquals(StringUtils.EMPTY, actual.getName()); } @Test @Secured(signType = "ai") void testGetProperties() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(eq(Constants.NAMESPACE_ID))).thenReturn("testNs"); when(request.getRequestURI()).thenReturn("/ai/mcp"); when(request.getParameter(eq("mcpName"))).thenReturn("testMcp"); when(request.getParameterMap()).thenReturn(new HashMap<>()); Resource actual = resourceParser.parse(request, secured); assertEquals(1, actual.getProperties().size()); assertTrue(actual.getProperties().containsKey( com.alibaba.nacos.plugin.auth.constant.Constants.Resource.ACTION)); } private Secured getMethodSecure() throws NoSuchMethodException { StackTraceElement[] traces = new Exception().getStackTrace(); StackTraceElement callerElement = traces[1]; String methodName = callerElement.getMethodName(); Method method = this.getClass().getDeclaredMethod(methodName); return method.getAnnotation(Secured.class); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/parser/http/ConfigHttpResourceParserTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.http; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.Resource; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class ConfigHttpResourceParserTest { @Mock private HttpServletRequest request; private ConfigHttpResourceParser resourceParser; @BeforeEach void setUp() throws Exception { resourceParser = new ConfigHttpResourceParser(); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithFullContext() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq("tenant"))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(Constants.GROUP))).thenReturn("testG"); Mockito.when(request.getParameter(eq(Constants.DATA_ID))).thenReturn("testD"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithNamespaceId() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq("namespaceId"))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(Constants.GROUP))).thenReturn("testG"); Mockito.when(request.getParameter(eq(Constants.DATA_ID))).thenReturn("testD"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithNamespaceIdFirst() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq("tenant"))).thenReturn("testNs"); Mockito.when(request.getParameter(eq("namespaceId"))).thenReturn("testNsFirst"); Mockito.when(request.getParameter(eq(Constants.GROUP))).thenReturn("testG"); Mockito.when(request.getParameter(eq(Constants.DATA_ID))).thenReturn("testD"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNsFirst", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithoutNamespace() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(Constants.GROUP))).thenReturn("testG"); Mockito.when(request.getParameter(eq(Constants.DATA_ID))).thenReturn("testD"); Resource actual = resourceParser.parse(request, secured); assertEquals(Constants.DEFAULT_NAMESPACE_ID, actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithoutGroup() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq("tenant"))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(Constants.DATA_ID))).thenReturn("testD"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(StringUtils.EMPTY, actual.getGroup()); assertEquals("testD", actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } @Test @Secured(signType = Constants.Config.CONFIG_MODULE) void testParseWithoutDataId() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq("tenant"))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(Constants.GROUP))).thenReturn("testG"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Config.CONFIG_MODULE, actual.getType()); } private Secured getMethodSecure() throws NoSuchMethodException { StackTraceElement[] traces = new Exception().getStackTrace(); StackTraceElement callerElement = traces[1]; String methodName = callerElement.getMethodName(); Method method = this.getClass().getDeclaredMethod(methodName); return method.getAnnotation(Secured.class); } @Test @Secured(tags = {"namespaceId:customNsParam"}) void testParseWithSecuredTags() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter("customNsParam")).thenReturn("tagNs"); String actualNamespaceId = resourceParser.getNamespaceId(request, secured); assertEquals("tagNs", actualNamespaceId); } @Test @Secured(tags = {"namespaceId:emptyParam"}) void testParseWithSecuredTagsButEmptyParam() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter("emptyParam")).thenReturn(StringUtils.EMPTY); when(request.getParameter(Constants.NAMESPACE_ID)).thenReturn("defaultNs"); String actualNamespaceId = resourceParser.getNamespaceId(request, secured); assertEquals("defaultNs", actualNamespaceId); } @Test @Secured(tags = {"invalidTag:param", "namespaceId:multiTag"}) void testParseWithMultipleTags() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter("multiTag")).thenReturn("multiNs"); String actualNamespaceId = resourceParser.getNamespaceId(request, secured); assertEquals("multiNs", actualNamespaceId); } @Test @Secured(tags = {"namespaceId:splitTag:extra"}) void testParseWithInvalidSplitTag() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter("splitTag")).thenReturn("splitNs"); String actualNamespaceId = resourceParser.getNamespaceId(request, secured); assertEquals("splitNs", actualNamespaceId); } @Test @Secured(tags = {"normalTag"}) void testParseWithNoNamespaceTag() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(Constants.NAMESPACE_ID)).thenReturn("defaultNs"); String actualNamespaceId = resourceParser.getNamespaceId(request, secured); assertEquals("defaultNs", actualNamespaceId); } @Test @Secured(tags = {"namespaceId"}) void testParseWithInvalidSplitTag1() throws NoSuchMethodException { Secured secured = getMethodSecure(); when(request.getParameter(Constants.NAMESPACE_ID)).thenReturn("defaultNs"); String actualNamespaceId = resourceParser.getNamespaceId(request, secured); assertEquals("defaultNs", actualNamespaceId); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/parser/http/NamingHttpResourceParserTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.parser.http; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.Resource; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class NamingHttpResourceParserTest { @Mock private HttpServletRequest request; private NamingHttpResourceParser resourceParser; @BeforeEach void setUp() throws Exception { resourceParser = new NamingHttpResourceParser(); } @Test @Secured() void testParseWithFullContext() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(CommonParams.GROUP_NAME))).thenReturn("testG"); Mockito.when(request.getParameter(eq(CommonParams.SERVICE_NAME))).thenReturn("testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); } @Test @Secured() void testParseWithoutNamespace() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(CommonParams.GROUP_NAME))).thenReturn("testG"); Mockito.when(request.getParameter(eq(CommonParams.SERVICE_NAME))).thenReturn("testS"); Resource actual = resourceParser.parse(request, secured); assertEquals(Constants.DEFAULT_NAMESPACE_ID, actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); } @Test @Secured() void testParseWithoutGroup() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(CommonParams.SERVICE_NAME))).thenReturn("testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(Constants.DEFAULT_GROUP, actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); } @Test @Secured() void testParseWithGroupInService() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(CommonParams.SERVICE_NAME))).thenReturn("testG@@testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); } @Test @Secured() void testParseWithoutService() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(CommonParams.GROUP_NAME))).thenReturn("testG"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); } @Test @Secured() void testParseWithoutGroupAndService() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNs"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals(StringUtils.EMPTY, actual.getGroup()); assertEquals(StringUtils.EMPTY, actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); } @Test @Secured(tags = {"testTag"}) void testParseWithTags() throws NoSuchMethodException { Secured secured = getMethodSecure(); Mockito.when(request.getParameter(eq(CommonParams.NAMESPACE_ID))).thenReturn("testNs"); Mockito.when(request.getParameter(eq(CommonParams.GROUP_NAME))).thenReturn("testG"); Mockito.when(request.getParameter(eq(CommonParams.SERVICE_NAME))).thenReturn("testS"); Resource actual = resourceParser.parse(request, secured); assertEquals("testNs", actual.getNamespaceId()); assertEquals("testG", actual.getGroup()); assertEquals("testS", actual.getName()); assertEquals(Constants.Naming.NAMING_MODULE, actual.getType()); assertTrue(actual.getProperties().containsKey("testTag")); } private Secured getMethodSecure() throws NoSuchMethodException { StackTraceElement[] traces = new Exception().getStackTrace(); StackTraceElement callerElement = traces[1]; String methodName = callerElement.getMethodName(); Method method = this.getClass().getDeclaredMethod(methodName); return method.getAnnotation(Secured.class); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/serveridentity/ServerIdentityCheckerHolderTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.serveridentity; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.common.spi.NacosServiceLoader; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertInstanceOf; class ServerIdentityCheckerHolderTest { Map, Collection>> servicesMap; private Class cachedCheckerClass; @BeforeEach void setUp() { servicesMap = (Map, Collection>>) ReflectionTestUtils.getField(NacosServiceLoader.class, "SERVICES"); cachedCheckerClass = (Class) ReflectionTestUtils.getField( ServerIdentityCheckerHolder.getInstance(), "checkerClass"); } @AfterEach void tearDown() { servicesMap.remove(ServerIdentityChecker.class); ReflectionTestUtils.setField(ServerIdentityCheckerHolder.getInstance(), "checkerClass", cachedCheckerClass); } @Test void testConstructorWithSingleImplementation() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { ServerIdentityCheckerHolder holder = getNewHolder(1); assertInstanceOf(MockChecker.class, holder.newChecker()); } @Test void testConstructorWithMultipleImplementation() throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException { ServerIdentityCheckerHolder holder = getNewHolder(2); assertInstanceOf(MockChecker.class, holder.newChecker()); } ServerIdentityCheckerHolder getNewHolder(int size) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { List> classes = new LinkedList<>(); for (int i = 0; i < size; i++) { classes.add(MockChecker.class); } servicesMap.put(ServerIdentityChecker.class, classes); Constructor constructor = ServerIdentityCheckerHolder.class.getDeclaredConstructor(); constructor.setAccessible(true); return constructor.newInstance(); } public static class MockChecker implements ServerIdentityChecker { @Override public void init(NacosAuthConfig authConfig) { } @Override public ServerIdentityResult check(ServerIdentity serverIdentity, Secured secured) { return ServerIdentityResult.success(); } } @Test void testNewCheckerWithInvalidClass() { ReflectionTestUtils.setField(ServerIdentityCheckerHolder.getInstance(), "checkerClass", ServerIdentityChecker.class); assertInstanceOf(DefaultChecker.class, ServerIdentityCheckerHolder.getInstance().newChecker()); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/serveridentity/ServerIdentityTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.serveridentity; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ServerIdentityTest { @Test void testGetIdentityKey() { ServerIdentity identity = new ServerIdentity("testKey", "testValue"); assertEquals("testKey", identity.getIdentityKey()); } @Test void testGetIdentityValue() { ServerIdentity identity = new ServerIdentity("testKey", "testValue"); assertEquals("testValue", identity.getIdentityValue()); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/util/AuthHeaderUtilTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.util; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.common.http.param.Header; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AuthHeaderUtilTest { private static final String TEST_KEY = "testKey"; private static final String TEST_VALUE = "testValue"; @Mock private NacosAuthConfig authConfig; @Mock private Request request; @Test void testAddIdentityToHttpHeaderWhenNotSupport() { when(authConfig.isSupportServerIdentity()).thenReturn(false); Header header = Header.newInstance(); AuthHeaderUtil.addIdentityToHeader(header, authConfig); assertNull(header.getValue(TEST_KEY)); } @Test void testAddIdentityToHttpHeaderWithBlankKey() { when(authConfig.isSupportServerIdentity()).thenReturn(true); when(authConfig.getServerIdentityKey()).thenReturn(""); Header header = Header.newInstance(); AuthHeaderUtil.addIdentityToHeader(header, authConfig); assertNull(header.getValue(TEST_KEY)); } @Test void testAddIdentityToHttpHeaderSuccess() { when(authConfig.isSupportServerIdentity()).thenReturn(true); when(authConfig.getServerIdentityKey()).thenReturn(TEST_KEY); when(authConfig.getServerIdentityValue()).thenReturn(TEST_VALUE); Header header = Header.newInstance(); AuthHeaderUtil.addIdentityToHeader(header, authConfig); assertEquals(TEST_VALUE, header.getValue(TEST_KEY)); } @Test void testAddIdentityToGrpcRequestWhenNotSupport() { when(authConfig.isSupportServerIdentity()).thenReturn(false); AuthHeaderUtil.addIdentityToHeader(request, authConfig); verify(request, never()).putHeader(anyString(), anyString()); } @Test void testAddIdentityToGrpcRequestWithBlankKey() { when(authConfig.isSupportServerIdentity()).thenReturn(true); when(authConfig.getServerIdentityKey()).thenReturn(" "); AuthHeaderUtil.addIdentityToHeader(request, authConfig); verify(request, never()).putHeader(anyString(), anyString()); } @Test void testAddIdentityToGrpcRequestSuccess() { when(authConfig.isSupportServerIdentity()).thenReturn(true); when(authConfig.getServerIdentityKey()).thenReturn(TEST_KEY); when(authConfig.getServerIdentityValue()).thenReturn(TEST_VALUE); AuthHeaderUtil.addIdentityToHeader(request, authConfig); verify(request).putHeader(TEST_KEY, TEST_VALUE); } @Test void testConstructor() { new AuthHeaderUtil(); } } ================================================ FILE: auth/src/test/java/com/alibaba/nacos/auth/util/LoggersTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.auth.util; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; 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.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @ExtendWith(MockitoExtension.class) class LoggersTest { private Level originalLevel; @BeforeEach void setUp() { originalLevel = ((Logger) Loggers.AUTH).getLevel(); } @AfterEach void tearDown() { if (originalLevel != null) { ((Logger) Loggers.AUTH).setLevel(originalLevel); } } @Test void testSetLogLevelForAuth() { Loggers.setLogLevel("auth", "DEBUG"); assertEquals(Level.DEBUG, ((Logger) Loggers.AUTH).getLevel()); } @Test void testSetLogLevelForNonAuth() { Level levelBefore = ((Logger) Loggers.AUTH).getLevel(); Loggers.setLogLevel("other", "DEBUG"); assertEquals(levelBefore, ((Logger) Loggers.AUTH).getLevel()); } @Test void testAuthLoggerNotNull() { assertNotNull(Loggers.AUTH); } @Test void testConstructor() { new Loggers(); } } ================================================ FILE: auth/src/test/resources/META-INF/services/com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService ================================================ # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.auth.mock.MockAuthPluginService ================================================ FILE: bootstrap/pom.xml ================================================ 4.0.0 com.alibaba.nacos nacos-all ${revision} ../pom.xml nacos-bootstrap nacos-bootstrap ${project.version} com.alibaba.nacos nacos-console com.alibaba.nacos nacos-core com.alibaba.nacos nacos-server com.alibaba.nacos nacos-mcp-registry-adaptor com.alibaba.nacos nacos-datasource-plugin-base ${revision} dev spring.profiles.active dev com.alibaba.nacos nacos-default-plugin-all ${project.version} release-nacos nacos-server maven-jar-plugin true true org.springframework.boot spring-boot-maven-plugin com.alibaba.nacos.bootstrap.NacosBootstrap ZIP repackage ================================================ FILE: bootstrap/src/main/java/com/alibaba/nacos/bootstrap/NacosBootstrap.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.bootstrap; import com.alibaba.nacos.mcpregistry.NacosMcpRegistry; import com.alibaba.nacos.NacosServerBasicApplication; import com.alibaba.nacos.NacosServerWebApplication; import com.alibaba.nacos.console.NacosConsole; import com.alibaba.nacos.core.listener.startup.NacosStartUp; import com.alibaba.nacos.core.listener.startup.NacosStartUpManager; import com.alibaba.nacos.sys.env.Constants; import com.alibaba.nacos.sys.env.DeploymentType; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.boot.Banner; import org.springframework.boot.ResourceBanner; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.io.ClassPathResource; import org.springframework.jmx.export.MBeanExporter; import org.springframework.jmx.support.RegistrationPolicy; /** * Nacos bootstrap class. * * @author xiweng.yy */ @SpringBootApplication public class NacosBootstrap { private static final String SPRING_JMX_ENABLED = "spring.jmx.enabled"; public static void main(String[] args) { String type = System.getProperty(Constants.NACOS_DEPLOYMENT_TYPE, Constants.NACOS_DEPLOYMENT_TYPE_MERGED); DeploymentType deploymentType = DeploymentType.getType(type); EnvUtil.setDeploymentType(deploymentType); switch (deploymentType) { case MERGED: startWithConsole(args); break; case SERVER: startWithoutConsole(args); break; case CONSOLE: startOnlyConsole(args); break; default: throw new IllegalArgumentException("Unsupported nacos deployment type " + type); } } private static void prepareCoreContext(ConfigurableApplicationContext coreContext) { if (coreContext.getEnvironment().getProperty(SPRING_JMX_ENABLED, Boolean.class, false)) { // Avoid duplicate registration MBean to exporter. coreContext.getBean(MBeanExporter.class).setRegistrationPolicy(RegistrationPolicy.IGNORE_EXISTING); } } private static void startWithoutConsole(String[] args) { ConfigurableApplicationContext coreContext = startCoreContext(args); prepareCoreContext(coreContext); ConfigurableApplicationContext webContext = startServerWebContext(args, coreContext); if (isEnabledMcpRegistryApi(coreContext)) { ConfigurableApplicationContext mcpRegistryContext = startMcpRegistryContext(args, coreContext); } } private static void startWithConsole(String[] args) { ConfigurableApplicationContext coreContext = startCoreContext(args); prepareCoreContext(coreContext); ConfigurableApplicationContext serverWebContext = startServerWebContext(args, coreContext); ConfigurableApplicationContext consoleContext = startConsoleContext(args, coreContext); if (isEnabledMcpRegistryApi(coreContext)) { ConfigurableApplicationContext mcpRegistryContext = startMcpRegistryContext(args, coreContext); } } private static ConfigurableApplicationContext startCoreContext(String[] args) { NacosStartUpManager.start(NacosStartUp.CORE_START_UP_PHASE); return new SpringApplicationBuilder(NacosServerBasicApplication.class).web(WebApplicationType.NONE) .banner(getBanner("core-banner.txt")).run(args); } private static ConfigurableApplicationContext startServerWebContext(String[] args, ConfigurableApplicationContext coreContext) { NacosStartUpManager.start(NacosStartUp.WEB_START_UP_PHASE); return new SpringApplicationBuilder(NacosServerWebApplication.class).parent(coreContext) .banner(getBanner("nacos-server-web-banner.txt")).run(args); } private static ConfigurableApplicationContext startConsoleContext(String[] args, ConfigurableApplicationContext coreContext) { NacosStartUpManager.start(NacosStartUp.CONSOLE_START_UP_PHASE); return new SpringApplicationBuilder(NacosConsole.class).parent(coreContext) .banner(getBanner("nacos-console-banner.txt")).run(args); } private static ConfigurableApplicationContext startMcpRegistryContext(String[] args, ConfigurableApplicationContext coreContext) { NacosStartUpManager.start(NacosStartUp.MCP_REGISTRY_START_UP_PHASE); return new SpringApplicationBuilder(NacosMcpRegistry.class).parent(coreContext) .banner(getBanner("nacos-mcp-registry-banner.txt")).run(args); } private static void startOnlyConsole(String[] args) { NacosStartUpManager.start(NacosStartUp.CONSOLE_START_UP_PHASE); new SpringApplicationBuilder(NacosConsole.class).banner( getBanner("nacos-console-banner.txt")).run(args); } private static Banner getBanner(String bannerFileName) { return new ResourceBanner(new ClassPathResource(bannerFileName)); } private static boolean isEnabledMcpRegistryApi(ConfigurableApplicationContext coreContext) { return coreContext.getEnvironment().getProperty("nacos.ai.mcp.registry.enabled", Boolean.class, false); } } ================================================ FILE: bootstrap/src/main/resources/application.properties ================================================ # # Copyright 1999-2025 Alibaba Group Holding Ltd. # # 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. # #--------------- Nacos Common Configurations ---------------# #*************** Nacos port Related Configurations ***************# ### Nacos Server Main port nacos.server.main.port=8848 #*************** Network Related Configurations ***************# ### If prefer hostname over ip for Nacos server addresses in cluster.conf: # nacos.inetutils.prefer-hostname-over-ip=false ### Specify local server's IP: # nacos.inetutils.ip-address= #*************** Datasource Related Configurations ***************# ### nacos.plugin.datasource.log.enabled=true ### Property settings for mysql (default) #spring.sql.init.platform=mysql ### Count of DB: # db.num=1 ### Connect URL of DB: # db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC # db.user=nacos # db.password=nacos ### Property settings for postgresql #spring.sql.init.platform=postgresql #db.num=1 #db.url.0=jdbc:postgresql://127.0.0.1:5432/nacos #db.user=nacos #db.password=nacos #db.pool.config.driverClassName=org.postgresql.Driver #*************** Metrics Related Configurations ***************# ### Metrics for prometheus #management.endpoints.web.exposure.include=prometheus ### Metrics for elastic search management.elastic.metrics.export.enabled=false #management.metrics.export.elastic.host=http://localhost:9200 ### Metrics for influx management.influx.metrics.export.enabled=false #management.metrics.export.influx.db=springboot #management.metrics.export.influx.uri=http://localhost:8086 #management.metrics.export.influx.auto-create-db=true #management.metrics.export.influx.consistency=one #management.metrics.export.influx.compressed=true #*************** Core Related Configurations ***************# ### set the WorkerID manually # nacos.core.snowflake.worker-id= ### Member-MetaData # nacos.core.member.meta.site= # nacos.core.member.meta.adweight= # nacos.core.member.meta.weight= ### MemberLookup ### Addressing pattern category, If set, the priority is highest # nacos.core.member.lookup.type=[file,address-server] ## Set the cluster list with a configuration file or command-line argument # nacos.member.list=192.168.16.101:8847?raft_port=8807,192.168.16.101?raft_port=8808,192.168.16.101:8849?raft_port=8809 ## for AddressServerMemberLookup # Maximum number of retries to query the address server upon initialization # nacos.core.address-server.retry=5 ## Server domain name address of [address-server] mode # address.server.domain=jmenv.tbsite.net ## Server port of [address-server] mode # address.server.port=8080 ## Request address of [address-server] mode # address.server.url=/nacos/serverlist #*************** JRaft Related Configurations ***************# ### Sets the Raft cluster election timeout, default value is 5 second # nacos.core.protocol.raft.data.election_timeout_ms=5000 ### Sets the amount of time the Raft snapshot will execute periodically, default is 30 minute # nacos.core.protocol.raft.data.snapshot_interval_secs=30 ### raft internal worker threads # nacos.core.protocol.raft.data.core_thread_num=8 ### Number of threads required for raft business request processing # nacos.core.protocol.raft.data.cli_service_thread_num=4 ### raft linear read strategy. Safe linear reads are used by default, that is, the Leader tenure is confirmed by heartbeat # nacos.core.protocol.raft.data.read_index_type=ReadOnlySafe ### rpc request timeout, default 5 seconds # nacos.core.protocol.raft.data.rpc_request_timeout_ms=5000 ### enable to support prometheus service discovery #nacos.prometheus.metrics.enabled=true #*************** Distro Related Configurations ***************# ### Distro data sync delay time, when sync task delayed, task will be merged for same data key. Default 1 second. # nacos.core.protocol.distro.data.sync.delayMs=1000 ### Distro data sync timeout for one sync data, default 3 seconds. # nacos.core.protocol.distro.data.sync.timeoutMs=3000 ### Distro data sync retry delay time when sync data failed or timeout, same behavior with delayMs, default 3 seconds. # nacos.core.protocol.distro.data.sync.retryDelayMs=3000 ### Distro data verify interval time, verify synced data whether expired for a interval. Default 5 seconds. # nacos.core.protocol.distro.data.verify.intervalMs=5000 ### Distro data verify timeout for one verify, default 3 seconds. # nacos.core.protocol.distro.data.verify.timeoutMs=3000 ### Distro data load retry delay when load snapshot data failed, default 30 seconds. # nacos.core.protocol.distro.data.load.retryDelayMs=30000 ### enable to support prometheus service discovery #nacos.prometheus.metrics.enabled=true #*************** Grpc Configurations ***************# ### Sets the maximum message size allowed to be received on the server. #nacos.remote.server.grpc.sdk.max-inbound-message-size=10485760 ### Sets the time(milliseconds) without read activity before sending a keepalive ping. The typical default is two hours. #nacos.remote.server.grpc.sdk.keep-alive-time=7200000 ### Sets a time(milliseconds) waiting for read activity after sending a keepalive ping. Defaults to 20 seconds. #nacos.remote.server.grpc.sdk.keep-alive-timeout=20000 ### Sets a time(milliseconds) that specify the most aggressive keep-alive time clients are permitted to configure. The typical default is 5 minutes #nacos.remote.server.grpc.sdk.permit-keep-alive-time=300000 ### cluster grpc(inside the nacos server) configuration #nacos.remote.server.grpc.cluster.max-inbound-message-size=10485760 ### Sets the time(milliseconds) without read activity before sending a keepalive ping. The typical default is two hours. #nacos.remote.server.grpc.cluster.keep-alive-time=7200000 ### Sets a time(milliseconds) waiting for read activity after sending a keepalive ping. Defaults to 20 seconds. #nacos.remote.server.grpc.cluster.keep-alive-timeout=20000 ### Sets a time(milliseconds) that specify the most aggressive keep-alive time clients are permitted to configure. The typical default is 5 minutes #nacos.remote.server.grpc.cluster.permit-keep-alive-time=300000 #*************** TLS Configuration ***************# #nacos.remote.server.rpc.tls.enableTls=true #nacos.remote.server.rpc.tls.certChainFile=file:{path of cert} #nacos.remote.server.rpc.tls.certPrivateKey=file:{path of private key} #*************** Config Module Related Configurations ***************# ### the maximum retry times for push nacos.config.push.maxRetryTime=50 #*************** Naming Module Related Configurations ***************# ### Data dispatch task execution period in milliseconds: ### If enable data warmup. If set to false, the server would accept request without local data preparation: # nacos.naming.data.warmup=true ### If enable the instance auto expiration, kind like of health check of instance: # nacos.naming.expireInstance=true nacos.naming.empty-service.auto-clean=true nacos.naming.empty-service.clean.initial-delay-ms=50000 nacos.naming.empty-service.clean.period-time-ms=30000 #*************** AI Module Related Configurations ***************# ### Whether nacos ai module is enabled, default true. the ai module need both config module and naming module enabled. # nacos.extension.ai.enabled=true ### Whether nacos mcp registry is enabled, default is false. ### When enabled=true, Nacos will start a `mcp registry` server with new port with `nacos.ai.mcp.registry.port` #nacos.ai.mcp.registry.enabled=false ### Nacos mcp registry port, default 9080: nacos.ai.mcp.registry.port=9080 #--------------- Nacos Web Server Configurations ---------------# #*************** Nacos Web Server Related Configurations ***************# ### Nacos Server Web context path: nacos.server.contextPath=/nacos #*************** Access Log Related Configurations ***************# ### If turn on the access log: server.tomcat.accesslog.enabled=true ### accesslog automatic cleaning time server.tomcat.accesslog.max-days=30 ### The access log pattern: server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D %{User-Agent}i %{Request-Source}i ### The directory of access log: server.tomcat.basedir=file:. #*************** API Related Configurations ***************# ### Include message field server.error.include-message=ALWAYS ### Enabled for open API compatibility # nacos.core.api.compatibility.client.enabled=true ### Enabled for admin API compatibility # nacos.core.api.compatibility.admin.enabled=false ### Enabled for console API compatibility # nacos.core.api.compatibility.console.enabled=false #--------------- Nacos Console Configurations ---------------# #*************** Nacos Console Related Configurations ***************# ### Nacos Console Main port nacos.console.port=8080 ### Maximum upload file size for console (e.g. skill zip). Default 10MB. Exceeding returns a clear error. spring.servlet.multipart.max-file-size=10MB spring.servlet.multipart.max-request-size=10MB ### Nacos Server Web context path: nacos.console.contextPath= ### Nacos Server context path, which link to nacos server `nacos.server.contextPath`, works when deployment type is `console` nacos.console.remote.server.context-path=/nacos #************** Console UI Configuration ***************# ### Turn on/off the nacos console ui. #nacos.console.ui.enabled=true #--------------- Nacos Plugin Configurations ---------------# #*************** CMDB Plugin Related Configurations ***************# ### The interval to dump external CMDB in seconds: # nacos.cmdb.dumpTaskInterval=3600 ### The interval of polling data change event in seconds: # nacos.cmdb.eventTaskInterval=10 ### The interval of loading labels in seconds: # nacos.cmdb.labelTaskInterval=300 ### If turn on data loading task: # nacos.cmdb.loadDataAtStart=false #*************** Auth Plugin Related Configurations ***************# ### The ignore urls of auth, will be deprecated in the future: nacos.security.ignore.urls=/,/error,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/**,/v1/console/health/**,/actuator/**,/v1/console/server/** ### The auth system to use, default 'nacos' and 'ldap' is supported, other type should be implemented by yourself: nacos.core.auth.system.type=nacos ### If turn on auth system: # Whether open nacos server API auth system nacos.core.auth.enabled=true # Whether open nacos admin API auth system nacos.core.auth.admin.enabled=true # Whether open nacos console API auth system nacos.core.auth.console.enabled=true ### Turn on/off caching of auth information. By turning on this switch, the update of auth information would have a 15 seconds delay. nacos.core.auth.caching.enabled=true ### worked when nacos.core.auth.enabled=true ### The two properties is the white list for auth and used by identity the request from other server. nacos.core.auth.server.identity.key= nacos.core.auth.server.identity.value= ### worked when nacos.core.auth.system.type=nacos or nacos.core.auth.console.enabled=true ### The token expiration in seconds: nacos.core.auth.plugin.nacos.token.cache.enable=false nacos.core.auth.plugin.nacos.token.expire.seconds=18000 ### The default token (Base64 string): #nacos.core.auth.plugin.nacos.token.secret.key=VGhpc0lzTXlDdXN0b21TZWNyZXRLZXkwMTIzNDU2Nzg= nacos.core.auth.plugin.nacos.token.secret.key= ### worked when nacos.core.auth.system.type=ldap\uFF0C{0} is Placeholder,replace login username #nacos.core.auth.ldap.url=ldap://localhost:389 #nacos.core.auth.ldap.basedc=dc=example,dc=org #nacos.core.auth.ldap.userDn=cn=admin,${nacos.core.auth.ldap.basedc} #nacos.core.auth.ldap.password=admin #nacos.core.auth.ldap.userdn=cn={0},dc=example,dc=org #nacos.core.auth.ldap.filter.prefix=uid #nacos.core.auth.ldap.case.sensitive=true #nacos.core.auth.ldap.ignore.partial.result.exception=false #*************** Control Plugin Related Configurations ***************# # plugin type #nacos.plugin.control.manager.type=nacos # local control rule storage dir, default ${nacos.home}/data/connection and ${nacos.home}/data/tps #nacos.plugin.control.rule.local.basedir=${nacos.home} # external control rule storage type, if exist #nacos.plugin.control.rule.external.storage= #*************** Config Change Plugin Related Configurations ***************# # webhook #nacos.core.config.plugin.webhook.enabled=false # It is recommended to use EB https://help.aliyun.com/document_detail/413974.html #nacos.core.config.plugin.webhook.url=http://localhost:8080/webhook/send?token=*** # The content push max capacity ,byte #nacos.core.config.plugin.webhook.contentMaxCapacity=102400 # whitelist #nacos.core.config.plugin.whitelist.enabled=false # The import file suffixs #nacos.core.config.plugin.whitelist.suffixs=xml,text,properties,yaml,html # fileformatcheck,which validate the import file of type and content #nacos.core.config.plugin.fileformatcheck.enabled=false #*************** Istio Plugin Related Configurations ***************# ### If turn on the MCP server: nacos.istio.mcp.server.enabled=false #--------------- Nacos Experimental Features Configurations ---------------# #*************** K8s Related Configurations ***************# ### If turn on the K8s sync: nacos.k8s.sync.enabled=false ### If use the Java API from an application outside a kubernetes cluster #nacos.k8s.sync.outsideCluster=false #nacos.k8s.sync.kubeConfig=/.kube/config #*************** Fuzzy Watch Configuration ***************# nacos.config.fuzzy.watch.max.pattern.count=20 nacos.config.fuzzy.watch.max.pattern.match.config.count=500 nacos.naming.fuzzy.watch.max.pattern.count=20 nacos.naming.fuzzy.watch.max.pattern.match.service.count=500 ================================================ FILE: client/filter-config.json ================================================ { "rules": [ { "includeClasses": "com.alibaba.nacos.**" }, { "excludeClasses": "org.mockito.**" }, { "excludeClasses": "java.lang.management.**" }, { "excludeClasses": "net.bytebuddy.**" }, { "excludeClasses": "junit.**" }, { "excludeClasses": "org.junit.**" }, { "excludeClasses": "org.graalvm.**" }, { "excludeClasses": "jdk.**" }, { "excludeClasses": "org.hamcrest.**" } ], "regexRules": [ {"excludeClasses": ".*Test$"} ] } ================================================ FILE: client/pom.xml ================================================ com.alibaba.nacos nacos-all ${revision} ../pom.xml 4.0.0 nacos-client jar nacos-client ${project.version} https://nacos.io Nacos client pom.xml file ${client.java.version} ${client.java.version} org.slf4j slf4j-api true ${project.groupId} nacos-api true ${project.groupId} nacos-common true com.alibaba.nacos nacos-client-basic com.alibaba.nacos nacos-encryption-plugin com.alibaba.nacos nacos-logback-adapter-12 com.alibaba.nacos logback-adapter com.alibaba.nacos nacos-log4j2-adapter com.fasterxml.jackson.core jackson-core com.fasterxml.jackson.core jackson-databind org.apache.httpcomponents.client5 httpclient5 org.apache.httpcomponents.core5 httpcore5 io.prometheus simpleclient org.yaml snakeyaml io.micrometer micrometer-core maven-shade-plugin 3.6.0 false package shade false true false true true true *:* META-INF/*.SF META-INF/*.DSA META-INF/*.RSA **/module-info.class com.google.*:* google/**/*.proto com.alibaba.nacos:nacos-api com/alibaba/nacos/api/**/** META-INF/**/*.* com.alibaba.nacos:nacos-api com.alibaba.nacos:nacos-common org.conscrypt:conscrypt-openjdk org.mortbay.jetty.alpn:alpn-boot org.eclipse.jetty.npn:npn-api com.google.guava:guava io.grpc:* io.opencensus:* org.javassist:* io.perfmark:perfmark-api com.google.*:* javax.annotation:javax.annotation-api org.checkerframework:* org.codehaus.mojo:* io.grpc com.alibaba.nacos.shaded.io.grpc io.grpc.netty.shaded.io.grpc.netty.* io.grpc.netty.shaded.io.grpc.netty com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty io.grpc.netty.shaded.io.grpc.netty.* com.google com.alibaba.nacos.shaded.com.google javax.annotation com.alibaba.nacos.shaded.javax.annotation io.perfmark com.alibaba.nacos.shaded.io.perfmark io.opencensus com.alibaba.nacos.shaded.io.opencensus org.codehaus com.alibaba.nacos.shaded.org.codehaus org.checkerframework com.alibaba.nacos.shaded.org.checkerframework android.annotation com.alibaba.nacos.shaded.android.annotation org.example com.alibaba.nacos.shaded.org.example maven-compiler-plugin ${maven-compiler-plugin.version} ${client.java.version} ${client.java.version} ${client.java.version} true true -parameters maven-compiler-plugin release-sign-artifacts maven-shade-plugin maven-jar-plugin pure-jar package jar pure release-nacos maven-shade-plugin maven-jar-plugin pure-jar package jar pure ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ability/ClientAbilityControlManager.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ability; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.ability.register.impl.SdkClientAbilities; import com.alibaba.nacos.common.ability.AbstractAbilityControlManager; import java.util.HashMap; import java.util.Map; /**. * @author Daydreamer * @description {@link AbstractAbilityControlManager} for nacos-client. * @date 2022/7/13 13:38 **/ public class ClientAbilityControlManager extends AbstractAbilityControlManager { public ClientAbilityControlManager() { } @Override protected Map> initCurrentNodeAbilities() { Map> abilities = new HashMap<>(1); abilities.put(AbilityMode.SDK_CLIENT, SdkClientAbilities.getStaticAbilities()); return abilities; } @Override public int getPriority() { // if server ability manager exist, you should choose the server one return 0; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/NacosAiService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.ai.AiService; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.listener.AbstractNacosAgentCardListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosMcpServerListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosPromptListener; import com.alibaba.nacos.api.ai.listener.AbstractNacosSkillListener; import com.alibaba.nacos.api.ai.listener.NacosAgentCardEvent; import com.alibaba.nacos.api.ai.listener.NacosMcpServerEvent; import com.alibaba.nacos.api.ai.listener.NacosPromptEvent; import com.alibaba.nacos.api.ai.listener.NacosSkillEvent; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillResource; import com.alibaba.nacos.api.ai.model.skills.SkillUtils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.NacosFactory; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.ai.cache.NacosAgentCardCacheHolder; import com.alibaba.nacos.client.ai.cache.NacosMcpServerCacheHolder; import com.alibaba.nacos.client.ai.cache.NacosPromptCacheHolder; import com.alibaba.nacos.client.ai.cache.NacosSkillCacheHolder; import com.alibaba.nacos.client.ai.event.AgentCardListenerInvoker; import com.alibaba.nacos.client.ai.event.AiChangeNotifier; import com.alibaba.nacos.client.ai.event.McpServerChangedEvent; import com.alibaba.nacos.client.ai.event.McpServerListenerInvoker; import com.alibaba.nacos.client.ai.event.PromptChangedEvent; import com.alibaba.nacos.client.ai.event.PromptListenerInvoker; import com.alibaba.nacos.client.ai.event.SkillListenerInvoker; import com.alibaba.nacos.client.ai.remote.AiClientProxy; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.ai.remote.AiHttpClientProxy; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; /** * Nacos AI client service implementation. * * @author xiweng.yy */ public class NacosAiService implements AiService { private static final Logger LOGGER = LogUtils.logger(NacosAiService.class); private final String namespaceId; private final AiGrpcClient grpcClient; private final AiClientProxy aiClientProxy; private final NacosMcpServerCacheHolder mcpServerCacheHolder; private final NacosAgentCardCacheHolder agentCardCacheHolder; private final NacosSkillCacheHolder skillCacheHolder; private final NacosPromptCacheHolder promptCacheHolder; private final AiChangeNotifier aiChangeNotifier; private final ConfigService configService; public NacosAiService(Properties properties) throws NacosException { NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); LOGGER.info(ClientBasicParamUtil.getInputParameters(clientProperties.asProperties())); this.namespaceId = initNamespace(clientProperties); this.configService = NacosFactory.createConfigService(clientProperties.asProperties()); this.grpcClient = new AiGrpcClient(namespaceId, clientProperties); String transportMode = clientProperties.getProperty(AiConstants.AI_TRANSPORT_MODE, AiConstants.AI_TRANSPORT_MODE_GRPC); if (AiConstants.AI_TRANSPORT_MODE_HTTP.equalsIgnoreCase(transportMode)) { LOGGER.info("AI transport mode is HTTP, using AiHttpClientProxy for prompt operations."); this.aiClientProxy = new AiHttpClientProxy(namespaceId, clientProperties); } else { this.aiClientProxy = this.grpcClient; } this.mcpServerCacheHolder = new NacosMcpServerCacheHolder(grpcClient, clientProperties); this.agentCardCacheHolder = new NacosAgentCardCacheHolder(grpcClient, clientProperties); this.skillCacheHolder = new NacosSkillCacheHolder(configService, this.namespaceId); this.promptCacheHolder = new NacosPromptCacheHolder(this.aiClientProxy, clientProperties); this.aiChangeNotifier = new AiChangeNotifier(); start(); } private String initNamespace(NacosClientProperties properties) { String tempNamespace = properties.getProperty(PropertyKeyConst.NAMESPACE); if (StringUtils.isBlank(tempNamespace)) { return Constants.DEFAULT_NAMESPACE_ID; } return tempNamespace; } private void start() throws NacosException { this.grpcClient.start(this.mcpServerCacheHolder, this.agentCardCacheHolder); NotifyCenter.registerToPublisher(McpServerChangedEvent.class, 16384); NotifyCenter.registerToPublisher(PromptChangedEvent.class, 16384); NotifyCenter.registerSubscriber(this.aiChangeNotifier); } @Override public McpServerDetailInfo getMcpServer(String mcpName, String version) throws NacosException { if (StringUtils.isBlank(mcpName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `mcpName` not present"); } return grpcClient.queryMcpServer(mcpName, version); } @Override public String releaseMcpServer(McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { if (null == serverSpecification) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `serverSpecification` not present"); } if (StringUtils.isBlank(serverSpecification.getName())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `serverSpecification.name` not present"); } if (null == serverSpecification.getVersionDetail() || StringUtils.isBlank( serverSpecification.getVersionDetail().getVersion())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `serverSpecification.versionDetail.version` not present"); } return grpcClient.releaseMcpServer(serverSpecification, toolSpecification, endpointSpecification); } @Override public void registerMcpServerEndpoint(String mcpName, String address, int port, String version) throws NacosException { if (StringUtils.isBlank(mcpName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `mcpName` can't be empty or null"); } Instance instance = new Instance(); instance.setIp(address); instance.setPort(port); instance.validate(); grpcClient.registerMcpServerEndpoint(mcpName, address, port, version); } @Override public void deregisterMcpServerEndpoint(String mcpName, String address, int port) throws NacosException { if (StringUtils.isBlank(mcpName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `mcpName` can't be empty or null"); } Instance instance = new Instance(); instance.setIp(address); instance.setPort(port); instance.validate(); grpcClient.deregisterMcpServerEndpoint(mcpName, address, port); } @Override public McpServerDetailInfo subscribeMcpServer(String mcpName, String version, AbstractNacosMcpServerListener mcpServerListener) throws NacosException { if (StringUtils.isBlank(mcpName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `mcpName` can't be empty or null"); } if (null == mcpServerListener) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `mcpServerListener` can't be empty or null"); } McpServerListenerInvoker listenerInvoker = new McpServerListenerInvoker(mcpServerListener); aiChangeNotifier.registerListener(mcpName, version, listenerInvoker); McpServerDetailInfo result = grpcClient.subscribeMcpServer(mcpName, version); if (null != result && !listenerInvoker.isInvoked()) { listenerInvoker.invoke(new NacosMcpServerEvent(result)); } return result; } @Override public void unsubscribeMcpServer(String mcpName, String version, AbstractNacosMcpServerListener mcpServerListener) throws NacosException { if (StringUtils.isBlank(mcpName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `mcpName` can't be empty or null"); } if (null == mcpServerListener) { return; } McpServerListenerInvoker listenerInvoker = new McpServerListenerInvoker(mcpServerListener); aiChangeNotifier.deregisterListener(mcpName, version, listenerInvoker); if (!aiChangeNotifier.isMcpServerSubscribed(mcpName, version)) { grpcClient.unsubscribeMcpServer(mcpName, version); } } @Override public AgentCardDetailInfo getAgentCard(String agentName, String version, String registrationType) throws NacosException { if (StringUtils.isBlank(agentName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentName` can't be empty or null"); } return grpcClient.getAgentCard(agentName, version, registrationType); } @Override public void releaseAgentCard(AgentCard agentCard, String registrationType, boolean setAsLatest) throws NacosException { if (null == agentCard) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentCard` can't be null"); } validateAgentCardField("name", agentCard.getName()); validateAgentCardField("version", agentCard.getVersion()); validateAgentCardField("protocolVersion", agentCard.getProtocolVersion()); if (StringUtils.isBlank(registrationType)) { registrationType = AiConstants.A2a.A2A_ENDPOINT_TYPE_SERVICE; } grpcClient.releaseAgentCard(agentCard, registrationType, setAsLatest); } @Override public void registerAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { if (StringUtils.isBlank(agentName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentName` can't be empty or null"); } validateAgentEndpoint(endpoint); grpcClient.registerAgentEndpoint(agentName, endpoint); } @Override public void registerAgentEndpoint(String agentName, Collection endpoints) throws NacosException { if (StringUtils.isBlank(agentName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentName` can't be empty or null"); } validateAgentEndpoint(endpoints); grpcClient.registerAgentEndpoints(agentName, endpoints); } @Override public void deregisterAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { if (StringUtils.isBlank(agentName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentName` can't be empty or null"); } validateAgentEndpoint(endpoint); grpcClient.deregisterAgentEndpoint(agentName, endpoint); } @Override public AgentCardDetailInfo subscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException { if (StringUtils.isBlank(agentName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentName` can't be empty or null"); } if (null == agentCardListener) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentCardListener` can't be empty or null"); } AgentCardListenerInvoker listenerInvoker = new AgentCardListenerInvoker(agentCardListener); aiChangeNotifier.registerListener(agentName, version, listenerInvoker); AgentCardDetailInfo result = grpcClient.subscribeAgentCard(agentName, version); if (null != result && !listenerInvoker.isInvoked()) { listenerInvoker.invoke(new NacosAgentCardEvent(result)); } return result; } @Override public void unsubscribeAgentCard(String agentName, String version, AbstractNacosAgentCardListener agentCardListener) throws NacosException { if (StringUtils.isBlank(agentName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `agentName` can't be empty or null"); } if (null == agentCardListener) { return; } AgentCardListenerInvoker listenerInvoker = new AgentCardListenerInvoker(agentCardListener); aiChangeNotifier.deregisterListener(agentName, version, listenerInvoker); if (!aiChangeNotifier.isAgentCardSubscribed(agentName, version)) { grpcClient.unsubscribeAgentCard(agentName, version); } } private void validateAgentEndpoint(Collection endpoints) throws NacosApiException { if (null == endpoints || endpoints.isEmpty()) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `endpoints` can't be empty or null, if want to deregister endpoints, please use deregister API."); } Set versions = new HashSet<>(); for (AgentEndpoint endpoint : endpoints) { validateAgentEndpoint(endpoint); versions.add(endpoint.getVersion()); } if (versions.size() > 1) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_VALIDATE_ERROR, String.format("Required parameter `endpoint.version` can't be different, current includes: %s.", String.join(",", versions))); } } private void validateAgentEndpoint(AgentEndpoint endpoint) throws NacosApiException { if (null == endpoint) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `endpoint` can't be null"); } if (StringUtils.isBlank(endpoint.getVersion())) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `endpoint.version` can't be empty or null"); } Instance instance = new Instance(); instance.setIp(endpoint.getAddress()); instance.setPort(endpoint.getPort()); instance.validate(); } private static void validateAgentCardField(String fieldName, String fieldValue) throws NacosApiException { if (StringUtils.isEmpty(fieldValue)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `agentCard." + fieldName + "` not present"); } } @Override public Skill loadSkill(String skillName) throws NacosException { if (StringUtils.isBlank(skillName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "Required parameter `skillName` not present"); } // Build main config info SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); // Query main config (skill.json) String mainConfigContent; try { mainConfigContent = configService.getConfig(mainConfigInfo.getDataId(), mainConfigInfo.getGroup(), 3000); } catch (NacosException e) { throw new NacosException(NacosException.NOT_FOUND, "Skill main configuration not found for skillName: " + skillName + ", error: " + e.getMessage()); } if (StringUtils.isBlank(mainConfigContent)) { throw new NacosException(NacosException.NOT_FOUND, "Skill main configuration not found for skillName: " + skillName); } // Parse main config SkillMainConfig mainConfig; try { mainConfig = JacksonUtils.toObj(mainConfigContent, SkillMainConfig.class); } catch (Exception e) { throw new NacosException(NacosException.SERVER_ERROR, "Failed to parse skill main configuration: " + e.getMessage(), e); } // Build Skill object Skill skill = new Skill(); skill.setNamespaceId(this.namespaceId); skill.setName(mainConfig.getName()); skill.setDescription(mainConfig.getDescription()); skill.setInstruction(mainConfig.getInstruction()); // Query all Resource configs Map resourceMap = new HashMap<>( mainConfig.getResources() != null ? mainConfig.getResources().size() : 16); if (mainConfig.getResources() != null && !mainConfig.getResources().isEmpty()) { for (SkillResourceRef resourceRef : mainConfig.getResources()) { // Generate resourceId from type and name String resourceId = SkillUtils.generateResourceId(resourceRef.getType(), resourceRef.getName()); // Query resource config using resourceRef info SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); String resourceContent; try { resourceContent = configService.getConfig(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), 3000); } catch (NacosException e) { LOGGER.warn("Resource configuration not found: dataId={}, group={}, error={}", resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), e.getMessage()); continue; } if (StringUtils.isNotBlank(resourceContent)) { try { SkillResource resource = JacksonUtils.toObj(resourceContent, SkillResource.class); // Use resource name as key (from resource object, not resourceId) resourceMap.put(resource.getName() != null ? resource.getName() : resourceId, resource); } catch (Exception e) { LOGGER.warn("Failed to parse resource configuration: dataId={}, group={}, error={}", resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), e.getMessage()); } } } } skill.setResource(resourceMap); return skill; } /** * Skill main config (from skill.json). */ private static class SkillMainConfig { private String name; private String description; private String instruction; private List resources; 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 String getInstruction() { return instruction; } public void setInstruction(String instruction) { this.instruction = instruction; } public List getResources() { return resources; } public void setResources(List resources) { this.resources = resources; } } /** * Skill resource reference (in skill.json). */ private static class SkillResourceRef { private String name; private String type; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } } @Override public Skill subscribeSkill(String skillName, AbstractNacosSkillListener skillListener) throws NacosException { if (StringUtils.isBlank(skillName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `skillName` can't be empty or null"); } if (null == skillListener) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `skillListener` can't be empty or null"); } SkillListenerInvoker listenerInvoker = new SkillListenerInvoker(skillListener); aiChangeNotifier.registerListener(skillName, listenerInvoker); Skill result = skillCacheHolder.subscribeSkill(skillName); if (null != result && !listenerInvoker.isInvoked()) { listenerInvoker.invoke(new NacosSkillEvent(skillName, result)); } return result; } @Override public void unsubscribeSkill(String skillName, AbstractNacosSkillListener skillListener) throws NacosException { if (StringUtils.isBlank(skillName)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `skillName` can't be empty or null"); } if (null == skillListener) { return; } SkillListenerInvoker listenerInvoker = new SkillListenerInvoker(skillListener); aiChangeNotifier.deregisterListener(skillName, listenerInvoker); if (!aiChangeNotifier.isSkillSubscribed(skillName)) { skillCacheHolder.unsubscribeSkill(skillName); } } // ==================== Prompt Methods ==================== @Override public Prompt getPrompt(String promptKey) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `promptKey` can't be empty or null"); } return getPromptByVersion(promptKey, null); } @Override public Prompt getPromptByVersion(String promptKey, String version) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `promptKey` can't be empty or null"); } if (StringUtils.isBlank(version)) { return aiClientProxy.queryPrompt(promptKey, null, null, null); } return aiClientProxy.queryPrompt(promptKey, version, null, null); } @Override public Prompt getPromptByLabel(String promptKey, String label) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `promptKey` can't be empty or null"); } if (StringUtils.isBlank(label)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `label` can't be empty or null"); } return aiClientProxy.queryPrompt(promptKey, null, label, null); } @Override public Prompt subscribePrompt(String promptKey, String version, String label, AbstractNacosPromptListener promptListener) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `promptKey` can't be empty or null"); } if (null == promptListener) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `promptListener` can't be null"); } PromptListenerInvoker listenerInvoker = new PromptListenerInvoker(promptListener); aiChangeNotifier.registerListener(promptKey, version, label, listenerInvoker); Prompt result = promptCacheHolder.subscribePrompt(promptKey, version, label); if (null != result && !listenerInvoker.isInvoked()) { listenerInvoker.invoke(new NacosPromptEvent(promptKey, result)); } return result; } @Override public void unsubscribePrompt(String promptKey, String version, String label, AbstractNacosPromptListener promptListener) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.PARAMETER_MISSING, "parameters `promptKey` can't be empty or null"); } if (null == promptListener) { return; } PromptListenerInvoker listenerInvoker = new PromptListenerInvoker(promptListener); aiChangeNotifier.deregisterListener(promptKey, version, label, listenerInvoker); if (!aiChangeNotifier.isPromptSubscribed(promptKey, version, label)) { promptCacheHolder.unsubscribePrompt(promptKey, version, label); } } @Override public void shutdown() throws NacosException { this.grpcClient.shutdown(); if (this.aiClientProxy != this.grpcClient) { this.aiClientProxy.shutdown(); } this.mcpServerCacheHolder.shutdown(); this.skillCacheHolder.shutdown(); this.promptCacheHolder.shutdown(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/cache/NacosAgentCardCacheHolder.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.cache; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentInterface; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.event.AgentCardChangedEvent; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.ai.utils.CacheKeyUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Nacos AI module agent card cache holder. * * @author xiweng.yy */ public class NacosAgentCardCacheHolder implements Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(NacosAgentCardCacheHolder.class); private final AiGrpcClient aiGrpcClient; private final Map agentCardCache; private final ScheduledExecutorService updaterExecutor; private final long updateIntervalMillis; private final Map updateTaskMap; public NacosAgentCardCacheHolder(AiGrpcClient aiGrpcClient, NacosClientProperties properties) { this.aiGrpcClient = aiGrpcClient; this.agentCardCache = new ConcurrentHashMap<>(4); this.updateTaskMap = new ConcurrentHashMap<>(4); this.updaterExecutor = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.ai.agent.card.updater")); this.updateIntervalMillis = properties.getLong(AiConstants.AI_AGENT_CARD_CACHE_UPDATE_INTERVAL, AiConstants.DEFAULT_AI_CACHE_UPDATE_INTERVAL); } public AgentCardDetailInfo getAgentCard(String agentName, String version) { String key = CacheKeyUtils.buildAgentCardKey(agentName, version); return agentCardCache.get(key); } /** * Process new agent card detail info. * * @param detailInfo new agent card detail info */ public void processAgentCardDetailInfo(AgentCardDetailInfo detailInfo) { String agentName = detailInfo.getName(); String version = detailInfo.getVersion(); Boolean isLatest = detailInfo.isLatestVersion(); String key = CacheKeyUtils.buildAgentCardKey(agentName, version); AgentCardDetailInfo oldAgentCard = agentCardCache.get(key); agentCardCache.put(key, detailInfo); if (null != isLatest && isLatest) { String latestVersionKey = CacheKeyUtils.buildAgentCardKey(agentName, null); agentCardCache.put(latestVersionKey, detailInfo); } if (isAgentCardChanged(oldAgentCard, detailInfo)) { LOGGER.info("agent card {} changed, from {} -> {}.", detailInfo.getName(), JacksonUtils.toJson(oldAgentCard), JacksonUtils.toJson(detailInfo)); NotifyCenter.publishEvent(new AgentCardChangedEvent(detailInfo)); } } /** * Add new update task for agent card. * * @param agentName name of agent card * @param version version of agent card */ public void addAgentCardUpdateTask(String agentName, String version) { String agentCardKey = CacheKeyUtils.buildAgentCardKey(agentName, version); this.updateTaskMap.computeIfAbsent(agentCardKey, s -> { AgentCardUpdater updateTask = new AgentCardUpdater(agentName, version); updaterExecutor.schedule(updateTask, updateIntervalMillis, TimeUnit.MILLISECONDS); return updateTask; }); } /** * Remove new update task for agent card. * * @param agentName name of agent card * @param version version of agent card */ public void removeAgentCardUpdateTask(String agentName, String version) { String agentNameKey = CacheKeyUtils.buildAgentCardKey(agentName, version); AgentCardUpdater updateTask = this.updateTaskMap.remove(agentNameKey); if (null != updateTask) { updateTask.cancel(); } } private boolean isAgentCardChanged(AgentCardDetailInfo oldAgentCard, AgentCardDetailInfo newAgentCard) { if (null == oldAgentCard) { LOGGER.info("init new agent card: {} -> {}", newAgentCard.getName(), JacksonUtils.toJson(newAgentCard)); return true; } if (!Objects.equals(oldAgentCard.getVersion(), newAgentCard.getVersion())) { return true; } List oldInterfaces = oldAgentCard.getAdditionalInterfaces(); List newInterfaces = newAgentCard.getAdditionalInterfaces(); if (Objects.isNull(oldInterfaces) && Objects.isNull(newInterfaces)) { return !Objects.equals(oldAgentCard.getUrl(), newAgentCard.getUrl()); } if (anyOneIsNull(oldInterfaces, newInterfaces)) { return true; } // two interfaces both not null. return !CollectionUtils.isEqualCollection(oldInterfaces, newInterfaces); } private boolean anyOneIsNull(List oldAdditionalInterfaces, List newAdditionalInterfaces) { if (Objects.isNull(oldAdditionalInterfaces)) { return true; } return Objects.isNull(newAdditionalInterfaces); } @Override public void shutdown() throws NacosException { this.updaterExecutor.shutdownNow(); } private class AgentCardUpdater implements Runnable { private final String agentName; private final String version; private final AtomicBoolean cancel; public AgentCardUpdater(String agentName, String version) { this.agentName = agentName; this.version = version; this.cancel = new AtomicBoolean(false); } @Override public void run() { if (cancel.get()) { return; } try { AgentCardDetailInfo detailInfo = aiGrpcClient.getAgentCard(agentName, version, StringUtils.EMPTY); processAgentCardDetailInfo(detailInfo); } catch (Exception e) { if (e instanceof NacosException) { NacosException nacosException = (NacosException) e; if (nacosException.getErrCode() == NacosException.NOT_FOUND) { return; } } LOGGER.warn("AgentCard updater execute query failed", e); } finally { if (!cancel.get()) { updaterExecutor.schedule(this, updateIntervalMillis, TimeUnit.MILLISECONDS); } } } public void cancel() { cancel.set(true); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/cache/NacosMcpServerCacheHolder.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.cache; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.event.McpServerChangedEvent; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.ai.utils.CacheKeyUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.StringUtils; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Nacos AI module mcp server cache holder. * * @author xiweng.yy */ public class NacosMcpServerCacheHolder implements Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(NacosMcpServerCacheHolder.class); private final AiGrpcClient aiGrpcClient; private final Map mcpServerCache; private final ObjectMapper objectMapper; private final ScheduledExecutorService updaterExecutor; private final long updateIntervalMillis; private final Map updateTaskMap; public NacosMcpServerCacheHolder(AiGrpcClient aiGrpcClient, NacosClientProperties properties) { this.aiGrpcClient = aiGrpcClient; this.mcpServerCache = new ConcurrentHashMap<>(4); this.updateTaskMap = new ConcurrentHashMap<>(4); this.objectMapper = JsonMapper.builder().configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build() .setSerializationInclusion(JsonInclude.Include.NON_NULL); this.updaterExecutor = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.ai.mcp.server.updater")); this.updateIntervalMillis = properties.getLong(AiConstants.AI_MCP_SERVER_CACHE_UPDATE_INTERVAL, AiConstants.DEFAULT_AI_CACHE_UPDATE_INTERVAL); } public McpServerDetailInfo getMcpServer(String mcpName, String version) { String key = CacheKeyUtils.buildMcpServerKey(mcpName, version); return mcpServerCache.get(key); } /** * Process new mcp server detail info. * * @param detailInfo new mcp server detail info */ public void processMcpServerDetailInfo(McpServerDetailInfo detailInfo) { String mcpName = detailInfo.getName(); String version = detailInfo.getVersionDetail().getVersion(); Boolean isLatest = detailInfo.getVersionDetail().getIs_latest(); String key = CacheKeyUtils.buildMcpServerKey(mcpName, version); McpServerDetailInfo oldMcpServer = mcpServerCache.get(key); mcpServerCache.put(key, detailInfo); if (null != isLatest && isLatest) { String latestVersionKey = CacheKeyUtils.buildMcpServerKey(mcpName, null); mcpServerCache.put(latestVersionKey, detailInfo); } if (isMcpServerChanged(oldMcpServer, detailInfo)) { LOGGER.info("mcp server {} changed.", detailInfo.getName()); NotifyCenter.publishEvent(new McpServerChangedEvent(detailInfo)); } } /** * Add new update task for mcp server. * * @param mcpName name of mcp server * @param version version of mcp server */ public void addMcpServerUpdateTask(String mcpName, String version) { String mcpServerKey = CacheKeyUtils.buildMcpServerKey(mcpName, version); this.updateTaskMap.computeIfAbsent(mcpServerKey, s -> { McpServerUpdater updateTask = new McpServerUpdater(mcpName, version); updaterExecutor.schedule(updateTask, updateIntervalMillis, TimeUnit.MILLISECONDS); return updateTask; }); } /** * Remove new update task for mcp server. * * @param mcpName name of mcp server * @param version version of mcp server */ public void removeMcpServerUpdateTask(String mcpName, String version) { String mcpServerKey = CacheKeyUtils.buildMcpServerKey(mcpName, version); McpServerUpdater updateTask = this.updateTaskMap.remove(mcpServerKey); if (null != updateTask) { updateTask.cancel(); } } private boolean isMcpServerChanged(McpServerDetailInfo oldMcpServer, McpServerDetailInfo detailInfo) { try { String newJson = objectMapper.writeValueAsString(detailInfo); if (null == oldMcpServer) { LOGGER.info("init new mcp service: {} -> {}", detailInfo.getName(), newJson); return true; } String oldJson = objectMapper.writeValueAsString(oldMcpServer); if (!StringUtils.equals(oldJson, newJson)) { LOGGER.info("mcp service changed: {} -> {}", oldJson, newJson); return true; } } catch (JsonProcessingException e) { LOGGER.error("Compare mcp server info failed: ", e); } return false; } @Override public void shutdown() throws NacosException { this.updaterExecutor.shutdownNow(); } private class McpServerUpdater implements Runnable { private final String mcpName; private final String version; private final AtomicBoolean cancel; public McpServerUpdater(String mcpName, String version) { this.mcpName = mcpName; this.version = version; this.cancel = new AtomicBoolean(false); } @Override public void run() { if (cancel.get()) { return; } try { McpServerDetailInfo detailInfo = aiGrpcClient.queryMcpServer(mcpName, version); processMcpServerDetailInfo(detailInfo); } catch (Exception e) { if (e instanceof NacosException) { NacosException nacosException = (NacosException) e; if (nacosException.getErrCode() == NacosException.NOT_FOUND) { return; } } LOGGER.warn("Mcp server updater execute query failed", e); } finally { if (!cancel.get()) { updaterExecutor.schedule(this, updateIntervalMillis, TimeUnit.MILLISECONDS); } } } public void cancel() { cancel.set(true); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/cache/NacosPromptCacheHolder.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.cache; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.remote.AiClientProxy; import com.alibaba.nacos.client.ai.event.PromptChangedEvent; import com.alibaba.nacos.client.ai.utils.CacheKeyUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Nacos AI module prompt cache holder. * * @author nacos */ public class NacosPromptCacheHolder implements Closeable { private static final Logger LOGGER = LogUtils.logger(NacosPromptCacheHolder.class); private final AiClientProxy aiClientProxy; private final Map promptCache; private final ScheduledExecutorService updaterExecutor; private final long updateIntervalMillis; private final Map updateTaskMap; public NacosPromptCacheHolder(AiClientProxy aiClientProxy, NacosClientProperties properties) { this.aiClientProxy = aiClientProxy; this.promptCache = new ConcurrentHashMap<>(4); this.updateTaskMap = new ConcurrentHashMap<>(4); this.updaterExecutor = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.ai.prompt.updater")); this.updateIntervalMillis = properties.getLong(AiConstants.AI_PROMPT_CACHE_UPDATE_INTERVAL, AiConstants.DEFAULT_AI_CACHE_UPDATE_INTERVAL); } private Prompt queryPrompt(String promptKey, String version, String label) throws NacosException { return queryPrompt(promptKey, version, label, null); } private Prompt queryPrompt(String promptKey, String version, String label, String md5) throws NacosException { return aiClientProxy.queryPrompt(promptKey, version, label, md5); } /** * Subscribe prompt and start polling for prompt changes. * * @param promptKey prompt key * @return current Prompt object, null if not found * @throws NacosException if error occurs */ public Prompt subscribePrompt(String promptKey, String version, String label) throws NacosException { if (StringUtils.isBlank(promptKey)) { throw new NacosException(NacosException.INVALID_PARAM, "Required parameter `promptKey` not present"); } String cacheKey = CacheKeyUtils.buildPromptKey(promptKey, version, label); Prompt prompt = null; try { prompt = queryPrompt(promptKey, version, label); processPrompt(promptKey, cacheKey, prompt); } catch (NacosException e) { if (e.getErrCode() != NacosException.NOT_FOUND) { throw e; } processPrompt(promptKey, cacheKey, null); } addPromptUpdateTask(promptKey, version, label); LOGGER.info("Subscribed prompt: {}, version: {}, label: {}", promptKey, version, label); return prompt; } /** * Unsubscribe prompt and remove update task. * * @param promptKey prompt key */ public void unsubscribePrompt(String promptKey, String version, String label) { if (StringUtils.isBlank(promptKey)) { return; } String cacheKey = CacheKeyUtils.buildPromptKey(promptKey, version, label); removePromptUpdateTask(promptKey, version, label); promptCache.remove(cacheKey); LOGGER.info("Unsubscribed prompt: {}, version: {}, label: {}", promptKey, version, label); } @Override public void shutdown() throws NacosException { this.updaterExecutor.shutdownNow(); } private void addPromptUpdateTask(String promptKey, String version, String label) { String key = CacheKeyUtils.buildPromptKey(promptKey, version, label); this.updateTaskMap.computeIfAbsent(key, s -> { PromptUpdater task = new PromptUpdater(promptKey, version, label); updaterExecutor.schedule(task, updateIntervalMillis, TimeUnit.MILLISECONDS); return task; }); } private void removePromptUpdateTask(String promptKey, String version, String label) { String key = CacheKeyUtils.buildPromptKey(promptKey, version, label); PromptUpdater task = this.updateTaskMap.remove(key); if (task != null) { task.cancel(); } } private void processPrompt(String promptKey, String cacheKey, Prompt newPrompt) { Prompt oldPrompt = promptCache.get(cacheKey); if (newPrompt == null) { promptCache.remove(cacheKey); } else { promptCache.put(cacheKey, newPrompt); } if (isPromptChanged(oldPrompt, newPrompt)) { NotifyCenter.publishEvent(new PromptChangedEvent(promptKey, cacheKey, newPrompt)); } } private boolean isPromptChanged(Prompt oldPrompt, Prompt newPrompt) { String oldJson = oldPrompt == null ? StringUtils.EMPTY : JacksonUtils.toJson(oldPrompt); String newJson = newPrompt == null ? StringUtils.EMPTY : JacksonUtils.toJson(newPrompt); return !StringUtils.equals(oldJson, newJson); } private class PromptUpdater implements Runnable { private final String promptKey; private final String version; private final String label; private final String cacheKey; private final AtomicBoolean cancel = new AtomicBoolean(false); PromptUpdater(String promptKey, String version, String label) { this.promptKey = promptKey; this.version = version; this.label = label; this.cacheKey = CacheKeyUtils.buildPromptKey(promptKey, version, label); } void cancel() { cancel.set(true); } @Override public void run() { if (cancel.get()) { return; } try { Prompt currentPrompt = promptCache.get(cacheKey); String currentMd5 = currentPrompt == null ? null : currentPrompt.getMd5(); Prompt latestPrompt = queryPrompt(promptKey, version, label, currentMd5); processPrompt(promptKey, cacheKey, latestPrompt); } catch (NacosException e) { if (e.getErrCode() == NacosException.NOT_FOUND) { processPrompt(promptKey, cacheKey, null); } else if (e.getErrCode() == NacosException.NOT_MODIFIED) { // No content change, keep local cache and skip callback. } else { LOGGER.warn("Prompt updater execute query failed: promptKey={}, err={}", promptKey, e.getErrMsg()); } } finally { if (!cancel.get()) { updaterExecutor.schedule(this, updateIntervalMillis, TimeUnit.MILLISECONDS); } } } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/cache/NacosSkillCacheHolder.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.cache; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillResource; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import java.util.concurrent.Executor; import com.alibaba.nacos.api.ai.model.skills.SkillUtils; import com.alibaba.nacos.client.ai.event.SkillChangedEvent; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.json.JsonMapper; import org.slf4j.Logger; 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; /** * Nacos AI module skill cache holder. * * @author nacos */ public class NacosSkillCacheHolder implements Closeable { private static final Logger LOGGER = LogUtils.logger(NacosSkillCacheHolder.class); private final ConfigService configService; private final String namespaceId; private final Map skillCache; private final Map subscriptionMap; private final ObjectMapper objectMapper; public NacosSkillCacheHolder(ConfigService configService, String namespaceId) { this.configService = configService; this.namespaceId = namespaceId; this.skillCache = new ConcurrentHashMap<>(4); this.subscriptionMap = new ConcurrentHashMap<>(4); this.objectMapper = JsonMapper.builder().configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES).build() .setSerializationInclusion(JsonInclude.Include.NON_NULL); } /** * Subscribe skill and start listening for configuration changes. * * @param skillName name of skill * @return current Skill object, nullable if skill not found * @throws NacosException if error occurs */ public Skill subscribeSkill(String skillName) throws NacosException { if (StringUtils.isBlank(skillName)) { throw new NacosException(NacosException.INVALID_PARAM, "Required parameter `skillName` not present"); } // Check if already subscribed if (subscriptionMap.containsKey(skillName)) { return skillCache.get(skillName); } // Load skill initially Skill skill = loadSkill(skillName); // Create subscription info SkillSubscriptionInfo subscriptionInfo = new SkillSubscriptionInfo(skillName); subscriptionInfo.setCurrentSkill(skill); // Build main config info SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); // Add listener for main config Listener mainConfigListener = new SkillConfigListener(skillName, true, null); try { configService.addListener(mainConfigInfo.getDataId(), mainConfigInfo.getGroup(), mainConfigListener); subscriptionInfo.setMainConfigListener(mainConfigListener); } catch (NacosException e) { LOGGER.warn("Failed to add listener for main config: skillName={}, error={}", skillName, e.getMessage()); } // Add listeners for all resource configs if (skill != null && skill.getResource() != null && !skill.getResource().isEmpty()) { // Load main config to get resourceId mapping SkillMainConfig mainConfig = loadMainConfig(skillName, mainConfigInfo.getGroup()); if (mainConfig != null && mainConfig.getResources() != null && !mainConfig.getResources().isEmpty()) { for (SkillResourceRef resourceRef : mainConfig.getResources()) { // Generate resourceId from type and name String resourceId = SkillUtils.generateResourceId(resourceRef.getType(), resourceRef.getName()); // Build resource config info using resourceRef SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); Listener resourceListener = new SkillConfigListener(skillName, false, resourceId); try { configService.addListener(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), resourceListener); subscriptionInfo.getResourceListeners().put(resourceId, resourceListener); } catch (NacosException e) { LOGGER.warn("Failed to add listener for resource config: skillName={}, resourceId={}, error={}", skillName, resourceId, e.getMessage()); } } } } // Cache skill and subscription info if (skill != null) { skillCache.put(skillName, skill); } subscriptionMap.put(skillName, subscriptionInfo); LOGGER.info("Subscribed skill: {}", skillName); return skill; } /** * Unsubscribe skill and remove all listeners. * * @param skillName name of skill */ public void unsubscribeSkill(String skillName) { if (StringUtils.isBlank(skillName)) { return; } SkillSubscriptionInfo subscriptionInfo = subscriptionMap.remove(skillName); if (subscriptionInfo == null) { return; } // Build main config info SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); // Remove main config listener if (subscriptionInfo.getMainConfigListener() != null) { try { configService.removeListener(mainConfigInfo.getDataId(), mainConfigInfo.getGroup(), subscriptionInfo.getMainConfigListener()); } catch (Exception e) { LOGGER.warn("Failed to remove listener for main config: skillName={}, error={}", skillName, e.getMessage()); } } // Remove all resource config listeners // Note: We need to load mainConfig to get resourceRef info for building ConfigInfo SkillMainConfig mainConfig = loadMainConfig(skillName, mainConfigInfo.getGroup()); if (mainConfig != null && mainConfig.getResources() != null) { Map resourceRefMap = new HashMap<>( mainConfig.getResources().size() > 0 ? (int) (mainConfig.getResources().size() / 0.75f + 1) : 16); for (SkillResourceRef resourceRef : mainConfig.getResources()) { String resourceId = SkillUtils.generateResourceId(resourceRef.getType(), resourceRef.getName()); resourceRefMap.put(resourceId, resourceRef); } for (Map.Entry entry : subscriptionInfo.getResourceListeners().entrySet()) { String resourceId = entry.getKey(); Listener listener = entry.getValue(); SkillResourceRef resourceRef = resourceRefMap.get(resourceId); if (resourceRef != null) { SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); try { configService.removeListener(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), listener); } catch (Exception e) { LOGGER.warn("Failed to remove listener for resource config: skillName={}, resourceId={}, error={}", skillName, resourceId, e.getMessage()); } } } } // Clear cache skillCache.remove(skillName); LOGGER.info("Unsubscribed skill: {}", skillName); } /** * Load skill from configuration. * * @param skillName name of skill * @return Skill object, nullable if skill not found */ private Skill loadSkill(String skillName) { // Build main config info SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); // Load main config SkillMainConfig mainConfig = loadMainConfig(skillName, mainConfigInfo.getGroup()); if (mainConfig == null) { return null; } // Build Skill object Skill skill = new Skill(); skill.setNamespaceId(this.namespaceId); skill.setName(mainConfig.getName()); skill.setDescription(mainConfig.getDescription()); skill.setInstruction(mainConfig.getInstruction()); // Query all Resource configs Map resourceMap = new HashMap<>( mainConfig.getResources() != null ? mainConfig.getResources().size() : 16); if (mainConfig.getResources() != null && !mainConfig.getResources().isEmpty()) { for (SkillResourceRef resourceRef : mainConfig.getResources()) { // Generate resourceId from type and name String resourceId = SkillUtils.generateResourceId(resourceRef.getType(), resourceRef.getName()); // Query resource config using resourceRef info SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); String resourceContent; try { resourceContent = configService.getConfig(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), 3000); } catch (NacosException e) { LOGGER.warn("Resource configuration not found: dataId={}, group={}, error={}", resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), e.getMessage()); continue; } if (StringUtils.isNotBlank(resourceContent)) { try { SkillResource resource = JacksonUtils.toObj(resourceContent, SkillResource.class); // Use resource name as key (from resource object, not resourceId) resourceMap.put(resource.getName() != null ? resource.getName() : resourceId, resource); } catch (Exception e) { LOGGER.warn("Failed to parse resource configuration: dataId={}, group={}, error={}", resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), e.getMessage()); } } } } skill.setResource(resourceMap); return skill; } /** * Load main config from configuration. * * @param skillName name of skill * @param skillGroup group of skill * @return SkillMainConfig object, nullable if not found */ private SkillMainConfig loadMainConfig(String skillName, String skillGroup) { // Query main config (skill.json) SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); String mainConfigContent; try { mainConfigContent = configService.getConfig(mainConfigInfo.getDataId(), skillGroup, 3000); } catch (NacosException e) { LOGGER.warn("Skill main configuration not found: skillName={}, error={}", skillName, e.getMessage()); return null; } if (StringUtils.isBlank(mainConfigContent)) { LOGGER.warn("Skill main configuration is blank: skillName={}", skillName); return null; } // Parse main config try { return JacksonUtils.toObj(mainConfigContent, SkillMainConfig.class); } catch (Exception e) { LOGGER.warn("Failed to parse skill main configuration: skillName={}, error={}", skillName, e.getMessage()); return null; } } /** * Reload skill and check for changes. * * @param skillName name of skill */ private void reloadSkill(String skillName) { try { SkillSubscriptionInfo subscriptionInfo = subscriptionMap.get(skillName); if (subscriptionInfo == null) { return; } Skill oldSkill = subscriptionInfo.getCurrentSkill(); Skill newSkill = loadSkill(skillName); if (isSkillChanged(oldSkill, newSkill)) { LOGGER.info("Skill {} changed.", skillName); subscriptionInfo.setCurrentSkill(newSkill); if (newSkill != null) { skillCache.put(skillName, newSkill); } else { skillCache.remove(skillName); } // Update resource listeners if resource list changed updateResourceListeners(skillName, oldSkill, newSkill); // Publish change event NotifyCenter.publishEvent(new SkillChangedEvent(skillName, newSkill)); } } catch (Exception e) { LOGGER.error("Failed to reload skill: skillName={}, error={}", skillName, e.getMessage(), e); } } /** * Update resource listeners based on resource list changes. * * @param skillName name of skill * @param oldSkill old skill object * @param newSkill new skill object */ private void updateResourceListeners(String skillName, Skill oldSkill, Skill newSkill) { SkillSubscriptionInfo subscriptionInfo = subscriptionMap.get(skillName); if (subscriptionInfo == null) { return; } SkillUtils.ConfigInfo mainConfigInfo = SkillUtils.buildSkillMainConfigInfo(skillName); // Load main configs to get resourceId mappings SkillMainConfig oldMainConfig = oldSkill != null ? loadMainConfig(skillName, mainConfigInfo.getGroup()) : null; SkillMainConfig newMainConfig = newSkill != null ? loadMainConfig(skillName, mainConfigInfo.getGroup()) : null; int oldResourceSize = oldMainConfig != null && oldMainConfig.getResources() != null ? oldMainConfig.getResources().size() : 0; int newResourceSize = newMainConfig != null && newMainConfig.getResources() != null ? newMainConfig.getResources().size() : 0; Set oldResourceIds = new HashSet<>(oldResourceSize > 0 ? (int) (oldResourceSize / 0.75f + 1) : 16); Map oldResourceRefMap = new HashMap<>( oldResourceSize > 0 ? (int) (oldResourceSize / 0.75f + 1) : 16); if (oldMainConfig != null && oldMainConfig.getResources() != null) { for (SkillResourceRef ref : oldMainConfig.getResources()) { String resourceId = SkillUtils.generateResourceId(ref.getType(), ref.getName()); oldResourceIds.add(resourceId); oldResourceRefMap.put(resourceId, ref); } } Set newResourceIds = new HashSet<>(newResourceSize > 0 ? (int) (newResourceSize / 0.75f + 1) : 16); Map newResourceRefMap = new HashMap<>( newResourceSize > 0 ? (int) (newResourceSize / 0.75f + 1) : 16); if (newMainConfig != null && newMainConfig.getResources() != null) { for (SkillResourceRef ref : newMainConfig.getResources()) { String resourceId = SkillUtils.generateResourceId(ref.getType(), ref.getName()); newResourceIds.add(resourceId); newResourceRefMap.put(resourceId, ref); } } // Remove listeners for deleted resources Set toRemove = new HashSet<>(oldResourceIds); toRemove.removeAll(newResourceIds); for (String resourceId : toRemove) { Listener listener = subscriptionInfo.getResourceListeners().remove(resourceId); if (listener != null) { SkillResourceRef resourceRef = oldResourceRefMap.get(resourceId); if (resourceRef != null) { SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); try { configService.removeListener(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), listener); } catch (Exception e) { LOGGER.warn("Failed to remove listener for deleted resource: skillName={}, resourceId={}, error={}", skillName, resourceId, e.getMessage()); } } } } // Add listeners for new resources Set toAdd = new HashSet<>(newResourceIds); toAdd.removeAll(oldResourceIds); for (String resourceId : toAdd) { SkillResourceRef resourceRef = newResourceRefMap.get(resourceId); if (resourceRef != null) { SkillUtils.ConfigInfo resourceConfigInfo = SkillUtils.buildSkillResourceConfigInfo( skillName, resourceRef.getType(), resourceRef.getName()); Listener resourceListener = new SkillConfigListener(skillName, false, resourceId); try { configService.addListener(resourceConfigInfo.getDataId(), resourceConfigInfo.getGroup(), resourceListener); subscriptionInfo.getResourceListeners().put(resourceId, resourceListener); } catch (NacosException e) { LOGGER.warn("Failed to add listener for new resource: skillName={}, resourceId={}, error={}", skillName, resourceId, e.getMessage()); } } } } /** * Check if skill has changed by comparing JSON serialization. * * @param oldSkill old skill object * @param newSkill new skill object * @return true if changed, false otherwise */ private boolean isSkillChanged(Skill oldSkill, Skill newSkill) { try { String newJson = objectMapper.writeValueAsString(newSkill); if (null == oldSkill) { LOGGER.info("init new skill: {} -> {}", newSkill != null ? newSkill.getName() : "null", newJson); return true; } String oldJson = objectMapper.writeValueAsString(oldSkill); if (!StringUtils.equals(oldJson, newJson)) { LOGGER.info("skill changed: {} -> {}", oldJson, newJson); return true; } } catch (JsonProcessingException e) { LOGGER.error("Compare skill info failed: ", e); } return false; } @Override public void shutdown() throws NacosException { // Unsubscribe all skills Set skillNames = new HashSet<>(subscriptionMap.keySet()); for (String skillName : skillNames) { unsubscribeSkill(skillName); } } /** * Skill configuration listener. */ private class SkillConfigListener implements Listener { private final String skillName; private final boolean isMainConfig; private final String resourceName; public SkillConfigListener(String skillName, boolean isMainConfig, String resourceName) { this.skillName = skillName; this.isMainConfig = isMainConfig; this.resourceName = resourceName; } @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { LOGGER.info("Skill configuration changed: skillName={}, isMainConfig={}, resourceName={}", skillName, isMainConfig, resourceName); // Reload skill when any configuration changes reloadSkill(skillName); } } /** * Skill subscription information. */ private static class SkillSubscriptionInfo { private final String skillName; private Listener mainConfigListener; private final Map resourceListeners; private Skill currentSkill; public SkillSubscriptionInfo(String skillName) { this.skillName = skillName; this.resourceListeners = new ConcurrentHashMap<>(); } public String getSkillName() { return skillName; } public Listener getMainConfigListener() { return mainConfigListener; } public void setMainConfigListener(Listener mainConfigListener) { this.mainConfigListener = mainConfigListener; } public Map getResourceListeners() { return resourceListeners; } public Skill getCurrentSkill() { return currentSkill; } public void setCurrentSkill(Skill currentSkill) { this.currentSkill = currentSkill; } } /** * Skill main config (from skill.json). */ private static class SkillMainConfig { private String name; private String description; private String instruction; private List resources; 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 String getInstruction() { return instruction; } public void setInstruction(String instruction) { this.instruction = instruction; } public List getResources() { return resources; } public void setResources(List resources) { this.resources = resources; } } /** * Skill resource reference (in skill.json). */ private static class SkillResourceRef { private String name; private String type; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getType() { return type; } public void setType(String type) { this.type = type; } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/AbstractAiListenerInvoker.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.NacosAiEvent; import com.alibaba.nacos.api.ai.listener.NacosAiListener; import com.alibaba.nacos.client.selector.ListenerInvoker; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; /** * Nacos AI module abstract ai listener invoker. * * @author xiweng.yy */ public abstract class AbstractAiListenerInvoker> implements ListenerInvoker { protected final L listener; private final AtomicBoolean invoked = new AtomicBoolean(false); public AbstractAiListenerInvoker(L listener) { this.listener = listener; } @Override public void invoke(E event) { invoked.set(true); logInvoke(event); if (listener.getExecutor() != null) { listener.getExecutor().execute(() -> listener.onEvent(event)); } else { listener.onEvent(event); } } /** * log invoker be invoked. * * @param event event */ protected abstract void logInvoke(E event); @Override public boolean isInvoked() { return invoked.get(); } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } if (this == o) { return true; } AbstractAiListenerInvoker that = (AbstractAiListenerInvoker) o; return Objects.equals(listener, that.listener); } @Override public int hashCode() { return Objects.hashCode(listener); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/AgentCardChangedEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.client.ai.utils.CacheKeyUtils; import com.alibaba.nacos.common.notify.Event; /** * Nacos AI module agent card changed event in nacos- client. * * @author xiweng.yy */ public class AgentCardChangedEvent extends Event { private static final long serialVersionUID = 2010793364377243018L; private final String agentName; private final String version; private final AgentCardDetailInfo agentCard; public AgentCardChangedEvent(AgentCardDetailInfo agentCard) { this.agentCard = agentCard; this.agentName = agentCard.getName(); this.version = buildVersion(agentCard); } private String buildVersion(AgentCardDetailInfo agentCard) { if (null == agentCard.isLatestVersion() || agentCard.isLatestVersion()) { return CacheKeyUtils.LATEST_VERSION; } return agentCard.getVersion(); } public String getAgentName() { return agentName; } public String getVersion() { return version; } public AgentCardDetailInfo getAgentCard() { return agentCard; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/AgentCardListenerInvoker.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.AbstractNacosAgentCardListener; import com.alibaba.nacos.api.ai.listener.NacosAgentCardEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Nacos AI module agent card listener invoker. * * @author xiweng.yy */ public class AgentCardListenerInvoker extends AbstractAiListenerInvoker { private static final Logger LOGGER = LoggerFactory.getLogger(AgentCardListenerInvoker.class); public AgentCardListenerInvoker(AbstractNacosAgentCardListener listener) { super(listener); } protected void logInvoke(NacosAgentCardEvent event) { LOGGER.info("Invoke event agentName: {} to Listener: {}", event.getAgentName(), listener.toString()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/AiChangeNotifier.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.NacosAgentCardEvent; import com.alibaba.nacos.api.ai.listener.NacosMcpServerEvent; import com.alibaba.nacos.api.ai.listener.NacosPromptEvent; import com.alibaba.nacos.api.ai.listener.NacosSkillEvent; import com.alibaba.nacos.client.ai.utils.CacheKeyUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Nacos AI module mcp server change notifier. * * @author xiweng.yy */ public class AiChangeNotifier extends SmartSubscriber { private final Map> mcpServerListenerInvokers; private final Map> agentCardListenerInvokers; private final Map> skillListenerInvokers; private final Map> promptListenerInvokers; public AiChangeNotifier() { this.mcpServerListenerInvokers = new ConcurrentHashMap<>(2); this.agentCardListenerInvokers = new ConcurrentHashMap<>(2); this.skillListenerInvokers = new ConcurrentHashMap<>(2); this.promptListenerInvokers = new ConcurrentHashMap<>(2); } @Override public void onEvent(Event event) { if (event instanceof McpServerChangedEvent) { handleMcpServerChangedEvent((McpServerChangedEvent) event); } else if (event instanceof AgentCardChangedEvent) { handleAgentCardChangedEvent((AgentCardChangedEvent) event); } else if (event instanceof SkillChangedEvent) { handleSkillChangedEvent((SkillChangedEvent) event); } else if (event instanceof PromptChangedEvent) { handlePromptChangedEvent((PromptChangedEvent) event); } } private void handleMcpServerChangedEvent(McpServerChangedEvent event) { String mcpServerKey = CacheKeyUtils.buildMcpServerKey(event.getMcpName(), event.getVersion()); if (!isSubscribed(mcpServerKey, mcpServerListenerInvokers)) { return; } NacosMcpServerEvent notifiedEvent = new NacosMcpServerEvent(event.getMcpServer()); for (McpServerListenerInvoker each : mcpServerListenerInvokers.get(mcpServerKey)) { each.invoke(notifiedEvent); } } private void handleAgentCardChangedEvent(AgentCardChangedEvent event) { String agentCardKey = CacheKeyUtils.buildAgentCardKey(event.getAgentName(), event.getVersion()); if (!isSubscribed(agentCardKey, agentCardListenerInvokers)) { return; } NacosAgentCardEvent notifiedEvent = new NacosAgentCardEvent(event.getAgentCard()); for (AgentCardListenerInvoker each : agentCardListenerInvokers.get(agentCardKey)) { each.invoke(notifiedEvent); } } private void handleSkillChangedEvent(SkillChangedEvent event) { String skillKey = CacheKeyUtils.buildSkillKey(event.getSkillName()); if (!isSubscribed(skillKey, skillListenerInvokers)) { return; } NacosSkillEvent notifiedEvent = new NacosSkillEvent(event.getSkillName(), event.getSkill()); for (SkillListenerInvoker each : skillListenerInvokers.get(skillKey)) { each.invoke(notifiedEvent); } } private void handlePromptChangedEvent(PromptChangedEvent event) { String promptCacheKey = event.getCacheKey(); if (!isSubscribed(promptCacheKey, promptListenerInvokers)) { return; } NacosPromptEvent notifiedEvent = new NacosPromptEvent(event.getPromptKey(), event.getPrompt()); for (PromptListenerInvoker each : promptListenerInvokers.get(promptCacheKey)) { each.invoke(notifiedEvent); } } @Override public List> subscribeTypes() { List> listenedEventTypes = new LinkedList<>(); listenedEventTypes.add(McpServerChangedEvent.class); listenedEventTypes.add(AgentCardChangedEvent.class); listenedEventTypes.add(SkillChangedEvent.class); listenedEventTypes.add(PromptChangedEvent.class); return listenedEventTypes; } /** * register mcp server listener. * * @param mcpName name of mcp server * @param version version of mcp server * @param listenerInvoker listener invoker */ public void registerListener(String mcpName, String version, McpServerListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String mcpServerKey = CacheKeyUtils.buildMcpServerKey(mcpName, version); mcpServerListenerInvokers.compute(mcpServerKey, (key, mcpServerListenerInvokers) -> { if (null == mcpServerListenerInvokers) { mcpServerListenerInvokers = new ConcurrentHashSet<>(); } mcpServerListenerInvokers.add(listenerInvoker); return mcpServerListenerInvokers; }); } /** * register agent card listener. * * @param agentName name of agent card * @param version version of agent card * @param listenerInvoker listener invoker */ public void registerListener(String agentName, String version, AgentCardListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String agentCardKey = CacheKeyUtils.buildAgentCardKey(agentName, version); agentCardListenerInvokers.compute(agentCardKey, (key, agentCardListenerInvokers) -> { if (null == agentCardListenerInvokers) { agentCardListenerInvokers = new ConcurrentHashSet<>(); } agentCardListenerInvokers.add(listenerInvoker); return agentCardListenerInvokers; }); } /** * register skill listener. * * @param skillName name of skill * @param listenerInvoker listener invoker */ public void registerListener(String skillName, SkillListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String skillKey = CacheKeyUtils.buildSkillKey(skillName); skillListenerInvokers.compute(skillKey, (key, skillListenerInvokers) -> { if (null == skillListenerInvokers) { skillListenerInvokers = new ConcurrentHashSet<>(); } skillListenerInvokers.add(listenerInvoker); return skillListenerInvokers; }); } /** * register prompt listener. * * @param promptKey prompt key * @param listenerInvoker listener invoker */ public void registerListener(String promptKey, String version, String label, PromptListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String key = CacheKeyUtils.buildPromptKey(promptKey, version, label); promptListenerInvokers.compute(key, (k, promptListenerInvokers) -> { if (null == promptListenerInvokers) { promptListenerInvokers = new ConcurrentHashSet<>(); } promptListenerInvokers.add(listenerInvoker); return promptListenerInvokers; }); } /** * deregister mcp server listener. * * @param mcpName name of mcp server * @param version version of mcp server * @param listenerInvoker listener invoker */ public void deregisterListener(String mcpName, String version, McpServerListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String mcpServerKey = CacheKeyUtils.buildMcpServerKey(mcpName, version); mcpServerListenerInvokers.compute(mcpServerKey, (key, mcpServerListenerInvokers) -> { if (null == mcpServerListenerInvokers) { return null; } mcpServerListenerInvokers.remove(listenerInvoker); return mcpServerListenerInvokers.isEmpty() ? null : mcpServerListenerInvokers; }); } /** * deregister agent card listener. * * @param agentName name of agent card * @param version version of agent card * @param listenerInvoker listener invoker */ public void deregisterListener(String agentName, String version, AgentCardListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String agentCardKey = CacheKeyUtils.buildAgentCardKey(agentName, version); agentCardListenerInvokers.compute(agentCardKey, (key, agentCardListenerInvokers) -> { if (null == agentCardListenerInvokers) { return null; } agentCardListenerInvokers.remove(listenerInvoker); return agentCardListenerInvokers.isEmpty() ? null : agentCardListenerInvokers; }); } /** * deregister skill listener. * * @param skillName name of skill * @param listenerInvoker listener invoker */ public void deregisterListener(String skillName, SkillListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String skillKey = CacheKeyUtils.buildSkillKey(skillName); skillListenerInvokers.compute(skillKey, (key, skillListenerInvokers) -> { if (null == skillListenerInvokers) { return null; } skillListenerInvokers.remove(listenerInvoker); return skillListenerInvokers.isEmpty() ? null : skillListenerInvokers; }); } /** * deregister prompt listener. * * @param promptKey prompt key * @param listenerInvoker listener invoker */ public void deregisterListener(String promptKey, String version, String label, PromptListenerInvoker listenerInvoker) { if (listenerInvoker == null) { return; } String key = CacheKeyUtils.buildPromptKey(promptKey, version, label); promptListenerInvokers.compute(key, (k, promptListenerInvokers) -> { if (null == promptListenerInvokers) { return null; } promptListenerInvokers.remove(listenerInvoker); return promptListenerInvokers.isEmpty() ? null : promptListenerInvokers; }); } /** * check mcp server is subscribed. * * @param mcpName name of mcp server * @param version version of mcp server * @return is mcp server subscribed */ public boolean isMcpServerSubscribed(String mcpName, String version) { String mcpServerKey = CacheKeyUtils.buildMcpServerKey(mcpName, version); return isSubscribed(mcpServerKey, mcpServerListenerInvokers); } /** * check agent card is subscribed. * * @param agentName name of agent card * @param version version of agent card * @return is agent card subscribed */ public boolean isAgentCardSubscribed(String agentName, String version) { String agentCardKey = CacheKeyUtils.buildAgentCardKey(agentName, version); return isSubscribed(agentCardKey, agentCardListenerInvokers); } /** * check skill is subscribed. * * @param skillName name of skill * @return is skill subscribed */ public boolean isSkillSubscribed(String skillName) { String skillKey = CacheKeyUtils.buildSkillKey(skillName); return isSubscribed(skillKey, skillListenerInvokers); } /** * check prompt is subscribed. * * @param promptKey prompt key * @return is prompt subscribed */ public boolean isPromptSubscribed(String promptKey, String version, String label) { String key = CacheKeyUtils.buildPromptKey(promptKey, version, label); return isSubscribed(key, promptListenerInvokers); } private > boolean isSubscribed(String key, Map> listenerInvokers) { return CollectionUtils.isNotEmpty(listenerInvokers.get(key)); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/McpServerChangedEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.client.ai.utils.CacheKeyUtils; import com.alibaba.nacos.common.notify.Event; /** * Nacos AI module mcp server changed event in nacos- client. * * @author xiweng.yy */ public class McpServerChangedEvent extends Event { private static final long serialVersionUID = 2010793364377243018L; private final String mcpName; private final String version; private final McpServerDetailInfo mcpServer; public McpServerChangedEvent(McpServerDetailInfo mcpServer) { this.mcpServer = mcpServer; this.mcpName = mcpServer.getName(); this.version = buildVersion(mcpServer); } private String buildVersion(McpServerDetailInfo mcpServer) { return mcpServer.getVersionDetail().getIs_latest() ? CacheKeyUtils.LATEST_VERSION : mcpServer.getVersionDetail().getVersion(); } public String getMcpName() { return mcpName; } public String getVersion() { return version; } public McpServerDetailInfo getMcpServer() { return mcpServer; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/McpServerListenerInvoker.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.AbstractNacosMcpServerListener; import com.alibaba.nacos.api.ai.listener.NacosMcpServerEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Nacos AI module mcp server listener invoker. * * @author xiweng.yy */ public class McpServerListenerInvoker extends AbstractAiListenerInvoker { private static final Logger LOGGER = LoggerFactory.getLogger(McpServerListenerInvoker.class); public McpServerListenerInvoker(AbstractNacosMcpServerListener listener) { super(listener); } protected void logInvoke(NacosMcpServerEvent event) { LOGGER.info("Invoke event namespaceId: {}, mcpId: {}, mcpName: {} to Listener: {}", event.getNamespaceId(), event.getMcpId(), event.getMcpName(), listener.toString()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/PromptChangedEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.common.notify.Event; /** * Prompt changed event for internal notification. * * @author nacos */ public class PromptChangedEvent extends Event { private static final long serialVersionUID = 1L; private final String promptKey; private final String cacheKey; private final Prompt prompt; public PromptChangedEvent(String promptKey, String cacheKey, Prompt prompt) { this.promptKey = promptKey; this.cacheKey = cacheKey; this.prompt = prompt; } public String getPromptKey() { return promptKey; } public Prompt getPrompt() { return prompt; } public String getCacheKey() { return cacheKey; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/PromptListenerInvoker.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.AbstractNacosPromptListener; import com.alibaba.nacos.api.ai.listener.NacosPromptEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Nacos AI module prompt listener invoker. * * @author nacos */ public class PromptListenerInvoker extends AbstractAiListenerInvoker { private static final Logger LOGGER = LoggerFactory.getLogger(PromptListenerInvoker.class); public PromptListenerInvoker(AbstractNacosPromptListener listener) { super(listener); } @Override protected void logInvoke(NacosPromptEvent event) { LOGGER.info("Invoke event promptKey: {} to Listener: {}", event.getPromptKey(), listener.toString()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/SkillChangedEvent.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.common.notify.Event; /** * Nacos AI module skill changed event in nacos-client. * * @author nacos */ public class SkillChangedEvent extends Event { private static final long serialVersionUID = 2010793364377243018L; private final String skillName; private final Skill skill; public SkillChangedEvent(String skillName, Skill skill) { this.skillName = skillName; this.skill = skill; } public String getSkillName() { return skillName; } public Skill getSkill() { return skill; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/event/SkillListenerInvoker.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.AbstractNacosSkillListener; import com.alibaba.nacos.api.ai.listener.NacosSkillEvent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Nacos AI module skill listener invoker. * * @author nacos */ public class SkillListenerInvoker extends AbstractAiListenerInvoker { private static final Logger LOGGER = LoggerFactory.getLogger(SkillListenerInvoker.class); public SkillListenerInvoker(AbstractNacosSkillListener listener) { super(listener); } @Override protected void logInvoke(NacosSkillEvent event) { LOGGER.info("Invoke event skillName: {} to Listener: {}", event.getSkillName(), listener.toString()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/AiClientProxy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.lifecycle.Closeable; /** * AI client proxy interface for abstracting transport layer (gRPC / HTTP). * *

    Defines AI operations that support switching between gRPC and HTTP transport. * Currently covers Prompt operations; extensible for Skill and other capabilities.

    * * @author nacos */ public interface AiClientProxy extends Closeable { /** * Query prompt by latest/version/label with optional md5 for conditional query. * * @param promptKey prompt key * @param version prompt version, optional * @param label prompt label, optional * @param md5 client md5 for conditional query, optional * @return prompt detail * @throws NacosException if request parameter is invalid or handle error */ Prompt queryPrompt(String promptKey, String version, String label, String md5) throws NacosException; } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/AiGrpcClient.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.AbstractAgentRequest; import com.alibaba.nacos.api.ai.remote.request.AbstractMcpRequest; import com.alibaba.nacos.api.ai.remote.request.AbstractPromptRequest; import com.alibaba.nacos.api.ai.remote.request.AgentEndpointRequest; import com.alibaba.nacos.api.ai.remote.request.BatchAgentEndpointRequest; import com.alibaba.nacos.api.ai.remote.request.McpServerEndpointRequest; import com.alibaba.nacos.api.ai.remote.request.QueryPromptRequest; import com.alibaba.nacos.api.ai.remote.request.QueryAgentCardRequest; import com.alibaba.nacos.api.ai.remote.request.QueryMcpServerRequest; import com.alibaba.nacos.api.ai.remote.request.ReleaseAgentCardRequest; import com.alibaba.nacos.api.ai.remote.request.ReleaseMcpServerRequest; import com.alibaba.nacos.api.ai.remote.response.AgentEndpointResponse; import com.alibaba.nacos.api.ai.remote.response.McpServerEndpointResponse; import com.alibaba.nacos.api.ai.remote.response.QueryPromptResponse; import com.alibaba.nacos.api.ai.remote.response.QueryAgentCardResponse; import com.alibaba.nacos.api.ai.remote.response.QueryMcpServerResponse; import com.alibaba.nacos.api.ai.remote.response.ReleaseAgentCardResponse; import com.alibaba.nacos.api.ai.remote.response.ReleaseMcpServerResponse; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.ai.cache.NacosAgentCardCacheHolder; import com.alibaba.nacos.client.ai.cache.NacosMcpServerCacheHolder; import com.alibaba.nacos.client.ai.remote.redo.AgentEndpointWrapper; import com.alibaba.nacos.client.ai.remote.redo.AiGrpcRedoService; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientConfigFactory; import com.alibaba.nacos.common.remote.client.RpcClientFactory; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.plugin.auth.api.RequestResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.client.constant.Constants.Security.SECURITY_INFO_REFRESH_INTERVAL_MILLS; /** * Nacos AI GRPC protocol client. * * @author xiweng.yy */ public class AiGrpcClient implements AiClientProxy { private static final Logger LOGGER = LoggerFactory.getLogger(AiGrpcClient.class); private final String namespaceId; private final String uuid; private final Long requestTimeout; private final RpcClient rpcClient; private final AbstractServerListManager serverListManager; private final AiGrpcRedoService redoService; private final NacosClientProperties properties; private SecurityProxy securityProxy; private NacosMcpServerCacheHolder mcpServerCacheHolder; private NacosAgentCardCacheHolder agentCardCacheHolder; private ScheduledThreadPoolExecutor executorService; public AiGrpcClient(String namespaceId, NacosClientProperties properties) { this.namespaceId = namespaceId; this.uuid = UUID.randomUUID().toString(); this.requestTimeout = Long.parseLong(properties.getProperty(AiConstants.AI_REQUEST_TIMEOUT, "-1")); this.rpcClient = buildRpcClient(properties); this.serverListManager = new NamingServerListManager(properties, namespaceId); this.redoService = new AiGrpcRedoService(properties, this); this.properties = properties; } private RpcClient buildRpcClient(NacosClientProperties properties) { Map labels = new HashMap<>(3); labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK); labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_AI); labels.put(Constants.APPNAME, AppNameUtils.getAppName()); GrpcClientConfig grpcClientConfig = RpcClientConfigFactory.getInstance() .createGrpcClientConfig(properties.asProperties(), labels); return RpcClientFactory.createClient(uuid, ConnectionType.GRPC, grpcClientConfig); } /** * Start the grpc client. * * @throws NacosException nacos exception */ public void start(NacosMcpServerCacheHolder mcpServerCacheHolder, NacosAgentCardCacheHolder agentCardCacheHolder) throws NacosException { this.mcpServerCacheHolder = mcpServerCacheHolder; this.agentCardCacheHolder = agentCardCacheHolder; this.serverListManager.start(); this.rpcClient.registerConnectionListener(this.redoService); this.rpcClient.serverListFactory(this.serverListManager); this.rpcClient.start(); this.securityProxy = new SecurityProxy(this.serverListManager, NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(properties); } private void initSecurityProxy(NacosClientProperties properties) { this.executorService = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.ai.security")); final Properties nacosClientPropertiesView = properties.asProperties(); this.securityProxy.login(nacosClientPropertiesView); this.executorService.scheduleWithFixedDelay(() -> securityProxy.login(nacosClientPropertiesView), 0, SECURITY_INFO_REFRESH_INTERVAL_MILLS, TimeUnit.MILLISECONDS); } /** * Do query mcp server by mcpId and version. * * @param mcpName name of mcp server * @param version version of mcp server, if input empty or null, return the latest version * @return mcp server detail info * @throws NacosException if request parameter is invalid or handle error */ public McpServerDetailInfo queryMcpServer(String mcpName, String version) throws NacosException { if (!isAbilitySupportedByServer(AbilityKey.SERVER_MCP_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support mcp registry feature."); } QueryMcpServerRequest request = new QueryMcpServerRequest(); request.setNamespaceId(namespaceId); request.setMcpName(mcpName); request.setVersion(version); QueryMcpServerResponse response = requestToServer(request, QueryMcpServerResponse.class); return response.getMcpServerDetailInfo(); } /** * Query prompt by latest/version/label. * * @param promptKey prompt key * @param version prompt version, optional * @param label prompt label, optional * @return prompt detail * @throws NacosException if request parameter is invalid or handle error */ public Prompt queryPrompt(String promptKey, String version, String label) throws NacosException { return queryPrompt(promptKey, version, label, null); } /** * Query prompt by latest/version/label with optional md5. * * @param promptKey prompt key * @param version prompt version, optional * @param label prompt label, optional * @param md5 client md5 for conditional query, optional * @return prompt detail * @throws NacosException if request parameter is invalid or handle error */ public Prompt queryPrompt(String promptKey, String version, String label, String md5) throws NacosException { QueryPromptRequest request = new QueryPromptRequest(); request.setNamespaceId(namespaceId); request.setPromptKey(promptKey); request.setVersion(version); request.setLabel(label); request.setMd5(md5); QueryPromptResponse response = requestToServer(request, QueryPromptResponse.class); return response.getPromptInfo(); } /** * Do release mcp server. * * @param serverSpecification mcp server specification * @param toolSpecification mcp server tool specification, optional * @return mcp id * @throws NacosException if request parameter is invalid or handle error */ public String releaseMcpServer(McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { LOGGER.info("[{}] RELEASE Mcp server {}, version {}", uuid, serverSpecification.getName(), serverSpecification.getVersionDetail().getVersion()); if (!isAbilitySupportedByServer(AbilityKey.SERVER_MCP_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support mcp registry feature."); } ReleaseMcpServerRequest request = new ReleaseMcpServerRequest(); request.setNamespaceId(namespaceId); request.setMcpName(serverSpecification.getName()); request.setServerSpecification(serverSpecification); request.setToolSpecification(toolSpecification); request.setEndpointSpecification(endpointSpecification); ReleaseMcpServerResponse response = requestToServer(request, ReleaseMcpServerResponse.class); return response.getMcpId(); } /** * Register endpoint to target mcp server and cached to redo service. * * @param mcpName name of mcp server * @param address address of mcp endpoint * @param port port of mcp endpoint * @param version version of mcp endpoint, if empty, the endpoint will return for all mcp version * @throws NacosException if request parameter is invalid or handle error */ public void registerMcpServerEndpoint(String mcpName, String address, int port, String version) throws NacosException { LOGGER.info("[{}] REGISTER Mcp server endpoint {}:{}, version {} into mcp server {}", uuid, address, port, version, mcpName); if (!isAbilitySupportedByServer(AbilityKey.SERVER_MCP_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support mcp registry feature."); } redoService.cachedMcpServerEndpointForRedo(mcpName, address, port, version); doRegisterMcpServerEndpoint(mcpName, address, port, version); } /** * Actual do Register endpoint to target mcp server. * * @param mcpName name of mcp server * @param address address of mcp endpoint * @param port port of mcp endpoint * @param version version of mcp endpoint, if empty, the endpoint will return for all mcp version * @throws NacosException if request parameter is invalid or handle error */ public void doRegisterMcpServerEndpoint(String mcpName, String address, int port, String version) throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setNamespaceId(namespaceId); request.setMcpName(mcpName); request.setAddress(address); request.setPort(port); request.setVersion(version); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); requestToServer(request, McpServerEndpointResponse.class); redoService.mcpServerEndpointRegistered(mcpName); } /** * Deregister endpoint from target mcp server and cached to redo service. * * @param mcpName name of mcp server * @param address address of mcp endpoint * @param port port of mcp endpoint * @throws NacosException if request parameter is invalid or handle error */ public void deregisterMcpServerEndpoint(String mcpName, String address, int port) throws NacosException { LOGGER.info("[{}] DE-REGISTER Mcp server endpoint {}:{} from mcp server {}", uuid, address, port, mcpName); if (!isAbilitySupportedByServer(AbilityKey.SERVER_MCP_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support mcp registry feature."); } redoService.mcpServerEndpointDeregister(mcpName); doDeregisterMcpServerEndpoint(mcpName, address, port); } /** * Actual do deregister endpoint from target mcp server. * * @param mcpName name of mcp server * @param address address of mcp endpoint * @param port port of mcp endpoint * @throws NacosException if request parameter is invalid or handle error */ public void doDeregisterMcpServerEndpoint(String mcpName, String address, int port) throws NacosException { McpServerEndpointRequest request = new McpServerEndpointRequest(); request.setNamespaceId(namespaceId); request.setMcpName(mcpName); request.setAddress(address); request.setPort(port); request.setType(AiRemoteConstants.DE_REGISTER_ENDPOINT); requestToServer(request, McpServerEndpointResponse.class); redoService.mcpServerEndpointDeregistered(mcpName); } /** * Subscribe mcp server latest version. * * @param mcpName name of mcp server * @param version version of mcp server * @return latest version mcp server * @throws NacosException if request parameter is invalid or handle error */ public McpServerDetailInfo subscribeMcpServer(String mcpName, String version) throws NacosException { if (!isAbilitySupportedByServer(AbilityKey.SERVER_MCP_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support mcp registry feature."); } McpServerDetailInfo cachedServer = mcpServerCacheHolder.getMcpServer(mcpName, version); if (null == cachedServer) { try { cachedServer = queryMcpServer(mcpName, version); mcpServerCacheHolder.processMcpServerDetailInfo(cachedServer); } catch (NacosException e) { if (NacosException.NOT_FOUND != e.getErrCode()) { throw e; } } mcpServerCacheHolder.addMcpServerUpdateTask(mcpName, version); } return cachedServer; } /** * Un-subscribe mcp server. * * @param mcpName name of mcp server * @param version version of mcp server * @throws NacosException if request parameter is invalid or handle error */ public void unsubscribeMcpServer(String mcpName, String version) throws NacosException { if (!isAbilitySupportedByServer(AbilityKey.SERVER_MCP_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support mcp registry feature."); } mcpServerCacheHolder.removeMcpServerUpdateTask(mcpName, version); } /** * Get agent card with nacos extension detail with target version. * * @param agentName name of agent card * @param version target version, if null or empty, get latest version * @param registrationType registration type * @return agent card with nacos extension detail * @throws NacosException if request parameter is invalid or agent card not found or handle error */ public AgentCardDetailInfo getAgentCard(String agentName, String version, String registrationType) throws NacosException { if (!isAbilitySupportedByServer(AbilityKey.SERVER_AGENT_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support agent registry feature."); } QueryAgentCardRequest request = new QueryAgentCardRequest(); request.setNamespaceId(this.namespaceId); request.setAgentName(agentName); request.setVersion(version); request.setRegistrationType(registrationType); QueryAgentCardResponse response = requestToServer(request, QueryAgentCardResponse.class); return response.getAgentCardDetailInfo(); } /** * Release new agent card or new version. * *

    * If current agent card and version exist, This API will do nothing. If current agent card exist but version not * exist, This API will release new version. If current t agent card not exist, This API will release new agent * card. *

    * * @param agentCard agent card need to release * @param registrationType {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_URL} or * {@link AiConstants.A2a#A2A_ENDPOINT_TYPE_SERVICE} * @param setAsLatest whether set new version as latest, default is false. This parameter is only effect when new version is released. * If current agent card not exist, whatever this parameter is, it will be set as latest. * @throws NacosException if request parameter is invalid or handle error */ public void releaseAgentCard(AgentCard agentCard, String registrationType, boolean setAsLatest) throws NacosException { LOGGER.info("[{}] Release Agent Card {}, version {}.", uuid, agentCard.getName(), agentCard.getVersion()); if (!isAbilitySupportedByServer(AbilityKey.SERVER_AGENT_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support agent registry feature."); } ReleaseAgentCardRequest request = new ReleaseAgentCardRequest(); request.setNamespaceId(this.namespaceId); request.setAgentName(agentCard.getName()); request.setRegistrationType(registrationType); request.setAgentCard(agentCard); request.setSetAsLatest(setAsLatest); requestToServer(request, ReleaseAgentCardResponse.class); } /** * Register agent endpoint into agent. * * @param agentName agent name * @param endpoint agent endpoint * @throws NacosException if request parameter is invalid or handle error */ public void registerAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { LOGGER.info("[{}] REGISTER Agent endpoint {} into agent {}", uuid, endpoint.toString(), agentName); if (!isAbilitySupportedByServer(AbilityKey.SERVER_AGENT_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support agent registry feature."); } redoService.cachedAgentEndpointForRedo(agentName, AgentEndpointWrapper.wrap(endpoint)); doRegisterAgentEndpoint(agentName, endpoint); } /** * Batch Register agent endpoint into agent. * * @param agentName agent name * @param endpoints agent endpoints * @throws NacosException if request parameter is invalid or handle error */ public void registerAgentEndpoints(String agentName, Collection endpoints) throws NacosException { LOGGER.info("[{}] BATCH REGISTER Agent endpoint size: {} into agent {}", uuid, endpoints.size(), agentName); if (!isAbilitySupportedByServer(AbilityKey.SERVER_AGENT_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support agent registry feature."); } redoService.cachedAgentEndpointForRedo(agentName, AgentEndpointWrapper.wrap(endpoints)); doRegisterAgentEndpoint(agentName, endpoints); } /** * Actual do register agent endpoint into agent. * * @param agentName agent name * @param endpoint agent endpoint * @throws NacosException if request parameter is invalid or handle error */ public void doRegisterAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); request.setNamespaceId(this.namespaceId); request.setAgentName(agentName); request.setType(AiRemoteConstants.REGISTER_ENDPOINT); request.setEndpoint(endpoint); requestToServer(request, AgentEndpointResponse.class); redoService.agentEndpointRegistered(agentName); } /** * Actual do batch register agent endpoint into agent. * * @param agentName agent name * @param endpoints agent endpoints * @throws NacosException if request parameter is invalid or handle error */ public void doRegisterAgentEndpoint(String agentName, Collection endpoints) throws NacosException { BatchAgentEndpointRequest request = new BatchAgentEndpointRequest(); request.setNamespaceId(this.namespaceId); request.setAgentName(agentName); request.setEndpoints(endpoints); requestToServer(request, AgentEndpointResponse.class); redoService.agentEndpointRegistered(agentName); } /** * Deregister agent endpoint from agent. * * @param agentName agent name * @param endpoint agent endpoint * @throws NacosException if request parameter is invalid or handle error */ public void deregisterAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { LOGGER.info("[{}] DE-REGISTER agent endpoint {} from agent {}", uuid, endpoint.toString(), agentName); if (!isAbilitySupportedByServer(AbilityKey.SERVER_AGENT_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support agent registry feature."); } redoService.agentEndpointDeregister(agentName); doDeregisterAgentEndpoint(agentName, endpoint); } /** * Actual do deregister agent endpoint from agent. * * @param agentName agent name * @param endpoint agent endpoint * @throws NacosException if request parameter is invalid or handle error */ public void doDeregisterAgentEndpoint(String agentName, AgentEndpoint endpoint) throws NacosException { AgentEndpointRequest request = new AgentEndpointRequest(); request.setNamespaceId(this.namespaceId); request.setAgentName(agentName); request.setType(AiRemoteConstants.DE_REGISTER_ENDPOINT); request.setEndpoint(endpoint); requestToServer(request, AgentEndpointResponse.class); redoService.agentEndpointDeregistered(agentName); } /** * Subscribe agent card. * * @param agentName name of agent card * @param version version of agent card * @return current agent card * @throws NacosException if request parameter is invalid or handle error */ public AgentCardDetailInfo subscribeAgentCard(String agentName, String version) throws NacosException { if (!isAbilitySupportedByServer(AbilityKey.SERVER_AGENT_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support agent registry feature."); } AgentCardDetailInfo cachedAgentCard = agentCardCacheHolder.getAgentCard(agentName, version); if (null == cachedAgentCard) { try { cachedAgentCard = getAgentCard(agentName, version, StringUtils.EMPTY); agentCardCacheHolder.processAgentCardDetailInfo(cachedAgentCard); } catch (NacosException e) { if (NacosException.NOT_FOUND != e.getErrCode()) { throw e; } } agentCardCacheHolder.addAgentCardUpdateTask(agentName, version); } return cachedAgentCard; } /** * Un-subscribe agent card. * * @param agentName name of agent card * @param version version of agent card * @throws NacosException if request parameter is invalid or handle error */ public void unsubscribeAgentCard(String agentName, String version) throws NacosException { if (!isAbilitySupportedByServer(AbilityKey.SERVER_AGENT_REGISTRY)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support agent registry feature."); } agentCardCacheHolder.removeAgentCardUpdateTask(agentName, version); } public boolean isEnable() { return rpcClient.isRunning(); } /** * Determine whether nacos-server supports the capability. * * @param abilityKey ability key * @return true if supported, otherwise false */ public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { return rpcClient.getConnectionAbility(abilityKey) == AbilityStatus.SUPPORTED; } private T requestToServer(Request request, Class responseClass) throws NacosException { Response response = null; try { if (request instanceof AbstractMcpRequest) { AbstractMcpRequest mcpRequest = (AbstractMcpRequest) request; request.putAllHeader(getSecurityHeaders(mcpRequest.getNamespaceId(), mcpRequest.getMcpName())); } else if (request instanceof AbstractAgentRequest) { AbstractAgentRequest agentRequest = (AbstractAgentRequest) request; request.putAllHeader(getSecurityHeaders(agentRequest.getNamespaceId(), agentRequest.getAgentName())); } else if (request instanceof AbstractPromptRequest) { AbstractPromptRequest promptRequest = (AbstractPromptRequest) request; request.putAllHeader(getSecurityHeaders(promptRequest.getNamespaceId(), promptRequest.getPromptKey())); } else { throw new NacosException(400, String.format("Unknown AI request type: %s", request.getClass().getSimpleName())); } response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) { // If the 403 login operation is triggered, refresh the accessToken of the client if (NacosException.NO_RIGHT == response.getErrorCode()) { securityProxy.reLogin(); } throw new NacosException(response.getErrorCode(), response.getMessage()); } if (responseClass.isAssignableFrom(response.getClass())) { return (T) response; } throw new NacosException(NacosException.SERVER_ERROR, String.format("Server return invalid response: %s", response.getClass().getSimpleName())); } catch (NacosException e) { LOGGER.warn("AI request {} execute failed, {}", request.getClass().getSimpleName(), e.getMessage()); throw e; } catch (Exception e) { LOGGER.warn("AI request {} execute failed. ", request.getClass().getSimpleName(), e); throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e); } } private Map getSecurityHeaders(String namespace, String mcpName) { RequestResource resource = buildRequestResource(namespace, mcpName); return securityProxy.getIdentityContext(resource); } private RequestResource buildRequestResource(String namespaceId, String mcpName) { RequestResource.Builder builder = RequestResource.aiBuilder(); builder.setNamespace(namespaceId); builder.setGroup(com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP); builder.setResource(null == mcpName ? StringUtils.EMPTY : mcpName); return builder.build(); } @Override public void shutdown() throws NacosException { rpcClient.shutdown(); serverListManager.shutdown(); if (null != securityProxy) { securityProxy.shutdown(); } if (null != executorService) { ThreadUtils.shutdownThreadPool(executorService, LOGGER); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/AiHttpClientProxy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.ContextPathUtil; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.tls.TlsSystemConfig; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.fasterxml.jackson.core.type.TypeReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.HttpURLConnection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.client.constant.Constants.Security.SECURITY_INFO_REFRESH_INTERVAL_MILLS; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * AI HTTP client proxy for AI operations over HTTP transport. * *

    Provides HTTP-based implementation of {@link AiClientProxy}, enabling AI operations * to go through HTTP instead of gRPC. This is useful when a gateway sits between client * and server that cannot handle gRPC traffic.

    * *

    Currently supports Prompt operations; extensible for Skill and other capabilities.

    * * @author nacos */ public class AiHttpClientProxy implements AiClientProxy { private static final Logger LOGGER = LoggerFactory.getLogger(AiHttpClientProxy.class); private static final String PROMPT_CLIENT_PATH = "/v3/client/ai/prompt"; private static final int MAX_RETRY = 3; private static final boolean ENABLE_HTTPS = Boolean.getBoolean(TlsSystemConfig.TLS_ENABLE); private final String namespaceId; private final NacosRestTemplate nacosRestTemplate; private final NamingServerListManager serverListManager; private final SecurityProxy securityProxy; private final ScheduledThreadPoolExecutor executorService; AiHttpClientProxy() { this.namespaceId = null; this.nacosRestTemplate = null; this.serverListManager = null; this.securityProxy = null; this.executorService = null; } public AiHttpClientProxy(String namespaceId, NacosClientProperties properties) throws NacosException { this.namespaceId = namespaceId; this.nacosRestTemplate = NamingHttpClientManager.getInstance().getNacosRestTemplate(); this.serverListManager = new NamingServerListManager(properties, namespaceId); this.serverListManager.start(); this.securityProxy = new SecurityProxy(this.serverListManager, this.nacosRestTemplate); this.executorService = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.ai.http.security")); final Properties nacosClientPropertiesView = properties.asProperties(); this.securityProxy.login(nacosClientPropertiesView); this.executorService.scheduleWithFixedDelay(() -> securityProxy.login(nacosClientPropertiesView), 0, SECURITY_INFO_REFRESH_INTERVAL_MILLS, TimeUnit.MILLISECONDS); } @Override public Prompt queryPrompt(String promptKey, String version, String label, String md5) throws NacosException { Map params = new HashMap<>(8); params.put("namespaceId", namespaceId); params.put("promptKey", promptKey); if (StringUtils.isNotBlank(version)) { params.put("version", version); } if (StringUtils.isNotBlank(label)) { params.put("label", label); } if (StringUtils.isNotBlank(md5)) { params.put("md5", md5); } RequestResource resource = RequestResource.aiBuilder().setNamespace(namespaceId) .setGroup(com.alibaba.nacos.api.common.Constants.DEFAULT_GROUP) .setResource(null == promptKey ? StringUtils.EMPTY : promptKey).build(); String responseBody = reqApi(PROMPT_CLIENT_PATH, params, resource); Result result = JacksonUtils.toObj(responseBody, new TypeReference>() { }); return result.getData(); } // ===== Generic HTTP infrastructure ===== private String reqApi(String api, Map params, RequestResource resource) throws NacosException { List servers = serverListManager.getServerList(); if (servers.isEmpty()) { throw new NacosException(NacosException.INVALID_PARAM, "no server available"); } NacosException exception = new NacosException(); int index = ThreadLocalRandom.current().nextInt(servers.size()); for (int i = 0; i < Math.max(servers.size(), MAX_RETRY); i++) { String server = servers.get(index % servers.size()); try { return callServer(api, params, server, resource); } catch (NacosException e) { exception = e; if (LOGGER.isDebugEnabled()) { LOGGER.debug("Request {} to server {} failed.", api, server, e); } } index = (index + 1) % servers.size(); } LOGGER.error("Request: {} failed, servers: {}, code: {}, msg: {}", api, servers, exception.getErrCode(), exception.getErrMsg()); throw new NacosException(exception.getErrCode(), "Failed to request API: " + api + " after all servers(" + servers + ") tried: " + exception.getMessage()); } private String callServer(String api, Map params, String server, RequestResource resource) throws NacosException { Map securityHeaders = securityProxy.getIdentityContext(resource); Header header = Header.newInstance(); header.addAll(securityHeaders); String url = buildUrl(server, api); try { HttpRestResult restResult = nacosRestTemplate.get(url, header, Query.newInstance().initParams(params), String.class); if (restResult.ok()) { return restResult.getData(); } if (HttpURLConnection.HTTP_NOT_MODIFIED == restResult.getCode()) { throw new NacosException(NacosException.NOT_MODIFIED, "not modified"); } if (HttpURLConnection.HTTP_FORBIDDEN == restResult.getCode()) { securityProxy.reLogin(); } throw new NacosException(restResult.getCode(), restResult.getMessage()); } catch (NacosException e) { throw e; } catch (Exception e) { LOGGER.error("[AI-HTTP] Failed to request {}", url, e); throw new NacosException(NacosException.SERVER_ERROR, e); } } private String buildUrl(String serverAddr, String relativePath) { if (!serverAddr.startsWith(HTTP_PREFIX) && !serverAddr.startsWith(HTTPS_PREFIX)) { serverAddr = (ENABLE_HTTPS ? HTTPS_PREFIX : HTTP_PREFIX) + serverAddr; } String contextPath = serverListManager.getContextPath(); return serverAddr + ContextPathUtil.normalizeContextPath(contextPath) + relativePath; } @Override public void shutdown() throws NacosException { serverListManager.shutdown(); if (securityProxy != null) { securityProxy.shutdown(); } if (executorService != null) { ThreadUtils.shutdownThreadPool(executorService, LOGGER); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/redo/AgentEndpointRedoData.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import com.alibaba.nacos.client.redo.data.RedoData; import java.util.Objects; /** * Nacos AI module mcp server endpoint redo data. * * @author xiweng.yy */ public class AgentEndpointRedoData extends RedoData { private final String agentName; public AgentEndpointRedoData(String agentName, AgentEndpointWrapper agentEndpoint) { this.agentName = agentName; this.set(agentEndpoint); } public String getAgentName() { return agentName; } @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; } AgentEndpointRedoData that = (AgentEndpointRedoData) o; return Objects.equals(agentName, that.agentName) && super.equals(o); } @Override public int hashCode() { return Objects.hash(super.hashCode(), agentName); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/redo/AgentEndpointWrapper.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import java.util.Collection; import java.util.Collections; import java.util.Objects; /** * Wrapper of {@link AgentEndpoint} and batched {@link AgentEndpoint}. * * @author xiweng.yy */ public class AgentEndpointWrapper { private final Collection data; private final boolean isBatch; private AgentEndpointWrapper(Collection data, boolean isBatch) { this.data = data; this.isBatch = isBatch; } public static AgentEndpointWrapper wrap(AgentEndpoint data) { return new AgentEndpointWrapper(Collections.singletonList(data), false); } public static AgentEndpointWrapper wrap(Collection data) { return new AgentEndpointWrapper(data, true); } public boolean isBatch() { return isBatch; } public AgentEndpoint getData() { if (isBatch) { throw new UnsupportedOperationException("Can't get single data from batched data."); } return data.iterator().next(); } public Collection getBatchData() { if (!isBatch) { throw new UnsupportedOperationException("Can't get batched data from single data."); } return data; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AgentEndpointWrapper that = (AgentEndpointWrapper) o; return isBatch == that.isBatch && Objects.equals(data, that.data); } @Override public int hashCode() { return Objects.hash(data, isBatch); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/redo/AiGrpcRedoService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.redo.data.RedoData; import com.alibaba.nacos.client.redo.service.AbstractRedoService; import com.alibaba.nacos.client.redo.service.AbstractRedoTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Set; /** * Nacos AI module redo service. * * @author xiweng.yy */ public class AiGrpcRedoService extends AbstractRedoService { private static final Logger LOGGER = LoggerFactory.getLogger(AiGrpcRedoService.class); private final AiGrpcClient aiGrpcClient; public AiGrpcRedoService(NacosClientProperties properties, AiGrpcClient aiGrpcClient) { super(LOGGER, properties, RemoteConstants.LABEL_MODULE_AI); this.aiGrpcClient = aiGrpcClient; startRedoTask(); } @Override protected AbstractRedoTask buildRedoTask() { return new AiRedoScheduledTask(this, aiGrpcClient); } public void cachedMcpServerEndpointForRedo(String mcpName, String address, int port, String version) { RedoData redoData = buildMcpServerEndpointRedoData(mcpName, address, port, version); super.cachedRedoData(mcpName, redoData, McpServerEndpoint.class); } public void removeMcpServerEndpointForRedo(String mcpName) { super.removeRedoData(mcpName, McpServerEndpoint.class); } public void mcpServerEndpointRegistered(String mcpName) { super.dataRegistered(mcpName, McpServerEndpoint.class); } public void mcpServerEndpointDeregister(String mcpName) { super.dataDeregister(mcpName, McpServerEndpoint.class); } public void mcpServerEndpointDeregistered(String mcpName) { super.dataDeregistered(mcpName, McpServerEndpoint.class); } public boolean isMcpServerEndpointRegistered(String mcpName) { return super.isDataRegistered(mcpName, McpServerEndpoint.class); } public Set> findMcpServerEndpointRedoData() { return super.findRedoData(McpServerEndpoint.class); } public McpServerEndpoint getMcpServerEndpoint(String mcpName) { RedoData redoData = super.getRedoData(mcpName, McpServerEndpoint.class); return redoData == null ? null : redoData.get(); } private RedoData buildMcpServerEndpointRedoData(String mcpName, String address, int port, String version) { McpServerEndpoint mcpServerEndpoint = new McpServerEndpoint(address, port, version); McpServerEndpointRedoData result = new McpServerEndpointRedoData(mcpName); result.set(mcpServerEndpoint); return result; } public void cachedAgentEndpointForRedo(String agentName, AgentEndpointWrapper wrapper) { AgentEndpointRedoData redoData = new AgentEndpointRedoData(agentName, wrapper); super.cachedRedoData(agentName, redoData, AgentEndpointWrapper.class); } public void removeAgentEndpointForRedo(String agentName) { super.removeRedoData(agentName, AgentEndpointWrapper.class); } public void agentEndpointRegistered(String agentName) { super.dataRegistered(agentName, AgentEndpointWrapper.class); } public void agentEndpointDeregister(String agentName) { super.dataDeregister(agentName, AgentEndpointWrapper.class); } public void agentEndpointDeregistered(String agentName) { super.dataDeregistered(agentName, AgentEndpointWrapper.class); } public boolean isAgentEndpointRegistered(String agentName) { return super.isDataRegistered(agentName, AgentEndpointWrapper.class); } public Set> findAgentEndpointRedoData() { return super.findRedoData(AgentEndpointWrapper.class); } public AgentEndpointWrapper getAgentEndpoint(String agentName) { RedoData redoData = super.getRedoData(agentName, AgentEndpointWrapper.class); return redoData == null ? null : redoData.get(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/redo/AiRedoScheduledTask.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.NamingRedoData; import com.alibaba.nacos.client.redo.data.RedoData; import com.alibaba.nacos.client.redo.service.AbstractRedoTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Nacos AI module redo task. * * @author xiweng.yy */ public class AiRedoScheduledTask extends AbstractRedoTask { private static final Logger LOGGER = LoggerFactory.getLogger(AiRedoScheduledTask.class); private final AiGrpcClient aiGrpcClient; public AiRedoScheduledTask(AiGrpcRedoService redoService, AiGrpcClient aiGrpcClient) { super(LOGGER, redoService); this.aiGrpcClient = aiGrpcClient; } @Override protected void redoData() throws NacosException { try { redoForMcpSeverEndpoint(); redoForAgentEndpoint(); } catch (Exception e) { LOGGER.warn("Redo task run with unexpected exception: ", e); } } private void redoForAgentEndpoint() { for (RedoData each : getRedoService().findAgentEndpointRedoData()) { AgentEndpointRedoData redoData = (AgentEndpointRedoData) each; try { redoForAgentEndpoint(redoData); } catch (NacosException e) { LOGGER.error("Redo agent endpoint operation {} for {}} failed. ", each.getRedoType(), redoData.getAgentName(), e); } } } private void redoForAgentEndpoint(AgentEndpointRedoData redoData) throws NacosException { NamingRedoData.RedoType redoType = redoData.getRedoType(); String agentName = redoData.getAgentName(); LOGGER.info("Redo agent endpoint operation {} for {}.", redoType, agentName); AgentEndpointWrapper wrapper = redoData.get(); switch (redoType) { case REGISTER: if (!aiGrpcClient.isEnable()) { return; } if (wrapper.isBatch()) { aiGrpcClient.doRegisterAgentEndpoint(agentName, wrapper.getBatchData()); } else { aiGrpcClient.doRegisterAgentEndpoint(agentName, wrapper.getData()); } break; case UNREGISTER: if (!aiGrpcClient.isEnable()) { return; } AgentEndpoint endpoint = wrapper.isBatch() ? wrapper.getBatchData().stream().findFirst().get() : wrapper.getData(); aiGrpcClient.doDeregisterAgentEndpoint(agentName, endpoint); break; case REMOVE: getRedoService().removeAgentEndpointForRedo(agentName); break; default: } } private void redoForMcpSeverEndpoint() { for (RedoData each : getRedoService().findMcpServerEndpointRedoData()) { McpServerEndpointRedoData redoData = (McpServerEndpointRedoData) each; try { redoForMcpServerEndpoint(redoData); } catch (NacosException e) { LOGGER.error("Redo mcp server endpoint operation {} for {}} failed. ", each.getRedoType(), redoData.getMcpName(), e); } } } private void redoForMcpServerEndpoint(McpServerEndpointRedoData redoData) throws NacosException { NamingRedoData.RedoType redoType = redoData.getRedoType(); String mcpName = redoData.getMcpName(); LOGGER.info("Redo mcp server endpoint operation {} for {}.", redoType, mcpName); McpServerEndpoint endpoint = redoData.get(); switch (redoType) { case REGISTER: if (!aiGrpcClient.isEnable()) { return; } aiGrpcClient.doRegisterMcpServerEndpoint(mcpName, endpoint.getAddress(), endpoint.getPort(), endpoint.getVersion()); break; case UNREGISTER: if (!aiGrpcClient.isEnable()) { return; } aiGrpcClient.doDeregisterMcpServerEndpoint(mcpName, endpoint.getAddress(), endpoint.getPort()); break; case REMOVE: getRedoService().removeMcpServerEndpointForRedo(mcpName); break; default: } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/redo/McpServerEndpoint.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import java.util.Objects; /** * Nacos AI module mcp server endpoint required information for redo data. * * @author xiweng.yy */ public class McpServerEndpoint { private final String address; private final int port; private final String version; public McpServerEndpoint(String address, int port, String version) { this.address = address; this.port = port; this.version = version; } public String getAddress() { return address; } public int getPort() { return port; } public String getVersion() { return version; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } McpServerEndpoint that = (McpServerEndpoint) o; return port == that.port && Objects.equals(address, that.address) && Objects.equals(version, that.version); } @Override public int hashCode() { return Objects.hash(address, port, version); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/remote/redo/McpServerEndpointRedoData.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import com.alibaba.nacos.client.redo.data.RedoData; import java.util.Objects; /** * Nacos AI module mcp server endpoint redo data. * * @author xiweng.yy */ public class McpServerEndpointRedoData extends RedoData { private final String mcpName; public McpServerEndpointRedoData(String mcpName) { this.mcpName = mcpName; } public String getMcpName() { return mcpName; } @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; } McpServerEndpointRedoData that = (McpServerEndpointRedoData) o; return Objects.equals(mcpName, that.mcpName) && super.equals(o); } @Override public int hashCode() { return Objects.hash(super.hashCode(), mcpName); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/ai/utils/CacheKeyUtils.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.utils; import com.alibaba.nacos.common.utils.StringUtils; /** * Nacos AI module cache key utils. * * @author xiweng.yy */ public class CacheKeyUtils { public static final String LATEST_VERSION = "latest"; /** * Build mcp server versioned key. * * @param mcpName name of mcp server * @param version version of mcp server, if version is blank or null, use latest version * @return mcp server versioned key, pattern ${mcpName}::${version} */ public static String buildMcpServerKey(String mcpName, String version) { return buildVersionedKey(mcpName, version); } /** * Build AgentCard versioned key. * * @param agentName name of agent name * @param version version of agent name, if version is blank or null, use latest version * @return mcp server versioned key, pattern ${mcpName}::${version} */ public static String buildAgentCardKey(String agentName, String version) { return buildVersionedKey(agentName, version); } /** * Build skill key. * * @param skillName name of skill * @return skill key, pattern ${skillName} */ public static String buildSkillKey(String skillName) { return skillName; } /** * Build prompt key. * * @param promptKey prompt key * @return prompt key for cache */ public static String buildPromptKey(String promptKey) { return promptKey; } /** * Build prompt query key. * * @param promptKey prompt key * @param version prompt version, optional * @param label prompt label, optional * @return prompt query key, pattern ${promptKey}::label:${label}|version:${version}|latest */ public static String buildPromptKey(String promptKey, String version, String label) { if (StringUtils.isNotBlank(label)) { return promptKey + "::label:" + label; } if (StringUtils.isNotBlank(version)) { return promptKey + "::version:" + version; } return promptKey + "::" + LATEST_VERSION; } private static String buildVersionedKey(String name, String version) { if (StringUtils.isBlank(version)) { version = LATEST_VERSION; } return name + "::" + version; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/NacosConfigService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigQueryResult; import com.alibaba.nacos.api.config.ConfigService; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.filter.impl.ConfigRequest; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.config.impl.ClientWorker; import com.alibaba.nacos.client.config.impl.ConfigFuzzyWatchContext; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; import com.alibaba.nacos.client.config.impl.LocalEncryptedDataKeyProcessor; import com.alibaba.nacos.client.config.utils.ParamUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.client.utils.PreInitUtils; import com.alibaba.nacos.client.utils.ValidatorUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.util.Collections; import java.util.Properties; import java.util.Set; import java.util.concurrent.Future; import static com.alibaba.nacos.api.common.Constants.ALL_PATTERN; /** * Config Impl. * * @author Nacos */ public class NacosConfigService implements ConfigService { private static final Logger LOGGER = LogUtils.logger(NacosConfigService.class); private static final String UP = "UP"; private static final String DOWN = "DOWN"; /** * long polling. */ private final ClientWorker worker; private String namespace; private final ConfigFilterChainManager configFilterChainManager; public NacosConfigService(Properties properties) throws NacosException { PreInitUtils.asyncPreLoadCostComponent(); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); LOGGER.info(ClientBasicParamUtil.getInputParameters(clientProperties.asProperties())); ValidatorUtils.checkInitParam(clientProperties); initNamespace(clientProperties); this.configFilterChainManager = new ConfigFilterChainManager(clientProperties.asProperties()); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); this.worker = new ClientWorker(this.configFilterChainManager, serverListManager, clientProperties); } private void initNamespace(NacosClientProperties properties) { namespace = ClientBasicParamUtil.parseNamespace(properties); properties.setProperty(PropertyKeyConst.NAMESPACE, namespace); } @Override public String getConfig(String dataId, String group, long timeoutMs) throws NacosException { return getConfigInner(namespace, dataId, group, timeoutMs); } @Override public String getConfigAndSignListener(String dataId, String group, long timeoutMs, Listener listener) throws NacosException { group = StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim(); ConfigResponse configResponse = worker.getAgent() .queryConfig(dataId, group, worker.getAgent().getTenant(), timeoutMs, false); String content = configResponse.getContent(); String encryptedDataKey = configResponse.getEncryptedDataKey(); worker.addTenantListenersWithContent(dataId, group, content, encryptedDataKey, Collections.singletonList(listener)); // get a decryptContent, fix https://github.com/alibaba/nacos/issues/7039 ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setGroup(group); cr.setContent(content); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); return cr.getContent(); } @Override public void addListener(String dataId, String group, Listener listener) throws NacosException { worker.addTenantListeners(dataId, group, Collections.singletonList(listener)); } @Override public void fuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { doAddFuzzyWatch(ALL_PATTERN, groupNamePattern, watcher); } @Override public void fuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { doAddFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } @Override public Future> fuzzyWatchWithGroupKeys(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { return doAddFuzzyWatch(ALL_PATTERN, groupNamePattern, watcher); } @Override public Future> fuzzyWatchWithGroupKeys(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { return doAddFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } private Future> doAddFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { ConfigFuzzyWatchContext configFuzzyWatchContext = worker.addTenantFuzzyWatcher(dataIdPattern, groupNamePattern, watcher); return configFuzzyWatchContext.createNewFuture(); } @Override public void cancelFuzzyWatch(String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { cancelFuzzyWatch(ALL_PATTERN, groupNamePattern, watcher); } @Override public void cancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { doCancelFuzzyWatch(dataIdPattern, groupNamePattern, watcher); } private void doCancelFuzzyWatch(String dataIdPattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { if (null == watcher) { return; } worker.removeFuzzyListenListener(dataIdPattern, groupNamePattern, watcher); } @Override public boolean publishConfig(String dataId, String group, String content) throws NacosException { return publishConfig(dataId, group, content, ConfigType.getDefaultType().getType()); } @Override public boolean publishConfig(String dataId, String group, String content, String type) throws NacosException { return publishConfigInner(namespace, dataId, group, null, null, null, content, type, null); } @Override public boolean publishConfigCas(String dataId, String group, String content, String casMd5) throws NacosException { return publishConfigInner(namespace, dataId, group, null, null, null, content, ConfigType.getDefaultType().getType(), casMd5); } @Override public boolean publishConfigCas(String dataId, String group, String content, String casMd5, String type) throws NacosException { return publishConfigInner(namespace, dataId, group, null, null, null, content, type, casMd5); } @Override public boolean removeConfig(String dataId, String group) throws NacosException { return removeConfigInner(namespace, dataId, group, null); } @Override public void removeListener(String dataId, String group, Listener listener) { worker.removeTenantListener(dataId, group, listener); } private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException { group = blank2defaultGroup(group); ParamUtils.checkKeyParam(dataId, group); ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setTenant(tenant); cr.setGroup(group); // We first try to use local failover content if exists. // A config content for failover is not created by client program automatically, // but is maintained by user. // This is designed for certain scenario like client emergency reboot, // changing config needed in the same time, while nacos server is down. String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant); if (content != null) { LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); cr.setContent(content); String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(worker.getAgentName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; } try { ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false); cr.setContent(response.getContent()); cr.setEncryptedDataKey(response.getEncryptedDataKey()); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; } catch (NacosException ioe) { if (NacosException.NO_RIGHT == ioe.getErrCode()) { throw ioe; } LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", worker.getAgentName(), dataId, group, tenant, ioe.toString()); } content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant); if (content != null) { LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); } cr.setContent(content); String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(worker.getAgentName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); content = cr.getContent(); return content; } private String blank2defaultGroup(String group) { return (StringUtils.isBlank(group)) ? Constants.DEFAULT_GROUP : group.trim(); } private ConfigResponse getConfigInnerWithResponse(String tenant, String dataId, String group, long timeoutMs) throws NacosException { group = blank2defaultGroup(group); ParamUtils.checkKeyParam(dataId, group); ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setTenant(tenant); cr.setGroup(group); // Try local failover first String content = LocalConfigInfoProcessor.getFailover(worker.getAgentName(), dataId, group, tenant); if (content != null) { LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); cr.setContent(content); String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(worker.getAgentName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); // Failover doesn't have MD5 from server configFilterChainManager.doFilter(null, cr); return cr; } try { ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs, false); cr.setContent(response.getContent()); cr.setMd5(response.getMd5()); cr.setEncryptedDataKey(response.getEncryptedDataKey()); cr.setConfigType(response.getConfigType()); configFilterChainManager.doFilter(null, cr); return cr; } catch (NacosException ioe) { if (NacosException.NO_RIGHT == ioe.getErrCode()) { throw ioe; } LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}", worker.getAgentName(), dataId, group, tenant, ioe.toString()); } // Fall back to snapshot content = LocalConfigInfoProcessor.getSnapshot(worker.getAgentName(), dataId, group, tenant); if (content != null) { LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}", worker.getAgentName(), dataId, group, tenant); } cr.setContent(content); String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(worker.getAgentName(), dataId, group, tenant); cr.setEncryptedDataKey(encryptedDataKey); // Snapshot doesn't have MD5 from server configFilterChainManager.doFilter(null, cr); return cr; } @Override public ConfigQueryResult getConfigWithResult(String dataId, String group, long timeoutMs) throws NacosException { ConfigResponse response = getConfigInnerWithResponse(namespace, dataId, group, timeoutMs); ConfigQueryResult result = new ConfigQueryResult(); result.setContent(response.getContent()); result.setMd5(response.getMd5()); result.setConfigType(response.getConfigType()); result.setEncryptedDataKey(response.getEncryptedDataKey()); return result; } private boolean removeConfigInner(String tenant, String dataId, String group, String tag) throws NacosException { group = blank2defaultGroup(group); ParamUtils.checkKeyParam(dataId, group); return worker.removeConfig(dataId, group, tenant, tag); } private boolean publishConfigInner(String tenant, String dataId, String group, String tag, String appName, String betaIps, String content, String type, String casMd5) throws NacosException { group = blank2defaultGroup(group); ParamUtils.checkParam(dataId, group, content); ConfigRequest cr = new ConfigRequest(); cr.setDataId(dataId); cr.setTenant(tenant); cr.setGroup(group); cr.setContent(content); cr.setType(type); configFilterChainManager.doFilter(cr, null); content = cr.getContent(); String encryptedDataKey = cr.getEncryptedDataKey(); return worker.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type); } @Override public String getServerStatus() { if (worker.isHealthServer()) { return UP; } else { return DOWN; } } @Override public void addConfigFilter(IConfigFilter configFilter) { configFilterChainManager.addFilter(configFilter); } @Override public void shutDown() throws NacosException { worker.shutdown(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/common/ConfigConstants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.common; /** * config common constants. * * @author Nacos */ public class ConfigConstants { public static final String TENANT = "tenant"; public static final String DATA_ID = "dataId"; public static final String GROUP = "group"; public static final String CONTENT = "content"; public static final String CONFIG_TYPE = "configType"; public static final String ENCRYPTED_DATA_KEY = "encryptedDataKey"; public static final String TYPE = "type"; public static final String MD5 = "md5"; } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/common/GroupKey.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.common; import com.alibaba.nacos.common.utils.StringUtils; /** * Synthesize the form of dataId+groupId. Escapes reserved characters in dataId and groupId. * * @author Nacos */ public class GroupKey { private static final char PLUS = '+'; private static final char PERCENT = '%'; private static final char TWO = '2'; private static final char B = 'B'; private static final char FIVE = '5'; public static String getKey(String dataId, String group) { return getKey(dataId, group, ""); } public static String getKey(String dataId, String group, String datumStr) { return doGetKey(dataId, group, datumStr); } public static String getKeyTenant(String dataId, String group, String tenant) { return doGetKey(dataId, group, tenant); } private static String doGetKey(String dataId, String group, String datumStr) { if (StringUtils.isBlank(dataId)) { throw new IllegalArgumentException("invalid dataId"); } if (StringUtils.isBlank(group)) { throw new IllegalArgumentException("invalid group"); } StringBuilder sb = new StringBuilder(); urlEncode(dataId, sb); sb.append(PLUS); urlEncode(group, sb); if (StringUtils.isNotEmpty(datumStr)) { sb.append(PLUS); urlEncode(datumStr, sb); } return sb.toString(); } /** * Parse key. * * @param groupKey group key * @return parsed key */ public static String[] parseKey(String groupKey) { StringBuilder sb = new StringBuilder(); String dataId = null; String group = null; String tenant = null; for (int i = 0; i < groupKey.length(); ++i) { char c = groupKey.charAt(i); if (PLUS == c) { if (null == dataId) { dataId = sb.toString(); sb.setLength(0); } else if (null == group) { group = sb.toString(); sb.setLength(0); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else if (PERCENT == c) { char next = groupKey.charAt(++i); char nextnext = groupKey.charAt(++i); if (TWO == next && B == nextnext) { sb.append(PLUS); } else if (TWO == next && FIVE == nextnext) { sb.append(PERCENT); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else { sb.append(c); } } if (group == null) { group = sb.toString(); } else { tenant = sb.toString(); } if (StringUtils.isBlank(dataId)) { throw new IllegalArgumentException("invalid dataId"); } if (StringUtils.isBlank(group)) { throw new IllegalArgumentException("invalid group"); } return new String[] {dataId, group, tenant}; } /** * + -> %2B % -> %25. */ static void urlEncode(String str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { char c = str.charAt(idx); if (PLUS == c) { sb.append("%2B"); } else if (PERCENT == c) { sb.append("%25"); } else { sb.append(c); } } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigContext.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigContext; import java.util.HashMap; import java.util.Map; /** * Config Context. * * @author Nacos */ public class ConfigContext implements IConfigContext { private final Map param = new HashMap<>(); @Override public Object getParameter(String key) { return param.get(key); } @Override public void setParameter(String key, Object value) { param.put(key, value); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigEncryptionFilter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.AbstractConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import java.util.Objects; import java.util.Properties; /** * Configure encryption filter. * * @author lixiaoshuang */ public class ConfigEncryptionFilter extends AbstractConfigFilter { private static final String DEFAULT_NAME = ConfigEncryptionFilter.class.getName(); @Override public void init(Properties properties) { } @Override public void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain) throws NacosException { if (Objects.nonNull(request) && request instanceof ConfigRequest && Objects.isNull(response)) { // Publish configuration, encrypt ConfigRequest configRequest = (ConfigRequest) request; String dataId = configRequest.getDataId(); String content = configRequest.getContent(); Pair pair = EncryptionHandler.encryptHandler(dataId, content); String secretKey = pair.getFirst(); String encryptContent = pair.getSecond(); if (!StringUtils.isBlank(encryptContent) && !encryptContent.equals(content)) { ((ConfigRequest) request).setContent(encryptContent); } if (!StringUtils.isBlank(secretKey) && !secretKey.equals(((ConfigRequest) request).getEncryptedDataKey())) { ((ConfigRequest) request).setEncryptedDataKey(secretKey); } else if (StringUtils.isBlank(((ConfigRequest) request).getEncryptedDataKey()) && StringUtils.isBlank(secretKey)) { ((ConfigRequest) request).setEncryptedDataKey(""); } } if (Objects.nonNull(response) && response instanceof ConfigResponse && Objects.isNull(request)) { // Get configuration, decrypt ConfigResponse configResponse = (ConfigResponse) response; String dataId = configResponse.getDataId(); String encryptedDataKey = configResponse.getEncryptedDataKey(); String content = configResponse.getContent(); Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, content); String secretKey = pair.getFirst(); String decryptContent = pair.getSecond(); if (!StringUtils.isBlank(decryptContent) && !decryptContent.equals(content)) { ((ConfigResponse) response).setContent(decryptContent); } if (!StringUtils.isBlank(secretKey) && !secretKey.equals(((ConfigResponse) response).getEncryptedDataKey())) { ((ConfigResponse) response).setEncryptedDataKey(secretKey); } else if (StringUtils.isBlank(((ConfigResponse) response).getEncryptedDataKey()) && StringUtils.isBlank(secretKey)) { ((ConfigResponse) response).setEncryptedDataKey(""); } } filterChain.doFilter(request, response); } @Override public int getOrder() { return 0; } @Override public String getFilterName() { return DEFAULT_NAME; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; import com.alibaba.nacos.api.exception.NacosException; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.ServiceLoader; /** * Config Filter Chain Management. * * @author Nacos */ public class ConfigFilterChainManager implements IConfigFilterChain { private final List filters = new ArrayList<>(); private final Properties initProperty; public ConfigFilterChainManager(Properties properties) { this.initProperty = properties; ServiceLoader configFilters = ServiceLoader.load(IConfigFilter.class); for (IConfigFilter configFilter : configFilters) { addFilter(configFilter); } } /** * Add filter. * * @param filter filter * @return this */ public synchronized ConfigFilterChainManager addFilter(IConfigFilter filter) { // init filter.init(this.initProperty); // ordered by order value int i = 0; while (i < this.filters.size()) { IConfigFilter currentValue = this.filters.get(i); if (currentValue.getFilterName().equals(filter.getFilterName())) { break; } if (filter.getOrder() >= currentValue.getOrder() && i < this.filters.size()) { i++; } else { this.filters.add(i, filter); break; } } if (i == this.filters.size()) { this.filters.add(i, filter); } return this; } @Override public void doFilter(IConfigRequest request, IConfigResponse response) throws NacosException { new VirtualFilterChain(this.filters).doFilter(request, response); } private static class VirtualFilterChain implements IConfigFilterChain { private final List additionalFilters; private int currentPosition = 0; public VirtualFilterChain(List additionalFilters) { this.additionalFilters = additionalFilters; } @Override public void doFilter(final IConfigRequest request, final IConfigResponse response) throws NacosException { if (this.currentPosition != this.additionalFilters.size()) { this.currentPosition++; IConfigFilter nextFilter = this.additionalFilters.get(this.currentPosition - 1); nextFilter.doFilter(request, response, this); } } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigContext; import com.alibaba.nacos.api.config.filter.IConfigRequest; import java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.client.config.common.ConfigConstants.CONTENT; import static com.alibaba.nacos.client.config.common.ConfigConstants.DATA_ID; import static com.alibaba.nacos.client.config.common.ConfigConstants.ENCRYPTED_DATA_KEY; import static com.alibaba.nacos.client.config.common.ConfigConstants.GROUP; import static com.alibaba.nacos.client.config.common.ConfigConstants.TENANT; import static com.alibaba.nacos.client.config.common.ConfigConstants.TYPE; /** * Config Request. * * @author Nacos */ public class ConfigRequest implements IConfigRequest { private final Map param = new HashMap<>(); private final IConfigContext configContext = new ConfigContext(); public String getTenant() { return (String) param.get(TENANT); } public void setTenant(String tenant) { param.put(TENANT, tenant); } public String getDataId() { return (String) param.get(DATA_ID); } public void setDataId(String dataId) { param.put(DATA_ID, dataId); } public String getGroup() { return (String) param.get(GROUP); } public void setGroup(String group) { param.put(GROUP, group); } public String getContent() { return (String) param.get(CONTENT); } public void setContent(String content) { param.put(CONTENT, content); } public String getType() { return (String) param.get(TYPE); } public void setType(String type) { param.put(TYPE, type); } public String getEncryptedDataKey() { return (String) param.get(ENCRYPTED_DATA_KEY); } public void setEncryptedDataKey(String encryptedDataKey) { param.put(ENCRYPTED_DATA_KEY, encryptedDataKey); } @Override public Object getParameter(String key) { return param.get(key); } @Override public void putParameter(String key, Object value) { param.put(key, value); } @Override public IConfigContext getConfigContext() { return configContext; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/filter/impl/ConfigResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigContext; import com.alibaba.nacos.api.config.filter.IConfigResponse; import java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.client.config.common.ConfigConstants.CONFIG_TYPE; import static com.alibaba.nacos.client.config.common.ConfigConstants.CONTENT; import static com.alibaba.nacos.client.config.common.ConfigConstants.DATA_ID; import static com.alibaba.nacos.client.config.common.ConfigConstants.ENCRYPTED_DATA_KEY; import static com.alibaba.nacos.client.config.common.ConfigConstants.GROUP; import static com.alibaba.nacos.client.config.common.ConfigConstants.MD5; import static com.alibaba.nacos.client.config.common.ConfigConstants.TENANT; /** * Config Response. * * @author Nacos */ public class ConfigResponse implements IConfigResponse { private final Map param = new HashMap<>(); private final IConfigContext configContext = new ConfigContext(); public String getTenant() { return (String) param.get(TENANT); } public void setTenant(String tenant) { param.put(TENANT, tenant); } public String getDataId() { return (String) param.get(DATA_ID); } public void setDataId(String dataId) { param.put(DATA_ID, dataId); } public String getGroup() { return (String) param.get(GROUP); } public void setGroup(String group) { param.put(GROUP, group); } public String getContent() { return (String) param.get(CONTENT); } public void setContent(String content) { param.put(CONTENT, content); } public String getConfigType() { return (String) param.get(CONFIG_TYPE); } public void setConfigType(String configType) { param.put(CONFIG_TYPE, configType); } public String getEncryptedDataKey() { return (String) param.get(ENCRYPTED_DATA_KEY); } public void setEncryptedDataKey(String encryptedDataKey) { param.put(ENCRYPTED_DATA_KEY, encryptedDataKey); } /** * Get MD5 hash of the config content. * * @return MD5 hash string * @since 3.0 */ public String getMd5() { return (String) param.get(MD5); } /** * Set MD5 hash of the config content. * * @param md5 MD5 hash string * @since 3.0 */ public void setMd5(String md5) { param.put(MD5, md5); } @Override public Object getParameter(String key) { return param.get(key); } @Override public void putParameter(String key, Object value) { param.put(key, value); } @Override public IConfigContext getConfigContext() { return configContext; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/http/HttpAgent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.http; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.lifecycle.Closeable; import java.util.Map; /** * HttpAgent. * * @author Nacos */ public interface HttpAgent extends Closeable { /** * start to get nacos ip list. * * @throws NacosException on get ip list error. */ void start() throws NacosException; /** * invoke http get method. * * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws Exception If an input or output exception occurred */ HttpRestResult httpGet(String path, Map headers, Map paramValues, String encoding, long readTimeoutMs) throws Exception; /** * invoke http post method. * * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws Exception If an input or output exception occurred */ HttpRestResult httpPost(String path, Map headers, Map paramValues, String encoding, long readTimeoutMs) throws Exception; /** * invoke http delete method. * * @param path http path * @param headers http headers * @param paramValues http paramValues http * @param encoding http encode * @param readTimeoutMs http timeout * @return HttpResult http response * @throws Exception If an input or output exception occurred */ HttpRestResult httpDelete(String path, Map headers, Map paramValues, String encoding, long readTimeoutMs) throws Exception; /** * get name. * * @return String */ String getName(); /** * get namespace. * * @return String */ String getNamespace(); /** * get tenant. * * @return String */ String getTenant(); /** * get encode. * * @return String */ String getEncode(); } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/http/MetricsHttpAgent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.http; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.common.http.HttpRestResult; import io.prometheus.client.Histogram; import java.util.Date; import java.util.Map; /** * MetricsHttpAgent. * * @author Nacos */ public class MetricsHttpAgent implements HttpAgent { private static final String GET = "GET"; private static final String POST = "POST"; private static final String DELETE = "DELETE"; private static final String DEFAULT_CODE = "NA"; private final HttpAgent httpAgent; public MetricsHttpAgent(HttpAgent httpAgent) { this.httpAgent = httpAgent; } @Override public void start() throws NacosException { httpAgent.start(); } @Override public HttpRestResult httpGet(String path, Map headers, Map paramValues, String encode, long readTimeoutMs) throws Exception { Date start = new Date(); Histogram.Child histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, DEFAULT_CODE); HttpRestResult result; try { result = httpAgent.httpGet(path, headers, paramValues, encode, readTimeoutMs); histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, String.valueOf(result.getCode())); } finally { histogram.observe(System.currentTimeMillis() - start.getTime()); } return result; } @Override public HttpRestResult httpPost(String path, Map headers, Map paramValues, String encode, long readTimeoutMs) throws Exception { Date start = new Date(); Histogram.Child histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, DEFAULT_CODE); HttpRestResult result; try { result = httpAgent.httpPost(path, headers, paramValues, encode, readTimeoutMs); histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, String.valueOf(result.getCode())); } finally { histogram.observe(System.currentTimeMillis() - start.getTime()); } return result; } @Override public HttpRestResult httpDelete(String path, Map headers, Map paramValues, String encode, long readTimeoutMs) throws Exception { Date start = new Date(); Histogram.Child histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, DEFAULT_CODE); HttpRestResult result; try { result = httpAgent.httpDelete(path, headers, paramValues, encode, readTimeoutMs); histogram = MetricsMonitor.getConfigRequestMonitor(GET, path, String.valueOf(result.getCode())); } finally { histogram.observe(System.currentTimeMillis() - start.getTime()); } return result; } @Override public String getName() { return httpAgent.getName(); } @Override public String getNamespace() { return httpAgent.getNamespace(); } @Override public String getTenant() { return httpAgent.getTenant(); } @Override public String getEncode() { return httpAgent.getEncode(); } @Override public void shutdown() throws NacosException { httpAgent.shutdown(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/http/ServerHttpAgent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.http; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.impl.ConfigHttpClientManager; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.client.utils.ContextPathUtil; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.ExceptionUtil; import org.slf4j.Logger; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.util.Map; import java.util.Properties; /** * Server Agent. * * @author water.lyl */ public class ServerHttpAgent implements HttpAgent { private static final Logger LOGGER = LogUtils.logger(ServerHttpAgent.class); private final NacosRestTemplate nacosRestTemplate = ConfigHttpClientManager.getInstance().getNacosRestTemplate(); private String encode; private int maxRetry = 3; final ConfigServerListManager serverListMgr; @Override public HttpRestResult httpGet(String path, Map headers, Map paramValues, String encode, long readTimeoutMs) throws Exception { final long endTime = System.currentTimeMillis() + readTimeoutMs; String currentServerAddr = serverListMgr.getCurrentServer(); int maxRetry = this.maxRetry; HttpClientConfig httpConfig = HttpClientConfig.builder() .setReadTimeOutMillis(Long.valueOf(readTimeoutMs).intValue()) .setConTimeOutMillis(ConfigHttpClientManager.getInstance().getConnectTimeoutOrDefault(100)).build(); do { try { Header newHeaders = Header.newInstance(); if (headers != null) { newHeaders.addAll(headers); } Query query = Query.newInstance().initParams(paramValues); HttpRestResult result = nacosRestTemplate.get(getUrl(currentServerAddr, path), httpConfig, newHeaders, query, String.class); if (isFail(result)) { LOGGER.error("[NACOS ConnectException] currentServerAddr: {}, httpCode: {}", serverListMgr.getCurrentServer(), result.getCode()); } else { // Update the currently available server addr serverListMgr.updateCurrentServerAddr(currentServerAddr); return result; } } catch (ConnectException connectException) { LOGGER.error("[NACOS ConnectException httpGet] currentServerAddr:{}, err : {}", serverListMgr.getCurrentServer(), connectException.getMessage()); } catch (SocketTimeoutException socketTimeoutException) { LOGGER.error("[NACOS SocketTimeoutException httpGet] currentServerAddr:{}, err : {}", serverListMgr.getCurrentServer(), socketTimeoutException.getMessage()); } catch (Exception ex) { LOGGER.error("[NACOS Exception httpGet] currentServerAddr: " + serverListMgr.getCurrentServer(), ex); throw ex; } if (serverListMgr.getIterator().hasNext()) { currentServerAddr = serverListMgr.getIterator().next(); } else { maxRetry--; if (maxRetry < 0) { throw new ConnectException( "[NACOS HTTP-GET] The maximum number of tolerable server reconnection errors has been reached"); } serverListMgr.refreshCurrentServerAddr(); } } while (System.currentTimeMillis() <= endTime); LOGGER.error("no available server"); throw new ConnectException("no available server"); } @Override public HttpRestResult httpPost(String path, Map headers, Map paramValues, String encode, long readTimeoutMs) throws Exception { final long endTime = System.currentTimeMillis() + readTimeoutMs; String currentServerAddr = serverListMgr.getCurrentServer(); int maxRetry = this.maxRetry; HttpClientConfig httpConfig = HttpClientConfig.builder() .setReadTimeOutMillis(Long.valueOf(readTimeoutMs).intValue()) .setConTimeOutMillis(ConfigHttpClientManager.getInstance().getConnectTimeoutOrDefault(3000)).build(); do { try { Header newHeaders = Header.newInstance(); if (headers != null) { newHeaders.addAll(headers); } HttpRestResult result = nacosRestTemplate.postForm(getUrl(currentServerAddr, path), httpConfig, newHeaders, paramValues, String.class); if (isFail(result)) { LOGGER.error("[NACOS ConnectException] currentServerAddr: {}, httpCode: {}", currentServerAddr, result.getCode()); } else { // Update the currently available server addr serverListMgr.updateCurrentServerAddr(currentServerAddr); return result; } } catch (ConnectException connectException) { LOGGER.error("[NACOS ConnectException httpPost] currentServerAddr: {}, err : {}", currentServerAddr, connectException.getMessage()); } catch (SocketTimeoutException socketTimeoutException) { LOGGER.error("[NACOS SocketTimeoutException httpPost] currentServerAddr: {}, err : {}", currentServerAddr, socketTimeoutException.getMessage()); } catch (Exception ex) { LOGGER.error("[NACOS Exception httpPost] currentServerAddr: " + currentServerAddr, ex); throw ex; } if (serverListMgr.getIterator().hasNext()) { currentServerAddr = serverListMgr.getIterator().next(); } else { maxRetry--; if (maxRetry < 0) { throw new ConnectException( "[NACOS HTTP-POST] The maximum number of tolerable server reconnection errors has been reached"); } serverListMgr.refreshCurrentServerAddr(); } } while (System.currentTimeMillis() <= endTime); LOGGER.error("no available server, currentServerAddr : {}", currentServerAddr); throw new ConnectException("no available server, currentServerAddr : " + currentServerAddr); } @Override public HttpRestResult httpDelete(String path, Map headers, Map paramValues, String encode, long readTimeoutMs) throws Exception { final long endTime = System.currentTimeMillis() + readTimeoutMs; String currentServerAddr = serverListMgr.getCurrentServer(); int maxRetry = this.maxRetry; HttpClientConfig httpConfig = HttpClientConfig.builder() .setReadTimeOutMillis(Long.valueOf(readTimeoutMs).intValue()) .setConTimeOutMillis(ConfigHttpClientManager.getInstance().getConnectTimeoutOrDefault(100)).build(); do { try { Header newHeaders = Header.newInstance(); if (headers != null) { newHeaders.addAll(headers); } Query query = Query.newInstance().initParams(paramValues); HttpRestResult result = nacosRestTemplate.delete(getUrl(currentServerAddr, path), httpConfig, newHeaders, query, String.class); if (isFail(result)) { LOGGER.error("[NACOS ConnectException] currentServerAddr: {}, httpCode: {}", serverListMgr.getCurrentServer(), result.getCode()); } else { // Update the currently available server addr serverListMgr.updateCurrentServerAddr(currentServerAddr); return result; } } catch (ConnectException connectException) { LOGGER.error("[NACOS ConnectException httpDelete] currentServerAddr:{}, err : {}", serverListMgr.getCurrentServer(), ExceptionUtil.getStackTrace(connectException)); } catch (SocketTimeoutException stoe) { LOGGER.error("[NACOS SocketTimeoutException httpDelete] currentServerAddr:{}, err : {}", serverListMgr.getCurrentServer(), ExceptionUtil.getStackTrace(stoe)); } catch (Exception ex) { LOGGER.error("[NACOS Exception httpDelete] currentServerAddr: " + serverListMgr.getCurrentServer(), ex); throw ex; } if (serverListMgr.getIterator().hasNext()) { currentServerAddr = serverListMgr.getIterator().next(); } else { maxRetry--; if (maxRetry < 0) { throw new ConnectException( "[NACOS HTTP-DELETE] The maximum number of tolerable server reconnection errors has been reached"); } serverListMgr.refreshCurrentServerAddr(); } } while (System.currentTimeMillis() <= endTime); LOGGER.error("no available server"); throw new ConnectException("no available server"); } private String getUrl(String serverAddr, String relativePath) { String contextPath = serverListMgr.getContextPath(); return serverAddr + ContextPathUtil.normalizeContextPath(contextPath) + relativePath; } private boolean isFail(HttpRestResult result) { return result.getCode() == HttpURLConnection.HTTP_INTERNAL_ERROR || result.getCode() == HttpURLConnection.HTTP_BAD_GATEWAY || result.getCode() == HttpURLConnection.HTTP_UNAVAILABLE || result.getCode() == HttpURLConnection.HTTP_NOT_FOUND; } public static String getAppname() { return AppNameUtils.getAppName(); } public ServerHttpAgent(ConfigServerListManager mgr) { this.serverListMgr = mgr; } public ServerHttpAgent(ConfigServerListManager mgr, Properties properties) { this.serverListMgr = mgr; } public ServerHttpAgent(Properties properties) throws NacosException { this.serverListMgr = new ConfigServerListManager(NacosClientProperties.PROTOTYPE.derive(properties)); } @Override public void start() throws NacosException { serverListMgr.start(); } @Override public String getName() { return serverListMgr.getName(); } @Override public String getNamespace() { return serverListMgr.getNamespace(); } @Override public String getTenant() { return serverListMgr.getTenant(); } @Override public String getEncode() { return encode; } @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); LOGGER.info("{} do shutdown begin", className); ConfigHttpClientManager.getInstance().shutdown(); serverListMgr.shutdown(); LOGGER.info("{} do shutdown stop", className); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/AbstractConfigChangeParser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeItem; import com.alibaba.nacos.api.config.PropertyChangeType; import com.alibaba.nacos.api.config.listener.ConfigChangeParser; import java.util.HashMap; import java.util.Map; /** * AbstractConfigChangeParser. * * @author rushsky518 */ public abstract class AbstractConfigChangeParser implements ConfigChangeParser { private final String configType; public AbstractConfigChangeParser(String configType) { this.configType = configType; } @Override public boolean isResponsibleFor(String type) { return this.configType.equalsIgnoreCase(type); } protected Map filterChangeData(Map oldMap, Map newMap) { Map result = new HashMap<>(16); for (Map.Entry e : (Iterable>) oldMap.entrySet()) { ConfigChangeItem cci; if (newMap.containsKey(e.getKey())) { if (e.getValue().equals(newMap.get(e.getKey()))) { continue; } cci = new ConfigChangeItem(e.getKey(), e.getValue().toString(), newMap.get(e.getKey()).toString()); cci.setType(PropertyChangeType.MODIFIED); } else { cci = new ConfigChangeItem(e.getKey(), e.getValue().toString(), null); cci.setType(PropertyChangeType.DELETED); } result.put(e.getKey(), cci); } for (Map.Entry e : (Iterable>) newMap.entrySet()) { if (!oldMap.containsKey(e.getKey())) { ConfigChangeItem cci = new ConfigChangeItem(e.getKey(), null, e.getValue().toString()); cci.setType(PropertyChangeType.ADDED); result.put(e.getKey(), cci); } } return result; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/CacheData.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigChangeEvent; import com.alibaba.nacos.api.config.ConfigChangeItem; import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.client.utils.TenantUtil; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.NumberUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import org.slf4j.Logger; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; /** * Listener Management. * * @author Nacos */ public class CacheData { private static final Logger LOGGER = LogUtils.logger(CacheData.class); private static final long DEFAULT_NOTIF_WARN_TIMEOUTS = 60000; private static long notifyWarnTimeout = DEFAULT_NOTIF_WARN_TIMEOUTS; static { initNotifyWarnTimeout(); } static long initNotifyWarnTimeout() { String notifyTimeouts = System.getProperty("nacos.listener.notify.warn.timeout"); if (StringUtils.isNotBlank(notifyTimeouts) && NumberUtils.isDigits(notifyTimeouts)) { notifyWarnTimeout = Long.parseLong(notifyTimeouts); LOGGER.info("config listener notify warn timeout millis is set to {}", notifyWarnTimeout); } else { LOGGER.info("config listener notify warn timeout millis use default {} millis ", DEFAULT_NOTIF_WARN_TIMEOUTS); notifyWarnTimeout = DEFAULT_NOTIF_WARN_TIMEOUTS; } return notifyWarnTimeout; } /** * double check lock initialization of scheduledExecutor. */ static volatile ScheduledThreadPoolExecutor scheduledExecutor; static ScheduledThreadPoolExecutor getNotifyBlockMonitor() { if (scheduledExecutor == null) { synchronized (CacheData.class) { if (scheduledExecutor == null) { scheduledExecutor = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.notify.block.monitor"), new ThreadPoolExecutor.DiscardPolicy()); scheduledExecutor.setRemoveOnCancelPolicy(true); // it will shut down when jvm exit. ThreadUtils.addShutdownHook(CacheData::shutdownScheduledExecutor); } } } return scheduledExecutor; } /** * shutdownScheduledExecutor. */ public static void shutdownScheduledExecutor() { if (scheduledExecutor != null && !scheduledExecutor.isShutdown()) { try { scheduledExecutor.shutdown(); // help gc scheduledExecutor = null; } catch (Exception e) { // ignore } } } static boolean initSnapshot; static { initSnapshot = NacosClientProperties.PROTOTYPE.getBoolean("nacos.cache.data.init.snapshot", true); LOGGER.info("nacos.cache.data.init.snapshot = {} ", initSnapshot); } public final String envName; private final ConfigFilterChainManager configFilterChainManager; public final String dataId; public final String group; public final String tenant; private final CopyOnWriteArrayList listeners; private volatile String md5; /** * whether use local config. */ private volatile boolean isUseLocalConfig = false; /** * last modify time. */ private volatile long localConfigLastModified; private volatile String content; private volatile String encryptedDataKey; /** * local cache change timestamp. */ private final AtomicLong lastModifiedTs = new AtomicLong(0); /** * notify change flag,for notify&sync concurrent control. 1.reset to false if starting to sync with server. 2.update * to true if receive config change notification. */ private final AtomicBoolean receiveNotifyChanged = new AtomicBoolean(false); private int taskId; private volatile boolean isInitializing = true; /** * if is cache data md5 sync with the server. */ private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); /** * if is cache data is discard,need to remove. */ private volatile boolean isDiscard = false; private String type; public boolean isInitializing() { return isInitializing; } public void setInitializing(boolean isInitializing) { this.isInitializing = isInitializing; } public String getMd5() { return md5; } public String getTenant() { return tenant; } public String getContent() { return content; } public void setContent(String content) { this.content = content; this.md5 = getMd5String(this.content); } public AtomicBoolean getReceiveNotifyChanged() { return receiveNotifyChanged; } /** * Getter method for property lastModifiedTs. * * @return property value of lastModifiedTs */ public AtomicLong getLastModifiedTs() { return lastModifiedTs; } /** * Setter method for property lastModifiedTs. * * @param lastModifiedTs value to be assigned to property lastModifiedTs */ public void setLastModifiedTs(long lastModifiedTs) { this.lastModifiedTs.set(lastModifiedTs); } public String getType() { return type; } public void setType(String type) { this.type = type; } /** * Add listener if CacheData already set new content, Listener should init lastCallMd5 by CacheData.md5 * * @param listener listener */ public void addListener(Listener listener) throws NacosException { if (null == listener) { throw new IllegalArgumentException("listener is null"); } ManagerListenerWrap wrap; if (listener instanceof AbstractConfigChangeListener) { ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setGroup(group); cr.setContent(content); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); String contentTmp = cr.getContent(); wrap = new ManagerListenerWrap(listener, md5, contentTmp); } else { wrap = new ManagerListenerWrap(listener, md5); } if (listeners.addIfAbsent(wrap)) { LOGGER.info("[{}] [add-listener] ok, tenant={}, dataId={}, group={}, cnt={}", envName, tenant, dataId, group, listeners.size()); } } /** * Remove listener. * * @param listener listener */ public void removeListener(Listener listener) { if (null == listener) { throw new IllegalArgumentException("listener is null"); } ManagerListenerWrap wrap = new ManagerListenerWrap(listener); if (listeners.remove(wrap)) { LOGGER.info("[{}] [remove-listener] ok, dataId={}, group={},tenant={}, cnt={}", envName, dataId, group, tenant, listeners.size()); } } /** * Returns the iterator on the listener list, read-only. It is guaranteed not to return NULL. */ public List getListeners() { List result = new ArrayList<>(); for (ManagerListenerWrap wrap : listeners) { result.add(wrap.listener); } return result; } public long getLocalConfigInfoVersion() { return localConfigLastModified; } public void setLocalConfigInfoVersion(long localConfigLastModified) { this.localConfigLastModified = localConfigLastModified; } public boolean isUseLocalConfigInfo() { return isUseLocalConfig; } public void setUseLocalConfigInfo(boolean useLocalConfigInfo) { this.isUseLocalConfig = useLocalConfigInfo; if (!useLocalConfigInfo) { localConfigLastModified = -1; } } public int getTaskId() { return taskId; } public void setTaskId(int taskId) { this.taskId = taskId; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((dataId == null) ? 0 : dataId.hashCode()); result = prime * result + ((group == null) ? 0 : group.hashCode()); return result; } @Override public boolean equals(Object obj) { if (null == obj || obj.getClass() != getClass()) { return false; } if (this == obj) { return true; } CacheData other = (CacheData) obj; return dataId.equals(other.dataId) && group.equals(other.group); } @Override public String toString() { return "CacheData [" + dataId + ", " + group + "]"; } void checkListenerMd5() { for (ManagerListenerWrap wrap : listeners) { if (!md5.equals(wrap.lastCallMd5)) { safeNotifyListener(dataId, group, content, type, md5, encryptedDataKey, wrap); } } } /** * check if all listeners md5 is equal with cache data. */ public boolean checkListenersMd5Consistent() { for (ManagerListenerWrap wrap : listeners) { if (!md5.equals(wrap.lastCallMd5)) { return false; } } return true; } class LongNotifyHandler implements Runnable { public LongNotifyHandler(String listenerClass, String dataId, String group, String tenant, String md5, long timeoutMills, Thread thread) { this.listenerClass = listenerClass; this.dataId = dataId; this.group = group; this.tenant = tenant; this.md5 = md5; this.timeoutMills = timeoutMills; this.thread = thread; } String listenerClass; long startTime = System.currentTimeMillis(); long timeoutMills; String dataId; String group; String tenant; String md5; Thread thread; @Override public void run() { String blockTrace = getTrace(thread.getStackTrace(), 5); LOGGER.warn("[{}] [notify-block-monitor] dataId={}, group={},tenant={}, md5={}, " + "receiveConfigInfo execute over {} mills,thread trace block : {}", envName, dataId, group, tenant, md5, timeoutMills, blockTrace); NotifyCenter.publishEvent( new ChangeNotifyBlockEvent(this.listenerClass, dataId, group, tenant, this.startTime, System.currentTimeMillis(), blockTrace)); } } private static String getTrace(StackTraceElement[] stackTrace, int traceDeep) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("\n"); int deep = 0; for (StackTraceElement element : stackTrace) { stringBuilder.append("\tat " + element + "\n"); deep++; if (traceDeep > 0 && deep > traceDeep) { stringBuilder.append("\tat ... \n"); break; } } return stringBuilder.toString(); } private void safeNotifyListener(final String dataId, final String group, final String content, final String type, final String md5, final String encryptedDataKey, final ManagerListenerWrap listenerWrap) { final Listener listener = listenerWrap.listener; if (listenerWrap.inNotifying) { LOGGER.warn( "[{}] [notify-currentSkip] dataId={}, group={},tenant={}, md5={}, listener={}, listener is not finish yet,will try next time.", envName, dataId, group, tenant, md5, listener); return; } NotifyTask job = new NotifyTask() { @Override public void run() { long start = System.currentTimeMillis(); ClassLoader myClassLoader = Thread.currentThread().getContextClassLoader(); ClassLoader appClassLoader = listener.getClass().getClassLoader(); ScheduledFuture timeSchedule = null; try { if (listener instanceof AbstractSharedListener) { AbstractSharedListener adapter = (AbstractSharedListener) listener; adapter.fillContext(dataId, group); LOGGER.info("[{}] [notify-context] dataId={}, group={},tenant={}, md5={}", envName, dataId, group, tenant, md5); } // Before executing the callback, set the thread classloader to the classloader of // the specific webapp to avoid exceptions or misuses when calling the spi interface in // the callback method (this problem occurs only in multi-application deployment). Thread.currentThread().setContextClassLoader(appClassLoader); ConfigResponse cr = new ConfigResponse(); cr.setDataId(dataId); cr.setGroup(group); cr.setContent(content); cr.setEncryptedDataKey(encryptedDataKey); configFilterChainManager.doFilter(null, cr); String contentTmp = cr.getContent(); timeSchedule = getNotifyBlockMonitor().schedule( new LongNotifyHandler(listener.getClass().getSimpleName(), dataId, group, tenant, md5, notifyWarnTimeout, Thread.currentThread()), notifyWarnTimeout, TimeUnit.MILLISECONDS); listenerWrap.inNotifying = true; listener.receiveConfigInfo(contentTmp); // compare lastContent and content if (listener instanceof AbstractConfigChangeListener) { Map data = ConfigChangeHandler.getInstance() .parseChangeData(listenerWrap.lastContent, contentTmp, type); ConfigChangeEvent event = new ConfigChangeEvent(data); ((AbstractConfigChangeListener) listener).receiveConfigChange(event); listenerWrap.lastContent = contentTmp; } listenerWrap.lastCallMd5 = md5; LOGGER.info( "[{}] [notify-ok] dataId={}, group={},tenant={}, md5={}, listener={} ,job run cost={} millis.", envName, dataId, group, tenant, md5, listener, (System.currentTimeMillis() - start)); } catch (NacosException ex) { LOGGER.error( "[{}] [notify-error] dataId={}, group={},tenant={},md5={}, listener={} errCode={} errMsg={},stackTrace :{}", envName, dataId, group, tenant, md5, listener, ex.getErrCode(), ex.getErrMsg(), getTrace(ex.getStackTrace(), 3)); } catch (Throwable t) { LOGGER.error("[{}] [notify-error] dataId={}, group={},tenant={}, md5={}, listener={} tx={}", envName, dataId, group, tenant, md5, listener, getTrace(t.getStackTrace(), 3)); } finally { listenerWrap.inNotifying = false; Thread.currentThread().setContextClassLoader(myClassLoader); if (timeSchedule != null) { timeSchedule.cancel(true); } } } }; try { if (null != listener.getExecutor()) { LOGGER.info( "[{}] [notify-listener] task submitted to user executor, dataId={}, group={},tenant={}, md5={}, listener={} ", envName, dataId, group, tenant, md5, listener); job.async = true; listener.getExecutor().execute(job); } else { LOGGER.info( "[{}] [notify-listener] task execute in nacos thread, dataId={}, group={},tenant={}, md5={}, listener={} ", envName, dataId, group, tenant, md5, listener); job.run(); } } catch (Throwable t) { LOGGER.error("[{}] [notify-listener-error] dataId={}, group={},tenant={}, md5={}, listener={} throwable={}", envName, dataId, group, tenant, md5, listener, t.getCause()); } } abstract class NotifyTask implements Runnable { boolean async = false; public boolean isAsync() { return async; } public void setAsync(boolean async) { this.async = async; } } public static String getMd5String(String config) { return (null == config) ? Constants.NULL : MD5Utils.md5Hex(config, Constants.ENCODE); } private String loadCacheContentFromDiskLocal(String name, String dataId, String group, String tenant) { String content = LocalConfigInfoProcessor.getFailover(name, dataId, group, tenant); content = (null != content) ? content : LocalConfigInfoProcessor.getSnapshot(name, dataId, group, tenant); return content; } /** * 1.first add listener.default is false;need to check. 2.receive config change notify,set false;need to check. * 3.last listener is remove,set to false;need to check * * @return */ public boolean isConsistentWithServer() { return isConsistentWithServer.get(); } public void setConsistentWithServer(boolean consistentWithServer) { isConsistentWithServer.set(consistentWithServer); } public boolean isDiscard() { return isDiscard; } public void setDiscard(boolean discard) { isDiscard = discard; } public CacheData(ConfigFilterChainManager configFilterChainManager, String envName, String dataId, String group) { this(configFilterChainManager, envName, dataId, group, TenantUtil.getUserTenantForAcm()); } public CacheData(ConfigFilterChainManager configFilterChainManager, String envName, String dataId, String group, String tenant) { if (null == dataId || null == group) { throw new IllegalArgumentException("dataId=" + dataId + ", group=" + group); } this.configFilterChainManager = configFilterChainManager; this.envName = envName; this.dataId = dataId; this.group = group; this.tenant = tenant; this.listeners = new CopyOnWriteArrayList<>(); this.isInitializing = true; if (initSnapshot) { this.content = loadCacheContentFromDiskLocal(envName, dataId, group, tenant); this.encryptedDataKey = loadEncryptedDataKeyFromDiskLocal(envName, dataId, group, tenant); this.md5 = getMd5String(this.content); } } // ================== public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } private String loadEncryptedDataKeyFromDiskLocal(String envName, String dataId, String group, String tenant) { String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(envName, dataId, group, tenant); if (encryptedDataKey != null) { return encryptedDataKey; } return LocalEncryptedDataKeyProcessor.getEncryptDataKeySnapshot(envName, dataId, group, tenant); } private static class ManagerListenerWrap { boolean inNotifying = false; final Listener listener; String lastCallMd5 = Constants.NULL; /** * here is a decryptContent. */ String lastContent = null; ManagerListenerWrap(Listener listener) { this.listener = listener; } ManagerListenerWrap(Listener listener, String md5) { this.listener = listener; this.lastCallMd5 = md5; } ManagerListenerWrap(Listener listener, String md5, String lastContent) { this(listener, md5); this.lastContent = lastContent; } @Override public boolean equals(Object obj) { if (null == obj || obj.getClass() != getClass()) { return false; } if (obj == this) { return true; } ManagerListenerWrap other = (ManagerListenerWrap) obj; return listener.equals(other.listener); } @Override public int hashCode() { return super.hashCode(); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ChangeNotifyBlockEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.common.notify.SlowEvent; /** * change notify block event. * * @author shiyiyue */ public class ChangeNotifyBlockEvent extends SlowEvent { public ChangeNotifyBlockEvent(String listener, String dataId, String group, String tenant, long startTime, long currentTime, String blockStack) { this.listener = listener; this.dataId = dataId; this.group = group; this.tenant = tenant; this.startTime = startTime; this.currentTime = currentTime; this.blockStack = blockStack; } private String listener; private String dataId; private String group; private String tenant; private long startTime; private long currentTime; private String blockStack; public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public long getStartTime() { return startTime; } public void setStartTime(long startTime) { this.startTime = startTime; } public long getCurrentTime() { return currentTime; } public void setCurrentTime(long currentTime) { this.currentTime = currentTime; } public String getBlockStack() { return blockStack; } public void setBlockStack(String blockStack) { this.blockStack = blockStack; } @Override public String toString() { return "ChangeNotifyBlockEvent{" + "listener='" + listener + '\'' + ", dataId='" + dataId + '\'' + ", group='" + group + '\'' + ", tenant='" + tenant + '\'' + ", startTime=" + startTime + ", currentTime=" + currentTime + ", blockStack='" + blockStack + '\'' + '}'; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ClientFuzzyWatchNotifyRequestHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; /** * fuzzy watch request from server . * @author shiyiyue */ public class ClientFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; public ClientFuzzyWatchNotifyRequestHandler(ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder) { this.configFuzzyWatchGroupKeyHolder = configFuzzyWatchGroupKeyHolder; } @Override public Response requestReply(Request request, Connection connection) { //fuzzy watch diff reconciliation sync if (request instanceof ConfigFuzzyWatchSyncRequest) { return configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest( (ConfigFuzzyWatchSyncRequest) request); } //fuzzy watch changed notify for a single config. include config changed or config delete. if (request instanceof ConfigFuzzyWatchChangeNotifyRequest) { return configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest( (ConfigFuzzyWatchChangeNotifyRequest) request); } return null; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ClientWorker.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; 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.request.ConfigRemoveRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.env.SourceType; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.client.utils.EnvUtil; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.client.utils.ParamUtil; import com.alibaba.nacos.client.utils.TenantUtil; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.labels.impl.DefaultLabelsCollectorManager; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ConnectionEventListener; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientConfigFactory; import com.alibaba.nacos.common.remote.client.RpcClientFactory; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConnLabelsUtils; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.common.utils.VersionUtils; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.google.gson.Gson; import com.google.gson.JsonObject; import org.slf4j.Logger; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.UUID; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.Executors; import static com.alibaba.nacos.api.common.Constants.APP_CONN_PREFIX; import static com.alibaba.nacos.api.common.Constants.ENCODE; /** * Long polling. * * @author Nacos */ public class ClientWorker implements Closeable { private static final Logger LOGGER = LogUtils.logger(ClientWorker.class); private static final String NOTIFY_HEADER = "notify"; private static final String TAG_PARAM = "tag"; private static final String APP_NAME_PARAM = "appName"; private static final String BETAIPS_PARAM = "betaIps"; private static final String TYPE_PARAM = "type"; private static final String ENCRYPTED_DATA_KEY_PARAM = "encryptedDataKey"; /** * groupKey -> cacheData. */ private final AtomicReference> cacheMap = new AtomicReference<>(new HashMap<>()); private final DefaultLabelsCollectorManager defaultLabelsCollectorManager = new DefaultLabelsCollectorManager(); private ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; private Map appLabels = new HashMap<>(); private final ConfigFilterChainManager configFilterChainManager; private final String uuid = UUID.randomUUID().toString(); private long requestTimeout; private final ConfigRpcTransportClient agent; private boolean enableRemoteSyncConfig = false; private static final int MIN_THREAD_NUM = 2; private static final int THREAD_MULTIPLE = 1; private boolean enableClientMetrics = true; /** * index(taskId)-> total cache count for this taskId. */ private final List taskIdCacheCountList = new ArrayList<>(); /** * Add listeners for data. * * @param dataId dataId of data * @param group group of data * @param listeners listeners */ public void addListeners(String dataId, String group, List listeners) throws NacosException { group = blank2defaultGroup(group); CacheData cache = addCacheDataIfAbsent(dataId, group); synchronized (cache) { for (Listener listener : listeners) { cache.addListener(listener); } cache.setDiscard(false); cache.setConsistentWithServer(false); // make sure cache exists in cacheMap if (getCache(dataId, group) != cache) { putCache(GroupKey.getKey(dataId, group), cache); } agent.notifyListenConfig(); } } /** * Add listeners for tenant. * * @param dataId dataId of data * @param group group of data * @param listeners listeners * @throws NacosException nacos exception */ public void addTenantListeners(String dataId, String group, List listeners) throws NacosException { group = blank2defaultGroup(group); String tenant = agent.getTenant(); CacheData cache = addCacheDataIfAbsent(dataId, group, tenant); synchronized (cache) { for (Listener listener : listeners) { cache.addListener(listener); } cache.setDiscard(false); cache.setConsistentWithServer(false); // ensure cache present in cacheMap if (getCache(dataId, group, tenant) != cache) { putCache(GroupKey.getKeyTenant(dataId, group, tenant), cache); } agent.notifyListenConfig(); } } /** * Add listeners for tenant with content. * * @param dataId dataId of data * @param group group of data * @param content content * @param encryptedDataKey encryptedDataKey * @param listeners listeners * @throws NacosException nacos exception */ public void addTenantListenersWithContent(String dataId, String group, String content, String encryptedDataKey, List listeners) throws NacosException { group = blank2defaultGroup(group); String tenant = agent.getTenant(); CacheData cache = addCacheDataIfAbsent(dataId, group, tenant); synchronized (cache) { cache.setEncryptedDataKey(encryptedDataKey); cache.setContent(content); for (Listener listener : listeners) { cache.addListener(listener); } cache.setDiscard(false); cache.setConsistentWithServer(false); // make sure cache exists in cacheMap if (getCache(dataId, group, tenant) != cache) { putCache(GroupKey.getKeyTenant(dataId, group, tenant), cache); } agent.notifyListenConfig(); } } /** * Remove listener. * * @param dataId dataId of data * @param group group of data * @param listener listener */ public void removeListener(String dataId, String group, Listener listener) { group = blank2defaultGroup(group); CacheData cache = getCache(dataId, group); if (null != cache) { synchronized (cache) { cache.removeListener(listener); if (cache.getListeners().isEmpty()) { cache.setConsistentWithServer(false); cache.setDiscard(true); agent.removeCache(dataId, group); } } } } /** * Remove listeners for tenant. * * @param dataId dataId of data * @param group group of data * @param listener listener */ public void removeTenantListener(String dataId, String group, Listener listener) { group = blank2defaultGroup(group); String tenant = agent.getTenant(); CacheData cache = getCache(dataId, group, tenant); if (null != cache) { synchronized (cache) { cache.removeListener(listener); if (cache.getListeners().isEmpty()) { cache.setConsistentWithServer(false); cache.setDiscard(true); agent.removeCache(dataId, group); } } } } /** * Adds a list of fuzzy listen listeners for the specified data ID pattern and group. * * @param dataIdPattern The pattern of the data ID to listen for. * @param groupPattern The group of the configuration. * @param fuzzyWatchEventWatcher The configFuzzyWatcher to add. * @throws NacosException If an error occurs while adding the listeners. */ public ConfigFuzzyWatchContext addTenantFuzzyWatcher(String dataIdPattern, String groupPattern, FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { return configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataIdPattern, groupPattern, fuzzyWatchEventWatcher); } /** * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. * * @param dataIdPattern The pattern of the data ID. * @param group The group of the configuration. * @param watcher The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ public void removeFuzzyListenListener(String dataIdPattern, String group, FuzzyWatchEventWatcher watcher) { configFuzzyWatchGroupKeyHolder.removeFuzzyWatcher(dataIdPattern, group, watcher); } void removeCache(String dataId, String group, String tenant) { String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); synchronized (cacheMap) { Map copy = new HashMap<>(cacheMap.get()); CacheData remove = copy.remove(groupKey); if (remove != null) { decreaseTaskIdCount(remove.getTaskId()); } cacheMap.set(copy); } LOGGER.info("[{}] [unsubscribe] {}", agent.getName(), groupKey); if (enableClientMetrics) { try { MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); } catch (Throwable t) { LOGGER.error("Failed to update metrics for listen config count", t); } } } /** * remove config. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param tag tag. * @return success or not. * @throws NacosException exception to throw. */ public boolean removeConfig(String dataId, String group, String tenant, String tag) throws NacosException { return agent.removeConfig(dataId, group, tenant, tag); } /** * publish config. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param appName appName. * @param tag tag. * @param betaIps betaIps. * @param content content. * @param casMd5 casMd5. * @param type type. * @return success or not. * @throws NacosException exception throw. */ public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, String betaIps, String content, String encryptedDataKey, String casMd5, String type) throws NacosException { return agent.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, encryptedDataKey, casMd5, type); } /** * Add cache data if absent. * * @param dataId data id if data * @param group group of data * @return cache data */ public CacheData addCacheDataIfAbsent(String dataId, String group) { CacheData cache = getCache(dataId, group); if (null != cache) { return cache; } String key = GroupKey.getKey(dataId, group); cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group); synchronized (cacheMap) { CacheData cacheFromMap = getCache(dataId, group); // multiple listeners on the same dataid+group and race condition,so double check again //other listener thread beat me to set to cacheMap if (null != cacheFromMap) { cache = cacheFromMap; //reset so that server not hang this check cache.setInitializing(true); } else { int taskId = calculateTaskId(); increaseTaskIdCount(taskId); cache.setTaskId(taskId); } Map copy = new HashMap<>(cacheMap.get()); copy.put(key, cache); cacheMap.set(copy); } LOGGER.info("[{}] [subscribe] {}", agent.getName(), key); if (enableClientMetrics) { try { MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); } catch (Throwable t) { LOGGER.error("Failed to update metrics for listen config count", t); } } return cache; } /** * Add cache data if absent. * * @param dataId data id if data * @param group group of data * @param tenant tenant of data * @return cache data */ public CacheData addCacheDataIfAbsent(String dataId, String group, String tenant) throws NacosException { CacheData cache = getCache(dataId, group, tenant); if (null != cache) { return cache; } String key = GroupKey.getKeyTenant(dataId, group, tenant); synchronized (cacheMap) { CacheData cacheFromMap = getCache(dataId, group, tenant); // multiple listeners on the same dataid+group and race condition,so // double check again // other listener thread beat me to set to cacheMap if (null != cacheFromMap) { cache = cacheFromMap; // reset so that server not hang this check cache.setInitializing(true); } else { cache = new CacheData(configFilterChainManager, agent.getName(), dataId, group, tenant); int taskId = calculateTaskId(); increaseTaskIdCount(taskId); cache.setTaskId(taskId); // fix issue # 1317 if (enableRemoteSyncConfig) { ConfigResponse response = getServerConfig(dataId, group, tenant, requestTimeout, false); cache.setEncryptedDataKey(response.getEncryptedDataKey()); cache.setContent(response.getContent()); } } Map copy = new HashMap<>(this.cacheMap.get()); copy.put(key, cache); cacheMap.set(copy); } LOGGER.info("[{}] [subscribe] {}", agent.getName(), key); if (enableClientMetrics) { try { MetricsMonitor.getListenConfigCountMonitor().set(cacheMap.get().size()); } catch (Throwable t) { LOGGER.error("Failed to update metrics for listen config count", t); } } return cache; } /** * Put cache. * * @param key groupKey * @param cache cache */ private void putCache(String key, CacheData cache) { synchronized (cacheMap) { Map copy = new HashMap<>(this.cacheMap.get()); copy.put(key, cache); cacheMap.set(copy); } } private void increaseTaskIdCount(int taskId) { taskIdCacheCountList.get(taskId).incrementAndGet(); } private void decreaseTaskIdCount(int taskId) { taskIdCacheCountList.get(taskId).decrementAndGet(); } private int calculateTaskId() { int perTaskSize = (int) ParamUtil.getPerTaskConfigSize(); for (int index = 0; index < taskIdCacheCountList.size(); index++) { if (taskIdCacheCountList.get(index).get() < perTaskSize) { return index; } } taskIdCacheCountList.add(new AtomicInteger(0)); return taskIdCacheCountList.size() - 1; } public CacheData getCache(String dataId, String group) { return getCache(dataId, group, TenantUtil.getUserTenantForAcm()); } public CacheData getCache(String dataId, String group, String tenant) { if (null == dataId || null == group) { throw new IllegalArgumentException(); } return cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); } public ConfigResponse getServerConfig(String dataId, String group, String tenant, long readTimeout, boolean notify) throws NacosException { if (StringUtils.isBlank(group)) { group = Constants.DEFAULT_GROUP; } return agent.queryConfig(dataId, group, tenant, readTimeout, notify); } private String blank2defaultGroup(String group) { return StringUtils.isBlank(group) ? Constants.DEFAULT_GROUP : group.trim(); } public ClientWorker(final ConfigFilterChainManager configFilterChainManager, ConfigServerListManager serverListManager, final NacosClientProperties properties) throws NacosException { this.configFilterChainManager = configFilterChainManager; init(properties); agent = new ConfigRpcTransportClient(properties, serverListManager); configFuzzyWatchGroupKeyHolder = new ConfigFuzzyWatchGroupKeyHolder(agent, uuid); ThreadPoolExecutor executor = instantiateClientExecutor(properties); agent.setExecutor(executor); agent.start(); configFuzzyWatchGroupKeyHolder.start(); } void initAppLabels(Properties properties) { this.appLabels = ConnLabelsUtils.addPrefixForEachKey(defaultLabelsCollectorManager.getLabels(properties), APP_CONN_PREFIX); } private ThreadPoolExecutor instantiateClientExecutor(final NacosClientProperties properties) { int workerThreadCount = initWorkerThreadCount(properties); return new ThreadPoolExecutor(workerThreadCount, workerThreadCount * 2, 60 * 5, TimeUnit.SECONDS, // when corePoolSize is not enough, task will not wait in queue, because SynchronousQueue 0 capacity // will create new thread to execute task util maximumPoolSize is reached new SynchronousQueue<>(), new NameThreadFactory("com.alibaba.nacos.client.executor"), // CallerRunsPolicy ensures that tasks are not lost new ThreadPoolExecutor.CallerRunsPolicy() ); } private int initWorkerThreadCount(NacosClientProperties properties) { int count = ThreadUtils.getSuitableThreadCount(THREAD_MULTIPLE); if (properties == null) { return count; } count = Math.min(count, properties.getInteger(PropertyKeyConst.CLIENT_WORKER_MAX_THREAD_COUNT, count)); count = Math.max(count, MIN_THREAD_NUM); return properties.getInteger(PropertyKeyConst.CLIENT_WORKER_THREAD_COUNT, count); } private void init(NacosClientProperties properties) { requestTimeout = ConvertUtils.toLong(properties.getProperty(PropertyKeyConst.CONFIG_REQUEST_TIMEOUT, "-1")); this.enableRemoteSyncConfig = Boolean.parseBoolean( properties.getProperty(PropertyKeyConst.ENABLE_REMOTE_SYNC_CONFIG)); this.enableClientMetrics = Boolean.parseBoolean( properties.getProperty(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true")); initAppLabels(properties.getProperties(SourceType.PROPERTIES)); } Map getMetrics(List metricsKeys) { Map metric = new HashMap<>(16); metric.put("listenConfigSize", String.valueOf(this.cacheMap.get().size())); metric.put("clientVersion", VersionUtils.getFullClientVersion()); metric.put("snapshotDir", LocalConfigInfoProcessor.LOCAL_SNAPSHOT_PATH); metric.put("addressUrl", agent.serverListManager.getAddressSource()); metric.put("isFixedServer", agent.serverListManager.isFixed()); metric.put("serverUrls", agent.serverListManager.getUrlString()); Map metricValues = getMetricsValue(metricsKeys); metric.put("metricValues", metricValues); Map metrics = new HashMap<>(1); metrics.put(uuid, JacksonUtils.toJson(metric)); return metrics; } private Map getMetricsValue( List metricsKeys) { if (metricsKeys == null) { return null; } Map values = new HashMap<>(16); for (ClientConfigMetricRequest.MetricsKey metricsKey : metricsKeys) { if (ClientConfigMetricRequest.MetricsKey.CACHE_DATA.equals(metricsKey.getType())) { CacheData cacheData = cacheMap.get().get(metricsKey.getKey()); values.putIfAbsent(metricsKey, cacheData == null ? null : cacheData.getContent() + ":" + cacheData.getMd5()); } if (ClientConfigMetricRequest.MetricsKey.SNAPSHOT_DATA.equals(metricsKey.getType())) { String[] configStr = GroupKey.parseKey(metricsKey.getKey()); String snapshot = LocalConfigInfoProcessor.getSnapshot(agent.getName(), configStr[0], configStr[1], configStr[2]); values.putIfAbsent(metricsKey, snapshot == null ? null : snapshot + ":" + MD5Utils.md5Hex(snapshot, ENCODE)); } } return values; } @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); LOGGER.info("{} do shutdown begin", className); if (configFuzzyWatchGroupKeyHolder != null) { configFuzzyWatchGroupKeyHolder.shutdown(); // help gc configFuzzyWatchGroupKeyHolder = null; } if (agent != null) { agent.shutdown(); } LOGGER.info("{} do shutdown stop", className); } /** * check if it has any connectable server endpoint. * * @return true: that means has atleast one connected rpc client. flase: that means does not have any connected rpc * client. */ public boolean isHealthServer() { return agent.isHealthServer(); } public class ConfigRpcTransportClient extends ConfigTransportClient { Map multiTaskExecutor = new HashMap<>(); private ExecutorService listenExecutor; private final BlockingQueue listenExecutebell = new ArrayBlockingQueue<>(1); private final Object bellItem = new Object(); private long lastAllSyncTime = System.currentTimeMillis(); Subscriber subscriber = null; /** * 3 minutes to check all listen cache keys. */ private static final long ALL_SYNC_INTERNAL = 3 * 60 * 1000L; public ConfigRpcTransportClient(NacosClientProperties properties, ConfigServerListManager serverListManager) { super(properties, serverListManager); } private ConnectionType getConnectionType() { return ConnectionType.GRPC; } @Override public void shutdown() throws NacosException { super.shutdown(); synchronized (RpcClientFactory.getAllClientEntries()) { LOGGER.info("Trying to shutdown transport client {}", this); Set> allClientEntries = RpcClientFactory.getAllClientEntries(); Iterator> iterator = allClientEntries.iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); if (entry.getKey().startsWith(uuid)) { LOGGER.info("Trying to shutdown rpc client {}", entry.getKey()); try { entry.getValue().shutdown(); } catch (NacosException nacosException) { nacosException.printStackTrace(); } LOGGER.info("Remove rpc client {}", entry.getKey()); iterator.remove(); } } LOGGER.info("Shutdown executor {}", agent.getExecutor()); agent.getExecutor().shutdown(); Map stringCacheDataMap = cacheMap.get(); for (Map.Entry entry : stringCacheDataMap.entrySet()) { entry.getValue().setConsistentWithServer(false); } if (subscriber != null) { NotifyCenter.deregisterSubscriber(subscriber); } multiTaskExecutor.values().forEach((executor) -> { if (executor != null && !executor.isShutdown()) { LOGGER.info("Shutdown multi task executor {}", executor); executor.shutdown(); } }); if (listenExecutor != null && !listenExecutor.isShutdown()) { LOGGER.info("Shutdown listen config executor {}", listenExecutor); listenExecutor.shutdown(); } } } private Map getLabels() { Map labels = new HashMap<>(2, 1); labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK); labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_CONFIG); labels.put(Constants.APPNAME, AppNameUtils.getAppName()); if (EnvUtil.getSelfVipserverTag() != null) { labels.put(Constants.VIPSERVER_TAG, EnvUtil.getSelfVipserverTag()); } if (EnvUtil.getSelfAmoryTag() != null) { labels.put(Constants.AMORY_TAG, EnvUtil.getSelfAmoryTag()); } if (EnvUtil.getSelfLocationTag() != null) { labels.put(Constants.LOCATION_TAG, EnvUtil.getSelfLocationTag()); } labels.putAll(appLabels); return labels; } ConfigChangeNotifyResponse handleConfigChangeNotifyRequest(ConfigChangeNotifyRequest configChangeNotifyRequest, String clientName) { LOGGER.info("[{}] [server-push] config changed. dataId={}, group={},tenant={}", clientName, configChangeNotifyRequest.getDataId(), configChangeNotifyRequest.getGroup(), configChangeNotifyRequest.getTenant()); String groupKey = GroupKey.getKeyTenant(configChangeNotifyRequest.getDataId(), configChangeNotifyRequest.getGroup(), configChangeNotifyRequest.getTenant()); CacheData cacheData = cacheMap.get().get(groupKey); if (cacheData != null) { synchronized (cacheData) { cacheData.getReceiveNotifyChanged().set(true); cacheData.setConsistentWithServer(false); notifyListenConfig(); } } return new ConfigChangeNotifyResponse(); } ClientConfigMetricResponse handleClientMetricsRequest(ClientConfigMetricRequest configMetricRequest) { ClientConfigMetricResponse response = new ClientConfigMetricResponse(); response.setMetrics(getMetrics(configMetricRequest.getMetricsKeys())); return response; } private void initRpcClientHandler(final RpcClient rpcClientInner) { /* * Register Config Change /Config ReSync Handler */ rpcClientInner.registerServerRequestHandler((request, connection) -> { //config change notify if (request instanceof ConfigChangeNotifyRequest) { return handleConfigChangeNotifyRequest((ConfigChangeNotifyRequest) request, rpcClientInner.getName()); } return null; }); rpcClientInner.registerServerRequestHandler((request, connection) -> { if (request instanceof ClientConfigMetricRequest) { return handleClientMetricsRequest((ClientConfigMetricRequest) request); } return null; }); rpcClientInner.registerServerRequestHandler( new ClientFuzzyWatchNotifyRequestHandler(configFuzzyWatchGroupKeyHolder)); rpcClientInner.registerConnectionListener(new ConnectionEventListener() { @Override public void onConnected(Connection connection) { LOGGER.info("[{}] Connected,notify listen context...", rpcClientInner.getName()); notifyListenConfig(); LOGGER.info("[{}] Connected,notify fuzzy listen context...", rpcClientInner.getName()); configFuzzyWatchGroupKeyHolder.notifyFuzzyWatchSync(); } @Override public void onDisConnect(Connection connection) { String taskId = rpcClientInner.getLabels().get("taskId"); LOGGER.info("[{}] DisConnected,reset listen context", rpcClientInner.getName()); Collection values = cacheMap.get().values(); for (CacheData cacheData : values) { if (StringUtils.isNotBlank(taskId)) { if (Integer.valueOf(taskId).equals(cacheData.getTaskId())) { cacheData.setConsistentWithServer(false); } } else { cacheData.setConsistentWithServer(false); } } LOGGER.info("[{}] DisConnected,reset fuzzy watch consistence status", rpcClientInner.getName()); configFuzzyWatchGroupKeyHolder.resetConsistenceStatus(); } }); rpcClientInner.serverListFactory(new ServerListFactory() { @Override public String genNextServer() { return ConfigRpcTransportClient.super.serverListManager.genNextServer(); } @Override public String getCurrentServer() { return ConfigRpcTransportClient.super.serverListManager.getCurrentServer(); } @Override public List getServerList() { return ConfigRpcTransportClient.super.serverListManager.getServerList(); } }); subscriber = new Subscriber() { @Override public void onEvent(Event event) { rpcClientInner.onServerListChange(); } @Override public Class subscribeType() { return ServerListChangeEvent.class; } }; NotifyCenter.registerSubscriber(subscriber); } @Override public void startInternal() { listenExecutor = Executors.newSingleThreadExecutor(new NameThreadFactory("com.alibaba.nacos.client.listen-executor")); listenExecutor.submit(() -> { while (!listenExecutor.isShutdown() && !listenExecutor.isTerminated()) { try { listenExecutebell.poll(5L, TimeUnit.SECONDS); if (listenExecutor.isShutdown() || listenExecutor.isTerminated()) { continue; } executeConfigListen(); } catch (Throwable e) { LOGGER.error("[rpc listen execute] [rpc listen] exception", e); try { Thread.sleep(50L); } catch (InterruptedException interruptedException) { //ignore } notifyListenConfig(); } } }); } @Override public String getName() { return serverListManager.getName(); } @Override public void notifyListenConfig() { listenExecutebell.offer(bellItem); } @Override public void executeConfigListen() throws NacosException { Map> listenCachesMap = new HashMap<>(16); Map> removeListenCachesMap = new HashMap<>(16); long now = System.currentTimeMillis(); boolean needAllSync = now - lastAllSyncTime >= ALL_SYNC_INTERNAL; for (CacheData cache : cacheMap.get().values()) { synchronized (cache) { checkLocalConfig(cache); // check local listeners consistent. if (cache.isConsistentWithServer()) { cache.checkListenerMd5(); if (!needAllSync) { continue; } } // If local configuration information is used, then skip the processing directly. if (cache.isUseLocalConfigInfo()) { continue; } if (!cache.isDiscard()) { List cacheDatas = listenCachesMap.computeIfAbsent(String.valueOf(cache.getTaskId()), k -> new LinkedList<>()); cacheDatas.add(cache); } else { List cacheDatas = removeListenCachesMap.computeIfAbsent( String.valueOf(cache.getTaskId()), k -> new LinkedList<>()); cacheDatas.add(cache); } } } //execute check listen ,return true if has change keys. boolean hasChangedKeys = checkListenCache(listenCachesMap); //execute check remove listen. checkRemoveListenCache(removeListenCachesMap); if (needAllSync) { lastAllSyncTime = now; } //If has changed keys,notify re sync md5. if (hasChangedKeys) { notifyListenConfig(); } } /** * Checks and handles local configuration for a given CacheData object. This method evaluates the use of * failover files for local configuration storage and updates the CacheData accordingly. * * @param cacheData The CacheData object to be processed. */ public void checkLocalConfig(CacheData cacheData) { final String dataId = cacheData.dataId; final String group = cacheData.group; final String tenant = cacheData.tenant; final String envName = cacheData.envName; // Check if a failover file exists for the specified dataId, group, and tenant. File file = LocalConfigInfoProcessor.getFailoverFile(envName, dataId, group, tenant); // If not using local config info and a failover file exists, load and use it. if (!cacheData.isUseLocalConfigInfo() && file.exists()) { String content = LocalConfigInfoProcessor.getFailover(envName, dataId, group, tenant); final String md5 = MD5Utils.md5Hex(content, Constants.ENCODE); cacheData.setUseLocalConfigInfo(true); cacheData.setLocalConfigInfoVersion(file.lastModified()); cacheData.setContent(content); LOGGER.warn("[{}] [failover-change] failover file created. dataId={}, group={}, tenant={}, md5={}", envName, dataId, group, tenant, md5); return; } // If use local config info, but the failover file is deleted, switch back to server config. if (cacheData.isUseLocalConfigInfo() && !file.exists()) { cacheData.setUseLocalConfigInfo(false); LOGGER.warn("[{}] [failover-change] failover file deleted. dataId={}, group={}, tenant={}", envName, dataId, group, tenant); return; } // When the failover file content changes, indicating a change in local configuration. if (cacheData.isUseLocalConfigInfo() && file.exists() && cacheData.getLocalConfigInfoVersion() != file.lastModified()) { String content = LocalConfigInfoProcessor.getFailover(envName, dataId, group, tenant); final String md5 = MD5Utils.md5Hex(content, Constants.ENCODE); cacheData.setUseLocalConfigInfo(true); cacheData.setLocalConfigInfoVersion(file.lastModified()); cacheData.setContent(content); LOGGER.warn("[{}] [failover-change] failover file changed. dataId={}, group={}, tenant={}, md5={}", envName, dataId, group, tenant, md5); } } private ExecutorService ensureSyncExecutor(String taskId) { if (!multiTaskExecutor.containsKey(taskId)) { multiTaskExecutor.put(taskId, new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), r -> { Thread thread = new Thread(r, "nacos.client.config.listener.task-" + taskId); thread.setDaemon(true); return thread; })); } return multiTaskExecutor.get(taskId); } private void refreshContentAndCheck(RpcClient rpcClient, String groupKey, boolean notify) { if (cacheMap.get() != null && cacheMap.get().containsKey(groupKey)) { CacheData cache = cacheMap.get().get(groupKey); refreshContentAndCheck(rpcClient, cache, notify); } } private void refreshContentAndCheck(RpcClient rpcClient, CacheData cacheData, boolean notify) { try { ConfigResponse response = this.queryConfigInner(rpcClient, cacheData.dataId, cacheData.group, cacheData.tenant, requestTimeout, notify); cacheData.setEncryptedDataKey(response.getEncryptedDataKey()); cacheData.setContent(response.getContent()); if (null != response.getConfigType()) { cacheData.setType(response.getConfigType()); } if (notify) { LOGGER.info("[{}] [data-received] dataId={}, group={}, tenant={}, md5={}, type={}", agent.getName(), cacheData.dataId, cacheData.group, cacheData.tenant, cacheData.getMd5(), response.getConfigType()); } cacheData.checkListenerMd5(); } catch (Exception e) { LOGGER.error("refresh content and check md5 fail ,dataId={},group={},tenant={} ", cacheData.dataId, cacheData.group, cacheData.tenant, e); } } private void checkRemoveListenCache(Map> removeListenCachesMap) throws NacosException { if (!removeListenCachesMap.isEmpty()) { List listenFutures = new ArrayList<>(); for (Map.Entry> entry : removeListenCachesMap.entrySet()) { String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); ExecutorService executorService = ensureSyncExecutor(taskId); Future future = executorService.submit(() -> { List removeListenCaches = entry.getValue(); ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(removeListenCaches); configChangeListenRequest.setListen(false); try { boolean removeSuccess = unListenConfigChange(rpcClient, configChangeListenRequest); if (removeSuccess) { for (CacheData cacheData : removeListenCaches) { synchronized (cacheData) { if (cacheData.isDiscard() && cacheData.getListeners().isEmpty()) { ClientWorker.this.removeCache(cacheData.dataId, cacheData.group, cacheData.tenant); } } } } } catch (Throwable e) { LOGGER.error("Async remove listen config change error ", e); try { Thread.sleep(50L); } catch (InterruptedException interruptedException) { //ignore } notifyListenConfig(); } }); listenFutures.add(future); } for (Future future : listenFutures) { try { future.get(); } catch (Throwable throwable) { LOGGER.error("Async remove listen config change error ", throwable); } } } } private boolean checkListenCache(Map> listenCachesMap) throws NacosException { final AtomicBoolean hasChangedKeys = new AtomicBoolean(false); if (!listenCachesMap.isEmpty()) { List listenFutures = new ArrayList<>(); for (Map.Entry> entry : listenCachesMap.entrySet()) { String taskId = entry.getKey(); RpcClient rpcClient = ensureRpcClient(taskId); ExecutorService executorService = ensureSyncExecutor(taskId); Future future = executorService.submit(() -> { List listenCaches = entry.getValue(); //reset notify change flag. for (CacheData cacheData : listenCaches) { cacheData.getReceiveNotifyChanged().set(false); } ConfigBatchListenRequest configChangeListenRequest = buildConfigRequest(listenCaches); configChangeListenRequest.setListen(true); try { ConfigChangeBatchListenResponse listenResponse = (ConfigChangeBatchListenResponse) requestProxy( rpcClient, configChangeListenRequest); if (listenResponse != null && listenResponse.isSuccess()) { Set changeKeys = new HashSet(); List changedConfigs = listenResponse.getChangedConfigs(); //handle changed keys,notify listener if (!CollectionUtils.isEmpty(changedConfigs)) { hasChangedKeys.set(true); for (ConfigChangeBatchListenResponse.ConfigContext changeConfig : changedConfigs) { String changeKey = GroupKey.getKeyTenant(changeConfig.getDataId(), changeConfig.getGroup(), changeConfig.getTenant()); changeKeys.add(changeKey); boolean isInitializing = cacheMap.get().get(changeKey).isInitializing(); refreshContentAndCheck(rpcClient, changeKey, !isInitializing); } } for (CacheData cacheData : listenCaches) { if (cacheData.getReceiveNotifyChanged().get()) { String changeKey = GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.getTenant()); if (!changeKeys.contains(changeKey)) { boolean isInitializing = cacheMap.get().get(changeKey).isInitializing(); refreshContentAndCheck(rpcClient, changeKey, !isInitializing); } } } //handler content configs for (CacheData cacheData : listenCaches) { cacheData.setInitializing(false); String groupKey = GroupKey.getKeyTenant(cacheData.dataId, cacheData.group, cacheData.getTenant()); if (!changeKeys.contains(groupKey)) { synchronized (cacheData) { if (!cacheData.getReceiveNotifyChanged().get()) { cacheData.setConsistentWithServer(true); } } } } } } catch (Throwable e) { LOGGER.error("Execute listen config change error ", e); try { Thread.sleep(50L); } catch (InterruptedException interruptedException) { //ignore } notifyListenConfig(); } }); listenFutures.add(future); } for (Future future : listenFutures) { try { future.get(); } catch (Throwable throwable) { LOGGER.error("Async listen config change error ", throwable); } } } return hasChangedKeys.get(); } RpcClient ensureRpcClient(String taskId) throws NacosException { synchronized (ClientWorker.this) { Map labels = getLabels(); Map newLabels = new HashMap<>(labels); newLabels.put("taskId", taskId); GrpcClientConfig grpcClientConfig = RpcClientConfigFactory.getInstance() .createGrpcClientConfig(properties, newLabels); RpcClient rpcClient = RpcClientFactory.createClient(uuid + "_config-" + taskId, getConnectionType(), grpcClientConfig); if (rpcClient.isWaitInitiated()) { initRpcClientHandler(rpcClient); rpcClient.setTenant(getTenant()); rpcClient.start(); } return rpcClient; } } /** * build config string. * * @param caches caches to build config string. * @return request. */ private ConfigBatchListenRequest buildConfigRequest(List caches) { ConfigBatchListenRequest configChangeListenRequest = new ConfigBatchListenRequest(); for (CacheData cacheData : caches) { configChangeListenRequest.addConfigListenContext(cacheData.group, cacheData.dataId, cacheData.tenant, cacheData.getMd5()); } return configChangeListenRequest; } @Override public void removeCache(String dataId, String group) { // Notify to rpc un listen ,and remove cache if success. notifyListenConfig(); } /** * send cancel listen config change request . * * @param configChangeListenRequest request of remove listen config string. */ private boolean unListenConfigChange(RpcClient rpcClient, ConfigBatchListenRequest configChangeListenRequest) throws NacosException { ConfigChangeBatchListenResponse response = (ConfigChangeBatchListenResponse) requestProxy(rpcClient, configChangeListenRequest); return response.isSuccess(); } @Override public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeouts, boolean notify) throws NacosException { RpcClient rpcClient = getOneRunningClient(); if (notify) { CacheData cacheData = cacheMap.get().get(GroupKey.getKeyTenant(dataId, group, tenant)); if (cacheData != null) { rpcClient = ensureRpcClient(String.valueOf(cacheData.getTaskId())); } } return queryConfigInner(rpcClient, dataId, group, tenant, readTimeouts, notify); } ConfigResponse queryConfigInner(RpcClient rpcClient, String dataId, String group, String tenant, long readTimeouts, boolean notify) throws NacosException { ConfigQueryRequest request = ConfigQueryRequest.build(dataId, group, tenant); request.putHeader(NOTIFY_HEADER, String.valueOf(notify)); ConfigQueryResponse response = (ConfigQueryResponse) requestProxy(rpcClient, request, readTimeouts); ConfigResponse configResponse = new ConfigResponse(); if (response.isSuccess()) { LocalConfigInfoProcessor.saveSnapshot(this.getName(), dataId, group, tenant, response.getContent()); configResponse.setContent(response.getContent()); // Set MD5 from server response configResponse.setMd5(response.getMd5()); String configType; if (StringUtils.isNotBlank(response.getContentType())) { configType = response.getContentType(); } else { configType = ConfigType.TEXT.getType(); } configResponse.setConfigType(configType); String encryptedDataKey = response.getEncryptedDataKey(); LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, encryptedDataKey); configResponse.setEncryptedDataKey(encryptedDataKey); return configResponse; } else if (response.getErrorCode() == ConfigQueryResponse.CONFIG_NOT_FOUND) { LocalConfigInfoProcessor.saveSnapshot(this.getName(), dataId, group, tenant, null); LocalEncryptedDataKeyProcessor.saveEncryptDataKeySnapshot(agent.getName(), dataId, group, tenant, null); return configResponse; } else if (response.getErrorCode() == ConfigQueryResponse.CONFIG_QUERY_CONFLICT) { LOGGER.error( "[{}] [sub-server-error] get server config being modified concurrently, dataId={}, group={}, " + "tenant={}", this.getName(), dataId, group, tenant); throw new NacosException(NacosException.CONFLICT, "data being modified, dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } else { LOGGER.error("[{}] [sub-server-error] dataId={}, group={}, tenant={}, code={}", this.getName(), dataId, group, tenant, response); throw new NacosException(response.getErrorCode(), "http error, code=" + response.getErrorCode() + ",msg=" + response.getMessage() + ",dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } } Response requestProxy(RpcClient rpcClientInner, Request request) throws NacosException { return requestProxy(rpcClientInner, request, requestTimeout); } private Response requestProxy(RpcClient rpcClientInner, Request request, long timeoutMills) throws NacosException { try { request.putAllHeader(super.getSecurityHeaders(resourceBuild(request))); request.putAllHeader(super.getCommonHeader()); } catch (Exception e) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e); } JsonObject asJsonObjectTemp = new Gson().toJsonTree(request).getAsJsonObject(); asJsonObjectTemp.remove("headers"); asJsonObjectTemp.remove("requestId"); boolean limit = Limiter.isLimit(request.getClass() + asJsonObjectTemp.toString()); if (limit) { throw new NacosException(NacosException.CLIENT_OVER_THRESHOLD, "More than client-side current limit threshold"); } Response response; if (timeoutMills < 0) { response = rpcClientInner.request(request); } else { response = rpcClientInner.request(request, timeoutMills); } // If the 403 login operation is triggered, refresh the accessToken of the client if (response.getErrorCode() == ConfigQueryResponse.NO_RIGHT) { reLogin(); } return response; } private RequestResource resourceBuild(Request request) { if (request instanceof ConfigQueryRequest) { String tenant = ((ConfigQueryRequest) request).getTenant(); String group = ((ConfigQueryRequest) request).getGroup(); String dataId = ((ConfigQueryRequest) request).getDataId(); return buildResource(tenant, group, dataId); } if (request instanceof ConfigPublishRequest) { String tenant = ((ConfigPublishRequest) request).getTenant(); String group = ((ConfigPublishRequest) request).getGroup(); String dataId = ((ConfigPublishRequest) request).getDataId(); return buildResource(tenant, group, dataId); } if (request instanceof ConfigRemoveRequest) { String tenant = ((ConfigRemoveRequest) request).getTenant(); String group = ((ConfigRemoveRequest) request).getGroup(); String dataId = ((ConfigRemoveRequest) request).getDataId(); return buildResource(tenant, group, dataId); } return RequestResource.configBuilder().build(); } RpcClient getOneRunningClient() throws NacosException { return ensureRpcClient("0"); } @Override public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, String betaIps, String content, String encryptedDataKey, String casMd5, String type) throws NacosException { try { ConfigPublishRequest request = new ConfigPublishRequest(dataId, group, tenant, content); request.setCasMd5(casMd5); request.putAdditionalParam(TAG_PARAM, tag); request.putAdditionalParam(APP_NAME_PARAM, appName); request.putAdditionalParam(BETAIPS_PARAM, betaIps); request.putAdditionalParam(TYPE_PARAM, type); request.putAdditionalParam(ENCRYPTED_DATA_KEY_PARAM, encryptedDataKey == null ? "" : encryptedDataKey); ConfigPublishResponse response = (ConfigPublishResponse) requestProxy(getOneRunningClient(), request); if (!response.isSuccess()) { LOGGER.warn("[{}] [publish-single] fail, dataId={}, group={}, tenant={}, code={}, msg={}", this.getName(), dataId, group, tenant, response.getErrorCode(), response.getMessage()); return false; } else { LOGGER.info("[{}] [publish-single] ok, dataId={}, group={}, tenant={}", getName(), dataId, group, tenant); return true; } } catch (Exception e) { LOGGER.warn("[{}] [publish-single] error, dataId={}, group={}, tenant={}, code={}, msg={}", this.getName(), dataId, group, tenant, "unknown", e.getMessage()); return false; } } @Override public boolean removeConfig(String dataId, String group, String tenant, String tag) throws NacosException { ConfigRemoveRequest request = new ConfigRemoveRequest(dataId, group, tenant, tag); ConfigRemoveResponse response = (ConfigRemoveResponse) requestProxy(getOneRunningClient(), request); return response.isSuccess(); } /** * check server is health. * * @return */ public boolean isHealthServer() { try { return getOneRunningClient().isRunning(); } catch (NacosException e) { LOGGER.warn("check server status failed.", e); return false; } } /** * Determine whether nacos-server supports the capability. * * @param abilityKey ability key * @return true if supported, otherwise false */ public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { try { return getOneRunningClient().getConnectionAbility(abilityKey) == AbilityStatus.SUPPORTED; } catch (NacosException e) { throw new NacosRuntimeException(e.getErrCode(), "Get Running Client failed: ", e); } } } public String getAgentName() { return agent.getName(); } public ConfigTransportClient getAgent() { return agent; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigChangeHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeItem; import com.alibaba.nacos.api.config.listener.ConfigChangeParser; import com.alibaba.nacos.common.spi.NacosServiceLoader; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; /** * ConfigChangeHandler. * * @author rushsky518 */ public class ConfigChangeHandler { private static class ConfigChangeHandlerHolder { private static final ConfigChangeHandler INSTANCE = new ConfigChangeHandler(); } private ConfigChangeHandler() { this.parserList = new LinkedList<>(); Collection loader = NacosServiceLoader.load(ConfigChangeParser.class); this.parserList.addAll(loader); this.parserList.add(new PropertiesChangeParser()); this.parserList.add(new YmlChangeParser()); } public static ConfigChangeHandler getInstance() { return ConfigChangeHandlerHolder.INSTANCE; } /** * Parse changed data. * * @param oldContent old data * @param newContent new data * @param type data type * @return change data map * @throws IOException io exception */ public Map parseChangeData(String oldContent, String newContent, String type) throws IOException { for (ConfigChangeParser changeParser : this.parserList) { if (changeParser.isResponsibleFor(type)) { return changeParser.doParse(oldContent, newContent, type); } } return Collections.emptyMap(); } private final List parserList; } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchContext.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.FuzzyWatchLoadWatcher; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * fuzzy watcher context for a single group key pattern. * *

    This class manages the context information for fuzzy listening, including environment name, task ID, data ID * pattern, group, tenant, listener set, and other related information. *

    * * @author stone-98 * @date 2024/3/4 */ public class ConfigFuzzyWatchContext { /** * Logger for FuzzyListenContext. */ private static final Logger LOGGER = LogUtils.logger(ConfigFuzzyWatchContext.class); /** * Environment name. */ private String envName; /** * Task ID. */ private int taskId; private String groupKeyPattern; /** * Set of data IDs associated with the context. */ private Set receivedGroupKeys = new ConcurrentHashSet<>(); long syncVersion = 0; /** * Flag indicating whether the context is consistent with the server. */ private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); /** * Condition object for waiting initialization completion. */ final AtomicBoolean initializationCompleted = new AtomicBoolean(false); /** * Flag indicating whether the context is discarded. */ private volatile boolean isDiscard = false; long patternLimitTs = 0; private static final long SUPPRESSED_PERIOD = 60 * 1000L; boolean patternLimitSuppressed() { return patternLimitTs > 0 && System.currentTimeMillis() - patternLimitTs < SUPPRESSED_PERIOD; } public void clearOverLimitTs() { this.patternLimitTs = 0; } public void refreshOverLimitTs() { this.patternLimitTs = System.currentTimeMillis(); } /** * Set of listeners associated with the context. */ private Set configFuzzyWatcherWrappers = new HashSet<>(); /** * Constructor with environment name, data ID pattern, and group. * * @param envName Environment name * @param groupKeyPattern groupKeyPattern */ public ConfigFuzzyWatchContext(String envName, String groupKeyPattern) { this.envName = envName; this.groupKeyPattern = groupKeyPattern; } /** * Calculate the listeners to notify based on the given UUID. * * @param uuid UUID to filter listeners * @return Set of listeners to notify */ public Set calculateListenersToNotify(String uuid) { Set listenersToNotify = new HashSet<>(); if (StringUtils.isEmpty(uuid)) { listenersToNotify = configFuzzyWatcherWrappers; } else { for (ConfigFuzzyWatcherWrapper listener : configFuzzyWatcherWrappers) { if (uuid.equals(listener.getUuid())) { listenersToNotify.add(listener); } } } return listenersToNotify; } /** * Notify the listener with the specified data ID, type, and UUID. * * @param groupKey groupKey * @param uuid UUID to filter listeners */ public void notifyWatcher(final String groupKey, final String changedType, final String syncType, final String uuid) { Set listenersToNotify = calculateListenersToNotify(uuid); doNotifyWatchers(groupKey, changedType, syncType, listenersToNotify); } /** * Perform the notification for the specified data ID, type, and listeners. * * @param groupKey groupKey * @param listenersToNotify Set of listeners to notify */ private void doNotifyWatchers(final String groupKey, final String changedType, final String syncType, Set listenersToNotify) { for (ConfigFuzzyWatcherWrapper watcher : listenersToNotify) { doNotifyWatcher(groupKey, changedType, syncType, watcher); } } /** * notify loader watcher. * * @param code over limit code,FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT or FUZZY_WATCH_PATTERN_OVER_LIMIT. */ public void notifyLoaderWatcher(int code) { if (this.patternLimitSuppressed()) { return; } boolean notify = false; for (ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper : calculateListenersToNotify(null)) { if (configFuzzyWatcherWrapper.fuzzyWatchEventWatcher instanceof FuzzyWatchLoadWatcher) { if (FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode().equals(code)) { ((FuzzyWatchLoadWatcher) configFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onConfigReachUpLimit(); notify = true; } if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode().equals(code)) { ((FuzzyWatchLoadWatcher) configFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onPatternOverLimit(); notify = true; } } } if (notify) { this.refreshOverLimitTs(); } } private void doNotifyWatcher(final String groupKey, final String changedType, final String syncType, ConfigFuzzyWatcherWrapper configFuzzyWatcher) { if (ADD_CONFIG.equals(changedType) && configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)) { return; } if (DELETE_CONFIG.equals(changedType) && !configFuzzyWatcher.getSyncGroupKeys().contains(groupKey)) { return; } String[] parseKey = GroupKey.parseKey(groupKey); String dataId = parseKey[0]; String group = parseKey[1]; String tenant = parseKey[2]; final String resetSyncType = initializationCompleted.get() ? syncType : FUZZY_WATCH_INIT_NOTIFY; AbstractFuzzyNotifyTask job = new AbstractFuzzyNotifyTask() { @Override public void run() { long start = System.currentTimeMillis(); ConfigFuzzyWatchChangeEvent event = ConfigFuzzyWatchChangeEvent.build(tenant, group, dataId, changedType, resetSyncType); if (configFuzzyWatcher != null) { configFuzzyWatcher.fuzzyWatchEventWatcher.onEvent(event); } LOGGER.info( "[{}] [notify-fuzzy-watcher-ok] dataId={}, group={}, tenant={}, watcher={}, job run cost={} millis.", envName, dataId, group, tenant, configFuzzyWatcher, (System.currentTimeMillis() - start)); if (changedType.equals(DELETE_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().remove(GroupKey.getKey(dataId, group, tenant)); } else if (changedType.equals(ADD_CONFIG)) { configFuzzyWatcher.getSyncGroupKeys().add(GroupKey.getKey(dataId, group, tenant)); } } }; try { if (null != configFuzzyWatcher.fuzzyWatchEventWatcher.getExecutor()) { LOGGER.info( "[{}] [notify-fuzzy-watcher] task submitted to user executor, dataId={}, group={}, tenant={}, listener={}.", envName, dataId, group, tenant, configFuzzyWatcher); job.async = true; configFuzzyWatcher.fuzzyWatchEventWatcher.getExecutor().execute(job); } else { LOGGER.info( "[{}] [notify-fuzzy-watcher] task execute in nacos thread, dataId={}, group={}, tenant={}, listener={}.", envName, dataId, group, tenant, configFuzzyWatcher); job.run(); } } catch (Throwable t) { LOGGER.error("[{}] [notify-fuzzy-watcher-error] dataId={}, group={}, tenant={}, listener={}, throwable={}.", envName, dataId, group, tenant, configFuzzyWatcher, t.getCause()); } } /** * Mark initialization as complete and notify waiting threads. */ public void markInitializationComplete() { initializationCompleted.set(true); synchronized (this) { this.notifyAll(); } } /** * Remove a watcher from the context. * * @param watcher watcher to be removed */ public void removeWatcher(FuzzyWatchEventWatcher watcher) { Iterator iterator = configFuzzyWatcherWrappers.iterator(); while (iterator.hasNext()) { ConfigFuzzyWatcherWrapper next = iterator.next(); if (next.fuzzyWatchEventWatcher.equals(watcher)) { iterator.remove(); LOGGER.info("[{}] [remove-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, watcher, next.getUuid()); } } } /** * Add a watcher to the context. * * @param configFuzzyWatcherWrapper watcher to be added */ public boolean addWatcher(ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper) { boolean added = configFuzzyWatcherWrappers.add(configFuzzyWatcherWrapper); if (added) { LOGGER.info("[{}] [add-fuzzy-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, configFuzzyWatcherWrapper.fuzzyWatchEventWatcher, configFuzzyWatcherWrapper.getUuid()); } return added; } /** * Get the environment name. * * @return Environment name */ public String getEnvName() { return envName; } /** * Set the environment name. * * @param envName Environment name to be set */ public void setEnvName(String envName) { this.envName = envName; } /** * Get the task ID. * * @return Task ID */ public int getTaskId() { return taskId; } /** * Set the task ID. * * @param taskId Task ID to be set */ public void setTaskId(int taskId) { this.taskId = taskId; } public String getGroupKeyPattern() { return groupKeyPattern; } /** * Get the flag indicating whether the context is consistent with the server. * * @return AtomicBoolean indicating whether the context is consistent with the server */ public boolean isConsistentWithServer() { return isConsistentWithServer.get(); } public void setConsistentWithServer(boolean isConsistentWithServer) { this.isConsistentWithServer.set(isConsistentWithServer); } /** * Check if the context is discarded. * * @return True if the context is discarded, otherwise false */ public boolean isDiscard() { return isDiscard; } /** * Set the flag indicating whether the context is discarded. * * @param discard True to mark the context as discarded, otherwise false */ public void setDiscard(boolean discard) { isDiscard = discard; } /** * Check if the context is initializing. * * @return True if the context is initializing, otherwise false */ public boolean isInitializing() { return !initializationCompleted.get(); } public int getReceivedGroupKeysCount() { return receivedGroupKeys.size(); } /** * Get the set of data IDs associated with the context. zw * * @return Set of data IDs */ public Set getReceivedGroupKeys() { return Collections.unmodifiableSet(receivedGroupKeys); } public void refreshSyncVersion() { this.syncVersion = System.currentTimeMillis(); } /** * add receive group key. * @param groupKey group key. * @return */ public boolean addReceivedGroupKey(String groupKey) { boolean added = receivedGroupKeys.add(groupKey); if (added) { refreshSyncVersion(); } return added; } /** * remove receive group key. * @param groupKey group key. * @return */ public boolean removeReceivedGroupKey(String groupKey) { boolean removed = receivedGroupKeys.remove(groupKey); if (removed) { refreshSyncVersion(); } return removed; } /** * Get the set of listeners associated with the context. * * @return Set of listeners */ public Set getConfigFuzzyWatcherWrappers() { return configFuzzyWatcherWrappers; } /** * Abstract task for fuzzy notification. */ abstract static class AbstractFuzzyNotifyTask implements Runnable { /** * Flag indicating whether the task is asynchronous. */ boolean async = false; /** * Check if the task is asynchronous. * * @return True if the task is asynchronous, otherwise false */ public boolean isAsync() { return async; } } void syncFuzzyWatchers() { for (ConfigFuzzyWatcherWrapper configFuzzyWatcher : configFuzzyWatcherWrappers) { if (configFuzzyWatcher.syncVersion == this.syncVersion) { continue; } Set receivedGroupKeysContext = new HashSet<>(getReceivedGroupKeys()); Set syncGroupKeys = configFuzzyWatcher.getSyncGroupKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( receivedGroupKeysContext, syncGroupKeys); if (CollectionUtils.isEmpty(groupKeyStates)) { configFuzzyWatcher.syncVersion = this.syncVersion; } else { for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { String changedType = groupKeyState.isExist() ? ADD_CONFIG : DELETE_CONFIG; doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, configFuzzyWatcher); } } } } /** * creat a new future of this context. * * @return */ public Future> createNewFuture() { Future> future = new Future>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { throw new UnsupportedOperationException("not support to cancel fuzzy watch"); } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return ConfigFuzzyWatchContext.this.initializationCompleted.get(); } @Override public Set get() throws InterruptedException, ExecutionException { if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { synchronized (ConfigFuzzyWatchContext.this) { ConfigFuzzyWatchContext.this.wait(); } } return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); } public Set get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { synchronized (ConfigFuzzyWatchContext.this) { ConfigFuzzyWatchContext.this.wait(unit.toMillis(timeout)); } } if (!ConfigFuzzyWatchContext.this.initializationCompleted.get()) { throw new TimeoutException( "fuzzy watch result future timeout for " + unit.toMillis(timeout) + " millis"); } return new HashSet<>(ConfigFuzzyWatchContext.this.getReceivedGroupKeys()); } }; return future; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolder.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchChangeNotifyResponse; import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchSyncResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import org.slf4j.Logger; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.CONFIG_CHANGED; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * config fuzzy watch context holder. * * @author shiyiyue */ public class ConfigFuzzyWatchGroupKeyHolder extends SmartSubscriber implements Closeable { private static final Logger LOGGER = LogUtils.logger(ClientWorker.class); private final ClientWorker.ConfigRpcTransportClient agent; private final String clientUuid; /** * fuzzyListenExecuteBell. */ private final BlockingQueue fuzzyListenExecuteBell = new ArrayBlockingQueue<>(1); private final Object bellItem = new Object(); private final AtomicLong fuzzyListenLastAllSyncTime = new AtomicLong(System.currentTimeMillis()); private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 3 * 60 * 1000; private ExecutorService fuzzyWatcherExecutor; private String taskId = "0"; /** * fuzzyListenGroupKey -> fuzzyListenContext. */ private final AtomicReference> fuzzyListenContextMap = new AtomicReference<>( new HashMap<>()); public ConfigFuzzyWatchGroupKeyHolder(ClientWorker.ConfigRpcTransportClient agent, String clientUuid) { this.clientUuid = clientUuid; this.agent = agent; NotifyCenter.registerSubscriber(this); } /** * start. */ public void start() { fuzzyWatcherExecutor = Executors.newSingleThreadScheduledExecutor( new NameThreadFactory("com.alibaba.nacos.client.fuzzy-watcher-executor") ); fuzzyWatcherExecutor.submit(() -> { while (!fuzzyWatcherExecutor.isShutdown() && !fuzzyWatcherExecutor.isTerminated()) { try { fuzzyListenExecuteBell.poll(5L, TimeUnit.SECONDS); if (fuzzyWatcherExecutor.isShutdown() || fuzzyWatcherExecutor.isTerminated()) { continue; } executeConfigFuzzyListen(); } catch (Throwable e) { LOGGER.error("[rpc-fuzzy-listen-execute] rpc fuzzy listen exception", e); try { Thread.sleep(500L); } catch (InterruptedException interruptedException) { //ignore } notifyFuzzyWatchSync(); } } }); } /** * Deregistering it from the NotifyCenter and shutting down the executor. */ @Override public void shutdown() { // deregister subscriber which registered in constructor NotifyCenter.deregisterSubscriber(this); if (fuzzyWatcherExecutor != null && !fuzzyWatcherExecutor.isShutdown()) { fuzzyWatcherExecutor.shutdown(); } } /** * Removes the fuzzy listen context for the specified data ID pattern and group. * * @param groupKeyPattern The pattern of the data ID. */ public void removeFuzzyListenContext(String groupKeyPattern) { synchronized (fuzzyListenContextMap) { Map copy = new HashMap<>(fuzzyListenContextMap.get()); copy.remove(groupKeyPattern); fuzzyListenContextMap.set(copy); } LOGGER.info("[{}] [fuzzy-watch-unsubscribe] {}", agent.getName(), groupKeyPattern); } /** * register fuzzy watcher. * * @param dataIdPattern dataIdPattern. * @param groupPattern groupPattern. * @param fuzzyWatchEventWatcher fuzzyWatchEventWatcher. * @return ConfigFuzzyWatchContext */ public ConfigFuzzyWatchContext registerFuzzyWatcher(String dataIdPattern, String groupPattern, FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { if (!agent.isAbilitySupportedByServer(AbilityKey.SERVER_FUZZY_WATCH)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support fuzzy watch feature."); } ConfigFuzzyWatchContext configFuzzyWatchContext = initFuzzyWatchContextIfAbsent(dataIdPattern, groupPattern); ConfigFuzzyWatcherWrapper configFuzzyWatcherWrapper = new ConfigFuzzyWatcherWrapper(fuzzyWatchEventWatcher); if (configFuzzyWatchContext.addWatcher(configFuzzyWatcherWrapper)) { if (configFuzzyWatchContext.getReceivedGroupKeys() != null) { for (String groupKey : configFuzzyWatchContext.getReceivedGroupKeys()) { ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = ConfigFuzzyWatchNotifyEvent.buildEvent( groupKey, configFuzzyWatchContext.getGroupKeyPattern(), ADD_CONFIG, FUZZY_WATCH_INIT_NOTIFY, this.clientUuid, configFuzzyWatcherWrapper.getUuid()); NotifyCenter.publishEvent(configFuzzyWatchNotifyEvent); } } } return configFuzzyWatchContext; } /** * Retrieves the FuzzyListenContext for the given data ID pattern and group. * * @param dataIdPattern The data ID pattern. * @param groupPattern The group name pattern. * @return The corresponding FuzzyListenContext, or null if not found. */ public ConfigFuzzyWatchContext getFuzzyListenContext(String dataIdPattern, String groupPattern) { return fuzzyListenContextMap.get() .get(FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern, agent.getTenant())); } /** * Handles a fuzzy listen init notify request. * *

    This method processes the incoming fuzzy listen init notify request from a client. It updates the fuzzy * listen context based on the request's information, and publishes events if necessary. * * @param request The fuzzy listen init notify request to handle. * @return A {@link ConfigFuzzyWatchSyncResponse} indicating the result of handling the request. */ ConfigFuzzyWatchSyncResponse handleFuzzyWatchSyncNotifyRequest(ConfigFuzzyWatchSyncRequest request) { String groupKeyPattern = request.getGroupKeyPattern(); ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(groupKeyPattern); if (Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY.equals(request.getSyncType())) { LOGGER.info("[{}] [fuzzy-watch] init-notify-finished, pattern ->{}, match group keys count {}", agent.getName(), request.getGroupKeyPattern(), context.getReceivedGroupKeysCount()); context.markInitializationComplete(); return new ConfigFuzzyWatchSyncResponse(); } LOGGER.info( "[{}] [fuzzy-watch] sync notify , pattern ->{},syncType={},,syncCount={},totalBatch={},currentBatch={}", agent.getName(), request.getGroupKeyPattern(), request.getSyncType(), request.getContexts().size(), request.getTotalBatch(), request.getCurrentBatch()); for (ConfigFuzzyWatchSyncRequest.Context requestContext : request.getContexts()) { switch (requestContext.getChangedType()) { case ADD_CONFIG: if (context.addReceivedGroupKey(requestContext.getGroupKey())) { LOGGER.info("[{}] [fuzzy-watch-diff-sync-push] local match group key added ,pattern ->{}, " + "group key ->{},publish fuzzy watch notify event", agent.getName(), request.getGroupKeyPattern(), requestContext.getGroupKey()); NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildEvent(requestContext.getGroupKey(), request.getGroupKeyPattern(), requestContext.getChangedType(), request.getSyncType(), this.clientUuid)); } break; case DELETE_CONFIG: if (context.removeReceivedGroupKey(requestContext.getGroupKey())) { LOGGER.info("[{}] [fuzzy-watch-diff-sync-push] local match group key remove ,pattern ->{}, " + "group key ->{},publish fuzzy watch notify event", agent.getName(), request.getGroupKeyPattern(), requestContext.getGroupKey()); NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildEvent(requestContext.getGroupKey(), request.getGroupKeyPattern(), requestContext.getChangedType(), request.getSyncType(), this.clientUuid)); } break; default: LOGGER.warn("Invalid config change type: {}", requestContext.getChangedType()); break; } } return new ConfigFuzzyWatchSyncResponse(); } /** * Removes a fuzzy listen listener for the specified data ID pattern, group, and listener. * * @param dataIdPattern The pattern of the data ID. * @param groupPattern The group of the configuration. * @param watcher The listener to remove. * @throws NacosException If an error occurs while removing the listener. */ public void removeFuzzyWatcher(String dataIdPattern, String groupPattern, FuzzyWatchEventWatcher watcher) { ConfigFuzzyWatchContext configFuzzyWatchContext = getFuzzyListenContext(dataIdPattern, groupPattern); if (configFuzzyWatchContext != null) { synchronized (configFuzzyWatchContext) { configFuzzyWatchContext.removeWatcher(watcher); if (configFuzzyWatchContext.getConfigFuzzyWatcherWrappers().isEmpty()) { configFuzzyWatchContext.setDiscard(true); configFuzzyWatchContext.setConsistentWithServer(false); } } } } /** * Handles a fuzzy listen notify change request. * *

    This method processes the incoming fuzzy listen notify change request from a client. It updates the fuzzy * listen context based on the request's information, and publishes events if necessary. * * @param request The fuzzy listen notify change request to handle. */ ConfigFuzzyWatchChangeNotifyResponse handlerFuzzyWatchChangeNotifyRequest( ConfigFuzzyWatchChangeNotifyRequest request) { LOGGER.info("[{}] [fuzzy-watch-change-notify-push] changeType={},groupKey={}", agent.getName(), request.getChangeType(), request.getGroupKey()); Map listenContextMap = fuzzyListenContextMap.get(); String[] groupItems = GroupKey.parseKey(request.getGroupKey()); Set matchedPatterns = FuzzyGroupKeyPattern.filterMatchedPatterns(listenContextMap.keySet(), groupItems[0], groupItems[1], groupItems[2]); for (String matchedPattern : matchedPatterns) { ConfigFuzzyWatchContext context = listenContextMap.get(matchedPattern); if (ADD_CONFIG.equals(request.getChangeType()) || CONFIG_CHANGED.equals(request.getChangeType())) { if (context.addReceivedGroupKey(request.getGroupKey())) { LOGGER.info("[{}] [fuzzy-watch-change-notify-push] match group key added ,pattern={},groupKey={}", agent.getName(), request.getChangeType(), request.getGroupKey()); NotifyCenter.publishEvent( ConfigFuzzyWatchNotifyEvent.buildEvent(request.getGroupKey(), matchedPattern, ADD_CONFIG, FUZZY_WATCH_RESOURCE_CHANGED, this.clientUuid)); } } else if (DELETE_CONFIG.equals(request.getChangeType()) && context.removeReceivedGroupKey( request.getGroupKey())) { NotifyCenter.publishEvent(ConfigFuzzyWatchNotifyEvent.buildEvent(request.getGroupKey(), matchedPattern, Constants.ConfigChangedType.DELETE_CONFIG, FUZZY_WATCH_RESOURCE_CHANGED, this.clientUuid)); } } return new ConfigFuzzyWatchChangeNotifyResponse(); } void notifyFuzzyWatchSync() { fuzzyListenExecuteBell.offer(bellItem); } /** * Execute fuzzy listen configuration changes. * *

    This method iterates through all fuzzy listen contexts and determines whether they need to be added or * removed based on their consistency with the server and discard status. It then calls the appropriate method to * execute the fuzzy listen operation. * * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ public void executeConfigFuzzyListen() throws NacosException { // Obtain the current timestamp long now = System.currentTimeMillis(); // Determine whether a full synchronization is needed boolean needAllSync = now - fuzzyListenLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; List needSyncContexts = new ArrayList<>(); // Iterate through all fuzzy listen contexts for (ConfigFuzzyWatchContext context : fuzzyListenContextMap.get().values()) { // Check if the context is consistent with the server if (context.isConsistentWithServer()) { context.syncFuzzyWatchers(); // Skip if a full synchronization is not needed if (!needAllSync) { continue; } } needSyncContexts.add(context); } // Execute fuzzy listen operation for addition doExecuteConfigFuzzyListen(needSyncContexts); // Update last all sync time if a full synchronization was performed if (needAllSync) { fuzzyListenLastAllSyncTime.set(now); } } void resetConsistenceStatus() { Collection configFuzzyWatchContexts = fuzzyListenContextMap.get().values(); for (ConfigFuzzyWatchContext context : configFuzzyWatchContexts) { context.setConsistentWithServer(false); } } /** * Execute fuzzy listen configuration changes for a specific map of contexts. * *

    This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It * waits for all tasks to complete and logs any errors that occur. * * @param contextLists The map of contexts to execute fuzzy listen operations for. * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ private void doExecuteConfigFuzzyListen(List contextLists) throws NacosException { // Return if the context map is null or empty if (CollectionUtils.isEmpty(contextLists)) { return; } // List to hold futures for asynchronous tasks List> listenFutures = new ArrayList<>(); RpcClient rpcClient = agent.ensureRpcClient(taskId); // Iterate through the context map and submit tasks for execution for (ConfigFuzzyWatchContext context : contextLists) { ExecutorService executorService = agent.getExecutor(); // Submit task for execution Future future = executorService.submit(() -> executeFuzzyWatchRequest(context, rpcClient)); listenFutures.add(future); } // Wait for all tasks to complete for (Future future : listenFutures) { try { future.get(); } catch (Throwable throwable) { // Log async listen error LOGGER.error("Async fuzzy listen config change error.", throwable); } } } void executeFuzzyWatchRequest(ConfigFuzzyWatchContext context, RpcClient rpcClient) { ConfigFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyListenConfigRequest(context); try { // Execute the fuzzy listen operation ConfigFuzzyWatchResponse listenResponse = (ConfigFuzzyWatchResponse) agent.requestProxy(rpcClient, configFuzzyWatchRequest); if (listenResponse != null && listenResponse.isSuccess()) { if (context.isDiscard()) { removeFuzzyListenContext(context.getGroupKeyPattern()); } else { context.setConsistentWithServer(true); } context.clearOverLimitTs(); } else if (listenResponse != null) { if (handleOverLoadEvent(context.getGroupKeyPattern(), listenResponse.getErrorCode())) { return; } LOGGER.error("Execute fuzzy watch config change error,code={},msg={}", listenResponse.getErrorCode(), listenResponse.getMessage()); } } catch (NacosException e) { if (handleOverLoadEvent(context.getGroupKeyPattern(), e.getErrCode())) { return; } // Log error and retry after a short delay LOGGER.error("Execute fuzzy watch config change error.", e); // Retry notification notifyFuzzyWatchSync(); } } private boolean handleOverLoadEvent(String pattern, int errorCode) { if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == errorCode || FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode() == errorCode) { LOGGER.warn(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed", pattern); NotifyCenter.publishEvent(ConfigFuzzyWatchLoadEvent.buildEvent(errorCode, pattern, this.clientUuid)); return true; } return false; } /** * Builds a request for fuzzy listen configuration. * * @param context The list of fuzzy listen contexts. * @return A {@code ConfigBatchFuzzyListenRequest} object representing the request. */ private ConfigFuzzyWatchRequest buildFuzzyListenConfigRequest(ConfigFuzzyWatchContext context) { ConfigFuzzyWatchRequest request = new ConfigFuzzyWatchRequest(); request.setGroupKeyPattern(context.getGroupKeyPattern()); request.setInitializing(context.isInitializing()); request.setWatchType((context.isDiscard() && CollectionUtils.isEmpty(context.getConfigFuzzyWatcherWrappers())) ? WATCH_TYPE_CANCEL_WATCH : WATCH_TYPE_WATCH); request.setReceivedGroupKeys(context.getReceivedGroupKeys()); return request; } /** * Adds a fuzzy listen context if it doesn't already exist for the specified data ID pattern and group. If the * context already exists, returns the existing context. * * @param dataIdPattern The pattern of the data ID. * @param groupPattern The group of the configuration. * @return The fuzzy listen context for the specified data ID pattern and group. */ private ConfigFuzzyWatchContext initFuzzyWatchContextIfAbsent(String dataIdPattern, String groupPattern) { ConfigFuzzyWatchContext context = getFuzzyListenContext(dataIdPattern, groupPattern); if (context != null) { return context; } synchronized (fuzzyListenContextMap) { ConfigFuzzyWatchContext contextFromMap = getFuzzyListenContext(dataIdPattern, groupPattern); if (contextFromMap != null) { context = contextFromMap; } else { String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupPattern, agent.getTenant()); context = new ConfigFuzzyWatchContext(agent.getName(), groupKeyPattern); context.setConsistentWithServer(false); Map copy = new HashMap<>(fuzzyListenContextMap.get()); copy.put(groupKeyPattern, context); LOGGER.info("[{}][fuzzy-watch] init fuzzy watch context , groupKeyPattern={} ,notify fuzzy watch sync ", agent.getName(), groupKeyPattern); fuzzyListenContextMap.set(copy); notifyFuzzyWatchSync(); } } return context; } @Override public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(ConfigFuzzyWatchNotifyEvent.class); result.add(ConfigFuzzyWatchLoadEvent.class); return result; } @Override public void onEvent(Event event) { if (event instanceof ConfigFuzzyWatchNotifyEvent) { ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = (ConfigFuzzyWatchNotifyEvent) event; if (!configFuzzyWatchNotifyEvent.getClientUuid().equals(clientUuid)) { return; } ConfigFuzzyWatchContext context = fuzzyListenContextMap.get() .get(configFuzzyWatchNotifyEvent.getGroupKeyPattern()); if (context == null) { return; } context.notifyWatcher(configFuzzyWatchNotifyEvent.getGroupKey(), configFuzzyWatchNotifyEvent.getChangedType(), configFuzzyWatchNotifyEvent.getSyncType(), configFuzzyWatchNotifyEvent.getWatcherUuid()); } if (event instanceof ConfigFuzzyWatchLoadEvent) { ConfigFuzzyWatchLoadEvent loadEvent = (ConfigFuzzyWatchLoadEvent) event; //instance check if (!loadEvent.getClientUuid().equals(clientUuid)) { return; } ConfigFuzzyWatchContext context = fuzzyListenContextMap.get().get(loadEvent.getGroupKeyPattern()); if (context == null) { return; } context.notifyLoaderWatcher(loadEvent.getCode()); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchLoadEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.common.notify.Event; /** * event published when a fuzzy watch pattern being suppressed because of pattern count or pattern matched config count is over limit. * @author shiyiyue * @date 2025/01/13 */ public class ConfigFuzzyWatchLoadEvent extends Event { private String clientUuid; /** * The groupKeyPattern of configuration. */ private String groupKeyPattern; private int code; /** * Constructs a new ConfigFuzzyWatchLoadEvent. */ public ConfigFuzzyWatchLoadEvent() { } /** * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * * @param code The type of notification. * @param groupKeyPattern The groupKeyPattern of notification. */ private ConfigFuzzyWatchLoadEvent(int code, String groupKeyPattern, String clientUuid) { this.code = code; this.groupKeyPattern = groupKeyPattern; this.clientUuid = clientUuid; } /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * * @param groupKeyPattern The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ public static ConfigFuzzyWatchLoadEvent buildEvent(int code, String groupKeyPattern, String clientUuid) { return new ConfigFuzzyWatchLoadEvent(code, groupKeyPattern, clientUuid); } public String getClientUuid() { return clientUuid; } public String getGroupKeyPattern() { return groupKeyPattern; } public int getCode() { return code; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchNotifyEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.common.notify.Event; /** * Event class for fuzzy listen notifications. * *

    This class represents an event used for notifying fuzzy listen changes. It extends {@link Event}, indicating * that it may be processed asynchronously. The event contains information about the group, dataId, type, and UUID of * the notification. * * @author stone-98 * @date 2024/3/4 */ public class ConfigFuzzyWatchNotifyEvent extends Event { private String clientUuid; /** * The uuid of this watcher for which that this notify event . */ private String watcherUuid; /** * The groupKeyPattern of configuration. */ private String groupKeyPattern; private String groupKey; /** * The type of notification (e.g., ADD_CONFIG, DELETE_CONFIG). */ private String changedType; private String syncType; /** * Constructs a new FuzzyListenNotifyEvent. */ public ConfigFuzzyWatchNotifyEvent() { } /** * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * * @param groupKey The groupKey of the configuration. * @param changedType The type of notification. */ private ConfigFuzzyWatchNotifyEvent(String groupKey, String changedType, String syncType, String groupKeyPattern, String clientUuid, String watcherUuid) { this.groupKey = groupKey; this.syncType = syncType; this.changedType = changedType; this.groupKeyPattern = groupKeyPattern; this.clientUuid = clientUuid; this.watcherUuid = watcherUuid; } /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ public static ConfigFuzzyWatchNotifyEvent buildEvent(String groupKey, String groupKeyPattern, String changedType, String syncType, String clientUuid) { return buildEvent(groupKey, groupKeyPattern, changedType, syncType, clientUuid, null); } /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * * @param groupKey The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ public static ConfigFuzzyWatchNotifyEvent buildEvent(String groupKey, String groupKeyPattern, String changedType, String syncType, String clientUuid, String watcherUuid) { ConfigFuzzyWatchNotifyEvent configFuzzyWatchNotifyEvent = new ConfigFuzzyWatchNotifyEvent(groupKey, changedType, syncType, groupKeyPattern, clientUuid, watcherUuid); return configFuzzyWatchNotifyEvent; } /** * Gets the UUID (Unique Identifier) of the listener. * * @return The UUID of the listener. */ public String getWatcherUuid() { return watcherUuid; } public String getClientUuid() { return clientUuid; } public String getGroupKeyPattern() { return groupKeyPattern; } public String getGroupKey() { return groupKey; } public String getSyncType() { return syncType; } /** * Gets the type of notification. * * @return The type of notification. */ public String getChangedType() { return changedType; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatcherWrapper.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.UUID; /** * ConfigFuzzyWatcherWrapper. * * @author shiyiyue */ public class ConfigFuzzyWatcherWrapper { long syncVersion = 0; FuzzyWatchEventWatcher fuzzyWatchEventWatcher; public ConfigFuzzyWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { this.fuzzyWatchEventWatcher = fuzzyWatchEventWatcher; } /** * Unique identifier for the listener. */ String uuid = UUID.randomUUID().toString(); private Set syncGroupKeys = new HashSet<>(); @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigFuzzyWatcherWrapper that = (ConfigFuzzyWatcherWrapper) o; return Objects.equals(fuzzyWatchEventWatcher, that.fuzzyWatchEventWatcher) && Objects.equals(uuid, that.uuid); } @Override public int hashCode() { return Objects.hash(fuzzyWatchEventWatcher, uuid); } Set getSyncGroupKeys() { return syncGroupKeys; } /** * Get the UUID (Unique Identifier) of the listener. * * @return The UUID of the listener */ String getUuid() { return uuid; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigHttpClientManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.client.utils.ParamUtil; import com.alibaba.nacos.common.http.AbstractHttpClientFactory; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpClientFactory; import com.alibaba.nacos.common.http.client.HttpClientRequestInterceptor; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.model.RequestHttpEntity; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import org.slf4j.Logger; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.List; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * config http Manager. * * @author mai.jh */ public class ConfigHttpClientManager implements Closeable { private static final Logger LOGGER = LogUtils.logger(ConfigHttpClientManager.class); private static final HttpClientFactory HTTP_CLIENT_FACTORY = new ConfigHttpClientFactory(); private static final int CON_TIME_OUT_MILLIS = ParamUtil.getConnectTimeout(); private static final int READ_TIME_OUT_MILLIS = ParamUtil.getReadTimeout(); private final LimiterHttpClientRequestInterceptor limiterHttpClientRequestInterceptor = new LimiterHttpClientRequestInterceptor(); private static class ConfigHttpClientManagerInstance { private static final ConfigHttpClientManager INSTANCE = new ConfigHttpClientManager(); } public static ConfigHttpClientManager getInstance() { return ConfigHttpClientManagerInstance.INSTANCE; } @Override public void shutdown() throws NacosException { NAMING_LOGGER.info("[ConfigHttpClientManager] Start destroying NacosRestTemplate"); try { HttpClientBeanHolder.shutdownNacosSyncRest(HTTP_CLIENT_FACTORY.getClass().getName()); } catch (Exception ex) { NAMING_LOGGER.error("[ConfigHttpClientManager] An exception occurred when the HTTP client was closed : {}", ExceptionUtil.getStackTrace(ex)); } NAMING_LOGGER.info("[ConfigHttpClientManager] Completed destruction of NacosRestTemplate"); } /** * get connectTimeout. * * @param connectTimeout connectTimeout * @return int return max timeout */ public int getConnectTimeoutOrDefault(int connectTimeout) { return Math.max(CON_TIME_OUT_MILLIS, connectTimeout); } /** * get NacosRestTemplate Instance. * * @return NacosRestTemplate */ public NacosRestTemplate getNacosRestTemplate() { NacosRestTemplate nacosRestTemplate = HttpClientBeanHolder.getNacosRestTemplate(HTTP_CLIENT_FACTORY); List interceptors = nacosRestTemplate.getInterceptors(); if (!interceptors.contains(limiterHttpClientRequestInterceptor)) { interceptors.add(limiterHttpClientRequestInterceptor); } return nacosRestTemplate; } /** * ConfigHttpClientFactory. */ private static class ConfigHttpClientFactory extends AbstractHttpClientFactory { @Override protected HttpClientConfig buildHttpClientConfig() { return HttpClientConfig.builder().setConTimeOutMillis(CON_TIME_OUT_MILLIS) .setReadTimeOutMillis(READ_TIME_OUT_MILLIS).build(); } @Override protected Logger assignLogger() { return LOGGER; } } /** * config Limiter implement. */ private static class LimiterHttpClientRequestInterceptor implements HttpClientRequestInterceptor { @Override public boolean isIntercept(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity) { final String body = requestHttpEntity.isEmptyBody() ? "" : JacksonUtils.toJson(requestHttpEntity.getBody()); return Limiter.isLimit(MD5Utils.md5Hex(uri + body, Constants.ENCODE)); } @Override public HttpClientResponse intercept() { return new LimitResponse(); } } /** * Limit Interrupt response. */ private static class LimitResponse implements HttpClientResponse { @Override public Header getHeaders() { return Header.EMPTY; } @Override public InputStream getBody() throws IOException { return new ByteArrayInputStream("More than client-side current limit threshold".getBytes(StandardCharsets.UTF_8)); } @Override public int getStatusCode() { return NacosException.CLIENT_OVER_THRESHOLD; } @Override public String getStatusText() { return null; } @Override public void close() { } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigServerListManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Random; /** * Config server list Manager. * * @author Nacos */ public class ConfigServerListManager extends AbstractServerListManager { private static final Logger LOGGER = LogUtils.logger(ConfigServerListManager.class); /** * The name of the different environment. */ private String name; private String tenant = ""; private volatile String currentServerAddr; private Iterator iterator; public ConfigServerListManager(NacosClientProperties properties) { super(properties); String namespace = properties.getProperty(PropertyKeyConst.NAMESPACE); if (StringUtils.isNotBlank(namespace)) { this.tenant = namespace; } } @Override protected String getModuleName() { return "Config"; } @Override protected NacosRestTemplate getNacosRestTemplate() { return ConfigHttpClientManager.getInstance().getNacosRestTemplate(); } @Override public void start() throws NacosException { super.start(); this.name = initServerName(properties); iterator = iterator(); currentServerAddr = iterator.next(); } private String initServerName(NacosClientProperties properties) { String serverName; //1.user define server name. if (properties.containsKey(PropertyKeyConst.SERVER_NAME)) { serverName = properties.getProperty(PropertyKeyConst.SERVER_NAME); } else { serverName = getServerName(); } serverName = serverName.replaceAll("\\/", "_"); serverName = serverName.replaceAll("\\:", "_"); return serverName; } Iterator iterator() { List serverList = getServerList(); if (serverList.isEmpty()) { LOGGER.error("[{}] [iterator-serverlist] No server address defined!", name); } return new ServerAddressIterator(serverList); } @Override public String genNextServer() { if (iterator == null || !iterator.hasNext()) { refreshCurrentServerAddr(); return currentServerAddr; } try { return iterator.next(); } catch (Exception ignored) { } refreshCurrentServerAddr(); return currentServerAddr; } @Override public String getCurrentServer() { if (StringUtils.isBlank(currentServerAddr)) { iterator = iterator(); currentServerAddr = iterator.next(); } return currentServerAddr; } public String getUrlString() { return getServerList().toString(); } @Override public String toString() { return "ServerManager-" + name + "-" + getUrlString(); } public boolean contain(String ip) { return getServerList().contains(ip); } public void refreshCurrentServerAddr() { iterator = iterator(); currentServerAddr = iterator.next(); } public void updateCurrentServerAddr(String currentServerAddr) { this.currentServerAddr = currentServerAddr; } public Iterator getIterator() { return iterator; } public String getName() { return name; } public String getTenant() { return tenant; } /** * Sort the address list, with the same room priority. */ private static class ServerAddressIterator implements Iterator { static class RandomizedServerAddress implements Comparable { static Random random = new Random(); String serverIp; int seed; public RandomizedServerAddress(String ip) { this.serverIp = ip; /* change random scope from 32 to Integer.MAX_VALUE to fix load balance issue */ this.seed = random.nextInt(Integer.MAX_VALUE); } @Override public int compareTo(RandomizedServerAddress other) { return other.seed - this.seed; } } final List sorted; final Iterator iter; public ServerAddressIterator(List source) { sorted = new ArrayList<>(); for (String address : source) { sorted.add(new RandomizedServerAddress(address)); } Collections.sort(sorted); iter = sorted.iterator(); } @Override public boolean hasNext() { return iter.hasNext(); } @Override public String next() { return iter.next().serverIp; } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/ConfigTransportClient.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.RequestResource; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * config transport client,include basic operations of config module. * * @author liuzunfei * @version $Id: ConfigTransportClient.java, v 0.1 2020年08月24日 2:01 PM liuzunfei Exp $ */ public abstract class ConfigTransportClient { private static final String CONFIG_INFO_HEADER = "exConfigInfo"; private static final String DEFAULT_CONFIG_INFO = "true"; String encode; String tenant; private ThreadPoolExecutor executor; final ConfigServerListManager serverListManager; final Properties properties; private int maxRetry = 3; private final long securityInfoRefreshIntervalMills = TimeUnit.SECONDS.toMillis(5); private ScheduledExecutorService loginScheduledExecutor; protected SecurityProxy securityProxy; /** * Shut down to ensure resource release. */ public void shutdown() throws NacosException { securityProxy.shutdown(); if (loginScheduledExecutor != null && !loginScheduledExecutor.isShutdown()) { loginScheduledExecutor.shutdown(); } } public ConfigTransportClient(NacosClientProperties properties, ConfigServerListManager serverListManager) { String encodeTmp = properties.getProperty(PropertyKeyConst.ENCODE); if (StringUtils.isBlank(encodeTmp)) { this.encode = Constants.ENCODE; } else { this.encode = encodeTmp.trim(); } this.tenant = properties.getProperty(PropertyKeyConst.NAMESPACE); this.serverListManager = serverListManager; this.properties = properties.asProperties(); this.securityProxy = new SecurityProxy(serverListManager, ConfigHttpClientManager.getInstance().getNacosRestTemplate()); } /** * Build the resource for current request. * * @param tenant tenant of config * @param group group of config * @param dataId dataId of config * @return resource */ protected RequestResource buildResource(String tenant, String group, String dataId) { return RequestResource.configBuilder().setNamespace(tenant).setGroup(group).setResource(dataId).build(); } protected Map getSecurityHeaders(RequestResource resource) throws Exception { return securityProxy.getIdentityContext(resource); } /** * get common header. * * @return headers. */ protected Map getCommonHeader() { Map headers = new HashMap<>(16); String ts = String.valueOf(System.currentTimeMillis()); String token = MD5Utils.md5Hex(ts + ClientBasicParamUtil.getAppKey(), Constants.ENCODE); headers.put(Constants.CLIENT_APPNAME_HEADER, AppNameUtils.getAppName()); headers.put(Constants.CLIENT_REQUEST_TS_HEADER, ts); headers.put(Constants.CLIENT_REQUEST_TOKEN_HEADER, token); headers.put(CONFIG_INFO_HEADER, DEFAULT_CONFIG_INFO); headers.put(Constants.CHARSET_KEY, encode); return headers; } private void initMaxRetry(Properties properties) { maxRetry = ConvertUtils.toInt(String.valueOf(properties.get(PropertyKeyConst.MAX_RETRY)), Constants.MAX_RETRY); } public void setExecutor(ThreadPoolExecutor executor) { this.executor = executor; } public ThreadPoolExecutor getExecutor() { return this.executor; } /** * base start client. */ public void start() throws NacosException { securityProxy.login(this.properties); this.loginScheduledExecutor = Executors.newSingleThreadScheduledExecutor(new NameThreadFactory("com.alibaba.nacos.client.login-executor")); this.loginScheduledExecutor.scheduleWithFixedDelay(() -> securityProxy.login(properties), 0, this.securityInfoRefreshIntervalMills, TimeUnit.MILLISECONDS); startInternal(); } public void reLogin() { securityProxy.reLogin(); } /** * start client inner. * * @throws NacosException exception may throw. */ public abstract void startInternal() throws NacosException; /** * get client name. * * @return name. */ public abstract String getName(); /** * get encode. * * @return encode. */ public String getEncode() { return this.encode; } /** * get tenant. * * @return tenant. */ public String getTenant() { return this.tenant; } /** * notify listen config. **/ public abstract void notifyListenConfig(); /** * listen change . * * @throws NacosException nacos exception throws, should retry. */ public abstract void executeConfigListen() throws NacosException; /** * remove cache implements. * * @param dataId dataId. * @param group group */ public abstract void removeCache(String dataId, String group); /** * query config. * * @param dataId dataId. * @param group group. * @param tenat tenat. * @param readTimeous readTimeous. * @param notify query for notify sync. * @return content. * @throws NacosException throw where query fail . */ public abstract ConfigResponse queryConfig(String dataId, String group, String tenat, long readTimeous, boolean notify) throws NacosException; /** * publish config. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param appName appName. * @param tag tag. * @param betaIps betaIps. * @param content content. * @param encryptedDataKey encryptedDataKey * @param casMd5 casMd5. * @param type type. * @return success or not. * @throws NacosException throw where publish fail. */ public abstract boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, String betaIps, String content, String encryptedDataKey, String casMd5, String type) throws NacosException; /** * remove config. * * @param dataid dataid. * @param group group. * @param tenat tenat. * @param tag tag. * @return success or not. * @throws NacosException throw where publish fail. */ public abstract boolean removeConfig(String dataid, String group, String tenat, String tag) throws NacosException; } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/Limiter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.LogUtils; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.google.common.util.concurrent.RateLimiter; import org.slf4j.Logger; import java.util.concurrent.TimeUnit; /** * Limiter. * * @author Nacos */ public class Limiter { private static final Logger LOGGER = LogUtils.logger(Limiter.class); private static final int CAPACITY_SIZE = 1000; private static final int LIMIT_TIME = 1000; private static final Cache CACHE = CacheBuilder.newBuilder().initialCapacity(CAPACITY_SIZE) .expireAfterAccess(1, TimeUnit.MINUTES).build(); private static final String LIMIT_TIME_PROPERTY = "limitTime"; /** * qps 5. */ private static double limit = 5; static { try { String limitTimeStr = NacosClientProperties.PROTOTYPE.getProperty(LIMIT_TIME_PROPERTY, String.valueOf(limit)); limit = Double.parseDouble(limitTimeStr); LOGGER.info("limitTime:{}", limit); } catch (Exception e) { LOGGER.error("init limitTime fail", e); } } /** * Judge whether access key is limited. * * @param accessKeyId access key * @return true if is limited, otherwise false */ public static boolean isLimit(String accessKeyId) { RateLimiter rateLimiter = null; try { rateLimiter = CACHE.get(accessKeyId, () -> RateLimiter.create(limit)); } catch (Exception e) { LOGGER.error("create limit fail", e); } if (rateLimiter != null && !rateLimiter.tryAcquire(LIMIT_TIME, TimeUnit.MILLISECONDS)) { LOGGER.error("access_key_id:{} limited", accessKeyId); return true; } return false; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/LocalConfigInfoProcessor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.client.utils.ConcurrentDiskUtil; import com.alibaba.nacos.client.config.utils.JvmUtil; import com.alibaba.nacos.client.config.utils.SnapShotSwitch; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import static com.alibaba.nacos.client.utils.ParamUtil.simplyEnvNameIfOverLimit; /** * Local Disaster Recovery Directory Tool. * * @author Nacos */ public class LocalConfigInfoProcessor { private static final Logger LOGGER = LogUtils.logger(LocalConfigInfoProcessor.class); public static final String LOCAL_SNAPSHOT_PATH; private static final String SUFFIX = "_nacos"; private static final String ENV_CHILD = "snapshot"; private static final String FAILOVER_FILE_CHILD_1 = "data"; private static final String FAILOVER_FILE_CHILD_2 = "config-data"; private static final String FAILOVER_FILE_CHILD_3 = "config-data-tenant"; private static final String SNAPSHOT_FILE_CHILD_1 = "snapshot"; private static final String SNAPSHOT_FILE_CHILD_2 = "snapshot-tenant"; static { LOCAL_SNAPSHOT_PATH = NacosClientProperties.PROTOTYPE.getProperty(com.alibaba.nacos.client.constant.Constants.SysEnv.JM_SNAPSHOT_PATH, NacosClientProperties.PROTOTYPE.getProperty(com.alibaba.nacos.client.constant.Constants.SysEnv.USER_HOME)) + File.separator + "nacos" + File.separator + "config"; LOGGER.info("LOCAL_SNAPSHOT_PATH:{}", LOCAL_SNAPSHOT_PATH); } public static String getFailover(String serverName, String dataId, String group, String tenant) { File localPath = getFailoverFile(serverName, dataId, group, tenant); if (!localPath.exists() || !localPath.isFile()) { return null; } try { return readFile(localPath); } catch (IOException ioe) { LOGGER.error("[" + serverName + "] get failover error, " + localPath, ioe); return null; } } /** * get snapshot file content. NULL means no local file or throw exception. */ public static String getSnapshot(String name, String dataId, String group, String tenant) { if (!SnapShotSwitch.getIsSnapShot()) { return null; } File file = getSnapshotFile(name, dataId, group, tenant); if (!file.exists() || !file.isFile()) { return null; } try { return readFile(file); } catch (IOException ioe) { LOGGER.error("[" + name + "]+get snapshot error, " + file, ioe); return null; } } protected static String readFile(File file) throws IOException { if (!file.exists() || !file.isFile()) { return null; } if (JvmUtil.isMultiInstance()) { return ConcurrentDiskUtil.getFileContent(file, Constants.ENCODE); } else { try (InputStream is = new FileInputStream(file)) { return IoUtils.toString(is, Constants.ENCODE); } } } /** * Save snapshot. * * @param envName env name * @param dataId data id * @param group group * @param tenant tenant * @param config config */ public static void saveSnapshot(String envName, String dataId, String group, String tenant, String config) { if (!SnapShotSwitch.getIsSnapShot()) { return; } File file = getSnapshotFile(envName, dataId, group, tenant); if (null == config) { try { IoUtils.delete(file); } catch (IOException ioe) { LOGGER.error("[" + envName + "] delete snapshot error, " + file, ioe); } } else { try { File parentFile = file.getParentFile(); if (!parentFile.exists()) { boolean isMdOk = parentFile.mkdirs(); if (!isMdOk) { LOGGER.error("[{}] save snapshot error", envName); } } if (JvmUtil.isMultiInstance()) { ConcurrentDiskUtil.writeFileContent(file, config, Constants.ENCODE); } else { IoUtils.writeStringToFile(file, config, Constants.ENCODE); } } catch (IOException ioe) { LOGGER.error("[" + envName + "] save snapshot error, " + file, ioe); } } } /** * clear the cache files under snapshot directory. */ public static void cleanAllSnapshot() { try { File rootFile = new File(LOCAL_SNAPSHOT_PATH); File[] files = rootFile.listFiles(); if (files == null || files.length == 0) { return; } for (File file : files) { if (file.getName().endsWith(SUFFIX)) { IoUtils.cleanDirectory(file); } } } catch (IOException ioe) { LOGGER.error("clean all snapshot error, " + ioe.toString(), ioe); } } /** * Clean snapshot. * * @param envName env name */ public static void cleanEnvSnapshot(String envName) { File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + SUFFIX); tmp = new File(tmp, ENV_CHILD); try { IoUtils.cleanDirectory(tmp); LOGGER.info("success delete {}-snapshot", envName); } catch (IOException e) { LOGGER.warn("fail delete {}-snapshot, exception: ", envName, e); } } static File getFailoverFile(String serverName, String dataId, String group, String tenant) { serverName = simplyEnvNameIfOverLimit(serverName); File tmp = new File(LOCAL_SNAPSHOT_PATH, serverName + SUFFIX); tmp = new File(tmp, FAILOVER_FILE_CHILD_1); if (StringUtils.isBlank(tenant)) { tmp = new File(tmp, FAILOVER_FILE_CHILD_2); } else { tmp = new File(tmp, FAILOVER_FILE_CHILD_3); tmp = new File(tmp, tenant); } return new File(new File(tmp, group), dataId); } static File getSnapshotFile(String envName, String dataId, String group, String tenant) { envName = simplyEnvNameIfOverLimit(envName); File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + SUFFIX); if (StringUtils.isBlank(tenant)) { tmp = new File(tmp, SNAPSHOT_FILE_CHILD_1); } else { tmp = new File(tmp, SNAPSHOT_FILE_CHILD_2); tmp = new File(tmp, tenant); } return new File(new File(tmp, group), dataId); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/LocalEncryptedDataKeyProcessor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.client.utils.ConcurrentDiskUtil; import com.alibaba.nacos.client.config.utils.JvmUtil; import com.alibaba.nacos.client.config.utils.SnapShotSwitch; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.IoUtils; import org.slf4j.Logger; import java.io.File; import java.io.IOException; import static com.alibaba.nacos.client.utils.ParamUtil.simplyEnvNameIfOverLimit; /** * Encrypted data key (EncryptedDataKey) local snapshot, disaster recovery directory related. * * @author luyanbo(RobberPhex) */ public class LocalEncryptedDataKeyProcessor extends LocalConfigInfoProcessor { private static final Logger LOGGER = LogUtils.logger(LocalEncryptedDataKeyProcessor.class); private static final String FAILOVER_CHILD_1 = "encrypted-data-key"; private static final String FAILOVER_CHILD_2 = "failover"; private static final String FAILOVER_CHILD_3 = "failover-tenant"; private static final String SNAPSHOT_CHILD_1 = "encrypted-data-key"; private static final String SNAPSHOT_CHILD_2 = "snapshot"; private static final String SNAPSHOT_CHILD_3 = "snapshot-tenant"; private static final String SUFFIX = "_nacos"; /** * Obtain the EncryptedDataKey of the disaster recovery configuration. NULL means there is no local file or an * exception is thrown. */ public static String getEncryptDataKeyFailover(String envName, String dataId, String group, String tenant) { envName = simplyEnvNameIfOverLimit(envName); File file = getEncryptDataKeyFailoverFile(envName, dataId, group, tenant); if (!file.exists() || !file.isFile()) { return null; } try { return readFile(file); } catch (IOException ioe) { LOGGER.error("[" + envName + "] get failover error, " + file, ioe); return null; } } /** * Get the EncryptedDataKey of the locally cached file. NULL means there is no local file or an exception is * thrown. */ public static String getEncryptDataKeySnapshot(String envName, String dataId, String group, String tenant) { if (!SnapShotSwitch.getIsSnapShot()) { return null; } File file = getEncryptDataKeySnapshotFile(envName, dataId, group, tenant); if (!file.exists() || !file.isFile()) { return null; } try { return readFile(file); } catch (IOException ioe) { LOGGER.error("[" + envName + "] get snapshot error, " + file, ioe); return null; } } /** * Save the snapshot of encryptDataKey. If the content is NULL, delete the snapshot. */ public static void saveEncryptDataKeySnapshot(String envName, String dataId, String group, String tenant, String encryptDataKey) { if (!SnapShotSwitch.getIsSnapShot()) { return; } File file = getEncryptDataKeySnapshotFile(envName, dataId, group, tenant); try { if (null == encryptDataKey) { try { IoUtils.delete(file); } catch (IOException ioe) { LOGGER.error("[" + envName + "] delete snapshot error, " + file, ioe); } } else { File parentFile = file.getParentFile(); if (!parentFile.exists()) { boolean isMdOk = parentFile.mkdirs(); if (!isMdOk) { LOGGER.error("[{}] save snapshot error", envName); } } if (JvmUtil.isMultiInstance()) { ConcurrentDiskUtil.writeFileContent(file, encryptDataKey, Constants.ENCODE); } else { IoUtils.writeStringToFile(file, encryptDataKey, Constants.ENCODE); } } } catch (IOException ioe) { LOGGER.error("[" + envName + "] save snapshot error, " + file, ioe); } } private static File getEncryptDataKeyFailoverFile(String envName, String dataId, String group, String tenant) { envName = simplyEnvNameIfOverLimit(envName); File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + SUFFIX); tmp = new File(tmp, FAILOVER_CHILD_1); if (StringUtils.isBlank(tenant)) { tmp = new File(tmp, FAILOVER_CHILD_2); } else { tmp = new File(tmp, FAILOVER_CHILD_3); tmp = new File(tmp, tenant); } return new File(new File(tmp, group), dataId); } private static File getEncryptDataKeySnapshotFile(String envName, String dataId, String group, String tenant) { envName = simplyEnvNameIfOverLimit(envName); File tmp = new File(LOCAL_SNAPSHOT_PATH, envName + SUFFIX); tmp = new File(tmp, SNAPSHOT_CHILD_1); if (StringUtils.isBlank(tenant)) { tmp = new File(tmp, SNAPSHOT_CHILD_2); } else { tmp = new File(tmp, SNAPSHOT_CHILD_3); tmp = new File(tmp, tenant); } return new File(new File(tmp, group), dataId); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/PropertiesChangeParser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeItem; import com.alibaba.nacos.common.utils.StringUtils; import java.io.IOException; import java.io.StringReader; import java.util.Map; import java.util.Properties; /** * PropertiesChangeParser. * * @author rushsky518 */ public class PropertiesChangeParser extends AbstractConfigChangeParser { private static final String CONFIG_TYPE = "properties"; public PropertiesChangeParser() { super(CONFIG_TYPE); } @Override public Map doParse(String oldContent, String newContent, String type) throws IOException { Properties oldProps = new Properties(); Properties newProps = new Properties(); if (StringUtils.isNotBlank(oldContent)) { oldProps.load(new StringReader(oldContent)); } if (StringUtils.isNotBlank(newContent)) { newProps.load(new StringReader(newContent)); } return filterChangeData(oldProps, newProps); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/impl/YmlChangeParser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeItem; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.utils.StringUtils; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.composer.ComposerException; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.MarkedYAMLException; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; /** * YmlChangeParser. * * @author rushsky518 */ public class YmlChangeParser extends AbstractConfigChangeParser { private static final String INVALID_CONSTRUCTOR_ERROR_INFO = "could not determine a constructor for the tag"; private static final String CONFIG_TYPE = "yaml"; public YmlChangeParser() { super(CONFIG_TYPE); } @Override public Map doParse(String oldContent, String newContent, String type) { Map oldMap = Collections.emptyMap(); Map newMap = Collections.emptyMap(); try { Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions())); if (StringUtils.isNotBlank(oldContent)) { oldMap = yaml.load(oldContent); oldMap = getFlattenedMap(oldMap); } if (StringUtils.isNotBlank(newContent)) { newMap = yaml.load(newContent); newMap = getFlattenedMap(newMap); } } catch (MarkedYAMLException e) { handleYamlException(e); } return filterChangeData(oldMap, newMap); } private void handleYamlException(MarkedYAMLException e) { if (e.getMessage().startsWith(INVALID_CONSTRUCTOR_ERROR_INFO) || e instanceof ComposerException) { throw new NacosRuntimeException(NacosException.INVALID_PARAM, "AbstractConfigChangeListener only support basic java data type for yaml. If you want to listen " + "key changes for custom classes, please use `Listener` to listener whole yaml configuration and parse it by yourself.", e); } throw e; } private Map getFlattenedMap(Map source) { Map result = new LinkedHashMap<>(128); buildFlattenedMap(result, source, null); return result; } private void buildFlattenedMap(Map result, Map source, String path) { for (Iterator> itr = source.entrySet().iterator(); itr.hasNext(); ) { Map.Entry e = itr.next(); String key = e.getKey(); if (StringUtils.isNotBlank(path)) { if (e.getKey().startsWith("[")) { key = path + key; } else { key = path + '.' + key; } } if (e.getValue() instanceof String) { result.put(key, e.getValue()); } else if (e.getValue() instanceof Map) { @SuppressWarnings("unchecked") Map map = (Map) e.getValue(); buildFlattenedMap(result, map, key); } else if (e.getValue() instanceof Collection) { @SuppressWarnings("unchecked") Collection collection = (Collection) e.getValue(); if (collection.isEmpty()) { result.put(key, ""); } else { int count = 0; for (Object object : collection) { buildFlattenedMap(result, Collections.singletonMap("[" + (count++) + "]", object), key); } } } else { result.put(key, (e.getValue() != null ? e.getValue() : "")); } } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/listener/impl/AbstractConfigChangeListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.listener.impl; import com.alibaba.nacos.api.config.ConfigChangeEvent; import com.alibaba.nacos.api.config.listener.AbstractListener; /** * AbstractConfigChangeListener. * * @author rushsky518 */ public abstract class AbstractConfigChangeListener extends AbstractListener { /** * handle config change. * * @param event config change event */ public abstract void receiveConfigChange(final ConfigChangeEvent event); @Override public void receiveConfigInfo(final String configInfo) { } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/listener/impl/PropertiesListener.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.listener.impl; import com.alibaba.nacos.api.config.listener.AbstractListener; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.io.IOException; import java.io.StringReader; import java.util.Properties; /** * Properties Listener. * * @author Nacos */ public abstract class PropertiesListener extends AbstractListener { private static final Logger LOGGER = LogUtils.logger(PropertiesListener.class); @Override public void receiveConfigInfo(String configInfo) { if (StringUtils.isEmpty(configInfo)) { return; } Properties properties = new Properties(); try { properties.load(new StringReader(configInfo)); innerReceive(properties); } catch (IOException e) { LOGGER.error("load properties error:" + configInfo, e); } } /** * properties type for receiver. * * @param properties properties */ public abstract void innerReceive(Properties properties); } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/utils/ContentUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import com.alibaba.nacos.api.common.Constants; import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR; /** * Content Util. * * @author Nacos */ public class ContentUtils { private static final int SHOW_CONTENT_SIZE = 100; /** * Verify increment pub content. * * @param content content * @throws IllegalArgumentException if content is not valid */ public static void verifyIncrementPubContent(String content) { if (content == null || content.length() == 0) { throw new IllegalArgumentException("publish/delete content can not be null"); } for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (c == '\r' || c == '\n') { throw new IllegalArgumentException("publish/delete content can not contain return and linefeed"); } if (c == Constants.WORD_SEPARATOR.charAt(0)) { throw new IllegalArgumentException("publish/delete content can not contain(char)2"); } } } public static String getContentIdentity(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { throw new IllegalArgumentException("content does not contain separator"); } return content.substring(0, index); } public static String getContent(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { throw new IllegalArgumentException("content does not contain separator"); } return content.substring(index + 1); } /** * Truncate content. * * @param content content * @return truncated content */ public static String truncateContent(String content) { if (content == null) { return ""; } else if (content.length() <= SHOW_CONTENT_SIZE) { return content; } else { return content.substring(0, SHOW_CONTENT_SIZE) + "..."; } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/utils/JvmUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.LogUtils; import org.slf4j.Logger; /** * Get jvm config. * * @author Nacos */ public class JvmUtil { /** * whether is multi instance. * * @return whether multi */ public static Boolean isMultiInstance() { return isMultiInstance; } private static Boolean isMultiInstance = false; private static final String TRUE = "true"; private static final Logger LOGGER = LogUtils.logger(JvmUtil.class); private static final String IS_MULTI_INSTANCE_PROPERTY = "isMultiInstance"; private static final String DEFAULT_IS_MULTI_INSTANCE = "false"; static { init(); } private static void init() { String multiDeploy = NacosClientProperties.PROTOTYPE .getProperty(IS_MULTI_INSTANCE_PROPERTY, DEFAULT_IS_MULTI_INSTANCE); if (TRUE.equals(multiDeploy)) { isMultiInstance = true; } LOGGER.info("isMultiInstance:{}", isMultiInstance); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/utils/ParamUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.StringUtils; import java.util.List; /** * Param check util. * * @author Nacos */ public class ParamUtils { private static final char[] VALID_CHARS = new char[] {'_', '-', '.', ':'}; private static final String CONTENT_INVALID_MSG = "content invalid"; private static final String DATAID_INVALID_MSG = "dataId invalid"; private static final String TENANT_INVALID_MSG = "tenant invalid"; private static final String BETAIPS_INVALID_MSG = "betaIps invalid"; private static final String GROUP_INVALID_MSG = "group invalid"; private static final String DATUMID_INVALID_MSG = "datumId invalid"; /** * Check the whitelist method, the legal parameters can only contain letters, numbers, and characters in validChars, and cannot be empty. * * @param param parameter * @return true if valid */ public static boolean isValid(String param) { if (param == null) { return false; } int length = param.length(); for (int i = 0; i < length; i++) { char ch = param.charAt(i); if (!Character.isLetterOrDigit(ch) && !isValidChar(ch)) { return false; } } return true; } private static boolean isValidChar(char ch) { for (char c : VALID_CHARS) { if (c == ch) { return true; } } return false; } /** * Check Tenant, dataId and group. * * @param tenant tenant * @param dataId dataId * @param group group * @throws NacosException nacos exception */ public static void checkTdg(String tenant, String dataId, String group) throws NacosException { checkTenant(tenant); if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, DATAID_INVALID_MSG); } if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, GROUP_INVALID_MSG); } } /** * Check key param. * * @param dataId dataId * @param group group * @throws NacosException nacos exception */ public static void checkKeyParam(String dataId, String group) throws NacosException { if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, DATAID_INVALID_MSG); } if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, GROUP_INVALID_MSG); } } /** * Check key param. * * @param dataId dataId * @param group group * @param datumId datumId * @throws NacosException nacos exception */ public static void checkKeyParam(String dataId, String group, String datumId) throws NacosException { if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, DATAID_INVALID_MSG); } if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, GROUP_INVALID_MSG); } if (StringUtils.isBlank(datumId) || !ParamUtils.isValid(datumId)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, DATUMID_INVALID_MSG); } } /** * Check key param. * * @param dataIds dataIds * @param group group * @throws NacosException nacos exception */ public static void checkKeyParam(List dataIds, String group) throws NacosException { if (dataIds == null || dataIds.size() == 0) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "dataIds invalid"); } for (String dataId : dataIds) { if (StringUtils.isBlank(dataId) || !ParamUtils.isValid(dataId)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, DATAID_INVALID_MSG); } } if (StringUtils.isBlank(group) || !ParamUtils.isValid(group)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, GROUP_INVALID_MSG); } } /** * Check parameter. * * @param dataId dataId * @param group group * @param content content * @throws NacosException nacos exception */ public static void checkParam(String dataId, String group, String content) throws NacosException { checkKeyParam(dataId, group); if (StringUtils.isBlank(content)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, CONTENT_INVALID_MSG); } } /** * Check parameter. * * @param dataId dataId * @param group group * @param datumId datumId * @param content content * @throws NacosException nacos exception */ public static void checkParam(String dataId, String group, String datumId, String content) throws NacosException { checkKeyParam(dataId, group, datumId); if (StringUtils.isBlank(content)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, CONTENT_INVALID_MSG); } } /** * Check Tenant. * * @param tenant tenant * @throws NacosException nacos exception */ public static void checkTenant(String tenant) throws NacosException { if (StringUtils.isBlank(tenant) || !ParamUtils.isValid(tenant)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, TENANT_INVALID_MSG); } } /** * Check beta ips. * * @param betaIps beta ips * @throws NacosException nacos exception */ public static void checkBetaIps(String betaIps) throws NacosException { if (StringUtils.isBlank(betaIps)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, BETAIPS_INVALID_MSG); } String[] ipsArr = betaIps.split(","); for (String ip : ipsArr) { if (!InternetAddressUtil.isIp(ip)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, BETAIPS_INVALID_MSG); } } } /** * Check content. * * @param content content * @throws NacosException nacos exception */ public static void checkContent(String content) throws NacosException { if (StringUtils.isBlank(content)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, CONTENT_INVALID_MSG); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/config/utils/SnapShotSwitch.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; /** * Snapshot switch. * * @author Nacos */ public class SnapShotSwitch { /** * whether use local cache. */ private static Boolean isSnapShot = true; public static Boolean getIsSnapShot() { return isSnapShot; } public static void setIsSnapShot(Boolean isSnapShot) { SnapShotSwitch.isSnapShot = isSnapShot; LocalConfigInfoProcessor.cleanAllSnapshot(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/lock/NacosLockService.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.LockService; import com.alibaba.nacos.api.lock.model.LockInstance; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.lock.remote.grpc.LockGrpcClient; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager; import com.alibaba.nacos.client.security.SecurityProxy; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.client.constant.Constants.Security.SECURITY_INFO_REFRESH_INTERVAL_MILLS; /** * nacos lock Service. * * @author 985492783@qq.com * @date 2023/8/24 19:51 */ public class NacosLockService implements LockService { private final LockGrpcClient lockGrpcClient; private final SecurityProxy securityProxy; private ScheduledExecutorService executorService; public NacosLockService(Properties properties) throws NacosException { NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); AbstractServerListManager serverListManager = new NamingServerListManager(properties); serverListManager.start(); this.securityProxy = new SecurityProxy(serverListManager, NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(nacosClientProperties); this.lockGrpcClient = new LockGrpcClient(nacosClientProperties, serverListManager, securityProxy); } private void initSecurityProxy(NacosClientProperties properties) { this.executorService = new ScheduledThreadPoolExecutor(1, r -> { Thread t = new Thread(r); t.setName("com.alibaba.nacos.client.lock.security"); t.setDaemon(true); return t; }); final Properties nacosClientPropertiesView = properties.asProperties(); this.securityProxy.login(nacosClientPropertiesView); this.executorService.scheduleWithFixedDelay(() -> securityProxy.login(nacosClientPropertiesView), 0, SECURITY_INFO_REFRESH_INTERVAL_MILLS, TimeUnit.MILLISECONDS); } @Override public Boolean lock(LockInstance instance) throws NacosException { return instance.lock(this); } @Override public Boolean unLock(LockInstance instance) throws NacosException { return instance.unLock(this); } @Override public Boolean remoteTryLock(LockInstance instance) throws NacosException { return lockGrpcClient.lock(instance); } @Override public Boolean remoteReleaseLock(LockInstance instance) throws NacosException { return lockGrpcClient.unLock(instance); } @Override public void shutdown() throws NacosException { lockGrpcClient.shutdown(); if (null != executorService) { executorService.shutdown(); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/lock/core/NLock.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock.core; import com.alibaba.nacos.api.lock.common.LockConstants; import com.alibaba.nacos.api.lock.model.LockInstance; /** * Nacos client lock entity. * * @author 985492783@qq.com * @date 2023/8/24 19:52 */ public class NLock extends LockInstance { private static final long serialVersionUID = -346054842454875524L; public NLock(String key, Long expireTimestamp) { super(key, expireTimestamp, LockConstants.NACOS_LOCK_TYPE); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/lock/core/NLockFactory.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock.core; /** * NLock factory. * * @author 985492783@qq.com * @date 2023/8/27 15:23 */ public class NLockFactory { /** * create NLock without expireTime. * * @param key key * @return NLock */ public static NLock getLock(String key) { return new NLock(key, -1L); } /** * create NLock with expireTime. * * @param key key * @return NLock */ public static NLock getLock(String key, Long expireTimestamp) { return new NLock(key, expireTimestamp); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/lock/remote/AbstractLockClient.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock.remote; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.plugin.auth.api.RequestResource; import java.util.HashMap; import java.util.Map; /** * abstract lock client. * @author 985492783@qq.com * @description AbstractLockClient * @date 2023/6/28 17:19 */ public abstract class AbstractLockClient implements LockClient { private final SecurityProxy securityProxy; private static final String APP_FILED = "app"; protected AbstractLockClient(SecurityProxy securityProxy) { this.securityProxy = securityProxy; } protected Map getSecurityHeaders() { RequestResource resource = RequestResource.lockBuilder().build(); Map result = this.securityProxy.getIdentityContext(resource); result.putAll(getAppHeaders()); return result; } protected Map getAppHeaders() { Map result = new HashMap<>(1); result.put(APP_FILED, AppNameUtils.getAppName()); return result; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/lock/remote/LockClient.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.lock.model.LockInstance; import com.alibaba.nacos.common.lifecycle.Closeable; /** * lock client interface. * * @author 985492783@qq.com * @description LockClient * @date 2023/6/28 17:19 */ public interface LockClient extends Closeable { /** * lock client get lock. * * @param instance instance. * @return Boolean. * @throws NacosException nacos Exception. */ Boolean lock(LockInstance instance) throws NacosException; /** * lock client unLock. * * @param instance instance. * @return Boolean. * @throws NacosException nacos Exception. */ Boolean unLock(LockInstance instance) throws NacosException; } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/lock/remote/grpc/LockGrpcClient.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock.remote.grpc; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.lock.constant.PropertyConstants; import com.alibaba.nacos.api.lock.model.LockInstance; import com.alibaba.nacos.api.lock.remote.AbstractLockRequest; import com.alibaba.nacos.api.lock.remote.LockOperationEnum; import com.alibaba.nacos.api.lock.remote.request.LockOperationRequest; import com.alibaba.nacos.api.lock.remote.response.LockOperationResponse; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.lock.remote.AbstractLockClient; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientFactory; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfigFactory; import com.alibaba.nacos.common.remote.client.ServerListFactory; import java.util.HashMap; import java.util.Map; import java.util.UUID; /** * lock grpc client. * * @author 985492783@qq.com * @description LockGrpcClient * @date 2023/6/28 17:35 */ public class LockGrpcClient extends AbstractLockClient { private final String uuid; private final Long requestTimeout; private final RpcClient rpcClient; public LockGrpcClient(NacosClientProperties properties, ServerListFactory serverListFactory, SecurityProxy securityProxy) throws NacosException { super(securityProxy); this.uuid = UUID.randomUUID().toString(); this.requestTimeout = Long.parseLong(properties.getProperty(PropertyConstants.LOCK_REQUEST_TIMEOUT, "-1")); Map labels = new HashMap<>(); labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK); labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_LOCK); labels.put(Constants.APPNAME, AppNameUtils.getAppName()); this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, labels, RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties.asProperties())); start(serverListFactory); } private void start(ServerListFactory serverListFactory) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.start(); } @Override public Boolean lock(LockInstance instance) throws NacosException { if (!isAbilitySupportedByServer()) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support lock feature."); } LockOperationRequest request = new LockOperationRequest(); request.setLockInstance(instance); request.setLockOperationEnum(LockOperationEnum.ACQUIRE); LockOperationResponse acquireLockResponse = requestToServer(request, LockOperationResponse.class); return (Boolean) acquireLockResponse.getResult(); } @Override public Boolean unLock(LockInstance instance) throws NacosException { if (!isAbilitySupportedByServer()) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support lock feature."); } LockOperationRequest request = new LockOperationRequest(); request.setLockInstance(instance); request.setLockOperationEnum(LockOperationEnum.RELEASE); LockOperationResponse acquireLockResponse = requestToServer(request, LockOperationResponse.class); return (Boolean) acquireLockResponse.getResult(); } @Override public void shutdown() throws NacosException { rpcClient.shutdown(); } private T requestToServer(AbstractLockRequest request, Class responseClass) throws NacosException { try { request.putAllHeader(getSecurityHeaders()); Response response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) { throw new NacosException(response.getErrorCode(), response.getMessage()); } if (responseClass.isAssignableFrom(response.getClass())) { return (T) response; } } catch (NacosException e) { throw e; } catch (Exception e) { throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e); } throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response"); } private boolean isAbilitySupportedByServer() { return rpcClient.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK) == AbilityStatus.SUPPORTED; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/logging/NacosLogging.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.logging; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.logging.NacosLoggingAdapter; import com.alibaba.nacos.common.logging.NacosLoggingAdapterBuilder; import com.alibaba.nacos.common.logging.NacosLoggingProperties; import com.alibaba.nacos.common.spi.NacosServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * nacos logging. * * @author mai.jh */ public class NacosLogging { private static final Logger LOGGER = LoggerFactory.getLogger(NacosLogging.class); private NacosLoggingAdapter loggingAdapter; private NacosLoggingProperties loggingProperties; private NacosLogging() { initLoggingAdapter(); } private void initLoggingAdapter() { Class loggerClass = LOGGER.getClass(); for (NacosLoggingAdapterBuilder each : NacosServiceLoader.load(NacosLoggingAdapterBuilder.class)) { LOGGER.info("Nacos Logging Adapter Builder: {}", each.getClass().getName()); NacosLoggingAdapter tempLoggingAdapter = buildLoggingAdapterFromBuilder(each); if (isAdaptLogging(tempLoggingAdapter, loggerClass)) { LOGGER.info("Nacos Logging Adapter: {} match {} success.", tempLoggingAdapter.getClass().getName(), loggerClass.getName()); loggingProperties = new NacosLoggingProperties(tempLoggingAdapter.getDefaultConfigLocation(), NacosClientProperties.PROTOTYPE.asProperties()); loggingAdapter = tempLoggingAdapter; } } if (null == loggingAdapter) { LOGGER.warn("Nacos Logging don't find adapter, logging will print into application logs."); return; } scheduleReloadTask(); } private NacosLoggingAdapter buildLoggingAdapterFromBuilder(NacosLoggingAdapterBuilder builder) { try { return builder.build(); } catch (Throwable e) { LOGGER.warn("Build Nacos Logging Adapter failed: {}", e.getMessage()); return null; } } private boolean isAdaptLogging(NacosLoggingAdapter loggingAdapter, Class loggerClass) { return null != loggingAdapter && loggingAdapter.isEnabled() && loggingAdapter.isAdaptedLogger(loggerClass); } private void scheduleReloadTask() { ScheduledExecutorService reloadContextService = ExecutorFactory.Managed .newSingleScheduledExecutorService("Nacos-Client", new NameThreadFactory("com.alibaba.nacos.client.logging")); reloadContextService.scheduleAtFixedRate(() -> { if (loggingAdapter.isNeedReloadConfiguration()) { loggingAdapter.loadConfiguration(loggingProperties); } }, 0, loggingProperties.getReloadInternal(), TimeUnit.SECONDS); } private static class NacosLoggingInstance { private static final NacosLogging INSTANCE = new NacosLogging(); } public static NacosLogging getInstance() { return NacosLoggingInstance.INSTANCE; } /** * Load logging Configuration. */ public void loadConfiguration() { try { if (null != loggingAdapter) { loggingAdapter.loadConfiguration(loggingProperties); } } catch (Throwable t) { LOGGER.warn("Load {} Configuration of Nacos fail, message: {}", LOGGER.getClass().getName(), t.getMessage()); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/monitor/MetricsMonitor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.monitor; import io.prometheus.client.Counter; import io.prometheus.client.Gauge; import io.prometheus.client.Histogram; /** * Metrics Monitor. * * @author Nacos */ public class MetricsMonitor { private static final Gauge NACOS_MONITOR_GAUGE = Gauge.build().name("nacos_monitor").labelNames("module", "name") .help("nacos_monitor").register(); private static final Histogram NACOS_CLIENT_REQUEST_HISTOGRAM = Histogram.build() .labelNames("module", "method", "url", "code").name("nacos_client_request").help("nacos_client_request") .register(); private static final Counter NACOS_CLIENT_NAMING_REQUEST_FAILED_TOTAL = Counter.build() .name("nacos_client_naming_request_failed_total").help("nacos_client_naming_request_failed_total") .labelNames("module", "req_class", "res_status", "res_code", "err_class").register(); public static Gauge.Child getServiceInfoMapSizeMonitor() { return NACOS_MONITOR_GAUGE.labels("naming", "serviceInfoMapSize"); } public static Gauge.Child getListenConfigCountMonitor() { return NACOS_MONITOR_GAUGE.labels("config", "listenConfigCount"); } public static Histogram.Child getConfigRequestMonitor(String method, String url, String code) { return NACOS_CLIENT_REQUEST_HISTOGRAM.labels("config", method, url, code); } public static Histogram.Child getNamingRequestMonitor(String method, String url, String code) { return NACOS_CLIENT_REQUEST_HISTOGRAM.labels("naming", method, url, code); } public static Counter.Child getNamingRequestFailedMonitor(String reqClass, String resStatus, String resCode, String errClass) { return NACOS_CLIENT_NAMING_REQUEST_FAILED_TOTAL.labels("naming", reqClass, resStatus, resCode, errClass); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingMaintainService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.NamingMaintainService; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; import com.alibaba.nacos.client.naming.utils.InitUtils; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.ValidatorUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.utils.ThreadUtils; import java.util.Map; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.client.constant.Constants.Security.SECURITY_INFO_REFRESH_INTERVAL_MILLS; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Nacos naming maintain service. * * @author liaochuntao * @since 1.0.1 * @deprecated Use {@link com.alibaba.nacos.api.naming.maintain.NamingMaintainService} in nacos-maintainer-client article tp replaced. */ @Deprecated public class NacosNamingMaintainService implements NamingMaintainService { private String namespace; private NamingHttpClientProxy serverProxy; private NamingServerListManager serverListManager; private SecurityProxy securityProxy; private ScheduledExecutorService executorService; public NacosNamingMaintainService(String serverList) throws NacosException { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList); init(properties); } public NacosNamingMaintainService(Properties properties) throws NacosException { init(properties); } private void init(Properties properties) throws NacosException { final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ValidatorUtils.checkInitParam(nacosClientProperties); namespace = InitUtils.initNamespaceForNaming(nacosClientProperties); InitUtils.initSerialization(); InitUtils.initWebRootContext(nacosClientProperties); serverListManager = new NamingServerListManager(nacosClientProperties, namespace); serverListManager.start(); securityProxy = new SecurityProxy(serverListManager, NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(properties); serverProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, nacosClientProperties); } private void initSecurityProxy(Properties properties) { this.executorService = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.naming.maintainService.security")); this.securityProxy.login(properties); this.executorService .scheduleWithFixedDelay(() -> securityProxy.login(properties), 0, SECURITY_INFO_REFRESH_INTERVAL_MILLS, TimeUnit.MILLISECONDS); } @Override public void updateInstance(String serviceName, Instance instance) throws NacosException { updateInstance(serviceName, Constants.DEFAULT_GROUP, instance); } @Override public void updateInstance(String serviceName, String groupName, Instance instance) throws NacosException { serverProxy.updateInstance(serviceName, groupName, instance); } @Override public Service queryService(String serviceName) throws NacosException { return queryService(serviceName, Constants.DEFAULT_GROUP); } @Override public Service queryService(String serviceName, String groupName) throws NacosException { return serverProxy.queryService(serviceName, groupName); } @Override public void createService(String serviceName) throws NacosException { createService(serviceName, Constants.DEFAULT_GROUP); } @Override public void createService(String serviceName, String groupName) throws NacosException { createService(serviceName, groupName, Constants.DEFAULT_PROTECT_THRESHOLD); } @Override public void createService(String serviceName, String groupName, float protectThreshold) throws NacosException { Service service = new Service(); service.setName(serviceName); service.setGroupName(groupName); service.setProtectThreshold(protectThreshold); createService(service, new NoneSelector()); } @Override public void createService(String serviceName, String groupName, float protectThreshold, String expression) throws NacosException { Service service = new Service(); service.setName(serviceName); service.setGroupName(groupName); service.setProtectThreshold(protectThreshold); ExpressionSelector selector = new ExpressionSelector(); selector.setExpression(expression); createService(service, selector); } @Override public void createService(Service service, AbstractSelector selector) throws NacosException { serverProxy.createService(service, selector); } @Override public boolean deleteService(String serviceName) throws NacosException { return deleteService(serviceName, Constants.DEFAULT_GROUP); } @Override public boolean deleteService(String serviceName, String groupName) throws NacosException { return serverProxy.deleteService(serviceName, groupName); } @Override public void updateService(String serviceName, String groupName, float protectThreshold) throws NacosException { Service service = new Service(); service.setName(serviceName); service.setGroupName(groupName); service.setProtectThreshold(protectThreshold); updateService(service, new NoneSelector()); } @Override public void updateService(String serviceName, String groupName, float protectThreshold, Map metadata) throws NacosException { Service service = new Service(); service.setName(serviceName); service.setGroupName(groupName); service.setProtectThreshold(protectThreshold); service.setMetadata(metadata); updateService(service, new NoneSelector()); } @Override public void updateService(Service service, AbstractSelector selector) throws NacosException { serverProxy.updateService(service, selector); } @Override public void shutDown() throws NacosException { String className = this.getClass().getName(); NAMING_LOGGER.info("{} do shutdown begin", className); serverListManager.shutdown(); serverProxy.shutdown(); ThreadUtils.shutdownThreadPool(executorService, NAMING_LOGGER); NAMING_LOGGER.info("{} do shutdown stop", className); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/NacosNamingService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming; import com.alibaba.nacos.api.PropertyKeyConst; 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.listener.EventListener; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; 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.NamingContext; import com.alibaba.nacos.api.naming.selector.NamingResult; import com.alibaba.nacos.api.naming.selector.NamingSelector; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.core.Balancer; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate; import com.alibaba.nacos.client.naming.selector.NamingSelectorFactory; import com.alibaba.nacos.client.naming.selector.NamingSelectorWrapper; import com.alibaba.nacos.client.naming.selector.ServiceInfoContext; import com.alibaba.nacos.client.naming.utils.InitUtils; import com.alibaba.nacos.client.naming.utils.UtilAndComs; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.client.utils.PreInitUtils; import com.alibaba.nacos.client.utils.ValidatorUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.UUID; import java.util.concurrent.Future; import static com.alibaba.nacos.api.common.Constants.ANY_PATTERN; import static com.alibaba.nacos.client.naming.selector.NamingSelectorFactory.getUniqueClusterString; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Nacos Naming Service. * * @author nkorange */ public class NacosNamingService implements NamingService { private static final String DEFAULT_NAMING_LOG_FILE_PATH = "naming.log"; private static final String UP = "UP"; private static final String DOWN = "DOWN"; /** * Each Naming service should have different namespace. */ private String namespace; @Deprecated private String logName; private ServiceInfoHolder serviceInfoHolder; private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; private InstancesChangeNotifier changeNotifier; private NamingClientProxy clientProxy; private String notifierEventScope; public NacosNamingService(String serverList) throws NacosException { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList); init(properties); } public NacosNamingService(Properties properties) throws NacosException { init(properties); } private void init(Properties properties) throws NacosException { PreInitUtils.asyncPreLoadCostComponent(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); NAMING_LOGGER.info(ClientBasicParamUtil.getInputParameters(nacosClientProperties.asProperties())); ValidatorUtils.checkInitParam(nacosClientProperties); this.namespace = InitUtils.initNamespaceForNaming(nacosClientProperties); InitUtils.initSerialization(); InitUtils.initWebRootContext(nacosClientProperties); initLogName(nacosClientProperties); this.notifierEventScope = UUID.randomUUID().toString(); this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope); NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384); NotifyCenter.registerSubscriber(changeNotifier); this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, nacosClientProperties); NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class, 16384); this.namingFuzzyWatchServiceListHolder = new NamingFuzzyWatchServiceListHolder(this.notifierEventScope); this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, nacosClientProperties, changeNotifier, namingFuzzyWatchServiceListHolder); } @Deprecated private void initLogName(NacosClientProperties properties) { logName = properties.getProperty(UtilAndComs.NACOS_NAMING_LOG_NAME, DEFAULT_NAMING_LOG_FILE_PATH); } @Override public void registerInstance(String serviceName, String ip, int port) throws NacosException { registerInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException { registerInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { registerInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName); } @Override public void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setWeight(1.0); instance.setClusterName(clusterName); registerInstance(serviceName, groupName, instance); } @Override public void registerInstance(String serviceName, Instance instance) throws NacosException { registerInstance(serviceName, Constants.DEFAULT_GROUP, instance); } @Override public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException { NamingUtils.checkInstanceIsLegal(instance); checkAndStripGroupNamePrefix(instance, groupName); clientProxy.registerService(serviceName, groupName, instance); } @Override public void batchRegisterInstance(String serviceName, String groupName, List instances) throws NacosException { NamingUtils.batchCheckInstanceIsLegal(instances); batchCheckAndStripGroupNamePrefix(instances, groupName); clientProxy.batchRegisterService(serviceName, groupName, instances); } @Override public void batchDeregisterInstance(String serviceName, String groupName, List instances) throws NacosException { NamingUtils.batchCheckInstanceIsLegal(instances); batchCheckAndStripGroupNamePrefix(instances, groupName); clientProxy.batchDeregisterService(serviceName, groupName, instances); } @Override public void deregisterInstance(String serviceName, String ip, int port) throws NacosException { deregisterInstance(serviceName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException { deregisterInstance(serviceName, groupName, ip, port, Constants.DEFAULT_CLUSTER_NAME); } @Override public void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException { deregisterInstance(serviceName, Constants.DEFAULT_GROUP, ip, port, clusterName); } @Override public void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName) throws NacosException { Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); instance.setClusterName(clusterName); deregisterInstance(serviceName, groupName, instance); } @Override public void deregisterInstance(String serviceName, Instance instance) throws NacosException { deregisterInstance(serviceName, Constants.DEFAULT_GROUP, instance); } @Override public void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException { NamingUtils.checkInstanceIsLegal(instance); checkAndStripGroupNamePrefix(instance, groupName); clientProxy.deregisterService(serviceName, groupName, instance); } @Override public List getAllInstances(String serviceName) throws NacosException { return getAllInstances(serviceName, new ArrayList<>()); } @Override public List getAllInstances(String serviceName, String groupName) throws NacosException { return getAllInstances(serviceName, groupName, new ArrayList<>()); } @Override public List getAllInstances(String serviceName, boolean subscribe) throws NacosException { return getAllInstances(serviceName, new ArrayList<>(), subscribe); } @Override public List getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException { return getAllInstances(serviceName, groupName, new ArrayList<>(), subscribe); } @Override public List getAllInstances(String serviceName, List clusters) throws NacosException { return getAllInstances(serviceName, clusters, true); } @Override public List getAllInstances(String serviceName, String groupName, List clusters) throws NacosException { return getAllInstances(serviceName, groupName, clusters, true); } @Override public List getAllInstances(String serviceName, List clusters, boolean subscribe) throws NacosException { return getAllInstances(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe); } @Override public List getAllInstances(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException { List list; ServiceInfo serviceInfo = getServiceInfo(serviceName, groupName, clusters, subscribe); if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { return new ArrayList<>(); } return list; } @Override public List selectInstances(String serviceName, boolean healthy) throws NacosException { return selectInstances(serviceName, new ArrayList<>(), healthy); } @Override public List selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException { return selectInstances(serviceName, groupName, healthy, true); } @Override public List selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException { return selectInstances(serviceName, new ArrayList<>(), healthy, subscribe); } @Override public List selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe) throws NacosException { return selectInstances(serviceName, groupName, new ArrayList<>(), healthy, subscribe); } @Override public List selectInstances(String serviceName, List clusters, boolean healthy) throws NacosException { return selectInstances(serviceName, clusters, healthy, true); } @Override public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy) throws NacosException { return selectInstances(serviceName, groupName, clusters, healthy, true); } @Override public List selectInstances(String serviceName, List clusters, boolean healthy, boolean subscribe) throws NacosException { return selectInstances(serviceName, Constants.DEFAULT_GROUP, clusters, healthy, subscribe); } @Override public List selectInstances(String serviceName, String groupName, List clusters, boolean healthy, boolean subscribe) throws NacosException { ServiceInfo serviceInfo = getServiceInfo(serviceName, groupName, clusters, subscribe); return selectInstances(serviceInfo, healthy); } private List selectInstances(ServiceInfo serviceInfo, boolean healthy) { List list; if (serviceInfo == null || CollectionUtils.isEmpty(list = serviceInfo.getHosts())) { return new ArrayList<>(); } Iterator iterator = list.iterator(); while (iterator.hasNext()) { Instance instance = iterator.next(); if (healthy != instance.isHealthy() || !instance.isEnabled() || instance.getWeight() <= 0) { iterator.remove(); } } return list; } private ServiceInfo getServiceInfo(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException { ServiceInfo serviceInfo; NamingSelector clusterSelector = NamingSelectorFactory.newClusterSelector(clusters); if (serviceInfoHolder.isFailoverSwitch()) { serviceInfo = getServiceInfoByFailover(serviceName, groupName, clusterSelector); if (serviceInfo != null && !serviceInfo.getHosts().isEmpty()) { NAMING_LOGGER.debug("getServiceInfo from failover,serviceName: {} data:{}", serviceName, JacksonUtils.toJson(serviceInfo.getHosts())); return serviceInfo; } } serviceInfo = getServiceInfoBySubscribe(serviceName, groupName, clusters, clusterSelector, subscribe); return serviceInfo; } private ServiceInfo getServiceInfoByFailover(String serviceName, String groupName, NamingSelector clusterSelector) { ServiceInfo result = serviceInfoHolder.getFailoverServiceInfo(serviceName, groupName); return doSelectInstance(result, clusterSelector); } private ServiceInfo getServiceInfoBySubscribe(String serviceName, String groupName, List clusters, NamingSelector selector, boolean subscribe) throws NacosException { ServiceInfo serviceInfo; if (subscribe) { serviceInfo = serviceInfoHolder.getServiceInfo(serviceName, groupName); serviceInfo = tryToSubscribe(serviceName, groupName, serviceInfo); serviceInfo = doSelectInstance(serviceInfo, selector); } else { String clusterString = NamingSelectorFactory.getUniqueClusterString(clusters); serviceInfo = clientProxy.queryInstancesOfService(serviceName, groupName, clusterString, false); } return serviceInfo; } private ServiceInfo tryToSubscribe(String serviceName, String groupName, ServiceInfo cachedServiceInfo) throws NacosException { // not found in cache, service never subscribed. if (null == cachedServiceInfo) { return clientProxy.subscribe(serviceName, groupName, StringUtils.EMPTY); } // found in cache, and subscribed. if (clientProxy.isSubscribed(serviceName, groupName, StringUtils.EMPTY)) { return cachedServiceInfo; } // found in cached, but not subscribed, such as cached from local file when starting. ServiceInfo result = cachedServiceInfo; try { result = clientProxy.subscribe(serviceName, groupName, StringUtils.EMPTY); } catch (NacosException e) { NAMING_LOGGER.warn("Subscribe from Server failed, will use local cache. fail message: ", e); } return result; } private ServiceInfo doSelectInstance(ServiceInfo serviceInfo, NamingSelector clusterSelector) { if (null == serviceInfo) { return null; } NamingContext context = new ServiceInfoContext(serviceInfo); NamingResult result = clusterSelector.select(context); serviceInfo.setHosts(result.getResult()); return serviceInfo; } @Override public Instance selectOneHealthyInstance(String serviceName) throws NacosException { return selectOneHealthyInstance(serviceName, new ArrayList<>()); } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException { return selectOneHealthyInstance(serviceName, groupName, true); } @Override public Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException { return selectOneHealthyInstance(serviceName, new ArrayList<>(), subscribe); } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException { return selectOneHealthyInstance(serviceName, groupName, new ArrayList<>(), subscribe); } @Override public Instance selectOneHealthyInstance(String serviceName, List clusters) throws NacosException { return selectOneHealthyInstance(serviceName, clusters, true); } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters) throws NacosException { return selectOneHealthyInstance(serviceName, groupName, clusters, true); } @Override public Instance selectOneHealthyInstance(String serviceName, List clusters, boolean subscribe) throws NacosException { return selectOneHealthyInstance(serviceName, Constants.DEFAULT_GROUP, clusters, subscribe); } @Override public Instance selectOneHealthyInstance(String serviceName, String groupName, List clusters, boolean subscribe) throws NacosException { ServiceInfo serviceInfo = getServiceInfo(serviceName, groupName, clusters, subscribe); return Balancer.RandomByWeight.selectHost(serviceInfo); } @Override public void subscribe(String serviceName, EventListener listener) throws NacosException { subscribe(serviceName, new ArrayList<>(), listener); } @Override public void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException { subscribe(serviceName, groupName, new ArrayList<>(), listener); } @Override public void subscribe(String serviceName, List clusters, EventListener listener) throws NacosException { subscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener); } @Override public void subscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException { NamingSelector clusterSelector = NamingSelectorFactory.newClusterSelector(clusters); doSubscribe(serviceName, groupName, getUniqueClusterString(clusters), clusterSelector, listener); } @Override public void subscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException { subscribe(serviceName, Constants.DEFAULT_GROUP, selector, listener); } @Override public void subscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException { doSubscribe(serviceName, groupName, Constants.NULL, selector, listener); } private void doSubscribe(String serviceName, String groupName, String clusters, NamingSelector selector, EventListener listener) throws NacosException { if (selector == null || listener == null) { return; } NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, groupName, clusters, selector, listener); changeNotifier.registerListener(groupName, serviceName, wrapper); notifyIfSubscribed(serviceName, groupName, wrapper); clientProxy.subscribe(serviceName, groupName, Constants.NULL); } @Override public void unsubscribe(String serviceName, EventListener listener) throws NacosException { unsubscribe(serviceName, new ArrayList<>(), listener); } @Override public void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException { unsubscribe(serviceName, groupName, new ArrayList<>(), listener); } @Override public void unsubscribe(String serviceName, List clusters, EventListener listener) throws NacosException { unsubscribe(serviceName, Constants.DEFAULT_GROUP, clusters, listener); } @Override public void unsubscribe(String serviceName, String groupName, List clusters, EventListener listener) throws NacosException { NamingSelector clusterSelector = NamingSelectorFactory.newClusterSelector(clusters); unsubscribe(serviceName, groupName, clusterSelector, listener); } @Override public void unsubscribe(String serviceName, NamingSelector selector, EventListener listener) throws NacosException { unsubscribe(serviceName, Constants.DEFAULT_GROUP, selector, listener); } @Override public void unsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException { doUnsubscribe(serviceName, groupName, selector, listener); } private void doUnsubscribe(String serviceName, String groupName, NamingSelector selector, EventListener listener) throws NacosException { if (selector == null || listener == null) { return; } NamingSelectorWrapper wrapper = new NamingSelectorWrapper(selector, listener); changeNotifier.deregisterListener(groupName, serviceName, wrapper); if (!changeNotifier.isSubscribed(groupName, serviceName)) { clientProxy.unsubscribe(serviceName, groupName, Constants.NULL); } } @Override public void fuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override public void fuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); } @Override public Future> fuzzyWatchWithServiceKeys(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { return doFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override public Future> fuzzyWatchWithServiceKeys(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher listener) throws NacosException { return doFuzzyWatch(serviceNamePattern, groupNamePattern, listener); } private Future> doFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) { if (null == watcher) { return null; } String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( groupKeyPattern, watcher); return namingFuzzyWatchContext.createNewFuture(); } @Override public void cancelFuzzyWatch(String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { doCancelFuzzyWatch(ANY_PATTERN, fixedGroupName, listener); } @Override public void cancelFuzzyWatch(String serviceNamePattern, String fixedGroupName, FuzzyWatchEventWatcher listener) throws NacosException { doCancelFuzzyWatch(serviceNamePattern, fixedGroupName, listener); } private void doCancelFuzzyWatch(String serviceNamePattern, String groupNamePattern, FuzzyWatchEventWatcher watcher) throws NacosException { if (null == watcher) { return; } String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(serviceNamePattern, groupNamePattern, namespace); NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyWatchContext( groupKeyPattern); if (namingFuzzyWatchContext != null) { namingFuzzyWatchContext.removeWatcher(watcher); } } @Override public ListView getServicesOfServer(int pageNo, int pageSize) throws NacosException { return getServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP); } @Override public ListView getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException { return getServicesOfServer(pageNo, pageSize, groupName, null); } @Override public ListView getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException { return getServicesOfServer(pageNo, pageSize, Constants.DEFAULT_GROUP, selector); } @Override public ListView getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException { return clientProxy.getServiceList(pageNo, pageSize, groupName, selector); } @Override public List getSubscribeServices() { return changeNotifier.getSubscribeServices(); } @Override public String getServerStatus() { return clientProxy.serverHealthy() ? UP : DOWN; } @Override public void shutDown() throws NacosException { serviceInfoHolder.shutdown(); clientProxy.shutdown(); namingFuzzyWatchServiceListHolder.shutdown(); NotifyCenter.deregisterSubscriber(changeNotifier); } private void batchCheckAndStripGroupNamePrefix(List instances, String groupName) throws NacosException { for (Instance instance : instances) { checkAndStripGroupNamePrefix(instance, groupName); } } private void checkAndStripGroupNamePrefix(Instance instance, String groupName) throws NacosException { String serviceName = instance.getServiceName(); if (NamingUtils.isServiceNameCompatibilityMode(serviceName)) { String groupNameOfInstance = NamingUtils.getGroupName(serviceName); if (!groupName.equals(groupNameOfInstance)) { throw new NacosException(NacosException.CLIENT_INVALID_PARAM, String.format( "wrong group name prefix of instance service name! it should be: %s, Instance: %s", groupName, instance)); } instance.setServiceName(NamingUtils.getServiceName(serviceName)); } } private void notifyIfSubscribed(String serviceName, String groupName, NamingSelectorWrapper wrapper) throws NacosException { if (clientProxy.isSubscribed(serviceName, groupName, StringUtils.EMPTY)) { NAMING_LOGGER.warn( "Duplicate subscribe for groupName: {}, serviceName: {}; directly use current cached to notify.", groupName, serviceName); ServiceInfo serviceInfo = serviceInfoHolder.getServiceInfo(serviceName, groupName); InstancesChangeEvent event = transferToEvent(serviceInfo); wrapper.notifyListener(event); } } private InstancesChangeEvent transferToEvent(ServiceInfo serviceInfo) { if (serviceInfo == null) { return null; } InstancesDiff diff = new InstancesDiff(); diff.setAddedInstances(serviceInfo.getHosts()); return new InstancesChangeEvent(notifierEventScope, serviceInfo.getName(), serviceInfo.getGroupName(), serviceInfo.getClusters(), serviceInfo.getHosts(), diff); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverData.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups; /** * Failover Data. * * @author zongkang.guo */ public class FailoverData { /** * failover type,naming or config. */ private final DataType dataType; /** * failover data. */ private final Object data; public FailoverData(DataType dataType, Object data) { this.data = data; this.dataType = dataType; } public enum DataType { /** * naming. */ naming, /** * config. */ config } public DataType getDataType() { return dataType; } public Object getData() { return data; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverDataSource.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups; import java.util.Map; /** * Failover Service Interface. * * @author Nacos */ public interface FailoverDataSource { /** * Get current disaster recovery switch. * * @return */ FailoverSwitch getSwitch(); /** * Get current disaster recovery data. * * @return map */ Map getFailoverData(); } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverReactor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.naming.cache.InstancesDiffer; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.ImmutableTag; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tag; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Failover reactor. * * @author nkorange */ public class FailoverReactor implements Closeable { private Map serviceMap = new ConcurrentHashMap<>(); private boolean failoverSwitchEnable; private final ServiceInfoHolder serviceInfoHolder; private final ScheduledExecutorService executorService; private final InstancesDiffer instancesDiffer; private FailoverDataSource failoverDataSource; private String notifierEventScope; public FailoverReactor(ServiceInfoHolder serviceInfoHolder, String notifierEventScope) { this.serviceInfoHolder = serviceInfoHolder; this.notifierEventScope = notifierEventScope; this.instancesDiffer = new InstancesDiffer(); Collection dataSources = NacosServiceLoader.load(FailoverDataSource.class); for (FailoverDataSource dataSource : dataSources) { failoverDataSource = dataSource; NAMING_LOGGER.info("FailoverDataSource type is {}", dataSource.getClass()); break; } // init executorService this.executorService = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.naming.failover")); this.init(); } /** * Init. */ public void init() { executorService.scheduleWithFixedDelay(new FailoverSwitchRefresher(), 0L, 5000L, TimeUnit.MILLISECONDS); } class FailoverSwitchRefresher implements Runnable { @Override public void run() { try { FailoverSwitch fSwitch = failoverDataSource.getSwitch(); if (fSwitch == null) { failoverSwitchEnable = false; return; } if (fSwitch.getEnabled() != failoverSwitchEnable) { NAMING_LOGGER.info("failover switch changed, new: {}", fSwitch.getEnabled()); } if (fSwitch.getEnabled()) { Map failoverMap = new ConcurrentHashMap<>(200); Map failoverData = failoverDataSource.getFailoverData(); for (Map.Entry entry : failoverData.entrySet()) { ServiceInfo newService = (ServiceInfo) entry.getValue().getData(); ServiceInfo oldService = serviceMap.get(entry.getKey()); InstancesDiff diff = instancesDiffer.doDiff(oldService, newService); if (diff.hasDifferent()) { NAMING_LOGGER.info("[NA] failoverdata isChangedServiceInfo. newService:{}", JacksonUtils.toJson(newService)); NotifyCenter.publishEvent(new InstancesChangeEvent(notifierEventScope, newService.getName(), newService.getGroupName(), newService.getClusters(), newService.getHosts(), diff)); } failoverMap.put(entry.getKey(), (ServiceInfo) entry.getValue().getData()); } if (!failoverMap.isEmpty()) { failoverServiceCntMetrics(); serviceMap = failoverMap; } failoverSwitchEnable = true; return; } if (failoverSwitchEnable && !fSwitch.getEnabled()) { Map serviceInfoMap = serviceInfoHolder.getServiceInfoMap(); for (Map.Entry entry : serviceMap.entrySet()) { ServiceInfo oldService = entry.getValue(); ServiceInfo newService = serviceInfoMap.get(entry.getKey()); if (newService != null) { InstancesDiff diff = instancesDiffer.doDiff(oldService, newService); if (diff.hasDifferent()) { NotifyCenter.publishEvent( new InstancesChangeEvent(notifierEventScope, newService.getName(), newService.getGroupName(), newService.getClusters(), newService.getHosts(), diff)); } } } serviceMap.clear(); failoverSwitchEnable = false; failoverServiceCntMetricsClear(); } } catch (Exception e) { NAMING_LOGGER.error("FailoverSwitchRefresher run err", e); } } } public boolean isFailoverSwitch() { return failoverSwitchEnable; } public boolean isFailoverSwitch(String serviceName) { return failoverSwitchEnable && serviceMap.containsKey(serviceName) && serviceMap.get(serviceName).ipCount() > 0; } public ServiceInfo getService(String key) { ServiceInfo serviceInfo = serviceMap.get(key); if (serviceInfo == null) { serviceInfo = new ServiceInfo(); serviceInfo.setName(key); } return serviceInfo; } /** * shutdown ThreadPool. * * @throws NacosException Nacos exception */ @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); NAMING_LOGGER.info("{} do shutdown begin", className); ThreadUtils.shutdownThreadPool(executorService, NAMING_LOGGER); NAMING_LOGGER.info("{} do shutdown stop", className); } private void failoverServiceCntMetrics() { for (Map.Entry entry : serviceMap.entrySet()) { String serviceName = entry.getKey(); List tags = new ArrayList<>(); tags.add(new ImmutableTag("service_name", serviceName)); if (Metrics.globalRegistry.find("nacos_naming_client_failover_instances").tags(tags).gauge() == null) { Gauge.builder("nacos_naming_client_failover_instances", () -> serviceMap.get(serviceName).ipCount()) .tags(tags).register(Metrics.globalRegistry); } } } private void failoverServiceCntMetricsClear() { for (Map.Entry entry : serviceMap.entrySet()) { Gauge gauge = Metrics.globalRegistry.find("nacos_naming_client_failover_instances") .tag("service_name", entry.getKey()).gauge(); if (gauge != null) { Metrics.globalRegistry.remove(gauge); } } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/backups/FailoverSwitch.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups; /** * Failover switch. * * @author zongkang.guo */ public class FailoverSwitch { /** * Failover switch enable. */ private final boolean enabled; public boolean getEnabled() { return enabled; } public FailoverSwitch(boolean enabled) { this.enabled = enabled; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/backups/NamingFailoverData.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; /** * Naming Failover Data. * * @author zongkang.guo */ public class NamingFailoverData extends FailoverData { private NamingFailoverData(ServiceInfo serviceInfo) { super(DataType.naming, serviceInfo); } public static NamingFailoverData newNamingFailoverData(ServiceInfo serviceInfo) { return new NamingFailoverData(serviceInfo); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/backups/datasource/DiskFailoverDataSource.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups.datasource; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.naming.backups.FailoverData; import com.alibaba.nacos.client.naming.backups.FailoverDataSource; import com.alibaba.nacos.client.naming.backups.FailoverSwitch; import com.alibaba.nacos.client.naming.backups.NamingFailoverData; import com.alibaba.nacos.client.naming.cache.DiskCache; import com.alibaba.nacos.client.naming.utils.CacheDirUtil; import com.alibaba.nacos.client.naming.utils.UtilAndComs; import com.alibaba.nacos.client.utils.ConcurrentDiskUtil; import com.alibaba.nacos.common.utils.StringUtils; import java.io.File; import java.nio.charset.Charset; import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Failover Data Disk Impl. * * @author zongkang.guo */ public class DiskFailoverDataSource implements FailoverDataSource { private static final String FAILOVER_DIR = "/failover"; private static final String IS_FAILOVER_MODE = "1"; private static final String NO_FAILOVER_MODE = "0"; private static final String FAILOVER_MODE_PARAM = "failover-mode"; private static final FailoverSwitch FAILOVER_SWITCH_FALSE = new FailoverSwitch(Boolean.FALSE); private static final FailoverSwitch FAILOVER_SWITCH_TRUE = new FailoverSwitch(Boolean.TRUE); private final Map switchParams = new ConcurrentHashMap<>(); private Map serviceMap = new ConcurrentHashMap<>(); private String failoverDir; private long lastModifiedMillis = 0L; public DiskFailoverDataSource() { failoverDir = CacheDirUtil.getCacheDir() + FAILOVER_DIR; switchParams.put(FAILOVER_MODE_PARAM, Boolean.FALSE.toString()); } class FailoverFileReader implements Runnable { @Override public void run() { Map domMap = new HashMap<>(200); try { File cacheDir = new File(failoverDir); DiskCache.createFileIfAbsent(cacheDir, true); File[] files = cacheDir.listFiles(); if (files == null) { return; } for (File file : files) { if (!file.isFile()) { continue; } if (file.getName().equals(UtilAndComs.FAILOVER_SWITCH)) { continue; } for (Map.Entry entry : DiskCache.parseServiceInfoFromCache(file).entrySet()) { domMap.put(entry.getKey(), NamingFailoverData.newNamingFailoverData(entry.getValue())); } } } catch (Exception e) { NAMING_LOGGER.error("[NA] failed to read cache file", e); } if (domMap.size() > 0) { serviceMap = domMap; } } } @Override public FailoverSwitch getSwitch() { try { File switchFile = Paths.get(failoverDir, UtilAndComs.FAILOVER_SWITCH).toFile(); if (!switchFile.exists()) { NAMING_LOGGER.debug("failover switch is not found, {}", switchFile.getName()); switchParams.put(FAILOVER_MODE_PARAM, Boolean.FALSE.toString()); return FAILOVER_SWITCH_FALSE; } long modified = switchFile.lastModified(); if (lastModifiedMillis < modified) { lastModifiedMillis = modified; String failover = ConcurrentDiskUtil.getFileContent(switchFile.getPath(), Charset.defaultCharset().toString()); if (!StringUtils.isEmpty(failover)) { String[] lines = failover.split(DiskCache.getLineSeparator()); for (String line : lines) { String line1 = line.trim(); if (IS_FAILOVER_MODE.equals(line1)) { switchParams.put(FAILOVER_MODE_PARAM, Boolean.TRUE.toString()); NAMING_LOGGER.info("failover-mode is on"); new FailoverFileReader().run(); return FAILOVER_SWITCH_TRUE; } else if (NO_FAILOVER_MODE.equals(line1)) { switchParams.put(FAILOVER_MODE_PARAM, Boolean.FALSE.toString()); NAMING_LOGGER.info("failover-mode is off"); return FAILOVER_SWITCH_FALSE; } } } } return switchParams.get(FAILOVER_MODE_PARAM).equals(Boolean.TRUE.toString()) ? FAILOVER_SWITCH_TRUE : FAILOVER_SWITCH_FALSE; } catch (Throwable e) { NAMING_LOGGER.error("[NA] failed to read failover switch.", e); switchParams.put(FAILOVER_MODE_PARAM, Boolean.FALSE.toString()); return FAILOVER_SWITCH_FALSE; } } @Override public Map getFailoverData() { if (Boolean.parseBoolean(switchParams.get(FAILOVER_MODE_PARAM))) { return serviceMap; } return new ConcurrentHashMap<>(0); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/cache/DiskCache.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.utils.ConcurrentDiskUtil; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Disk cache. * * @author xuanyin */ public class DiskCache { /** * Write service info to dir. * * @param dom service info * @param dir directory */ public static void write(ServiceInfo dom, String dir) { try { makeSureCacheDirExists(dir); File file = new File(dir, dom.getKeyEncoded()); createFileIfAbsent(file, false); StringBuilder keyContentBuffer = new StringBuilder(); String json = dom.getJsonFromServer(); if (StringUtils.isEmpty(json)) { json = JacksonUtils.toJson(dom); } keyContentBuffer.append(json); //Use the concurrent API to ensure the consistency. ConcurrentDiskUtil.writeFileContent(file, keyContentBuffer.toString(), Charset.defaultCharset().toString()); } catch (Throwable e) { NAMING_LOGGER.error("[NA] failed to write cache for dom:" + dom.getName(), e); } } public static String getLineSeparator() { return System.getProperty("line.separator"); } /** * Read service info from disk. * * @param cacheDir cache file dir * @return service infos */ public static Map read(String cacheDir) { Map domMap = new HashMap<>(16); try { File[] files = makeSureCacheDirExists(cacheDir).listFiles(); if (files == null || files.length == 0) { return domMap; } for (File file : files) { if (!file.isFile()) { continue; } domMap.putAll(parseServiceInfoFromCache(file)); } } catch (Throwable e) { NAMING_LOGGER.error("[NA] failed to read cache file", e); } return domMap; } /** * Parse Service info from cache file or failover file. * * @param file cache file or failover file * @return Service info * @throws UnsupportedEncodingException if the file is not encoded in UTF-8 */ public static Map parseServiceInfoFromCache(File file) throws UnsupportedEncodingException { Map result = new HashMap<>(1); String fileName = URLDecoder.decode(file.getName(), "UTF-8"); if (!(fileName.endsWith(Constants.SERVICE_INFO_SPLITER + "meta") || fileName .endsWith(Constants.SERVICE_INFO_SPLITER + "special-url"))) { ServiceInfo dom = new ServiceInfo(fileName); List ips = new ArrayList<>(); dom.setHosts(ips); ServiceInfo newFormat = null; try (BufferedReader reader = new BufferedReader( new StringReader(ConcurrentDiskUtil.getFileContent(file, Charset.defaultCharset().toString())))) { String json; while ((json = reader.readLine()) != null) { try { if (!json.startsWith("{")) { continue; } newFormat = JacksonUtils.toObj(json, ServiceInfo.class); if (StringUtils.isEmpty(newFormat.getName())) { ips.add(JacksonUtils.toObj(json, Instance.class)); } } catch (Throwable e) { NAMING_LOGGER.error("[NA] error while parsing cache file: " + json, e); } } } catch (Exception e) { NAMING_LOGGER.error("[NA] failed to read cache for dom: " + file.getName(), e); } if (newFormat != null && !StringUtils.isEmpty(newFormat.getName()) && !CollectionUtils .isEmpty(newFormat.getHosts())) { result.put(dom.getKey(), newFormat); } else if (!CollectionUtils.isEmpty(dom.getHosts())) { result.put(dom.getKey(), dom); } } return result; } /** * Create file if absent. * * @param file file * @param isDir is dir * @throws IOException if any io exception during create. */ public static void createFileIfAbsent(File file, boolean isDir) throws IOException { if (file.exists()) { return; } boolean createResult = isDir ? file.mkdirs() : file.createNewFile(); if (!createResult && !file.exists()) { throw new IllegalStateException("failed to create cache : " + (isDir ? "dir" : file) + file.getPath()); } } private static File makeSureCacheDirExists(String dir) throws IOException { File cacheDir = new File(dir); createFileIfAbsent(cacheDir, true); return cacheDir; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/cache/FuzzyWatchEventWatcherWrapper.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.UUID; /** * fuzzy watcher wrapper. * * @author shiyiyue */ public class FuzzyWatchEventWatcherWrapper { long syncVersion = 0; FuzzyWatchEventWatcher fuzzyWatchEventWatcher; String uuid = UUID.randomUUID().toString(); public FuzzyWatchEventWatcherWrapper(FuzzyWatchEventWatcher fuzzyWatchEventWatcher) { this.fuzzyWatchEventWatcher = fuzzyWatchEventWatcher; } private Set syncServiceKeys = new HashSet<>(); final String getUuid() { return uuid; } Set getSyncServiceKeys() { return syncServiceKeys; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } FuzzyWatchEventWatcherWrapper that = (FuzzyWatchEventWatcherWrapper) o; return Objects.equals(fuzzyWatchEventWatcher, that.fuzzyWatchEventWatcher); } @Override public int hashCode() { return Objects.hash(fuzzyWatchEventWatcher); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/cache/InstancesDiffer.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; 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 static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * The instance list differ for nacos naming. * * @author xiweng.yy */ public final class InstancesDiffer { /** * Do instance different for input service info. * * @param oldService old service info * @param newService new service info * @return {@link InstancesDiff} of the differences between old and new service info. */ public InstancesDiff doDiff(ServiceInfo oldService, ServiceInfo newService) { InstancesDiff instancesDiff = new InstancesDiff(); if (null == oldService) { NAMING_LOGGER.info("init new ips({}) service: {} -> {}", newService.ipCount(), newService.getKey(), JacksonUtils.toJson(newService.getHosts())); instancesDiff.setAddedInstances(newService.getHosts()); return instancesDiff; } if (oldService.getLastRefTime() > newService.getLastRefTime()) { NAMING_LOGGER.warn("out of date data received, old-t: {}, new-t: {}", oldService.getLastRefTime(), newService.getLastRefTime()); return instancesDiff; } Map oldHostMap = new HashMap<>(oldService.getHosts().size()); for (Instance host : oldService.getHosts()) { oldHostMap.put(host.toInetAddr(), host); } Map newHostMap = new HashMap<>(newService.getHosts().size()); for (Instance host : newService.getHosts()) { newHostMap.put(host.toInetAddr(), host); } Set modHosts = new HashSet<>(); Set newHosts = new HashSet<>(); Set remvHosts = new HashSet<>(); List> newServiceHosts = new ArrayList<>(newHostMap.entrySet()); for (Map.Entry entry : newServiceHosts) { Instance host = entry.getValue(); String key = entry.getKey(); if (oldHostMap.containsKey(key) && !StringUtils.equals(host.toString(), oldHostMap.get(key).toString())) { modHosts.add(host); continue; } if (!oldHostMap.containsKey(key)) { newHosts.add(host); } } for (Map.Entry entry : oldHostMap.entrySet()) { Instance host = entry.getValue(); String key = entry.getKey(); if (newHostMap.containsKey(key)) { continue; } //add to remove hosts remvHosts.add(host); } if (newHosts.size() > 0) { NAMING_LOGGER.info("new ips({}) service: {} -> {}", newHosts.size(), newService.getKey(), JacksonUtils.toJson(newHosts)); instancesDiff.setAddedInstances(newHosts); } if (remvHosts.size() > 0) { NAMING_LOGGER.info("removed ips({}) service: {} -> {}", remvHosts.size(), newService.getKey(), JacksonUtils.toJson(remvHosts)); instancesDiff.setRemovedInstances(remvHosts); } if (modHosts.size() > 0) { NAMING_LOGGER.info("modified ips({}) service: {} -> {}", modHosts.size(), newService.getKey(), JacksonUtils.toJson(modHosts)); instancesDiff.setModifiedInstances(modHosts); } return instancesDiff; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchContext.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.listener.FuzzyWatchLoadWatcher; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * fuzzy wather context for a single group key pattern. * *

    This class manages the context information for fuzzy listening, including environment name, task ID, data ID * pattern, group, tenant, listener set, and other related information. *

    * * @author stone-98 * @date 2024/3/4 */ public class NamingFuzzyWatchContext { /** * Logger for FuzzyListenContext. */ private static final Logger LOGGER = LogUtils.logger(NamingFuzzyWatchContext.class); /** * Environment name. */ private String envName; private String groupKeyPattern; /** * Set of service keys associated with the context. */ private Set receivedServiceKeys = new ConcurrentHashSet<>(); private long syncVersion = 0; /** * Flag indicating whether the context is consistent with the server. */ private final AtomicBoolean isConsistentWithServer = new AtomicBoolean(); /** * Condition object for waiting initialization completion. */ final AtomicBoolean initializationCompleted = new AtomicBoolean(false); /** * Flag indicating whether the context is discarded. */ private volatile boolean isDiscard = false; /** * Set of listeners associated with the context. */ private final Set fuzzyWatchEventWatcherWrappers = new HashSet<>(); long patternLimitTs = 0; private static final long SUPPRESSED_PERIOD = 60 * 1000L; boolean patternLimitSuppressed() { return patternLimitTs > 0 && System.currentTimeMillis() - patternLimitTs < SUPPRESSED_PERIOD; } public void clearOverLimitTs() { this.patternLimitTs = 0; } public void refreshOverLimitTs() { this.patternLimitTs = System.currentTimeMillis(); } public void refreshSyncVersion() { this.syncVersion = System.currentTimeMillis(); } /** * Constructor with environment name, data ID pattern, and group. * * @param envName Environment name * @param groupKeyPattern groupKeyPattern */ public NamingFuzzyWatchContext(String envName, String groupKeyPattern) { this.envName = envName; this.groupKeyPattern = groupKeyPattern; } private void doNotifyWatcher(final String serviceKey, final String changedType, final String syncType, FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper) { if (ADD_SERVICE.equals(changedType) && fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() .contains(serviceKey)) { return; } if (DELETE_SERVICE.equals(changedType) && !fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() .contains(serviceKey)) { return; } String[] serviceKeyItems = NamingUtils.parseServiceKey(serviceKey); String namespace = serviceKeyItems[0]; String groupName = serviceKeyItems[1]; String serviceName = serviceKeyItems[2]; final String resetSyncType = !initializationCompleted.get() ? FUZZY_WATCH_INIT_NOTIFY : syncType; Runnable job = () -> { long start = System.currentTimeMillis(); FuzzyWatchChangeEvent event = new FuzzyWatchChangeEvent(serviceName, groupName, namespace, changedType, resetSyncType); if (fuzzyWatchEventWatcherWrapper != null) { fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.onEvent(event); } LOGGER.info( "[{}] [notify-watcher-ok] serviceName={}, groupName={}, namespace={}, watcher={},changedType={}, job run cost={} millis.", envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher, changedType, (System.currentTimeMillis() - start)); if (changedType.equals(DELETE_SERVICE)) { fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() .remove(NamingUtils.getServiceKey(namespace, groupName, serviceName)); } else if (changedType.equals(ADD_SERVICE)) { fuzzyWatchEventWatcherWrapper.getSyncServiceKeys() .add(NamingUtils.getServiceKey(namespace, groupName, serviceName)); } }; try { if (null != fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.getExecutor()) { LOGGER.info( "[{}] [notify-watcher] task submitted to user executor, serviceName={}, groupName={}, namespace={}, listener={}.", envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper); fuzzyWatchEventWatcherWrapper.fuzzyWatchEventWatcher.getExecutor().execute(job); } else { LOGGER.info( "[{}] [notify-watcher] task execute in nacos thread, serviceName={}, groupName={}, namespace={}, listener={}.", envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper); job.run(); } } catch (Throwable t) { LOGGER.error( "[{}] [notify-watcher-error] serviceName={}, groupName={}, namespace={}, listener={}, throwable={}.", envName, serviceName, groupName, namespace, fuzzyWatchEventWatcherWrapper, t.getCause()); } } /** * Mark initialization as complete and notify waiting threads. */ public void markInitializationComplete() { LOGGER.info("[{}] [fuzzy-watch] pattern init notify finish pattern={},match service count {}", envName, groupKeyPattern, receivedServiceKeys.size()); initializationCompleted.set(true); synchronized (this) { notifyAll(); } } /** * Remove a watcher from the context. * * @param watcher watcher to be removed */ public synchronized void removeWatcher(FuzzyWatchEventWatcher watcher) { Iterator iterator = fuzzyWatchEventWatcherWrappers.iterator(); while (iterator.hasNext()) { FuzzyWatchEventWatcherWrapper next = iterator.next(); if (next.fuzzyWatchEventWatcher.equals(watcher)) { iterator.remove(); LOGGER.info("[{}] [remove-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", getEnvName(), this.groupKeyPattern, watcher, next.getUuid()); } } if (fuzzyWatchEventWatcherWrappers.isEmpty()) { this.setConsistentWithServer(false); this.setDiscard(true); } } /** * Get the environment name. * * @return Environment name */ public String getEnvName() { return envName; } /** * Set the environment name. * * @param envName Environment name to be set */ public void setEnvName(String envName) { this.envName = envName; } public String getGroupKeyPattern() { return groupKeyPattern; } /** * Get the flag indicating whether the context is consistent with the server. * * @return AtomicBoolean indicating whether the context is consistent with the server */ public boolean isConsistentWithServer() { return isConsistentWithServer.get(); } public void setConsistentWithServer(boolean isConsistentWithServer) { this.isConsistentWithServer.set(isConsistentWithServer); } /** * Check if the context is discarded. * * @return True if the context is discarded, otherwise false */ public boolean isDiscard() { return isDiscard; } /** * Set the flag indicating whether the context is discarded. * * @param discard True to mark the context as discarded, otherwise false */ public void setDiscard(boolean discard) { isDiscard = discard; } /** * Check if the context is initializing. * * @return True if the context is initializing, otherwise false */ public boolean isInitializing() { return !initializationCompleted.get(); } /** * Get the set of data IDs associated with the context. * * @return Set of data IDs */ public Set getReceivedServiceKeys() { return Collections.unmodifiableSet(receivedServiceKeys); } /** * add received service key. * * @param serviceKey service key. * @return */ public boolean addReceivedServiceKey(String serviceKey) { boolean added = receivedServiceKeys.add(serviceKey); if (added) { refreshSyncVersion(); } return added; } /** * remove received service key. * * @param serviceKey service key. * @return */ public boolean removeReceivedServiceKey(String serviceKey) { boolean removed = receivedServiceKeys.remove(serviceKey); if (removed) { refreshSyncVersion(); } return removed; } /** * Get the set of listeners associated with the context. * * @return Set of listeners */ public Set getFuzzyWatchEventWatcherWrappers() { return fuzzyWatchEventWatcherWrappers; } void syncFuzzyWatchers() { for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcher : fuzzyWatchEventWatcherWrappers) { if (namingFuzzyWatcher.syncVersion == this.syncVersion) { continue; } Set receivedServiceKeysContext = new HashSet<>(this.getReceivedServiceKeys()); Set syncGroupKeys = namingFuzzyWatcher.getSyncServiceKeys(); List groupKeyStates = FuzzyGroupKeyPattern.diffGroupKeys( receivedServiceKeysContext, syncGroupKeys); if (CollectionUtils.isEmpty(groupKeyStates)) { namingFuzzyWatcher.syncVersion = this.syncVersion; } else { for (FuzzyGroupKeyPattern.GroupKeyState groupKeyState : groupKeyStates) { String changedType = groupKeyState.isExist() ? ADD_SERVICE : DELETE_SERVICE; doNotifyWatcher(groupKeyState.getGroupKey(), changedType, FUZZY_WATCH_DIFF_SYNC_NOTIFY, namingFuzzyWatcher); } } } } void notifyFuzzyWatchers(String serviceKey, String changedType, String syncType, String watcherUuid) { for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcher : filterWatchers(watcherUuid)) { doNotifyWatcher(serviceKey, changedType, syncType, namingFuzzyWatcher); } } void notifyOverLimitWatchers(int code) { if (this.patternLimitSuppressed()) { return; } boolean notify = false; for (FuzzyWatchEventWatcherWrapper namingFuzzyWatcherWrapper : filterWatchers(null)) { if (namingFuzzyWatcherWrapper.fuzzyWatchEventWatcher instanceof FuzzyWatchLoadWatcher) { if (FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode().equals(code)) { ((FuzzyWatchLoadWatcher) namingFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onServiceReachUpLimit(); notify = true; } if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode().equals(code)) { ((FuzzyWatchLoadWatcher) namingFuzzyWatcherWrapper.fuzzyWatchEventWatcher).onPatternOverLimit(); notify = true; } } } if (notify) { this.refreshOverLimitTs(); } } private Set filterWatchers(String uuid) { if (StringUtils.isBlank(uuid) || CollectionUtils.isEmpty(getFuzzyWatchEventWatcherWrappers())) { return getFuzzyWatchEventWatcherWrappers(); } else { return getFuzzyWatchEventWatcherWrappers().stream().filter(a -> a.getUuid().equals(uuid)) .collect(Collectors.toSet()); } } /** * create a new future of this context. * * @return */ public Future> createNewFuture() { Future> completableFuture = new Future>() { @Override public boolean cancel(boolean mayInterruptIfRunning) { throw new UnsupportedOperationException("not support to cancel fuzzy watch"); } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return NamingFuzzyWatchContext.this.initializationCompleted.get(); } @Override public ListView get() throws InterruptedException { synchronized (NamingFuzzyWatchContext.this) { while (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { NamingFuzzyWatchContext.this.wait(); } } ListView result = new ListView<>(); result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); result.setCount(result.getData().size()); return result; } @Override public ListView get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException { if (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { synchronized (NamingFuzzyWatchContext.this) { NamingFuzzyWatchContext.this.wait(unit.toMillis(timeout)); } } if (!NamingFuzzyWatchContext.this.initializationCompleted.get()) { throw new TimeoutException( "fuzzy watch result future timeout for " + unit.toMillis(timeout) + " millis"); } ListView result = new ListView<>(); result.setData(Arrays.asList(NamingFuzzyWatchContext.this.receivedServiceKeys.toArray(new String[0]))); result.setCount(result.getData().size()); return result; } }; return completableFuture; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolder.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.naming.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchLoadEvent; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import org.slf4j.Logger; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * Naming client fuzzy watch service list holder. * * @author tanyongquan */ public class NamingFuzzyWatchServiceListHolder extends SmartSubscriber implements Closeable { private static final Logger LOGGER = LogUtils.logger(NamingFuzzyWatchServiceListHolder.class); private String notifierEventScope; private NamingGrpcClientProxy namingGrpcClientProxy; /** * fuzzyListenExecuteBell. */ private final BlockingQueue fuzzyWatchExecuteBell = new ArrayBlockingQueue<>(1); private final Object bellItem = new Object(); private final AtomicLong fuzzyWatchLastAllSyncTime = new AtomicLong(System.currentTimeMillis()); private static final long FUZZY_LISTEN_ALL_SYNC_INTERNAL = 3 * 60 * 1000; ScheduledExecutorService executorService; /** * The contents of {@code patternMatchMap} are Map{pattern -> Set[matched services]}. */ private Map fuzzyMatchContextMap = new ConcurrentHashMap<>(); public NamingFuzzyWatchServiceListHolder(String notifierEventScope) { this.notifierEventScope = notifierEventScope; NotifyCenter.registerSubscriber(this); } /** * shut down. */ @Override public void shutdown() { // deregister subscriber which registered in constructor NotifyCenter.deregisterSubscriber(this); if (executorService != null && !executorService.isShutdown()) { executorService.shutdown(); } } /** * start. */ public void start() { executorService = Executors.newSingleThreadScheduledExecutor( new NameThreadFactory("com.alibaba.nacos.client.naming.fuzzy.watch.Worker")); executorService.submit(() -> { while (!executorService.isShutdown() && !executorService.isTerminated()) { try { fuzzyWatchExecuteBell.poll(5L, TimeUnit.SECONDS); if (executorService.isShutdown() || executorService.isTerminated()) { continue; } executeNamingFuzzyWatch(); } catch (Throwable e) { LOGGER.error("[rpc-fuzzy-watch-execute] rpc fuzzy watch exception", e); try { Thread.sleep(50L); } catch (InterruptedException interruptedException) { //ignore } notifyFuzzyWatchSync(); } } }); } public void registerNamingGrpcClientProxy(NamingGrpcClientProxy namingGrpcClientProxy) { this.namingGrpcClientProxy = namingGrpcClientProxy; } public NamingFuzzyWatchContext getFuzzyWatchContext(String groupKeyPattern) { return fuzzyMatchContextMap.get(groupKeyPattern); } /** * Add a watcher to the context. * * @param watcher watcher to be added */ public NamingFuzzyWatchContext registerFuzzyWatcher(String groupKeyPattern, FuzzyWatchEventWatcher watcher) { if (!namingGrpcClientProxy.isAbilitySupportedByServer(AbilityKey.SERVER_FUZZY_WATCH)) { throw new NacosRuntimeException(NacosException.SERVER_NOT_IMPLEMENTED, "Request Nacos server version is too low, not support fuzzy watch feature."); } NamingFuzzyWatchContext namingFuzzyWatchContext = initFuzzyWatchContextIfNeed(groupKeyPattern); namingFuzzyWatchContext.setDiscard(false); synchronized (namingFuzzyWatchContext) { FuzzyWatchEventWatcherWrapper fuzzyWatchEventWatcherWrapper = new FuzzyWatchEventWatcherWrapper(watcher); if (namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers().add(fuzzyWatchEventWatcherWrapper)) { LOGGER.info(" [add-watcher-ok] groupKeyPattern={}, watcher={},uuid={} ", groupKeyPattern, watcher, fuzzyWatchEventWatcherWrapper.getUuid()); Set receivedServiceKeys = namingFuzzyWatchContext.getReceivedServiceKeys(); if (CollectionUtils.isNotEmpty(receivedServiceKeys)) { for (String serviceKey : receivedServiceKeys) { NamingFuzzyWatchNotifyEvent namingFuzzyWatchNotifyEvent = NamingFuzzyWatchNotifyEvent.build( notifierEventScope, groupKeyPattern, serviceKey, ADD_SERVICE, FUZZY_WATCH_INIT_NOTIFY, fuzzyWatchEventWatcherWrapper.getUuid()); NotifyCenter.publishEvent(namingFuzzyWatchNotifyEvent); } } } } return namingFuzzyWatchContext; } /** * init fuzzy watch context. * * @param groupKeyPattern groupKeyPattern. * @return fuzzy context. */ public NamingFuzzyWatchContext initFuzzyWatchContextIfNeed(String groupKeyPattern) { if (!fuzzyMatchContextMap.containsKey(groupKeyPattern)) { synchronized (fuzzyMatchContextMap) { if (fuzzyMatchContextMap.containsKey(groupKeyPattern)) { return fuzzyMatchContextMap.get(groupKeyPattern); } LOGGER.info("[fuzzy-watch] init fuzzy watch context for pattern {}", groupKeyPattern); fuzzyMatchContextMap.putIfAbsent(groupKeyPattern, new NamingFuzzyWatchContext(notifierEventScope, groupKeyPattern)); notifyFuzzyWatchSync(); } } return fuzzyMatchContextMap.get(groupKeyPattern); } /** * remove fuzzy watch context for pattern. * * @param groupKeyPattern group key pattern. */ public synchronized void removePatternMatchCache(String groupKeyPattern) { NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(groupKeyPattern); if (namingFuzzyWatchContext == null) { return; } if (namingFuzzyWatchContext.isDiscard() && namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers() .isEmpty()) { LOGGER.info("[fuzzy-watch] remove fuzzy watch context for pattern {}", groupKeyPattern); fuzzyMatchContextMap.remove(groupKeyPattern); } } /** * notify sync fuzzy watch with server. */ void notifyFuzzyWatchSync() { fuzzyWatchExecuteBell.offer(bellItem); } /** * Execute fuzzy listen configuration changes. * *

    This method iterates through all fuzzy listen contexts and determines whether they need to be added or * removed based on their consistency with the server and discard status. It then calls the appropriate method to * execute the fuzzy listen operation. * * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ public void executeNamingFuzzyWatch() throws NacosException { // Obtain the current timestamp long now = System.currentTimeMillis(); // Determine whether a full synchronization is needed boolean needAllSync = now - fuzzyWatchLastAllSyncTime.get() >= FUZZY_LISTEN_ALL_SYNC_INTERNAL; List needSyncContexts = new ArrayList<>(); // Iterate through all fuzzy listen contexts for (NamingFuzzyWatchContext context : fuzzyMatchContextMap.values()) { // Check if the context is consistent with the server if (context.isConsistentWithServer()) { context.syncFuzzyWatchers(); // Skip if a full synchronization is not needed if (!needAllSync) { continue; } } needSyncContexts.add(context); } // Execute fuzzy listen operation for addition doExecuteNamingFuzzyWatch(needSyncContexts); // Update last all sync time if a full synchronization was performed if (needAllSync) { fuzzyWatchLastAllSyncTime.set(now); } } public void resetConsistenceStatus() { fuzzyMatchContextMap.values() .forEach(fuzzyWatcherContext -> fuzzyWatcherContext.setConsistentWithServer(false)); } /** * Execute fuzzy listen configuration changes for a specific map of contexts. * *

    This method submits tasks to execute fuzzy listen operations asynchronously for the provided contexts. It * waits for all tasks to complete and logs any errors that occur. * * @param contextLists The map of contexts to execute fuzzy listen operations for. * @throws NacosException If an error occurs during the execution of fuzzy listen configuration changes. */ private void doExecuteNamingFuzzyWatch(List contextLists) throws NacosException { // Return if the context map is null or empty if (CollectionUtils.isEmpty(contextLists)) { return; } // Iterate through the context map and submit tasks for execution for (NamingFuzzyWatchContext entry : contextLists) { // Submit task for execution NamingFuzzyWatchRequest configFuzzyWatchRequest = buildFuzzyWatchNamingRequest(entry); try { // Execute the fuzzy listen operation NamingFuzzyWatchResponse listenResponse = namingGrpcClientProxy.fuzzyWatchRequest( configFuzzyWatchRequest); if (listenResponse != null && listenResponse.isSuccess()) { if (configFuzzyWatchRequest.getWatchType().equals(WATCH_TYPE_CANCEL_WATCH)) { removePatternMatchCache(entry.getGroupKeyPattern()); } else { entry.setConsistentWithServer(true); } entry.clearOverLimitTs(); } } catch (NacosException e) { // Log error and retry after a short delay if (FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode() == e.getErrCode() || FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode() == e.getErrCode()) { LOGGER.error(" fuzzy watch pattern over limit,pattern ->{} ,fuzzy watch will be suppressed,msg={}", entry.getGroupKeyPattern(), e.getErrMsg()); NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEvent = NamingFuzzyWatchLoadEvent.buildEvent( e.getErrCode(), entry.getGroupKeyPattern(), notifierEventScope); NotifyCenter.publishEvent(namingFuzzyWatchLoadEvent); } else { LOGGER.error(" fuzzy watch request fail.", e); try { Thread.sleep(1000L); } catch (InterruptedException interruptedException) { // Ignore interruption } // Retry notification notifyFuzzyWatchSync(); } } } } private NamingFuzzyWatchRequest buildFuzzyWatchNamingRequest(NamingFuzzyWatchContext namingFuzzyWatchContext) { NamingFuzzyWatchRequest namingFuzzyWatchRequest = new NamingFuzzyWatchRequest(); namingFuzzyWatchRequest.setInitializing(namingFuzzyWatchContext.isInitializing()); namingFuzzyWatchRequest.setNamespace(namingGrpcClientProxy.getNamespaceId()); namingFuzzyWatchRequest.setReceivedGroupKeys(namingFuzzyWatchContext.getReceivedServiceKeys()); namingFuzzyWatchRequest.setGroupKeyPattern(namingFuzzyWatchContext.getGroupKeyPattern()); if (namingFuzzyWatchContext.isDiscard() && namingFuzzyWatchContext.getFuzzyWatchEventWatcherWrappers() .isEmpty()) { namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_CANCEL_WATCH); } else { namingFuzzyWatchRequest.setWatchType(WATCH_TYPE_WATCH); } return namingFuzzyWatchRequest; } public Map getFuzzyMatchContextMap() { return fuzzyMatchContextMap; } @Override public void onEvent(Event event) { if (event instanceof NamingFuzzyWatchNotifyEvent) { if (!event.scope().equals(notifierEventScope)) { return; } NamingFuzzyWatchNotifyEvent watchNotifyEvent = (NamingFuzzyWatchNotifyEvent) event; String changedType = watchNotifyEvent.getChangedType(); String syncType = watchNotifyEvent.getSyncType(); String serviceKey = watchNotifyEvent.getServiceKey(); String pattern = watchNotifyEvent.getPattern(); String watchUuid = watchNotifyEvent.getWatcherUuid(); NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get(pattern); if (namingFuzzyWatchContext == null) { return; } namingFuzzyWatchContext.notifyFuzzyWatchers(serviceKey, changedType, syncType, watchUuid); } if (event instanceof NamingFuzzyWatchLoadEvent) { if (!event.scope().equals(notifierEventScope)) { return; } NamingFuzzyWatchLoadEvent overLimitEvent = (NamingFuzzyWatchLoadEvent) event; NamingFuzzyWatchContext namingFuzzyWatchContext = fuzzyMatchContextMap.get( overLimitEvent.getGroupKeyPattern()); if (namingFuzzyWatchContext == null) { return; } namingFuzzyWatchContext.notifyOverLimitWatchers(overLimitEvent.getCode()); } } @Override public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(NamingFuzzyWatchNotifyEvent.class); result.add(NamingFuzzyWatchLoadEvent.class); return result; } public String getNotifierEventScope() { return notifierEventScope; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/cache/ServiceInfoHolder.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.backups.FailoverReactor; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.utils.CacheDirUtil; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Naming client service information holder. * * @author xiweng.yy */ public class ServiceInfoHolder implements Closeable { private final ConcurrentMap serviceInfoMap; private final FailoverReactor failoverReactor; private final boolean pushEmptyProtection; private final InstancesDiffer instancesDiffer; private String cacheDir; private String notifierEventScope; private boolean enableClientMetrics = true; public ServiceInfoHolder(String namespace, String notifierEventScope, NacosClientProperties properties) { cacheDir = CacheDirUtil.initCacheDir(namespace, properties); instancesDiffer = new InstancesDiffer(); if (isLoadCacheAtStart(properties)) { this.serviceInfoMap = new ConcurrentHashMap<>(DiskCache.read(this.cacheDir)); } else { this.serviceInfoMap = new ConcurrentHashMap<>(16); } this.failoverReactor = new FailoverReactor(this, notifierEventScope); this.pushEmptyProtection = isPushEmptyProtect(properties); this.notifierEventScope = notifierEventScope; this.enableClientMetrics = Boolean.parseBoolean( properties.getProperty(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true")); } private boolean isLoadCacheAtStart(NacosClientProperties properties) { boolean loadCacheAtStart = false; if (properties != null && StringUtils.isNotEmpty( properties.getProperty(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START))) { loadCacheAtStart = ConvertUtils.toBoolean( properties.getProperty(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START)); } return loadCacheAtStart; } private boolean isPushEmptyProtect(NacosClientProperties properties) { boolean pushEmptyProtection = false; if (properties != null && StringUtils.isNotEmpty( properties.getProperty(PropertyKeyConst.NAMING_PUSH_EMPTY_PROTECTION))) { pushEmptyProtection = ConvertUtils.toBoolean( properties.getProperty(PropertyKeyConst.NAMING_PUSH_EMPTY_PROTECTION)); } return pushEmptyProtection; } public Map getServiceInfoMap() { return serviceInfoMap; } public ServiceInfo getServiceInfo(final String serviceName, final String groupName) { String key = NamingUtils.getGroupedName(serviceName, groupName); ServiceInfo serviceInfo = serviceInfoMap.get(key); return serviceInfo == null ? null : serviceInfo.clone(); } /** * Process service json. * * @param json service json * @return service info */ public ServiceInfo processServiceInfo(String json) { ServiceInfo serviceInfo = JacksonUtils.toObj(json, ServiceInfo.class); serviceInfo.setJsonFromServer(json); return processServiceInfo(serviceInfo); } /** * Process service info. * * @param serviceInfo new service info * @return service info */ public ServiceInfo processServiceInfo(ServiceInfo serviceInfo) { String serviceKey = serviceInfo.getKeyWithoutClusters(); if (serviceKey == null) { NAMING_LOGGER.warn("process service info but serviceKey is null, service host: {}", JacksonUtils.toJson(serviceInfo.getHosts())); return null; } ServiceInfo oldService = serviceInfoMap.get(serviceKey); if (isEmptyOrErrorPush(serviceInfo)) { //empty or error push, just ignore NAMING_LOGGER.warn("process service info but found empty or error push, serviceKey: {}, " + "pushEmptyProtection: {}, hosts: {}", serviceKey, pushEmptyProtection, serviceInfo.getHosts()); return oldService; } serviceInfoMap.put(serviceKey, serviceInfo); InstancesDiff diff = getServiceInfoDiff(oldService, serviceInfo); if (StringUtils.isBlank(serviceInfo.getJsonFromServer())) { serviceInfo.setJsonFromServer(JacksonUtils.toJson(serviceInfo)); } if (enableClientMetrics) { try { MetricsMonitor.getServiceInfoMapSizeMonitor().set(serviceInfoMap.size()); } catch (Throwable t) { NAMING_LOGGER.error("Failed to update metrics for service info map size", t); } } if (diff.hasDifferent()) { NAMING_LOGGER.info("current ips:({}) service: {} -> {}", serviceInfo.ipCount(), serviceKey, JacksonUtils.toJson(serviceInfo.getHosts())); if (!failoverReactor.isFailoverSwitch(serviceKey)) { NotifyCenter.publishEvent( new InstancesChangeEvent(notifierEventScope, serviceInfo.getName(), serviceInfo.getGroupName(), serviceInfo.getClusters(), serviceInfo.getHosts(), diff)); } DiskCache.write(serviceInfo, cacheDir); } return serviceInfo; } private boolean isEmptyOrErrorPush(ServiceInfo serviceInfo) { return null == serviceInfo.getHosts() || (pushEmptyProtection && !serviceInfo.validate()); } private InstancesDiff getServiceInfoDiff(ServiceInfo oldService, ServiceInfo newService) { return instancesDiffer.doDiff(oldService, newService); } public String getCacheDir() { return cacheDir; } public boolean isFailoverSwitch() { return failoverReactor.isFailoverSwitch(); } public ServiceInfo getFailoverServiceInfo(final String serviceName, final String groupName) { String key = NamingUtils.getGroupedName(serviceName, groupName); return failoverReactor.getService(key); } @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); NAMING_LOGGER.info("{} do shutdown begin", className); failoverReactor.shutdown(); NAMING_LOGGER.info("{} do shutdown stop", className); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/core/Balancer.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.naming.utils.Chooser; import com.alibaba.nacos.client.naming.utils.Pair; import com.alibaba.nacos.common.utils.CollectionUtils; import java.util.ArrayList; import java.util.List; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Balancer. * * @author xuanyin */ public class Balancer { public static class RandomByWeight { /** * Select all instance. * * @param serviceInfo service information * @return all instance of services */ public static List selectAll(ServiceInfo serviceInfo) { List hosts = serviceInfo.getHosts(); if (CollectionUtils.isEmpty(hosts)) { throw new IllegalStateException("no host to srv for serviceInfo: " + serviceInfo.getName()); } return hosts; } /** * Random select one instance from service. * * @param dom service * @return random instance */ public static Instance selectHost(ServiceInfo dom) { return getHostByRandomWeight(selectAll(dom)); } } /** * Return one host from the host list by random-weight. * * @param hosts The list of the host. * @return The random-weight result of the host */ protected static Instance getHostByRandomWeight(List hosts) { NAMING_LOGGER.debug("entry randomWithWeight"); if (hosts == null || hosts.size() == 0) { NAMING_LOGGER.debug("hosts == null || hosts.size() == 0"); return null; } NAMING_LOGGER.debug("new Chooser"); List> hostsWithWeight = new ArrayList<>(); for (Instance host : hosts) { if (host.isHealthy()) { hostsWithWeight.add(new Pair(host, host.getWeight())); } } NAMING_LOGGER.debug("for (Host host : hosts)"); Chooser vipChooser = new Chooser<>("www.taobao.com"); vipChooser.refresh(hostsWithWeight); NAMING_LOGGER.debug("vipChooser.refresh"); return vipChooser.randomWithWeight(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/core/NamingServerListManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosLoadException; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.address.PropertiesListProvider; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.JustForTest; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import org.slf4j.Logger; import java.util.List; import java.util.Properties; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.atomic.AtomicInteger; /** * Naming server list manager. * * @author xiweng.yy */ public class NamingServerListManager extends AbstractServerListManager { private static final Logger LOGGER = LogUtils.logger(NamingServerListManager.class); private final AtomicInteger currentIndex = new AtomicInteger(); private String nacosDomain; private boolean isDomain; @JustForTest public NamingServerListManager(Properties properties) { this(NacosClientProperties.PROTOTYPE.derive(properties), ""); } public NamingServerListManager(NacosClientProperties properties, String namespace) { super(properties, namespace); } @Override public void start() throws NacosException { super.start(); List serverList = getServerList(); if (serverList.isEmpty()) { throw new NacosLoadException("serverList is empty,please check configuration"); } else { currentIndex.set(ThreadLocalRandom.current().nextInt(serverList.size())); } if (serverListProvider instanceof PropertiesListProvider) { if (serverList.size() == 1) { isDomain = true; nacosDomain = serverList.get(0); } } } public String getNacosDomain() { return nacosDomain; } public boolean isDomain() { return isDomain; } @Override protected String getModuleName() { return "Naming"; } @Override protected NacosRestTemplate getNacosRestTemplate() { return NamingHttpClientManager.getInstance().getNacosRestTemplate(); } @Override public String genNextServer() { int index = currentIndex.incrementAndGet() % getServerList().size(); return getServerList().get(index); } @Override public String getCurrentServer() { return getServerList().get(currentIndex.get() % getServerList().size()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/core/ProtectMode.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; /** * Protect mode. * * @author nkorange */ public class ProtectMode { private float protectThreshold; public ProtectMode() { this.protectThreshold = 0.8F; } public float getProtectThreshold() { return protectThreshold; } public void setProtectThreshold(float protectThreshold) { this.protectThreshold = protectThreshold; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/core/ServiceInfoUpdateService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Service information update service. * * @author xiweng.yy */ public class ServiceInfoUpdateService implements Closeable { private static final long DEFAULT_DELAY = 1000L; private static final int DEFAULT_UPDATE_CACHE_TIME_MULTIPLE = 6; private static final int MIN_THREAD_NUM = 1; private final Map> futureMap = new HashMap<>(); private final ServiceInfoHolder serviceInfoHolder; private final ScheduledExecutorService executor; private final NamingClientProxy namingClientProxy; private final InstancesChangeNotifier changeNotifier; private final boolean asyncQuerySubscribeService; public ServiceInfoUpdateService(NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder, NamingClientProxy namingClientProxy, InstancesChangeNotifier changeNotifier) { this.asyncQuerySubscribeService = isAsyncQueryForSubscribeService(properties); this.executor = new ScheduledThreadPoolExecutor(initPollingThreadCount(properties), new NameThreadFactory("com.alibaba.nacos.client.naming.updater")); this.serviceInfoHolder = serviceInfoHolder; this.namingClientProxy = namingClientProxy; this.changeNotifier = changeNotifier; } private boolean isAsyncQueryForSubscribeService(NacosClientProperties properties) { if (properties == null || !properties.containsKey(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE)) { return false; } return ConvertUtils.toBoolean(properties.getProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE), false); } private int initPollingThreadCount(NacosClientProperties properties) { int count = ThreadUtils.getSuitableThreadCount(1) > 1 ? ThreadUtils.getSuitableThreadCount(1) / 2 : 1; if (properties == null) { return count; } count = Math.min(properties.getInteger(PropertyKeyConst.NAMING_POLLING_MAX_THREAD_COUNT, count), count); count = Math.max(count, MIN_THREAD_NUM); return properties.getInteger(PropertyKeyConst.NAMING_POLLING_THREAD_COUNT, count); } /** * Schedule update if absent. * * @param serviceName service name * @param groupName group name * @param clusters clusters */ public void scheduleUpdateIfAbsent(String serviceName, String groupName, String clusters) { if (!asyncQuerySubscribeService) { return; } String serviceKey = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), clusters); if (futureMap.get(serviceKey) != null) { return; } synchronized (futureMap) { if (futureMap.get(serviceKey) != null) { return; } ScheduledFuture future = addTask(new UpdateTask(serviceName, groupName, clusters)); futureMap.put(serviceKey, future); } } private synchronized ScheduledFuture addTask(UpdateTask task) { return executor.schedule(task, DEFAULT_DELAY, TimeUnit.MILLISECONDS); } /** * Stop to schedule update if contain task. * * @param serviceName service name * @param groupName group name * @param clusters clusters */ public void stopUpdateIfContain(String serviceName, String groupName, String clusters) { String serviceKey = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), clusters); if (!futureMap.containsKey(serviceKey)) { return; } synchronized (futureMap) { if (!futureMap.containsKey(serviceKey)) { return; } futureMap.remove(serviceKey); } } @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); NAMING_LOGGER.info("{} do shutdown begin", className); ThreadUtils.shutdownThreadPool(executor, NAMING_LOGGER); NAMING_LOGGER.info("{} do shutdown stop", className); } public class UpdateTask implements Runnable { long lastRefTime = Long.MAX_VALUE; private boolean isCancel; private final String serviceName; private final String groupName; private final String clusters; private final String groupedServiceName; private final String serviceKey; /** * the fail situation. 1:can't connect to server 2:serviceInfo's hosts is empty */ private int failCount = 0; public UpdateTask(String serviceName, String groupName, String clusters) { this.serviceName = serviceName; this.groupName = groupName; this.clusters = clusters; this.groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName); this.serviceKey = ServiceInfo.getKey(groupedServiceName, clusters); } @Override public void run() { long delayTime = DEFAULT_DELAY; try { if (!changeNotifier.isSubscribed(groupName, serviceName) && !futureMap.containsKey( serviceKey)) { NAMING_LOGGER.info("update task is stopped, service:{}, clusters:{}", groupedServiceName, clusters); isCancel = true; return; } ServiceInfo serviceObj = serviceInfoHolder.getServiceInfoMap().get(serviceKey); if (serviceObj == null) { serviceObj = namingClientProxy.queryInstancesOfService(serviceName, groupName, clusters, false); serviceInfoHolder.processServiceInfo(serviceObj); // TODO multiple time can be configured. delayTime = serviceObj.getCacheMillis() * DEFAULT_UPDATE_CACHE_TIME_MULTIPLE; lastRefTime = serviceObj.getLastRefTime(); return; } if (serviceObj.getLastRefTime() <= lastRefTime) { serviceObj = namingClientProxy.queryInstancesOfService(serviceName, groupName, clusters, false); serviceInfoHolder.processServiceInfo(serviceObj); } lastRefTime = serviceObj.getLastRefTime(); if (CollectionUtils.isEmpty(serviceObj.getHosts())) { incFailCount(); return; } // TODO multiple time can be configured. delayTime = serviceObj.getCacheMillis() * DEFAULT_UPDATE_CACHE_TIME_MULTIPLE; resetFailCount(); } catch (NacosException e) { handleNacosException(e); } catch (Throwable e) { handleUnknownException(e); } finally { if (!isCancel) { executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60), TimeUnit.MILLISECONDS); } } } private void handleNacosException(NacosException e) { incFailCount(); int errorCode = e.getErrCode(); if (NacosException.SERVER_ERROR == errorCode) { handleUnknownException(e); } NAMING_LOGGER.warn("Can't update serviceName: {}, reason: {}", groupedServiceName, e.getErrMsg()); } private void handleUnknownException(Throwable throwable) { incFailCount(); NAMING_LOGGER.warn("[NA] failed to update serviceName: {}", groupedServiceName, throwable); } private void incFailCount() { int limit = 6; if (failCount == limit) { return; } failCount++; } private void resetFailCount() { failCount = 0; } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/event/InstancesChangeEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.notify.Event; import java.util.List; /** * Instances change event. * * @author horizonzy * @since 1.4.1 */ public class InstancesChangeEvent extends Event { private static final long serialVersionUID = -8823087028212249603L; private final String eventScope; private final String serviceName; private final String groupName; private final String clusters; private final List hosts; private InstancesDiff instancesDiff; public InstancesChangeEvent(String eventScope, String serviceName, String groupName, String clusters, List hosts, InstancesDiff diff) { this.eventScope = eventScope; this.serviceName = serviceName; this.groupName = groupName; this.clusters = clusters; this.hosts = hosts; this.instancesDiff = diff; } public String getServiceName() { return serviceName; } public String getGroupName() { return groupName; } public String getClusters() { return clusters; } public List getHosts() { return hosts; } public InstancesDiff getInstancesDiff() { return instancesDiff; } @Override public String scope() { return this.eventScope; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/event/InstancesChangeNotifier.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.naming.selector.NamingSelectorWrapper; import com.alibaba.nacos.client.selector.SelectorManager; import com.alibaba.nacos.common.JustForTest; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.listener.Subscriber; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.UUID; /** * A subscriber to notify eventListener callback. * * @author horizonzy * @since 1.4.1 */ public class InstancesChangeNotifier extends Subscriber { private final String eventScope; private final SelectorManager selectorManager = new SelectorManager<>(); @JustForTest public InstancesChangeNotifier() { this.eventScope = UUID.randomUUID().toString(); } public InstancesChangeNotifier(String eventScope) { this.eventScope = eventScope; } /** * register listener. * * @param groupName group name * @param serviceName serviceName * @param wrapper selectorWrapper */ public void registerListener(String groupName, String serviceName, NamingSelectorWrapper wrapper) { if (wrapper == null) { return; } String subId = NamingUtils.getGroupedName(serviceName, groupName); selectorManager.addSelectorWrapper(subId, wrapper); } /** * deregister listener. * * @param groupName group name * @param serviceName serviceName * @param wrapper selectorWrapper */ public void deregisterListener(String groupName, String serviceName, NamingSelectorWrapper wrapper) { if (wrapper == null) { return; } String subId = NamingUtils.getGroupedName(serviceName, groupName); selectorManager.removeSelectorWrapper(subId, wrapper); } /** * check serviceName,groupName is subscribed. * * @param groupName group name * @param serviceName serviceName * @return is serviceName,clusters subscribed */ public boolean isSubscribed(String groupName, String serviceName) { String subId = NamingUtils.getGroupedName(serviceName, groupName); return selectorManager.isSubscribed(subId); } public List getSubscribeServices() { List serviceInfos = new ArrayList<>(); for (String key : selectorManager.getSubscriptions()) { serviceInfos.add(ServiceInfo.fromKey(key)); } return serviceInfos; } @Override public void onEvent(InstancesChangeEvent event) { String subId = NamingUtils.getGroupedName(event.getServiceName(), event.getGroupName()); Collection selectorWrappers = selectorManager.getSelectorWrappers(subId); for (NamingSelectorWrapper selectorWrapper : selectorWrappers) { selectorWrapper.notifyListener(event); } } @Override public Class subscribeType() { return InstancesChangeEvent.class; } @Override public boolean scopeMatches(InstancesChangeEvent event) { return this.eventScope.equals(event.scope()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/event/InstancesDiff.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.utils.CollectionUtils; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * The differences in instances compared to the last callback. * * @author lideyou */ public class InstancesDiff { private final List addedInstances = new ArrayList<>(); private final List removedInstances = new ArrayList<>(); private final List modifiedInstances = new ArrayList<>(); public InstancesDiff() { } public InstancesDiff(List addedInstances, List removedInstances, List modifiedInstances) { setAddedInstances(addedInstances); setRemovedInstances(removedInstances); setModifiedInstances(modifiedInstances); } public List getAddedInstances() { return addedInstances; } public void setAddedInstances(Collection addedInstances) { this.addedInstances.clear(); if (CollectionUtils.isNotEmpty(addedInstances)) { this.addedInstances.addAll(addedInstances); } } public List getRemovedInstances() { return removedInstances; } public void setRemovedInstances(Collection removedInstances) { this.removedInstances.clear(); if (CollectionUtils.isNotEmpty(removedInstances)) { this.removedInstances.addAll(removedInstances); } } public List getModifiedInstances() { return modifiedInstances; } public void setModifiedInstances(Collection modifiedInstances) { this.modifiedInstances.clear(); if (CollectionUtils.isNotEmpty(modifiedInstances)) { this.modifiedInstances.addAll(modifiedInstances); } } /** * Check if any instances have changed. * * @return true if there are instances that have changed */ public boolean hasDifferent() { return isAdded() || isRemoved() || isModified(); } /** * Check if any instances have been added. * * @return true if there are instances that have been added. */ public boolean isAdded() { return CollectionUtils.isNotEmpty(this.addedInstances); } /** * Check if any instances have been added. * * @return true if there are instances that have been added. */ public boolean isRemoved() { return CollectionUtils.isNotEmpty(this.removedInstances); } /** * Check if any instances have been added. * * @return true if there are instances that have been added. */ public boolean isModified() { return CollectionUtils.isNotEmpty(this.modifiedInstances); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchLoadEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.common.notify.Event; /** * Event class for fuzzy listen notifications. * *

    This class represents an event used for notifying fuzzy listen changes. It extends {@link Event}, indicating * that it may be processed asynchronously. The event contains information about the group, dataId, type, and UUID of * the notification. * * @author shiyiyue * @date 2025/01/13 */ public class NamingFuzzyWatchLoadEvent extends Event { private String eventScope; /** * The groupKeyPattern of configuration. */ private String groupKeyPattern; private int code; /** * Constructs a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * * @param code The type of notification. * @param groupKeyPattern The groupKeyPattern of notification. */ private NamingFuzzyWatchLoadEvent(int code, String groupKeyPattern, String eventScope) { this.code = code; this.groupKeyPattern = groupKeyPattern; this.eventScope = eventScope; } /** * Builds a new FuzzyListenNotifyEvent with the specified group, dataId, and type. * * @param groupKeyPattern The groupKey of the configuration. * @return A new FuzzyListenNotifyEvent instance. */ public static NamingFuzzyWatchLoadEvent buildEvent(int code, String groupKeyPattern, String scope) { return new NamingFuzzyWatchLoadEvent(code, groupKeyPattern, scope); } @Override public String scope() { return eventScope; } public String getGroupKeyPattern() { return groupKeyPattern; } public int getCode() { return code; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/event/NamingFuzzyWatchNotifyEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.common.notify.Event; /** * Watch notify event, including service change/watch initial. * * @author tanyongquan */ public class NamingFuzzyWatchNotifyEvent extends Event { private final String scope; private String watcherUuid; private String serviceKey; private String pattern; private final String changedType; private final String syncType; private NamingFuzzyWatchNotifyEvent(String scope, String pattern, String serviceKey, String changedType, String syncType, String watcherUuid) { this.scope = scope; this.pattern = pattern; this.serviceKey = serviceKey; this.changedType = changedType; this.syncType = syncType; this.watcherUuid = watcherUuid; } public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, String changedType, String syncType) { return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType, null); } public static NamingFuzzyWatchNotifyEvent build(String eventScope, String pattern, String serviceKey, String changedType, String syncType, String watcherUuid) { return new NamingFuzzyWatchNotifyEvent(eventScope, pattern, serviceKey, changedType, syncType, watcherUuid); } public String getPattern() { return pattern; } public String getChangedType() { return changedType; } @Override public String scope() { return this.scope; } public String getWatcherUuid() { return watcherUuid; } public String getServiceKey() { return serviceKey; } public String getScope() { return scope; } public String getSyncType() { return syncType; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/listener/AbstractNamingChangeListener.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.listener; import com.alibaba.nacos.api.naming.listener.AbstractEventListener; import com.alibaba.nacos.api.naming.listener.Event; /** * Listener for NamingChangeEvent. * * @author lideyou */ public abstract class AbstractNamingChangeListener extends AbstractEventListener { @Override public final void onEvent(Event event) { if (event instanceof NamingChangeEvent) { onChange((NamingChangeEvent) event); } } /** * Callback when instances have changed. * * @param event NamingChangeEvent */ public abstract void onChange(NamingChangeEvent event); } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/listener/NamingChangeEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.listener; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.naming.event.InstancesDiff; import java.util.List; /** * Naming Event with instance change information. * * @author lideyou */ public class NamingChangeEvent extends NamingEvent { private final InstancesDiff instancesDiff; public NamingChangeEvent(String serviceName, List instances, InstancesDiff instancesDiff) { super(serviceName, instances); this.instancesDiff = instancesDiff; } public NamingChangeEvent(String serviceName, String groupName, String clusters, List instances, InstancesDiff instancesDiff) { super(serviceName, groupName, clusters, instances); this.instancesDiff = instancesDiff; } public boolean isAdded() { return this.instancesDiff.isAdded(); } public boolean isRemoved() { return this.instancesDiff.isRemoved(); } public boolean isModified() { return this.instancesDiff.isModified(); } public List getAddedInstances() { return this.instancesDiff.getAddedInstances(); } public List getRemovedInstances() { return this.instancesDiff.getRemovedInstances(); } public List getModifiedInstances() { return this.instancesDiff.getModifiedInstances(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxy.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.notify.listener.Subscriber; import java.util.HashMap; import java.util.Map; /** * Abstract Naming client proxy. * * @author xiweng.yy */ public abstract class AbstractNamingClientProxy extends Subscriber implements NamingClientProxy { private static final String APP_FILED = "app"; private final SecurityProxy securityProxy; protected AbstractNamingClientProxy(SecurityProxy securityProxy) { this.securityProxy = securityProxy; } protected Map getSecurityHeaders(String namespace, String group, String serviceName) { RequestResource resource = RequestResource.namingBuilder().setNamespace(namespace).setGroup(group) .setResource(serviceName).build(); Map result = this.securityProxy.getIdentityContext(resource); result.putAll(getAppHeaders()); return result; } protected Map getAppHeaders() { Map result = new HashMap<>(1); result.put(APP_FILED, AppNameUtils.getAppName()); return result; } protected void reLogin() { securityProxy.reLogin(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxy.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.common.lifecycle.Closeable; import java.util.List; /** * Naming Client Proxy. * * @author xiweng.yy */ public interface NamingClientProxy extends Closeable { /** * Register an instance to service with specified instance properties. * * @param serviceName name of service * @param groupName group of service * @param instance instance to register * @throws NacosException nacos exception */ void registerService(String serviceName, String groupName, Instance instance) throws NacosException; /** * Batch register instance to service with specified instance properties. * * @param serviceName service name * @param groupName group name * @param instances instance * @throws NacosException nacos exception * @since 2.1.1 */ void batchRegisterService(String serviceName, String groupName, List instances) throws NacosException; /** * Batch deRegister instance to service with specified instance properties. * * @param serviceName service name * @param groupName group name * @param instances deRegister instance * @throws NacosException nacos exception * @since 2.2.0 */ void batchDeregisterService(String serviceName, String groupName, List instances) throws NacosException; /** * Deregister instance from a service. * * @param serviceName name of service * @param groupName group name * @param instance instance * @throws NacosException nacos exception */ void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException; /** * Update instance to service. * * @param serviceName service name * @param groupName group name * @param instance instance * @throws NacosException nacos exception */ void updateInstance(String serviceName, String groupName, Instance instance) throws NacosException; /** * Query instance list. * * @param serviceName service name * @param groupName group name * @param clusters clusters * @param healthyOnly healthy only * @return service info * @throws NacosException nacos exception */ ServiceInfo queryInstancesOfService(String serviceName, String groupName, String clusters, boolean healthyOnly) throws NacosException; /** * Query Service. * * @param serviceName service name * @param groupName group name * @return service * @throws NacosException nacos exception */ Service queryService(String serviceName, String groupName) throws NacosException; /** * Create service. * * @param service service * @param selector selector * @throws NacosException nacos exception */ void createService(Service service, AbstractSelector selector) throws NacosException; /** * Delete service. * * @param serviceName service name * @param groupName group name * @return true if delete ok * @throws NacosException nacos exception */ boolean deleteService(String serviceName, String groupName) throws NacosException; /** * Update service. * * @param service service * @param selector selector * @throws NacosException nacos exception */ void updateService(Service service, AbstractSelector selector) throws NacosException; /** * Get service list. * * @param pageNo page number * @param pageSize size per page * @param groupName group name of service * @param selector selector * @return list of service * @throws NacosException nacos exception */ ListView getServiceList(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException; /** * Subscribe service. * * @param serviceName service name * @param groupName group name * @param clusters clusters, current only support subscribe all clusters, maybe deprecated * @return current service info of subscribe service * @throws NacosException nacos exception */ ServiceInfo subscribe(String serviceName, String groupName, String clusters) throws NacosException; /** * Unsubscribe service. * * @param serviceName service name * @param groupName group name * @param clusters clusters, current only support subscribe all clusters, maybe deprecated * @throws NacosException nacos exception */ void unsubscribe(String serviceName, String groupName, String clusters) throws NacosException; /** * Judge whether service has been subscribed. * * @param serviceName service name * @param groupName group name * @param clusters clusters, current only support subscribe all clusters, maybe deprecated * @return {@code true} if subscribed, otherwise {@code false} * @throws NacosException nacos exception */ boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException; /** * Check Server healthy. * * @return true if server is healthy */ boolean serverHealthy(); } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegate.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.core.ServiceInfoUpdateService; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import java.util.List; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.client.constant.Constants.Security.SECURITY_INFO_REFRESH_INTERVAL_MILLS; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Delegate of naming client proxy. * * @author xiweng.yy */ public class NamingClientProxyDelegate implements NamingClientProxy { private final NamingServerListManager serverListManager; private final ServiceInfoUpdateService serviceInfoUpdateService; private final ServiceInfoHolder serviceInfoHolder; private final NamingHttpClientProxy httpClientProxy; private final NamingGrpcClientProxy grpcClientProxy; private final SecurityProxy securityProxy; private ScheduledExecutorService executorService; public NamingClientProxyDelegate(String namespace, ServiceInfoHolder serviceInfoHolder, NacosClientProperties properties, InstancesChangeNotifier changeNotifier, NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { this.serviceInfoUpdateService = new ServiceInfoUpdateService(properties, serviceInfoHolder, this, changeNotifier); this.serverListManager = new NamingServerListManager(properties, namespace); this.serverListManager.start(); this.serviceInfoHolder = serviceInfoHolder; this.securityProxy = new SecurityProxy(this.serverListManager, NamingHttpClientManager.getInstance().getNacosRestTemplate()); initSecurityProxy(properties); this.httpClientProxy = new NamingHttpClientProxy(namespace, securityProxy, serverListManager, properties); this.grpcClientProxy = new NamingGrpcClientProxy(namespace, securityProxy, serverListManager, properties, serviceInfoHolder, namingFuzzyWatchServiceListHolder); } private void initSecurityProxy(NacosClientProperties properties) { this.executorService = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.naming.security")); final Properties nacosClientPropertiesView = properties.asProperties(); this.securityProxy.login(nacosClientPropertiesView); this.executorService.scheduleWithFixedDelay(() -> securityProxy.login(nacosClientPropertiesView), 0, SECURITY_INFO_REFRESH_INTERVAL_MILLS, TimeUnit.MILLISECONDS); } @Override public void registerService(String serviceName, String groupName, Instance instance) throws NacosException { getExecuteClientProxy(instance).registerService(serviceName, groupName, instance); } @Override public void batchRegisterService(String serviceName, String groupName, List instances) throws NacosException { NAMING_LOGGER.info("batchRegisterInstance instances: {} ,serviceName: {} begin.", instances, serviceName); if (CollectionUtils.isEmpty(instances)) { NAMING_LOGGER.warn("batchRegisterInstance instances is Empty:{}", instances); } grpcClientProxy.batchRegisterService(serviceName, groupName, instances); NAMING_LOGGER.info("batchRegisterInstance instances: {} ,serviceName: {} finish.", instances, serviceName); } @Override public void batchDeregisterService(String serviceName, String groupName, List instances) throws NacosException { NAMING_LOGGER.info("batch DeregisterInstance instances: {} ,serviceName: {} begin.", instances, serviceName); if (CollectionUtils.isEmpty(instances)) { NAMING_LOGGER.warn("batch DeregisterInstance instances is Empty:{}", instances); } grpcClientProxy.batchDeregisterService(serviceName, groupName, instances); NAMING_LOGGER.info("batch DeregisterInstance instances: {} ,serviceName: {} finish.", instances, serviceName); } @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { getExecuteClientProxy(instance).deregisterService(serviceName, groupName, instance); } @Override public void updateInstance(String serviceName, String groupName, Instance instance) throws NacosException { } @Override public ServiceInfo queryInstancesOfService(String serviceName, String groupName, String clusters, boolean healthyOnly) throws NacosException { return grpcClientProxy.queryInstancesOfService(serviceName, groupName, clusters, healthyOnly); } @Override public Service queryService(String serviceName, String groupName) throws NacosException { return null; } @Override public void createService(Service service, AbstractSelector selector) throws NacosException { } @Override public boolean deleteService(String serviceName, String groupName) throws NacosException { return false; } @Override public void updateService(Service service, AbstractSelector selector) throws NacosException { } @Override public ListView getServiceList(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException { return grpcClientProxy.getServiceList(pageNo, pageSize, groupName, selector); } @Override public ServiceInfo subscribe(String serviceName, String groupName, String clusters) throws NacosException { NAMING_LOGGER.info("[SUBSCRIBE-SERVICE] service:{}, group:{}, clusters:{} ", serviceName, groupName, clusters); String serviceNameWithGroup = NamingUtils.getGroupedName(serviceName, groupName); String serviceKey = ServiceInfo.getKey(serviceNameWithGroup, clusters); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, groupName, clusters); ServiceInfo result = serviceInfoHolder.getServiceInfoMap().get(serviceKey); if (null == result || !isSubscribed(serviceName, groupName, clusters)) { result = grpcClientProxy.subscribe(serviceName, groupName, clusters); } serviceInfoHolder.processServiceInfo(result); return result; } @Override public void unsubscribe(String serviceName, String groupName, String clusters) throws NacosException { NAMING_LOGGER.debug("[UNSUBSCRIBE-SERVICE] service:{}, group:{}, cluster:{} ", serviceName, groupName, clusters); serviceInfoUpdateService.stopUpdateIfContain(serviceName, groupName, clusters); grpcClientProxy.unsubscribe(serviceName, groupName, clusters); } @Override public boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException { return grpcClientProxy.isSubscribed(serviceName, groupName, clusters); } @Override public boolean serverHealthy() { return grpcClientProxy.serverHealthy() || httpClientProxy.serverHealthy(); } private NamingClientProxy getExecuteClientProxy(Instance instance) { if (instance.isEphemeral() || grpcClientProxy.isAbilitySupportedByServer( AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)) { return grpcClientProxy; } return httpClientProxy; } @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); NAMING_LOGGER.info("{} do shutdown begin", className); serviceInfoUpdateService.shutdown(); serverListManager.shutdown(); httpClientProxy.shutdown(); grpcClientProxy.shutdown(); securityProxy.shutdown(); ThreadUtils.shutdownThreadPool(executorService, NAMING_LOGGER); NAMING_LOGGER.info("{} do shutdown stop", className); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingFuzzyWatchNotifyRequestHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchChangeNotifyResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchContext; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchNotifyEvent; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import java.util.Collection; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_RESOURCE_CHANGED; /** * handle fuzzy watch request from server. * * @author shiyiyue */ public class NamingFuzzyWatchNotifyRequestHandler implements ServerRequestHandler { NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; public NamingFuzzyWatchNotifyRequestHandler(NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) { this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; NotifyCenter.registerToPublisher(NamingFuzzyWatchNotifyEvent.class, 1000); } @Override public Response requestReply(Request request, Connection connection) { if (request instanceof NamingFuzzyWatchSyncRequest) { NamingFuzzyWatchSyncRequest watchNotifySyncRequest = (NamingFuzzyWatchSyncRequest) request; NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() .get(watchNotifySyncRequest.getGroupKeyPattern()); if (namingFuzzyWatchContext != null) { Collection serviceKeys = watchNotifySyncRequest.getContexts(); if (watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_INIT_NOTIFY) || watchNotifySyncRequest.getSyncType().equals(Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY)) { for (NamingFuzzyWatchSyncRequest.Context serviceKey : serviceKeys) { // may have a 'change event' sent to client before 'init event' if (namingFuzzyWatchContext.addReceivedServiceKey(serviceKey.getServiceKey())) { NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( namingFuzzyWatchServiceListHolder.getNotifierEventScope(), watchNotifySyncRequest.getGroupKeyPattern(), serviceKey.getServiceKey(), serviceKey.getChangedType(), watchNotifySyncRequest.getSyncType())); } } } else if (watchNotifySyncRequest.getSyncType().equals(Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY)) { namingFuzzyWatchContext.markInitializationComplete(); } } return new NamingFuzzyWatchChangeNotifyResponse(); } else if (request instanceof NamingFuzzyWatchChangeNotifyRequest) { NamingFuzzyWatchChangeNotifyRequest notifyChangeRequest = (NamingFuzzyWatchChangeNotifyRequest) request; String[] serviceKeyItems = NamingUtils.parseServiceKey(notifyChangeRequest.getServiceKey()); String namespace = serviceKeyItems[0]; String groupName = serviceKeyItems[1]; String serviceName = serviceKeyItems[2]; Collection matchedPattern = FuzzyGroupKeyPattern.filterMatchedPatterns( namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap().keySet(), serviceName, groupName, namespace); String serviceChangeType = notifyChangeRequest.getChangedType(); switch (serviceChangeType) { case Constants.ServiceChangedType.ADD_SERVICE: case Constants.ServiceChangedType.INSTANCE_CHANGED: for (String pattern : matchedPattern) { NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() .get(pattern); if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.addReceivedServiceKey( ((NamingFuzzyWatchChangeNotifyRequest) request).getServiceKey())) { //publish local service add event NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( namingFuzzyWatchServiceListHolder.getNotifierEventScope(), pattern, notifyChangeRequest.getServiceKey(), Constants.ServiceChangedType.ADD_SERVICE, FUZZY_WATCH_RESOURCE_CHANGED)); } } break; case Constants.ServiceChangedType.DELETE_SERVICE: for (String pattern : matchedPattern) { NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyMatchContextMap() .get(pattern); if (namingFuzzyWatchContext != null && namingFuzzyWatchContext.removeReceivedServiceKey( notifyChangeRequest.getServiceKey())) { NotifyCenter.publishEvent(NamingFuzzyWatchNotifyEvent.build( namingFuzzyWatchServiceListHolder.getNotifierEventScope(), pattern, notifyChangeRequest.getServiceKey(), Constants.ServiceChangedType.DELETE_SERVICE, FUZZY_WATCH_RESOURCE_CHANGED)); } } break; default: break; } return new NamingFuzzyWatchChangeNotifyResponse(); } return null; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxy.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest; import com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.ServiceListRequest; 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.BatchInstanceResponse; import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.ServiceListResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientConfigFactory; import com.alibaba.nacos.common.remote.client.RpcClientFactory; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; import static com.alibaba.nacos.api.remote.RemoteConstants.MONITOR_LABEL_NONE; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Naming grpc client proxy. * * @author xiweng.yy */ public class NamingGrpcClientProxy extends AbstractNamingClientProxy { private final String namespaceId; private final String uuid; private final Long requestTimeout; private final RpcClient rpcClient; private final NamingGrpcRedoService redoService; private boolean enableClientMetrics = true; public NamingGrpcClientProxy(String namespaceId, SecurityProxy securityProxy, ServerListFactory serverListFactory, NacosClientProperties properties, ServiceInfoHolder serviceInfoHolder, NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { super(securityProxy); this.namespaceId = namespaceId; this.uuid = UUID.randomUUID().toString(); this.requestTimeout = Long.parseLong(properties.getProperty(CommonParams.NAMING_REQUEST_TIMEOUT, "-1")); Map labels = new HashMap<>(); labels.put(RemoteConstants.LABEL_SOURCE, RemoteConstants.LABEL_SOURCE_SDK); labels.put(RemoteConstants.LABEL_MODULE, RemoteConstants.LABEL_MODULE_NAMING); labels.put(Constants.APPNAME, AppNameUtils.getAppName()); namingFuzzyWatchServiceListHolder.registerNamingGrpcClientProxy(this); GrpcClientConfig grpcClientConfig = RpcClientConfigFactory.getInstance() .createGrpcClientConfig(properties.asProperties(), labels); this.rpcClient = RpcClientFactory.createClient(uuid, ConnectionType.GRPC, grpcClientConfig); this.redoService = new NamingGrpcRedoService(this, namingFuzzyWatchServiceListHolder, properties); this.enableClientMetrics = Boolean.parseBoolean( properties.getProperty(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true")); NAMING_LOGGER.info("Create naming rpc client for uuid->{}", uuid); start(serverListFactory, serviceInfoHolder, namingFuzzyWatchServiceListHolder); } private void start(ServerListFactory serverListFactory, ServiceInfoHolder serviceInfoHolder, NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder) throws NacosException { rpcClient.serverListFactory(serverListFactory); rpcClient.registerConnectionListener(redoService); rpcClient.registerServerRequestHandler(new NamingPushRequestHandler(serviceInfoHolder)); rpcClient.registerServerRequestHandler( new NamingFuzzyWatchNotifyRequestHandler(namingFuzzyWatchServiceListHolder)); rpcClient.start(); namingFuzzyWatchServiceListHolder.start(); NotifyCenter.registerSubscriber(this); } @Override public void onEvent(ServerListChangeEvent event) { rpcClient.onServerListChange(); } @Override public Class subscribeType() { return ServerListChangeEvent.class; } @Override public void registerService(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName, instance); if (instance.isEphemeral()) { registerServiceForEphemeral(serviceName, groupName, instance); } else { doRegisterServiceForPersistent(serviceName, groupName, instance); } } private void registerServiceForEphemeral(String serviceName, String groupName, Instance instance) throws NacosException { redoService.cacheInstanceForRedo(serviceName, groupName, instance); doRegisterService(serviceName, groupName, instance); } @Override public void batchRegisterService(String serviceName, String groupName, List instances) throws NacosException { redoService.cacheInstanceForRedo(serviceName, groupName, instances); doBatchRegisterService(serviceName, groupName, instances); } @Override public void batchDeregisterService(String serviceName, String groupName, List instances) throws NacosException { synchronized (redoService.getRegisteredInstances()) { List retainInstance = getRetainInstance(serviceName, groupName, instances); batchRegisterService(serviceName, groupName, retainInstance); } } /** * Get instance list that need to be Retained. * * @param serviceName service name * @param groupName group name * @param deRegisterInstances deregister instance list * @return instance list that need to be retained. */ private List getRetainInstance(String serviceName, String groupName, List deRegisterInstances) throws NacosException { if (CollectionUtils.isEmpty(deRegisterInstances)) { throw new NacosException(NacosException.INVALID_PARAM, String.format("[Batch deRegistration] need deRegister instance is empty, instances: %s,", deRegisterInstances)); } String combinedServiceName = NamingUtils.getGroupedName(serviceName, groupName); InstanceRedoData instanceRedoData = redoService.getRegisteredInstancesByKey(combinedServiceName); if (!(instanceRedoData instanceof BatchInstanceRedoData)) { throw new NacosException(NacosException.INVALID_PARAM, String.format( "[Batch deRegistration] batch deRegister is not BatchInstanceRedoData type , instances: %s,", deRegisterInstances)); } BatchInstanceRedoData batchInstanceRedoData = (BatchInstanceRedoData) instanceRedoData; List allRedoInstances = batchInstanceRedoData.getInstances(); if (CollectionUtils.isEmpty(allRedoInstances)) { throw new NacosException(NacosException.INVALID_PARAM, String.format( "[Batch deRegistration] not found all registerInstance , serviceName:%s , groupName: %s", serviceName, groupName)); } Map deRegisterInstanceMap = deRegisterInstances.stream() .collect(Collectors.toMap(Function.identity(), Function.identity())); List retainInstances = new ArrayList<>(); for (Instance redoInstance : allRedoInstances) { boolean needRetained = true; Iterator> it = deRegisterInstanceMap.entrySet().iterator(); while (it.hasNext()) { Instance deRegisterInstance = it.next().getKey(); // only compare Ip & Port because redoInstance's instanceId or serviceName might be null but deRegisterInstance's might not be null. if (compareIpAndPort(deRegisterInstance, redoInstance)) { needRetained = false; // clear current entry to speed up next redoInstance comparing. it.remove(); break; } } if (needRetained) { retainInstances.add(redoInstance); } } return retainInstances; } private boolean compareIpAndPort(Instance deRegisterInstance, Instance redoInstance) { return ((deRegisterInstance.getIp().equals(redoInstance.getIp())) && (deRegisterInstance.getPort() == redoInstance.getPort())); } /** * Execute batch register operation. * * @param serviceName service name * @param groupName group name * @param instances instances * @throws NacosException NacosException */ public void doBatchRegisterService(String serviceName, String groupName, List instances) throws NacosException { BatchInstanceRequest request = new BatchInstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.BATCH_REGISTER_INSTANCE, instances); requestToServer(request, BatchInstanceResponse.class); redoService.instanceRegistered(serviceName, groupName); } /** * Execute register operation. * * @param serviceName name of service * @param groupName group of service * @param instance instance to register * @throws NacosException nacos exception */ public void doRegisterService(String serviceName, String groupName, Instance instance) throws NacosException { InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.REGISTER_INSTANCE, instance); requestToServer(request, Response.class); redoService.instanceRegistered(serviceName, groupName); } /** * Execute register operation for persistent instance. * * @param serviceName name of service * @param groupName group of service * @param instance instance to register * @throws NacosException nacos exception */ public void doRegisterServiceForPersistent(String serviceName, String groupName, Instance instance) throws NacosException { PersistentInstanceRequest request = new PersistentInstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.REGISTER_INSTANCE, instance); requestToServer(request, Response.class); } @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}", namespaceId, serviceName, instance); if (instance.isEphemeral()) { deregisterServiceForEphemeral(serviceName, groupName, instance); } else { doDeregisterServiceForPersistent(serviceName, groupName, instance); } } private void deregisterServiceForEphemeral(String serviceName, String groupName, Instance instance) throws NacosException { String key = NamingUtils.getGroupedName(serviceName, groupName); InstanceRedoData instanceRedoData = redoService.getRegisteredInstancesByKey(key); if (instanceRedoData instanceof BatchInstanceRedoData) { List instances = new ArrayList<>(); if (null != instance) { instances.add(instance); } batchDeregisterService(serviceName, groupName, instances); } else { redoService.instanceDeregister(serviceName, groupName); doDeregisterService(serviceName, groupName, instance); } } /** * Execute deregister operation. * * @param serviceName service name * @param groupName group name * @param instance instance * @throws NacosException nacos exception */ public void doDeregisterService(String serviceName, String groupName, Instance instance) throws NacosException { InstanceRequest request = new InstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.DE_REGISTER_INSTANCE, instance); requestToServer(request, Response.class); redoService.instanceDeregistered(serviceName, groupName); } /** * Execute deregister operation for persistent instance. * * @param serviceName service name * @param groupName group name * @param instance instance * @throws NacosException nacos exception */ public void doDeregisterServiceForPersistent(String serviceName, String groupName, Instance instance) throws NacosException { PersistentInstanceRequest request = new PersistentInstanceRequest(namespaceId, serviceName, groupName, NamingRemoteConstants.DE_REGISTER_INSTANCE, instance); requestToServer(request, Response.class); } @Override public void updateInstance(String serviceName, String groupName, Instance instance) throws NacosException { } @Override public ServiceInfo queryInstancesOfService(String serviceName, String groupName, String clusters, boolean healthyOnly) throws NacosException { ServiceQueryRequest request = new ServiceQueryRequest(namespaceId, serviceName, groupName); request.setCluster(clusters); request.setHealthyOnly(healthyOnly); QueryServiceResponse response = requestToServer(request, QueryServiceResponse.class); return response.getServiceInfo(); } @Override public Service queryService(String serviceName, String groupName) throws NacosException { return null; } @Override public void createService(Service service, AbstractSelector selector) throws NacosException { } @Override public boolean deleteService(String serviceName, String groupName) throws NacosException { return false; } @Override public void updateService(Service service, AbstractSelector selector) throws NacosException { } @Override public ListView getServiceList(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException { ServiceListRequest request = new ServiceListRequest(namespaceId, groupName, pageNo, pageSize); if (selector != null) { if (SelectorType.valueOf(selector.getType()) == SelectorType.label) { request.setSelector(JacksonUtils.toJson(selector)); } } ServiceListResponse response = requestToServer(request, ServiceListResponse.class); ListView result = new ListView<>(); result.setCount(response.getCount()); result.setData(response.getServiceNames()); return result; } @Override public ServiceInfo subscribe(String serviceName, String groupName, String clusters) throws NacosException { NAMING_LOGGER.info("[GRPC-SUBSCRIBE] service:{}, group:{}, cluster:{} ", serviceName, groupName, clusters); redoService.cacheSubscriberForRedo(serviceName, groupName, clusters); return doSubscribe(serviceName, groupName, clusters); } /** * Execute subscribe operation. * * @param serviceName service name * @param groupName group name * @param clusters clusters, current only support subscribe all clusters, maybe deprecated * @return current service info of subscribe service * @throws NacosException nacos exception */ public ServiceInfo doSubscribe(String serviceName, String groupName, String clusters) throws NacosException { SubscribeServiceRequest request = new SubscribeServiceRequest(namespaceId, groupName, serviceName, clusters, true); SubscribeServiceResponse response = requestToServer(request, SubscribeServiceResponse.class); redoService.subscriberRegistered(serviceName, groupName, clusters); return response.getServiceInfo(); } @Override public void unsubscribe(String serviceName, String groupName, String clusters) throws NacosException { NAMING_LOGGER.info("[GRPC-UNSUBSCRIBE] service:{}, group:{}, cluster:{} ", serviceName, groupName, clusters); redoService.subscriberDeregister(serviceName, groupName, clusters); doUnsubscribe(serviceName, groupName, clusters); } @Override public boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException { return redoService.isSubscriberRegistered(serviceName, groupName, clusters); } /** * Execute unsubscribe operation. * * @param serviceName service name * @param groupName group name * @param clusters clusters, current only support subscribe all clusters, maybe deprecated * @throws NacosException nacos exception */ public void doUnsubscribe(String serviceName, String groupName, String clusters) throws NacosException { SubscribeServiceRequest request = new SubscribeServiceRequest(namespaceId, groupName, serviceName, clusters, false); requestToServer(request, SubscribeServiceResponse.class); redoService.removeSubscriberForRedo(serviceName, groupName, clusters); } @Override public boolean serverHealthy() { return rpcClient.isRunning(); } /** * Determine whether nacos-server supports the capability. * * @param abilityKey ability key * @return true if supported, otherwise false */ public boolean isAbilitySupportedByServer(AbilityKey abilityKey) { return rpcClient.getConnectionAbility(abilityKey) == AbilityStatus.SUPPORTED; } /** * Execute unsubscribe operation. * * @param namingFuzzyWatchRequest namingFuzzyWatchRequest * @throws NacosException nacos exception */ public NamingFuzzyWatchResponse fuzzyWatchRequest(NamingFuzzyWatchRequest namingFuzzyWatchRequest) throws NacosException { return requestToServer(namingFuzzyWatchRequest, NamingFuzzyWatchResponse.class); } private T requestToServer(Request request, Class responseClass) throws NacosException { Response response = null; try { if (request instanceof AbstractNamingRequest) { request.putAllHeader(getSecurityHeaders(((AbstractNamingRequest) request).getNamespace(), ((AbstractNamingRequest) request).getGroupName(), ((AbstractNamingRequest) request).getServiceName())); } else if (request instanceof NamingFuzzyWatchRequest) { request.putAllHeader( getSecurityHeaders(((NamingFuzzyWatchRequest) request).getNamespace(), null, null)); } else { throw new NacosException(400, "unknown naming request type"); } response = requestTimeout < 0 ? rpcClient.request(request) : rpcClient.request(request, requestTimeout); if (ResponseCode.SUCCESS.getCode() != response.getResultCode()) { // If the 403 login operation is triggered, refresh the accessToken of the client if (NacosException.NO_RIGHT == response.getErrorCode()) { reLogin(); } throw new NacosException(response.getErrorCode(), response.getMessage()); } if (responseClass.isAssignableFrom(response.getClass())) { return (T) response; } NAMING_LOGGER.error("Server return unexpected response '{}', expected response should be '{}'", response.getClass().getName(), responseClass.getName()); throw new NacosException(NacosException.SERVER_ERROR, "Server return invalid response"); } catch (NacosException e) { recordRequestFailedMetrics(request, e, response); throw e; } catch (Exception e) { recordRequestFailedMetrics(request, e, response); throw new NacosException(NacosException.SERVER_ERROR, "Request nacos server failed: ", e); } } /** * Records registration metrics for a service instance. * * @param request The registration request object. * @param exception The Exception encountered during the registration process, or null if registration was * successful. * @param response The response object containing registration result information, or null if registration failed. */ private void recordRequestFailedMetrics(Request request, Exception exception, Response response) { if (!enableClientMetrics) { return; } try { if (Objects.isNull(response)) { MetricsMonitor.getNamingRequestFailedMonitor(request.getClass().getSimpleName(), MONITOR_LABEL_NONE, MONITOR_LABEL_NONE, exception.getClass().getSimpleName()).inc(); } else { MetricsMonitor.getNamingRequestFailedMonitor(request.getClass().getSimpleName(), String.valueOf(response.getResultCode()), String.valueOf(response.getErrorCode()), MONITOR_LABEL_NONE).inc(); } } catch (Throwable t) { NAMING_LOGGER.warn("Fail to record metrics for request {}", request.getClass().getSimpleName(), t); } } @Override public void shutdown() throws NacosException { NAMING_LOGGER.info("Shutdown naming grpc client proxy for uuid->{}", uuid); redoService.shutdown(); shutDownAndRemove(uuid); NotifyCenter.deregisterSubscriber(this); } private void shutDownAndRemove(String uuid) { synchronized (RpcClientFactory.getAllClientEntries()) { try { RpcClientFactory.destroyClient(uuid); NAMING_LOGGER.info("shutdown and remove naming rpc client for uuid ->{}", uuid); } catch (NacosException e) { NAMING_LOGGER.warn("Fail to shutdown naming rpc client for uuid ->{}", uuid); } } } public boolean isEnable() { return rpcClient.isRunning(); } public String getNamespaceId() { return namespaceId; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; import com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; /** * Naming push request handler. * * @author xiweng.yy */ public class NamingPushRequestHandler implements ServerRequestHandler { private final ServiceInfoHolder serviceInfoHolder; public NamingPushRequestHandler(ServiceInfoHolder serviceInfoHolder) { this.serviceInfoHolder = serviceInfoHolder; } @Override public Response requestReply(Request request, Connection connection) { if (request instanceof NotifySubscriberRequest) { NotifySubscriberRequest notifyRequest = (NotifySubscriberRequest) request; serviceInfoHolder.processServiceInfo(notifyRequest.getServiceInfo()); return new NotifySubscriberResponse(); } return null; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ConnectionEventListener; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Naming client gprc redo service. * *

    When connection reconnect to server, redo the register and subscribe. * TODO refactor to extends from {@link com.alibaba.nacos.client.redo.service.AbstractRedoService} * * @author xiweng.yy */ public class NamingGrpcRedoService implements ConnectionEventListener { private static final String REDO_THREAD_NAME = "com.alibaba.nacos.client.naming.grpc.redo"; private int redoThreadCount; private long redoDelayTime; private final ConcurrentMap registeredInstances = new ConcurrentHashMap<>(); private final ConcurrentMap subscribes = new ConcurrentHashMap<>(); private final NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; private final ScheduledExecutorService redoExecutor; private volatile boolean connected = false; public NamingGrpcRedoService(NamingGrpcClientProxy clientProxy, NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder, NacosClientProperties properties) { setProperties(properties); this.namingFuzzyWatchServiceListHolder = namingFuzzyWatchServiceListHolder; this.redoExecutor = new ScheduledThreadPoolExecutor(redoThreadCount, new NameThreadFactory(REDO_THREAD_NAME)); this.redoExecutor.scheduleWithFixedDelay(new RedoScheduledTask(clientProxy, this), redoDelayTime, redoDelayTime, TimeUnit.MILLISECONDS); } private void setProperties(NacosClientProperties properties) { redoDelayTime = properties.getLong(PropertyKeyConst.REDO_DELAY_TIME, Constants.DEFAULT_REDO_DELAY_TIME); redoThreadCount = properties.getInteger(PropertyKeyConst.REDO_DELAY_THREAD_COUNT, Constants.DEFAULT_REDO_THREAD_COUNT); } public ConcurrentMap getRegisteredInstances() { return registeredInstances; } public boolean isConnected() { return connected; } @Override public void onConnected(Connection connection) { connected = true; LogUtils.NAMING_LOGGER.info("Grpc connection connect"); } @Override public void onDisConnect(Connection connection) { connected = false; LogUtils.NAMING_LOGGER.warn("Grpc connection disconnect, mark to redo"); synchronized (registeredInstances) { registeredInstances.values().forEach(instanceRedoData -> instanceRedoData.setRegistered(false)); } synchronized (subscribes) { subscribes.values().forEach(subscriberRedoData -> subscriberRedoData.setRegistered(false)); } synchronized (namingFuzzyWatchServiceListHolder) { namingFuzzyWatchServiceListHolder.resetConsistenceStatus(); } LogUtils.NAMING_LOGGER.warn("mark to redo completed"); } /** * Cache registered instance for redo. * * @param serviceName service name * @param groupName group name * @param instance registered instance */ public void cacheInstanceForRedo(String serviceName, String groupName, Instance instance) { String key = NamingUtils.getGroupedName(serviceName, groupName); InstanceRedoData redoData = InstanceRedoData.build(serviceName, groupName, instance); synchronized (registeredInstances) { registeredInstances.put(key, redoData); } } /** * Cache registered instance for redo. * * @param serviceName service name * @param groupName group name * @param instances batch registered instance */ public void cacheInstanceForRedo(String serviceName, String groupName, List instances) { String key = NamingUtils.getGroupedName(serviceName, groupName); BatchInstanceRedoData redoData = BatchInstanceRedoData.build(serviceName, groupName, instances); synchronized (registeredInstances) { registeredInstances.put(key, redoData); } } /** * Instance register successfully, mark registered status as {@code true}. * * @param serviceName service name * @param groupName group name */ public void instanceRegistered(String serviceName, String groupName) { String key = NamingUtils.getGroupedName(serviceName, groupName); synchronized (registeredInstances) { InstanceRedoData redoData = registeredInstances.get(key); if (null != redoData) { redoData.registered(); } } } /** * Instance deregister, mark unregistering status as {@code true}. * * @param serviceName service name * @param groupName group name */ public void instanceDeregister(String serviceName, String groupName) { String key = NamingUtils.getGroupedName(serviceName, groupName); synchronized (registeredInstances) { InstanceRedoData redoData = registeredInstances.get(key); if (null != redoData) { redoData.setUnregistering(true); redoData.setExpectedRegistered(false); } } } /** * Instance deregister finished, mark unregistered status. * * @param serviceName service name * @param groupName group name */ public void instanceDeregistered(String serviceName, String groupName) { String key = NamingUtils.getGroupedName(serviceName, groupName); synchronized (registeredInstances) { InstanceRedoData redoData = registeredInstances.get(key); if (null != redoData) { redoData.unregistered(); } } } /** * Remove registered instance for redo. * * @param serviceName service name * @param groupName group name */ public void removeInstanceForRedo(String serviceName, String groupName) { String key = NamingUtils.getGroupedName(serviceName, groupName); synchronized (registeredInstances) { InstanceRedoData redoData = registeredInstances.get(key); if (null != redoData && !redoData.isExpectedRegistered()) { registeredInstances.remove(key); } } } /** * Find all instance redo data which need do redo. * * @return set of {@code InstanceRedoData} need to do redo. */ public Set findInstanceRedoData() { Set result = new HashSet<>(); synchronized (registeredInstances) { for (InstanceRedoData each : registeredInstances.values()) { if (each.isNeedRedo()) { result.add(each); } } } return result; } /** * Cache subscriber for redo. * * @param serviceName service name * @param groupName group name * @param cluster cluster */ public void cacheSubscriberForRedo(String serviceName, String groupName, String cluster) { String key = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), cluster); SubscriberRedoData redoData = SubscriberRedoData.build(serviceName, groupName, cluster); synchronized (subscribes) { subscribes.put(key, redoData); } } /** * Subscriber register successfully, mark registered status as {@code true}. * * @param serviceName service name * @param groupName group name * @param cluster cluster */ public void subscriberRegistered(String serviceName, String groupName, String cluster) { String key = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), cluster); synchronized (subscribes) { SubscriberRedoData redoData = subscribes.get(key); if (null != redoData) { redoData.setRegistered(true); } } } /** * Subscriber deregister, mark unregistering status as {@code true}. * * @param serviceName service name * @param groupName group name * @param cluster cluster */ public void subscriberDeregister(String serviceName, String groupName, String cluster) { String key = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), cluster); synchronized (subscribes) { SubscriberRedoData redoData = subscribes.get(key); if (null != redoData) { redoData.setUnregistering(true); redoData.setExpectedRegistered(false); } } } /** * Judge subscriber has registered to server. * * @param serviceName service name * @param groupName group name * @param cluster cluster * @return {@code true} if subscribed, otherwise {@code false} */ public boolean isSubscriberRegistered(String serviceName, String groupName, String cluster) { String key = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), cluster); synchronized (subscribes) { SubscriberRedoData redoData = subscribes.get(key); return null != redoData && redoData.isRegistered(); } } /** * Remove subscriber for redo. * * @param serviceName service name * @param groupName group name * @param cluster cluster */ public void removeSubscriberForRedo(String serviceName, String groupName, String cluster) { String key = ServiceInfo.getKey(NamingUtils.getGroupedName(serviceName, groupName), cluster); synchronized (subscribes) { SubscriberRedoData redoData = subscribes.get(key); if (null != redoData && !redoData.isExpectedRegistered()) { subscribes.remove(key); } } } /** * Find all subscriber redo data which need do redo. * * @return set of {@code SubscriberRedoData} need to do redo. */ public Set findSubscriberRedoData() { Set result = new HashSet<>(); synchronized (subscribes) { for (SubscriberRedoData each : subscribes.values()) { if (each.isNeedRedo()) { result.add(each); } } } return result; } /** * get Cache service. * * @return cache service */ public InstanceRedoData getRegisteredInstancesByKey(String combinedServiceName) { return registeredInstances.get(combinedServiceName); } /** * Shutdown redo service. */ public void shutdown() { LogUtils.NAMING_LOGGER.info("Shutdown grpc redo service executor " + redoExecutor); registeredInstances.clear(); subscribes.clear(); redoExecutor.shutdownNow(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTask.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.NamingRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.common.task.AbstractExecuteTask; /** * Redo task. * TODO refactor to extends from {@link com.alibaba.nacos.client.redo.service.AbstractRedoTask} * * @author xiweng.yy */ public class RedoScheduledTask extends AbstractExecuteTask { private final NamingGrpcClientProxy clientProxy; private final NamingGrpcRedoService redoService; public RedoScheduledTask(NamingGrpcClientProxy clientProxy, NamingGrpcRedoService redoService) { this.clientProxy = clientProxy; this.redoService = redoService; } @Override public void run() { if (!redoService.isConnected()) { LogUtils.NAMING_LOGGER.warn("Grpc Connection is disconnect, skip current redo task"); return; } try { redoForInstances(); redoForSubscribes(); } catch (Exception e) { LogUtils.NAMING_LOGGER.warn("Redo task run with unexpected exception: ", e); } } private void redoForInstances() { for (InstanceRedoData each : redoService.findInstanceRedoData()) { try { redoForInstance(each); } catch (NacosException e) { LogUtils.NAMING_LOGGER.error("Redo instance operation {} for {}@@{} failed. ", each.getRedoType(), each.getGroupName(), each.getServiceName(), e); } } } private void redoForInstance(InstanceRedoData redoData) throws NacosException { NamingRedoData.RedoType redoType = redoData.getRedoType(); String serviceName = redoData.getServiceName(); String groupName = redoData.getGroupName(); LogUtils.NAMING_LOGGER.info("Redo instance operation {} for {}@@{}", redoType, groupName, serviceName); switch (redoType) { case REGISTER: if (isClientDisabled()) { return; } processRegisterRedoType(redoData, serviceName, groupName); break; case UNREGISTER: if (isClientDisabled()) { return; } clientProxy.doDeregisterService(serviceName, groupName, redoData.get()); break; case REMOVE: redoService.removeInstanceForRedo(serviceName, groupName); break; default: } } private void processRegisterRedoType(InstanceRedoData redoData, String serviceName, String groupName) throws NacosException { if (redoData instanceof BatchInstanceRedoData) { // Execute Batch Register BatchInstanceRedoData batchInstanceRedoData = (BatchInstanceRedoData) redoData; clientProxy.doBatchRegisterService(serviceName, groupName, batchInstanceRedoData.getInstances()); return; } clientProxy.doRegisterService(serviceName, groupName, redoData.get()); } private void redoForSubscribes() { for (SubscriberRedoData each : redoService.findSubscriberRedoData()) { try { redoForSubscribe(each); } catch (NacosException e) { LogUtils.NAMING_LOGGER.error("Redo subscriber operation {} for {}@@{}#{} failed. ", each.getRedoType(), each.getGroupName(), each.getServiceName(), each.get(), e); } } } private void redoForSubscribe(SubscriberRedoData redoData) throws NacosException { NamingRedoData.RedoType redoType = redoData.getRedoType(); String serviceName = redoData.getServiceName(); String groupName = redoData.getGroupName(); String cluster = redoData.get(); LogUtils.NAMING_LOGGER.info("Redo subscriber operation {} for {}@@{}#{}", redoType, groupName, serviceName, cluster); switch (redoData.getRedoType()) { case REGISTER: if (isClientDisabled()) { return; } clientProxy.doSubscribe(serviceName, groupName, cluster); break; case UNREGISTER: if (isClientDisabled()) { return; } clientProxy.doUnsubscribe(serviceName, groupName, cluster); break; case REMOVE: redoService.removeSubscriberForRedo(redoData.getServiceName(), redoData.getGroupName(), redoData.get()); break; default: } } private boolean isClientDisabled() { return !clientProxy.isEnable(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/BatchInstanceRedoData.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo.data; import com.alibaba.nacos.api.naming.pojo.Instance; import java.util.List; import java.util.Objects; /** * batch instance redo service. * * @author chenhao26 */ public class BatchInstanceRedoData extends InstanceRedoData { List instances; public List getInstances() { return instances; } public void setInstances(List instances) { this.instances = instances; } protected BatchInstanceRedoData(String serviceName, String groupName) { super(serviceName, groupName); } /** * build BatchInstanceRedoData. * * @param serviceName service name * @param groupName group name * @param instances instances * @return build BatchInstanceRedoData */ public static BatchInstanceRedoData build(String serviceName, String groupName, List instances) { BatchInstanceRedoData result = new BatchInstanceRedoData(serviceName, groupName); result.setInstances(instances); return result; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof BatchInstanceRedoData)) { return false; } if (!super.equals(o)) { return false; } BatchInstanceRedoData redoData = (BatchInstanceRedoData) o; return Objects.equals(instances, redoData.instances); } @Override public int hashCode() { return Objects.hash(super.hashCode(), instances); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/InstanceRedoData.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo.data; import com.alibaba.nacos.api.naming.pojo.Instance; /** * Redo data for register service instance. * * @author xiweng.yy */ public class InstanceRedoData extends NamingRedoData { protected InstanceRedoData(String serviceName, String groupName) { super(serviceName, groupName); } /** * Build a new {@code RedoData} for register service instance. * * @param serviceName service name for redo data * @param groupName group name for redo data * @param instance instance for redo data * @return new {@code RedoData} for register service instance */ public static InstanceRedoData build(String serviceName, String groupName, Instance instance) { InstanceRedoData result = new InstanceRedoData(serviceName, groupName); result.set(instance); return result; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/NamingRedoData.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo.data; import com.alibaba.nacos.client.redo.data.RedoData; import java.util.Objects; /** * Nacos naming redo data. * * @author xiweng.yy */ public abstract class NamingRedoData extends RedoData { private final String serviceName; private final String groupName; protected NamingRedoData(String serviceName, String groupName) { super(); this.serviceName = serviceName; this.groupName = groupName; } public String getServiceName() { return serviceName; } public String getGroupName() { return groupName; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } NamingRedoData redoData = (NamingRedoData) o; return super.equals(o) && serviceName.equals(redoData.serviceName) && groupName.equals(redoData.groupName); } @Override public int hashCode() { return Objects.hash(super.hashCode(), serviceName, groupName); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/SubscriberRedoData.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo.data; /** * Redo data for subscribers. * * @author xiweng.yy */ public class SubscriberRedoData extends NamingRedoData { private SubscriberRedoData(String serviceName, String groupName) { super(serviceName, groupName); } /** * Build a new {@code RedoData} for subscribers. * * @param serviceName service name for redo data * @param groupName group name for redo data * @param clusters clusters for redo data * @return new {@code RedoData} for subscribers */ public static SubscriberRedoData build(String serviceName, String groupName, String clusters) { SubscriberRedoData result = new SubscriberRedoData(serviceName, groupName); result.set(clusters); return result; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.http; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.http.AbstractHttpClientFactory; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpClientFactory; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.tls.TlsSystemConfig; import com.alibaba.nacos.common.utils.ExceptionUtil; import org.slf4j.Logger; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * http Manager. * * @author mai.jh */ public class NamingHttpClientManager implements Closeable { private static final int READ_TIME_OUT_MILLIS = Integer .getInteger("com.alibaba.nacos.client.naming.rtimeout", 50000); private static final int CON_TIME_OUT_MILLIS = Integer.getInteger("com.alibaba.nacos.client.naming.ctimeout", 3000); private static final boolean ENABLE_HTTPS = Boolean.getBoolean(TlsSystemConfig.TLS_ENABLE); private static final int MAX_REDIRECTS = 5; private static final HttpClientFactory HTTP_CLIENT_FACTORY = new NamingHttpClientFactory(); private static class NamingHttpClientManagerInstance { private static final NamingHttpClientManager INSTANCE = new NamingHttpClientManager(); } public static NamingHttpClientManager getInstance() { return NamingHttpClientManagerInstance.INSTANCE; } public String getPrefix() { return ENABLE_HTTPS ? HTTPS_PREFIX : HTTP_PREFIX; } public NacosRestTemplate getNacosRestTemplate() { return HttpClientBeanHolder.getNacosRestTemplate(HTTP_CLIENT_FACTORY); } @Override public void shutdown() throws NacosException { NAMING_LOGGER.info("[NamingHttpClientManager] Start destroying NacosRestTemplate"); try { HttpClientBeanHolder.shutdownNacosSyncRest(HTTP_CLIENT_FACTORY.getClass().getName()); } catch (Exception ex) { NAMING_LOGGER.error("[NamingHttpClientManager] An exception occurred when the HTTP client was closed : {}", ExceptionUtil.getStackTrace(ex)); } NAMING_LOGGER.info("[NamingHttpClientManager] Completed destruction of NacosRestTemplate"); } private static class NamingHttpClientFactory extends AbstractHttpClientFactory { @Override protected HttpClientConfig buildHttpClientConfig() { return HttpClientConfig.builder().setConTimeOutMillis(CON_TIME_OUT_MILLIS) .setReadTimeOutMillis(READ_TIME_OUT_MILLIS).setMaxRedirects(MAX_REDIRECTS).build(); } @Override protected Logger assignLogger() { return NAMING_LOGGER; } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxy.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.http; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.CommonParams; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.AbstractNamingClientProxy; import com.alibaba.nacos.client.naming.utils.UtilAndComs; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.HttpMethod; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import org.apache.hc.core5.http.HttpStatus; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * Naming proxy. * * @author nkorange */ public class NamingHttpClientProxy extends AbstractNamingClientProxy { private final NacosRestTemplate nacosRestTemplate = NamingHttpClientManager.getInstance().getNacosRestTemplate(); private static final int DEFAULT_SERVER_PORT = 8848; private static final String MODULE_NAME = "Naming"; private static final String IP_PARAM = "ip"; private static final String PORT_PARAM = "port"; private static final String WEIGHT_PARAM = "weight"; private static final String ENABLE_PARAM = "enabled"; private static final String EPHEMERAL_PARAM = "ephemeral"; private static final String META_PARAM = "metadata"; private static final String SELECTOR_PARAM = "selector"; private static final String HEALTHY_PARAM = "healthy"; private static final String PROTECT_THRESHOLD_PARAM = "protectThreshold"; private static final String CLUSTERS_PARAM = "clusters"; private static final String CLIENT_IP_PARAM = "clientIP"; private static final String HEALTHY_ONLY_PARAM = "healthyOnly"; private static final String REGISTER_ENABLE_PARAM = "enable"; private final String namespaceId; private final NamingServerListManager serverListManager; private final int maxRetry; private int serverPort = DEFAULT_SERVER_PORT; private boolean enableClientMetrics = true; public NamingHttpClientProxy(String namespaceId, SecurityProxy securityProxy, NamingServerListManager serverListManager, NacosClientProperties properties) { super(securityProxy); this.serverListManager = serverListManager; this.setServerPort(DEFAULT_SERVER_PORT); this.namespaceId = namespaceId; this.maxRetry = ConvertUtils.toInt(properties.getProperty(PropertyKeyConst.NAMING_REQUEST_DOMAIN_RETRY_COUNT, String.valueOf(UtilAndComs.REQUEST_DOMAIN_RETRY_COUNT))); this.enableClientMetrics = Boolean.parseBoolean( properties.getProperty(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true")); } @Override public void onEvent(ServerListChangeEvent event) { // do nothing in http client } @Override public Class subscribeType() { return ServerListChangeEvent.class; } @Override public void registerService(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance: {}", namespaceId, serviceName, instance); String groupedServiceName = NamingUtils.getGroupedName(serviceName, groupName); if (instance.isEphemeral()) { throw new UnsupportedOperationException( "Do not support register ephemeral instances by HTTP, please use gRPC replaced."); } final Map params = new HashMap<>(32); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, groupedServiceName); params.put(CommonParams.GROUP_NAME, groupName); params.put(CommonParams.CLUSTER_NAME, instance.getClusterName()); params.put(IP_PARAM, instance.getIp()); params.put(PORT_PARAM, String.valueOf(instance.getPort())); params.put(WEIGHT_PARAM, String.valueOf(instance.getWeight())); params.put(REGISTER_ENABLE_PARAM, String.valueOf(instance.isEnabled())); params.put(HEALTHY_PARAM, String.valueOf(instance.isHealthy())); params.put(EPHEMERAL_PARAM, String.valueOf(instance.isEphemeral())); params.put(META_PARAM, JacksonUtils.toJson(instance.getMetadata())); reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.POST); } @Override public void batchRegisterService(String serviceName, String groupName, List instances) { throw new UnsupportedOperationException( "Do not support persistent instances to perform batch registration methods."); } @Override public void batchDeregisterService(String serviceName, String groupName, List instances) { throw new UnsupportedOperationException( "Do not support persistent instances to perform batch de registration methods."); } @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER.info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}", namespaceId, serviceName, instance); if (instance.isEphemeral()) { return; } final Map params = new HashMap<>(16); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, NamingUtils.getGroupedName(serviceName, groupName)); params.put(CommonParams.CLUSTER_NAME, instance.getClusterName()); params.put(IP_PARAM, instance.getIp()); params.put(PORT_PARAM, String.valueOf(instance.getPort())); params.put(EPHEMERAL_PARAM, String.valueOf(instance.isEphemeral())); reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.DELETE); } @Override public void updateInstance(String serviceName, String groupName, Instance instance) throws NacosException { NAMING_LOGGER.info("[UPDATE-SERVICE] {} update service {} with instance: {}", namespaceId, serviceName, instance); final Map params = new HashMap<>(32); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, serviceName); params.put(CommonParams.GROUP_NAME, groupName); params.put(CommonParams.CLUSTER_NAME, instance.getClusterName()); params.put(IP_PARAM, instance.getIp()); params.put(PORT_PARAM, String.valueOf(instance.getPort())); params.put(WEIGHT_PARAM, String.valueOf(instance.getWeight())); params.put(ENABLE_PARAM, String.valueOf(instance.isEnabled())); params.put(EPHEMERAL_PARAM, String.valueOf(instance.isEphemeral())); params.put(META_PARAM, JacksonUtils.toJson(instance.getMetadata())); reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.PUT); } @Override public ServiceInfo queryInstancesOfService(String serviceName, String groupName, String clusters, boolean healthyOnly) { throw new UnsupportedOperationException( "Do not support query instance by http client,please use gRPC replaced."); } @Override public Service queryService(String serviceName, String groupName) throws NacosException { NAMING_LOGGER.info("[QUERY-SERVICE] {} query service : {}, {}", namespaceId, serviceName, groupName); final Map params = new HashMap<>(16); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, serviceName); params.put(CommonParams.GROUP_NAME, groupName); String result = reqApi(UtilAndComs.nacosUrlService, params, HttpMethod.GET); return JacksonUtils.toObj(result, Service.class); } @Override public void createService(Service service, AbstractSelector selector) throws NacosException { NAMING_LOGGER.info("[CREATE-SERVICE] {} creating service : {}", namespaceId, service); final Map params = new HashMap<>(16); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, service.getName()); params.put(CommonParams.GROUP_NAME, service.getGroupName()); params.put(PROTECT_THRESHOLD_PARAM, String.valueOf(service.getProtectThreshold())); params.put(META_PARAM, JacksonUtils.toJson(service.getMetadata())); params.put(SELECTOR_PARAM, JacksonUtils.toJson(selector)); reqApi(UtilAndComs.nacosUrlService, params, HttpMethod.POST); } @Override public boolean deleteService(String serviceName, String groupName) throws NacosException { NAMING_LOGGER.info("[DELETE-SERVICE] {} deleting service : {} with groupName : {}", namespaceId, serviceName, groupName); final Map params = new HashMap<>(16); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, serviceName); params.put(CommonParams.GROUP_NAME, groupName); String result = reqApi(UtilAndComs.nacosUrlService, params, HttpMethod.DELETE); return "ok".equals(result); } @Override public void updateService(Service service, AbstractSelector selector) throws NacosException { NAMING_LOGGER.info("[UPDATE-SERVICE] {} updating service : {}", namespaceId, service); final Map params = new HashMap<>(16); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.SERVICE_NAME, service.getName()); params.put(CommonParams.GROUP_NAME, service.getGroupName()); params.put(PROTECT_THRESHOLD_PARAM, String.valueOf(service.getProtectThreshold())); params.put(META_PARAM, JacksonUtils.toJson(service.getMetadata())); params.put(SELECTOR_PARAM, JacksonUtils.toJson(selector)); reqApi(UtilAndComs.nacosUrlService, params, HttpMethod.PUT); } @Override public boolean serverHealthy() { try { String result = reqApi(UtilAndComs.webContext + "/v3/admin/core/state/liveness", new HashMap<>(8), HttpMethod.GET); JsonNode json = JacksonUtils.toObj(result); int statusCode = json.get("code").asInt(); return 0 == statusCode; } catch (Exception e) { return false; } } @Override public ListView getServiceList(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException { Map params = new HashMap<>(16); params.put("pageNo", String.valueOf(pageNo)); params.put("pageSize", String.valueOf(pageSize)); params.put(CommonParams.NAMESPACE_ID, namespaceId); params.put(CommonParams.GROUP_NAME, groupName); if (selector != null) { switch (SelectorType.valueOf(selector.getType())) { case none: break; case label: ExpressionSelector expressionSelector = (ExpressionSelector) selector; params.put(SELECTOR_PARAM, JacksonUtils.toJson(expressionSelector)); break; default: break; } } String result = reqApi(UtilAndComs.nacosUrlBase + "/service/list", params, HttpMethod.GET); JsonNode json = JacksonUtils.toObj(result); ListView listView = new ListView<>(); listView.setCount(json.get("count").asInt()); listView.setData(JacksonUtils.toObj(json.get("doms").toString(), new TypeReference>() { })); return listView; } @Override public ServiceInfo subscribe(String serviceName, String groupName, String clusters) throws NacosException { throw new UnsupportedOperationException("Do not support subscribe service by UDP, please use gRPC replaced."); } @Override public void unsubscribe(String serviceName, String groupName, String clusters) throws NacosException { } @Override public boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException { return true; } public String reqApi(String api, Map params, String method) throws NacosException { return reqApi(api, params, Collections.EMPTY_MAP, method); } public String reqApi(String api, Map params, Map body, String method) throws NacosException { return reqApi(api, params, body, serverListManager.getServerList(), method); } /** * Request api. * * @param api api * @param params parameters * @param body body * @param servers servers * @param method http method * @return result * @throws NacosException nacos exception */ public String reqApi(String api, Map params, Map body, List servers, String method) throws NacosException { params.put(CommonParams.NAMESPACE_ID, getNamespaceId()); if (CollectionUtils.isEmpty(servers) && !serverListManager.isDomain()) { throw new NacosException(NacosException.INVALID_PARAM, "no server available"); } NacosException exception = new NacosException(); if (serverListManager.isDomain()) { String nacosDomain = serverListManager.getNacosDomain(); for (int i = 0; i < maxRetry; i++) { try { return callServer(api, params, body, nacosDomain, method); } catch (NacosException e) { exception = e; if (NAMING_LOGGER.isDebugEnabled()) { NAMING_LOGGER.debug("request {} failed.", nacosDomain, e); } } } } else { int index = ThreadLocalRandom.current().nextInt(servers.size()); for (int i = 0; i < servers.size(); i++) { String server = servers.get(index); try { return callServer(api, params, body, server, method); } catch (NacosException e) { exception = e; if (NAMING_LOGGER.isDebugEnabled()) { NAMING_LOGGER.debug("request {} failed.", server, e); } } index = (index + 1) % servers.size(); } } NAMING_LOGGER.error("request: {} failed, servers: {}, code: {}, msg: {}", api, servers, exception.getErrCode(), exception.getErrMsg()); throw new NacosException(exception.getErrCode(), "failed to req API:" + api + " after all servers(" + servers + ") tried: " + exception.getMessage()); } /** * Call server. * * @param api api * @param params parameters * @param body body * @param curServer ? * @param method http method * @return result * @throws NacosException nacos exception */ public String callServer(String api, Map params, Map body, String curServer, String method) throws NacosException { long start = System.currentTimeMillis(); long end = 0; String namespace = params.get(CommonParams.NAMESPACE_ID); String group = params.get(CommonParams.GROUP_NAME); String serviceName = params.get(CommonParams.SERVICE_NAME); params.putAll(getSecurityHeaders(namespace, group, serviceName)); Header header = HttpUtils.builderHeader(MODULE_NAME); String url; if (curServer.startsWith(HTTPS_PREFIX) || curServer.startsWith(HTTP_PREFIX)) { url = curServer + api; } else { if (!InternetAddressUtil.containsPort(curServer)) { curServer = curServer + InternetAddressUtil.IP_PORT_SPLITER + serverPort; } url = NamingHttpClientManager.getInstance().getPrefix() + curServer + api; } try { HttpRestResult restResult = nacosRestTemplate.exchangeForm(url, header, Query.newInstance().initParams(params), body, method, String.class); end = System.currentTimeMillis(); if (enableClientMetrics) { try { MetricsMonitor.getNamingRequestMonitor(method, url, String.valueOf(restResult.getCode())) .observe(end - start); } catch (Throwable t) { NAMING_LOGGER.error("Failed to record metrics. Method: {}, URL: {}, HTTP Status Code: {}", method, url, restResult.getCode(), t); } } if (restResult.ok()) { return restResult.getData(); } if (HttpStatus.SC_NOT_MODIFIED == restResult.getCode()) { return StringUtils.EMPTY; } // If the 403 login operation is triggered, refresh the accessToken of the client if (HttpStatus.SC_FORBIDDEN == restResult.getCode()) { reLogin(); } throw new NacosException(restResult.getCode(), restResult.getMessage()); } catch (NacosException e) { NAMING_LOGGER.error("[NA] failed to request", e); throw e; } catch (Exception e) { NAMING_LOGGER.error("[NA] failed to request", e); throw new NacosException(NacosException.SERVER_ERROR, e); } } public String getNamespaceId() { return namespaceId; } public void setServerPort(int serverPort) { this.serverPort = serverPort; String sp = NacosClientProperties.PROTOTYPE.getProperty(SystemPropertyKeyConst.NAMING_SERVER_PORT); if (StringUtils.isNotBlank(sp)) { this.serverPort = Integer.parseInt(sp); } } @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); NAMING_LOGGER.info("{} do shutdown begin", className); NamingHttpClientManager.getInstance().shutdown(); NAMING_LOGGER.info("{} do shutdown stop", className); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/selector/DefaultNamingSelector.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.selector.NamingContext; import com.alibaba.nacos.api.naming.selector.NamingResult; import com.alibaba.nacos.api.naming.selector.NamingSelector; import java.util.Collections; import java.util.List; import java.util.function.Predicate; import java.util.stream.Collectors; /** * Default naming selector. * * @author lideyou */ public class DefaultNamingSelector implements NamingSelector { private final Predicate filter; public DefaultNamingSelector(Predicate filter) { this.filter = filter; } @Override public NamingResult select(NamingContext context) { List instances = doFilter(context.getInstances()); return () -> instances; } private List doFilter(List instances) { return instances == null ? Collections.emptyList() : instances.stream().filter(filter).collect(Collectors.toList()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/selector/NamingListenerInvoker.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.listener.AbstractEventListener; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.client.selector.ListenerInvoker; import java.util.Objects; import java.util.concurrent.atomic.AtomicBoolean; import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER; /** * Naming listener invoker. * * @author lideyou */ public class NamingListenerInvoker implements ListenerInvoker { private final EventListener listener; private final AtomicBoolean invoked = new AtomicBoolean(false); public NamingListenerInvoker(EventListener listener) { this.listener = listener; } @Override public void invoke(NamingEvent event) { invoked.set(true); logInvoke(event); if (listener instanceof AbstractEventListener && ((AbstractEventListener) listener).getExecutor() != null) { ((AbstractEventListener) listener).getExecutor().execute(() -> listener.onEvent(event)); } else { listener.onEvent(event); } } private void logInvoke(NamingEvent event) { NAMING_LOGGER.info("Invoke event groupName: {}, serviceName: {} to Listener: {}", event.getGroupName(), event.getServiceName(), listener.toString()); } @Override public boolean isInvoked() { return invoked.get(); } @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) { return false; } if (this == o) { return true; } NamingListenerInvoker that = (NamingListenerInvoker) o; return Objects.equals(listener, that.listener); } @Override public int hashCode() { return Objects.hashCode(listener); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/selector/NamingSelectorFactory.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.selector.NamingSelector; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Collection; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.function.Predicate; import java.util.regex.Pattern; /** * Selectors factory. * * @author lideyou */ public final class NamingSelectorFactory { public static final NamingSelector EMPTY_SELECTOR = context -> context::getInstances; public static final NamingSelector HEALTHY_SELECTOR = new DefaultNamingSelector(Instance::isHealthy); /** * Cluster selector. */ private static class ClusterSelector extends DefaultNamingSelector { private final String clusterString; public ClusterSelector(Predicate filter, String clusterString) { super(filter); this.clusterString = clusterString; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ClusterSelector that = (ClusterSelector) o; return Objects.equals(this.clusterString, that.clusterString); } @Override public int hashCode() { return Objects.hashCode(this.clusterString); } } private NamingSelectorFactory() { } /** * Create a cluster selector. * * @param clusters target cluster * @return cluster selector */ public static NamingSelector newClusterSelector(Collection clusters) { if (CollectionUtils.isNotEmpty(clusters)) { final Set set = new HashSet<>(clusters); Predicate filter = instance -> set.contains(instance.getClusterName()); String clusterString = getUniqueClusterString(clusters); return new ClusterSelector(filter, clusterString); } else { return EMPTY_SELECTOR; } } /** * Create a IP selector. * * @param regex regular expression of IP * @return IP selector */ public static NamingSelector newIpSelector(String regex) { if (regex == null) { throw new IllegalArgumentException("The parameter 'regex' cannot be null."); } return new DefaultNamingSelector(instance -> Pattern.matches(regex, instance.getIp())); } /** * Create a metadata selector. * * @param metadata metadata that needs to be matched * @return metadata selector */ public static NamingSelector newMetadataSelector(Map metadata) { return newMetadataSelector(metadata, false); } /** * Create a metadata selector. * * @param metadata target metadata * @param isAny true if any of the metadata needs to be matched, false if all the metadata need to be matched. * @return metadata selector */ public static NamingSelector newMetadataSelector(Map metadata, boolean isAny) { if (metadata == null) { throw new IllegalArgumentException("The parameter 'metadata' cannot be null."); } Predicate filter = instance -> instance.getMetadata().size() >= metadata.size(); for (Map.Entry entry : metadata.entrySet()) { Predicate nextFilter = instance -> { Map map = instance.getMetadata(); return Objects.equals(map.get(entry.getKey()), entry.getValue()); }; if (isAny) { filter = filter.or(nextFilter); } else { filter = filter.and(nextFilter); } } return new DefaultNamingSelector(filter); } public static String getUniqueClusterString(Collection cluster) { TreeSet treeSet = new TreeSet<>(cluster); return StringUtils.join(treeSet, ","); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/selector/NamingSelectorWrapper.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; 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.selector.NamingContext; import com.alibaba.nacos.api.naming.selector.NamingSelector; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.listener.NamingChangeEvent; import com.alibaba.nacos.client.selector.AbstractSelectorWrapper; import com.alibaba.nacos.common.utils.CollectionUtils; import java.util.Collections; import java.util.List; /** * Naming selector wrapper. * * @author lideyou */ public class NamingSelectorWrapper extends AbstractSelectorWrapper { private String serviceName; private String groupName; private String clusters; private final InnerNamingContext namingContext = new InnerNamingContext(); private class InnerNamingContext implements NamingContext { private List instances; @Override public String getServiceName() { return serviceName; } @Override public String getGroupName() { return groupName; } @Override public String getClusters() { return clusters; } @Override public List getInstances() { return instances; } private void setInstances(List instances) { this.instances = instances; } } public NamingSelectorWrapper(NamingSelector selector, EventListener listener) { super(selector, new NamingListenerInvoker(listener)); } public NamingSelectorWrapper(String serviceName, String groupName, String clusters, NamingSelector selector, EventListener listener) { this(selector, listener); this.serviceName = serviceName; this.groupName = groupName; this.clusters = clusters; } @Override protected boolean isSelectable(InstancesChangeEvent event) { return event != null && event.getHosts() != null && event.getInstancesDiff() != null; } @Override public boolean isCallable(NamingEvent event) { if (event == null) { return false; } NamingChangeEvent changeEvent = (NamingChangeEvent) event; return changeEvent.isAdded() || changeEvent.isRemoved() || changeEvent.isModified(); } @Override protected NamingEvent buildListenerEvent(InstancesChangeEvent event) { List currentIns = Collections.emptyList(); if (CollectionUtils.isNotEmpty(event.getHosts())) { currentIns = doSelect(event.getHosts()); } InstancesDiff diff = event.getInstancesDiff(); InstancesDiff newDiff = new InstancesDiff(); if (diff.isAdded()) { newDiff.setAddedInstances(doSelect(diff.getAddedInstances())); } if (diff.isRemoved()) { newDiff.setRemovedInstances(doSelect(diff.getRemovedInstances())); } if (diff.isModified()) { newDiff.setModifiedInstances(doSelect(diff.getModifiedInstances())); } return new NamingChangeEvent(serviceName, groupName, clusters, currentIns, newDiff); } private List doSelect(List instances) { NamingContext context = getNamingContext(instances); return this.getSelector().select(context).getResult(); } private NamingContext getNamingContext(final List instances) { namingContext.setInstances(instances); return namingContext; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/selector/ServiceInfoContext.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.selector.NamingContext; import java.util.List; /** * Service info context. * * @author xiweng.yy */ public class ServiceInfoContext implements NamingContext { private final ServiceInfo serviceInfo; public ServiceInfoContext(ServiceInfo serviceInfo) { this.serviceInfo = serviceInfo; } @Override public String getServiceName() { return serviceInfo.getName(); } @Override public String getGroupName() { return serviceInfo.getGroupName(); } @Override public String getClusters() { return serviceInfo.getClusters(); } @Override public List getInstances() { return serviceInfo.getHosts(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/utils/CacheDirUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.common.utils.StringUtils; import java.io.File; import com.alibaba.nacos.client.env.NacosClientProperties; /** * Cache Dir Utils. * * @author zongkang.guo */ public class CacheDirUtil { private static String cacheDir; private static final String JM_SNAPSHOT_PATH_PROPERTY = "JM.SNAPSHOT.PATH"; private static final String FILE_PATH_NACOS = "nacos"; private static final String FILE_PATH_NAMING = "naming"; private static final String USER_HOME_PROPERTY = "user.home"; /** * Init cache dir. * * @param namespace namespace. * @param properties nacosClientProperties. * @return */ public static String initCacheDir(String namespace, NacosClientProperties properties) { String jmSnapshotPath = properties.getProperty(JM_SNAPSHOT_PATH_PROPERTY); String namingCacheRegistryDir = ""; if (properties.getProperty(PropertyKeyConst.NAMING_CACHE_REGISTRY_DIR) != null) { namingCacheRegistryDir = File.separator + properties.getProperty(PropertyKeyConst.NAMING_CACHE_REGISTRY_DIR); } if (!StringUtils.isBlank(jmSnapshotPath)) { cacheDir = jmSnapshotPath + File.separator + FILE_PATH_NACOS + namingCacheRegistryDir + File.separator + FILE_PATH_NAMING + File.separator + namespace; } else { cacheDir = properties.getProperty(USER_HOME_PROPERTY) + File.separator + FILE_PATH_NACOS + namingCacheRegistryDir + File.separator + FILE_PATH_NAMING + File.separator + namespace; } return cacheDir; } public static String getCacheDir() { return cacheDir; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/utils/Chooser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** * Chooser. * * @author alibaba */ public class Chooser { private final K uniqueKey; private volatile Ref ref; public Chooser(K uniqueKey) { this(uniqueKey, new ArrayList<>()); } public Chooser(K uniqueKey, List> pairs) { Ref ref = new Ref<>(pairs); ref.refresh(); this.uniqueKey = uniqueKey; this.ref = ref; } /** * Random get one item. * * @return item */ public T random() { List items = ref.items; if (items.size() == 0) { return null; } if (items.size() == 1) { return items.get(0); } return items.get(ThreadLocalRandom.current().nextInt(items.size())); } /** * Random get one item with weight. * * @return item */ public T randomWithWeight() { Ref ref = this.ref; double random = ThreadLocalRandom.current().nextDouble(0, 1); int index = Arrays.binarySearch(ref.weights, random); if (index < 0) { index = -index - 1; } else { return ref.items.get(index); } if (index < ref.weights.length) { if (random < ref.weights[index]) { return ref.items.get(index); } } if (ref.weights.length == 0) { throw new IllegalStateException("Cumulative Weight wrong , the array length is equal to 0."); } /* This should never happen, but it ensures we will return a correct * object in case there is some floating point inequality problem * wrt the cumulative probabilities. */ return ref.items.get(ref.items.size() - 1); } public K getUniqueKey() { return uniqueKey; } public Ref getRef() { return ref; } /** * refresh items. * * @param itemsWithWeight items with weight */ public void refresh(List> itemsWithWeight) { Ref newRef = new Ref<>(itemsWithWeight); newRef.refresh(); newRef.poller = this.ref.poller.refresh(newRef.items); this.ref = newRef; } public class Ref { private List> itemsWithWeight = new ArrayList<>(); private final List items = new ArrayList<>(); private Poller poller = new GenericPoller<>(items); private double[] weights; public Ref(List> itemsWithWeight) { if (itemsWithWeight != null) { this.itemsWithWeight = itemsWithWeight; } } /** * Refresh. */ public void refresh() { double originWeightSum = 0; int size = 0; for (Pair item : itemsWithWeight) { double weight = item.weight(); //ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest if (weight <= 0) { continue; } items.add(item.item()); if (Double.isInfinite(weight)) { weight = 10000.0D; } if (Double.isNaN(weight)) { weight = 1.0D; } originWeightSum += weight; size++; } weights = new double[size]; double exactWeight; double randomRange = 0D; int index = 0; for (Pair item : itemsWithWeight) { double singleWeight = item.weight(); //ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest if (singleWeight <= 0) { continue; } exactWeight = singleWeight / originWeightSum; weights[index] = randomRange + exactWeight; randomRange = weights[index++]; } double doublePrecisionDelta = 0.0001; if (index == 0 || (Math.abs(weights[index - 1] - 1) < doublePrecisionDelta)) { return; } throw new IllegalStateException( "Cumulative Weight calculate wrong , the sum of probabilities does not equals 1."); } @Override public int hashCode() { return itemsWithWeight.hashCode(); } @SuppressWarnings("unchecked") @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null) { return false; } if (getClass() != other.getClass()) { return false; } Ref otherRef = (Ref) other; return this.itemsWithWeight.equals(otherRef.itemsWithWeight); } } @Override public int hashCode() { return uniqueKey.hashCode(); } @Override public boolean equals(Object other) { if (this == other) { return true; } if (other == null) { return false; } if (getClass() != other.getClass()) { return false; } Chooser otherChooser = (Chooser) other; if (this.uniqueKey == null) { if (otherChooser.getUniqueKey() != null) { return false; } } else { if (otherChooser.getUniqueKey() == null) { return false; } else if (!this.uniqueKey.equals(otherChooser.getUniqueKey())) { return false; } } return this.ref.equals(otherChooser.getRef()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/utils/GenericPoller.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * Generic Poller. * * @author nkorange */ public class GenericPoller implements Poller { private final AtomicInteger index = new AtomicInteger(0); private List items = new ArrayList<>(); public GenericPoller(List items) { this.items = items; } @Override public T next() { return items.get(Math.abs(index.getAndIncrement() % items.size())); } @Override public Poller refresh(List items) { return new GenericPoller<>(items); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/utils/InitUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.api.selector.SelectorType; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.env.SourceType; import com.alibaba.nacos.client.utils.ContextPathUtil; import com.alibaba.nacos.client.utils.LogUtils; import com.alibaba.nacos.client.utils.TemplateUtils; import com.alibaba.nacos.client.utils.TenantUtil; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; /** * Init utils. * * @author liaochuntao * @author deshao */ public class InitUtils { /** * Add a difference to the name naming. This method simply initializes the namespace for Naming. Config * initialization is not the same, so it cannot be reused directly. * * @param properties properties * @return namespace */ public static String initNamespaceForNaming(NacosClientProperties properties) { String tmpNamespace = null; String isUseCloudNamespaceParsing = properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, properties.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING))); if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) { tmpNamespace = TenantUtil.getUserTenantForAns(); LogUtils.NAMING_LOGGER.info("initializer namespace from ans.namespace attribute : {}", tmpNamespace); tmpNamespace = TemplateUtils.stringEmptyAndThenExecute(tmpNamespace, () -> { String namespace = properties.getProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE); LogUtils.NAMING_LOGGER.info( "initializer namespace from ALIBABA_ALIWARE_NAMESPACE attribute :" + namespace); return namespace; }); } tmpNamespace = TemplateUtils.stringEmptyAndThenExecute(tmpNamespace, () -> { String namespace = properties.getPropertyFrom(SourceType.JVM, PropertyKeyConst.NAMESPACE); LogUtils.NAMING_LOGGER.info("initializer namespace from namespace attribute :" + namespace); return namespace; }); if (StringUtils.isEmpty(tmpNamespace)) { tmpNamespace = properties.getProperty(PropertyKeyConst.NAMESPACE); } tmpNamespace = TemplateUtils.stringEmptyAndThenExecute(tmpNamespace, () -> UtilAndComs.DEFAULT_NAMESPACE_ID); return tmpNamespace; } /** * Init web root context. * * @param properties properties * @since 1.4.1 */ public static void initWebRootContext(NacosClientProperties properties) { final String webContext = properties.getProperty(PropertyKeyConst.CONTEXT_PATH); TemplateUtils.stringNotEmptyAndThenExecute(webContext, () -> { UtilAndComs.webContext = ContextPathUtil.normalizeContextPath(webContext); UtilAndComs.nacosUrlBase = UtilAndComs.webContext + "/v1/ns"; UtilAndComs.nacosUrlInstance = UtilAndComs.nacosUrlBase + "/instance"; }); } /** * Register subType for serialization. * *

    * Now these subType implementation class has registered in static code. But there are some problem for classloader. * The implementation class will be loaded when they are used, which will make deserialize before register. *

    * *

    * 子类实现类中的静态代码串中已经向Jackson进行了注册,但是由于classloader的原因,只有当 该子类被使用的时候,才会加载该类。这可能会导致Jackson先进性反序列化,再注册子类,从而导致 反序列化失败。 *

    */ public static void initSerialization() { // TODO register in implementation class or remove subType JacksonUtils.registerSubtype(NoneSelector.class, SelectorType.none.name()); JacksonUtils.registerSubtype(ExpressionSelector.class, SelectorType.label.name()); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/utils/Pair.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; /** * Pair. * * @author nkorange */ public class Pair { private final T item; private final double weight; public Pair(T item, double weight) { this.item = item; this.weight = weight; } public T item() { return item; } public double weight() { return weight; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/utils/Poller.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import java.util.List; /** * Poller. * * @author nkorange */ public interface Poller { /** * Get next element selected by poller. * * @return next element */ T next(); /** * Update items stored in poller. * * @param items new item list * @return new poller instance */ Poller refresh(List items); } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/naming/utils/UtilAndComs.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; /** * Util and constants. * * @author xuanyin.zy */ public class UtilAndComs { public static String webContext = "/nacos"; public static String nacosUrlBase = webContext + "/v1/ns"; public static String nacosUrlInstance = nacosUrlBase + "/instance"; public static String nacosUrlService = nacosUrlBase + "/service"; public static final String ENV_LIST_KEY = "envList"; public static final String ALL_IPS = "000--00-ALL_IPS--00--000"; public static final String FAILOVER_SWITCH = "00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00"; public static final String DEFAULT_NAMESPACE_ID = "public"; public static final int REQUEST_DOMAIN_RETRY_COUNT = 3; @Deprecated public static final String NACOS_NAMING_LOG_NAME = "com.alibaba.nacos.naming.log.filename"; @Deprecated public static final String NACOS_NAMING_LOG_LEVEL = "com.alibaba.nacos.naming.log.level"; public static final String ENV_CONFIGS = "00-00---000-ENV_CONFIGS-000---00-00"; public static final String VIP_CLIENT_FILE = "vipclient.properties"; public static final String ALL_HOSTS = "00-00---000-ALL_HOSTS-000---00-00"; } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/redo/data/RedoData.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.redo.data; import java.util.Objects; /** * Nacos naming redo data. * * @author xiweng.yy */ public abstract class RedoData { /** * Expected states for finally. * *
      *
    • {@code true} meas the cached data expect registered to server finally.
    • *
    • {@code false} means unregistered from server.
    • *
    */ private volatile boolean expectedRegistered; /** * If {@code true} means cached data has been registered to server successfully. */ private volatile boolean registered; /** * If {@code true} means cached data is unregistering from server. */ private volatile boolean unregistering; private T data; protected RedoData() { this.expectedRegistered = true; } public void setExpectedRegistered(boolean registered) { this.expectedRegistered = registered; } public boolean isExpectedRegistered() { return expectedRegistered; } public boolean isRegistered() { return registered; } public boolean isUnregistering() { return unregistering; } public void setRegistered(boolean registered) { this.registered = registered; } public void setUnregistering(boolean unregistering) { this.unregistering = unregistering; } public T get() { return data; } public void set(T data) { this.data = data; } public void registered() { this.registered = true; this.unregistering = false; } public void unregistered() { this.registered = false; this.unregistering = true; } public boolean isNeedRedo() { return !RedoType.NONE.equals(getRedoType()); } /** * Get redo type for current redo data without expected state. * *
      *
    • {@code registered=true} & {@code unregistering=false} means data has registered, so redo should not do anything.
    • *
    • {@code registered=true} & {@code unregistering=true} means data has registered and now need unregister.
    • *
    • {@code registered=false} & {@code unregistering=false} means not registered yet, need register again.
    • *
    • {@code registered=false} & {@code unregistering=true} means not registered yet and not continue to register.
    • *
    * * @return redo type */ public RedoType getRedoType() { if (isRegistered() && !isUnregistering()) { return expectedRegistered ? RedoType.NONE : RedoType.UNREGISTER; } else if (isRegistered() && isUnregistering()) { return RedoType.UNREGISTER; } else if (!isRegistered() && !isUnregistering()) { return RedoType.REGISTER; } else { return expectedRegistered ? RedoType.REGISTER : RedoType.REMOVE; } } public enum RedoType { /** * Redo register. */ REGISTER, /** * Redo unregister. */ UNREGISTER, /** * Redo nothing. */ NONE, /** * Remove redo data. */ REMOVE; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } RedoData redoData = (RedoData) o; return registered == redoData.registered && unregistering == redoData.unregistering && Objects.equals(data, redoData.data); } @Override public int hashCode() { return Objects.hash(registered, unregistering, data); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/redo/service/AbstractRedoService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.redo.service; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.redo.data.RedoData; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.ConnectionEventListener; import org.slf4j.Logger; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Abstract redo service. * * @author xiweng.yy */ public abstract class AbstractRedoService implements ConnectionEventListener, Closeable { private static final String REDO_THREAD_NAME_PATTERN = "com.alibaba.nacos.client.%s.redo"; private final Logger logger; private final ScheduledExecutorService redoExecutor; private final Map, Map>> redoDataMap; private int redoThreadCount; private long redoDelayTime; private volatile boolean connected = false; protected AbstractRedoService(Logger logger, NacosClientProperties properties, String module) { this.logger = logger; setProperties(properties); this.redoExecutor = new ScheduledThreadPoolExecutor(redoThreadCount, new NameThreadFactory(String.format(REDO_THREAD_NAME_PATTERN, module))); this.redoDataMap = new ConcurrentHashMap<>(2); } private void setProperties(NacosClientProperties properties) { redoDelayTime = properties.getLong(PropertyKeyConst.REDO_DELAY_TIME, Constants.DEFAULT_REDO_DELAY_TIME); redoThreadCount = properties.getInteger(PropertyKeyConst.REDO_DELAY_THREAD_COUNT, Constants.DEFAULT_REDO_THREAD_COUNT); } protected void startRedoTask() { this.redoExecutor.scheduleWithFixedDelay(buildRedoTask(), redoDelayTime, redoDelayTime, TimeUnit.MILLISECONDS); } /** * Build redo task to do redo work. * * @return redo task */ protected abstract AbstractRedoTask buildRedoTask(); @Override public void onConnected(Connection connection) { connected = true; logger.info("Grpc connection connect"); } @Override public void onDisConnect(Connection connection) { connected = false; logger.warn("Grpc connection disconnect, mark to redo"); for (Class each : redoDataMap.keySet()) { Map> actualRedoData = this.redoDataMap.get(each); synchronized (actualRedoData) { actualRedoData.values().forEach(redoData -> redoData.setRegistered(false)); } } logger.warn("mark to redo completed"); } @Override public void shutdown() { logger.info("Shutdown grpc redo service executor {}", redoExecutor); redoDataMap.clear(); redoExecutor.shutdownNow(); } public boolean isConnected() { return connected; } /** * Cache the redo data by class and redo data key. * * @param key key of redo data * @param redoData the redo data * @param clazz clazz of stored in {@link RedoData}. */ public void cachedRedoData(String key, RedoData redoData, Class clazz) { Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { actualRedoData.put(key, redoData); } } /** * Remove data for redo. * * @param key key of redo data * @param clazz clazz of stored in {@link RedoData}. */ public void removeRedoData(String key, Class clazz) { Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { RedoData redoData = actualRedoData.get(key); if (null != redoData && !redoData.isExpectedRegistered()) { actualRedoData.remove(key); } } } /** * Data register successfully, mark registered status as {@code true}. * * @param key key of redo data * @param clazz clazz of stored in {@link RedoData}. */ public void dataRegistered(String key, Class clazz) { Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { RedoData redoData = actualRedoData.get(key); if (null != redoData) { redoData.registered(); } } } /** * Data deregister, mark unregistering status as {@code true}. * * @param key key of redo data * @param clazz clazz of stored in {@link RedoData}. */ public void dataDeregister(String key, Class clazz) { Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { RedoData redoData = actualRedoData.get(key); if (null != redoData) { redoData.setUnregistering(true); redoData.setExpectedRegistered(false); } } } /** * Data deregister finished, mark unregistering status as {@code true}. * * @param key key of redo data * @param clazz clazz of stored in {@link RedoData}. */ public void dataDeregistered(String key, Class clazz) { Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { RedoData redoData = actualRedoData.get(key); if (null != redoData) { redoData.unregistered(); } } } /** * Judge data has registered to server. * * @param key key of redo data * @param clazz clazz of stored in {@link RedoData}. * @return {@code true} if registered, otherwise {@code false} */ public boolean isDataRegistered(String key, Class clazz) { Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { RedoData redoData = actualRedoData.get(key); return null != redoData && redoData.isRegistered(); } } /** * Find all redo data which need to do redo. * * @return set of {@link RedoData} need to do redo. */ public Set> findRedoData(Class clazz) { Set> result = new HashSet<>(); Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { for (RedoData each : actualRedoData.values()) { if (each.isNeedRedo()) { result.add((RedoData) each); } } } return result; } /** * get Cache redo data. * * @param key key of redo data * @param clazz clazz of stored in {@link RedoData}. * @return cache redo data */ public RedoData getRedoData(String key, Class clazz) { Map> actualRedoData = this.redoDataMap.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>(2)); synchronized (actualRedoData) { return (RedoData) actualRedoData.get(key); } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/redo/service/AbstractRedoTask.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.redo.service; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.task.AbstractExecuteTask; import org.slf4j.Logger; /** * Nacos client abstract redo task. * * @author xiweng.yy */ public abstract class AbstractRedoTask extends AbstractExecuteTask { private final Logger logger; private final S redoService; public AbstractRedoTask(Logger logger, S redoService) { this.logger = logger; this.redoService = redoService; } @Override public void run() { if (!redoService.isConnected()) { logger.warn("Grpc Connection is disconnect, skip current redo task"); return; } try { redoData(); } catch (Exception e) { logger.warn("Redo task run with unexpected exception: ", e); } } /** * Do actual redo task. * * @throws NacosException if redo task failed. */ protected abstract void redoData() throws NacosException; protected S getRedoService() { return redoService; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/security/SecurityProxy.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.security; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.plugin.auth.spi.client.ClientAuthPluginManager; import com.alibaba.nacos.plugin.auth.spi.client.ClientAuthService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * Security proxy to update security information. * * @author nkorange * @since 1.2.0 */ public class SecurityProxy implements Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(SecurityProxy.class); private ClientAuthPluginManager clientAuthPluginManager; /** * Construct from serverListManager, nacosRestTemplate, init client auth plugin. * * @param serverListManager a server list manager that client request to. * @Param nacosRestTemplate http request template. */ public SecurityProxy(AbstractServerListManager serverListManager, NacosRestTemplate nacosRestTemplate) { clientAuthPluginManager = new ClientAuthPluginManager(); clientAuthPluginManager.init(serverListManager.getServerList(), nacosRestTemplate); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(ServerListChangeEvent event) { clientAuthPluginManager.refreshServerList(serverListManager.getServerList()); } @Override public Class subscribeType() { return ServerListChangeEvent.class; } }); } /** * Login all available ClientAuthService instance. * * @param properties login identity information. */ public void login(Properties properties) { if (clientAuthPluginManager.getAuthServiceSpiImplSet().isEmpty()) { return; } for (ClientAuthService clientAuthService : clientAuthPluginManager.getAuthServiceSpiImplSet()) { clientAuthService.login(properties); } } /** * get the context of all nacosRestTemplate instance. * * @return a combination of all context. */ public Map getIdentityContext(RequestResource resource) { Map header = new HashMap<>(1); for (ClientAuthService clientAuthService : clientAuthPluginManager.getAuthServiceSpiImplSet()) { LoginIdentityContext loginIdentityContext = clientAuthService.getLoginIdentityContext(resource); for (String key : loginIdentityContext.getAllKey()) { header.put(key, loginIdentityContext.getParameter(key)); } } return header; } @Override public void shutdown() throws NacosException { clientAuthPluginManager.shutdown(); } /** * Login again to refresh the accessToken. */ public void reLogin() { if (clientAuthPluginManager.getAuthServiceSpiImplSet().isEmpty()) { return; } for (ClientAuthService clientAuthService : clientAuthPluginManager.getAuthServiceSpiImplSet()) { try { LoginIdentityContext loginIdentityContext = clientAuthService.getLoginIdentityContext(new RequestResource()); if (loginIdentityContext != null) { loginIdentityContext.setParameter(NacosAuthLoginConstant.RELOGINFLAG, "true"); } } catch (Exception e) { LOGGER.error("[SecurityProxy] set reLoginFlag failed.", e); } } } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/selector/AbstractSelectorWrapper.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.selector; import com.alibaba.nacos.api.selector.client.Selector; import com.alibaba.nacos.common.notify.Event; import java.util.Objects; /** * Selector Wrapper. * * @param the type of selector * @param the type of original event * @param the type of listener callback event * @author lideyou */ public abstract class AbstractSelectorWrapper, E, T extends Event> { private final S selector; private final ListenerInvoker listener; public AbstractSelectorWrapper(S selector, ListenerInvoker listener) { this.selector = selector; this.listener = listener; } /** * Check whether the event can be callback. * * @param event original event * @return true if the event can be callback */ protected abstract boolean isSelectable(T event); /** * Check whether the result can be callback. * * @param event select result * @return true if the result can be callback */ protected abstract boolean isCallable(E event); /** * Build an event received by the listener. * * @param event original event * @return listener event */ protected abstract E buildListenerEvent(T event); /** * Notify listener. * * @param event original event */ public void notifyListener(T event) { if (!isSelectable(event)) { return; } E newEvent = buildListenerEvent(event); if (isCallable(newEvent)) { // lock listener to make sure isInvoked is thread safe. synchronized (listener) { listener.invoke(newEvent); } } } /** * Notify listener If the listener is not invoked. * * @param event original event */ public void notifyIfListenerIfNotNotified(T event) { if (!isSelectable(event)) { return; } E newEvent = buildListenerEvent(event); synchronized (listener) { if (!listener.isInvoked()) { listener.invoke(newEvent); } } } public ListenerInvoker getListener() { return this.listener; } public S getSelector() { return this.selector; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } AbstractSelectorWrapper that = (AbstractSelectorWrapper) o; return Objects.equals(selector, that.selector) && Objects.equals(listener, that.listener); } @Override public int hashCode() { return Objects.hash(selector, listener); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/selector/ListenerInvoker.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.selector; /** * Listener invoker. * * @param the type of event received by the listener * @author lideyou */ public interface ListenerInvoker { /** * Invoke inner listener. * * @param event event */ void invoke(E event); /** * Mark the listener whether invoked once. It should return {@code true} after {@link #invoke(E)} called at lease once. * * @return {@code true} if this listener has invoked at least once, {@code false} otherwise */ boolean isInvoked(); } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/selector/SelectorManager.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.selector; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Selector Manager. * * @param the type of selector wrapper * @author lideyou */ public class SelectorManager> { Map> selectorMap = new ConcurrentHashMap<>(); /** * Add a selectorWrapper to subId. * * @param subId subscription id * @param wrapper selector wrapper */ public void addSelectorWrapper(String subId, S wrapper) { selectorMap.compute(subId, (k, v) -> { if (v == null) { v = new ConcurrentHashSet<>(); } v.add(wrapper); return v; }); } /** * Get all SelectorWrappers by id. * * @param subId subscription id * @return the set of SelectorWrappers */ public Set getSelectorWrappers(String subId) { return selectorMap.getOrDefault(subId, Collections.emptySet()); } /** * Remove a SelectorWrapper by id. * * @param subId subscription id * @param wrapper selector wrapper */ public void removeSelectorWrapper(String subId, S wrapper) { selectorMap.computeIfPresent(subId, (k, v) -> { v.remove(wrapper); return v.isEmpty() ? null : v; }); } /** * Remove a subscription by id. * * @param subId subscription id */ public void removeSubscription(String subId) { selectorMap.remove(subId); } /** * Get all subscriptions. * * @return all subscriptions */ public Set getSubscriptions() { return selectorMap.keySet(); } /** * Determine whether subId is subscribed. * * @param subId subscription id * @return true if is subscribed */ public boolean isSubscribed(String subId) { return CollectionUtils.isNotEmpty(this.getSelectorWrappers(subId)); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/utils/ConcurrentDiskUtil.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.channels.FileChannel; import java.nio.channels.FileLock; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; /** * Concurrent Disk util. * * @author nkorange */ public class ConcurrentDiskUtil { private static final Logger LOGGER = LoggerFactory.getLogger(ConcurrentDiskUtil.class); private static final String READ_ONLY = "r"; private static final String READ_WRITE = "rw"; private static final int RETRY_COUNT = 10; private static final int SLEEP_BASETIME = 10; /** * get file content. * * @param path file path * @param charsetName charsetName * @return content * @throws IOException IOException */ public static String getFileContent(String path, String charsetName) throws IOException { File file = new File(path); return getFileContent(file, charsetName); } /** * get file content. * * @param file file * @param charsetName charsetName * @return content * @throws IOException IOException */ public static String getFileContent(File file, String charsetName) throws IOException { try (RandomAccessFile fis = new RandomAccessFile(file, READ_ONLY); FileChannel fcin = fis.getChannel(); FileLock rlock = tryLock(file, fcin, true)) { int fileSize = (int) fcin.size(); ByteBuffer byteBuffer = ByteBuffer.allocate(fileSize); fcin.read(byteBuffer); byteBuffer.flip(); return byteBufferToString(byteBuffer, charsetName); } } /** * write file content. * * @param path file path * @param content content * @param charsetName charsetName * @return whether write ok * @throws IOException IOException */ public static Boolean writeFileContent(String path, String content, String charsetName) throws IOException { File file = new File(path); return writeFileContent(file, content, charsetName); } /** * write file content. * * @param file file * @param content content * @param charsetName charsetName * @return whether write ok * @throws IOException IOException */ public static Boolean writeFileContent(File file, String content, String charsetName) throws IOException { if (!file.exists() && !file.createNewFile()) { return false; } try (RandomAccessFile raf = new RandomAccessFile(file, READ_WRITE); FileChannel channel = raf.getChannel(); FileLock lock = tryLock(file, channel, false)) { byte[] contentBytes = content.getBytes(charsetName); ByteBuffer sendBuffer = ByteBuffer.wrap(contentBytes); while (sendBuffer.hasRemaining()) { channel.write(sendBuffer); } channel.truncate(contentBytes.length); } catch (FileNotFoundException e) { throw new IOException("file not exist"); } return true; } /** * transfer ByteBuffer to String. * * @param buffer buffer * @param charsetName charsetName * @return String * @throws IOException IOException */ public static String byteBufferToString(ByteBuffer buffer, String charsetName) throws IOException { Charset charset = Charset.forName(charsetName); CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = decoder.decode(buffer.asReadOnlyBuffer()); return charBuffer.toString(); } private static void sleep(int time) { try { Thread.sleep(time); } catch (InterruptedException e) { LOGGER.warn("sleep wrong", e); // set the interrupted flag Thread.currentThread().interrupt(); } } private static FileLock tryLock(File file, FileChannel channel, boolean shared) throws IOException { FileLock result = null; int i = 0; do { try { result = channel.tryLock(0L, Long.MAX_VALUE, shared); } catch (Exception e) { ++i; if (i > RETRY_COUNT) { LOGGER.error("[NA] lock " + file.getName() + " fail;retryed time: " + i, e); throw new IOException("lock " + file.getAbsolutePath() + " conflict"); } sleep(SLEEP_BASETIME * i); LOGGER.warn("lock " + file.getName() + " conflict;retry time: " + i); } } while (null == result); return result; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/utils/EnvUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.common.Constants; import org.slf4j.Logger; import java.util.List; import java.util.Map; import java.util.Objects; /** * env util. * * @author Nacos */ public class EnvUtil { public static final Logger LOGGER = LogUtils.logger(EnvUtil.class); private static String selfAmoryTag; private static String selfVipserverTag; private static String selfLocationTag; public static void setSelfEnv(Map> headers) { if (headers != null) { List amoryTagTmp = headers.get(Constants.AMORY_TAG); if (amoryTagTmp == null) { if (selfAmoryTag != null) { selfAmoryTag = null; LOGGER.warn("selfAmoryTag:null"); } } else { String amoryTagTmpStr = listToString(amoryTagTmp); if (!Objects.equals(amoryTagTmpStr, selfAmoryTag)) { selfAmoryTag = amoryTagTmpStr; LOGGER.warn("selfAmoryTag:{}", selfAmoryTag); } } List vipserverTagTmp = headers.get(Constants.VIPSERVER_TAG); if (vipserverTagTmp == null) { if (selfVipserverTag != null) { selfVipserverTag = null; LOGGER.warn("selfVipserverTag:null"); } } else { String vipserverTagTmpStr = listToString(vipserverTagTmp); if (!Objects.equals(vipserverTagTmpStr, selfVipserverTag)) { selfVipserverTag = vipserverTagTmpStr; LOGGER.warn("selfVipserverTag:{}", selfVipserverTag); } } List locationTagTmp = headers.get(Constants.LOCATION_TAG); if (locationTagTmp == null) { if (selfLocationTag != null) { selfLocationTag = null; LOGGER.warn("selfLocationTag:null"); } } else { String locationTagTmpStr = listToString(locationTagTmp); if (!Objects.equals(locationTagTmpStr, selfLocationTag)) { selfLocationTag = locationTagTmpStr; LOGGER.warn("selfLocationTag:{}", selfLocationTag); } } } } public static String getSelfAmoryTag() { return selfAmoryTag; } public static String getSelfVipserverTag() { return selfVipserverTag; } public static String getSelfLocationTag() { return selfLocationTag; } private static String listToString(List list) { if (list == null || list.isEmpty()) { return null; } StringBuilder result = new StringBuilder(); for (String string : list) { result.append(string); result.append(','); } return result.substring(0, result.length() - 1); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/utils/LogUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.client.logging.NacosLogging; import org.slf4j.Logger; import static org.slf4j.LoggerFactory.getLogger; /** * Log utils. * * @author hxy1991 * @since 0.9.0 */ public class LogUtils { public static final Logger NAMING_LOGGER; static { NacosLogging.getInstance().loadConfiguration(); NAMING_LOGGER = getLogger("com.alibaba.nacos.client.naming"); } public static Logger logger(Class clazz) { return getLogger(clazz); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/utils/ParamUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; /** * manage param tool. * * @author nacos */ public class ParamUtil { private static final Logger LOGGER = LogUtils.logger(ParamUtil.class); private static int connectTimeout; private static int readTimeout; private static double perTaskConfigSize; private static final String NACOS_CONNECT_TIMEOUT_KEY = "NACOS.CONNECT.TIMEOUT"; private static final String NACOS_READ_TIMEOUT_KEY = "NACOS.READ.TIMEOUT"; private static final String DEFAULT_NACOS_CONNECT_TIMEOUT = "1000"; private static final String DEFAULT_NACOS_READ_TIMEOUT = "3000"; private static final String PER_TASK_CONFIG_SIZE_KEY = "PER_TASK_CONFIG_SIZE"; private static final String DEFAULT_PER_TASK_CONFIG_SIZE_KEY = "3000"; static { // Client identity information connectTimeout = initConnectionTimeout(); LOGGER.info("[settings] [http-client] connect timeout:{}", connectTimeout); readTimeout = initReadTimeout(); LOGGER.info("[settings] [http-client] read timeout:{}", readTimeout); perTaskConfigSize = initPerTaskConfigSize(); LOGGER.info("PER_TASK_CONFIG_SIZE: {}", perTaskConfigSize); } private static int initConnectionTimeout() { String tmp = DEFAULT_NACOS_CONNECT_TIMEOUT; try { tmp = NacosClientProperties.PROTOTYPE.getProperty(NACOS_CONNECT_TIMEOUT_KEY, DEFAULT_NACOS_CONNECT_TIMEOUT); return Integer.parseInt(tmp); } catch (NumberFormatException e) { final String msg = "[http-client] invalid connect timeout:" + tmp; LOGGER.error("[settings] " + msg, e); throw new IllegalArgumentException(msg, e); } } private static int initReadTimeout() { String tmp = DEFAULT_NACOS_READ_TIMEOUT; try { tmp = NacosClientProperties.PROTOTYPE.getProperty(NACOS_READ_TIMEOUT_KEY, DEFAULT_NACOS_READ_TIMEOUT); return Integer.parseInt(tmp); } catch (NumberFormatException e) { final String msg = "[http-client] invalid read timeout:" + tmp; LOGGER.error("[settings] " + msg, e); throw new IllegalArgumentException(msg, e); } } private static double initPerTaskConfigSize() { try { return Double.parseDouble(NacosClientProperties.PROTOTYPE.getProperty(PER_TASK_CONFIG_SIZE_KEY, DEFAULT_PER_TASK_CONFIG_SIZE_KEY)); } catch (NumberFormatException e) { LOGGER.error("[PER_TASK_CONFIG_SIZE] PER_TASK_CONFIG_SIZE invalid", e); throw new IllegalArgumentException("invalid PER_TASK_CONFIG_SIZE, expected value type double", e); } } public static int getConnectTimeout() { return connectTimeout; } public static void setConnectTimeout(int connectTimeout) { ParamUtil.connectTimeout = connectTimeout; } public static int getReadTimeout() { return readTimeout; } public static void setReadTimeout(int readTimeout) { ParamUtil.readTimeout = readTimeout; } public static double getPerTaskConfigSize() { return perTaskConfigSize; } public static void setPerTaskConfigSize(double perTaskConfigSize) { ParamUtil.perTaskConfigSize = perTaskConfigSize; } public static final int MAX_ENV_NAME_LENGTH = 50; /** * simply env name if name is too long. * * @param envName env name. * @return env name. */ public static String simplyEnvNameIfOverLimit(String envName) { if (StringUtils.isNotBlank(envName) && envName.length() > MAX_ENV_NAME_LENGTH) { return envName.substring(0, MAX_ENV_NAME_LENGTH) + MD5Utils.md5Hex(envName, "UTF-8"); } return envName; } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/utils/PreInitUtils.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.client.auth.ram.utils.SpasAdapter; import com.alibaba.nacos.common.utils.JacksonUtils; /** * Async do pre init to load some cost component. * *
      *
    • JacksonUtil
    • *
    • SpasAdapter
    • *
    * * @author xiweng.yy */ public class PreInitUtils { /** * Async pre load cost component. */ public static void asyncPreLoadCostComponent() { Thread preLoadThread = new Thread(() -> { // Jackson util will init static {@code ObjectMapper}, which will cost hundreds milliseconds. JacksonUtils.createEmptyJsonNode(); // Ram auth plugin will try to get credential from env and system when leak input identity by properties. SpasAdapter.getAk(); }); preLoadThread.start(); } } ================================================ FILE: client/src/main/java/com/alibaba/nacos/client/utils/ValidatorUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.env.NacosClientProperties; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * All parameter validation tools. * * @author liaochuntao */ public final class ValidatorUtils { private static final Pattern CONTEXT_PATH_MATCH = Pattern.compile("(\\/)\\1+"); public static void checkInitParam(NacosClientProperties properties) throws NacosException { checkContextPath(properties.getProperty(PropertyKeyConst.CONTEXT_PATH)); } /** * Check context path. * * @param contextPath context path */ public static void checkContextPath(String contextPath) { if (contextPath == null) { return; } Matcher matcher = CONTEXT_PATH_MATCH.matcher(contextPath); if (matcher.find()) { throw new IllegalArgumentException("Illegal url path expression"); } } } ================================================ FILE: client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/jni-config.json ================================================ [ { "name":"[Lcom.sun.management.internal.DiagnosticCommandArgumentInfo;" }, { "name":"[Lcom.sun.management.internal.DiagnosticCommandInfo;" }, { "name":"com.sun.management.internal.DiagnosticCommandArgumentInfo", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","boolean","boolean","boolean","int"] }] }, { "name":"com.sun.management.internal.DiagnosticCommandInfo", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","boolean","java.util.List"] }] }, { "name":"java.lang.InternalError", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, { "name":"java.lang.SecurityManager", "fields":[{"name":"initialized"}] }, { "name":"java.util.Arrays", "methods":[{"name":"asList","parameterTypes":["java.lang.Object[]"] }] }, { "name":"sun.instrument.InstrumentationImpl", "methods":[ {"name":"","parameterTypes":["long","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] } ] }, { "name":"sun.management.VMManagementImpl", "fields":[ {"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"} ] } ] ================================================ FILE: client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/predefined-classes-config.json ================================================ [ { "type":"agent-extracted", "classes":[ ] } ] ================================================ FILE: client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/proxy-config.json ================================================ [ ] ================================================ FILE: client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/reflect-config.json ================================================ [ { "name":"[B" }, { "name":"[C" }, { "name":"[D" }, { "name":"[F" }, { "name":"[I" }, { "name":"[J" }, { "name":"[Ljava.lang.String;" }, { "name":"[Ljavax.management.openmbean.CompositeData;" }, { "name":"[Lorg.apache.logging.log4j.core.Appender;" }, { "name":"[Lorg.apache.logging.log4j.core.appender.rolling.TriggeringPolicy;" }, { "name":"[Lorg.apache.logging.log4j.core.appender.rolling.action.Action;" }, { "name":"[Lorg.apache.logging.log4j.core.appender.rolling.action.PathCondition;" }, { "name":"[Lorg.apache.logging.log4j.core.config.AppenderRef;" }, { "name":"[Lorg.apache.logging.log4j.core.config.LoggerConfig;" }, { "name":"[Lorg.apache.logging.log4j.core.config.Property;" }, { "name":"[S" }, { "name":"[Z" }, { "name":"ch.qos.logback.classic.AsyncAppender", "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.Logger" }, { "name":"ch.qos.logback.classic.LoggerContext" }, { "name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder", "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.DateConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.LevelConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.LoggerConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.MessageConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.ThreadConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.core.AsyncAppenderBase", "methods":[ {"name":"setDiscardingThreshold","parameterTypes":["int"] }, {"name":"setNeverBlock","parameterTypes":["boolean"] }, {"name":"setQueueSize","parameterTypes":["int"] } ] }, { "name":"ch.qos.logback.core.OutputStreamAppender", "methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }] }, { "name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder", "methods":[{"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }] }, { "name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase", "methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }] }, { "name":"ch.qos.logback.core.rolling.FixedWindowRollingPolicy", "queryAllPublicMethods":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setMaxIndex","parameterTypes":["int"] } ] }, { "name":"ch.qos.logback.core.rolling.RollingFileAppender", "queryAllPublicMethods":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setFile","parameterTypes":["java.lang.String"] }, {"name":"setRollingPolicy","parameterTypes":["ch.qos.logback.core.rolling.RollingPolicy"] }, {"name":"setTriggeringPolicy","parameterTypes":["ch.qos.logback.core.rolling.TriggeringPolicy"] } ] }, { "name":"ch.qos.logback.core.rolling.RollingPolicyBase", "methods":[ {"name":"setFileNamePattern","parameterTypes":["java.lang.String"] }, {"name":"setParent","parameterTypes":["ch.qos.logback.core.FileAppender"] } ] }, { "name":"ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy", "queryAllPublicMethods":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setMaxFileSize","parameterTypes":["ch.qos.logback.core.util.FileSize"] } ] }, { "name":"ch.qos.logback.core.rolling.helper.IntegerTokenConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.core.util.FileSize", "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.api.ability.ClientAbilities", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getConfigAbility","parameterTypes":[] }, {"name":"getNamingAbility","parameterTypes":[] }, {"name":"getRemoteAbility","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.ability.ServerAbilities", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getConfigAbility","parameterTypes":[] }, {"name":"getNamingAbility","parameterTypes":[] }, {"name":"getRemoteAbility","parameterTypes":[] }, {"name":"setConfigAbility","parameterTypes":["com.alibaba.nacos.api.config.ability.ServerConfigAbility"] }, {"name":"setNamingAbility","parameterTypes":["com.alibaba.nacos.api.naming.ability.ServerNamingAbility"] }, {"name":"setRemoteAbility","parameterTypes":["com.alibaba.nacos.api.remote.ability.ServerRemoteAbility"] } ] }, { "name":"com.alibaba.nacos.api.config.ability.ClientConfigAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"isSupportRemoteMetrics","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.ability.ServerConfigAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"isSupportRemoteMetrics","parameterTypes":[] }, {"name":"setSupportRemoteMetrics","parameterTypes":["boolean"] } ] }, { "name": "com.alibaba.nacos.api.config.remote.request.AbstractConfigRequest", "allDeclaredFields": true, "allDeclaredConstructors": true, "methods": [ {"name": "getDataId","parameterTypes": [ ]}, {"name": "getGroup","parameterTypes": [ ]}, {"name": "getModule","parameterTypes": [ ]}, {"name": "getTenant","parameterTypes": [ ]}, {"name": "setDataId","parameterTypes": ["java.lang.String"]}, {"name": "setGroup","parameterTypes": ["java.lang.String"]}, {"name": "setTenant","parameterTypes": ["java.lang.String"]} ] }, { "name":"com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getMetricsKeys","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest$MetricsKey", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getKey","parameterTypes":[] }, {"name":"getType","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getConfigListenContexts","parameterTypes":[] }, {"name":"isListen","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest$ConfigListenContext", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getDataId","parameterTypes":[] }, {"name":"getGroup","parameterTypes":[] }, {"name":"getMd5","parameterTypes":[] }, {"name":"getTenant","parameterTypes":[] } ] }, { "name": "com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest", "allDeclaredConstructors": true, "allDeclaredFields": true, "methods": [ {"name": "setGroup","parameterTypes": ["java.lang.String"]}, {"name": "getTenant","parameterTypes": [ ]}, {"name": "setTenant","parameterTypes": ["java.lang.String"]}, {"name": "setDataId","parameterTypes": ["java.lang.String"]}, {"name": "getGroup","parameterTypes": [ ]}, {"name": "getDataId","parameterTypes": [ ]}, {"name": "getModule","parameterTypes": [ ]} ] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getAdditionMap","parameterTypes":[] }, {"name":"getCasMd5","parameterTypes":[] }, {"name":"getContent","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getTag","parameterTypes":[] }, {"name":"isNotify","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getTag","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getLastModified","parameterTypes":[] }, {"name":"getTag","parameterTypes":[] }, {"name":"isBatch","parameterTypes":[] }, {"name":"isBeta","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setMetrics","parameterTypes":["java.util.Map"] } ] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setChangedConfigs","parameterTypes":["java.util.List"] } ] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse$ConfigContext", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setDataId","parameterTypes":["java.lang.String"] }, {"name":"setGroup","parameterTypes":["java.lang.String"] }, {"name":"setTenant","parameterTypes":["java.lang.String"] } ] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setBeta","parameterTypes":["boolean"] }, {"name":"setContent","parameterTypes":["java.lang.String"] }, {"name":"setContentType","parameterTypes":["java.lang.String"] }, {"name":"setEncryptedDataKey","parameterTypes":["java.lang.String"] }, {"name":"setLastModified","parameterTypes":["long"] }, {"name":"setMd5","parameterTypes":["java.lang.String"] }, {"name":"setTag","parameterTypes":["java.lang.String"] } ] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.grpc.auto.Metadata", "methods":[ {"name":"getClientIp","parameterTypes":[] }, {"name":"getClientIpBytes","parameterTypes":[] }, {"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"getTypeBytes","parameterTypes":[] }, {"name":"newBuilder","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.grpc.auto.Metadata$Builder", "methods":[ {"name":"clearClientIp","parameterTypes":[] }, {"name":"clearType","parameterTypes":[] }, {"name":"getClientIp","parameterTypes":[] }, {"name":"getClientIpBytes","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"getTypeBytes","parameterTypes":[] }, {"name":"setClientIp","parameterTypes":["java.lang.String"] }, {"name":"setClientIpBytes","parameterTypes":["com.google.protobuf.ByteString"] }, {"name":"setType","parameterTypes":["java.lang.String"] }, {"name":"setTypeBytes","parameterTypes":["com.google.protobuf.ByteString"] } ] }, { "name":"com.alibaba.nacos.api.grpc.auto.Payload", "methods":[ {"name":"getBody","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"hasBody","parameterTypes":[] }, {"name":"hasMetadata","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.grpc.auto.Payload$Builder", "methods":[ {"name":"clearBody","parameterTypes":[] }, {"name":"clearMetadata","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getBodyBuilder","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"getMetadataBuilder","parameterTypes":[] }, {"name":"hasBody","parameterTypes":[] }, {"name":"hasMetadata","parameterTypes":[] }, {"name":"setBody","parameterTypes":["com.google.protobuf.Any"] }, {"name":"setMetadata","parameterTypes":["com.alibaba.nacos.api.grpc.auto.Metadata"] } ] }, { "name":"com.alibaba.nacos.api.naming.ability.ClientNamingAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"isSupportDeltaPush","parameterTypes":[] }, {"name":"isSupportRemoteMetric","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.ability.ServerNamingAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"isSupportJraft","parameterTypes":[] }, {"name":"setSupportJraft","parameterTypes":["boolean"] } ] }, { "name":"com.alibaba.nacos.api.naming.listener.EventListener", "methods":[{"name":"onEvent","parameterTypes":["com.alibaba.nacos.api.naming.listener.Event"] }] }, { "name":"com.alibaba.nacos.api.naming.pojo.Instance", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getClusterName","parameterTypes":[] }, {"name":"getInstanceHeartBeatInterval","parameterTypes":[] }, {"name":"getInstanceHeartBeatTimeOut","parameterTypes":[] }, {"name":"getInstanceId","parameterTypes":[] }, {"name":"getInstanceIdGenerator","parameterTypes":[] }, {"name":"getIp","parameterTypes":[] }, {"name":"getIpDeleteTimeout","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"getPort","parameterTypes":[] }, {"name":"getServiceName","parameterTypes":[] }, {"name":"getWeight","parameterTypes":[] }, {"name":"isEnabled","parameterTypes":[] }, {"name":"isEphemeral","parameterTypes":[] }, {"name":"isHealthy","parameterTypes":[] }, {"name":"setClusterName","parameterTypes":["java.lang.String"] }, {"name":"setEnabled","parameterTypes":["boolean"] }, {"name":"setEphemeral","parameterTypes":["boolean"] }, {"name":"setHealthy","parameterTypes":["boolean"] }, {"name":"setInstanceId","parameterTypes":["java.lang.String"] }, {"name":"setIp","parameterTypes":["java.lang.String"] }, {"name":"setMetadata","parameterTypes":["java.util.Map"] }, {"name":"setPort","parameterTypes":["int"] }, {"name":"setServiceName","parameterTypes":["java.lang.String"] }, {"name":"setWeight","parameterTypes":["double"] } ] }, { "name":"com.alibaba.nacos.api.naming.pojo.ServiceInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getCacheMillis","parameterTypes":[] }, {"name":"getChecksum","parameterTypes":[] }, {"name":"getClusters","parameterTypes":[] }, {"name":"getGroupName","parameterTypes":[] }, {"name":"getHosts","parameterTypes":[] }, {"name":"getLastRefTime","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isAllIPs","parameterTypes":[] }, {"name":"isReachProtectionThreshold","parameterTypes":[] }, {"name":"isValid","parameterTypes":[] }, {"name":"setAllIPs","parameterTypes":["boolean"] }, {"name":"setCacheMillis","parameterTypes":["long"] }, {"name":"setChecksum","parameterTypes":["java.lang.String"] }, {"name":"setClusters","parameterTypes":["java.lang.String"] }, {"name":"setGroupName","parameterTypes":["java.lang.String"] }, {"name":"setHosts","parameterTypes":["java.util.List"] }, {"name":"setLastRefTime","parameterTypes":["long"] }, {"name":"setName","parameterTypes":["java.lang.String"] }, {"name":"setReachProtectionThreshold","parameterTypes":["boolean"] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[ {"name":"getGroupName","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNamespace","parameterTypes":[] }, {"name":"getServiceName","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.AbstractFuzzyWatchNotifyRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getInstances","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] }, {"name":"getServiceChangedType","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.InstanceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getInstance","parameterTypes":[] }, {"name":"getType","parameterTypes":[] } ] }, { "name": "com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest", "allDeclaredFields": true, "allDeclaredConstructors": true, "methods": [ {"name": "getGroupName","parameterTypes": [ ]}, {"name": "getModule","parameterTypes": [ ]}, {"name": "getNamespace","parameterTypes": [ ]}, {"name": "getServiceInfo","parameterTypes": [ ]}, {"name": "getServiceName","parameterTypes": [ ]}, {"name": "setGroupName","parameterTypes": ["java.lang.String"]}, {"name": "setNamespace","parameterTypes": ["java.lang.String"]}, {"name": "setServiceInfo","parameterTypes": ["com.alibaba.nacos.api.naming.pojo.ServiceInfo"]}, {"name": "setServiceName","parameterTypes": ["java.lang.String"]} ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getInstance","parameterTypes":[] }, {"name":"getType","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.ServiceListRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getPageNo","parameterTypes":[] }, {"name":"getPageSize","parameterTypes":[] }, {"name":"getSelector","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getCluster","parameterTypes":[] }, {"name":"getUdpPort","parameterTypes":[] }, {"name":"isHealthyOnly","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyChangeRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getServiceName","parameterTypes":[] }, {"name":"getGroupName","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.FuzzyWatchNotifyInitRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getServicesName","parameterTypes":[] }, {"name":"getPattern","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getClusters","parameterTypes":[] }, {"name":"isSubscribe","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.response.InstanceResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setType","parameterTypes":["java.lang.String"] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setServiceInfo","parameterTypes":["com.alibaba.nacos.api.naming.pojo.ServiceInfo"] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.response.ServiceListResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setCount","parameterTypes":["int"] }, {"name":"setServiceNames","parameterTypes":["java.util.List"] } ] }, { "name":"com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setServiceInfo","parameterTypes":["com.alibaba.nacos.api.naming.pojo.ServiceInfo"] } ] }, { "name":"com.alibaba.nacos.api.remote.Payload", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.api.remote.ability.ClientRemoteAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"isSupportRemoteConnection","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.ability.ServerRemoteAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"isGrpcReportEnabled","parameterTypes":[] }, {"name":"isSupportRemoteConnection","parameterTypes":[] }, {"name":"setGrpcReportEnabled","parameterTypes":["boolean"] }, {"name":"setSupportRemoteConnection","parameterTypes":["boolean"] } ] }, { "name":"com.alibaba.nacos.api.remote.request.ClientDetectionRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getModule","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.request.ConnectResetRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getConnectionId","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] }, {"name":"getServerIp","parameterTypes":[] }, {"name":"getServerPort","parameterTypes":[] } ] }, { "name": "com.alibaba.nacos.api.remote.request.ConnectionSetupRequest", "allDeclaredFields": true, "allDeclaredConstructors": true, "methods": [ {"name": "getAbilityTable","parameterTypes": [ ]}, {"name": "getClientVersion","parameterTypes": [ ]}, {"name": "getLabels","parameterTypes": [ ]}, {"name": "getTenant","parameterTypes": [ ]}, {"name": "setAbilityTable","parameterTypes": ["java.util.Map"]}, {"name": "setClientVersion","parameterTypes": ["java.lang.String"]}, {"name": "setLabels","parameterTypes": ["java.util.Map"]}, {"name": "setTenant","parameterTypes": ["java.lang.String"]} ] }, { "name":"com.alibaba.nacos.api.remote.request.HealthCheckRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.api.remote.request.InternalRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getModule","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.request.PushAckRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getException","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }, {"name":"isSuccess","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.remote.request.Request", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[ {"name":"getHeaders","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.remote.request.ServerCheckRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.api.remote.request.ServerLoaderInfoRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.api.remote.request.ServerReloadRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getReloadCount","parameterTypes":[] }, {"name":"getReloadServer","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.remote.request.ServerRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.api.remote.request.SetupAckRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getAbilityTable","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.api.remote.response.ClientDetectionResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.response.ConnectResetResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.response.ErrorResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.response.HealthCheckResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.response.Response", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[ {"name":"setErrorCode","parameterTypes":["int"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setResultCode","parameterTypes":["int"] }, {"name":"setMessage","parameterTypes":["java.lang.String"] } ] }, { "name":"com.alibaba.nacos.api.remote.response.ServerCheckResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setConnectionId","parameterTypes":["java.lang.String"] }, {"name":"setSupportAbilityNegotiation","parameterTypes":["boolean"] } ] }, { "name":"com.alibaba.nacos.api.remote.response.ServerLoaderInfoResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setLoaderMetrics","parameterTypes":["java.util.Map"] } ] }, { "name":"com.alibaba.nacos.api.remote.response.ServerReloadResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.response.SetupAckResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.auth.impl.NacosClientAuthServiceImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.auth.ram.RamClientAuthServiceImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.auth.ram.identify.CredentialListener", "methods":[{"name":"onUpdateCredential","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.auth.ram.identify.CredentialWatcher", "methods":[{"name":"stop","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.auth.ram.identify.StsCredentialHolder" }, { "name":"com.alibaba.nacos.client.config.NacosConfigService", "methods":[{"name":"","parameterTypes":["java.util.Properties"] }] }, { "name":"com.alibaba.nacos.client.naming.NacosNamingService", "methods":[{"name":"","parameterTypes":["java.util.Properties"] }] }, { "name":"com.alibaba.nacos.client.config.impl.ClientWorker", "methods":[ {"name":"addTenantListeners","parameterTypes":["java.lang.String","java.lang.String","java.util.List"] }, {"name":"addTenantListenersWithContent","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.util.List"] }, {"name":"getAgent","parameterTypes":[] }, {"name":"getAgentName","parameterTypes":[] }, {"name":"getServerConfig","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","long","boolean"] }, {"name":"isHealthServer","parameterTypes":[] }, {"name":"publishConfig","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfig","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeTenantListener","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.config.listener.Listener"] }, {"name":"shutdown","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.client.config.impl.ClientWorker$ConfigRpcTransportClient", "methods":[{"name":"isHealthServer","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.config.impl.ServerListManager", "methods":[ {"name":"getName","parameterTypes":[] }, {"name":"getNextServerAddr","parameterTypes":[] }, {"name":"getServerUrls","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"start","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.client.logging.AbstractNacosLogging", "methods":[{"name":"loadConfiguration","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.logging.log4j2.Log4J2NacosLogging" }, { "name":"com.alibaba.nacos.client.logging.log4j2.NacosClientPropertiesLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.naming.cache.ServiceInfoHolder", "methods":[ {"name":"getServiceInfoMap","parameterTypes":[] }, {"name":"processServiceInfo","parameterTypes":["com.alibaba.nacos.api.naming.pojo.ServiceInfo"] } ] }, { "name":"com.alibaba.nacos.client.naming.core.ServerListManager", "methods":[{"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.client.naming.event.InstancesChangeEvent", "methods":[ {"name":"getClusters","parameterTypes":[] }, {"name":"getGroupName","parameterTypes":[] }, {"name":"getHosts","parameterTypes":[] }, {"name":"getServiceName","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.client.naming.event.InstancesChangeNotifier", "methods":[ {"name":"deregisterListener","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.listener.EventListener"] }, {"name":"getSubscribeServices","parameterTypes":[] }, {"name":"isSubscribed","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"registerListener","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.listener.EventListener"] } ] }, { "name":"com.alibaba.nacos.client.naming.remote.NamingClientProxy", "methods":[ {"name":"batchDeregisterService","parameterTypes":["java.lang.String","java.lang.String","java.util.List"] }, {"name":"batchRegisterService","parameterTypes":["java.lang.String","java.lang.String","java.util.List"] }, {"name":"createService","parameterTypes":["com.alibaba.nacos.api.naming.pojo.Service","com.alibaba.nacos.api.selector.AbstractSelector"] }, {"name":"deleteService","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"deregisterService","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"getServiceList","parameterTypes":["int","int","java.lang.String","com.alibaba.nacos.api.selector.AbstractSelector"] }, {"name":"isSubscribed","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"queryInstancesOfService","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","boolean"] }, {"name":"queryService","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"registerService","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"serverHealthy","parameterTypes":[] }, {"name":"subscribe","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"unsubscribe","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateInstance","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"updateService","parameterTypes":["com.alibaba.nacos.api.naming.pojo.Service","com.alibaba.nacos.api.selector.AbstractSelector"] } ] }, { "name":"com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy", "methods":[ {"name":"batchRegisterService","parameterTypes":["java.lang.String","java.lang.String","java.util.List"] }, {"name":"deregisterService","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"getServiceList","parameterTypes":["int","int","java.lang.String","com.alibaba.nacos.api.selector.AbstractSelector"] }, {"name":"queryInstancesOfService","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","boolean"] }, {"name":"registerService","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"serverHealthy","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"subscribe","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"unsubscribe","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] } ] }, { "name":"com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy", "methods":[ {"name":"batchDeregisterService","parameterTypes":["java.lang.String","java.lang.String","java.util.List"] }, {"name":"batchRegisterService","parameterTypes":["java.lang.String","java.lang.String","java.util.List"] }, {"name":"createService","parameterTypes":["com.alibaba.nacos.api.naming.pojo.Service","com.alibaba.nacos.api.selector.AbstractSelector"] }, {"name":"deleteService","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"deregisterService","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"getServiceList","parameterTypes":["int","int","java.lang.String","com.alibaba.nacos.api.selector.AbstractSelector"] }, {"name":"queryInstancesOfService","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","boolean"] }, {"name":"queryService","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"registerService","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"serverHealthy","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"subscribe","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"unsubscribe","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateInstance","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"updateService","parameterTypes":["com.alibaba.nacos.api.naming.pojo.Service","com.alibaba.nacos.api.selector.AbstractSelector"] } ] }, { "name":"com.alibaba.nacos.common.http.HttpRestResult", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getHeader","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.http.client.InterceptingHttpClientRequest" }, { "name":"com.alibaba.nacos.common.http.client.NacosRestTemplate", "methods":[{"name":"postForm","parameterTypes":["java.lang.String","com.alibaba.nacos.common.http.param.Header","com.alibaba.nacos.common.http.param.Query","java.util.Map","java.lang.reflect.Type"] }] }, { "name":"com.alibaba.nacos.common.http.client.request.HttpClientRequest", "methods":[{"name":"execute","parameterTypes":["java.net.URI","java.lang.String","com.alibaba.nacos.common.model.RequestHttpEntity"] }] }, { "name":"com.alibaba.nacos.common.lifecycle.Closeable", "methods":[{"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.model.RestResult", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[ {"name":"getCode","parameterTypes":[] }, {"name":"getData","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.common.notify.DefaultPublisher", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.remote.TlsConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[ {"name":"getCertChainFile","parameterTypes":[] }, {"name":"getCertPrivateKey","parameterTypes":[] }, {"name":"getCertPrivateKeyPassword","parameterTypes":[] }, {"name":"getCiphers","parameterTypes":[] }, {"name":"getEnableTls","parameterTypes":[] }, {"name":"getMutualAuthEnable","parameterTypes":[] }, {"name":"getProtocols","parameterTypes":[] }, {"name":"getSslProvider","parameterTypes":[] }, {"name":"getTrustAll","parameterTypes":[] }, {"name":"getTrustCollectionCertFile","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.common.remote.client.RpcClient" }, { "name":"com.alibaba.nacos.common.remote.client.RpcClientTlsConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.core.cluster.Member", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getAbilities","parameterTypes":[] }, {"name":"getAddress","parameterTypes":[] }, {"name":"getExtendInfo","parameterTypes":[] }, {"name":"getFailAccessCnt","parameterTypes":[] }, {"name":"getIp","parameterTypes":[] }, {"name":"getPort","parameterTypes":[] }, {"name":"getState","parameterTypes":[] }, {"name":"isGrpcReportEnabled","parameterTypes":[] }, {"name":"setAbilities","parameterTypes":["com.alibaba.nacos.api.ability.ServerAbilities"] }, {"name":"setAddress","parameterTypes":["java.lang.String"] }, {"name":"setExtendInfo","parameterTypes":["java.util.Map"] }, {"name":"setFailAccessCnt","parameterTypes":["boolean"] }, {"name":"setGrpcReportEnabled","parameterTypes":["boolean"] }, {"name":"setIp","parameterTypes":["java.lang.String"] }, {"name":"setPort","parameterTypes":["int"] }, {"name":"setState","parameterTypes":["com.alibaba.nacos.core.cluster.NodeState"] } ] }, { "name":"com.alibaba.nacos.core.cluster.remote.request.AbstractClusterRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getModule","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.cluster.remote.request.MemberReportRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getNode","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.cluster.remote.response.MemberReportResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setNode","parameterTypes":["com.alibaba.nacos.core.cluster.Member"] } ] }, { "name":"com.alibaba.nacos.core.distributed.distro.entity.DistroData", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getContent","parameterTypes":[] }, {"name":"getDistroKey","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"setContent","parameterTypes":["byte[]"] }, {"name":"setDistroKey","parameterTypes":["com.alibaba.nacos.core.distributed.distro.entity.DistroKey"] }, {"name":"setType","parameterTypes":["com.alibaba.nacos.consistency.DataOperation"] } ] }, { "name":"com.alibaba.nacos.core.distributed.distro.entity.DistroKey", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"getResourceKey","parameterTypes":[] }, {"name":"getResourceType","parameterTypes":[] }, {"name":"getTargetServer","parameterTypes":[] }, {"name":"setResourceKey","parameterTypes":["java.lang.String"] }, {"name":"setResourceType","parameterTypes":["java.lang.String"] }, {"name":"setTargetServer","parameterTypes":["java.lang.String"] } ] }, { "name":"com.alibaba.nacos.naming.cluster.remote.request.DistroDataRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"getDataOperation","parameterTypes":[] }, {"name":"getDistroData","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.naming.cluster.remote.response.DistroDataResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"setDistroData","parameterTypes":["com.alibaba.nacos.core.distributed.distro.entity.DistroData"] } ] }, { "name":"com.fasterxml.jackson.core.JsonParser" }, { "name":"com.fasterxml.jackson.databind.JsonNode" }, { "name":"com.fasterxml.jackson.databind.ObjectMapper" }, { "name":"com.fasterxml.jackson.databind.ext.Java7HandlersImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.google.common.util.concurrent.AbstractFuture", "fields":[ {"name":"listeners"}, {"name":"value"}, {"name":"waiters"} ] }, { "name":"com.google.common.util.concurrent.AbstractFuture$Waiter", "fields":[ {"name":"next"}, {"name":"thread"} ] }, { "name":"com.google.protobuf.ExtensionRegistry", "methods":[{"name":"getEmptyRegistry","parameterTypes":[] }] }, { "name":"com.intellij.junit4.JUnit4IdeaTestRunner", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.intellij.rt.execution.application.AppMainV2$Agent", "methods":[{"name":"premain","parameterTypes":["java.lang.String","java.lang.instrument.Instrumentation"] }] }, { "name":"com.sun.crypto.provider.HmacSHA1", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.jndi.dns.DnsContextFactory" }, { "name":"com.sun.management.GarbageCollectorMXBean", "queryAllPublicMethods":true }, { "name":"com.sun.management.GcInfo", "queryAllPublicMethods":true }, { "name":"com.sun.management.HotSpotDiagnosticMXBean", "queryAllPublicMethods":true }, { "name":"com.sun.management.OperatingSystemMXBean", "queryAllPublicMethods":true }, { "name":"com.sun.management.ThreadMXBean", "queryAllPublicMethods":true }, { "name":"com.sun.management.VMOption", "queryAllPublicMethods":true }, { "name":"com.sun.management.internal.GarbageCollectorExtImpl", "queryAllPublicConstructors":true }, { "name":"com.sun.management.internal.HotSpotDiagnostic", "queryAllPublicConstructors":true }, { "name":"com.sun.management.internal.HotSpotThreadImpl", "queryAllPublicConstructors":true }, { "name":"com.sun.management.internal.OperatingSystemImpl", "queryAllPublicConstructors":true }, { "name":"com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"double", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.shaded.com.google.common.util.concurrent.AbstractFuture", "fields":[ {"name":"listeners"}, {"name":"value"}, {"name":"waiters"} ] }, { "name":"com.alibaba.nacos.shaded.com.google.common.util.concurrent.AbstractFuture$Waiter", "fields":[ {"name":"next"}, {"name":"thread"} ] }, { "name":"com.alibaba.nacos.shaded.com.google.protobuf.Any", "methods":[ {"name":"getTypeUrl","parameterTypes":[] }, {"name":"getTypeUrlBytes","parameterTypes":[] }, {"name":"getValue","parameterTypes":[] }, {"name":"newBuilder","parameterTypes":[] } ] }, { "name":"com.alibaba.nacos.shaded.com.google.protobuf.Any$Builder", "methods":[ {"name":"clearTypeUrl","parameterTypes":[] }, {"name":"clearValue","parameterTypes":[] }, {"name":"getTypeUrl","parameterTypes":[] }, {"name":"getTypeUrlBytes","parameterTypes":[] }, {"name":"getValue","parameterTypes":[] }, {"name":"setTypeUrl","parameterTypes":["java.lang.String"] }, {"name":"setTypeUrlBytes","parameterTypes":["com.alibaba.nacos.shaded.com.google.protobuf.ByteString"] }, {"name":"setValue","parameterTypes":["com.alibaba.nacos.shaded.com.google.protobuf.ByteString"] } ] }, { "name":"com.alibaba.nacos.shaded.com.google.protobuf.ExtensionRegistry", "methods":[{"name":"getEmptyRegistry","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.shaded.io.grpc.internal.DnsNameResolverProvider" }, { "name":"com.alibaba.nacos.shaded.io.grpc.internal.PickFirstLoadBalancerProvider" }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.AbstractNettyHandler", "methods":[ {"name":"channelActive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler", "methods":[ {"name":"channelInactive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"write","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$GrpcNegotiationHandler", "methods":[{"name":"userEventTriggered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$ProtocolNegotiationHandler", "methods":[{"name":"userEventTriggered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$WaitUntilActiveHandler", "methods":[{"name":"channelActive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.grpc.netty.WriteBufferingAndExceptionHandler", "methods":[ {"name":"channelInactive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"close","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.buffer.AbstractReferenceCountedByteBuf", "fields":[{"name":"refCnt"}] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelDuplexHandler", "methods":[ {"name":"bind","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"close","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelInboundHandlerAdapter", "methods":[ {"name":"channelActive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext", "methods":[ {"name":"bind","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"channelActive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"userEventTriggered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"write","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$TailContext", "methods":[ {"name":"channelActive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder", "methods":[ {"name":"channelRead","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"userEventTriggered","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler", "methods":[ {"name":"bind","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"channelReadComplete","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"connect","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] } ] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.ReferenceCountUtil", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields", "fields":[{"name":"producerLimit"}] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields", "fields":[{"name":"consumerIndex"}] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields", "fields":[{"name":"producerIndex"}] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField", "fields":[{"name":"consumerIndex"}] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField", "fields":[{"name":"producerIndex"}] }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField", "fields":[{"name":"producerLimit"}] }, { "name":"com.alibaba.nacos.shaded.io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider" }, { "name":"io.grpc.internal.DnsNameResolverProvider" }, { "name":"io.grpc.internal.JndiResourceResolverFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"io.grpc.internal.PickFirstLoadBalancerProvider" }, { "name":"io.grpc.netty.shaded.io.grpc.netty.AbstractNettyHandler", "methods":[ {"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] } ] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler", "methods":[ {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$GrpcNegotiationHandler", "methods":[{"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$ProtocolNegotiationHandler", "methods":[{"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$WaitUntilActiveHandler", "methods":[{"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.WriteBufferingAndExceptionHandler", "methods":[ {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.channel.epoll.Epoll" }, { "name":"com.alibaba.nacos.shaded.io.grpc.netty.shaded.io.netty.util.AbstractReferenceCounted" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.AbstractReferenceCountedByteBuf", "fields":[{"name":"refCnt"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.ChannelDuplexHandler", "methods":[ {"name":"bind","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"io.grpc.netty.shaded.io.netty.channel.ChannelInboundHandlerAdapter", "methods":[ {"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] } ] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext", "methods":[ {"name":"bind","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] } ] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$TailContext", "methods":[ {"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] } ] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultFileRegion" }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.Epoll", "methods":[ {"name":"isAvailable","parameterTypes":[] }, {"name":"unavailabilityCause","parameterTypes":[] } ] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.NativeDatagramPacketArray$NativeDatagramPacket" }, { "name":"io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.PeerCredentials" }, { "name":"io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder", "methods":[ {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] } ] }, { "name":"io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler", "methods":[ {"name":"bind","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] } ] }, { "name":"io.grpc.netty.shaded.io.netty.util.AbstractReferenceCounted", "fields":[{"name":"refCnt"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.ReferenceCountUtil", "queryAllDeclaredMethods":true }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields", "fields":[{"name":"producerLimit"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields", "fields":[{"name":"consumerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields", "fields":[{"name":"producerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField", "fields":[{"name":"consumerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField", "fields":[{"name":"producerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField", "fields":[{"name":"producerLimit"}] }, { "name":"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider" }, { "name":"java.io.Closeable", "methods":[{"name":"close","parameterTypes":[] }] }, { "name":"java.io.FileDescriptor" }, { "name":"java.io.ObjectInputStream", "queryAllPublicMethods":true }, { "name":"java.io.Serializable", "queryAllDeclaredMethods":true }, { "name":"java.lang.Boolean", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Byte", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Character", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Deprecated", "queryAllPublicMethods":true }, { "name":"java.lang.Double", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Float", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Integer", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Long", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Object", "allDeclaredFields":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"clone","parameterTypes":[] }, {"name":"toString","parameterTypes":[] } ] }, { "name":"java.lang.ProcessHandle", "methods":[ {"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] } ] }, { "name":"java.lang.Short", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.StackTraceElement", "queryAllPublicMethods":true }, { "name":"java.lang.String" }, { "name":"java.lang.Void", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.reflect.Method" }, { "name":"java.math.BigDecimal" }, { "name":"java.math.BigInteger" }, { "name":"java.net.InetSocketAddress", "methods":[{"name":"getHostString","parameterTypes":[] }] }, { "name":"java.nio.Bits", "fields":[{"name":"UNALIGNED"}] }, { "name":"java.nio.Buffer", "fields":[{"name":"address"}] }, { "name":"java.nio.ByteBuffer", "methods":[{"name":"alignedSlice","parameterTypes":["int"] }] }, { "name":"java.nio.DirectByteBuffer", "methods":[{"name":"","parameterTypes":["long","int"] }] }, { "name":"java.nio.channels.FileChannel" }, { "name":"java.nio.channels.spi.SelectorProvider", "methods":[{"name":"openSocketChannel","parameterTypes":["java.net.ProtocolFamily"] }] }, { "name":"java.security.SecureRandomParameters" }, { "name":"java.sql.Date" }, { "name":"java.sql.Timestamp" }, { "name":"java.util.Date" }, { "name":"java.util.PropertyPermission", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"java.util.concurrent.atomic.LongAdder", "queryAllPublicConstructors":true, "methods":[ {"name":"","parameterTypes":[] }, {"name":"add","parameterTypes":["long"] }, {"name":"sum","parameterTypes":[] } ] }, { "name":"java.util.logging.LogManager", "methods":[{"name":"getLoggingMXBean","parameterTypes":[] }] }, { "name":"java.util.logging.LoggingMXBean", "queryAllPublicMethods":true }, { "name":"javax.management.MBeanOperationInfo", "queryAllPublicMethods":true, "methods":[{"name":"getSignature","parameterTypes":[] }] }, { "name":"javax.management.MBeanServerBuilder", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"javax.management.NotificationBroadcasterSupport", "methods":[{"name":"getNotificationInfo","parameterTypes":[] }] }, { "name":"javax.management.ObjectName" }, { "name":"javax.management.openmbean.CompositeData" }, { "name":"javax.management.openmbean.OpenMBeanOperationInfoSupport" }, { "name":"javax.management.openmbean.TabularData" }, { "name":"javax.naming.directory.InitialDirContext" }, { "name":"org.apache.logging.log4j.core.appender.AbstractAppender$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.AppenderSet" }, { "name":"org.apache.logging.log4j.core.appender.AsyncAppender", "queryAllDeclaredMethods":true, "methods":[{"name":"newBuilder","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.appender.AsyncAppender$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.ConsoleAppender" }, { "name":"org.apache.logging.log4j.core.appender.CountingNoOpAppender" }, { "name":"org.apache.logging.log4j.core.appender.FailoverAppender" }, { "name":"org.apache.logging.log4j.core.appender.FailoversPlugin" }, { "name":"org.apache.logging.log4j.core.appender.FileAppender" }, { "name":"org.apache.logging.log4j.core.appender.HttpAppender" }, { "name":"org.apache.logging.log4j.core.appender.MemoryMappedFileAppender" }, { "name":"org.apache.logging.log4j.core.appender.NullAppender" }, { "name":"org.apache.logging.log4j.core.appender.OutputStreamAppender" }, { "name":"org.apache.logging.log4j.core.appender.RandomAccessFileAppender" }, { "name":"org.apache.logging.log4j.core.appender.RollingFileAppender", "queryAllDeclaredMethods":true, "methods":[{"name":"newBuilder","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.appender.RollingFileAppender$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender" }, { "name":"org.apache.logging.log4j.core.appender.ScriptAppenderSelector" }, { "name":"org.apache.logging.log4j.core.appender.SmtpAppender" }, { "name":"org.apache.logging.log4j.core.appender.SocketAppender" }, { "name":"org.apache.logging.log4j.core.appender.SyslogAppender" }, { "name":"org.apache.logging.log4j.core.appender.WriterAppender" }, { "name":"org.apache.logging.log4j.core.appender.db.ColumnMapping" }, { "name":"org.apache.logging.log4j.core.appender.db.jdbc.ColumnConfig" }, { "name":"org.apache.logging.log4j.core.appender.db.jdbc.DataSourceConnectionSource" }, { "name":"org.apache.logging.log4j.core.appender.db.jdbc.DriverManagerConnectionSource" }, { "name":"org.apache.logging.log4j.core.appender.db.jdbc.FactoryMethodConnectionSource" }, { "name":"org.apache.logging.log4j.core.appender.db.jdbc.JdbcAppender" }, { "name":"org.apache.logging.log4j.core.appender.mom.JmsAppender" }, { "name":"org.apache.logging.log4j.core.appender.mom.jeromq.JeroMqAppender" }, { "name":"org.apache.logging.log4j.core.appender.mom.kafka.KafkaAppender" }, { "name":"org.apache.logging.log4j.core.appender.nosql.NoSqlAppender" }, { "name":"org.apache.logging.log4j.core.appender.rewrite.LoggerNameLevelRewritePolicy" }, { "name":"org.apache.logging.log4j.core.appender.rewrite.MapRewritePolicy" }, { "name":"org.apache.logging.log4j.core.appender.rewrite.PropertiesRewritePolicy" }, { "name":"org.apache.logging.log4j.core.appender.rewrite.RewriteAppender" }, { "name":"org.apache.logging.log4j.core.appender.rolling.CompositeTriggeringPolicy", "queryAllDeclaredMethods":true, "methods":[{"name":"createPolicy","parameterTypes":["org.apache.logging.log4j.core.appender.rolling.TriggeringPolicy[]"] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.CronTriggeringPolicy" }, { "name":"org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy", "queryAllDeclaredMethods":true, "methods":[{"name":"newBuilder","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.DefaultRolloverStrategy$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.rolling.DirectWriteRolloverStrategy" }, { "name":"org.apache.logging.log4j.core.appender.rolling.NoOpTriggeringPolicy" }, { "name":"org.apache.logging.log4j.core.appender.rolling.OnStartupTriggeringPolicy" }, { "name":"org.apache.logging.log4j.core.appender.rolling.SizeBasedTriggeringPolicy", "queryAllDeclaredMethods":true, "methods":[{"name":"createPolicy","parameterTypes":["java.lang.String"] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy", "queryAllDeclaredMethods":true, "methods":[{"name":"newBuilder","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.TimeBasedTriggeringPolicy$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.DeleteAction", "queryAllDeclaredMethods":true, "methods":[{"name":"createDeleteAction","parameterTypes":["java.lang.String","boolean","int","boolean","org.apache.logging.log4j.core.appender.rolling.action.PathSorter","org.apache.logging.log4j.core.appender.rolling.action.PathCondition[]","org.apache.logging.log4j.core.appender.rolling.action.ScriptCondition","org.apache.logging.log4j.core.config.Configuration"] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAccumulatedFileCount" }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAccumulatedFileSize" }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAll" }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.IfAny" }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.IfFileName", "queryAllDeclaredMethods":true, "methods":[{"name":"createNameCondition","parameterTypes":["java.lang.String","java.lang.String","org.apache.logging.log4j.core.appender.rolling.action.PathCondition[]"] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.IfLastModified", "queryAllDeclaredMethods":true, "methods":[{"name":"createAgeCondition","parameterTypes":["org.apache.logging.log4j.core.appender.rolling.action.Duration","org.apache.logging.log4j.core.appender.rolling.action.PathCondition[]"] }] }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.IfNot" }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.PathSortByModificationTime" }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.PosixViewAttributeAction" }, { "name":"org.apache.logging.log4j.core.appender.rolling.action.ScriptCondition" }, { "name":"org.apache.logging.log4j.core.appender.routing.IdlePurgePolicy" }, { "name":"org.apache.logging.log4j.core.appender.routing.Route" }, { "name":"org.apache.logging.log4j.core.appender.routing.Routes" }, { "name":"org.apache.logging.log4j.core.appender.routing.RoutingAppender" }, { "name":"org.apache.logging.log4j.core.async.ArrayBlockingQueueFactory" }, { "name":"org.apache.logging.log4j.core.async.AsyncLoggerConfig" }, { "name":"org.apache.logging.log4j.core.async.AsyncLoggerConfig$RootLogger" }, { "name":"org.apache.logging.log4j.core.async.DisruptorBlockingQueueFactory" }, { "name":"org.apache.logging.log4j.core.async.JCToolsBlockingQueueFactory" }, { "name":"org.apache.logging.log4j.core.async.LinkedTransferQueueFactory" }, { "name":"org.apache.logging.log4j.core.config.AppenderRef", "queryAllDeclaredMethods":true, "methods":[{"name":"createAppenderRef","parameterTypes":["java.lang.String","org.apache.logging.log4j.Level","org.apache.logging.log4j.core.Filter"] }] }, { "name":"org.apache.logging.log4j.core.config.AppendersPlugin", "queryAllDeclaredMethods":true, "methods":[{"name":"createAppenders","parameterTypes":["org.apache.logging.log4j.core.Appender[]"] }] }, { "name":"org.apache.logging.log4j.core.config.CustomLevelConfig" }, { "name":"org.apache.logging.log4j.core.config.CustomLevels" }, { "name":"org.apache.logging.log4j.core.config.DefaultAdvertiser" }, { "name":"org.apache.logging.log4j.core.config.HttpWatcher" }, { "name":"org.apache.logging.log4j.core.config.LoggerConfig", "queryAllDeclaredMethods":true, "methods":[{"name":"createLogger","parameterTypes":["boolean","org.apache.logging.log4j.Level","java.lang.String","java.lang.String","org.apache.logging.log4j.core.config.AppenderRef[]","org.apache.logging.log4j.core.config.Property[]","org.apache.logging.log4j.core.config.Configuration","org.apache.logging.log4j.core.Filter"] }] }, { "name":"org.apache.logging.log4j.core.config.LoggerConfig$RootLogger" }, { "name":"org.apache.logging.log4j.core.config.LoggersPlugin", "queryAllDeclaredMethods":true, "methods":[{"name":"createLoggers","parameterTypes":["org.apache.logging.log4j.core.config.LoggerConfig[]"] }] }, { "name":"org.apache.logging.log4j.core.config.PropertiesPlugin", "queryAllDeclaredMethods":true, "methods":[{"name":"configureSubstitutor","parameterTypes":["org.apache.logging.log4j.core.config.Property[]","org.apache.logging.log4j.core.config.Configuration"] }] }, { "name":"org.apache.logging.log4j.core.config.Property", "queryAllDeclaredMethods":true, "methods":[{"name":"createProperty","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"org.apache.logging.log4j.core.config.ScriptsPlugin" }, { "name":"org.apache.logging.log4j.core.config.arbiters.ClassArbiter" }, { "name":"org.apache.logging.log4j.core.config.arbiters.DefaultArbiter" }, { "name":"org.apache.logging.log4j.core.config.arbiters.ScriptArbiter" }, { "name":"org.apache.logging.log4j.core.config.arbiters.SelectArbiter" }, { "name":"org.apache.logging.log4j.core.config.arbiters.SystemPropertyArbiter" }, { "name":"org.apache.logging.log4j.core.config.json.JsonConfigurationFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigDecimalConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BigIntegerConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$BooleanConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteArrayConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ByteConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharArrayConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharacterConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CharsetConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ClassConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$CronExpressionConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DoubleConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$DurationConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FileConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$FloatConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$InetAddressConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$IntegerConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LevelConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$LongConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PathConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$PatternConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$SecurityProviderConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$ShortConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$StringConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UriConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UrlConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.convert.TypeConverters$UuidConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.validation.validators.RequiredValidator", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginAttributeVisitor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginBuilderAttributeVisitor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginConfigurationVisitor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginElementVisitor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.plugins.visitors.PluginValueVisitor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.properties.PropertiesConfigurationFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.xml.XmlConfigurationFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.config.yaml.YamlConfigurationFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.filter.AbstractFilterable$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.filter.BurstFilter" }, { "name":"org.apache.logging.log4j.core.filter.CompositeFilter" }, { "name":"org.apache.logging.log4j.core.filter.DenyAllFilter" }, { "name":"org.apache.logging.log4j.core.filter.DynamicThresholdFilter" }, { "name":"org.apache.logging.log4j.core.filter.LevelMatchFilter" }, { "name":"org.apache.logging.log4j.core.filter.LevelRangeFilter" }, { "name":"org.apache.logging.log4j.core.filter.MapFilter" }, { "name":"org.apache.logging.log4j.core.filter.MarkerFilter" }, { "name":"org.apache.logging.log4j.core.filter.NoMarkerFilter" }, { "name":"org.apache.logging.log4j.core.filter.RegexFilter" }, { "name":"org.apache.logging.log4j.core.filter.ScriptFilter" }, { "name":"org.apache.logging.log4j.core.filter.StringMatchFilter" }, { "name":"org.apache.logging.log4j.core.filter.StructuredDataFilter" }, { "name":"org.apache.logging.log4j.core.filter.ThreadContextMapFilter" }, { "name":"org.apache.logging.log4j.core.filter.ThresholdFilter" }, { "name":"org.apache.logging.log4j.core.filter.TimeFilter" }, { "name":"org.apache.logging.log4j.core.impl.Log4jContextFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.jmx.AppenderAdmin", "queryAllPublicConstructors":true }, { "name":"org.apache.logging.log4j.core.jmx.AppenderAdminMBean", "queryAllPublicMethods":true }, { "name":"org.apache.logging.log4j.core.jmx.ContextSelectorAdmin", "queryAllPublicConstructors":true }, { "name":"org.apache.logging.log4j.core.jmx.ContextSelectorAdminMBean", "queryAllPublicMethods":true }, { "name":"org.apache.logging.log4j.core.jmx.LoggerContextAdmin", "queryAllPublicConstructors":true }, { "name":"org.apache.logging.log4j.core.jmx.LoggerContextAdminMBean", "queryAllPublicMethods":true }, { "name":"org.apache.logging.log4j.core.jmx.StatusLoggerAdmin", "queryAllPublicConstructors":true }, { "name":"org.apache.logging.log4j.core.jmx.StatusLoggerAdminMBean", "queryAllPublicMethods":true }, { "name":"org.apache.logging.log4j.core.layout.CsvLogEventLayout" }, { "name":"org.apache.logging.log4j.core.layout.CsvParameterLayout" }, { "name":"org.apache.logging.log4j.core.layout.GelfLayout" }, { "name":"org.apache.logging.log4j.core.layout.HtmlLayout" }, { "name":"org.apache.logging.log4j.core.layout.JsonLayout" }, { "name":"org.apache.logging.log4j.core.layout.LevelPatternSelector" }, { "name":"org.apache.logging.log4j.core.layout.LoggerFields" }, { "name":"org.apache.logging.log4j.core.layout.MarkerPatternSelector" }, { "name":"org.apache.logging.log4j.core.layout.MessageLayout" }, { "name":"org.apache.logging.log4j.core.layout.PatternLayout", "queryAllDeclaredMethods":true, "methods":[{"name":"newBuilder","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.layout.PatternLayout$Builder", "allDeclaredFields":true }, { "name":"org.apache.logging.log4j.core.layout.PatternMatch" }, { "name":"org.apache.logging.log4j.core.layout.Rfc5424Layout" }, { "name":"org.apache.logging.log4j.core.layout.ScriptPatternSelector" }, { "name":"org.apache.logging.log4j.core.layout.SerializedLayout" }, { "name":"org.apache.logging.log4j.core.layout.SyslogLayout" }, { "name":"org.apache.logging.log4j.core.layout.XmlLayout" }, { "name":"org.apache.logging.log4j.core.layout.YamlLayout" }, { "name":"org.apache.logging.log4j.core.lookup.ContextMapLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.DateLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.EnvironmentLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.EventLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.JavaLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.JmxRuntimeInputArgumentsLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.JndiLookup" }, { "name":"org.apache.logging.log4j.core.lookup.Log4jLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.LowerLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.MainMapLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.MapLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.MarkerLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.ResourceBundleLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.StructuredDataLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.SystemPropertiesLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.lookup.UpperLookup", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.core.net.MulticastDnsAdvertiser" }, { "name":"org.apache.logging.log4j.core.net.SocketAddress" }, { "name":"org.apache.logging.log4j.core.net.SocketOptions" }, { "name":"org.apache.logging.log4j.core.net.SocketPerformancePreferences" }, { "name":"org.apache.logging.log4j.core.net.ssl.KeyStoreConfiguration" }, { "name":"org.apache.logging.log4j.core.net.ssl.SslConfiguration" }, { "name":"org.apache.logging.log4j.core.net.ssl.TrustStoreConfiguration" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Black" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Blue" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Cyan" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Green" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Magenta" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Red" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$White" }, { "name":"org.apache.logging.log4j.core.pattern.AbstractStyleNameConverter$Yellow" }, { "name":"org.apache.logging.log4j.core.pattern.ClassNamePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.DatePatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.EncodingPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.EndOfBatchPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.EqualsIgnoreCaseReplacementConverter" }, { "name":"org.apache.logging.log4j.core.pattern.EqualsReplacementConverter" }, { "name":"org.apache.logging.log4j.core.pattern.ExtendedThrowablePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.FileDatePatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.FileLocationPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.FullLocationPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.HighlightConverter" }, { "name":"org.apache.logging.log4j.core.pattern.IntegerPatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.LevelPatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.LineLocationPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.LineSeparatorPatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.LoggerFqcnPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.LoggerPatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.MapPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.MarkerPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.MarkerSimpleNamePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.MaxLengthConverter" }, { "name":"org.apache.logging.log4j.core.pattern.MdcPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.MessagePatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["org.apache.logging.log4j.core.config.Configuration","java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.MethodLocationPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.NanoTimePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.NdcPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.ProcessIdPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.RegexReplacement" }, { "name":"org.apache.logging.log4j.core.pattern.RegexReplacementConverter" }, { "name":"org.apache.logging.log4j.core.pattern.RelativeTimePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.RepeatPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.RootThrowablePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.SequenceNumberPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.StyleConverter" }, { "name":"org.apache.logging.log4j.core.pattern.ThreadIdPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.ThreadNamePatternConverter", "queryAllDeclaredMethods":true, "methods":[{"name":"newInstance","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.apache.logging.log4j.core.pattern.ThreadPriorityPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.ThrowablePatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.UuidPatternConverter" }, { "name":"org.apache.logging.log4j.core.pattern.VariablesNotEmptyReplacementConverter" }, { "name":"org.apache.logging.log4j.core.script.Script" }, { "name":"org.apache.logging.log4j.core.script.ScriptFile" }, { "name":"org.apache.logging.log4j.core.script.ScriptRef" }, { "name":"org.apache.logging.log4j.core.util.ExecutorServices" }, { "name":"org.apache.logging.log4j.core.util.KeyValuePair" }, { "name":"org.apache.logging.log4j.message.DefaultFlowMessageFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.message.ReusableMessageFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.logging.log4j.util.internal.DefaultObjectInputFilter", "queryAllPublicMethods":true }, { "name":"sun.management.ClassLoadingImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.CompilationImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.ManagementFactoryHelper$1", "queryAllPublicConstructors":true }, { "name":"sun.management.ManagementFactoryHelper$PlatformLoggingImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.MemoryImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.MemoryManagerImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.MemoryPoolImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.RuntimeImpl", "queryAllPublicConstructors":true }, { "name":"sun.misc.Unsafe", "allDeclaredFields":true, "methods":[ {"name":"arrayBaseOffset","parameterTypes":["java.lang.Class"] }, {"name":"arrayIndexScale","parameterTypes":["java.lang.Class"] }, {"name":"copyMemory","parameterTypes":["long","long","long"] }, {"name":"copyMemory","parameterTypes":["java.lang.Object","long","java.lang.Object","long","long"] }, {"name":"getAndAddLong","parameterTypes":["java.lang.Object","long","long"] }, {"name":"getAndSetObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] }, {"name":"getBoolean","parameterTypes":["java.lang.Object","long"] }, {"name":"getByte","parameterTypes":["long"] }, {"name":"getByte","parameterTypes":["java.lang.Object","long"] }, {"name":"getDouble","parameterTypes":["java.lang.Object","long"] }, {"name":"getFloat","parameterTypes":["java.lang.Object","long"] }, {"name":"getInt","parameterTypes":["long"] }, {"name":"getInt","parameterTypes":["java.lang.Object","long"] }, {"name":"getLong","parameterTypes":["long"] }, {"name":"getLong","parameterTypes":["java.lang.Object","long"] }, {"name":"getObject","parameterTypes":["java.lang.Object","long"] }, {"name":"invokeCleaner","parameterTypes":["java.nio.ByteBuffer"] }, {"name":"objectFieldOffset","parameterTypes":["java.lang.reflect.Field"] }, {"name":"putBoolean","parameterTypes":["java.lang.Object","long","boolean"] }, {"name":"putByte","parameterTypes":["long","byte"] }, {"name":"putByte","parameterTypes":["java.lang.Object","long","byte"] }, {"name":"putDouble","parameterTypes":["java.lang.Object","long","double"] }, {"name":"putFloat","parameterTypes":["java.lang.Object","long","float"] }, {"name":"putInt","parameterTypes":["long","int"] }, {"name":"putInt","parameterTypes":["java.lang.Object","long","int"] }, {"name":"putLong","parameterTypes":["long","long"] }, {"name":"putLong","parameterTypes":["java.lang.Object","long","long"] }, {"name":"putObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] }, {"name":"storeFence","parameterTypes":[] } ] }, { "name":"sun.nio.ch.SelectorImpl", "fields":[ {"name":"publicSelectedKeys"}, {"name":"selectedKeys"} ] }, { "name":"sun.reflect.ReflectionFactory", "methods":[ {"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] } ] }, { "name":"sun.security.provider.DRBG", "methods":[{"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] }, { "name":"sun.security.provider.MD5", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.SHA", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.SHA2$SHA256", "methods":[{"name":"","parameterTypes":[] }] } ] ================================================ FILE: client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/resource-config.json ================================================ { "resources":{ "includes":[ { "pattern":"\\QMETA-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.api.config.filter.IConfigFilter\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.api.remote.Payload\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService\\E" }, { "pattern":"\\QMETA-INF/services/com.oracle.truffle.api.TruffleLanguage$Provider\\E" }, { "pattern":"\\QMETA-INF/services/com.oracle.truffle.api.instrumentation.TruffleInstrument$Provider\\E" }, { "pattern":"\\QMETA-INF/services/io.grpc.LoadBalancerProvider\\E" }, { "pattern":"\\QMETA-INF/services/io.grpc.ManagedChannelProvider\\E" }, { "pattern":"\\QMETA-INF/services/io.grpc.NameResolverProvider\\E" }, { "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.core.util.ContextDataProvider\\E" }, { "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.spi.Provider\\E" }, { "pattern":"\\QMETA-INF/services/org.apache.logging.log4j.util.PropertySource\\E" }, { "pattern":"\\Q\\E" }, { "pattern":"\\Qcom/alibaba/nacos/client/logging/log4j2\\E" }, { "pattern":"\\Qcom/oracle/truffle/nfi/backend/libffi/LibFFILanguage.class\\E" }, { "pattern":"\\Qmockito-extensions/org.mockito.plugins.MemberAccessor\\E" }, { "pattern":"\\Qmockito-extensions/org.mockito.plugins.MockMaker\\E" }, { "pattern":"\\Qnacos-log4j2.xml\\E" }, { "pattern":"\\Qnacos-logback.xml\\E" }, { "pattern":"\\Qnacos-version.txt\\E" }, { "pattern":"\\Qorg/mockito/internal/creation/bytebuddy/MockMethodAdvice$ForEquals.class\\E" }, { "pattern":"\\Qorg/mockito/internal/creation/bytebuddy/MockMethodAdvice$ForHashCode.class\\E" }, { "pattern":"\\Qorg/mockito/internal/creation/bytebuddy/MockMethodAdvice$ForStatic.class\\E" }, { "pattern":"\\Qorg/mockito/internal/creation/bytebuddy/MockMethodAdvice.class\\E" }, { "pattern":"\\Qorg/mockito/internal/creation/bytebuddy/inject/MockMethodDispatcher.raw\\E" }, { "pattern":"\\Qorg/slf4j/impl/StaticLoggerBinder.class\\E" } ]}, "bundles":[] } ================================================ FILE: client/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-client/serialization-config.json ================================================ { "types":[ { "name":"com.alibaba.nacos.client.naming.event.InstancesChangeEvent", "customTargetConstructorClass":"java.lang.Object" } ], "lambdaCapturingTypes":[ ], "proxies":[ ] } ================================================ FILE: client/src/main/resources/META-INF/services/com.alibaba.nacos.api.config.filter.IConfigFilter ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.client.config.filter.impl.ConfigEncryptionFilter ================================================ FILE: client/src/main/resources/META-INF/services/com.alibaba.nacos.client.naming.backups.FailoverDataSource ================================================ # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.client.naming.backups.datasource.DiskFailoverDataSource ================================================ FILE: client/src/main/resources/META-INF/services/com.alibaba.nacos.common.ability.AbstractAbilityControlManager ================================================ # # Copyright 1999-2022 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.client.ability.ClientAbilityControlManager ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ability/AbilityTest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ability; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.RequestFuture; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientConfig; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class AbilityTest { private RpcClient rpcClient; private Connection connection; @Test void testReceive() throws Exception { rpcClient = new RpcClient(new RpcClientConfig() { @Override public String name() { return "test"; } @Override public int retryTimes() { return 1; } @Override public long timeOutMills() { return 3000L; } @Override public long connectionKeepAlive() { return 5000L; } @Override public int healthCheckRetryTimes() { return 1; } @Override public long healthCheckTimeOut() { return 3000L; } @Override public Map labels() { return new HashMap<>(); } }) { @Override public ConnectionType getConnectionType() { return null; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) throws Exception { connection = new Connection(new RpcClient.ServerInfo()) { { super.abilityTable = new HashMap<>(); super.abilityTable.put(AbilityKey.SERVER_FUZZY_WATCH.getName(), true); super.abilityTable.put(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName(), false); } @Override public Response request(Request request, long timeoutMills) throws NacosException { return null; } @Override public RequestFuture requestFuture(Request request) throws NacosException { return null; } @Override public void asyncRequest(Request request, RequestCallBack requestCallBack) throws NacosException { } @Override public void close() { } }; ; return connection; } }; rpcClient.start(); // test not ready assertNull(rpcClient.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); // test ready rpcClient.serverListFactory(new ServerListFactory() { @Override public String genNextServer() { return "localhost:8848"; } @Override public String getCurrentServer() { return "localhost:8848"; } @Override public List getServerList() { return null; } }); rpcClient.start(); // if connect successfully assertEquals(AbilityStatus.SUPPORTED, rpcClient.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); assertEquals(AbilityStatus.NOT_SUPPORTED, rpcClient.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK)); } @AfterEach void testServerRequestAbility() { //test support ServerRequestHandler serverRequestHandler = (request, connection) -> { assertEquals(AbilityStatus.SUPPORTED, connection.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); assertEquals(AbilityStatus.NOT_SUPPORTED, connection.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK)); return new Response() { }; }; serverRequestHandler.requestReply(null, connection); // test no ability table serverRequestHandler = (request, connection) -> { assertEquals(AbilityStatus.UNKNOWN, connection.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)); return new Response() { }; }; serverRequestHandler.requestReply(null, new TestConnection(new RpcClient.ServerInfo())); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ability/ClientAbilityControlManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ability; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientAbilityControlManagerTest { ClientAbilityControlManager clientAbilityControlManager; @BeforeEach void setUp() { clientAbilityControlManager = new ClientAbilityControlManager(); } @Test void testInitCurrentNodeAbilities() { Map> actual = clientAbilityControlManager.initCurrentNodeAbilities(); assertEquals(1, actual.size()); assertTrue(actual.containsKey(AbilityMode.SDK_CLIENT)); // Current not define sdk ability. assertEquals(4, actual.get(AbilityMode.SDK_CLIENT).size()); } @Test void testGetPriority() { assertEquals(0, clientAbilityControlManager.getPriority()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/NacosAiServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.ai.listener.AbstractNacosMcpServerListener; import com.alibaba.nacos.api.ai.listener.NacosMcpServerEvent; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.client.ai.cache.NacosAgentCardCacheHolder; import com.alibaba.nacos.client.ai.cache.NacosMcpServerCacheHolder; import com.alibaba.nacos.client.ai.event.AiChangeNotifier; import com.alibaba.nacos.client.ai.event.McpServerListenerInvoker; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Properties; import java.util.UUID; 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.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NacosAiServiceTest { @Mock private AiGrpcClient grpcClient; @Mock private NacosMcpServerCacheHolder mcpServerCacheHolder; @Mock private NacosAgentCardCacheHolder agentCardCacheHolder; @Mock private AiChangeNotifier aiChangeNotifier; NacosAiService nacosAiService; @BeforeEach void setUp() throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1"); nacosAiService = new NacosAiService(properties); } @AfterEach void tearDown() throws NacosException { if (null != nacosAiService) { nacosAiService.shutdown(); } } @Test void testConstructorWithNamespace() throws NoSuchFieldException, IllegalAccessException, NacosException { Field field = NacosAiService.class.getDeclaredField("namespaceId"); field.setAccessible(true); assertEquals(Constants.DEFAULT_NAMESPACE_ID, field.get(nacosAiService)); NacosAiService aiService = null; try { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1"); properties.put(PropertyKeyConst.NAMESPACE, "test"); aiService = new NacosAiService(properties); assertEquals("test", field.get(aiService)); } finally { if (null != aiService) { aiService.shutdown(); } } } @Test void getMcpServer() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); when(grpcClient.queryMcpServer("testMcpName", "1.0.0")).thenReturn(new McpServerDetailInfo()); assertNotNull(nacosAiService.getMcpServer("testMcpName", "1.0.0")); } @Test void getMcpServerWithInvalidMcpName() throws NoSuchFieldException, IllegalAccessException, NacosException { assertThrows(NacosApiException.class, () -> nacosAiService.getMcpServer("", "1.0.0")); } @Test void releaseMcpServer() throws NacosException, NoSuchFieldException, IllegalAccessException { injectMocks(); McpServerBasicInfo serverSpecification = new McpServerBasicInfo(); serverSpecification.setName("testMcpName"); serverSpecification.setVersionDetail(new ServerVersionDetail()); serverSpecification.getVersionDetail().setVersion("1.0.0"); String id = UUID.randomUUID().toString(); when(grpcClient.releaseMcpServer(serverSpecification, null, null)).thenReturn(id); assertEquals(id, nacosAiService.releaseMcpServer(serverSpecification, null)); } @Test void releaseMcpServerWithInvalidParameters() throws NacosException { assertThrows(NacosApiException.class, () -> nacosAiService.releaseMcpServer(null, null)); McpServerBasicInfo serverSpecification = new McpServerBasicInfo(); assertThrows(NacosApiException.class, () -> nacosAiService.releaseMcpServer(serverSpecification, null)); serverSpecification.setName("testMcpName"); assertThrows(NacosApiException.class, () -> nacosAiService.releaseMcpServer(serverSpecification, null)); serverSpecification.setVersionDetail(new ServerVersionDetail()); assertThrows(NacosApiException.class, () -> nacosAiService.releaseMcpServer(serverSpecification, null)); } @Test void registerMcpServerEndpoint() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); nacosAiService.registerMcpServerEndpoint("testMcpName", "1.1.1.1", 8848, "1.0.0"); verify(grpcClient).registerMcpServerEndpoint("testMcpName", "1.1.1.1", 8848, "1.0.0"); } @Test void registerMcpServerEndpointWithInvalidParameters() { assertThrows(NacosApiException.class, () -> nacosAiService.registerMcpServerEndpoint("", null, -1, "1.0.0")); assertThrows(NacosApiException.class, () -> nacosAiService.registerMcpServerEndpoint("testMcpName", null, -1, "1.0.0")); assertThrows(NacosApiException.class, () -> nacosAiService.registerMcpServerEndpoint("testMcpName", "1.1.1.1", -1, "1.0.0")); } @Test void deregisterMcpServerEndpoint() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); nacosAiService.deregisterMcpServerEndpoint("testMcpName", "1.1.1.1", 8848); verify(grpcClient).deregisterMcpServerEndpoint("testMcpName", "1.1.1.1", 8848); } @Test void deregisterMcpServerEndpointWithInvalidParameters() { assertThrows(NacosApiException.class, () -> nacosAiService.deregisterMcpServerEndpoint("", null, -1)); assertThrows(NacosApiException.class, () -> nacosAiService.deregisterMcpServerEndpoint("testMcpName", null, -1)); assertThrows(NacosApiException.class, () -> nacosAiService.deregisterMcpServerEndpoint("testMcpName", "1.1.1.1", -1)); } @Test void subscribeMcpServer() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); AbstractNacosMcpServerListener listener = Mockito.mock(AbstractNacosMcpServerListener.class); McpServerDetailInfo expected = new McpServerDetailInfo(); when(grpcClient.subscribeMcpServer("testMcpName", null)).thenReturn(expected); McpServerDetailInfo actual = nacosAiService.subscribeMcpServer("testMcpName", listener); assertEquals(expected, actual); verify(aiChangeNotifier).registerListener(eq("testMcpName"), isNull(), any(McpServerListenerInvoker.class)); verify(listener).onEvent(any(NacosMcpServerEvent.class)); } @Test void subscribeMcpServerWithInvalidParameters() { assertThrows(NacosApiException.class, () -> nacosAiService.subscribeMcpServer("", null)); assertThrows(NacosApiException.class, () -> nacosAiService.subscribeMcpServer("testMcpName", null)); } @Test void unsubscribeMcpServer() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); AbstractNacosMcpServerListener listener = Mockito.mock(AbstractNacosMcpServerListener.class); nacosAiService.unsubscribeMcpServer("testMcpName", listener); verify(aiChangeNotifier).deregisterListener(eq("testMcpName"), isNull(), any(McpServerListenerInvoker.class)); verify(grpcClient).unsubscribeMcpServer("testMcpName", null); } @Test void unsubscribeMcpServerWithOtherListener() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); when(aiChangeNotifier.isMcpServerSubscribed("testMcpName", null)).thenReturn(true); AbstractNacosMcpServerListener listener = Mockito.mock(AbstractNacosMcpServerListener.class); nacosAiService.unsubscribeMcpServer("testMcpName", listener); verify(aiChangeNotifier).deregisterListener(eq("testMcpName"), isNull(), any(McpServerListenerInvoker.class)); verify(grpcClient, never()).unsubscribeMcpServer("testMcpName", null); } @Test void unsubscribeMcpServerWithNullListener() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); nacosAiService.unsubscribeMcpServer("testMcpName", null); verify(aiChangeNotifier, never()).deregisterListener(eq("testMcpName"), isNull(), any(McpServerListenerInvoker.class)); verify(grpcClient, never()).unsubscribeMcpServer("testMcpName", null); } @Test void unsubscribeMcpServerWithInvalidParameters() { assertThrows(NacosApiException.class, () -> nacosAiService.unsubscribeMcpServer("", null)); } @Test void registerAgentEndpointWithCollection() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMocks(); Collection endpoints = createTestEndpoints(); nacosAiService.registerAgentEndpoint("testAgent", endpoints); verify(grpcClient).registerAgentEndpoints("testAgent", endpoints); } @Test void registerAgentEndpointWithCollectionInvalidAgentName() { Collection endpoints = createTestEndpoints(); assertThrows(NacosApiException.class, () -> nacosAiService.registerAgentEndpoint("", endpoints)); } @Test void registerAgentEndpointWithCollectionNullEndpoints() { assertThrows(NacosApiException.class, () -> nacosAiService.registerAgentEndpoint("testAgent", (Collection) null)); } @Test void registerAgentEndpointWithCollectionEmptyEndpoints() { assertThrows(NacosApiException.class, () -> nacosAiService.registerAgentEndpoint("testAgent", new ArrayList<>())); } @Test void registerAgentEndpointWithCollectionNullEndpointInList() { Collection endpoints = Arrays.asList(new AgentEndpoint(), null); assertThrows(NacosApiException.class, () -> nacosAiService.registerAgentEndpoint("testAgent", endpoints)); } @Test void registerAgentEndpointWithCollectionEndpointWithoutVersion() { AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("1.1.1.1"); endpoint.setPort(8080); // No version set Collection endpoints = Arrays.asList(endpoint); assertThrows(NacosApiException.class, () -> nacosAiService.registerAgentEndpoint("testAgent", endpoints)); } @Test void registerAgentEndpointWithCollectionDifferentVersions() { AgentEndpoint endpoint1 = new AgentEndpoint(); endpoint1.setAddress("1.1.1.1"); endpoint1.setPort(8080); endpoint1.setVersion("1.0.0"); AgentEndpoint endpoint2 = new AgentEndpoint(); endpoint2.setAddress("2.2.2.2"); endpoint2.setPort(9090); endpoint2.setVersion("2.0.0"); Collection endpoints = Arrays.asList(endpoint1, endpoint2); assertThrows(NacosApiException.class, () -> nacosAiService.registerAgentEndpoint("testAgent", endpoints)); } private void injectMocks() throws NoSuchFieldException, IllegalAccessException { Field field = NacosAiService.class.getDeclaredField("grpcClient"); field.setAccessible(true); final AiGrpcClient autoBuildGrpcClient = (AiGrpcClient) field.get(nacosAiService); field.set(nacosAiService, grpcClient); field = NacosAiService.class.getDeclaredField("aiClientProxy"); field.setAccessible(true); field.set(nacosAiService, grpcClient); field = NacosAiService.class.getDeclaredField("mcpServerCacheHolder"); field.setAccessible(true); NacosMcpServerCacheHolder autoBuildCacheHolder = (NacosMcpServerCacheHolder) field.get(nacosAiService); field.set(nacosAiService, mcpServerCacheHolder); field = NacosAiService.class.getDeclaredField("agentCardCacheHolder"); field.setAccessible(true); NacosAgentCardCacheHolder autoBuildAgentCacheHolder = (NacosAgentCardCacheHolder) field.get(nacosAiService); field.set(nacosAiService, agentCardCacheHolder); field = NacosAiService.class.getDeclaredField("aiChangeNotifier"); field.setAccessible(true); field.set(nacosAiService, aiChangeNotifier); try { autoBuildGrpcClient.shutdown(); autoBuildCacheHolder.shutdown(); autoBuildAgentCacheHolder.shutdown(); } catch (NacosException ignored) { } } private Collection createTestEndpoints() { AgentEndpoint endpoint1 = new AgentEndpoint(); endpoint1.setAddress("1.1.1.1"); endpoint1.setPort(8080); endpoint1.setVersion("1.0.0"); AgentEndpoint endpoint2 = new AgentEndpoint(); endpoint2.setAddress("2.2.2.2"); endpoint2.setPort(9090); endpoint2.setVersion("1.0.0"); return Arrays.asList(endpoint1, endpoint2); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/cache/NacosMcpServerCacheHolderTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.cache; import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.AfterEach; 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.fail; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.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; import org.mockito.junit.jupiter.MockitoExtension; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.registry.Repository; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.event.McpServerChangedEvent; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; @ExtendWith(MockitoExtension.class) class NacosMcpServerCacheHolderTest { @Mock private AiGrpcClient aiGrpcClient; NacosMcpServerCacheHolder cacheHolder; @BeforeEach void setUp() { Properties properties = new Properties(); properties.put(AiConstants.AI_MCP_SERVER_CACHE_UPDATE_INTERVAL, "100"); cacheHolder = new NacosMcpServerCacheHolder(aiGrpcClient, NacosClientProperties.PROTOTYPE.derive(properties)); } @AfterEach void tearDown() throws NacosException { cacheHolder.shutdown(); NotifyCenter.deregisterPublisher(McpServerChangedEvent.class); } @Test() void processMcpServerDetailInfo() throws InterruptedException { assertNull(cacheHolder.getMcpServer("test", "1.0.0")); MockEventSubscriber subscriber = new MockEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(true); cacheHolder.processMcpServerDetailInfo(mcpServerDetailInfo); assertNotNull(cacheHolder.getMcpServer("test", "1.0.0")); assertEquals(mcpServerDetailInfo, cacheHolder.getMcpServer("test", "1.0.0")); int retry = 0; while (retry < 3) { TimeUnit.MILLISECONDS.sleep(500); if (subscriber.invokedMark.get()) { return; } retry++; } fail("Subscriber for McpServerChangedEvent don't be invoked."); } @Test() void processMcpServerDetailInfoLatest() throws InterruptedException { assertNull(cacheHolder.getMcpServer("test", "1.0.0")); MockEventSubscriber subscriber = new MockEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(true); cacheHolder.processMcpServerDetailInfo(mcpServerDetailInfo); assertNotNull(cacheHolder.getMcpServer("test", "1.0.0")); assertNotNull(cacheHolder.getMcpServer("test", null)); assertEquals(mcpServerDetailInfo, cacheHolder.getMcpServer("test", "1.0.0")); int retry = 0; while (retry < 3) { TimeUnit.MILLISECONDS.sleep(500); if (subscriber.invokedMark.get()) { return; } retry++; } fail("Subscriber for McpServerChangedEvent don't be invoked."); } @Test() void processMcpServerDetailInfoDiff() throws InterruptedException { McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(true); cacheHolder.processMcpServerDetailInfo(mcpServerDetailInfo); mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(true); mcpServerDetailInfo.setProtocol(AiConstants.Mcp.MCP_PROTOCOL_STDIO); MockEventSubscriber subscriber = new MockEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); cacheHolder.processMcpServerDetailInfo(mcpServerDetailInfo); assertEquals(mcpServerDetailInfo, cacheHolder.getMcpServer("test", "1.0.0")); int retry = 0; while (retry < 3) { TimeUnit.MILLISECONDS.sleep(500); if (subscriber.invokedMark.get()) { return; } retry++; } fail("Subscriber for McpServerChangedEvent don't be invoked."); } @Test() void processMcpServerDetailInfoNoDiff() throws InterruptedException { McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(true); cacheHolder.processMcpServerDetailInfo(mcpServerDetailInfo); MockEventSubscriber subscriber = new MockEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); cacheHolder.processMcpServerDetailInfo(mcpServerDetailInfo); assertEquals(mcpServerDetailInfo, cacheHolder.getMcpServer("test", "1.0.0")); int retry = 0; while (retry < 3) { TimeUnit.MILLISECONDS.sleep(500); if (subscriber.invokedMark.get()) { fail("Subscriber for McpServerChangedEvent should not be invoked, but invoked."); } retry++; } } @Test @Disabled void processMcpServerDetailInfoWithException() throws InterruptedException { McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); // empty bean Repository will cause json serialize exception mcpServerDetailInfo.setRepository(new Repository()); MockEventSubscriber subscriber = new MockEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); cacheHolder.processMcpServerDetailInfo(mcpServerDetailInfo); int retry = 0; while (retry < 3) { TimeUnit.MILLISECONDS.sleep(500); if (subscriber.invokedMark.get()) { fail("Subscriber for McpServerChangedEvent should not be invoked, but invoked."); } retry++; } } @Test void addMcpServerUpdateTask() throws NacosException, InterruptedException { assertNull(cacheHolder.getMcpServer("test", "1.0.0")); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); when(aiGrpcClient.queryMcpServer("test", "1.0.0")).thenReturn(mcpServerDetailInfo); cacheHolder.addMcpServerUpdateTask("test", "1.0.0"); TimeUnit.MILLISECONDS.sleep(110); assertNotNull(cacheHolder.getMcpServer("test", "1.0.0")); TimeUnit.MILLISECONDS.sleep(110); verify(aiGrpcClient, times(2)).queryMcpServer("test", "1.0.0"); } @Test void runUpdateTaskWithException() throws NacosException, InterruptedException { assertNull(cacheHolder.getMcpServer("test", "1.0.0")); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); when(aiGrpcClient.queryMcpServer("test", "1.0.0")).thenThrow(new RuntimeException("test")); cacheHolder.addMcpServerUpdateTask("test", "1.0.0"); TimeUnit.MILLISECONDS.sleep(110); assertNull(cacheHolder.getMcpServer("test", "1.0.0")); TimeUnit.MILLISECONDS.sleep(110); verify(aiGrpcClient, times(2)).queryMcpServer("test", "1.0.0"); } @Test void removeMcpServerUpdateTask() throws NacosException, InterruptedException { assertNull(cacheHolder.getMcpServer("test", "1.0.0")); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); when(aiGrpcClient.queryMcpServer("test", "1.0.0")).thenReturn(mcpServerDetailInfo); cacheHolder.addMcpServerUpdateTask("test", "1.0.0"); TimeUnit.MILLISECONDS.sleep(110); assertNotNull(cacheHolder.getMcpServer("test", "1.0.0")); cacheHolder.removeMcpServerUpdateTask("test", "1.0.0"); TimeUnit.MILLISECONDS.sleep(110); verify(aiGrpcClient).queryMcpServer("test", "1.0.0"); } @Test void removeMcpServerUpdateTaskImmediately() throws NacosException, InterruptedException { assertNull(cacheHolder.getMcpServer("test", "1.0.0")); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); cacheHolder.addMcpServerUpdateTask("test", "1.0.0"); cacheHolder.removeMcpServerUpdateTask("test", "1.0.0"); TimeUnit.MILLISECONDS.sleep(110); verify(aiGrpcClient, never()).queryMcpServer("test", null); } private static class MockEventSubscriber extends Subscriber { private final AtomicBoolean invokedMark; private MockEventSubscriber() { this.invokedMark = new AtomicBoolean(false); } @Override public void onEvent(McpServerChangedEvent event) { invokedMark.set(true); } @Override public Class subscribeType() { return McpServerChangedEvent.class; } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/cache/NacosPromptCacheHolderTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.cache; import com.alibaba.nacos.api.ai.constant.AiConstants; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.event.PromptChangedEvent; import com.alibaba.nacos.client.ai.remote.AiClientProxy; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; 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.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NacosPromptCacheHolderTest { @Mock private AiClientProxy aiClientProxy; private NacosPromptCacheHolder cacheHolder; @BeforeEach void setUp() { Properties properties = new Properties(); properties.put(AiConstants.AI_PROMPT_CACHE_UPDATE_INTERVAL, "100"); cacheHolder = new NacosPromptCacheHolder(aiClientProxy, NacosClientProperties.PROTOTYPE.derive(properties)); } @AfterEach void tearDown() throws NacosException { cacheHolder.shutdown(); NotifyCenter.deregisterPublisher(PromptChangedEvent.class); } @Test void subscribePromptShouldReturnNullAndScheduleWhenNotFound() throws Exception { when(aiClientProxy.queryPrompt("p1", "1.0.0", null, null)) .thenThrow(new NacosException(NacosException.NOT_FOUND, "not found")); Prompt result = cacheHolder.subscribePrompt("p1", "1.0.0", null); assertNull(result); assertEquals(1, getUpdateTaskMap().size()); } @Test void subscribePromptShouldCacheAndPublishEventWhenFound() throws Exception { Prompt prompt = new Prompt("p1", "1.0.0", "v1"); prompt.setMd5("m1"); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, null)).thenReturn(prompt); MockPromptEventSubscriber subscriber = new MockPromptEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); cacheHolder.subscribePrompt("p1", "1.0.0", null); assertNotNull(getPromptCache().get("p1::version:1.0.0")); waitForSubscriber(subscriber); assertTrue(subscriber.invokedMark.get()); } @Test void updaterShouldIgnoreWhenNotModified() throws Exception { Prompt prompt = new Prompt("p1", "1.0.0", "v1"); prompt.setMd5("m1"); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, null)).thenReturn(prompt); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, "m1")) .thenThrow(new NacosException(NacosException.NOT_MODIFIED, "up to date")); cacheHolder.subscribePrompt("p1", "1.0.0", null); MockPromptEventSubscriber subscriber = new MockPromptEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); Runnable updater = getOnlyUpdater(); updater.run(); TimeUnit.MILLISECONDS.sleep(50); assertEquals("v1", getPromptCache().get("p1::version:1.0.0").getTemplate()); assertTrue(!subscriber.invokedMark.get()); } @Test void updaterShouldEvictAndPublishNullEventWhenNotFound() throws Exception { Prompt prompt = new Prompt("p1", "1.0.0", "v1"); prompt.setMd5("m1"); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, null)).thenReturn(prompt); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, "m1")) .thenThrow(new NacosException(NacosException.NOT_FOUND, "not found")); cacheHolder.subscribePrompt("p1", "1.0.0", null); MockPromptEventSubscriber subscriber = new MockPromptEventSubscriber(); NotifyCenter.registerSubscriber(subscriber); Runnable updater = getOnlyUpdater(); updater.run(); assertNull(getPromptCache().get("p1::version:1.0.0")); waitForSubscriber(subscriber); assertTrue(subscriber.invokedMark.get()); } @Test void unsubscribePromptShouldCancelTaskAndRemoveCache() throws Exception { Prompt prompt = new Prompt("p1", "1.0.0", "v1"); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, null)).thenReturn(prompt); cacheHolder.subscribePrompt("p1", "1.0.0", null); cacheHolder.unsubscribePrompt("p1", "1.0.0", null); assertTrue(getUpdateTaskMap().isEmpty()); assertTrue(getPromptCache().isEmpty()); verify(aiClientProxy, never()).queryPrompt("p1", null, null, null); } @Test void subscribePromptShouldThrowWhenUnexpectedException() throws Exception { when(aiClientProxy.queryPrompt("p1", "1.0.0", null, null)) .thenThrow(new NacosException(NacosException.SERVER_ERROR, "server error")); org.junit.jupiter.api.Assertions.assertThrows(NacosException.class, () -> cacheHolder.subscribePrompt("p1", "1.0.0", null)); } @Test void updaterShouldIgnoreGeneralExceptionAndKeepCache() throws Exception { Prompt prompt = new Prompt("p1", "1.0.0", "v1"); prompt.setMd5("m1"); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, null)).thenReturn(prompt); when(aiClientProxy.queryPrompt("p1", "1.0.0", null, "m1")) .thenThrow(new NacosException(NacosException.SERVER_ERROR, "server error")); cacheHolder.subscribePrompt("p1", "1.0.0", null); Runnable updater = getOnlyUpdater(); updater.run(); assertNotNull(getPromptCache().get("p1::version:1.0.0")); assertEquals(1, getUpdateTaskMap().size()); } @SuppressWarnings("unchecked") private Map getPromptCache() throws Exception { Field field = NacosPromptCacheHolder.class.getDeclaredField("promptCache"); field.setAccessible(true); return (Map) field.get(cacheHolder); } @SuppressWarnings("unchecked") private Map getUpdateTaskMap() throws Exception { Field field = NacosPromptCacheHolder.class.getDeclaredField("updateTaskMap"); field.setAccessible(true); return (Map) field.get(cacheHolder); } private Runnable getOnlyUpdater() throws Exception { Object updater = getUpdateTaskMap().values().iterator().next(); return (Runnable) updater; } private void waitForSubscriber(MockPromptEventSubscriber subscriber) throws InterruptedException { for (int i = 0; i < 4; i++) { if (subscriber.invokedMark.get()) { return; } TimeUnit.MILLISECONDS.sleep(200); } } private static class MockPromptEventSubscriber extends Subscriber { private final AtomicBoolean invokedMark = new AtomicBoolean(false); @Override public void onEvent(PromptChangedEvent event) { invokedMark.set(true); } @Override public Class subscribeType() { return PromptChangedEvent.class; } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/event/AiChangeNotifierPromptTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.AbstractNacosPromptListener; import com.alibaba.nacos.api.ai.listener.NacosPromptEvent; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class AiChangeNotifierPromptTest { @Test void promptEventShouldDispatchByCacheKeyOnly() { AiChangeNotifier notifier = new AiChangeNotifier(); AtomicBoolean v1Called = new AtomicBoolean(false); AtomicBoolean v2Called = new AtomicBoolean(false); notifier.registerListener("p1", "1.0.0", null, new PromptListenerInvoker(newPromptListener(v1Called))); notifier.registerListener("p1", "2.0.0", null, new PromptListenerInvoker(newPromptListener(v2Called))); Prompt prompt = new Prompt(); prompt.setPromptKey("p1"); notifier.onEvent(new PromptChangedEvent("p1", "p1::version:1.0.0", prompt)); assertTrue(v1Called.get()); assertFalse(v2Called.get()); } @Test void deregisterShouldStopDispatchForTargetKey() { AiChangeNotifier notifier = new AiChangeNotifier(); AtomicBoolean called = new AtomicBoolean(false); PromptListenerInvoker invoker = new PromptListenerInvoker(newPromptListener(called)); notifier.registerListener("p1", null, "prod", invoker); notifier.deregisterListener("p1", null, "prod", invoker); notifier.onEvent(new PromptChangedEvent("p1", "p1::label:prod", new Prompt())); assertFalse(called.get()); } @Test void isPromptSubscribedShouldMatchTripleKey() { AiChangeNotifier notifier = new AiChangeNotifier(); PromptListenerInvoker invoker = new PromptListenerInvoker(newPromptListener(new AtomicBoolean(false))); notifier.registerListener("p1", null, "prod", invoker); assertTrue(notifier.isPromptSubscribed("p1", null, "prod")); assertFalse(notifier.isPromptSubscribed("p1", "1.0.0", null)); } private AbstractNacosPromptListener newPromptListener(AtomicBoolean mark) { return new AbstractNacosPromptListener() { @Override public void onEvent(NacosPromptEvent event) { mark.set(true); } }; } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/event/AiChangeNotifierTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.AbstractNacosMcpServerListener; import com.alibaba.nacos.api.ai.listener.NacosMcpServerEvent; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 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.reset; import static org.mockito.Mockito.verify; class AiChangeNotifierTest { AiChangeNotifier changeNotifier; private AtomicBoolean invokedMark; private McpServerDetailInfo mcpServerDetailInfo; @BeforeEach void setUp() { changeNotifier = new AiChangeNotifier(); invokedMark = new AtomicBoolean(false); mcpServerDetailInfo = new McpServerDetailInfo(); mcpServerDetailInfo.setName("test"); mcpServerDetailInfo.setVersionDetail(new ServerVersionDetail()); mcpServerDetailInfo.getVersionDetail().setVersion("1.0.0"); mcpServerDetailInfo.getVersionDetail().setIs_latest(true); } @AfterEach void tearDown() { } @Test void onEventWithoutListener() { assertDoesNotThrow(() -> changeNotifier.onEvent(new McpServerChangedEvent(mcpServerDetailInfo))); } @Test void onEvent() { AbstractNacosMcpServerListener listener = new AbstractNacosMcpServerListener() { @Override public void onEvent(NacosMcpServerEvent event) { invokedMark.set(true); } }; McpServerListenerInvoker invoker = new McpServerListenerInvoker(listener); changeNotifier.registerListener("test", null, invoker); assertDoesNotThrow(() -> changeNotifier.onEvent(new McpServerChangedEvent(mcpServerDetailInfo))); assertTrue(invokedMark.get()); assertTrue(invoker.isInvoked()); } @Test void onEventNotLatestVersion() { AbstractNacosMcpServerListener listener = new AbstractNacosMcpServerListener() { @Override public void onEvent(NacosMcpServerEvent event) { invokedMark.set(true); } }; McpServerListenerInvoker invoker = new McpServerListenerInvoker(listener); changeNotifier.registerListener("test", "1.0.0", invoker); mcpServerDetailInfo.getVersionDetail().setIs_latest(false); assertDoesNotThrow(() -> changeNotifier.onEvent(new McpServerChangedEvent(mcpServerDetailInfo))); assertTrue(invokedMark.get()); assertTrue(invoker.isInvoked()); } @Test void deregisterListener() { AbstractNacosMcpServerListener listener = new AbstractNacosMcpServerListener() { @Override public void onEvent(NacosMcpServerEvent event) { invokedMark.set(true); } }; AbstractNacosMcpServerListener listener2 = Mockito.mock(AbstractNacosMcpServerListener.class); McpServerListenerInvoker invoker = new McpServerListenerInvoker(listener); McpServerListenerInvoker invoker2 = new McpServerListenerInvoker(listener2); changeNotifier.registerListener("test", null, invoker); changeNotifier.registerListener("test", null, invoker2); assertDoesNotThrow(() -> changeNotifier.onEvent(new McpServerChangedEvent(mcpServerDetailInfo))); assertTrue(invokedMark.get()); assertTrue(invoker.isInvoked()); assertTrue(invoker2.isInvoked()); verify(listener2).onEvent(any(NacosMcpServerEvent.class)); invokedMark.set(false); reset(listener2); changeNotifier.deregisterListener("test", null, invoker2); assertDoesNotThrow(() -> changeNotifier.onEvent(new McpServerChangedEvent(mcpServerDetailInfo))); assertTrue(invokedMark.get()); verify(listener2, Mockito.never()).onEvent(any(NacosMcpServerEvent.class)); invokedMark.set(false); changeNotifier.deregisterListener("test", null, invoker); assertDoesNotThrow(() -> changeNotifier.onEvent(new McpServerChangedEvent(mcpServerDetailInfo))); assertFalse(invokedMark.get()); } @Test void registerNullListener() { changeNotifier.registerListener("test", null, (McpServerListenerInvoker) null); assertFalse(changeNotifier.isMcpServerSubscribed("test", "")); } @Test void deregisterNullListener() { changeNotifier.deregisterListener("test", null, (McpServerListenerInvoker) null); assertFalse(changeNotifier.isMcpServerSubscribed("test", "")); } @Test void deregisterNonExistedListener() { AbstractNacosMcpServerListener listener = new AbstractNacosMcpServerListener() { @Override public void onEvent(NacosMcpServerEvent event) { invokedMark.set(true); } }; McpServerListenerInvoker invoker = new McpServerListenerInvoker(listener); changeNotifier.deregisterListener("test", null, invoker); assertFalse(changeNotifier.isMcpServerSubscribed("test", "")); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/event/McpServerListenerInvokerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.event; import com.alibaba.nacos.api.ai.listener.AbstractNacosMcpServerListener; import com.alibaba.nacos.api.ai.listener.NacosMcpServerEvent; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; 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.junit.jupiter.MockitoExtension; import java.util.concurrent.Executor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class McpServerListenerInvokerTest { @Mock Executor executor; @Mock AbstractNacosMcpServerListener listener; McpServerListenerInvoker invoker; @BeforeEach void setUp() { invoker = new McpServerListenerInvoker(listener); } @Test void invokerByExecutor() { when(listener.getExecutor()).thenReturn(executor); doAnswer(invocation -> { invocation.getArgument(0, Runnable.class).run(); return null; }).when(executor).execute(any(Runnable.class)); invoker.invoke(new NacosMcpServerEvent(new McpServerDetailInfo())); verify(executor).execute(any(Runnable.class)); verify(listener).onEvent(any(NacosMcpServerEvent.class)); } @Test void testEquals() { assertEquals(invoker, invoker); assertNotEquals(invoker, null); assertNotEquals(invoker, new Object()); assertEquals(invoker, new McpServerListenerInvoker(listener)); assertNotEquals(invoker, new McpServerListenerInvoker(new AbstractNacosMcpServerListener() { @Override public void onEvent(NacosMcpServerEvent event) { } })); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/remote/AiGrpcClientTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.ai.model.mcp.registry.ServerVersionDetail; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.ai.remote.AiRemoteConstants; import com.alibaba.nacos.api.ai.remote.request.McpServerEndpointRequest; import com.alibaba.nacos.api.ai.remote.request.QueryMcpServerRequest; import com.alibaba.nacos.api.ai.remote.request.QueryPromptRequest; import com.alibaba.nacos.api.ai.remote.request.ReleaseMcpServerRequest; import com.alibaba.nacos.api.ai.remote.response.McpServerEndpointResponse; import com.alibaba.nacos.api.ai.remote.response.QueryMcpServerResponse; import com.alibaba.nacos.api.ai.remote.response.QueryPromptResponse; import com.alibaba.nacos.api.ai.remote.response.ReleaseMcpServerResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; import com.alibaba.nacos.api.naming.remote.response.InstanceResponse; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.ai.cache.NacosAgentCardCacheHolder; import com.alibaba.nacos.client.ai.cache.NacosMcpServerCacheHolder; import com.alibaba.nacos.client.ai.remote.redo.AiGrpcRedoService; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.remote.client.RpcClient; 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.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.UUID; 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.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AiGrpcClientTest { @Mock private RpcClient rpcClient; @Mock private AbstractServerListManager serverListManager; @Mock private AiGrpcRedoService redoService; @Mock private SecurityProxy securityProxy; @Mock private NacosMcpServerCacheHolder mcpServerCacheHolder; @Mock private NacosAgentCardCacheHolder agentCardCacheHolder; AiGrpcClient aiGrpcClient; @BeforeEach void setUp() { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1"); NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); aiGrpcClient = new AiGrpcClient("test", clientProperties); } @AfterEach void tearDown() throws NacosException { aiGrpcClient.shutdown(); } @Test void start() throws NacosException { assertDoesNotThrow(() -> aiGrpcClient.start(mcpServerCacheHolder, agentCardCacheHolder)); } @Test void queryMcpServer() throws NacosException, NoSuchFieldException, IllegalAccessException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); QueryMcpServerResponse response = new QueryMcpServerResponse(); response.setMcpServerDetailInfo(mcpServerDetailInfo); when(rpcClient.request(any(QueryMcpServerRequest.class))).thenReturn(response); McpServerDetailInfo actual = aiGrpcClient.queryMcpServer("test", "1.0.0"); assertEquals(mcpServerDetailInfo, actual); } @Test void queryMcpServerWithErrorCode() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); Response response = ErrorResponse.build(NacosException.INVALID_PARAM, "test"); when(rpcClient.request(any(QueryMcpServerRequest.class))).thenReturn(response); assertThrows(NacosException.class, () -> aiGrpcClient.queryMcpServer("test", "1.0.0")); } @Test void queryMcpServerWithNoRight() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); Response response = ErrorResponse.build(NacosException.NO_RIGHT, "test"); when(rpcClient.request(any(QueryMcpServerRequest.class))).thenReturn(response); assertThrows(NacosException.class, () -> aiGrpcClient.queryMcpServer("test", "1.0.0")); verify(securityProxy).reLogin(); } @Test void queryMcpServerWithUnExpectedResponse() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); ReleaseMcpServerResponse response = new ReleaseMcpServerResponse(); when(rpcClient.request(any(QueryMcpServerRequest.class))).thenReturn(response); assertThrows(NacosException.class, () -> aiGrpcClient.queryMcpServer("test", "1.0.0")); } @Test void queryMcpServerWithException() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); when(rpcClient.request(any(QueryMcpServerRequest.class))).thenThrow(new RuntimeException("test")); assertThrows(NacosException.class, () -> aiGrpcClient.queryMcpServer("test", "1.0.0")); } @Test void queryPromptShouldBuildRequestWithVersionLabelMd5() throws Exception { injectMock(); when(securityProxy.getIdentityContext(any())).thenReturn(new HashMap<>()); QueryPromptResponse response = new QueryPromptResponse(); Prompt prompt = new Prompt(); prompt.setPromptKey("p1"); prompt.setVersion("1.0.0"); response.setPromptInfo(prompt); when(rpcClient.request(any(QueryPromptRequest.class))).thenReturn(response); Prompt actual = aiGrpcClient.queryPrompt("p1", "1.0.0", "prod", "m1"); assertEquals("p1", actual.getPromptKey()); ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(QueryPromptRequest.class); verify(rpcClient).request(reqCaptor.capture()); QueryPromptRequest captured = reqCaptor.getValue(); assertEquals("test", captured.getNamespaceId()); assertEquals("p1", captured.getPromptKey()); assertEquals("1.0.0", captured.getVersion()); assertEquals("prod", captured.getLabel()); assertEquals("m1", captured.getMd5()); } @Test void requestToServerShouldInjectSecurityHeaderForPromptRequest() throws Exception { injectMock(); Map headers = new HashMap<>(); headers.put("k", "v"); when(securityProxy.getIdentityContext(any())).thenReturn(headers); QueryPromptResponse response = new QueryPromptResponse(); response.setPromptInfo(new Prompt("p1", "1.0.0", "hello")); when(rpcClient.request(any(QueryPromptRequest.class))).thenReturn(response); aiGrpcClient.queryPrompt("p1", "1.0.0", null, null); verify(securityProxy).getIdentityContext(any()); ArgumentCaptor reqCaptor = ArgumentCaptor.forClass(QueryPromptRequest.class); verify(rpcClient).request(reqCaptor.capture()); assertEquals("v", reqCaptor.getValue().getHeader("k")); } @Test void queryPromptShouldReloginWhen403() throws Exception { injectMock(); when(securityProxy.getIdentityContext(any())).thenReturn(new HashMap<>()); Response response = ErrorResponse.build(NacosException.NO_RIGHT, "no right"); when(rpcClient.request(any(QueryPromptRequest.class))).thenReturn(response); assertThrows(NacosException.class, () -> aiGrpcClient.queryPrompt("p1", null, null, null)); verify(securityProxy).reLogin(); } @Test void queryPromptShouldThrowWhenResponseTypeMismatch() throws Exception { injectMock(); when(securityProxy.getIdentityContext(any())).thenReturn(new HashMap<>()); QueryMcpServerResponse response = new QueryMcpServerResponse(); when(rpcClient.request(any(QueryPromptRequest.class))).thenReturn(response); assertThrows(NacosException.class, () -> aiGrpcClient.queryPrompt("p1", null, null, null)); } @Test void releaseMcpServer() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); McpServerBasicInfo serverSpec = new McpServerBasicInfo(); serverSpec.setName("test"); serverSpec.setVersionDetail(new ServerVersionDetail()); serverSpec.getVersionDetail().setVersion("1.0.0"); String id = UUID.randomUUID().toString(); ReleaseMcpServerResponse response = new ReleaseMcpServerResponse(); response.setMcpId(id); when(rpcClient.request(any(ReleaseMcpServerRequest.class))).thenReturn(response); assertEquals(id, aiGrpcClient.releaseMcpServer(serverSpec, new McpToolSpecification(), null)); } @Test void registerMcpServerEndpoint() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); McpServerEndpointResponse response = new McpServerEndpointResponse(); response.setType(AiRemoteConstants.REGISTER_ENDPOINT); when(rpcClient.request(any(McpServerEndpointRequest.class))).thenReturn(response); aiGrpcClient.registerMcpServerEndpoint("test", "127.0.0.1", 8080, "1.0.0"); verify(redoService).cachedMcpServerEndpointForRedo("test", "127.0.0.1", 8080, "1.0.0"); verify(redoService).mcpServerEndpointRegistered("test"); } @Test void deregisterMcpServerEndpoint() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); McpServerEndpointResponse response = new McpServerEndpointResponse(); response.setType(AiRemoteConstants.DE_REGISTER_ENDPOINT); when(rpcClient.request(any(McpServerEndpointRequest.class))).thenReturn(response); aiGrpcClient.deregisterMcpServerEndpoint("test", "127.0.0.1", 8080); verify(redoService).mcpServerEndpointDeregister("test"); verify(redoService).mcpServerEndpointDeregistered("test"); } @Test void subscribeMcpServer() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); QueryMcpServerResponse response = new QueryMcpServerResponse(); response.setMcpServerDetailInfo(mcpServerDetailInfo); when(rpcClient.request(any(QueryMcpServerRequest.class))).thenReturn(response); assertEquals(mcpServerDetailInfo, aiGrpcClient.subscribeMcpServer("test", null)); verify(mcpServerCacheHolder).processMcpServerDetailInfo(mcpServerDetailInfo); verify(mcpServerCacheHolder).addMcpServerUpdateTask("test", null); } @Test void subscribeMcpServerAlreadySubscribed() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); McpServerDetailInfo mcpServerDetailInfo = new McpServerDetailInfo(); when(mcpServerCacheHolder.getMcpServer("test", null)).thenReturn(mcpServerDetailInfo); assertEquals(mcpServerDetailInfo, aiGrpcClient.subscribeMcpServer("test", null)); verify(rpcClient, never()).request(any(QueryMcpServerRequest.class)); verify(mcpServerCacheHolder, never()).processMcpServerDetailInfo(mcpServerDetailInfo); verify(mcpServerCacheHolder, never()).addMcpServerUpdateTask("test", null); } @Test void unsubscribeMcpServer() throws NoSuchFieldException, IllegalAccessException, NacosException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.SUPPORTED); aiGrpcClient.unsubscribeMcpServer("test", null); verify(mcpServerCacheHolder).removeMcpServerUpdateTask("test", null); } @Test void queryMcpServerWithFeatureDisabled() throws NoSuchFieldException, IllegalAccessException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.NOT_SUPPORTED); assertThrows(NacosRuntimeException.class, () -> aiGrpcClient.queryMcpServer("test", "1.0.0")); } @Test void releaseMcpServerWithFeatureDisabled() throws NoSuchFieldException, IllegalAccessException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.NOT_SUPPORTED); McpServerBasicInfo serverSpec = new McpServerBasicInfo(); serverSpec.setName("test"); serverSpec.setVersionDetail(new ServerVersionDetail()); serverSpec.getVersionDetail().setVersion("1.0.0"); assertThrows(NacosRuntimeException.class, () -> aiGrpcClient.releaseMcpServer(serverSpec, null, null)); } @Test void registerMcpServerEndpointWithFeatureDisabled() throws NoSuchFieldException, IllegalAccessException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.NOT_SUPPORTED); assertThrows(NacosRuntimeException.class, () -> aiGrpcClient.registerMcpServerEndpoint("test", "127.0.0.1", 8080, "1.0.0")); } @Test void deregisterMcpServerEndpointWithFeatureDisabled() throws NoSuchFieldException, IllegalAccessException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.NOT_SUPPORTED); assertThrows(NacosRuntimeException.class, () -> aiGrpcClient.deregisterMcpServerEndpoint("test", "127.0.0.1", 8080)); } @Test void subscribeMcpServerWithFeatureDisabled() throws NoSuchFieldException, IllegalAccessException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.NOT_SUPPORTED); assertThrows(NacosRuntimeException.class, () -> aiGrpcClient.subscribeMcpServer("test", null)); } @Test void unsubscribeMcpServerWithFeatureDisabled() throws NoSuchFieldException, IllegalAccessException { injectMock(); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_MCP_REGISTRY)).thenReturn(AbilityStatus.NOT_SUPPORTED); assertThrows(NacosRuntimeException.class, () -> aiGrpcClient.unsubscribeMcpServer("test", null)); } @Test void isEnable() throws NoSuchFieldException, IllegalAccessException { injectMock(); assertFalse(aiGrpcClient.isEnable()); when(rpcClient.isRunning()).thenReturn(true); assertTrue(aiGrpcClient.isEnable()); } @Test void requestToServerWithoutMcpRequest() throws NoSuchMethodException, NoSuchFieldException, IllegalAccessException { Method method = AiGrpcClient.class.getDeclaredMethod("requestToServer", Request.class, Class.class); method.setAccessible(true); injectMock(); try { method.invoke(aiGrpcClient, new InstanceRequest(), InstanceResponse.class); } catch (InvocationTargetException e) { Throwable targetException = e.getTargetException(); assertInstanceOf(NacosException.class, targetException); assertEquals(400, ((NacosException) targetException).getErrCode()); } } private void injectMock() throws NoSuchFieldException, IllegalAccessException { Field field = AiGrpcClient.class.getDeclaredField("rpcClient"); field.setAccessible(true); RpcClient autoRpcClient = (RpcClient) field.get(aiGrpcClient); field.set(aiGrpcClient, rpcClient); field = AiGrpcClient.class.getDeclaredField("serverListManager"); field.setAccessible(true); AbstractServerListManager autoServerListManager = (AbstractServerListManager) field.get(aiGrpcClient); field.set(aiGrpcClient, serverListManager); field = AiGrpcClient.class.getDeclaredField("redoService"); field.setAccessible(true); AiGrpcRedoService autoRedoService = (AiGrpcRedoService) field.get(aiGrpcClient); field.set(aiGrpcClient, redoService); field = AiGrpcClient.class.getDeclaredField("securityProxy"); field.setAccessible(true); field.set(aiGrpcClient, securityProxy); field = AiGrpcClient.class.getDeclaredField("mcpServerCacheHolder"); field.setAccessible(true); field.set(aiGrpcClient, mcpServerCacheHolder); try { autoRpcClient.shutdown(); autoServerListManager.shutdown(); autoRedoService.shutdown(); } catch (NacosException ignored) { } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/remote/AiHttpClientProxyTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote; import com.alibaba.nacos.api.ai.model.prompt.Prompt; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.api.model.v2.Result; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.concurrent.ScheduledThreadPoolExecutor; 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.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AiHttpClientProxyTest { @Mock private NacosRestTemplate nacosRestTemplate; @Mock private NamingServerListManager serverListManager; @Mock private SecurityProxy securityProxy; private AiHttpClientProxy httpClientProxy; @BeforeEach void setUp() throws Exception { httpClientProxy = createProxyWithMocks(); } @AfterEach void tearDown() throws NacosException { if (httpClientProxy != null) { httpClientProxy.shutdown(); } } @Test void queryPromptSuccess() throws Exception { Prompt expectedPrompt = new Prompt("test-key", "1.0.0", "Hello {{name}}"); expectedPrompt.setMd5("abc123"); Result result = Result.success(expectedPrompt); String responseBody = JacksonUtils.toJson(result); HttpRestResult httpResult = new HttpRestResult<>(); httpResult.setCode(200); httpResult.setData(responseBody); when(serverListManager.getServerList()).thenReturn(Arrays.asList("127.0.0.1:8848")); when(serverListManager.getContextPath()).thenReturn("/nacos"); when(securityProxy.getIdentityContext(any())).thenReturn(new HashMap<>()); doReturn(httpResult).when(nacosRestTemplate) .get(anyString(), any(Header.class), any(Query.class), eq(String.class)); Prompt actual = httpClientProxy.queryPrompt("test-key", "1.0.0", null, null); assertNotNull(actual); assertEquals("test-key", actual.getPromptKey()); assertEquals("1.0.0", actual.getVersion()); assertEquals("Hello {{name}}", actual.getTemplate()); assertEquals("abc123", actual.getMd5()); } @Test void queryPromptNotModifiedShouldThrow() throws Exception { HttpRestResult httpResult = new HttpRestResult<>(); httpResult.setCode(HttpURLConnection.HTTP_NOT_MODIFIED); when(serverListManager.getServerList()).thenReturn(Arrays.asList("127.0.0.1:8848")); when(serverListManager.getContextPath()).thenReturn("/nacos"); when(securityProxy.getIdentityContext(any())).thenReturn(new HashMap<>()); doReturn(httpResult).when(nacosRestTemplate) .get(anyString(), any(Header.class), any(Query.class), eq(String.class)); NacosException exception = assertThrows(NacosException.class, () -> httpClientProxy.queryPrompt("test-key", null, null, "md5-value")); assertEquals(NacosException.NOT_MODIFIED, exception.getErrCode()); } @Test void queryPromptNoServerAvailable() { when(serverListManager.getServerList()).thenReturn(Collections.emptyList()); NacosException exception = assertThrows(NacosException.class, () -> httpClientProxy.queryPrompt("test-key", null, null, null)); assertEquals(NacosException.INVALID_PARAM, exception.getErrCode()); } @Test void queryPromptServerError() throws Exception { HttpRestResult httpResult = new HttpRestResult<>(); httpResult.setCode(500); httpResult.setMessage("Internal Server Error"); when(serverListManager.getServerList()).thenReturn(Arrays.asList("127.0.0.1:8848")); when(serverListManager.getContextPath()).thenReturn("/nacos"); when(securityProxy.getIdentityContext(any())).thenReturn(new HashMap<>()); doReturn(httpResult).when(nacosRestTemplate) .get(anyString(), any(Header.class), any(Query.class), eq(String.class)); NacosException exception = assertThrows(NacosException.class, () -> httpClientProxy.queryPrompt("test-key", null, null, null)); assertEquals(500, exception.getErrCode()); } @Test void queryPromptByLabel() throws Exception { Prompt expectedPrompt = new Prompt("test-key", "2.0.0", "Label prompt"); Result result = Result.success(expectedPrompt); String responseBody = JacksonUtils.toJson(result); HttpRestResult httpResult = new HttpRestResult<>(); httpResult.setCode(200); httpResult.setData(responseBody); when(serverListManager.getServerList()).thenReturn(Arrays.asList("127.0.0.1:8848")); when(serverListManager.getContextPath()).thenReturn("/nacos"); when(securityProxy.getIdentityContext(any())).thenReturn(new HashMap<>()); doReturn(httpResult).when(nacosRestTemplate) .get(anyString(), any(Header.class), any(Query.class), eq(String.class)); Prompt actual = httpClientProxy.queryPrompt("test-key", null, "prod", null); assertNotNull(actual); assertEquals("test-key", actual.getPromptKey()); } private AiHttpClientProxy createProxyWithMocks() throws Exception { AiHttpClientProxy proxy = new AiHttpClientProxy(); injectField(proxy, "namespaceId", "public"); injectField(proxy, "nacosRestTemplate", nacosRestTemplate); injectField(proxy, "serverListManager", serverListManager); injectField(proxy, "securityProxy", securityProxy); injectField(proxy, "executorService", new ScheduledThreadPoolExecutor(1)); return proxy; } private void injectField(Object target, String fieldName, Object value) throws Exception { Field field = target.getClass().getDeclaredField(fieldName); field.setAccessible(true); field.set(target, value); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/remote/redo/AiGrpcRedoServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.redo.data.RedoData; 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.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(MockitoExtension.class) class AiGrpcRedoServiceTest { @Mock private AiGrpcClient aiGrpcClient; AiGrpcRedoService redoService; @BeforeEach void setUp() { NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); redoService = new AiGrpcRedoService(properties, aiGrpcClient); } @AfterEach void tearDown() { redoService.shutdown(); } @Test void cachedMcpServerEndpointForRedo() { redoService.cachedMcpServerEndpointForRedo("test", "127.0.0.1", 8080, "1.0.0"); McpServerEndpoint redoData = redoService.getMcpServerEndpoint("test"); assertEquals("127.0.0.1", redoData.getAddress()); assertEquals(8080, redoData.getPort()); assertEquals("1.0.0", redoData.getVersion()); assertFalse(redoService.isMcpServerEndpointRegistered("test")); redoService.mcpServerEndpointRegistered("test"); assertTrue(redoService.isMcpServerEndpointRegistered("test")); redoService.mcpServerEndpointDeregister("test"); assertTrue(redoService.isMcpServerEndpointRegistered("test")); redoService.mcpServerEndpointDeregistered("test"); assertFalse(redoService.isMcpServerEndpointRegistered("test")); redoService.removeMcpServerEndpointForRedo("test"); redoData = redoService.getMcpServerEndpoint("test"); assertNull(redoData); } @Test void findMcpServerEndpointRedoData() { redoService.cachedMcpServerEndpointForRedo("test", "127.0.0.1", 8080, "1.0.0"); redoService.mcpServerEndpointRegistered("test"); redoService.cachedMcpServerEndpointForRedo("test2", "127.0.0.1", 8080, "1.0.0"); Set> redoDatas = redoService.findMcpServerEndpointRedoData(); assertEquals(1, redoDatas.size()); RedoData redoData = redoDatas.iterator().next(); assertInstanceOf(McpServerEndpointRedoData.class, redoData); assertEquals("test2", ((McpServerEndpointRedoData) redoData).getMcpName()); } @Test void cachedAgentEndpointForRedoWithSingleEndpoint() { AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("127.0.0.1"); endpoint.setPort(8080); AgentEndpointWrapper wrapper = AgentEndpointWrapper.wrap(endpoint); redoService.cachedAgentEndpointForRedo("testAgent", wrapper); assertFalse(redoService.isAgentEndpointRegistered("testAgent")); redoService.agentEndpointRegistered("testAgent"); assertTrue(redoService.isAgentEndpointRegistered("testAgent")); redoService.agentEndpointDeregister("testAgent"); assertTrue(redoService.isAgentEndpointRegistered("testAgent")); redoService.agentEndpointDeregistered("testAgent"); assertFalse(redoService.isAgentEndpointRegistered("testAgent")); redoService.removeAgentEndpointForRedo("testAgent"); Set> redoDatas = redoService.findAgentEndpointRedoData(); assertTrue(redoDatas.isEmpty()); } @Test void cachedAgentEndpointForRedoWithBatchEndpoint() { AgentEndpoint endpoint1 = new AgentEndpoint(); endpoint1.setAddress("127.0.0.1"); endpoint1.setPort(8080); AgentEndpoint endpoint2 = new AgentEndpoint(); endpoint2.setAddress("127.0.0.2"); endpoint2.setPort(8081); AgentEndpointWrapper wrapper = AgentEndpointWrapper.wrap(Collections.singletonList(endpoint1)); redoService.cachedAgentEndpointForRedo("testAgent", wrapper); assertFalse(redoService.isAgentEndpointRegistered("testAgent")); Set> redoDatas = redoService.findAgentEndpointRedoData(); assertEquals(1, redoDatas.size()); RedoData redoData = redoDatas.iterator().next(); assertInstanceOf(AgentEndpointRedoData.class, redoData); assertEquals("testAgent", ((AgentEndpointRedoData) redoData).getAgentName()); redoService.agentEndpointRegistered("testAgent"); assertTrue(redoService.isAgentEndpointRegistered("testAgent")); redoDatas = redoService.findAgentEndpointRedoData(); assertTrue(redoDatas.isEmpty()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/remote/redo/AiRedoScheduledTaskTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import com.alibaba.nacos.api.ai.model.a2a.AgentEndpoint; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.ai.remote.AiGrpcClient; import com.alibaba.nacos.client.redo.data.RedoData; 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.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.HashSet; import java.util.Set; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class AiRedoScheduledTaskTest { @Mock private AiGrpcClient aiGrpcClient; @Mock private AiGrpcRedoService aiGrpcRedoService; AiRedoScheduledTask task; @BeforeEach void setUp() { task = new AiRedoScheduledTask(aiGrpcRedoService, aiGrpcClient); } @AfterEach void tearDown() { } @Test void testRunForRedo() throws NacosException { Set> set = new HashSet<>(); set.add(buildMcpServerEndpointRedoData("test", RedoData.RedoType.REGISTER)); set.add(buildMcpServerEndpointRedoData("test1", RedoData.RedoType.UNREGISTER)); set.add(buildMcpServerEndpointRedoData("test2", RedoData.RedoType.REMOVE)); when(aiGrpcRedoService.findMcpServerEndpointRedoData()).thenReturn(set); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); task.run(); verify(aiGrpcClient).doRegisterMcpServerEndpoint("test", "127.0.0.1", 8080, "1.0.0"); verify(aiGrpcClient).doDeregisterMcpServerEndpoint("test1", "127.0.0.1", 8080); verify(aiGrpcRedoService).removeMcpServerEndpointForRedo("test2"); } @Test void testRunForRedoConnectionDisconnect() throws NacosException { Set> set = new HashSet<>(); set.add(buildMcpServerEndpointRedoData("test", RedoData.RedoType.REGISTER)); set.add(buildMcpServerEndpointRedoData("test1", RedoData.RedoType.UNREGISTER)); set.add(buildMcpServerEndpointRedoData("test2", RedoData.RedoType.REMOVE)); when(aiGrpcRedoService.findMcpServerEndpointRedoData()).thenReturn(set); when(aiGrpcRedoService.isConnected()).thenReturn(true); task.run(); verify(aiGrpcClient, never()).doRegisterMcpServerEndpoint("test", "127.0.0.1", 8080, "1.0.0"); verify(aiGrpcClient, never()).doDeregisterMcpServerEndpoint("test1", "127.0.0.1", 8080); verify(aiGrpcRedoService).removeMcpServerEndpointForRedo("test2"); } @Test void testRunForRedoWithSingleNacosException() throws NacosException { Set> set = new HashSet<>(); set.add(buildMcpServerEndpointRedoData("test", RedoData.RedoType.REGISTER)); set.add(buildMcpServerEndpointRedoData("test1", RedoData.RedoType.UNREGISTER)); set.add(buildMcpServerEndpointRedoData("test2", RedoData.RedoType.REMOVE)); when(aiGrpcRedoService.findMcpServerEndpointRedoData()).thenReturn(set); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); doThrow(new NacosException(500, "test")).when(aiGrpcClient) .doRegisterMcpServerEndpoint("test", "127.0.0.1", 8080, "1.0.0"); task.run(); verify(aiGrpcClient).doRegisterMcpServerEndpoint("test", "127.0.0.1", 8080, "1.0.0"); verify(aiGrpcClient).doDeregisterMcpServerEndpoint("test1", "127.0.0.1", 8080); verify(aiGrpcRedoService).removeMcpServerEndpointForRedo("test2"); } @Test void testRunForRedoWithOtherException() throws NacosException { Set> set = new HashSet<>(); set.add(buildMcpServerEndpointRedoData("test", RedoData.RedoType.REGISTER)); set.add(buildMcpServerEndpointRedoData("test1", RedoData.RedoType.REGISTER)); set.add(buildMcpServerEndpointRedoData("test2", RedoData.RedoType.REGISTER)); String fistMcpName = ((McpServerEndpointRedoData) set.iterator().next()).getMcpName(); when(aiGrpcRedoService.findMcpServerEndpointRedoData()).thenReturn(set); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); doThrow(new RuntimeException("test")).when(aiGrpcClient) .doRegisterMcpServerEndpoint(fistMcpName, "127.0.0.1", 8080, "1.0.0"); task.run(); verify(aiGrpcClient, times(1)).doRegisterMcpServerEndpoint(anyString(), anyString(), any(int.class), anyString()); } @Test void testRunForAgentEndpointRedoWithSingleRegister() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.REGISTER, false)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); // Run the task task.run(); // Verify interactions AgentEndpoint expectedEndpoint = new AgentEndpoint(); expectedEndpoint.setAddress("127.0.0.1"); expectedEndpoint.setPort(8080); verify(aiGrpcClient).doRegisterAgentEndpoint("testAgent", expectedEndpoint); } @Test void testRunForAgentEndpointRedoWithBatchRegister() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.REGISTER, true)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); // Run the task task.run(); // Verify interactions AgentEndpoint expectedEndpoint = new AgentEndpoint(); expectedEndpoint.setAddress("127.0.0.1"); expectedEndpoint.setPort(8080); verify(aiGrpcClient).doRegisterAgentEndpoint("testAgent", Collections.singletonList(expectedEndpoint)); } @Test void testRunForAgentEndpointRedoWithUnregister() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.UNREGISTER, false)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); // Run the task task.run(); // Verify interactions AgentEndpoint expectedEndpoint = new AgentEndpoint(); expectedEndpoint.setAddress("127.0.0.1"); expectedEndpoint.setPort(8080); verify(aiGrpcClient).doDeregisterAgentEndpoint("testAgent", expectedEndpoint); } @Test void testRunForAgentEndpointRedoWithBatchUnregister() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.UNREGISTER, true)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); // Run the task task.run(); // Verify interactions AgentEndpoint expectedEndpoint = new AgentEndpoint(); expectedEndpoint.setAddress("127.0.0.1"); expectedEndpoint.setPort(8080); verify(aiGrpcClient).doDeregisterAgentEndpoint("testAgent", expectedEndpoint); } @Test void testRunForAgentEndpointRedoWithRemove() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.REMOVE, false)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcRedoService.isConnected()).thenReturn(true); // Run the task task.run(); // Verify interactions verify(aiGrpcRedoService).removeAgentEndpointForRedo("testAgent"); } @Test void testRunForAgentEndpointRedoWithClientDisabled() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.REGISTER, false)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcClient.isEnable()).thenReturn(false); // Client is disabled when(aiGrpcRedoService.isConnected()).thenReturn(true); // Run the task task.run(); // Verify that no interactions happened with the client verify(aiGrpcClient, never()).doRegisterAgentEndpoint(anyString(), any(AgentEndpoint.class)); } @Test void testRunForAgentEndpointRedoWithUnregisterClientDisabled() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.UNREGISTER, false)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcClient.isEnable()).thenReturn(false); // Client is disabled when(aiGrpcRedoService.isConnected()).thenReturn(true); // Run the task task.run(); // Verify that no interactions happened with the client verify(aiGrpcClient, never()).doRegisterAgentEndpoint(anyString(), any(AgentEndpoint.class)); } @Test void testRunForAgentEndpointRedoWithNacosException() throws NacosException { // Prepare test data Set> agentEndpointSet = new HashSet<>(); agentEndpointSet.add(buildAgentEndpointRedoData("testAgent", RedoData.RedoType.REGISTER, false)); // Mock service methods when(aiGrpcRedoService.findAgentEndpointRedoData()).thenReturn(agentEndpointSet); when(aiGrpcClient.isEnable()).thenReturn(true); when(aiGrpcRedoService.isConnected()).thenReturn(true); AgentEndpoint expectedEndpoint = new AgentEndpoint(); expectedEndpoint.setAddress("127.0.0.1"); expectedEndpoint.setPort(8080); doThrow(new NacosException(500, "test")).when(aiGrpcClient) .doRegisterAgentEndpoint("testAgent", expectedEndpoint); // Run the task - should not throw exception task.run(); // Verify interactions verify(aiGrpcClient).doRegisterAgentEndpoint("testAgent", expectedEndpoint); } private McpServerEndpointRedoData buildMcpServerEndpointRedoData(String mcpName, RedoData.RedoType redoType) { McpServerEndpoint mcpServerEndpoint = new McpServerEndpoint("127.0.0.1", 8080, "1.0.0"); McpServerEndpointRedoData result = new McpServerEndpointRedoData(mcpName); result.set(mcpServerEndpoint); switch (redoType) { case UNREGISTER: result.registered(); result.setUnregistering(true); result.setExpectedRegistered(false); break; case REMOVE: result.unregistered(); result.setExpectedRegistered(false); break; default: } return result; } private AgentEndpointRedoData buildAgentEndpointRedoData(String agentName, RedoData.RedoType redoType, boolean isBatch) { // Prepare test data AgentEndpoint endpoint = new AgentEndpoint(); endpoint.setAddress("127.0.0.1"); endpoint.setPort(8080); AgentEndpointWrapper wrapper = isBatch ? AgentEndpointWrapper.wrap(Collections.singletonList(endpoint)) : AgentEndpointWrapper.wrap(endpoint); AgentEndpointRedoData agentEndpointRedoData = new AgentEndpointRedoData(agentName, wrapper); switch (redoType) { case UNREGISTER: agentEndpointRedoData.registered(); agentEndpointRedoData.setUnregistering(true); agentEndpointRedoData.setExpectedRegistered(false); break; case REMOVE: agentEndpointRedoData.unregistered(); agentEndpointRedoData.setExpectedRegistered(false); break; default: } return agentEndpointRedoData; } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/remote/redo/McpServerEndpointRedoDataTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class McpServerEndpointRedoDataTest { @Test void testEquals() { McpServerEndpointRedoData data = new McpServerEndpointRedoData("mcpName"); data.set(new McpServerEndpoint("127.0.0.1", 8080, "v1")); assertEquals(data, data); assertNotEquals(data, null); assertNotEquals(data, new Object()); McpServerEndpointRedoData data1 = new McpServerEndpointRedoData("mcpName"); data1.set(new McpServerEndpoint("127.0.0.1", 8080, "v1")); assertEquals(data, data1); data1 = new McpServerEndpointRedoData("mcpName2"); data1.set(new McpServerEndpoint("127.0.0.1", 8080, "v1")); assertNotEquals(data, data1); data1 = new McpServerEndpointRedoData("mcpName"); data1.set(new McpServerEndpoint("127.0.0.1", 8081, "v1")); assertNotEquals(data, data1); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/remote/redo/McpServerEndpointTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.remote.redo; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class McpServerEndpointTest { @Test void testEquals() { McpServerEndpoint mcpServerEndpoint = new McpServerEndpoint("127.0.0.1", 8080, "v1"); assertEquals(mcpServerEndpoint, mcpServerEndpoint); assertNotEquals(mcpServerEndpoint, null); assertNotEquals(mcpServerEndpoint, new Object()); McpServerEndpoint mcpServerEndpoint1 = new McpServerEndpoint("127.0.0.1", 8080, "v1"); assertEquals(mcpServerEndpoint, mcpServerEndpoint1); mcpServerEndpoint1 = new McpServerEndpoint("127.0.0.1", 8080, "v2"); assertNotEquals(mcpServerEndpoint, mcpServerEndpoint1); mcpServerEndpoint1 = new McpServerEndpoint("127.0.0.2", 8080, "v1"); assertNotEquals(mcpServerEndpoint, mcpServerEndpoint1); mcpServerEndpoint1 = new McpServerEndpoint("127.0.0.1", 8081, "v1"); assertNotEquals(mcpServerEndpoint, mcpServerEndpoint1); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/ai/utils/CacheKeyUtilsTest.java ================================================ /* * Copyright 1999-2026 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.ai.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class CacheKeyUtilsTest { @Test void buildPromptKeyShouldUseLabelFirst() { String key = CacheKeyUtils.buildPromptKey("p1", "1.0.0", "prod"); assertEquals("p1::label:prod", key); } @Test void buildPromptKeyShouldUseVersionWhenNoLabel() { String key = CacheKeyUtils.buildPromptKey("p1", "1.0.0", null); assertEquals("p1::version:1.0.0", key); } @Test void buildPromptKeyShouldUseLatestWhenNoLabelAndVersion() { String key = CacheKeyUtils.buildPromptKey("p1", null, null); assertEquals("p1::latest", key); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/NacosConfigServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.config.listener.FuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.config.impl.ClientWorker; import com.alibaba.nacos.client.config.impl.ConfigFuzzyWatchContext; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; import com.alibaba.nacos.client.config.impl.ConfigTransportClient; import com.alibaba.nacos.client.config.impl.LocalConfigInfoProcessor; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; 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.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; import java.util.Arrays; import java.util.Collections; import java.util.Properties; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Future; import static com.alibaba.nacos.api.common.Constants.ALL_PATTERN; 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.anyString; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class NacosConfigServiceTest { private NacosConfigService nacosConfigService; private ClientWorker mockWoker; private void setFinal(Field field, Object ins, Object newValue) throws Exception { field.setAccessible(true); field.set(ins, newValue); field.setAccessible(false); } @BeforeEach void mock() throws Exception { final Properties properties = new Properties(); properties.put("serverAddr", "1.1.1.1"); nacosConfigService = new NacosConfigService(properties); mockWoker = Mockito.mock(ClientWorker.class); setFinal(NacosConfigService.class.getDeclaredField("worker"), nacosConfigService, mockWoker); } @AfterEach void clean() { LocalConfigInfoProcessor.cleanAllSnapshot(); } @Test void testGetConfigFromServer() throws NacosException { final String dataId = "1"; final String group = "2"; final String tenant = "public"; final int timeout = 3000; ConfigResponse response = new ConfigResponse(); response.setContent("aa"); response.setConfigType("bb"); Mockito.when(mockWoker.getServerConfig(dataId, group, tenant, timeout, false)).thenReturn(response); final String config = nacosConfigService.getConfig(dataId, group, timeout); assertEquals("aa", config); Mockito.verify(mockWoker, Mockito.times(1)).getServerConfig(dataId, group, tenant, timeout, false); } @Test void testGetConfigFromFailOver() throws NacosException { final String dataId = "1failover"; final String group = "2"; final String tenant = "public"; MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( LocalConfigInfoProcessor.class); try { String contentFailOver = "failOverContent" + System.currentTimeMillis(); localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(contentFailOver); final int timeout = 3000; final String config = nacosConfigService.getConfig(dataId, group, timeout); assertEquals(contentFailOver, config); } finally { localConfigInfoProcessorMockedStatic.close(); } } @Test void testGetConfigFromLocalCache() throws NacosException { final String dataId = "1localcache"; final String group = "2"; final String tenant = "public"; MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( LocalConfigInfoProcessor.class); try { String contentFailOver = "localCacheContent" + System.currentTimeMillis(); //fail over null localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(null); //snapshot content localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getSnapshot(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(contentFailOver); //form server error. final int timeout = 3000; Mockito.when(mockWoker.getServerConfig(dataId, group, tenant, timeout, false)) .thenThrow(new NacosException()); final String config = nacosConfigService.getConfig(dataId, group, timeout); assertEquals(contentFailOver, config); } finally { localConfigInfoProcessorMockedStatic.close(); } } @Test void testGetConfig403() throws NacosException { final String dataId = "1localcache403"; final String group = "2"; final String tenant = "public"; MockedStatic localConfigInfoProcessorMockedStatic = Mockito.mockStatic( LocalConfigInfoProcessor.class); try { //fail over null localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getFailover(any(), eq(dataId), eq(group), eq(tenant))) .thenReturn(null); //form server error. final int timeout = 3000; Mockito.when(mockWoker.getServerConfig(dataId, group, tenant, timeout, false)) .thenThrow(new NacosException(NacosException.NO_RIGHT, "no right")); try { nacosConfigService.getConfig(dataId, group, timeout); assertTrue(false); } catch (NacosException e) { assertEquals(NacosException.NO_RIGHT, e.getErrCode()); } } finally { localConfigInfoProcessorMockedStatic.close(); } } @Test void testGetConfigAndSignListener() throws NacosException { final String dataId = "1"; final String group = "2"; final String tenant = ""; final String content = "123"; final int timeout = 3000; final Listener listener = new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { } }; Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "aaa"); final NacosClientProperties nacosProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager mgr = new ConfigServerListManager(nacosProperties); mgr.start(); ConfigTransportClient client = new ConfigTransportClient(nacosProperties, mgr) { @Override public void startInternal() throws NacosException { // NOOP } @Override public String getName() { return "TestConfigTransportClient"; } @Override public void notifyListenConfig() { // NOOP } @Override public void executeConfigListen() { // NOOP } @Override public void removeCache(String dataId, String group) { // NOOP } @Override public ConfigResponse queryConfig(String dataId, String group, String tenant, long readTimeous, boolean notify) throws NacosException { ConfigResponse configResponse = new ConfigResponse(); configResponse.setContent(content); configResponse.setDataId(dataId); configResponse.setGroup(group); configResponse.setTenant(tenant); return configResponse; } @Override public boolean publishConfig(String dataId, String group, String tenant, String appName, String tag, String betaIps, String content, String encryptedDataKey, String casMd5, String type) throws NacosException { return false; } @Override public boolean removeConfig(String dataId, String group, String tenant, String tag) throws NacosException { return false; } }; Mockito.when(mockWoker.getAgent()).thenReturn(client); final String config = nacosConfigService.getConfigAndSignListener(dataId, group, timeout, listener); assertEquals(content, config); Mockito.verify(mockWoker, Mockito.times(1)) .addTenantListenersWithContent(dataId, group, content, null, Collections.singletonList(listener)); assertEquals(content, config); Mockito.verify(mockWoker, Mockito.times(1)) .addTenantListenersWithContent(dataId, group, content, null, Arrays.asList(listener)); } @Test void testAddListener() throws NacosException { String dataId = "1"; String group = "2"; Listener listener = new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { } }; nacosConfigService.addListener(dataId, group, listener); Mockito.verify(mockWoker, Mockito.times(1)).addTenantListeners(dataId, group, Arrays.asList(listener)); } @Test void testPublishConfig() throws NacosException { String dataId = "1"; String group = "2"; String content = "123"; String namespace = "public"; String type = ConfigType.getDefaultType().getType(); Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)) .thenReturn(true); final boolean b = nacosConfigService.publishConfig(dataId, group, content); assertTrue(b); Mockito.verify(mockWoker, Mockito.times(1)) .publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); } @Test void testPublishConfig2() throws NacosException { String dataId = "1"; String group = "2"; String content = "123"; String namespace = "public"; String type = ConfigType.PROPERTIES.getType(); Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", null, type)) .thenReturn(true); final boolean b = nacosConfigService.publishConfig(dataId, group, content, type); assertTrue(b); Mockito.verify(mockWoker, Mockito.times(1)) .publishConfig(dataId, group, namespace, null, null, null, content, "", null, type); } @Test void testPublishConfigCas() throws NacosException { String dataId = "1"; String group = "2"; String content = "123"; String namespace = "public"; String casMd5 = "96147704e3cb8be8597d55d75d244a02"; String type = ConfigType.getDefaultType().getType(); Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)) .thenReturn(true); final boolean b = nacosConfigService.publishConfigCas(dataId, group, content, casMd5); assertTrue(b); Mockito.verify(mockWoker, Mockito.times(1)) .publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); } @Test void testPublishConfigCas2() throws NacosException { String dataId = "1"; String group = "2"; String content = "123"; String namespace = "public"; String casMd5 = "96147704e3cb8be8597d55d75d244a02"; String type = ConfigType.PROPERTIES.getType(); Mockito.when(mockWoker.publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type)) .thenReturn(true); final boolean b = nacosConfigService.publishConfigCas(dataId, group, content, casMd5, type); assertTrue(b); Mockito.verify(mockWoker, Mockito.times(1)) .publishConfig(dataId, group, namespace, null, null, null, content, "", casMd5, type); } @Test void testRemoveConfig() throws NacosException { String dataId = "1"; String group = "2"; String tenant = "public"; Mockito.when(mockWoker.removeConfig(dataId, group, tenant, null)).thenReturn(true); final boolean b = nacosConfigService.removeConfig(dataId, group); assertTrue(b); Mockito.verify(mockWoker, Mockito.times(1)).removeConfig(dataId, group, tenant, null); } @Test void testRemoveListener() { String dataId = "1"; String group = "2"; Listener listener = new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { } }; nacosConfigService.removeListener(dataId, group, listener); Mockito.verify(mockWoker, Mockito.times(1)).removeTenantListener(dataId, group, listener); } @Test void testGetServerStatus() { Mockito.when(mockWoker.isHealthServer()).thenReturn(true); assertEquals("UP", nacosConfigService.getServerStatus()); Mockito.verify(mockWoker, Mockito.times(1)).isHealthServer(); Mockito.when(mockWoker.isHealthServer()).thenReturn(false); assertEquals("DOWN", nacosConfigService.getServerStatus()); Mockito.verify(mockWoker, Mockito.times(2)).isHealthServer(); } @Test void testFuzzyWatch1() throws NacosException { String groupNamePattern = "default_group"; FuzzyWatchEventWatcher fuzzyWatchEventWatcher = new FuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } @Override public Executor getExecutor() { return null; } }; ConfigFuzzyWatchContext context = Mockito.mock(ConfigFuzzyWatchContext.class); Mockito.when(mockWoker.addTenantFuzzyWatcher(anyString(), anyString(), any())).thenReturn(context); nacosConfigService.fuzzyWatch(groupNamePattern, fuzzyWatchEventWatcher); Mockito.verify(mockWoker, Mockito.times(1)) .addTenantFuzzyWatcher(ALL_PATTERN, groupNamePattern, fuzzyWatchEventWatcher); Mockito.verify(context, Mockito.times(1)).createNewFuture(); } @Test void testFuzzyWatch2() throws NacosException { String groupNamePattern = "default_group"; String dataIdPattern = "dataId*"; FuzzyWatchEventWatcher fuzzyWatchEventWatcher = new FuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } @Override public Executor getExecutor() { return null; } }; ConfigFuzzyWatchContext context = Mockito.mock(ConfigFuzzyWatchContext.class); Mockito.when(mockWoker.addTenantFuzzyWatcher(anyString(), anyString(), any())).thenReturn(context); nacosConfigService.fuzzyWatch(dataIdPattern, groupNamePattern, fuzzyWatchEventWatcher); Mockito.verify(mockWoker, Mockito.times(1)) .addTenantFuzzyWatcher(dataIdPattern, groupNamePattern, fuzzyWatchEventWatcher); Mockito.verify(context, Mockito.times(1)).createNewFuture(); } @Test void testFuzzyWatch3() throws NacosException { String groupNamePattern = "group"; String dataIdPattern = "dataId*"; String namespace = "public"; FuzzyWatchEventWatcher fuzzyWatchEventWatcher = new FuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } @Override public Executor getExecutor() { return null; } }; String patternKey = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupNamePattern, namespace); ConfigFuzzyWatchContext context = new ConfigFuzzyWatchContext("", patternKey); Mockito.when(mockWoker.addTenantFuzzyWatcher(anyString(), anyString(), any())).thenReturn(context); Future> setFuture = nacosConfigService.fuzzyWatchWithGroupKeys(groupNamePattern, fuzzyWatchEventWatcher); Mockito.verify(mockWoker, Mockito.times(1)) .addTenantFuzzyWatcher(ALL_PATTERN, groupNamePattern, fuzzyWatchEventWatcher); Assertions.assertNotNull(setFuture); } @Test void testFuzzyWatch4() throws NacosException { String groupNamePattern = "group"; String dataIdPattern = "dataId*"; String namespace = "public"; FuzzyWatchEventWatcher fuzzyWatchEventWatcher = new FuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } @Override public Executor getExecutor() { return null; } }; String patternKey = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, groupNamePattern, namespace); ConfigFuzzyWatchContext context = new ConfigFuzzyWatchContext("", patternKey); Mockito.when(mockWoker.addTenantFuzzyWatcher(anyString(), anyString(), any())).thenReturn(context); Future> setFuture = nacosConfigService.fuzzyWatchWithGroupKeys(dataIdPattern, groupNamePattern, fuzzyWatchEventWatcher); Mockito.verify(mockWoker, Mockito.times(1)) .addTenantFuzzyWatcher(dataIdPattern, groupNamePattern, fuzzyWatchEventWatcher); Assertions.assertNotNull(setFuture); } @Test void testShutDown() { Assertions.assertDoesNotThrow(() -> { nacosConfigService.shutDown(); }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/common/GroupKeyTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.common; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class GroupKeyTest { @Test void testGetKey() { assertEquals("1+foo", GroupKey.getKey("1", "foo")); assertEquals("1+foo+bar", GroupKey.getKey("1", "foo", "bar")); assertEquals("1+f%2Boo+b%25ar", GroupKey.getKey("1", "f+oo", "b%ar")); } @Test void testGetKeyTenant() { assertEquals("1+foo+bar", GroupKey.getKeyTenant("1", "foo", "bar")); } @Test void testParseKey() { assertArrayEquals(new String[] {"a", "f+oo", null}, GroupKey.parseKey("a+f%2Boo")); assertArrayEquals(new String[] {"b", "f%oo", null}, GroupKey.parseKey("b+f%25oo")); assertArrayEquals(new String[] {"a", "b", "c"}, GroupKey.parseKey("a+b+c")); } @Test void testParseKeyIllegalArgumentException1() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.parseKey(""); }); } @Test void testParseKeyIllegalArgumentException2() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.parseKey("f%oo"); }); } @Test void testParseKeyIllegalArgumentException3() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.parseKey("f+o+o+bar"); }); } @Test void testParseKeyIllegalArgumentException4() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.parseKey("f++bar"); }); } @Test void testGetKeyDatIdParam() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.getKey("", "a"); }); } @Test void testGetKeyGroupParam() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.getKey("a", ""); }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigContextTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ConfigContextTest { @Test void testParameter() { ConfigContext context = new ConfigContext(); String key = "key"; String v = "v"; context.setParameter(key, v); String actual = (String) context.getParameter(key); assertEquals(v, actual); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigEncryptionFilterTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.exception.NacosException; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; /** * ConfigCryptoFilterTest. * * @author lixiaoshuang */ @ExtendWith(MockitoExtension.class) class ConfigEncryptionFilterTest { private ConfigEncryptionFilter configEncryptionFilter; @Mock private ConfigRequest configRequest; @Mock private ConfigResponse configResponse; @Mock private IConfigFilterChain iConfigFilterChain; @BeforeEach void setUp() throws Exception { configEncryptionFilter = new ConfigEncryptionFilter(); } @Test void doFilter() throws NacosException { Mockito.when(configRequest.getDataId()).thenReturn("cipher-aes-test"); Mockito.when(configRequest.getContent()).thenReturn("nacos"); configEncryptionFilter.doFilter(configRequest, null, iConfigFilterChain); Mockito.verify(configRequest, Mockito.atLeast(1)).getDataId(); Mockito.verify(configRequest, Mockito.atLeast(1)).getContent(); Mockito.when(configResponse.getDataId()).thenReturn("test-dataid"); Mockito.when(configResponse.getContent()).thenReturn("nacos"); Mockito.when(configResponse.getEncryptedDataKey()).thenReturn("1234567890"); configEncryptionFilter.doFilter(null, configResponse, iConfigFilterChain); Mockito.verify(configResponse, Mockito.atLeast(1)).getDataId(); Mockito.verify(configResponse, Mockito.atLeast(1)).getContent(); Mockito.verify(configResponse, Mockito.atLeast(1)).getEncryptedDataKey(); } @Test void testGetOrder() { int order = configEncryptionFilter.getOrder(); assertEquals(0, order); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigEncryptionFilterTest1.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.encryption.EncryptionPluginManager; import com.alibaba.nacos.plugin.encryption.spi.EncryptionPluginService; 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.junit.jupiter.MockitoExtension; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.util.Base64; import static org.junit.jupiter.api.Assertions.assertEquals; /** * AES encryption algorithm testing dataId with prefix cipher. * * @Author shiwenyu * @Date 2023/12/23 9:45 PM * @Version 1.0 */ @ExtendWith(MockitoExtension.class) class ConfigEncryptionFilterTest1 { private ConfigEncryptionFilter configEncryptionFilter; private EncryptionPluginService mockEncryptionPluginService; @Mock private IConfigFilterChain iConfigFilterChain; @BeforeEach void setUp() throws Exception { mockEncryptionPluginService = new EncryptionPluginService() { private static final String ALGORITHM = "AES"; private static final String AES_PKCS5P = "AES/ECB/PKCS5Padding"; // 随机生成密钥-用来加密数据内容 private final String contentKey = generateKey(); // 随机生成密钥-用来加密密钥 private final String theKeyOfContentKey = generateKey(); private String generateKey() { SecureRandom secureRandom = new SecureRandom(); KeyGenerator keyGenerator; try { keyGenerator = KeyGenerator.getInstance(ALGORITHM); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } keyGenerator.init(128, secureRandom); SecretKey secretKey = keyGenerator.generateKey(); byte[] keyBytes = secretKey.getEncoded(); return Base64.getEncoder().encodeToString(keyBytes); } @Override public String encrypt(String secretKey, String content) { return Base64.getEncoder().encodeToString(aes(Cipher.ENCRYPT_MODE, content, secretKey)); } @Override public String decrypt(String secretKey, String content) { if (StringUtils.isBlank(secretKey)) { return null; } return aesDecrypt(content, secretKey); } @Override public String generateSecretKey() { return contentKey; } @Override public String algorithmName() { return ALGORITHM.toLowerCase(); } @Override public String encryptSecretKey(String secretKey) { return Base64.getEncoder().encodeToString(aes(Cipher.ENCRYPT_MODE, generateSecretKey(), theKeyOfContentKey)); } @Override public String decryptSecretKey(String secretKey) { if (StringUtils.isBlank(secretKey)) { return null; } return aesDecrypt(secretKey, theKeyOfContentKey); } private byte[] aes(int mode, String content, String key) { try { return aesBytes(mode, content.getBytes(StandardCharsets.UTF_8), key); } catch (Exception e) { throw new RuntimeException(e); } } private byte[] aesBytes(int mode, byte[] content, String key) { SecretKeySpec keySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), ALGORITHM); Cipher cipher = null; try { cipher = Cipher.getInstance(AES_PKCS5P); cipher.init(mode, keySpec); return cipher.doFinal(content); } catch (Exception e) { throw new RuntimeException(e); } } private String aesDecrypt(String content, String key) { byte[] bytes = aesBytes(Cipher.DECRYPT_MODE, Base64.getDecoder().decode(content), key); return new String(bytes, StandardCharsets.UTF_8); } }; EncryptionPluginManager.join(mockEncryptionPluginService); configEncryptionFilter = new ConfigEncryptionFilter(); } @Test void testDoFilterEncryptedData() throws NacosException { String dataId = "cipher-aes-test"; String content = "nacos"; final String encryptionContent = mockEncryptionPluginService.encrypt( mockEncryptionPluginService.generateSecretKey(), content); final String theKeyOfContentKey = mockEncryptionPluginService.encryptSecretKey( mockEncryptionPluginService.generateSecretKey()); ConfigRequest configRequest = new ConfigRequest(); configRequest.setDataId(dataId); configRequest.setContent(content); configEncryptionFilter.doFilter(configRequest, null, iConfigFilterChain); assertEquals(configRequest.getContent(), encryptionContent); assertEquals(configRequest.getEncryptedDataKey(), theKeyOfContentKey); ConfigResponse configResponse = new ConfigResponse(); configResponse.setDataId(dataId); configResponse.setContent(encryptionContent); configResponse.setEncryptedDataKey(theKeyOfContentKey); configEncryptionFilter.doFilter(null, configResponse, iConfigFilterChain); assertEquals(configResponse.getContent(), content); assertEquals(configResponse.getEncryptedDataKey(), mockEncryptionPluginService.generateSecretKey()); } @Test void testDoFilter() throws NacosException { String dataId = "test"; String content = "nacos"; ConfigRequest configRequest = new ConfigRequest(); configRequest.setDataId(dataId); configRequest.setContent(content); configEncryptionFilter.doFilter(configRequest, null, iConfigFilterChain); assertEquals(configRequest.getContent(), content); assertEquals("", configRequest.getEncryptedDataKey()); ConfigResponse configResponse = new ConfigResponse(); configResponse.setDataId(dataId); configResponse.setContent(content); configResponse.setEncryptedDataKey(""); configEncryptionFilter.doFilter(null, configResponse, iConfigFilterChain); assertEquals(configResponse.getContent(), content); assertEquals("", configResponse.getEncryptedDataKey()); } @Test void testGetOrder() { int order = configEncryptionFilter.getOrder(); assertEquals(0, order); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainManagerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigContext; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; class ConfigFilterChainManagerTest { @Test void testAddFilterOrder() throws NacosException { final ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(new Properties()); MyIConfigFilter filter1 = new MyIConfigFilter("filter1", 1); MyIConfigFilter filter2 = new MyIConfigFilter("filter2", 2); MyIConfigFilter filter3 = new MyIConfigFilter("filter3", 3); //random order configFilterChainManager.addFilter(filter2); configFilterChainManager.addFilter(filter1); configFilterChainManager.addFilter(filter3); ConfigRequest configRequest = new ConfigRequest(); configFilterChainManager.doFilter(configRequest, new ConfigResponse()); IConfigContext configContext = configRequest.getConfigContext(); // doFilter works assertEquals(1, configContext.getParameter("filter1")); assertEquals(2, configContext.getParameter("filter2")); assertEquals(3, configContext.getParameter("filter3")); //order List orders = (List) configContext.getParameter("orders"); assertEquals(Arrays.asList(1, 2, 3), orders); } @Test void testAddFilterNotRepeat() throws NacosException { final ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(new Properties()); MyIConfigFilter filter1 = new MyIConfigFilter("filter1", 1); MyIConfigFilter filter2 = new MyIConfigFilter("filter2", 2); MyIConfigFilter repeatFilter = new MyIConfigFilter("filter1", 1); configFilterChainManager.addFilter(filter2); configFilterChainManager.addFilter(filter1); configFilterChainManager.addFilter(repeatFilter); ConfigRequest configRequest = new ConfigRequest(); configFilterChainManager.doFilter(configRequest, new ConfigResponse()); IConfigContext configContext = configRequest.getConfigContext(); assertEquals(2, configContext.getParameter("filterCount")); } private static class MyIConfigFilter implements IConfigFilter { private String name; private int order; public MyIConfigFilter(String name, int order) { this.name = name; this.order = order; } @Override public void init(Properties properties) { } @Override public void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain) throws NacosException { IConfigContext configContext = request.getConfigContext(); // save filter info configContext.setParameter(name, order); // save filter order if (configContext.getParameter("orders") == null) { configContext.setParameter("orders", new ArrayList()); } List orders = (List) configContext.getParameter("orders"); orders.add(order); // save filter count if (configContext.getParameter("filterCount") == null) { configContext.setParameter("filterCount", 0); } Integer filterCount = (Integer) configContext.getParameter("filterCount"); filterCount = filterCount + 1; configContext.setParameter("filterCount", filterCount); // do next filterChain.doFilter(request, response); } @Override public int getOrder() { return order; } @Override public String getFilterName() { return name; } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigFilterChainTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ConfigFilterChainTest { @Test void testConfigFilterChain() { ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(null); configFilterChainManager.addFilter(new DemoFilter1()); configFilterChainManager.addFilter(new DemoFilter2()); ConfigRequest configRequest = new ConfigRequest(); ConfigResponse configResponse = new ConfigResponse(); try { configFilterChainManager.doFilter(configRequest, configResponse); assertEquals(DemoFilter1.class.getName(), configRequest.getParameter("filter1")); assertEquals(DemoFilter2.class.getName(), configRequest.getParameter("filter2")); } catch (NacosException e) { e.printStackTrace(); } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigRequestTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigContext; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class ConfigRequestTest { @Test void testGetterAndSetter() { ConfigRequest configRequest = new ConfigRequest(); String dataId = "id"; String group = "group"; String tenant = "n"; String content = "abc"; String type = "properties"; configRequest.setContent(content); configRequest.setDataId(dataId); configRequest.setGroup(group); configRequest.setTenant(tenant); configRequest.setType(type); assertEquals(dataId, configRequest.getDataId()); assertEquals(group, configRequest.getGroup()); assertEquals(tenant, configRequest.getTenant()); assertEquals(content, configRequest.getContent()); assertEquals(type, configRequest.getType()); } @Test void testGetParameter() { ConfigRequest configRequest = new ConfigRequest(); String dataId = "id"; String group = "group"; String tenant = "n"; String content = "abc"; configRequest.setContent(content); configRequest.setDataId(dataId); configRequest.setGroup(group); configRequest.setTenant(tenant); assertEquals(dataId, configRequest.getParameter("dataId")); assertEquals(group, configRequest.getParameter("group")); assertEquals(tenant, configRequest.getParameter("tenant")); assertEquals(content, configRequest.getParameter("content")); } @Test void testGetConfigContext() { ConfigRequest configRequest = new ConfigRequest(); IConfigContext configContext = configRequest.getConfigContext(); assertNotNull(configContext); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/ConfigResponseTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigContext; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class ConfigResponseTest { @Test void testGetterAndSetter() { ConfigResponse configResponse = new ConfigResponse(); String dataId = "id"; String group = "group"; String tenant = "n"; String content = "abc"; String type = "yaml"; configResponse.setContent(content); configResponse.setDataId(dataId); configResponse.setGroup(group); configResponse.setTenant(tenant); configResponse.setConfigType(type); assertEquals(dataId, configResponse.getDataId()); assertEquals(group, configResponse.getGroup()); assertEquals(tenant, configResponse.getTenant()); assertEquals(content, configResponse.getContent()); assertEquals(type, configResponse.getConfigType()); } @Test void getParameter() { ConfigResponse configResponse = new ConfigResponse(); String dataId = "id"; String group = "group"; String tenant = "n"; String content = "abc"; String custom = "custom"; configResponse.setContent(content); configResponse.setDataId(dataId); configResponse.setGroup(group); configResponse.setTenant(tenant); configResponse.putParameter(custom, custom); assertEquals(dataId, configResponse.getParameter("dataId")); assertEquals(group, configResponse.getParameter("group")); assertEquals(tenant, configResponse.getParameter("tenant")); assertEquals(content, configResponse.getParameter("content")); assertEquals(custom, configResponse.getParameter("custom")); } @Test void getConfigContext() { ConfigResponse configResponse = new ConfigResponse(); IConfigContext configContext = configResponse.getConfigContext(); assertNotNull(configContext); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter1.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; public class DemoFilter1 implements IConfigFilter { private static final String DEFAULT_NAME = DemoFilter1.class.getName(); @Override public void init(Properties properties) { } @Override public void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain) throws NacosException { request.putParameter("filter1", DEFAULT_NAME); filterChain.doFilter(request, response); } @Override public int getOrder() { return 0; } @Override public String getFilterName() { return DEFAULT_NAME; } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/filter/impl/DemoFilter2.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.filter.impl; import com.alibaba.nacos.api.config.filter.IConfigFilter; import com.alibaba.nacos.api.config.filter.IConfigFilterChain; import com.alibaba.nacos.api.config.filter.IConfigRequest; import com.alibaba.nacos.api.config.filter.IConfigResponse; import com.alibaba.nacos.api.exception.NacosException; import java.util.Properties; public class DemoFilter2 implements IConfigFilter { private static final String DEFAULT_NAME = DemoFilter2.class.getName(); @Override public void init(Properties properties) { } @Override public void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain) throws NacosException { request.putParameter("filter2", DEFAULT_NAME); filterChain.doFilter(request, response); } @Override public int getOrder() { return 0; } @Override public String getFilterName() { return DEFAULT_NAME; } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/http/MetricsHttpAgentTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.http; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.param.Header; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class MetricsHttpAgentTest { @Test void testGetter() { String name = "name"; String encode = "UTF-8"; String tenant = "aaa"; String namespace = "aaa"; final HttpAgent mockHttpAgent = new MockHttpAgent(name, encode, tenant, namespace); final MetricsHttpAgent metricsHttpAgent = new MetricsHttpAgent(mockHttpAgent); assertEquals(name, metricsHttpAgent.getName()); assertEquals(encode, metricsHttpAgent.getEncode()); assertEquals(tenant, metricsHttpAgent.getTenant()); assertEquals(namespace, metricsHttpAgent.getNamespace()); } @Test void testLifeCycle() throws NacosException { String name = "name"; String encode = "UTF-8"; String tenant = "aaa"; String namespace = "aaa"; final MockHttpAgent mockHttpAgent = new MockHttpAgent(name, encode, tenant, namespace); final MetricsHttpAgent metricsHttpAgent = new MetricsHttpAgent(mockHttpAgent); metricsHttpAgent.start(); assertTrue(mockHttpAgent.isStart()); metricsHttpAgent.shutdown(); assertTrue(mockHttpAgent.isShutdown()); } @Test void testHttpMethod() throws Exception { String name = "name"; String encode = "UTF-8"; String tenant = "aaa"; String namespace = "aaa"; final MockHttpAgent mockHttpAgent = new MockHttpAgent(name, encode, tenant, namespace); final MetricsHttpAgent metricsHttpAgent = new MetricsHttpAgent(mockHttpAgent); final HttpRestResult result1 = metricsHttpAgent.httpGet("/aa", new HashMap(), new HashMap(), "UTF-8", 1L); assertEquals("get /aa", result1.getMessage()); final HttpRestResult result2 = metricsHttpAgent.httpPost("/aa", new HashMap(), new HashMap(), "UTF-8", 1L); assertEquals("post /aa", result2.getMessage()); final HttpRestResult result3 = metricsHttpAgent.httpDelete("/aa", new HashMap(), new HashMap(), "UTF-8", 1L); assertEquals("delete /aa", result3.getMessage()); } private static class MockHttpAgent implements HttpAgent { private String name; private String encode; private String tenant; private String namespace; private boolean start = false; private boolean shutdown = false; public MockHttpAgent(String name, String encode, String tenant, String namespace) { this.name = name; this.encode = encode; this.tenant = tenant; this.namespace = namespace; } @Override public void start() throws NacosException { start = true; } @Override public HttpRestResult httpGet(String path, Map headers, Map paramValues, String encoding, long readTimeoutMs) throws Exception { return new HttpRestResult(Header.newInstance(), 200, "get", "get " + path); } @Override public HttpRestResult httpPost(String path, Map headers, Map paramValues, String encoding, long readTimeoutMs) throws Exception { return new HttpRestResult(Header.newInstance(), 200, "post", "post " + path); } @Override public HttpRestResult httpDelete(String path, Map headers, Map paramValues, String encoding, long readTimeoutMs) throws Exception { return new HttpRestResult(Header.newInstance(), 200, "delete", "delete " + path); } @Override public String getName() { return name; } @Override public String getNamespace() { return namespace; } @Override public String getTenant() { return tenant; } @Override public String getEncode() { return encode; } @Override public void shutdown() throws NacosException { shutdown = true; } public boolean isStart() { return start; } public boolean isShutdown() { return shutdown; } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/http/ServerHttpAgentTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.http; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.impl.ConfigServerListManager; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; 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.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; import java.net.ConnectException; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.util.Collections; import java.util.Iterator; import java.util.Map; import java.util.Properties; 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.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove strictness lenient @MockitoSettings(strictness = Strictness.LENIENT) class ServerHttpAgentTest { private static final String SERVER_ADDRESS_1 = "http://1.1.1.1:8848"; private static final String SERVER_ADDRESS_2 = "http://2.2.2.2:8848"; @Mock ConfigServerListManager serverListManager; NacosRestTemplate cachedNacosRestTemplate; @Mock HttpRestResult mockResult; @Mock Iterator mockIterator; @Mock NacosRestTemplate nacosRestTemplate; ServerHttpAgent serverHttpAgent; HttpRestResult httpRestResult; @BeforeEach void setUp() throws Exception { serverHttpAgent = new ServerHttpAgent(serverListManager, new Properties()); injectRestTemplate(); when(serverListManager.getCurrentServer()).thenReturn(SERVER_ADDRESS_1); when(serverListManager.getIterator()).thenReturn(mockIterator); when(serverListManager.getServerList()).thenReturn(Collections.singletonList(SERVER_ADDRESS_2)); when(mockIterator.next()).thenReturn(SERVER_ADDRESS_2); Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); cachedNacosRestTemplate = restMap.get( "com.alibaba.nacos.client.config.impl.ConfigHttpClientManager$ConfigHttpClientFactory"); restMap.put("com.alibaba.nacos.client.config.impl.ConfigHttpClientManager$ConfigHttpClientFactory", nacosRestTemplate); httpRestResult = new HttpRestResult(); httpRestResult.setData("127.0.0.1:8848"); httpRestResult.setCode(200); Mockito.when(nacosRestTemplate.get(contains("aaa:8080"), any(), any(), any())).thenReturn(httpRestResult); } private void injectRestTemplate() throws NoSuchFieldException, IllegalAccessException { Field restTemplateField = ServerHttpAgent.class.getDeclaredField("nacosRestTemplate"); restTemplateField.setAccessible(true); restTemplateField.set(serverHttpAgent, nacosRestTemplate); } @AfterEach void tearDown() throws NacosException, NoSuchFieldException, IllegalAccessException { serverHttpAgent.shutdown(); if (null != cachedNacosRestTemplate) { Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); restMap.put("com.alibaba.nacos.client.config.impl.ConfigHttpClientManager$ConfigHttpClientFactory", cachedNacosRestTemplate); } } @Test void testConstruct() throws NacosException { NacosClientProperties mockedProperties = mock(NacosClientProperties.class); when(mockedProperties.getProperty(PropertyKeyConst.ENDPOINT)).thenReturn("aaa"); when(mockedProperties.derive()).thenReturn(mockedProperties); ConfigServerListManager server = new ConfigServerListManager(mockedProperties); final ServerHttpAgent serverHttpAgent1 = new ServerHttpAgent(server); assertNotNull(serverHttpAgent1); final ServerHttpAgent serverHttpAgent2 = new ServerHttpAgent(server, new Properties()); assertNotNull(serverHttpAgent2); final Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "1.1.1.1"); final ServerHttpAgent serverHttpAgent3 = new ServerHttpAgent(properties); assertNotNull(serverHttpAgent3); } @Test void testGetterAndSetter() throws Exception { NacosClientProperties mockedProperties = mock(NacosClientProperties.class); when(mockedProperties.getProperty(PropertyKeyConst.ENDPOINT)).thenReturn("aaa"); when(mockedProperties.getProperty(PropertyKeyConst.NAMESPACE)).thenReturn("namespace1"); when(mockedProperties.getProperty(PropertyKeyConst.ENDPOINT_REFRESH_INTERVAL_SECONDS, "30")).thenReturn("30"); when(mockedProperties.derive()).thenReturn(mockedProperties); ConfigServerListManager server = new ConfigServerListManager(mockedProperties); server.start(); final ServerHttpAgent serverHttpAgent = new ServerHttpAgent(server, new Properties()); final String appname = ServerHttpAgent.getAppname(); //set by AppNameUtils, init in ParamUtils static block assertEquals("unknown", appname); final String encode = serverHttpAgent.getEncode(); final String namespace = serverHttpAgent.getNamespace(); final String tenant = serverHttpAgent.getTenant(); final String name = serverHttpAgent.getName(); assertNull(encode); assertEquals("namespace1", namespace); assertEquals("namespace1", tenant); assertEquals("Config-custom-aaa_8080_nacos_serverlist_namespace1", name); server.shutdown(); } @Test void testLifCycle() throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "aaa"); ConfigServerListManager server = Mockito.mock(ConfigServerListManager.class); final ServerHttpAgent serverHttpAgent = new ServerHttpAgent(server, properties); serverHttpAgent.start(); Mockito.verify(server).start(); Assertions.assertDoesNotThrow(() -> { serverHttpAgent.shutdown(); }); } @Test void testHttpGetSuccess() throws Exception { when(nacosRestTemplate.get(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_OK); HttpRestResult actual = serverHttpAgent.httpGet("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); assertEquals(mockResult, actual); } @Test void testHttpGetFailed() throws Exception { assertThrows(ConnectException.class, () -> { when(nacosRestTemplate.get(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); serverHttpAgent.httpGet("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); }); } @Test void testHttpWithRequestException() throws Exception { assertThrows(NacosException.class, () -> { when(nacosRestTemplate.get(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenThrow(new ConnectException(), new SocketTimeoutException(), new NacosException()); serverHttpAgent.httpGet("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); }); } @Test void testRetryWithNewServer() throws Exception { when(mockIterator.hasNext()).thenReturn(true); when(nacosRestTemplate.get(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenThrow(new ConnectException()); when(nacosRestTemplate.get(eq(SERVER_ADDRESS_2 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_OK); HttpRestResult actual = serverHttpAgent.httpGet("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); assertEquals(mockResult, actual); } @Test void testRetryTimeout() throws Exception { assertThrows(ConnectException.class, () -> { when(nacosRestTemplate.get(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenThrow(new SocketTimeoutException()); serverHttpAgent.httpGet("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 0); }); } @Test void testHttpPostSuccess() throws Exception { when(nacosRestTemplate.postForm(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), anyMap(), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_OK); HttpRestResult actual = serverHttpAgent.httpPost("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); assertEquals(mockResult, actual); } @Test void testHttpPostFailed() throws Exception { assertThrows(ConnectException.class, () -> { when(nacosRestTemplate.postForm(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), anyMap(), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); serverHttpAgent.httpPost("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); }); } @Test void testHttpPostWithRequestException() throws Exception { assertThrows(NacosException.class, () -> { when(nacosRestTemplate.postForm(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), anyMap(), eq(String.class))).thenThrow(new ConnectException(), new SocketTimeoutException(), new NacosException()); serverHttpAgent.httpPost("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); }); } @Test void testRetryPostWithNewServer() throws Exception { when(mockIterator.hasNext()).thenReturn(true); when(nacosRestTemplate.postForm(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), anyMap(), eq(String.class))).thenThrow(new ConnectException()); when(nacosRestTemplate.postForm(eq(SERVER_ADDRESS_2 + "/test"), any(HttpClientConfig.class), any(Header.class), anyMap(), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_OK); HttpRestResult actual = serverHttpAgent.httpPost("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); assertEquals(mockResult, actual); } @Test void testRetryPostTimeout() throws Exception { assertThrows(ConnectException.class, () -> { when(nacosRestTemplate.postForm(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), anyMap(), eq(String.class))).thenThrow(new SocketTimeoutException()); serverHttpAgent.httpPost("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 0); }); } @Test void testHttpDeleteSuccess() throws Exception { when(nacosRestTemplate.delete(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_OK); HttpRestResult actual = serverHttpAgent.httpDelete("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); assertEquals(mockResult, actual); } @Test void testHttpDeleteFailed() throws Exception { assertThrows(ConnectException.class, () -> { when(nacosRestTemplate.delete(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_NOT_FOUND); serverHttpAgent.httpDelete("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); }); } @Test void testHttpDeleteWithRequestException() throws Exception { assertThrows(NacosException.class, () -> { when(nacosRestTemplate.delete(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenThrow(new ConnectException(), new SocketTimeoutException(), new NacosException()); serverHttpAgent.httpDelete("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); }); } @Test void testRetryDeleteWithNewServer() throws Exception { when(mockIterator.hasNext()).thenReturn(true); when(nacosRestTemplate.delete(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenThrow(new ConnectException()); when(nacosRestTemplate.delete(eq(SERVER_ADDRESS_2 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenReturn(mockResult); when(mockResult.getCode()).thenReturn(HttpURLConnection.HTTP_OK); HttpRestResult actual = serverHttpAgent.httpDelete("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 1000); assertEquals(mockResult, actual); } @Test void testRetryDeleteTimeout() throws Exception { assertThrows(ConnectException.class, () -> { when(nacosRestTemplate.delete(eq(SERVER_ADDRESS_1 + "/test"), any(HttpClientConfig.class), any(Header.class), any(Query.class), eq(String.class))).thenThrow(new SocketTimeoutException()); serverHttpAgent.httpDelete("/test", Collections.emptyMap(), Collections.emptyMap(), "UTF-8", 0); }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/CacheDataTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeEvent; import com.alibaba.nacos.api.config.PropertyChangeType; import com.alibaba.nacos.api.config.listener.AbstractSharedListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.listener.impl.AbstractConfigChangeListener; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.MD5Utils; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Properties; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; 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; class CacheDataTest { @Test void testConstructorAndEquals() { ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); final CacheData cacheData1 = new CacheData(filter, "name1", "key", "group", "tenant"); assertEquals("CacheData [key, group]", cacheData1.toString()); final CacheData cacheData2 = new CacheData(filter, "name2", "key", "group"); assertEquals(cacheData1, cacheData2); assertEquals(cacheData1.hashCode(), cacheData2.hashCode()); final CacheData cacheData3 = new CacheData(filter, "name2", "key3", "group", "tenant"); assertNotEquals(cacheData1, cacheData3); } @Test void testGetter() { ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); final CacheData cacheData1 = new CacheData(filter, "name1", "key", "group", "tenant"); assertTrue(cacheData1.isInitializing()); assertNull(cacheData1.getContent()); assertEquals(0, cacheData1.getTaskId()); assertFalse(cacheData1.isConsistentWithServer()); assertFalse(cacheData1.isUseLocalConfigInfo()); assertEquals(0, cacheData1.getLastModifiedTs().intValue()); assertEquals(0, cacheData1.getLocalConfigInfoVersion()); cacheData1.setInitializing(false); cacheData1.setContent("123"); cacheData1.setTaskId(123); cacheData1.setConsistentWithServer(true); cacheData1.setType("123"); long timeStamp = new Date().getTime(); cacheData1.setLastModifiedTs(timeStamp); cacheData1.setUseLocalConfigInfo(true); cacheData1.setLocalConfigInfoVersion(timeStamp); assertFalse(cacheData1.isInitializing()); assertEquals("123", cacheData1.getContent()); assertEquals(MD5Utils.md5Hex("123", "UTF-8"), cacheData1.getMd5()); assertEquals(123, cacheData1.getTaskId()); assertTrue(cacheData1.isConsistentWithServer()); assertEquals("123", cacheData1.getType()); assertTrue(cacheData1.isUseLocalConfigInfo()); assertEquals(timeStamp, cacheData1.getLastModifiedTs().longValue()); assertEquals(timeStamp, cacheData1.getLocalConfigInfoVersion()); } @Test void testNotifyWarnTimeout() { System.setProperty("nacos.listener.notify.warn.timeout", "5000"); long notifyWarnTimeout = CacheData.initNotifyWarnTimeout(); assertEquals(5000, notifyWarnTimeout); System.setProperty("nacos.listener.notify.warn.timeout", "1bf000abc"); long notifyWarnTimeout2 = CacheData.initNotifyWarnTimeout(); assertEquals(60000, notifyWarnTimeout2); } @Test void testListener() throws NacosException { ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); final CacheData cacheData1 = new CacheData(filter, "name1", "key", "group", "tenant"); Listener listener = new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { } }; cacheData1.addListener(listener); assertEquals(1, cacheData1.getListeners().size()); assertEquals(listener, cacheData1.getListeners().get(0)); cacheData1.removeListener(listener); assertEquals(0, cacheData1.getListeners().size()); } @Test void testCheckListenerMd5() throws NacosException { ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); final CacheData data = new CacheData(filter, "name1", "key", "group", "tenant"); final List list = new ArrayList<>(); Listener listener = new Listener() { @Override public Executor getExecutor() { return Runnable::run; } @Override public void receiveConfigInfo(String configInfo) { list.add(configInfo); } }; data.addListener(listener); data.checkListenerMd5(); assertTrue(data.checkListenersMd5Consistent()); assertEquals(0, list.size()); data.setContent("new"); assertFalse(data.checkListenersMd5Consistent()); data.checkListenerMd5(); assertEquals(1, list.size()); assertEquals("new", list.get(0)); } @Test void testCheckListenerMd5NotifyTimeouts() throws NacosException { System.setProperty("nacos.listener.notify.warn.timeout", "1000"); long notifyWarnTimeout = CacheData.initNotifyWarnTimeout(); assertEquals(1000, notifyWarnTimeout); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); final CacheData data = new CacheData(filter, "name1", "keytimeouts", "group", "tenant"); Listener listener = new Listener() { @Override public Executor getExecutor() { return Runnable::run; } @Override public void receiveConfigInfo(String configInfo) { try { Thread.sleep(11000); } catch (InterruptedException e) { throw new RuntimeException(e); } } }; AtomicReference dataIdNotifyTimeouts = new AtomicReference(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ChangeNotifyBlockEvent changeNotifyBlockEvent = (ChangeNotifyBlockEvent) event; dataIdNotifyTimeouts.set(changeNotifyBlockEvent.getDataId()); System.out.println("timeout:" + changeNotifyBlockEvent.getDataId()); } @Override public Class subscribeType() { return ChangeNotifyBlockEvent.class; } }); data.addListener(listener); data.setContent("new"); data.checkListenerMd5(); assertTrue(data.checkListenersMd5Consistent()); assertEquals("keytimeouts", dataIdNotifyTimeouts.get()); } @Test void testAbstractSharedListener() throws NacosException { ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); final CacheData data = new CacheData(filter, "name1", "keyshare", "group", "tenant"); final String[] dataIdReceive = new String[1]; final String[] groupReceive = new String[1]; final String[] contentReceive = new String[1]; Listener listener = new AbstractSharedListener() { @Override public Executor getExecutor() { return Runnable::run; } @Override public void innerReceive(String dataId, String group, String configInfo) { dataIdReceive[0] = dataId; groupReceive[0] = group; contentReceive[0] = configInfo; } }; data.addListener(listener); String content = "content" + System.currentTimeMillis(); data.setContent(content); data.checkListenerMd5(); assertTrue(data.checkListenersMd5Consistent()); assertEquals("keyshare", dataIdReceive[0]); assertEquals("group", groupReceive[0]); assertEquals(contentReceive[0], content); } @Test void testAbstractConfigChangeListener() throws NacosException { ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); final CacheData data = new CacheData(filter, "name1", "keyshare", "group", "tenant"); data.setType("properties"); data.setContent("a=a\nb=b\nc=c"); AtomicReference changeItemReceived = new AtomicReference<>(); Listener listener = new AbstractConfigChangeListener() { @Override public void receiveConfigChange(ConfigChangeEvent event) { changeItemReceived.set(event); } @Override public Executor getExecutor() { return Runnable::run; } }; data.addListener(listener); String content = "b=b\nc=abc\nd=d"; data.setContent(content); data.checkListenerMd5(); assertTrue(data.checkListenersMd5Consistent()); assertEquals(PropertyChangeType.DELETED, changeItemReceived.get().getChangeItem("a").getType()); assertEquals(PropertyChangeType.MODIFIED, changeItemReceived.get().getChangeItem("c").getType()); assertEquals(PropertyChangeType.ADDED, changeItemReceived.get().getChangeItem("d").getType()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/ClientWorkerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.listener.AbstractListener; import com.alibaba.nacos.api.config.listener.Listener; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; 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.request.ConfigRemoveRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager; import com.alibaba.nacos.client.config.filter.impl.ConfigResponse; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientFactory; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.fasterxml.jackson.databind.JsonNode; import io.prometheus.client.Gauge; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static com.alibaba.nacos.api.annotation.NacosProperties.NAMESPACE; 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.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class ClientWorkerTest { private static final String TEST_NAMESPACE = "TEST_NAMESPACE"; MockedStatic rpcClientFactoryMockedStatic; MockedStatic localConfigInfoProcessorMockedStatic; @Mock RpcClient rpcClient; private ClientWorker clientWorker; private ClientWorker clientWorkerSpy; @BeforeEach void before() throws Exception { rpcClientFactoryMockedStatic = Mockito.mockStatic(RpcClientFactory.class); rpcClientFactoryMockedStatic.when(() -> RpcClientFactory.createClient(anyString(), any(ConnectionType.class), any(GrpcClientConfig.class))).thenReturn(rpcClient); rpcClientFactoryMockedStatic.when(() -> RpcClientFactory.createClient(anyString(), any(ConnectionType.class), any(GrpcClientConfig.class))).thenReturn(rpcClient); localConfigInfoProcessorMockedStatic = Mockito.mockStatic(LocalConfigInfoProcessor.class); Properties properties = new Properties(); properties.put(PropertyKeyConst.NAMESPACE, TEST_NAMESPACE); ConfigFilterChainManager filter = new ConfigFilterChainManager(properties); ConfigServerListManager serverListManager = mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); try { clientWorker = new ClientWorker(filter, serverListManager, nacosClientProperties); } catch (NacosException e) { throw new RuntimeException(e); } clientWorkerSpy = Mockito.spy(clientWorker); } @AfterEach void after() { rpcClientFactoryMockedStatic.close(); localConfigInfoProcessorMockedStatic.close(); } @Test void testConstruct() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); assertNotNull(clientWorker); } @Test void testAddListenerWithoutTenant() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); String dataId = "a"; String group = "b"; Listener listener = new AbstractListener() { @Override public void receiveConfigInfo(String configInfo) { } }; clientWorker.addListeners(dataId, group, Collections.singletonList(listener)); List listeners = clientWorker.getCache(dataId, group).getListeners(); assertEquals(1, listeners.size()); assertEquals(listener, listeners.get(0)); clientWorker.removeListener(dataId, group, listener); listeners = clientWorker.getCache(dataId, group).getListeners(); assertEquals(0, listeners.size()); CacheData cacheData = clientWorker.addCacheDataIfAbsent(dataId, group); assertEquals(cacheData, clientWorker.getCache(dataId, group)); } @Test void testListenerWithTenant() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); Listener listener = new AbstractListener() { @Override public void receiveConfigInfo(String configInfo) { } }; String dataId = "a"; String group = "b"; clientWorker.addTenantListeners(dataId, group, Collections.singletonList(listener)); List listeners = clientWorker.getCache(dataId, group).getListeners(); assertEquals(1, listeners.size()); assertEquals(listener, listeners.get(0)); clientWorker.removeTenantListener(dataId, group, listener); listeners = clientWorker.getCache(dataId, group).getListeners(); assertEquals(0, listeners.size()); String content = "d"; clientWorker.addTenantListenersWithContent(dataId, group, content, null, Collections.singletonList(listener)); listeners = clientWorker.getCache(dataId, group).getListeners(); assertEquals(1, listeners.size()); assertEquals(listener, listeners.get(0)); clientWorker.removeTenantListener(dataId, group, listener); listeners = clientWorker.getCache(dataId, group).getListeners(); assertEquals(0, listeners.size()); String tenant = "c"; CacheData cacheData = clientWorker.addCacheDataIfAbsent(dataId, group, tenant); assertEquals(cacheData, clientWorker.getCache(dataId, group, tenant)); clientWorker.removeCache(dataId, group, tenant); assertNull(clientWorker.getCache(dataId, group, tenant)); } @Test void testPublishConfigSuccess() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); String dataId = "a"; String group = "b"; String tenant = "c"; String content = "d"; String appName = "app"; String tag = "tag"; String betaIps = "1.1.1.1"; String casMd5 = "1111"; String type = "properties"; Mockito.when(rpcClient.request(any(ConfigPublishRequest.class))).thenReturn(new ConfigPublishResponse()); boolean b = clientWorker.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, null, casMd5, type); assertTrue(b); } @Test void testPublishConfigFail() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); String dataId = "a"; String group = "b"; String tenant = "c"; String content = "d"; String appName = "app"; String tag = "tag"; String betaIps = "1.1.1.1"; String casMd5 = "1111"; String type = "properties"; Mockito.when(rpcClient.request(any(ConfigPublishRequest.class))) .thenReturn(ConfigPublishResponse.buildFailResponse(503, "over limit")); boolean b = clientWorker.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, null, casMd5, type); assertFalse(b); } @Test void testPublishConfigException() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); String dataId = "a"; String group = "b"; String tenant = "c"; String content = "d"; String appName = "app"; String tag = "tag"; String betaIps = "1.1.1.1"; String casMd5 = "1111"; String type = "properties"; Mockito.when(rpcClient.request(any(ConfigPublishRequest.class))).thenThrow(new NacosException()); boolean b = clientWorker.publishConfig(dataId, group, tenant, appName, tag, betaIps, content, null, casMd5, type); assertFalse(b); } @Test void testRemoveConfig() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); String dataId = "a"; String group = "b"; String tenant = "c"; String tag = "tag"; try { Mockito.when(rpcClient.request(any(ConfigRemoveRequest.class))) .thenThrow(new NacosException(503, "overlimit")); clientWorker.removeConfig(dataId, group, tenant, tag); fail(); } catch (NacosException e) { assertEquals("overlimit", e.getErrMsg()); assertEquals(503, e.getErrCode()); } } @Test void testGeConfigConfigSuccess() throws NacosException { Properties prop = new Properties(); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(null, agent, nacosClientProperties); String dataId = "a"; String group = "b"; String tenant = "c"; String content = "content" + System.currentTimeMillis(); Mockito.when(rpcClient.request(any(ConfigQueryRequest.class), anyLong())) .thenReturn(ConfigQueryResponse.buildSuccessResponse(content)); ConfigResponse configResponse = clientWorker.getServerConfig(dataId, group, tenant, 100, true); assertEquals(content, configResponse.getContent()); localConfigInfoProcessorMockedStatic.verify( () -> LocalConfigInfoProcessor.saveSnapshot(eq(clientWorker.getAgentName()), eq(dataId), eq(group), eq(tenant), eq(content)), times(1)); } @Test void testHandleConfigChangeReqeust() throws Exception { Properties prop = new Properties(); String tenant = "c"; prop.put(NAMESPACE, tenant); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(null, agent, nacosClientProperties); AtomicReference> cacheMapMocked = Mockito.mock(AtomicReference.class); Field cacheMap = ClientWorker.class.getDeclaredField("cacheMap"); cacheMap.setAccessible(true); cacheMap.set(clientWorker, cacheMapMocked); Map cacheDataMapMocked = Mockito.mock(Map.class); Mockito.when(cacheMapMocked.get()).thenReturn(cacheDataMapMocked); CacheData cacheDataMocked = Mockito.mock(CacheData.class); AtomicBoolean atomicBoolean = Mockito.mock(AtomicBoolean.class); Mockito.when(cacheDataMocked.getReceiveNotifyChanged()).thenReturn(atomicBoolean); String dataId = "a"; String group = "b"; Mockito.when(cacheDataMapMocked.get(GroupKey.getKeyTenant(dataId, group, tenant))).thenReturn(cacheDataMocked); ConfigChangeNotifyRequest configChangeNotifyRequest = ConfigChangeNotifyRequest.build(dataId, group, tenant); ((ClientWorker.ConfigRpcTransportClient) clientWorker.getAgent()).handleConfigChangeNotifyRequest( configChangeNotifyRequest, "testname"); Mockito.verify(cacheDataMocked, times(1)).setConsistentWithServer(false); Mockito.verify(atomicBoolean, times(1)).set(true); } @Test void testHandleClientMetricsReqeust() throws Exception { Properties prop = new Properties(); String tenant = "c"; prop.put(NAMESPACE, tenant); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(null, agent, nacosClientProperties); AtomicReference> cacheMapMocked = Mockito.mock(AtomicReference.class); Field cacheMap = ClientWorker.class.getDeclaredField("cacheMap"); cacheMap.setAccessible(true); cacheMap.set(clientWorker, cacheMapMocked); Map cacheDataMapMocked = Mockito.mock(Map.class); Mockito.when(cacheMapMocked.get()).thenReturn(cacheDataMapMocked); CacheData cacheDataMocked = Mockito.mock(CacheData.class); String content = "content1324567"; String md5 = MD5Utils.md5Hex(content, "UTF-8"); Mockito.when(cacheDataMocked.getContent()).thenReturn(content); Mockito.when(cacheDataMocked.getMd5()).thenReturn(md5); Field uuid1 = ClientWorker.class.getDeclaredField("uuid"); uuid1.setAccessible(true); String uuid = (String) uuid1.get(clientWorker); String dataId = "a23456789"; String group = "b"; Mockito.when(cacheDataMapMocked.get(GroupKey.getKeyTenant(dataId, group, tenant))).thenReturn(cacheDataMocked); ClientConfigMetricRequest configMetricsRequest = new ClientConfigMetricRequest(); configMetricsRequest.setMetricsKeys(Arrays.asList( ClientConfigMetricRequest.MetricsKey.build(ClientConfigMetricRequest.MetricsKey.CACHE_DATA, GroupKey.getKeyTenant(dataId, group, tenant)), ClientConfigMetricRequest.MetricsKey.build(ClientConfigMetricRequest.MetricsKey.SNAPSHOT_DATA, GroupKey.getKeyTenant(dataId, group, tenant)))); ClientConfigMetricResponse metricResponse = ((ClientWorker.ConfigRpcTransportClient) clientWorker.getAgent()).handleClientMetricsRequest( configMetricsRequest); JsonNode jsonNode = JacksonUtils.toObj(metricResponse.getMetrics().get(uuid).toString()); String metricValues = jsonNode.get("metricValues") .get(ClientConfigMetricRequest.MetricsKey.build(ClientConfigMetricRequest.MetricsKey.CACHE_DATA, GroupKey.getKeyTenant(dataId, group, tenant)).toString()).textValue(); int colonIndex = metricValues.lastIndexOf(":"); assertEquals(content, metricValues.substring(0, colonIndex)); assertEquals(md5, metricValues.substring(colonIndex + 1, metricValues.length())); } @Test void testGeConfigConfigNotFound() throws NacosException { Properties prop = new Properties(); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(null, agent, nacosClientProperties); String dataId = "a"; String group = "b"; String tenant = "c"; ConfigQueryResponse configQueryResponse = new ConfigQueryResponse(); configQueryResponse.setErrorInfo(ConfigQueryResponse.CONFIG_NOT_FOUND, "config not found"); Mockito.when(rpcClient.request(any(ConfigQueryRequest.class), anyLong())).thenReturn(configQueryResponse); ConfigResponse configResponse = clientWorker.getServerConfig(dataId, group, tenant, 100, true); assertNull(configResponse.getContent()); localConfigInfoProcessorMockedStatic.verify( () -> LocalConfigInfoProcessor.saveSnapshot(eq(clientWorker.getAgentName()), eq(dataId), eq(group), eq(tenant), eq(null)), times(1)); } @Test void testGeConfigConfigConflict() throws NacosException { Properties prop = new Properties(); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(null, agent, nacosClientProperties); String dataId = "a"; String group = "b"; String tenant = "c"; ConfigQueryResponse configQueryResponse = new ConfigQueryResponse(); configQueryResponse.setErrorInfo(ConfigQueryResponse.CONFIG_QUERY_CONFLICT, "config is being modified"); Mockito.when(rpcClient.request(any(ConfigQueryRequest.class), anyLong())).thenReturn(configQueryResponse); try { clientWorker.getServerConfig(dataId, group, tenant, 100, true); fail(); } catch (NacosException e) { assertEquals(NacosException.CONFLICT, e.getErrCode()); } } @Test void testShutdown() throws NacosException, NoSuchFieldException, IllegalAccessException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); clientWorker.shutdown(); Field agent1 = ClientWorker.class.getDeclaredField("agent"); agent1.setAccessible(true); ConfigTransportClient o = (ConfigTransportClient) agent1.get(clientWorker); assertTrue(o.getExecutor().isShutdown()); agent1.setAccessible(false); assertNull(clientWorker.getAgentName()); } @Test void testExecuteConfigListen() throws Exception { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); Mockito.when(agent.getName()).thenReturn("mocktest"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); clientWorker.shutdown(); List cacheDatas = new ArrayList<>(); String group = "group123"; String tenant = "tenant122324"; //mock discards cache String dataIdDiscard = "dataIdDiscard" + System.currentTimeMillis(); CacheData cacheDataDiscard = discardCache(filter, agent.getName(), dataIdDiscard, group, tenant); cacheDatas.add(cacheDataDiscard); //mock use local cache String dataIdUseLocalCache = "dataIdUseLocalCache" + System.currentTimeMillis(); CacheData cacheUseLocalCache = useLocalCache(filter, agent.getName(), dataIdUseLocalCache, group, tenant, "content" + System.currentTimeMillis()); assertFalse(cacheUseLocalCache.isUseLocalConfigInfo()); cacheDatas.add(cacheUseLocalCache); //mock normal cache String dataIdNormal = "dataIdNormal" + System.currentTimeMillis(); CacheData cacheNormal = normalNotConsistentCache(filter, agent.getName(), dataIdNormal, group, tenant); AtomicReference normalContent = new AtomicReference<>(); cacheNormal.addListener(new Listener() { @Override public Executor getExecutor() { return null; } @Override public void receiveConfigInfo(String configInfo) { System.out.println(configInfo); normalContent.set(configInfo); } }); cacheDatas.add(cacheNormal); cacheNormal.setInitializing(false); Map cacheDataMapMocked = Mockito.mock(Map.class); Mockito.when(cacheDataMapMocked.get(GroupKey.getKeyTenant(dataIdNormal, group, tenant))) .thenReturn(cacheNormal); Mockito.when(cacheDataMapMocked.containsKey(GroupKey.getKeyTenant(dataIdNormal, group, tenant))) .thenReturn(true); Mockito.when(cacheDataMapMocked.values()).thenReturn(cacheDatas); AtomicReference> cacheMapMocked = Mockito.mock(AtomicReference.class); Mockito.when(cacheMapMocked.get()).thenReturn(cacheDataMapMocked); Field cacheMap = ClientWorker.class.getDeclaredField("cacheMap"); cacheMap.setAccessible(true); cacheMap.set(clientWorker, cacheMapMocked); //mock request ConfigChangeBatchListenResponse.ConfigContext configContext = new ConfigChangeBatchListenResponse.ConfigContext(); configContext.setDataId(dataIdNormal); configContext.setGroup(group); configContext.setTenant(tenant); ConfigChangeBatchListenResponse response = new ConfigChangeBatchListenResponse(); response.setChangedConfigs(Collections.singletonList(configContext)); RpcClient rpcClientInner = Mockito.mock(RpcClient.class); Mockito.when(rpcClientInner.isWaitInitiated()).thenReturn(true, false); rpcClientFactoryMockedStatic.when(() -> RpcClientFactory.createClient(anyString(), any(ConnectionType.class), any(GrpcClientConfig.class))).thenReturn(rpcClientInner); // mock listen and remove listen request Mockito.when(rpcClientInner.request(any(ConfigBatchListenRequest.class))).thenReturn(response, response); // mock query changed config ConfigQueryResponse configQueryResponse = new ConfigQueryResponse(); configQueryResponse.setContent("content" + System.currentTimeMillis()); configQueryResponse.setContentType(ConfigType.JSON.getType()); Mockito.when(rpcClientInner.request(any(ConfigQueryRequest.class))).thenReturn(configQueryResponse); (clientWorker.getAgent()).executeConfigListen(); //assert //use local cache. assertTrue(cacheUseLocalCache.isUseLocalConfigInfo()); //discard cache to be deleted. assertFalse(cacheMapMocked.get().containsKey(GroupKey.getKeyTenant(dataIdDiscard, group, tenant))); //normal cache listener be notified. assertEquals(configQueryResponse.getContent(), normalContent.get()); } private CacheData discardCache(ConfigFilterChainManager filter, String envName, String dataId, String group, String tenant) { CacheData cacheData = new CacheData(filter, envName, dataId, group, tenant); cacheData.setDiscard(true); cacheData.setConsistentWithServer(false); File file = Mockito.mock(File.class); Mockito.when(file.exists()).thenReturn(false); localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getFailoverFile(envName, dataId, group, tenant)).thenReturn(file); return cacheData; } private CacheData normalNotConsistentCache(ConfigFilterChainManager filter, String envName, String dataId, String group, String tenant) throws NacosException { CacheData cacheData = new CacheData(filter, envName, dataId, group, tenant); cacheData.setDiscard(false); cacheData.setConsistentWithServer(false); File file = Mockito.mock(File.class); Mockito.when(file.exists()).thenReturn(false); localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getFailoverFile(envName, dataId, group, tenant)).thenReturn(file); return cacheData; } private CacheData useLocalCache(ConfigFilterChainManager filter, String envName, String dataId, String group, String tenant, String failOverContent) { CacheData cacheData = new CacheData(filter, envName, dataId, group, tenant); cacheData.setDiscard(true); File file = Mockito.mock(File.class); Mockito.when(file.exists()).thenReturn(true); localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getFailoverFile(envName, dataId, group, tenant)).thenReturn(file); localConfigInfoProcessorMockedStatic.when( () -> LocalConfigInfoProcessor.getFailover(envName, dataId, group, tenant)).thenReturn(failOverContent); return cacheData; } @Test void testIsHealthServer() throws NacosException, NoSuchFieldException, IllegalAccessException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); ClientWorker.ConfigRpcTransportClient client = Mockito.mock(ClientWorker.ConfigRpcTransportClient.class); Mockito.when(client.isHealthServer()).thenReturn(Boolean.TRUE); Field declaredField = ClientWorker.class.getDeclaredField("agent"); declaredField.setAccessible(true); declaredField.set(clientWorker, client); assertTrue(clientWorker.isHealthServer()); Mockito.when(client.isHealthServer()).thenReturn(Boolean.FALSE); assertFalse(clientWorker.isHealthServer()); } @Test void testPutCache() throws Exception { // 反射调用私有方法putCacheIfAbsent Method putCacheMethod = ClientWorker.class.getDeclaredMethod("putCache", String.class, CacheData.class); putCacheMethod.setAccessible(true); Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); String key = "testKey"; CacheData cacheData = new CacheData(filter, "env", "dataId", "group"); putCacheMethod.invoke(clientWorker, key, cacheData); Field cacheMapField = ClientWorker.class.getDeclaredField("cacheMap"); cacheMapField.setAccessible(true); AtomicReference> cacheMapRef = (AtomicReference>) cacheMapField.get( clientWorker); // 检查cacheMap是否包含特定的key assertNotNull(cacheMapRef.get().get(key)); assertEquals(cacheData, cacheMapRef.get().get(key)); // 测试再次插入相同的key将覆盖原始的值 CacheData newCacheData = new CacheData(filter, "newEnv", "newDataId", "newGroup"); putCacheMethod.invoke(clientWorker, key, newCacheData); // 检查key对应的value是否改变为newCacheData assertEquals(newCacheData, cacheMapRef.get().get(key)); } @Test void testAddListenersEnsureCacheDataSafe() throws NacosException, IllegalAccessException, NoSuchFieldException { String dataId = "testDataId"; String group = "testGroup"; // 将key-cacheData插入到cacheMap中 CacheData cacheData = new CacheData(null, "env", dataId, group); Field cacheMapField = ClientWorker.class.getDeclaredField("cacheMap"); cacheMapField.setAccessible(true); AtomicReference> cacheMapRef = (AtomicReference>) cacheMapField.get( clientWorker); String key = GroupKey.getKey(dataId, group); cacheMapRef.get().put(key, cacheData); // 当addCacheDataIfAbsent得到的differentCacheData,同cacheMap中该key对应的cacheData不一致 CacheData differentCacheData = new CacheData(null, "env", dataId, group); doReturn(differentCacheData).when(clientWorkerSpy).addCacheDataIfAbsent(anyString(), anyString()); // 使用addListeners将differentCacheData插入到cacheMap中 clientWorkerSpy.addListeners(dataId, group, Collections.EMPTY_LIST); CacheData cacheDataFromCache1 = clientWorker.getCache(dataId, group); assertNotNull(cacheDataFromCache1); assertEquals(cacheDataFromCache1, differentCacheData); assertFalse(cacheDataFromCache1.isDiscard()); assertFalse(cacheDataFromCache1.isConsistentWithServer()); // 再次调用addListeners,此时addCacheDataIfAbsent得到的cacheData同cacheMap中该key对应的cacheData一致,均为differentCacheData clientWorkerSpy.addListeners(dataId, group, Collections.EMPTY_LIST); CacheData cacheDataFromCache2 = clientWorker.getCache(dataId, group); assertNotNull(cacheDataFromCache2); assertEquals(cacheDataFromCache2, differentCacheData); assertFalse(cacheDataFromCache2.isDiscard()); assertFalse(cacheDataFromCache2.isConsistentWithServer()); } @Test void testAddTenantListenersEnsureCacheDataSafe() throws NacosException, IllegalAccessException, NoSuchFieldException { String dataId = "testDataId"; String group = "testGroup"; // 将key-cacheData插入到cacheMap中 CacheData cacheData = new CacheData(null, "env", dataId, group); Field cacheMapField = ClientWorker.class.getDeclaredField("cacheMap"); cacheMapField.setAccessible(true); AtomicReference> cacheMapRef = (AtomicReference>) cacheMapField.get( clientWorker); String key = GroupKey.getKeyTenant(dataId, group, TEST_NAMESPACE); cacheMapRef.get().put(key, cacheData); // 当addCacheDataIfAbsent得到的differentCacheData,同cacheMap中该key对应的cacheData不一致 CacheData differentCacheData = new CacheData(null, "env", dataId, group); doReturn(differentCacheData).when(clientWorkerSpy) .addCacheDataIfAbsent(anyString(), anyString(), eq(TEST_NAMESPACE)); // 使用addListeners将differentCacheData插入到cacheMap中 clientWorkerSpy.addTenantListeners(dataId, group, Collections.EMPTY_LIST); CacheData cacheDataFromCache1 = clientWorker.getCache(dataId, group, TEST_NAMESPACE); assertNotNull(cacheDataFromCache1); assertEquals(cacheDataFromCache1, differentCacheData); assertFalse(cacheDataFromCache1.isDiscard()); assertFalse(cacheDataFromCache1.isConsistentWithServer()); // 再次调用addListeners,此时addCacheDataIfAbsent得到的cacheData同cacheMap中该key对应的cacheData一致,均为differentCacheData clientWorkerSpy.addTenantListeners(dataId, group, Collections.EMPTY_LIST); CacheData cacheDataFromCache2 = clientWorker.getCache(dataId, group, TEST_NAMESPACE); assertNotNull(cacheDataFromCache2); assertEquals(cacheDataFromCache2, differentCacheData); assertFalse(cacheDataFromCache2.isDiscard()); assertFalse(cacheDataFromCache2.isConsistentWithServer()); } @Test void testAddTenantListenersWithContentEnsureCacheDataSafe() throws NacosException, IllegalAccessException, NoSuchFieldException { String dataId = "testDataId"; String group = "testGroup"; // 将key-cacheData插入到cacheMap中 CacheData cacheData = new CacheData(null, "env", dataId, group); Field cacheMapField = ClientWorker.class.getDeclaredField("cacheMap"); cacheMapField.setAccessible(true); AtomicReference> cacheMapRef = (AtomicReference>) cacheMapField.get( clientWorker); String key = GroupKey.getKeyTenant(dataId, group, TEST_NAMESPACE); cacheMapRef.get().put(key, cacheData); // 当addCacheDataIfAbsent得到的differentCacheData,同cacheMap中该key对应的cacheData不一致 CacheData differentCacheData = new CacheData(null, "env", dataId, group); doReturn(differentCacheData).when(clientWorkerSpy) .addCacheDataIfAbsent(anyString(), anyString(), eq(TEST_NAMESPACE)); // 使用addListeners将differentCacheData插入到cacheMap中 clientWorkerSpy.addTenantListenersWithContent(dataId, group, "", "", Collections.EMPTY_LIST); CacheData cacheDataFromCache1 = clientWorker.getCache(dataId, group, TEST_NAMESPACE); assertNotNull(cacheDataFromCache1); assertEquals(cacheDataFromCache1, differentCacheData); assertFalse(cacheDataFromCache1.isDiscard()); assertFalse(cacheDataFromCache1.isConsistentWithServer()); // 再次调用addListeners,此时addCacheDataIfAbsent得到的cacheData同cacheMap中该key对应的cacheData一致,均为differentCacheData clientWorkerSpy.addTenantListenersWithContent(dataId, group, "", "", Collections.EMPTY_LIST); CacheData cacheDataFromCache2 = clientWorker.getCache(dataId, group, TEST_NAMESPACE); assertNotNull(cacheDataFromCache2); assertEquals(cacheDataFromCache2, differentCacheData); assertFalse(cacheDataFromCache2.isDiscard()); assertFalse(cacheDataFromCache2.isConsistentWithServer()); } @Test void testResponse403() throws NacosException { Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = Mockito.mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); final ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); ConfigRemoveResponse response = ConfigRemoveResponse.buildFailResponse("accessToken invalid"); response.setErrorCode(ConfigQueryResponse.NO_RIGHT); Mockito.when(rpcClient.request(any(ConfigRemoveRequest.class))).thenReturn(response); boolean result = clientWorker.removeConfig("a", "b", "c", "tag"); assertFalse(result); } @Test void testRemoveCacheWithMetricsEnabled() throws Exception { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; Properties prop = new Properties(); prop.put("enableClientMetrics", "true"); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); final ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getListenConfigCountMonitor).thenReturn(mockGaugeChild); clientWorker.removeCache(dataId, group, tenant); verify(mockGaugeChild, times(1)).set(0); } } @Test void testRemoveCacheWithMetricsDisabled() throws Exception { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; Properties prop = new Properties(); prop.put(PropertyKeyConst.ENABLE_CLIENT_METRICS, "false"); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); final ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); clientWorkerSpy = Mockito.spy(clientWorker); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getListenConfigCountMonitor).thenReturn(mockGaugeChild); clientWorker.removeCache(dataId, group, tenant); verify(mockGaugeChild, times(0)).set(0); } } @Test void testRemoveCacheWithDefaultClientMetricsEnabled() throws Exception { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); final ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getListenConfigCountMonitor).thenReturn(mockGaugeChild); clientWorker.removeCache(dataId, group, tenant); verify(mockGaugeChild, times(1)).set(0); } } @Test void testMetricsMonitorSetThrowsException() throws NacosException { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; Properties prop = new Properties(); prop.put(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true"); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = mock(ConfigServerListManager.class); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); final ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); clientWorkerSpy = Mockito.spy(clientWorker); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getListenConfigCountMonitor).thenReturn(mockGaugeChild); RuntimeException exception = new RuntimeException("Mocked exception"); doThrow(exception).when(mockGaugeChild).set(0); assertDoesNotThrow(() -> clientWorker.removeCache(dataId, group, tenant)); } } @Test public void testAddCacheDataIfAbsentEnableClientMetricsTrue() throws NacosException { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; Properties prop = new Properties(); prop.put(PropertyKeyConst.ENABLE_CLIENT_METRICS, "true"); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = mock(ConfigServerListManager.class); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getListenConfigCountMonitor).thenReturn(mockGaugeChild); clientWorker.addCacheDataIfAbsent(dataId, group, tenant); verify(mockGaugeChild, times(1)).set(1); } } @Test public void testAddCacheDataIfAbsentEnableClientMetricsFalse() throws NacosException { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; Properties prop = new Properties(); prop.put(PropertyKeyConst.ENABLE_CLIENT_METRICS, "false"); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = mock(ConfigServerListManager.class); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { clientWorker.addCacheDataIfAbsent(dataId, group, tenant); mockedMetricsMonitor.verify(MetricsMonitor::getListenConfigCountMonitor, never()); } } @Test public void testAddCacheDataIfAbsentEnableClientMetricsNotSet() throws NacosException { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; Properties prop = new Properties(); ConfigFilterChainManager filter = new ConfigFilterChainManager(new Properties()); ConfigServerListManager agent = mock(ConfigServerListManager.class); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); ClientWorker clientWorker = new ClientWorker(filter, agent, nacosClientProperties); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getListenConfigCountMonitor).thenReturn(mockGaugeChild); clientWorker.addCacheDataIfAbsent(dataId, group, tenant); verify(mockGaugeChild, times(1)).set(1); } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigChangeHandlerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeItem; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; class ConfigChangeHandlerTest { @Test void testParseProperties() throws IOException { Map properties = ConfigChangeHandler.getInstance().parseChangeData("", "app.name = nacos", "properties"); assertEquals("nacos", ((ConfigChangeItem) properties.get("app.name")).getNewValue()); } @Test void testParseYaml() throws IOException { Map properties = ConfigChangeHandler.getInstance().parseChangeData("", "app:\n name: nacos", "yaml"); assertEquals("nacos", ((ConfigChangeItem) properties.get("app.name")).getNewValue()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigFuzzyWatchGroupKeyHolderTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.config.listener.AbstractFuzzyWatchEventWatcher; import com.alibaba.nacos.api.config.listener.ConfigFuzzyWatchChangeEvent; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.client.config.common.GroupKey; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; 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.ArgumentMatchers; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class ConfigFuzzyWatchGroupKeyHolderTest { ConfigFuzzyWatchGroupKeyHolder configFuzzyWatchGroupKeyHolder; @Mock ClientWorker.ConfigRpcTransportClient rpcTransportClient; String clientId = "conn" + System.currentTimeMillis(); String tenant = "t1"; @BeforeEach void before() { configFuzzyWatchGroupKeyHolder = new ConfigFuzzyWatchGroupKeyHolder(rpcTransportClient, clientId); doReturn(true).when(rpcTransportClient).isAbilitySupportedByServer(AbilityKey.SERVER_FUZZY_WATCH); } @AfterEach void after() { } @Test void testRegisterFuzzyWatcherAndNotify() throws InterruptedException { when(rpcTransportClient.getTenant()).thenReturn(tenant); String dataId = "dataId"; String group = "group"; AtomicInteger watcher1Flag = new AtomicInteger(0); AtomicInteger watcher2Flag = new AtomicInteger(0); configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataId + "*", group, new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { watcher1Flag.incrementAndGet(); } }); configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher(dataId + "*", group, new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { watcher2Flag.incrementAndGet(); } }); String groupKey1 = GroupKey.getKeyTenant(dataId + 1, group, tenant); //build init notify add Set contexts = new HashSet<>(); contexts.add(ConfigFuzzyWatchSyncRequest.Context.build(groupKey1, ADD_CONFIG)); String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataId + "*", group, tenant); ConfigFuzzyWatchSyncRequest initNotifyRequest = ConfigFuzzyWatchSyncRequest.buildSyncRequest( FUZZY_WATCH_INIT_NOTIFY, contexts, groupKeyPattern, 1, 1); configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest(initNotifyRequest); //check watcher notified Thread.sleep(100L); System.out.println(watcher1Flag.get()); Assertions.assertTrue(watcher1Flag.get() == 1); //build change notify add String changedGroupKey2Add = GroupKey.getKeyTenant(dataId + 2, group, tenant); ConfigFuzzyWatchChangeNotifyRequest changedNotifyRequest = new ConfigFuzzyWatchChangeNotifyRequest( changedGroupKey2Add, ADD_CONFIG); configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest(changedNotifyRequest); //check watcher notified Thread.sleep(100L); Assertions.assertTrue(watcher1Flag.get() == 2); //check not complete future timeout ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.getFuzzyListenContext( dataId + "*", group); Future> newFutureNotFinish = configFuzzyWatchContext.createNewFuture(); try { newFutureNotFinish.get(1000L, TimeUnit.MILLISECONDS); Assertions.assertFalse(true); } catch (TimeoutException e) { Assertions.assertTrue(true); } catch (Throwable throwable) { Assertions.assertFalse(true); } // build init finish notify ConfigFuzzyWatchSyncRequest configFuzzyWatchSyncRequest = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( groupKeyPattern); configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest(configFuzzyWatchSyncRequest); //check a completed future. Future> newFutureFinish = configFuzzyWatchContext.createNewFuture(); try { Set groupKeys = newFutureFinish.get(10L, TimeUnit.MILLISECONDS); Assertions.assertTrue( groupKeys != null && groupKeys.contains(groupKey1) && groupKeys.contains(changedGroupKey2Add)); } catch (Exception e) { Assertions.assertTrue(false); } //check watcher notified delete Thread.sleep(100L); Assertions.assertTrue(watcher1Flag.get() == 2); Assertions.assertTrue(watcher1Flag.get() == 2); //build change notify String changedGroupKey2Delete = changedGroupKey2Add; ConfigFuzzyWatchChangeNotifyRequest changedNotifyRequestDelete = new ConfigFuzzyWatchChangeNotifyRequest( changedGroupKey2Delete, DELETE_CONFIG); configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest(changedNotifyRequestDelete); //check watcher notified delete Thread.sleep(100L); Assertions.assertTrue(watcher1Flag.get() == 3); Assertions.assertTrue(watcher1Flag.get() == 3); Future> newFuture = configFuzzyWatchContext.createNewFuture(); try { Set groupKeys = newFuture.get(10L, TimeUnit.MILLISECONDS); Assertions.assertTrue( groupKeys != null && groupKeys.contains(groupKey1) && !groupKeys.contains(changedGroupKey2Delete)); } catch (Exception e) { Assertions.assertTrue(false); } //build sync delete String groupKey1Delete = groupKey1; //build init notify add Set contextsDelete = new HashSet<>(); contextsDelete.add(ConfigFuzzyWatchSyncRequest.Context.build(groupKey1Delete, DELETE_CONFIG)); ConfigFuzzyWatchSyncRequest deleteNotifyRequest = ConfigFuzzyWatchSyncRequest.buildSyncRequest( FUZZY_WATCH_DIFF_SYNC_NOTIFY, contextsDelete, groupKeyPattern, 1, 1); configFuzzyWatchGroupKeyHolder.handleFuzzyWatchSyncNotifyRequest(deleteNotifyRequest); //check watcher notified delete Thread.sleep(100L); Assertions.assertTrue(watcher1Flag.get() == 4); Assertions.assertTrue(watcher1Flag.get() == 4); Future> newFutureEmpty = configFuzzyWatchContext.createNewFuture(); try { Set groupKeys = newFutureEmpty.get(10L, TimeUnit.MILLISECONDS); Assertions.assertTrue(CollectionUtils.isEmpty(groupKeys)); } catch (Exception e) { Assertions.assertTrue(false); } configFuzzyWatchGroupKeyHolder.resetConsistenceStatus(); Assertions.assertFalse(configFuzzyWatchContext.isConsistentWithServer()); } @Test void testExecuteConfigFuzzyListen() throws NacosException { when(rpcTransportClient.getTenant()).thenReturn(tenant); ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("da1*", "group*", new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } }); configFuzzyWatchContext.setConsistentWithServer(true); configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("da2*", "group*", new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } }); configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("da3*", "group*", new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } }); RpcClient rpcClient = Mockito.mock(RpcClient.class); when(rpcTransportClient.ensureRpcClient(eq("0"))).thenReturn(rpcClient); ThreadPoolExecutor scheduledExecutorService = Mockito.mock(ThreadPoolExecutor.class); when(rpcTransportClient.getExecutor()).thenReturn(scheduledExecutorService); when(scheduledExecutorService.submit(any(Runnable.class))).thenReturn(Mockito.mock(Future.class)); configFuzzyWatchGroupKeyHolder.executeConfigFuzzyListen(); verify(scheduledExecutorService, times(2)).submit(ArgumentMatchers.any(Runnable.class)); } @Test void testExecuteFuzzyWatchRequestNormal() throws NacosException { reset(rpcTransportClient); String envName = "name"; String groupKeyPattern = "pattern"; ConfigFuzzyWatchContext configFuzzyWatchContext = new ConfigFuzzyWatchContext(envName, groupKeyPattern); configFuzzyWatchContext.refreshOverLimitTs(); RpcClient rpcClient = Mockito.mock(RpcClient.class); when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( new ConfigFuzzyWatchResponse()); configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest(configFuzzyWatchContext, rpcClient); Assertions.assertTrue(!configFuzzyWatchContext.patternLimitSuppressed()); Assertions.assertTrue(configFuzzyWatchContext.isConsistentWithServer()); } @Test void testExecuteFuzzyWatchRequestRemove() throws NacosException { AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } }; configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("*", "*", abstractFuzzyWatchEventWatcher); configFuzzyWatchGroupKeyHolder.removeFuzzyWatcher("*", "*", abstractFuzzyWatchEventWatcher); RpcClient rpcClient = Mockito.mock(RpcClient.class); when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( new ConfigFuzzyWatchResponse()); configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest( configFuzzyWatchGroupKeyHolder.getFuzzyListenContext("*", "*"), rpcClient); Assertions.assertTrue(configFuzzyWatchGroupKeyHolder.getFuzzyListenContext("*", "*") == null); } @Test void testExecuteFuzzyWatchRequestOverLoad() throws NacosException, InterruptedException { AtomicBoolean patternOvrFlag = new AtomicBoolean(false); AtomicBoolean configCountOverFlag = new AtomicBoolean(false); AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } @Override public void onPatternOverLimit() { patternOvrFlag.set(true); } @Override public void onConfigReachUpLimit() { configCountOverFlag.set(true); } }; ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("*", "*", abstractFuzzyWatchEventWatcher); RpcClient rpcClient = Mockito.mock(RpcClient.class); //test pattern over load ConfigFuzzyWatchResponse overloadResponse = new ConfigFuzzyWatchResponse(); overloadResponse.setErrorInfo(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( overloadResponse); configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest(configFuzzyWatchContext, rpcClient); Thread.sleep(100L); Assertions.assertTrue(configFuzzyWatchContext.patternLimitSuppressed()); Assertions.assertTrue(!configFuzzyWatchContext.isConsistentWithServer()); Assertions.assertTrue(patternOvrFlag.get()); Assertions.assertFalse(configCountOverFlag.get()); configFuzzyWatchContext.clearOverLimitTs(); ConfigFuzzyWatchResponse countOverloadResponse = new ConfigFuzzyWatchResponse(); countOverloadResponse.setErrorInfo(FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getMsg()); when(rpcTransportClient.requestProxy(eq(rpcClient), any(ConfigFuzzyWatchRequest.class))).thenReturn( countOverloadResponse); configFuzzyWatchGroupKeyHolder.executeFuzzyWatchRequest(configFuzzyWatchContext, rpcClient); Thread.sleep(100L); Assertions.assertTrue(configFuzzyWatchContext.patternLimitSuppressed()); Assertions.assertTrue(!configFuzzyWatchContext.isConsistentWithServer()); Assertions.assertTrue(configCountOverFlag.get()); } @Test void testSyncWhenWatcherFail() throws NacosException, InterruptedException { when(rpcTransportClient.getTenant()).thenReturn(tenant); String groupKey = GroupKey.getKeyTenant("dataIdName124", "group", tenant); AtomicInteger watcherFlag = new AtomicInteger(0); AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { int get = watcherFlag.incrementAndGet(); if (get < 2) { System.out.println("times " + get + " fail"); throw new RuntimeException("mock exception"); } else { System.out.println("times " + get + " success"); } } }; ConfigFuzzyWatchContext configFuzzyWatchContext = configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher( "dataIdName*", "group", abstractFuzzyWatchEventWatcher); ConfigFuzzyWatchChangeNotifyRequest configFuzzyWatchChangeNotifyRequest = new ConfigFuzzyWatchChangeNotifyRequest( groupKey, ADD_CONFIG); configFuzzyWatchGroupKeyHolder.handlerFuzzyWatchChangeNotifyRequest(configFuzzyWatchChangeNotifyRequest); TimeUnit.MILLISECONDS.sleep(100L); //notify 1, fail configFuzzyWatchContext.syncFuzzyWatchers(); //notify 2,success configFuzzyWatchContext.syncFuzzyWatchers(); //notify 3 +, will not trigger watchers. configFuzzyWatchContext.syncFuzzyWatchers(); configFuzzyWatchContext.syncFuzzyWatchers(); configFuzzyWatchContext.syncFuzzyWatchers(); //expect 2 times notified Assertions.assertEquals(2, watcherFlag.get()); } @Test void testFuzzyWatchNotSupport() { when(rpcTransportClient.isAbilitySupportedByServer(AbilityKey.SERVER_FUZZY_WATCH)).thenReturn(false); Assertions.assertThrows(NacosRuntimeException.class, () -> { configFuzzyWatchGroupKeyHolder.registerFuzzyWatcher("dataIdName*", "group", new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(ConfigFuzzyWatchChangeEvent event) { } }); }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigHttpClientManagerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class ConfigHttpClientManagerTest { @Test void test() { final ConfigHttpClientManager instance1 = ConfigHttpClientManager.getInstance(); final ConfigHttpClientManager instance2 = ConfigHttpClientManager.getInstance(); assertEquals(instance1, instance2); final NacosRestTemplate nacosRestTemplate = instance1.getNacosRestTemplate(); assertNotNull(nacosRestTemplate); final int time1 = instance1.getConnectTimeoutOrDefault(10); assertEquals(1000, time1); final int time2 = instance1.getConnectTimeoutOrDefault(2000); assertEquals(2000, time2); Assertions.assertDoesNotThrow(() -> { instance1.shutdown(); }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/ConfigServerListManagerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.address.AbstractServerListProvider; import com.alibaba.nacos.client.address.EndpointServerListProvider; import com.alibaba.nacos.client.address.ServerListProvider; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class ConfigServerListManagerTest { @Mock NacosRestTemplate nacosRestTemplate; NacosRestTemplate cachedNacosRestTemplate; HttpRestResult httpRestResult; @BeforeEach void setUp() throws Exception { Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); cachedNacosRestTemplate = restMap.get( "com.alibaba.nacos.client.config.impl.ConfigHttpClientManager$ConfigHttpClientFactory"); restMap.put("com.alibaba.nacos.client.config.impl.ConfigHttpClientManager$ConfigHttpClientFactory", nacosRestTemplate); httpRestResult = new HttpRestResult<>(); httpRestResult.setData("127.0.0.1:8848"); httpRestResult.setCode(200); when(nacosRestTemplate.get(contains("1.1.1.1:9090"), any(), any(), any())).thenReturn(httpRestResult); } @AfterEach void tearDown() throws Exception { if (null != cachedNacosRestTemplate) { Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); restMap.put("com.alibaba.nacos.client.config.impl.ConfigHttpClientManager$ConfigHttpClientFactory", cachedNacosRestTemplate); } } @Test void testStart() throws NacosException { NacosClientProperties mockedProperties = mock(NacosClientProperties.class); when(mockedProperties.getProperty(PropertyKeyConst.ENDPOINT)).thenReturn("1.1.1.1"); when(mockedProperties.getProperty(PropertyKeyConst.ENDPOINT_PORT)).thenReturn("9090"); when(mockedProperties.getProperty(PropertyKeyConst.ENDPOINT_REFRESH_INTERVAL_SECONDS, "30")).thenReturn("30"); when(mockedProperties.derive()).thenReturn(mockedProperties); final ConfigServerListManager mgr = new ConfigServerListManager(mockedProperties); try { mgr.start(); assertEquals("Config-custom-1.1.1.1_9090_nacos_serverlist", mgr.getName()); } finally { mgr.shutdown(); } } @Test void testStartWithCustomServerName() throws NacosException { NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.SERVER_NAME, "test"); properties.setProperty(PropertyKeyConst.SERVER_ADDR, "1.1.1.1"); final ConfigServerListManager mgr = new ConfigServerListManager(properties); try { mgr.start(); assertEquals("test", mgr.getName()); } finally { mgr.shutdown(); } } @Test void testGetter() throws NacosException { { NacosClientProperties mockedProperties = mock(NacosClientProperties.class); when(mockedProperties.getProperty(PropertyKeyConst.SERVER_ADDR)).thenReturn("1.1.1.1"); when(mockedProperties.getProperty(PropertyKeyConst.NAMESPACE)).thenReturn("namespace"); when(mockedProperties.derive()).thenReturn(mockedProperties); final ConfigServerListManager mgr = new ConfigServerListManager(mockedProperties); mgr.start(); assertEquals("nacos", mgr.getContextPath()); assertEquals("Config-fixed-namespace-1.1.1.1_8848", mgr.getName()); assertEquals("namespace", mgr.getTenant()); assertEquals("namespace", mgr.getNamespace()); assertEquals("Config-fixed-namespace-1.1.1.1_8848", mgr.getServerName()); } { Properties properties = new Properties(); properties.put(PropertyKeyConst.CONTEXT_PATH, "aaa"); properties.put(PropertyKeyConst.ENDPOINT, "1.1.1.1"); properties.put(PropertyKeyConst.ENDPOINT_PORT, "9090"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); final ConfigServerListManager mgr2 = new ConfigServerListManager(nacosClientProperties); mgr2.start(); assertEquals("aaa", mgr2.getContextPath()); } // Test https { Properties properties = new Properties(); properties.put(PropertyKeyConst.CONTEXT_PATH, "aaa"); properties.put(PropertyKeyConst.SERVER_ADDR, "https://1.1.1.1:8848"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); final ConfigServerListManager mgr2 = new ConfigServerListManager(nacosClientProperties); mgr2.start(); assertEquals("aaa", mgr2.getContextPath()); assertEquals("[https://1.1.1.1:8848]", mgr2.getServerList().toString()); } { Properties properties2 = new Properties(); properties2.put(PropertyKeyConst.CONTEXT_PATH, "aaa"); properties2.put(PropertyKeyConst.SERVER_ADDR, "1.1.1.1:8848"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties2); final ConfigServerListManager mgr3 = new ConfigServerListManager(nacosClientProperties); mgr3.start(); assertEquals(1, mgr3.getServerList().size()); assertEquals("1.1.1.1:8848", mgr3.getServerList().get(0)); assertEquals("[1.1.1.1:8848]", mgr3.getUrlString()); assertTrue(mgr3.contain("1.1.1.1:8848")); assertEquals("ServerManager-Config-fixed-1.1.1.1_8848-[1.1.1.1:8848]", mgr3.toString()); } { Properties properties3 = new Properties(); properties3.put(PropertyKeyConst.CONTEXT_PATH, "aaa"); properties3.put(PropertyKeyConst.SERVER_ADDR, "1.1.1.1:8848,2.2.2.2:8848"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties3); final ConfigServerListManager mgr4 = new ConfigServerListManager(nacosClientProperties); mgr4.start(); assertEquals(2, mgr4.getServerList().size()); assertEquals("1.1.1.1:8848", mgr4.getServerList().get(0)); assertEquals("2.2.2.2:8848", mgr4.getServerList().get(1)); assertTrue(mgr4.contain("1.1.1.1:8848")); assertEquals("ServerManager-Config-fixed-1.1.1.1_8848-2.2.2.2_8848-[1.1.1.1:8848, 2.2.2.2:8848]", mgr4.toString()); } { Properties properties4 = new Properties(); properties4.put(PropertyKeyConst.CONTEXT_PATH, "aaa"); properties4.put(PropertyKeyConst.SERVER_ADDR, "1.1.1.1:8848;2.2.2.2:8848"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties4); final ConfigServerListManager mgr5 = new ConfigServerListManager(nacosClientProperties); mgr5.start(); assertEquals(2, mgr5.getServerList().size()); assertEquals("1.1.1.1:8848", mgr5.getServerList().get(0)); assertEquals("2.2.2.2:8848", mgr5.getServerList().get(1)); assertTrue(mgr5.contain("1.1.1.1:8848")); assertEquals("ServerManager-Config-fixed-1.1.1.1_8848-2.2.2.2_8848-[1.1.1.1:8848, 2.2.2.2:8848]", mgr5.toString()); } } @Test void testIterator() throws NacosException { NacosClientProperties mockedProperties = mock(NacosClientProperties.class); when(mockedProperties.getProperty(PropertyKeyConst.SERVER_ADDR)).thenReturn("1.1.1.1:8848"); when(mockedProperties.getProperty(PropertyKeyConst.NAMESPACE)).thenReturn("aaa"); when(mockedProperties.derive()).thenReturn(mockedProperties); final ConfigServerListManager mgr = new ConfigServerListManager(mockedProperties); mgr.start(); // new iterator final Iterator it = mgr.iterator(); assertTrue(it.hasNext()); assertEquals("1.1.1.1:8848", it.next()); Iterator initIterator = mgr.getIterator(); assertNotNull(initIterator); mgr.refreshCurrentServerAddr(); assertNotNull(mgr.getIterator()); assertNotEquals(initIterator, mgr.getIterator()); final String currentServerAddr = mgr.getCurrentServer(); assertEquals("1.1.1.1:8848", currentServerAddr); final String nextServerAddr = mgr.genNextServer(); assertEquals("1.1.1.1:8848", nextServerAddr); final Iterator iterator1 = mgr.iterator(); assertTrue(iterator1.hasNext()); } @Test void testAddressServerBaseServerAddrsStr() throws NacosException { Properties properties = new Properties(); String serverAddrStr = "nacos.test.com:8080"; properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverAddrStr); String endpointContextPath = "/endpoint"; properties.setProperty(PropertyKeyConst.CONTEXT_PATH, endpointContextPath); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); assertEquals(1, serverListManager.getServerList().size()); assertTrue(serverListManager.getServerList().contains(serverAddrStr)); } @Test void testAddressServerBaseEndpoint() throws NacosException { Properties properties = new Properties(); String endpoint = "1.1.1.1"; properties.setProperty(PropertyKeyConst.ENDPOINT, endpoint); String endpointPort = "9090"; properties.setProperty(PropertyKeyConst.ENDPOINT_PORT, endpointPort); String endpointContextPath = "/endpoint"; properties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, endpointContextPath); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); assertTrue(serverListManager.getAddressSource() .startsWith(HTTP_PREFIX + endpoint + ":" + endpointPort + endpointContextPath)); } @Test void testInitParam() throws NacosException, NoSuchFieldException, IllegalAccessException { Properties properties = new Properties(); String endpoint = "1.1.1.1"; properties.setProperty(PropertyKeyConst.ENDPOINT, endpoint); String endpointPort = "9090"; properties.setProperty(PropertyKeyConst.ENDPOINT_PORT, endpointPort); String endpointContextPath = "/endpointContextPath"; properties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, endpointContextPath); String contextPath = "/contextPath"; properties.setProperty(PropertyKeyConst.CONTEXT_PATH, contextPath); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); Field providerField = AbstractServerListManager.class.getDeclaredField("serverListProvider"); providerField.setAccessible(true); ServerListProvider serverListProvider = (ServerListProvider) providerField.get(serverListManager); Field endpointField = EndpointServerListProvider.class.getDeclaredField("endpoint"); endpointField.setAccessible(true); String fieldEndpoint = (String) endpointField.get(serverListProvider); assertEquals(endpoint, fieldEndpoint); Field endpointPortField = EndpointServerListProvider.class.getDeclaredField("endpointPort"); endpointPortField.setAccessible(true); String fieldEndpointPort = String.valueOf(endpointPortField.get(serverListProvider)); assertEquals(endpointPort, fieldEndpointPort); Field endpointContextPathField = EndpointServerListProvider.class.getDeclaredField("endpointContextPath"); endpointContextPathField.setAccessible(true); String fieldEndpointContextPath = String.valueOf(endpointContextPathField.get(serverListProvider)); assertEquals(endpointContextPath, fieldEndpointContextPath); Field contentPathField = AbstractServerListProvider.class.getDeclaredField("contextPath"); contentPathField.setAccessible(true); String fieldContentPath = String.valueOf(contentPathField.get(serverListProvider)); assertEquals(fieldContentPath, contextPath); } @Test void testWithEndpointContextPath() throws NacosException { Properties properties = new Properties(); String endpoint = "1.1.1.1"; properties.setProperty(PropertyKeyConst.ENDPOINT, endpoint); String endpointPort = "9090"; properties.setProperty(PropertyKeyConst.ENDPOINT_PORT, endpointPort); String endpointContextPath = "/endpointContextPath"; properties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, endpointContextPath); String contextPath = "/contextPath"; properties.setProperty(PropertyKeyConst.CONTEXT_PATH, contextPath); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); assertTrue(serverListManager.getAddressSource().contains(endpointContextPath)); assertTrue(serverListManager.getName().contains("endpointContextPath")); } @Test void testWithEndpointClusterName() throws NacosException { Properties properties = new Properties(); String endpoint = "1.1.1.1"; properties.setProperty(PropertyKeyConst.ENDPOINT, endpoint); String endpointPort = "9090"; properties.setProperty(PropertyKeyConst.ENDPOINT_PORT, endpointPort); String testEndpointClusterName = "testEndpointClusterName"; properties.setProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME, testEndpointClusterName); String testClusterName = "testClusterName"; properties.setProperty(PropertyKeyConst.CLUSTER_NAME, testClusterName); String endpointContextPath = "/endpointContextPath"; properties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, endpointContextPath); String contextPath = "/contextPath"; properties.setProperty(PropertyKeyConst.CONTEXT_PATH, contextPath); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); String addressSource = serverListManager.getAddressSource(); assertTrue(addressSource.contains(endpointContextPath)); assertTrue(serverListManager.getName().contains("endpointContextPath")); assertTrue(addressSource.contains(testEndpointClusterName)); assertTrue(serverListManager.getName().contains(testEndpointClusterName)); assertFalse(addressSource.contains(testClusterName)); assertFalse(serverListManager.getName().contains(testClusterName)); } @Test void testWithoutEndpointContextPath() throws NacosException { Properties properties = new Properties(); String endpoint = "1.1.1.1"; properties.setProperty(PropertyKeyConst.ENDPOINT, endpoint); String endpointPort = "9090"; properties.setProperty(PropertyKeyConst.ENDPOINT_PORT, endpointPort); String contextPath = "/contextPath"; properties.setProperty(PropertyKeyConst.CONTEXT_PATH, contextPath); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); String endpointContextPath = "/endpointContextPath"; assertFalse(serverListManager.getAddressSource().contains(endpointContextPath)); assertTrue(serverListManager.getAddressSource().contains(contextPath)); assertFalse(serverListManager.getName().contains("endpointContextPath")); assertTrue(serverListManager.getName().contains("contextPath")); } @Test void testUseEndpointParsingRule() throws NacosException { System.setProperty("nacos.endpoint", "1.1.1.1"); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.ENDPOINT, "${nacos.endpoint}"); properties.setProperty(PropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE, "true"); properties.setProperty(PropertyKeyConst.ENDPOINT_PORT, "9090"); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); String addressServerUrl = serverListManager.getAddressSource(); assertTrue(addressServerUrl.startsWith("http://1.1.1.1")); } @Test void testUpdateCurrentServerAddr() throws NacosException { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, "1.1.1.1:8848,2.2.2.2:8848"); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); assertTrue("1.1.1.1:8848,2.2.2.2:8848".contains(serverListManager.getCurrentServer())); serverListManager.updateCurrentServerAddr(null); assertTrue("1.1.1.1:8848,2.2.2.2:8848".contains(serverListManager.getCurrentServer())); serverListManager.updateCurrentServerAddr("1.1.1.1:8848"); assertEquals("1.1.1.1:8848", serverListManager.getCurrentServer()); } @Test void testStartWithEmptyServerList() throws NacosException { Properties properties = new Properties(); properties.setProperty("EmptyList", "true"); properties.setProperty("MockTest", "true"); NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); final ConfigServerListManager mgr = new ConfigServerListManager(clientProperties); try { assertThrows(NacosException.class, mgr::start); } finally { mgr.shutdown(); } } @Test void testGenNextServer() throws NacosException { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, "1.1.1.1:8848,2.2.2.2:8848"); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); String currentServer = serverListManager.getCurrentServer(); String expectedServer = "1.1.1.1:8848,2.2.2.2:8848".replace(currentServer, ""); expectedServer = expectedServer.replace(",", ""); assertEquals(expectedServer, serverListManager.genNextServer()); // Don't throw NoSuchElementException, re-generate server list and re-shuffle. assertTrue("1.1.1.1:8848,2.2.2.2:8848".contains(serverListManager.genNextServer())); } @Test void testGenNextServerWithMockConcurrent() throws NacosException, NoSuchFieldException, IllegalAccessException { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, "1.1.1.1:8848,2.2.2.2:8848"); final NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ConfigServerListManager serverListManager = new ConfigServerListManager(clientProperties); serverListManager.start(); Iterator mockIterator = mock(Iterator.class); Field field = ConfigServerListManager.class.getDeclaredField("iterator"); field.setAccessible(true); field.set(serverListManager, mockIterator); // Mock async call gen next server, hasNext return `ture` and item be got by other thread. when(mockIterator.hasNext()).thenReturn(true); when(mockIterator.next()).thenThrow(new NoSuchElementException()); assertNotNull(serverListManager.genNextServer()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/LimiterTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class LimiterTest { @Test void testIsLimit() { String keyId = "a"; //For initiating. assertFalse(Limiter.isLimit(keyId)); long start = System.currentTimeMillis(); for (int j = 0; j < 5; j++) { assertFalse(Limiter.isLimit(keyId)); } long elapse = System.currentTimeMillis() - start; // assert < limit 5qps assertTrue(elapse > 980); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/PropertiesChangeParserTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeItem; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class PropertiesChangeParserTest { private final PropertiesChangeParser parser = new PropertiesChangeParser(); private final String type = "properties"; @Test void testType() { assertTrue(parser.isResponsibleFor(type)); } @Test void testAddKey() throws IOException { Map map = parser.doParse("", "app.name = nacos", type); assertNull(map.get("app.name").getOldValue()); assertEquals("nacos", map.get("app.name").getNewValue()); } @Test void testRemoveKey() throws IOException { Map map = parser.doParse("app.name = nacos", "", type); assertEquals("nacos", map.get("app.name").getOldValue()); assertNull(map.get("app.name").getNewValue()); } @Test void testModifyKey() throws IOException { Map map = parser.doParse("app.name = rocketMQ", "app.name = nacos", type); assertEquals("rocketMQ", map.get("app.name").getOldValue()); assertEquals("nacos", map.get("app.name").getNewValue()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/impl/YmlChangeParserTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.impl; import com.alibaba.nacos.api.config.ConfigChangeItem; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.Map; 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; class YmlChangeParserTest { private final YmlChangeParser parser = new YmlChangeParser(); private final String type = "yaml"; @Test void testType() { assertTrue(parser.isResponsibleFor(type)); } @Test void testAddKey() throws IOException { Map map = parser.doParse("", "app:\n name: nacos", type); assertNull(map.get("app.name").getOldValue()); assertEquals("nacos", map.get("app.name").getNewValue()); } @Test void testRemoveKey() throws IOException { Map map = parser.doParse("app:\n name: nacos", "", type); assertEquals("nacos", map.get("app.name").getOldValue()); assertNull(map.get("app.name").getNewValue()); } @Test void testModifyKey() throws IOException { Map map = parser.doParse("app:\n name: rocketMQ", "app:\n name: nacos", type); assertEquals("rocketMQ", map.get("app.name").getOldValue()); assertEquals("nacos", map.get("app.name").getNewValue()); } @Test void testComplexYaml() throws IOException { /* * map: * key1: "string" * key2: * - item1 * - item2 * - item3 * key3: 123 */ String s = "map:\n" + " key1: \"string\"\n" + " key2:\n" + " - item1\n" + " - item2\n" + " - item3\n" + " key3: 123 \n"; Map map = parser.doParse(s, s, type); assertEquals(0, map.size()); } @Test void testChangeInvalidKey() { assertThrows(NacosRuntimeException.class, () -> { parser.doParse("anykey:\n a", "anykey: !!javax.script.ScriptEngineManager [\n" + " !!java.net.URLClassLoader [[\n" + " !!java.net.URL [\"http://[yourhost]:[port]/yaml-payload.jar\"]\n" + " ]]\n" + "]", type); }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/listener/impl/AbstractConfigChangeListenerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.listener.impl; import com.alibaba.nacos.api.config.ConfigChangeEvent; import org.junit.jupiter.api.Test; import java.util.ArrayDeque; import java.util.Deque; import static org.junit.jupiter.api.Assertions.assertEquals; class AbstractConfigChangeListenerTest { @Test void receiveConfigInfo() { final Deque data = new ArrayDeque(); AbstractConfigChangeListener a = new AbstractConfigChangeListener() { @Override public void receiveConfigChange(ConfigChangeEvent event) { } @Override public void receiveConfigInfo(String configInfo) { super.receiveConfigInfo(configInfo); data.offer(configInfo); } }; a.receiveConfigInfo("foo"); final String actual = data.poll(); assertEquals("foo", actual); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/listener/impl/PropertiesListenerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.listener.impl; import org.junit.jupiter.api.Test; import java.util.ArrayDeque; import java.util.Deque; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class PropertiesListenerTest { @Test void testReceiveConfigInfo() { final Deque q2 = new ArrayDeque(); PropertiesListener a = new PropertiesListener() { @Override public void innerReceive(Properties properties) { q2.offer(properties); } }; a.receiveConfigInfo("foo=bar"); final Properties actual = q2.poll(); assertEquals(1, actual.size()); assertEquals("bar", actual.getProperty("foo")); } @Test void testReceiveConfigInfoEmpty() { final Deque q2 = new ArrayDeque(); PropertiesListener a = new PropertiesListener() { @Override public void innerReceive(Properties properties) { q2.offer(properties); } }; a.receiveConfigInfo(""); final Properties actual = q2.poll(); assertNull(actual); } @Test void testReceiveConfigInfoIsNotProperties() { final Deque q2 = new ArrayDeque(); PropertiesListener a = new PropertiesListener() { @Override public void innerReceive(Properties properties) { q2.offer(properties); } }; a.receiveConfigInfo(null); final Properties actual = q2.poll(); assertNull(actual); } @Test void testInnerReceive() { final Deque q2 = new ArrayDeque(); PropertiesListener a = new PropertiesListener() { @Override public void innerReceive(Properties properties) { q2.offer(properties); } }; Properties input = new Properties(); input.put("foo", "bar"); a.innerReceive(input); final Properties actual = q2.poll(); assertEquals(1, actual.size()); assertEquals("bar", actual.getProperty("foo")); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/utils/ContentUtilsTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import org.junit.jupiter.api.Test; import java.util.Arrays; import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class ContentUtilsTest { @Test void testVerifyIncrementPubContent() { String content = "aabbb"; ContentUtils.verifyIncrementPubContent(content); } @Test void testVerifyIncrementPubContentFail1() { Throwable exception = assertThrows(IllegalArgumentException.class, () -> { String content = null; ContentUtils.verifyIncrementPubContent(content); }); assertTrue(exception.getMessage().contains("publish/delete content can not be null")); } @Test void testVerifyIncrementPubContentFail2() { Throwable exception = assertThrows(IllegalArgumentException.class, () -> { String content = "aa\rbbb"; ContentUtils.verifyIncrementPubContent(content); }); assertTrue(exception.getMessage().contains("publish/delete content can not contain return and linefeed")); } @Test void testVerifyIncrementPubContentFail3() { Throwable exception = assertThrows(IllegalArgumentException.class, () -> { String content = ""; ContentUtils.verifyIncrementPubContent(content); }); assertTrue(exception.getMessage().contains("publish/delete content can not be null")); } @Test void testVerifyIncrementPubContentFail4() { Throwable exception = assertThrows(IllegalArgumentException.class, () -> { String content = "aa" + WORD_SEPARATOR + "bbb"; ContentUtils.verifyIncrementPubContent(content); }); assertTrue(exception.getMessage().contains("publish/delete content can not contain(char)2")); } @Test void testGetContentIdentity() { String content = "aa" + WORD_SEPARATOR + "bbb"; String content1 = ContentUtils.getContentIdentity(content); assertEquals("aa", content1); } @Test void testGetContentIdentityFail() { assertThrows(IllegalArgumentException.class, () -> { String content = "aabbb"; ContentUtils.getContentIdentity(content); }); } @Test void testGetContent() { String content = "aa" + WORD_SEPARATOR + "bbb"; String content1 = ContentUtils.getContent(content); assertEquals("bbb", content1); } @Test void testGetContentFail() { assertThrows(IllegalArgumentException.class, () -> { String content = "aabbb"; ContentUtils.getContent(content); }); } @Test void testTruncateContent() { String content = "aa"; String actual = ContentUtils.truncateContent(content); assertEquals(content, actual); } @Test void testTruncateLongContent() { char[] arr = new char[101]; Arrays.fill(arr, 'a'); String content = new String(arr); String actual = ContentUtils.truncateContent(content); assertEquals(content.substring(0, 100) + "...", actual); } @Test void testTruncateContentNull() { String actual = ContentUtils.truncateContent(null); assertEquals("", actual); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/utils/JvmUtilTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class JvmUtilTest { Method initMethod; @BeforeEach void setUp() throws NoSuchMethodException { initMethod = JvmUtil.class.getDeclaredMethod("init"); initMethod.setAccessible(true); } @AfterEach void tearDown() throws NoSuchFieldException, IllegalAccessException { System.clearProperty("isMultiInstance"); Field field = JvmUtil.class.getDeclaredField("isMultiInstance"); field.setAccessible(true); field.set(JvmUtil.class, false); } @Test void testIsMultiInstance() throws InvocationTargetException, IllegalAccessException { initMethod.invoke(JvmUtil.class); Boolean multiInstance = JvmUtil.isMultiInstance(); assertFalse(multiInstance); } @Test void testIsMultiInstance2() throws InvocationTargetException, IllegalAccessException { System.setProperty("isMultiInstance", "true"); initMethod.invoke(JvmUtil.class); Boolean multiInstance = JvmUtil.isMultiInstance(); assertTrue(multiInstance); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/utils/ParamUtilsTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; 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; import static org.junit.jupiter.api.Assertions.fail; class ParamUtilsTest { @Test void testIsValid() { String content = "abcABC09.:_-"; assertTrue(ParamUtils.isValid(content)); content = null; assertFalse(ParamUtils.isValid(content)); content = "@"; assertFalse(ParamUtils.isValid(content)); content = "+"; assertFalse(ParamUtils.isValid(content)); content = "/"; assertFalse(ParamUtils.isValid(content)); } @Test void testCheckTdg() throws NacosException { String tenant = "a"; String dataId = "b"; String group = "c"; ParamUtils.checkTdg(tenant, dataId, group); } @Test void testCheckTdgFail1() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { String tenant = "a"; String dataId = ""; String group = "c"; ParamUtils.checkTdg(tenant, dataId, group); }); assertTrue(exception.getMessage().contains("dataId invalid")); } @Test void testCheckTdgFail2() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { String tenant = "a"; String dataId = "b"; String group = ""; ParamUtils.checkTdg(tenant, dataId, group); }); assertTrue(exception.getMessage().contains("group invalid")); } @Test void testCheckKeyParam1() throws NacosException { String dataId = "b"; String group = "c"; ParamUtils.checkKeyParam(dataId, group); try { dataId = ""; group = "c"; ParamUtils.checkKeyParam(dataId, group); fail(); } catch (NacosException e) { assertEquals("dataId invalid", e.getMessage()); } try { dataId = "b"; group = ""; ParamUtils.checkKeyParam(dataId, group); fail(); } catch (NacosException e) { assertEquals("group invalid", e.getMessage()); } } @Test void testCheckKeyParam2() throws NacosException { String dataId = "b"; String group = "c"; String datumId = "a"; ParamUtils.checkKeyParam(dataId, group, datumId); try { dataId = ""; group = "c"; ParamUtils.checkKeyParam(dataId, group, datumId); fail(); } catch (NacosException e) { assertEquals("dataId invalid", e.getMessage()); } try { dataId = "b"; group = ""; ParamUtils.checkKeyParam(dataId, group, datumId); fail(); } catch (NacosException e) { assertEquals("group invalid", e.getMessage()); } try { dataId = "b"; group = "c"; datumId = ""; ParamUtils.checkKeyParam(dataId, group, datumId); fail(); } catch (NacosException e) { assertEquals("datumId invalid", e.getMessage()); } } @Test void testCheckKeyParam3() throws NacosException { String dataId = "b"; String group = "c"; ParamUtils.checkKeyParam(Arrays.asList(dataId), group); try { group = "c"; ParamUtils.checkKeyParam(new ArrayList(), group); fail(); } catch (NacosException e) { assertEquals("dataIds invalid", e.getMessage()); } try { dataId = ""; group = "c"; ParamUtils.checkKeyParam(Arrays.asList(dataId), group); fail(); } catch (NacosException e) { assertEquals("dataId invalid", e.getMessage()); } try { dataId = "b"; group = ""; ParamUtils.checkKeyParam(Arrays.asList(dataId), group); fail(); } catch (NacosException e) { assertEquals("group invalid", e.getMessage()); } } @Test void testCheckParam() throws NacosException { String dataId = "b"; String group = "c"; String content = "a"; ParamUtils.checkParam(dataId, group, content); } @Test void testCheckParamFail() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { String dataId = "b"; String group = "c"; String content = ""; ParamUtils.checkParam(dataId, group, content); }); assertTrue(exception.getMessage().contains("content invalid")); } @Test void testCheckParam2() throws NacosException { String dataId = "b"; String group = "c"; String datumId = "d"; String content = "a"; ParamUtils.checkParam(dataId, group, datumId, content); } @Test void testCheckParam2Fail() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { String dataId = "b"; String group = "c"; String datumId = "d"; String content = ""; ParamUtils.checkParam(dataId, group, datumId, content); }); assertTrue(exception.getMessage().contains("content invalid")); } @Test void testCheckTenant() throws NacosException { String tenant = "a"; ParamUtils.checkTenant(tenant); } @Test void testCheckTenantFail() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { String tenant = ""; ParamUtils.checkTenant(tenant); }); assertTrue(exception.getMessage().contains("tenant invalid")); } @Test void testCheckBetaIps() throws NacosException { ParamUtils.checkBetaIps("127.0.0.1"); } @Test void testCheckBetaIpsFail1() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { ParamUtils.checkBetaIps(""); }); assertTrue(exception.getMessage().contains("betaIps invalid")); } @Test void testCheckBetaIpsFail2() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { ParamUtils.checkBetaIps("aaa"); }); assertTrue(exception.getMessage().contains("betaIps invalid")); } @Test void testCheckContent() throws NacosException { ParamUtils.checkContent("aaa"); } @Test void testCheckContentFail() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { ParamUtils.checkContent(""); }); assertTrue(exception.getMessage().contains("content invalid")); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/config/utils/SnapShotSwitchTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.config.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; class SnapShotSwitchTest { @Test void testGetIsSnapShot() { Boolean isSnapShot = SnapShotSwitch.getIsSnapShot(); assertTrue(isSnapShot); SnapShotSwitch.setIsSnapShot(false); assertFalse(SnapShotSwitch.getIsSnapShot()); SnapShotSwitch.setIsSnapShot(true); assertTrue(SnapShotSwitch.getIsSnapShot()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/lock/NacosLockServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.lock.core.NLock; import com.alibaba.nacos.client.lock.core.NLockFactory; import com.alibaba.nacos.client.lock.remote.grpc.LockGrpcClient; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.Properties; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class NacosLockServiceTest { @Mock private LockGrpcClient lockGrpcClient; private NacosLockService lockService; @BeforeEach void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1"); lockService = new NacosLockService(properties); injectMock(); } private void injectMock() throws NoSuchFieldException, IllegalAccessException { Field lockGrpcClientField = NacosLockService.class.getDeclaredField("lockGrpcClient"); lockGrpcClientField.setAccessible(true); lockGrpcClientField.set(lockService, lockGrpcClient); } @AfterEach void tearDown() throws NacosException { lockService.shutdown(); } @Test void lock() throws NacosException { NLock nLock = NLockFactory.getLock("test"); lockService.lock(nLock); verify(lockGrpcClient).lock(nLock); } @Test void unLock() throws NacosException { NLock nLock = NLockFactory.getLock("test"); lockService.unLock(nLock); verify(lockGrpcClient).unLock(nLock); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/lock/remote/grpc/LockGrpcClientTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.lock.remote.grpc; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.lock.remote.AbstractLockRequest; import com.alibaba.nacos.api.lock.remote.response.LockOperationResponse; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.ServerCheckResponse; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.lock.core.NLockFactory; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.ServerListFactory; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class LockGrpcClientTest { @Mock private RpcClient rpcClient; @Mock private SecurityProxy securityProxy; @Mock private ServerListFactory serverListFactory; private LockGrpcClient lockGrpcClient; @BeforeEach void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException { lockGrpcClient = new LockGrpcClient(NacosClientProperties.PROTOTYPE, serverListFactory, securityProxy); Field rpcClientField = LockGrpcClient.class.getDeclaredField("rpcClient"); rpcClientField.setAccessible(true); rpcClientField.set(lockGrpcClient, rpcClient); } @AfterEach void tearDown() throws NacosException { lockGrpcClient.shutdown(); } private void mockRequest() { Map context = new HashMap<>(); when(securityProxy.getIdentityContext(any())).thenReturn(context); when(rpcClient.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK)).thenReturn(AbilityStatus.SUPPORTED); } @Test void lockNotSupportedFeature() { when(rpcClient.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK)).thenReturn(AbilityStatus.NOT_SUPPORTED); assertThrows(NacosRuntimeException.class, () -> lockGrpcClient.lock(NLockFactory.getLock("test", -1L))); } @Test void lockWithNacosException() throws NacosException { mockRequest(); when(rpcClient.request(any(AbstractLockRequest.class))).thenThrow(new NacosException(NacosException.SERVER_ERROR, "test")); assertThrows(NacosException.class, () -> lockGrpcClient.lock(NLockFactory.getLock("test", -1L)), "test"); } @Test void lockWithOtherException() throws NacosException { mockRequest(); when(rpcClient.request(any(AbstractLockRequest.class))).thenThrow(new RuntimeException("test")); assertThrows(NacosException.class, () -> lockGrpcClient.lock(NLockFactory.getLock("test", -1L)), "Request nacos server failed: test"); } @Test void lockWithUnexpectedResponse() throws NacosException { mockRequest(); when(rpcClient.request(any(AbstractLockRequest.class))).thenReturn(new ServerCheckResponse()); assertThrows(NacosException.class, () -> lockGrpcClient.lock(NLockFactory.getLock("test", -1L)), "Server return invalid response"); } @Test void lockFailed() throws NacosException { mockRequest(); when(rpcClient.request(any(AbstractLockRequest.class))).thenReturn(ErrorResponse.build(500, "test fail code")); assertThrows(NacosException.class, () -> lockGrpcClient.lock(NLockFactory.getLock("test", -1L)), "test fail code"); } @Test void lockSuccess() throws NacosException { mockRequest(); when(rpcClient.request(any(AbstractLockRequest.class))).thenReturn(new LockOperationResponse(true)); assertTrue(lockGrpcClient.lock(NLockFactory.getLock("test", -1L))); } @Test void unLockNotSupportedFeature() { when(rpcClient.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK)).thenReturn(AbilityStatus.NOT_SUPPORTED); assertThrows(NacosRuntimeException.class, () -> lockGrpcClient.unLock(NLockFactory.getLock("test", -1L))); } @Test void unlockSuccess() throws NacosException { mockRequest(); when(rpcClient.request(any(AbstractLockRequest.class))).thenReturn(new LockOperationResponse(true)); assertTrue(lockGrpcClient.unLock(NLockFactory.getLock("test", -1L))); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/logging/NacosLoggingTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.logging; import com.alibaba.nacos.common.logging.NacosLoggingAdapter; import com.alibaba.nacos.common.logging.NacosLoggingProperties; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.doThrow; @ExtendWith(MockitoExtension.class) class NacosLoggingTest { @Mock NacosLoggingAdapter loggingAdapter; NacosLoggingProperties loggingProperties; NacosLogging instance; @BeforeEach void setUp() throws NoSuchFieldException, IllegalAccessException { loggingProperties = new NacosLoggingProperties("", new Properties()); instance = NacosLogging.getInstance(); Field loggingPropertiesField = NacosLogging.class.getDeclaredField("loggingProperties"); loggingPropertiesField.setAccessible(true); loggingPropertiesField.set(instance, loggingProperties); } @Test void testGetInstance() { NacosLogging instance = NacosLogging.getInstance(); assertNotNull(instance); } @Test void testLoadConfiguration() throws NoSuchFieldException, IllegalAccessException { instance = NacosLogging.getInstance(); Field nacosLogging = NacosLogging.class.getDeclaredField("loggingAdapter"); nacosLogging.setAccessible(true); nacosLogging.set(instance, loggingAdapter); instance.loadConfiguration(); Mockito.verify(loggingAdapter, Mockito.times(1)).loadConfiguration(loggingProperties); } @Test void testLoadConfigurationWithException() throws NoSuchFieldException, IllegalAccessException { instance = NacosLogging.getInstance(); Field nacosLoggingField = NacosLogging.class.getDeclaredField("loggingAdapter"); nacosLoggingField.setAccessible(true); NacosLoggingAdapter cachedLogging = (NacosLoggingAdapter) nacosLoggingField.get(instance); try { doThrow(new RuntimeException()).when(loggingAdapter).loadConfiguration(loggingProperties); nacosLoggingField.set(instance, loggingAdapter); instance.loadConfiguration(); // without exception thrown } finally { nacosLoggingField.set(instance, cachedLogging); } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingMaintainServiceTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; import com.alibaba.nacos.client.security.SecurityProxy; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentMatcher; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class NacosNamingMaintainServiceTest { private NacosNamingMaintainService nacosNamingMaintainService; private NamingHttpClientProxy serverProxy; private NamingServerListManager serverListManager; private SecurityProxy securityProxy; private ScheduledExecutorService executorService; @BeforeEach void setUp() throws Exception { Properties prop = new Properties(); prop.setProperty(PropertyKeyConst.NAMESPACE, "public"); prop.setProperty("serverAddr", "localhost"); nacosNamingMaintainService = new NacosNamingMaintainService(prop); serverProxy = mock(NamingHttpClientProxy.class); serverListManager = mock(NamingServerListManager.class); securityProxy = mock(SecurityProxy.class); executorService = mock(ScheduledExecutorService.class); Field serverProxyField = NacosNamingMaintainService.class.getDeclaredField("serverProxy"); serverProxyField.setAccessible(true); serverProxyField.set(nacosNamingMaintainService, serverProxy); Field serverListManagerField = NacosNamingMaintainService.class.getDeclaredField("serverListManager"); serverListManagerField.setAccessible(true); serverListManagerField.set(nacosNamingMaintainService, serverListManager); Field securityProxyFiled = NacosNamingMaintainService.class.getDeclaredField("securityProxy"); securityProxyFiled.setAccessible(true); securityProxyFiled.set(nacosNamingMaintainService, securityProxy); Field executorServiceField = NacosNamingMaintainService.class.getDeclaredField("executorService"); executorServiceField.setAccessible(true); executorServiceField.set(nacosNamingMaintainService, executorService); } @AfterEach void tearDown() throws Exception { } @Test void testConstructor() throws NacosException { NacosNamingMaintainService client = new NacosNamingMaintainService("localhost"); assertNotNull(client); } @Test void testUpdateInstance1() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); //when nacosNamingMaintainService.updateInstance(serviceName, groupName, instance); //then verify(serverProxy, times(1)).updateInstance(serviceName, groupName, instance); } @Test void testUpdateInstance2() throws NacosException { //given String serviceName = "service1"; Instance instance = new Instance(); //when nacosNamingMaintainService.updateInstance(serviceName, instance); //then verify(serverProxy, times(1)).updateInstance(serviceName, Constants.DEFAULT_GROUP, instance); } @Test void testQueryService1() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; //when nacosNamingMaintainService.queryService(serviceName, groupName); //then verify(serverProxy, times(1)).queryService(serviceName, groupName); } @Test void testQueryService2() throws NacosException { //given String serviceName = "service1"; Instance instance = new Instance(); //when nacosNamingMaintainService.queryService(serviceName); //then verify(serverProxy, times(1)).queryService(serviceName, Constants.DEFAULT_GROUP); } @Test void testCreateService1() throws NacosException { //given String serviceName = "service1"; //when nacosNamingMaintainService.createService(serviceName); //then verify(serverProxy, times(1)).createService(argThat(new ArgumentMatcher() { @Override public boolean matches(Service service) { return service.getName().equals(serviceName) && service.getGroupName().equals(Constants.DEFAULT_GROUP) && Math.abs(service.getProtectThreshold() - Constants.DEFAULT_PROTECT_THRESHOLD) < 0.1f && service.getMetadata().size() == 0; } }), argThat(o -> o instanceof NoneSelector)); } @Test void testCreateService2() throws NacosException { //given String serviceName = "service1"; String groupName = "groupName"; //when nacosNamingMaintainService.createService(serviceName, groupName); //then verify(serverProxy, times(1)).createService(argThat(new ArgumentMatcher() { @Override public boolean matches(Service service) { return service.getName().equals(serviceName) && service.getGroupName().equals(groupName) && Math.abs(service.getProtectThreshold() - Constants.DEFAULT_PROTECT_THRESHOLD) < 0.1f && service.getMetadata().size() == 0; } }), argThat(o -> o instanceof NoneSelector)); } @Test void testCreateService3() throws NacosException { //given String serviceName = "service1"; String groupName = "groupName"; float protectThreshold = 0.1f; //when nacosNamingMaintainService.createService(serviceName, groupName, protectThreshold); //then verify(serverProxy, times(1)).createService(argThat(new ArgumentMatcher() { @Override public boolean matches(Service service) { return service.getName().equals(serviceName) && service.getGroupName().equals(groupName) && Math.abs(service.getProtectThreshold() - protectThreshold) < 0.1f && service.getMetadata().size() == 0; } }), argThat(o -> o instanceof NoneSelector)); } @Test void testCreateService5() throws NacosException { //given String serviceName = "service1"; String groupName = "groupName"; float protectThreshold = 0.1f; String expression = "k=v"; //when nacosNamingMaintainService.createService(serviceName, groupName, protectThreshold, expression); //then verify(serverProxy, times(1)).createService(argThat(new ArgumentMatcher() { @Override public boolean matches(Service service) { return service.getName().equals(serviceName) && service.getGroupName().equals(groupName) && Math.abs(service.getProtectThreshold() - protectThreshold) < 0.1f && service.getMetadata().size() == 0; } }), argThat(o -> ((ExpressionSelector) o).getExpression().equals(expression))); } @Test void testCreateService4() throws NacosException { //given Service service = new Service(); AbstractSelector selector = new NoneSelector(); //when nacosNamingMaintainService.createService(service, selector); //then verify(serverProxy, times(1)).createService(service, selector); } @Test void testDeleteService1() throws NacosException { //given String serviceName = "service1"; //when nacosNamingMaintainService.deleteService(serviceName); //then verify(serverProxy, times(1)).deleteService(serviceName, Constants.DEFAULT_GROUP); } @Test void testDeleteService2() throws NacosException { //given String serviceName = "service1"; String groupName = "groupName"; //when nacosNamingMaintainService.deleteService(serviceName, groupName); //then verify(serverProxy, times(1)).deleteService(serviceName, groupName); } @Test void testUpdateService1() throws NacosException { //given String serviceName = "service1"; String groupName = "groupName"; float protectThreshold = 0.1f; //when nacosNamingMaintainService.updateService(serviceName, groupName, protectThreshold); //then verify(serverProxy, times(1)).updateService(argThat(new ArgumentMatcher() { @Override public boolean matches(Service service) { return service.getName().equals(serviceName) && service.getGroupName().equals(groupName) && Math.abs(service.getProtectThreshold() - protectThreshold) < 0.1f; } }), argThat(o -> o instanceof NoneSelector)); } @Test void testUpdateService2() throws NacosException { //given String serviceName = "service1"; String groupName = "groupName"; float protectThreshold = 0.1f; Map meta = new HashMap<>(); meta.put("k", "v"); //when nacosNamingMaintainService.updateService(serviceName, groupName, protectThreshold, meta); //then verify(serverProxy, times(1)).updateService(argThat(new ArgumentMatcher() { @Override public boolean matches(Service service) { return service.getName().equals(serviceName) && service.getGroupName().equals(groupName) && Math.abs(service.getProtectThreshold() - protectThreshold) < 0.1f && service.getMetadata().size() == 1; } }), argThat(o -> o instanceof NoneSelector)); } @Test void testUpdateService3() throws NacosException { //given Service service = new Service(); AbstractSelector selector = new NoneSelector(); //when nacosNamingMaintainService.updateService(service, selector); //then verify(serverProxy, times(1)).updateService(service, selector); } @Test void testShutDown() throws NacosException { //when nacosNamingMaintainService.shutDown(); //then verify(serverProxy, times(1)).shutdown(); verify(serverListManager, times(1)).shutdown(); verify(executorService, times(1)).shutdown(); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/NacosNamingServiceTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; import com.alibaba.nacos.client.naming.selector.NamingSelectorFactory; import com.alibaba.nacos.client.naming.selector.NamingSelectorWrapper; import com.alibaba.nacos.client.naming.utils.UtilAndComs; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.StringUtils; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import static com.alibaba.nacos.client.naming.selector.NamingSelectorFactory.getUniqueClusterString; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; 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.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NacosNamingServiceTest { @Mock private NamingClientProxy proxy; @Mock private InstancesChangeNotifier changeNotifier; @Mock private ServiceInfoHolder serviceInfoHolder; private NacosNamingService client; @BeforeEach void before() throws NoSuchFieldException, NacosException, IllegalAccessException { Properties prop = new Properties(); prop.setProperty("serverAddr", "localhost"); prop.put(PropertyKeyConst.NAMESPACE, "test"); client = new NacosNamingService(prop); injectMocks(client); } @AfterEach void tearDown() throws NacosException { client.shutDown(); } private void injectMocks(NacosNamingService client) throws NoSuchFieldException, IllegalAccessException { // inject proxy Field serverProxyField = NacosNamingService.class.getDeclaredField("clientProxy"); serverProxyField.setAccessible(true); try { ((NamingHttpClientProxy) serverProxyField.get(client)).shutdown(); } catch (Throwable ignored) { } serverProxyField.set(client, proxy); // inject notifier doReturn(InstancesChangeEvent.class).when(changeNotifier).subscribeType(); Field changeNotifierField = NacosNamingService.class.getDeclaredField("changeNotifier"); changeNotifierField.setAccessible(true); changeNotifierField.set(client, changeNotifier); // inject service info holder Field serviceInfoHolderField = NacosNamingService.class.getDeclaredField("serviceInfoHolder"); serviceInfoHolderField.setAccessible(true); try { ((ServiceInfoHolder) serviceInfoHolderField.get(client)).shutdown(); } catch (Throwable ignored) { } serviceInfoHolderField.set(client, serviceInfoHolder); } @Test void testRegisterInstanceSingle() throws NacosException { //given String serviceName = "service1"; String ip = "1.1.1.1"; int port = 10000; //when client.registerInstance(serviceName, ip, port); //then verify(proxy, times(1)).registerService(eq(serviceName), eq(Constants.DEFAULT_GROUP), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(Constants.DEFAULT_CLUSTER_NAME))); } @Test void testRegisterInstanceSingleWithGroup() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; String ip = "1.1.1.1"; int port = 10000; //when client.registerInstance(serviceName, groupName, ip, port); //then verify(proxy, times(1)).registerService(eq(serviceName), eq(groupName), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(Constants.DEFAULT_CLUSTER_NAME))); } @Test void testRegisterInstanceSingleWithCluster() throws NacosException { //given String serviceName = "service1"; String clusterName = "cluster1"; String ip = "1.1.1.1"; int port = 10000; //when client.registerInstance(serviceName, ip, port, clusterName); //then verify(proxy, times(1)).registerService(eq(serviceName), eq(Constants.DEFAULT_GROUP), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(clusterName))); } @Test void testRegisterInstanceSingleFull() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; String clusterName = "cluster1"; String ip = "1.1.1.1"; int port = 10000; //when client.registerInstance(serviceName, groupName, ip, port, clusterName); //then verify(proxy, times(1)).registerService(eq(serviceName), eq(groupName), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(clusterName))); } @Test void testRegisterInstanceByInstanceOnlyService() throws NacosException { //given String serviceName = "service1"; Instance instance = new Instance(); //when client.registerInstance(serviceName, instance); //then verify(proxy, times(1)).registerService(serviceName, Constants.DEFAULT_GROUP, instance); } @Test void testRegisterInstanceByInstanceFullName() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); //when client.registerInstance(serviceName, groupName, instance); //then verify(proxy, times(1)).registerService(serviceName, groupName, instance); } @Test void testRegisterInstanceByInstanceWithCluster() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { //given String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setClusterName("cluster1,cluster2"); //when client.registerInstance(serviceName, groupName, instance); }); assertTrue(exception.getMessage().contains( "Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: cluster1,cluster2)")); } @Test void testBatchRegisterInstance() throws NacosException { Instance instance = new Instance(); String serviceName = "service1"; String ip = "1.1.1.1"; int port = 10000; instance.setServiceName(serviceName); instance.setEphemeral(true); instance.setPort(port); instance.setIp(ip); List instanceList = new ArrayList<>(); instanceList.add(instance); //when client.batchRegisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList); //then verify(proxy, times(1)).batchRegisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP), argThat(instances -> CollectionUtils.isEqualCollection(instanceList, instances))); } @Test void testBatchRegisterInstanceWithGroupNamePrefix() throws NacosException { Instance instance = new Instance(); String serviceName = "service1"; String ip = "1.1.1.1"; int port = 10000; instance.setServiceName(Constants.DEFAULT_GROUP + "@@" + serviceName); instance.setEphemeral(true); instance.setPort(port); instance.setIp(ip); List instanceList = new ArrayList<>(); instanceList.add(instance); //when client.batchRegisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList); //then verify(proxy, times(1)).batchRegisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP), argThat(instances -> CollectionUtils.isEqualCollection(instanceList, instances))); } @Test void testBatchRegisterInstanceWithWrongGroupNamePrefix() throws NacosException { Instance instance = new Instance(); String serviceName = "service1"; String ip = "1.1.1.1"; int port = 10000; instance.setServiceName("WrongGroup" + "@@" + serviceName); instance.setEphemeral(true); instance.setPort(port); instance.setIp(ip); List instanceList = new ArrayList<>(); instanceList.add(instance); //when try { client.batchRegisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList); } catch (Exception e) { assertTrue(e instanceof NacosException); assertTrue(e.getMessage().contains("wrong group name prefix of instance service name")); } } @Test void testBatchDeRegisterInstance() throws NacosException { Instance instance = new Instance(); String serviceName = "service1"; String ip = "1.1.1.1"; int port = 10000; instance.setServiceName(serviceName); instance.setEphemeral(true); instance.setPort(port); instance.setIp(ip); List instanceList = new ArrayList<>(); instanceList.add(instance); //when try { client.batchDeregisterInstance(serviceName, Constants.DEFAULT_GROUP, instanceList); } catch (Exception e) { assertTrue(e instanceof NacosException); assertTrue(e.getMessage().contains("not found")); } } @Test void testDeregisterInstanceSingle() throws NacosException { //given String serviceName = "service1"; String ip = "1.1.1.1"; int port = 10000; //when client.deregisterInstance(serviceName, ip, port); //then verify(proxy, times(1)).deregisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(Constants.DEFAULT_CLUSTER_NAME))); } @Test void testDeregisterInstanceSingleWithGroup() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; String ip = "1.1.1.1"; int port = 10000; //when client.deregisterInstance(serviceName, groupName, ip, port); //then verify(proxy, times(1)).deregisterService(eq(serviceName), eq(groupName), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(Constants.DEFAULT_CLUSTER_NAME))); } @Test void testDeregisterInstanceSingleWithCluster() throws NacosException { //given String serviceName = "service1"; String clusterName = "cluster1"; String ip = "1.1.1.1"; int port = 10000; //when client.deregisterInstance(serviceName, ip, port, clusterName); //then verify(proxy, times(1)).deregisterService(eq(serviceName), eq(Constants.DEFAULT_GROUP), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(clusterName))); } @Test void testDeregisterInstanceSingleFull() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; String clusterName = "cluster1"; String ip = "1.1.1.1"; int port = 10000; //when client.deregisterInstance(serviceName, groupName, ip, port, clusterName); //then verify(proxy, times(1)).deregisterService(eq(serviceName), eq(groupName), argThat(instance -> instance.getIp().equals(ip) && instance.getPort() == port && Math.abs(instance.getWeight() - 1.0) < 0.01f && instance.getClusterName() .equals(clusterName))); } @Test void testDeregisterInstanceByInstanceOnlyService() throws NacosException { //given String serviceName = "service1"; Instance instance = new Instance(); //when client.deregisterInstance(serviceName, instance); //then verify(proxy, times(1)).deregisterService(serviceName, Constants.DEFAULT_GROUP, instance); } @Test void testDeregisterInstanceByInstanceFullName() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); //when client.deregisterInstance(serviceName, groupName, instance); //then verify(proxy, times(1)).deregisterService(serviceName, groupName, instance); } @Test void testGetAllInstancesOnlyService() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(new Instance()); when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstancesFullName() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.setGroupName(groupName); serviceInfo.addHost(new Instance()); when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName, groupName); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstancesOnlyServiceNotSubscribe() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(new Instance()); when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "", false)).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName, false); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstancesFullNameNotSubscribe() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.setGroupName(groupName); serviceInfo.addHost(new Instance()); when(proxy.queryInstancesOfService(serviceName, groupName, "", false)).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName, groupName, false); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstancesWithServiceAndClusters() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance("cluster1", false)); serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, false)); List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName, clusterList); //then assertEquals(1, result.size()); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstancesWithFullNameAndClusters() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; // when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.setGroupName(groupName); serviceInfo.addHost(mockInstance("cluster1", false)); serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, false)); List clusterList = Arrays.asList("cluster1", "cluster2"); serviceInfo.getHosts().get(1).setClusterName(Constants.DEFAULT_CLUSTER_NAME); when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName, groupName, clusterList); //then assertEquals(1, result.size()); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstancesWithServiceAndClustersNotSubscribe() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance("cluster1", false)); List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "cluster1,cluster2", false)).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName, clusterList, false); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstancesWithFullNameAndClustersNotSubscribe() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; // when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.setGroupName(groupName); serviceInfo.addHost(mockInstance("cluster1", false)); List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.queryInstancesOfService(serviceName, groupName, "cluster1,cluster2", false)).thenReturn(serviceInfo); List result = client.getAllInstances(serviceName, groupName, clusterList, false); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstanceFromFailover() throws NacosException { when(serviceInfoHolder.isFailoverSwitch()).thenReturn(true); ServiceInfo serviceInfo = new ServiceInfo("group1@@service1"); serviceInfo.setHosts(Collections.singletonList(new Instance())); when(serviceInfoHolder.getFailoverServiceInfo("service1", "group1")).thenReturn(serviceInfo); List actual = client.getAllInstances("service1", "group1", false); verify(proxy, never()).queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean()); assertEquals(1, actual.size()); assertEquals(new Instance(), actual.get(0)); } @Test void testGetAllInstanceFromFailoverEmpty() throws NacosException { when(serviceInfoHolder.isFailoverSwitch()).thenReturn(true); ServiceInfo serviceInfo = new ServiceInfo("group1@@service1"); when(serviceInfoHolder.getFailoverServiceInfo("service1", "group1")).thenReturn(serviceInfo); List actual = client.getAllInstances("service1", "group1", false); verify(proxy).queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean()); assertEquals(0, actual.size()); } @Test void testGetAllInstanceWithCacheAndSubscribeException() throws NacosException { String serviceName = "service1"; ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(new Instance()); when(serviceInfoHolder.getServiceInfo(serviceName, Constants.DEFAULT_GROUP)).thenReturn(serviceInfo); when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenThrow(new NacosException(500, "test")); List result = client.getAllInstances(serviceName); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testGetAllInstanceWithoutCacheAndSubscribeException() throws NacosException { String serviceName = "service1"; when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenThrow(new NacosException(500, "test")); assertThrows(NacosException.class, () -> client.getAllInstances(serviceName)); } @Test void testGetAllInstanceWithCacheAndSubscribed() throws NacosException { String serviceName = "service1"; ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(new Instance()); when(serviceInfoHolder.getServiceInfo(serviceName, Constants.DEFAULT_GROUP)).thenReturn(serviceInfo); when(proxy.isSubscribed(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(true); List result = client.getAllInstances(serviceName); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesOnlyService() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, true); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesFullName() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, groupName, true); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesOnlyServiceNotSubscribe() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "", false)).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, true, false); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesFullNameNotSubscribe() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.queryInstancesOfService(serviceName, groupName, "", false)).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, groupName, true, false); //then assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesWithServiceAndClusters() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance("cluster1", true)); serviceInfo.addHost(mockInstance("cluster1", false)); List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, "")).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, clusterList, true); //then assertEquals(1, result.size()); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesWithFullNameAndClusters() throws NacosException { //given String serviceName = "service1"; final String groupName = "group1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance("cluster1", true)); serviceInfo.addHost(mockInstance("cluster1", false)); List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.subscribe(serviceName, groupName, "")).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, groupName, clusterList, true); //then assertEquals(1, result.size()); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesWithServiceAndClustersNotSubscribe() throws NacosException { //given String serviceName = "service1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance("cluster1", true)); serviceInfo.addHost(mockInstance("cluster1", false)); List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.queryInstancesOfService(serviceName, Constants.DEFAULT_GROUP, "cluster1,cluster2", false)).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, clusterList, true, false); //then assertEquals(1, result.size()); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesWithFullNameAndClustersNotSubscribe() throws NacosException { //given String serviceName = "service1"; final String groupName = "group1"; //when ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setName(serviceName); serviceInfo.addHost(mockInstance("cluster1", true)); serviceInfo.addHost(mockInstance("cluster1", false)); List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.queryInstancesOfService(serviceName, groupName, "cluster1,cluster2", false)).thenReturn(serviceInfo); List result = client.selectInstances(serviceName, groupName, clusterList, true, false); //then assertEquals(1, result.size()); assertEquals(serviceInfo.getHosts().get(0), result.get(0)); } @Test void testSelectInstancesWithHealthyFlag() throws NacosException { //given Instance healthyInstance = new Instance(); healthyInstance.setHealthy(true); Instance instance1 = new Instance(); instance1.setHealthy(false); Instance instance2 = new Instance(); instance2.setHealthy(true); instance2.setEnabled(false); Instance instance3 = new Instance(); instance3.setHealthy(true); instance3.setWeight(0.0); List hosts = new ArrayList<>(); hosts.add(healthyInstance); hosts.add(instance1); hosts.add(instance2); hosts.add(instance3); ServiceInfo info = new ServiceInfo(); info.setHosts(hosts); String serviceName = "service1"; String groupName = "group1"; List clusterList = Arrays.asList("cluster1", "cluster2"); when(proxy.queryInstancesOfService(serviceName, groupName, "cluster1,cluster2", false)).thenReturn(info); //when List instances = client.selectInstances(serviceName, groupName, clusterList, true, false); //then assertEquals(1, instances.size()); assertSame(healthyInstance, instances.get(0)); } @Test void testSelectOneHealthyInstanceOnlyService() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.subscribe(anyString(), anyString(), anyString())).thenReturn(infoWithHealthyInstance); String serviceName = "service1"; //when Instance instance = client.selectOneHealthyInstance(serviceName); //then assertNotNull(instance); } @Test void testSelectOneHealthyInstanceFullName() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.subscribe(anyString(), anyString(), anyString())).thenReturn(infoWithHealthyInstance); String serviceName = "service1"; String groupName = "group1"; //when Instance instance = client.selectOneHealthyInstance(serviceName, groupName); //then assertNotNull(instance); } @Test void testSelectOneHealthyInstanceOnlyServiceNotSubscribe() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn( infoWithHealthyInstance); String serviceName = "service1"; //when Instance instance = client.selectOneHealthyInstance(serviceName, false); //then assertNotNull(instance); } @Test void testSelectOneHealthyInstanceFullNameNotSubscribe() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn( infoWithHealthyInstance); String serviceName = "service1"; String groupName = "group1"; //when Instance instance = client.selectOneHealthyInstance(serviceName, groupName, false); //then assertNotNull(instance); } @Test void testSelectOneHealthyInstanceWithServiceAndClusters() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance("cluster1", true)); infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); String serviceName = "service1"; when(proxy.subscribe(serviceName, Constants.DEFAULT_GROUP, StringUtils.EMPTY)).thenReturn( infoWithHealthyInstance); List clusterList = Arrays.asList("cluster1", "cluster2"); //when Instance instance = client.selectOneHealthyInstance(serviceName, clusterList); //then assertNotNull(instance); assertEquals("cluster1", instance.getClusterName()); } @Test void testSelectOneHealthyInstanceWithFullNameAndClusters() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance("cluster1", true)); infoWithHealthyInstance.addHost(mockInstance(Constants.DEFAULT_CLUSTER_NAME, true)); when(proxy.subscribe(anyString(), anyString(), anyString())).thenReturn(infoWithHealthyInstance); String serviceName = "service1"; String groupName = "group1"; List clusterList = Arrays.asList("cluster1", "cluster2"); //when Instance instance = client.selectOneHealthyInstance(serviceName, groupName, clusterList); //then assertNotNull(instance); assertEquals("cluster1", instance.getClusterName()); } @Test void testSelectOneHealthyInstanceWithServiceAndClustersNotSubscribe() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance("cluster1", true)); when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn( infoWithHealthyInstance); String serviceName = "service1"; List clusterList = Arrays.asList("cluster1", "cluster2"); //when Instance instance = client.selectOneHealthyInstance(serviceName, clusterList, false); //then assertNotNull(instance); assertEquals("cluster1", instance.getClusterName()); } @Test void testSelectOneHealthyInstanceWithFullNameAndClustersNotSubscribe() throws NacosException { //given ServiceInfo infoWithHealthyInstance = new ServiceInfo(); infoWithHealthyInstance.addHost(mockInstance("cluster1", true)); when(proxy.queryInstancesOfService(anyString(), anyString(), anyString(), anyBoolean())).thenReturn( infoWithHealthyInstance); String serviceName = "service1"; String groupName = "group1"; List clusterList = Arrays.asList("cluster1", "cluster2"); //when Instance instance = client.selectOneHealthyInstance(serviceName, groupName, clusterList, false); //then assertNotNull(instance); assertEquals("cluster1", instance.getClusterName()); } @Test void testSubscribeOnlyService() throws NacosException { //given String serviceName = "service1"; EventListener listener = event -> { }; //when client.subscribe(serviceName, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, Constants.DEFAULT_GROUP, Constants.NULL, NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener); //then verify(changeNotifier, times(1)).registerListener(Constants.DEFAULT_GROUP, serviceName, wrapper); verify(proxy, times(1)).subscribe(serviceName, Constants.DEFAULT_GROUP, ""); } @Test void testSubscribeFullName() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; EventListener listener = event -> { }; //when client.subscribe(serviceName, groupName, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, groupName, Constants.NULL, NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener); //then verify(changeNotifier, times(1)).registerListener(groupName, serviceName, wrapper); verify(proxy, times(1)).subscribe(serviceName, groupName, ""); } @Test void testSubscribeWithServiceAndClusters() throws NacosException { //given String serviceName = "service1"; List clusterList = Arrays.asList("cluster1", "cluster2"); EventListener listener = event -> { }; //when client.subscribe(serviceName, clusterList, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, Constants.DEFAULT_GROUP, Constants.NULL, NamingSelectorFactory.newClusterSelector(clusterList), listener); //then verify(changeNotifier, times(1)).registerListener(Constants.DEFAULT_GROUP, serviceName, wrapper); verify(proxy, times(1)).subscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL); } @Test void testSubscribeWithFullNameAndClusters() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; List clusterList = Arrays.asList("cluster1", "cluster2"); EventListener listener = event -> { }; //when client.subscribe(serviceName, groupName, clusterList, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, groupName, getUniqueClusterString(clusterList), NamingSelectorFactory.newClusterSelector(clusterList), listener); //then verify(changeNotifier, times(1)).registerListener(groupName, serviceName, wrapper); verify(proxy, times(1)).subscribe(serviceName, groupName, Constants.NULL); } @Test public void testSubscribeWithServiceAndCustomSelector() throws NacosException { String serviceName = "service1"; EventListener listener = event -> { }; //when client.subscribe(serviceName, NamingSelectorFactory.HEALTHY_SELECTOR, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, Constants.DEFAULT_GROUP, Constants.NULL, NamingSelectorFactory.HEALTHY_SELECTOR, listener); //then verify(changeNotifier, times(1)).registerListener(Constants.DEFAULT_GROUP, serviceName, wrapper); verify(proxy, times(1)).subscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL); } @Test public void testSubscribeWithFullNameAndCustomSelector() throws NacosException { String serviceName = "service1"; String groupName = "group1"; EventListener listener = event -> { }; //when client.subscribe(serviceName, groupName, NamingSelectorFactory.HEALTHY_SELECTOR, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(serviceName, groupName, Constants.NULL, NamingSelectorFactory.HEALTHY_SELECTOR, listener); //then verify(changeNotifier, times(1)).registerListener(groupName, serviceName, wrapper); verify(proxy, times(1)).subscribe(serviceName, groupName, Constants.NULL); } @Test void testSubscribeWithNullListener() throws NacosException { String serviceName = "service1"; String groupName = "group1"; //when client.subscribe(serviceName, groupName, null); //then verify(changeNotifier, never()).registerListener(groupName, serviceName, new NamingSelectorWrapper(NamingSelectorFactory.newIpSelector(""), null)); verify(proxy, never()).subscribe(serviceName, groupName, ""); } @Test void testSubscribeDuplicate() throws NacosException { String serviceName = "service1"; when(proxy.isSubscribed(serviceName, Constants.DEFAULT_GROUP, StringUtils.EMPTY)).thenReturn(true); ServiceInfo serviceInfo = new ServiceInfo(Constants.DEFAULT_GROUP + "@@" + serviceName); serviceInfo.addHost(new Instance()); when(serviceInfoHolder.getServiceInfo(serviceName, Constants.DEFAULT_GROUP)).thenReturn(serviceInfo); final AtomicBoolean flag = new AtomicBoolean(false); client.subscribe(serviceName, event -> flag.set(true)); assertTrue(flag.get()); } @Test void testUnSubscribeOnlyService() throws NacosException { //given String serviceName = "service1"; EventListener listener = event -> { }; when(changeNotifier.isSubscribed(Constants.DEFAULT_GROUP, serviceName)).thenReturn(false); //when client.unsubscribe(serviceName, listener); //then NamingSelectorWrapper wrapper = new NamingSelectorWrapper( NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener); verify(changeNotifier, times(1)).deregisterListener(Constants.DEFAULT_GROUP, serviceName, wrapper); verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL); } @Test void testUnSubscribeFullName() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; EventListener listener = event -> { }; when(changeNotifier.isSubscribed(groupName, serviceName)).thenReturn(false); //when client.unsubscribe(serviceName, groupName, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper( NamingSelectorFactory.newClusterSelector(Collections.emptyList()), listener); //then verify(changeNotifier, times(1)).deregisterListener(groupName, serviceName, wrapper); verify(proxy, times(1)).unsubscribe(serviceName, groupName, Constants.NULL); } @Test void testUnSubscribeWithServiceAndClusters() throws NacosException { //given String serviceName = "service1"; List clusterList = Arrays.asList("cluster1", "cluster2"); EventListener listener = event -> { }; when(changeNotifier.isSubscribed(Constants.DEFAULT_GROUP, serviceName)).thenReturn(false); //when client.unsubscribe(serviceName, clusterList, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.newClusterSelector(clusterList), listener); //then verify(changeNotifier, times(1)).deregisterListener(Constants.DEFAULT_GROUP, serviceName, wrapper); verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL); } @Test void testUnSubscribeWithFullNameAndClusters() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; List clusterList = Arrays.asList("cluster1", "cluster2"); EventListener listener = event -> { }; when(changeNotifier.isSubscribed(groupName, serviceName)).thenReturn(false); //when client.unsubscribe(serviceName, groupName, clusterList, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.newClusterSelector(clusterList), listener); //then verify(changeNotifier, times(1)).deregisterListener(groupName, serviceName, wrapper); verify(proxy, times(1)).unsubscribe(serviceName, groupName, Constants.NULL); } @Test public void testUnSubscribeWithServiceAndCustomSelector() throws NacosException { //given String serviceName = "service1"; EventListener listener = event -> { }; when(changeNotifier.isSubscribed(Constants.DEFAULT_GROUP, serviceName)).thenReturn(false); //when client.unsubscribe(serviceName, NamingSelectorFactory.HEALTHY_SELECTOR, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.HEALTHY_SELECTOR, listener); //then verify(changeNotifier, times(1)).deregisterListener(Constants.DEFAULT_GROUP, serviceName, wrapper); verify(proxy, times(1)).unsubscribe(serviceName, Constants.DEFAULT_GROUP, Constants.NULL); } @Test public void testUnSubscribeWithFullNameAndCustomSelector() throws NacosException { //given String serviceName = "service1"; String groupName = "group1"; EventListener listener = event -> { }; when(changeNotifier.isSubscribed(groupName, serviceName)).thenReturn(false); //when client.unsubscribe(serviceName, groupName, NamingSelectorFactory.HEALTHY_SELECTOR, listener); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(NamingSelectorFactory.HEALTHY_SELECTOR, listener); //then verify(changeNotifier, times(1)).deregisterListener(groupName, serviceName, wrapper); verify(proxy, times(1)).unsubscribe(serviceName, groupName, Constants.NULL); } @Test void testUnSubscribeWithNullListener() throws NacosException { String serviceName = "service1"; String groupName = "group1"; //when client.unsubscribe(serviceName, groupName, null); //then verify(changeNotifier, never()).deregisterListener(groupName, serviceName, new NamingSelectorWrapper(NamingSelectorFactory.newIpSelector(""), null)); verify(proxy, never()).unsubscribe(serviceName, groupName, ""); } @Test void testGetServicesOfServer1() throws NacosException { //given int pageNo = 1; int pageSize = 10; //when client.getServicesOfServer(pageNo, pageSize); //then verify(proxy, times(1)).getServiceList(pageNo, pageSize, Constants.DEFAULT_GROUP, null); } @Test void testGetServicesOfServer2() throws NacosException { //given int pageNo = 1; int pageSize = 10; String groupName = "group1"; //when client.getServicesOfServer(pageNo, pageSize, groupName); //then verify(proxy, times(1)).getServiceList(pageNo, pageSize, groupName, null); } @Test void testGetServicesOfServer3() throws NacosException { //given int pageNo = 1; int pageSize = 10; AbstractSelector selector = new AbstractSelector("aaa") { @Override public String getType() { return super.getType(); } }; //when client.getServicesOfServer(pageNo, pageSize, selector); //then verify(proxy, times(1)).getServiceList(pageNo, pageSize, Constants.DEFAULT_GROUP, selector); } @Test void testGetServicesOfServer4() throws NacosException { //given int pageNo = 1; int pageSize = 10; String groupName = "group1"; AbstractSelector selector = new AbstractSelector("aaa") { @Override public String getType() { return super.getType(); } }; //when client.getServicesOfServer(pageNo, pageSize, groupName, selector); //then verify(proxy, times(1)).getServiceList(pageNo, pageSize, groupName, selector); } @Test void testGetSubscribeServices() { //when client.getSubscribeServices(); //then verify(changeNotifier, times(1)).getSubscribeServices(); } @Test void testGetServerStatus() { //given when(proxy.serverHealthy()).thenReturn(true); //when String serverStatus = client.getServerStatus(); //then assertEquals("UP", serverStatus); } @Test void testGetServerStatusFail() { //given when(proxy.serverHealthy()).thenReturn(false); //when String serverStatus = client.getServerStatus(); //then assertEquals("DOWN", serverStatus); } @Test void testShutDown() throws NacosException { //when client.shutDown(); //then verify(proxy, times(1)).shutdown(); } @Test void testConstructorWithServerList() throws NacosException, NoSuchFieldException, IllegalAccessException { NacosNamingService namingService = new NacosNamingService("localhost"); try { Field namespaceField = NacosNamingService.class.getDeclaredField("namespace"); namespaceField.setAccessible(true); String namespace = (String) namespaceField.get(namingService); assertEquals(UtilAndComs.DEFAULT_NAMESPACE_ID, namespace); } finally { namingService.shutDown(); } } private Instance mockInstance(String clusterName, boolean healthy) { Instance instance = new Instance(); instance.setClusterName(clusterName); instance.setHealthy(healthy); return instance; } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/backups/FailoverReactorTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.common.utils.ReflectUtils; import io.micrometer.core.instrument.Gauge; import io.micrometer.core.instrument.ImmutableTag; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tag; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; 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.when; @ExtendWith(MockitoExtension.class) class FailoverReactorTest { @Mock ServiceInfoHolder holder; @Mock FailoverDataSource failoverDataSource; FailoverReactor failoverReactor; @BeforeEach void setUp() throws NoSuchFieldException, IllegalAccessException { failoverReactor = new FailoverReactor(holder, UUID.randomUUID().toString()); Field failoverDataSourceField = FailoverReactor.class.getDeclaredField("failoverDataSource"); failoverDataSourceField.setAccessible(true); failoverDataSourceField.set(failoverReactor, failoverDataSource); } @AfterEach void tearDown() throws NacosException { failoverReactor.shutdown(); Gauge gauge = Metrics.globalRegistry.find("nacos_naming_client_failover_instances").tag("service_name", "g@@s1") .gauge(); if (gauge != null) { Metrics.globalRegistry.remove(gauge); } } @Test void testIsFailoverSwitch() throws NacosException { assertFalse(failoverReactor.isFailoverSwitch()); } @Test void testGetService() throws NacosException { ServiceInfo info = failoverReactor.getService("aa@@bb"); assertEquals(new ServiceInfo("aa@@bb").toString(), info.toString()); } @Test void testRefreshFromDisabledToEnabled() throws InterruptedException { // make sure the first no delay refresh thread finished. TimeUnit.MILLISECONDS.sleep(500); FailoverSwitch mockFailoverSwitch = new FailoverSwitch(true); when(failoverDataSource.getSwitch()).thenReturn(mockFailoverSwitch); Map map = new HashMap<>(); ServiceInfo serviceInfo = new ServiceInfo("a@@b"); serviceInfo.addHost(new Instance()); map.put("a@@b", NamingFailoverData.newNamingFailoverData(serviceInfo)); when(failoverDataSource.getFailoverData()).thenReturn(map); // waiting refresh thread work TimeUnit.MILLISECONDS.sleep(5500); ServiceInfo actual = failoverReactor.getService("a@@b"); assertEquals(serviceInfo, actual); } @Test void testRefreshFromDisabledToEnabledWithException() throws InterruptedException { // make sure the first no delay refresh thread finished. TimeUnit.MILLISECONDS.sleep(500); FailoverSwitch mockFailoverSwitch = new FailoverSwitch(true); when(failoverDataSource.getSwitch()).thenReturn(mockFailoverSwitch); when(failoverDataSource.getFailoverData()).thenReturn(null); // waiting refresh thread work TimeUnit.MILLISECONDS.sleep(5500); assertTrue(((Map) ReflectUtils.getFieldValue(failoverReactor, "serviceMap", new HashMap<>())).isEmpty()); } @Test void testRefreshFromEnabledToDisabled() throws InterruptedException, NoSuchFieldException, IllegalAccessException { // make sure the first no delay refresh thread finished. TimeUnit.MILLISECONDS.sleep(500); FailoverSwitch mockFailoverSwitch = new FailoverSwitch(false); when(failoverDataSource.getSwitch()).thenReturn(mockFailoverSwitch); Field failoverSwitchEnableField = FailoverReactor.class.getDeclaredField("failoverSwitchEnable"); failoverSwitchEnableField.setAccessible(true); failoverSwitchEnableField.set(failoverReactor, true); Map map = new HashMap<>(); ServiceInfo serviceInfo = new ServiceInfo("a@@b"); serviceInfo.addHost(new Instance()); map.put("a@@b", serviceInfo); when(holder.getServiceInfoMap()).thenReturn(map); Field serviceMapField = FailoverReactor.class.getDeclaredField("serviceMap"); serviceMapField.setAccessible(true); serviceMapField.set(failoverReactor, map); // waiting refresh thread work TimeUnit.MILLISECONDS.sleep(5500); ServiceInfo actual = failoverReactor.getService("a@@b"); assertNotEquals(serviceInfo, actual); } @Test void testFailoverServiceCntMetrics() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { ((Map) ReflectUtils.getFieldValue(failoverReactor, "serviceMap", new HashMap<>())).put("g@@s1", new ServiceInfo()); Method method = FailoverReactor.class.getDeclaredMethod("failoverServiceCntMetrics"); method.setAccessible(true); method.invoke(failoverReactor); // No exception } @Test void testFailoverServiceCntMetricsClear() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { String serviceName = "g@@s1"; List tags = new ArrayList<>(); tags.add(new ImmutableTag("service_name", serviceName)); Gauge.builder("nacos_naming_client_failover_instances", () -> 1).tags(tags).register(Metrics.globalRegistry); ((Map) ReflectUtils.getFieldValue(failoverReactor, "serviceMap", new HashMap<>())).put(serviceName, new ServiceInfo()); Method method = FailoverReactor.class.getDeclaredMethod("failoverServiceCntMetricsClear"); method.setAccessible(true); method.invoke(failoverReactor); // No exception } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/backups/datasource/DiskFailoverDataSourceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.backups.datasource; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.naming.backups.FailoverData; import com.alibaba.nacos.client.naming.backups.FailoverSwitch; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.Map; 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 DiskFailoverDataSourceTest { DiskFailoverDataSource dataSource; @BeforeEach void setUp() { dataSource = new DiskFailoverDataSource(); } @Test void testGetSwitchWithNonExistFailoverSwitchFile() { FailoverSwitch actual = dataSource.getSwitch(); assertFalse(actual.getEnabled()); } @Test void testGetSwitchForFailoverDisabled() throws NoSuchFieldException, IllegalAccessException { String dir = DiskFailoverDataSourceTest.class.getClassLoader().getResource("failover_test/disabled").getPath(); injectFailOverDir(dir); assertFalse(dataSource.getSwitch().getEnabled()); Map actual = dataSource.getFailoverData(); assertTrue(actual.isEmpty()); } @Test void testGetSwitchForFailoverEnabled() throws NoSuchFieldException, IllegalAccessException { String dir = DiskFailoverDataSourceTest.class.getClassLoader().getResource("failover_test/enabled").getPath(); injectFailOverDir(dir); assertTrue(dataSource.getSwitch().getEnabled()); Map actual = dataSource.getFailoverData(); assertEquals(1, actual.size()); assertTrue(actual.containsKey("legal@@with_name@@file")); assertEquals(FailoverData.DataType.naming, actual.get("legal@@with_name@@file").getDataType()); assertEquals("1.1.1.1", ((ServiceInfo) actual.get("legal@@with_name@@file").getData()).getHosts().get(0).getIp()); } @Test void testGetFailoverDataForFailoverDisabled() { Map actual = dataSource.getFailoverData(); assertTrue(actual.isEmpty()); } private void injectFailOverDir(String failoverDir) throws NoSuchFieldException, IllegalAccessException { Field failoverDirField = DiskFailoverDataSource.class.getDeclaredField("failoverDir"); failoverDirField.setAccessible(true); failoverDirField.set(dataSource, failoverDir); } @Test void testGetSwitchForFailoverEnabledKeep() throws NoSuchFieldException, IllegalAccessException { String dir = DiskFailoverDataSourceTest.class.getClassLoader().getResource("failover_test/enabled").getPath(); injectFailOverDir(dir); assertTrue(dataSource.getSwitch().getEnabled()); assertTrue(dataSource.getSwitch().getEnabled()); } @Test void testGetSwitchForFailoverDisabledKeep() throws NoSuchFieldException, IllegalAccessException { String dir = DiskFailoverDataSourceTest.class.getClassLoader().getResource("failover_test/disabled").getPath(); injectFailOverDir(dir); assertFalse(dataSource.getSwitch().getEnabled()); assertFalse(dataSource.getSwitch().getEnabled()); } @Test void testGetSwitchWithException() throws NoSuchFieldException, IllegalAccessException { injectFailOverDir("invalid\\0path"); assertFalse(dataSource.getSwitch().getEnabled()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/cache/DiskCacheTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.File; import java.io.UnsupportedEncodingException; import java.util.Collections; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; class DiskCacheTest { private static final String CACHE_DIR = DiskCacheTest.class.getClassLoader().getResource("disk_cache_test").getPath() + "cache/"; private ServiceInfo serviceInfo; private Instance instance; @BeforeEach void setUp() { System.out.println(CACHE_DIR); serviceInfo = new ServiceInfo("G@@testName", "testClusters"); instance = new Instance(); instance.setClusterName("testClusters"); instance.setIp("1.1.1.1"); instance.setPort(1234); instance.setServiceName("testName"); instance.addMetadata("chinese", "中文"); serviceInfo.setHosts(Collections.singletonList(instance)); } @AfterEach void tearDown() { File file = new File(CACHE_DIR); if (file.exists() && file.list().length > 0) { for (File each : file.listFiles()) { each.delete(); } file.deleteOnExit(); } } @Test void testCache() { DiskCache.write(serviceInfo, CACHE_DIR); Map actual = DiskCache.read(CACHE_DIR); assertEquals(1, actual.size()); assertTrue(actual.containsKey(serviceInfo.getKey())); assertServiceInfo(actual.get(serviceInfo.getKey()), serviceInfo); } @Test void testWriteCacheWithErrorPath() { File file = new File(CACHE_DIR, serviceInfo.getKeyEncoded()); try { file.mkdirs(); DiskCache.write(serviceInfo, CACHE_DIR); assertTrue(file.isDirectory()); } finally { file.delete(); } } @Test void testReadCacheForAllSituation() { String dir = DiskCacheTest.class.getClassLoader().getResource("disk_cache_test").getPath(); Map actual = DiskCache.read(dir); assertEquals(2, actual.size()); assertTrue(actual.containsKey("legal@@no_name@@file")); assertEquals("1.1.1.1", actual.get("legal@@no_name@@file").getHosts().get(0).getIp()); assertTrue(actual.containsKey("legal@@with_name@@file")); assertEquals("1.1.1.1", actual.get("legal@@with_name@@file").getHosts().get(0).getIp()); } @Test void testReadCacheForNullFile() { Map actual = DiskCache.read(null); assertTrue(actual.isEmpty()); } @Test void testParseServiceInfoFromNonExistFile() throws UnsupportedEncodingException { File file = new File("non%40%40exist%40%40file"); Map actual = DiskCache.parseServiceInfoFromCache(file); assertTrue(actual.isEmpty()); } @Test void testCreateFileIfAbsentForDir() throws Throwable { assertThrows(IllegalStateException.class, () -> { File file = mock(File.class); DiskCache.createFileIfAbsent(file, true); }); } @Test void testCreateFileIfAbsentForFile() throws Throwable { assertThrows(IllegalStateException.class, () -> { File file = mock(File.class); DiskCache.createFileIfAbsent(file, false); }); } private void assertServiceInfo(ServiceInfo actual, ServiceInfo expected) { assertEquals(actual.getName(), expected.getName()); assertEquals(actual.getGroupName(), expected.getGroupName()); assertEquals(actual.getClusters(), expected.getClusters()); assertEquals(actual.getCacheMillis(), expected.getCacheMillis()); assertEquals(actual.getLastRefTime(), expected.getLastRefTime()); assertEquals(actual.getKey(), expected.getKey()); assertHosts(actual.getHosts(), expected.getHosts()); } private void assertHosts(List actual, List expected) { assertEquals(actual.size(), expected.size()); for (int i = 0; i < expected.size(); i++) { assertInstance(actual.get(i), expected.get(i)); } } private void assertInstance(Instance actual, Instance expected) { assertEquals(actual.getServiceName(), expected.getServiceName()); assertEquals(actual.getClusterName(), expected.getClusterName()); assertEquals(actual.getIp(), expected.getIp()); assertEquals(actual.getPort(), expected.getPort()); assertEquals(actual.getMetadata(), expected.getMetadata()); } @Test void testGetLineSeparator() { String lineSeparator = DiskCache.getLineSeparator(); assertTrue(lineSeparator.length() > 0); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/cache/NamingFuzzyWatchServiceListHolderTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.naming.listener.AbstractFuzzyWatchEventWatcher; import com.alibaba.nacos.api.naming.listener.FuzzyWatchChangeEvent; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchRequest; import com.alibaba.nacos.api.naming.remote.request.NamingFuzzyWatchSyncRequest; import com.alibaba.nacos.api.naming.remote.response.NamingFuzzyWatchResponse; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.event.NamingFuzzyWatchLoadEvent; import com.alibaba.nacos.client.naming.remote.gprc.NamingFuzzyWatchNotifyRequestHandler; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; 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.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicInteger; import static com.alibaba.nacos.api.common.Constants.FINISH_FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.ADD_SERVICE; import static com.alibaba.nacos.api.common.Constants.ServiceChangedType.DELETE_SERVICE; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class NamingFuzzyWatchServiceListHolderTest { NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; NamingFuzzyWatchNotifyRequestHandler namingFuzzyWatchNotifyRequestHandler; String eventScope = "scope" + System.currentTimeMillis(); @Mock NamingGrpcClientProxy namingGrpcClientProxy; @Mock Connection connection; @BeforeEach void before() { namingFuzzyWatchServiceListHolder = new NamingFuzzyWatchServiceListHolder(eventScope); namingFuzzyWatchServiceListHolder.registerNamingGrpcClientProxy(namingGrpcClientProxy); namingFuzzyWatchNotifyRequestHandler = new NamingFuzzyWatchNotifyRequestHandler( namingFuzzyWatchServiceListHolder); when(namingGrpcClientProxy.isAbilitySupportedByServer(AbilityKey.SERVER_FUZZY_WATCH)).thenReturn(true); } @AfterEach void after() { } @Test void testOnEventWatchNotify() throws InterruptedException { String serviceKey = NamingUtils.getServiceKey("namespace123", "group", "serviceName124"); String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); AtomicInteger watcherFlag = new AtomicInteger(0); namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(generatePattern, new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { watcherFlag.incrementAndGet(); } }); NamingFuzzyWatchChangeNotifyRequest namingFuzzyWatchChangeNotifyRequest = new NamingFuzzyWatchChangeNotifyRequest( serviceKey, ADD_SERVICE); Response response = namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchChangeNotifyRequest, connection); Assertions.assertNotNull(response); Thread.sleep(100L); Assertions.assertEquals(1, watcherFlag.get()); Response duplicatedResponse = namingFuzzyWatchNotifyRequestHandler.requestReply( namingFuzzyWatchChangeNotifyRequest, connection); Assertions.assertNotNull(duplicatedResponse); Thread.sleep(100L); Assertions.assertEquals(1, watcherFlag.get()); namingFuzzyWatchChangeNotifyRequest.setChangedType(DELETE_SERVICE); Response deleteResponse = namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchChangeNotifyRequest, connection); Assertions.assertNotNull(deleteResponse); Thread.sleep(100L); Assertions.assertEquals(2, watcherFlag.get()); } @Test void testOnEventWatchSync() throws InterruptedException { String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); AtomicInteger watcherFlag = new AtomicInteger(0); NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( generatePattern, new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { watcherFlag.incrementAndGet(); } }); String serviceKey1 = NamingUtils.getServiceKey("namespace123", "group", "serviceName124"); String serviceKey2 = NamingUtils.getServiceKey("namespace123", "group", "serviceName124234"); Set contexts = new HashSet<>(); contexts.add(NamingFuzzyWatchSyncRequest.Context.build(serviceKey1, ADD_SERVICE)); contexts.add(NamingFuzzyWatchSyncRequest.Context.build(serviceKey2, ADD_SERVICE)); //init notify NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequest = new NamingFuzzyWatchSyncRequest(generatePattern, FUZZY_WATCH_INIT_NOTIFY, contexts); Response responseInitNotify = namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchSyncRequest, connection); Assertions.assertNotNull(responseInitNotify); Thread.sleep(100L); Assertions.assertEquals(2, watcherFlag.get()); try { Future> newFuture = namingFuzzyWatchContext.createNewFuture(); newFuture.get(100L, TimeUnit.MILLISECONDS); Assertions.fail(); } catch (TimeoutException timeoutException) { Assertions.assertTrue(true); } catch (ExecutionException e) { throw new RuntimeException(e); } //init finish notify NamingFuzzyWatchSyncRequest namingFuzzyWatchSyncRequestFinish = new NamingFuzzyWatchSyncRequest(generatePattern, FINISH_FUZZY_WATCH_INIT_NOTIFY, null); Response responseInitNotifyFinish = namingFuzzyWatchNotifyRequestHandler.requestReply( namingFuzzyWatchSyncRequestFinish, connection); Assertions.assertNotNull(responseInitNotifyFinish); Thread.sleep(100L); Assertions.assertEquals(2, watcherFlag.get()); try { Future> newFuture = namingFuzzyWatchContext.createNewFuture(); ListView stringListView = newFuture.get(); Assertions.assertTrue( stringListView.getData().contains(serviceKey1) && stringListView.getData().contains(serviceKey2)); Assertions.assertFalse(newFuture.isCancelled()); Assertions.assertTrue(newFuture.isDone()); try { newFuture.cancel(true); Assertions.fail(); } catch (UnsupportedOperationException unsupportedOperationException) { Assertions.assertTrue(true); } } catch (Exception timeoutException) { Assertions.fail(); } namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(generatePattern, new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { watcherFlag.incrementAndGet(); } }); Thread.sleep(100L); Assertions.assertEquals(4, watcherFlag.get()); } @Test void testOnEventLoadEvent() throws InterruptedException { String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); AtomicInteger watcherPatternOverFlag = new AtomicInteger(0); AtomicInteger watcherServiceOverFlag = new AtomicInteger(0); namingFuzzyWatchServiceListHolder.registerFuzzyWatcher(generatePattern, new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { } @Override public void onPatternOverLimit() { watcherPatternOverFlag.incrementAndGet(); } @Override public void onServiceReachUpLimit() { watcherServiceOverFlag.incrementAndGet(); } }); NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEvent = NamingFuzzyWatchLoadEvent.buildEvent( FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), generatePattern, eventScope); namingFuzzyWatchServiceListHolder.onEvent(namingFuzzyWatchLoadEvent); Assertions.assertEquals(1, watcherPatternOverFlag.get()); Assertions.assertEquals(0, watcherServiceOverFlag.get()); NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEventDup = NamingFuzzyWatchLoadEvent.buildEvent( FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), generatePattern, eventScope); namingFuzzyWatchServiceListHolder.onEvent(namingFuzzyWatchLoadEventDup); Assertions.assertEquals(1, watcherPatternOverFlag.get()); Assertions.assertEquals(0, watcherServiceOverFlag.get()); NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.getFuzzyWatchContext( generatePattern); namingFuzzyWatchContext.clearOverLimitTs(); NamingFuzzyWatchLoadEvent namingFuzzyWatchLoadEvent2 = NamingFuzzyWatchLoadEvent.buildEvent( FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), generatePattern, eventScope); namingFuzzyWatchServiceListHolder.onEvent(namingFuzzyWatchLoadEvent2); Assertions.assertEquals(1, watcherPatternOverFlag.get()); Assertions.assertEquals(1, watcherServiceOverFlag.get()); } @Test void testExecuteNamingFuzzyWatch() throws NacosException, InterruptedException { String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); AtomicInteger watcherPatternOverFlag = new AtomicInteger(0); AtomicInteger watcherServiceOverFlag = new AtomicInteger(0); AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { } @Override public void onPatternOverLimit() { watcherPatternOverFlag.incrementAndGet(); } @Override public void onServiceReachUpLimit() { watcherServiceOverFlag.incrementAndGet(); } }; NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( generatePattern, abstractFuzzyWatchEventWatcher); Assertions.assertFalse(namingFuzzyWatchContext.isConsistentWithServer()); //check success fuzzy watch when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenReturn( NamingFuzzyWatchResponse.buildSuccessResponse()); namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); Assertions.assertTrue(namingFuzzyWatchContext.isConsistentWithServer()); //check sync skip namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); namingFuzzyWatchServiceListHolder.resetConsistenceStatus(); //check over fuzzy watch pattern count when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenThrow( new NacosException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg())); namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); Thread.sleep(1000L); Assertions.assertEquals(1, watcherPatternOverFlag.get()); Assertions.assertEquals(0, watcherServiceOverFlag.get()); namingFuzzyWatchContext.clearOverLimitTs(); //check over fuzzy watch service count when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenThrow( new NacosException(FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getMsg())); namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); Thread.sleep(100L); Assertions.assertEquals(1, watcherPatternOverFlag.get()); Assertions.assertEquals(1, watcherServiceOverFlag.get()); when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenThrow( new NacosException(500, "unknow")); namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); //check cancel fuzzy watch namingFuzzyWatchContext.removeWatcher(abstractFuzzyWatchEventWatcher); when(namingGrpcClientProxy.fuzzyWatchRequest(any(NamingFuzzyWatchRequest.class))).thenReturn( NamingFuzzyWatchResponse.buildSuccessResponse()); namingFuzzyWatchServiceListHolder.executeNamingFuzzyWatch(); Assertions.assertNull(namingFuzzyWatchServiceListHolder.getFuzzyWatchContext(generatePattern)); } @Test void testSyncWhenWatcherFail() throws NacosException { String serviceKey = NamingUtils.getServiceKey("namespace123", "group", "serviceName124"); String generatePattern = FuzzyGroupKeyPattern.generatePattern("serviceName124*", "group", "namespace123"); AtomicInteger watcherFlag = new AtomicInteger(0); AbstractFuzzyWatchEventWatcher abstractFuzzyWatchEventWatcher = new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { int get = watcherFlag.incrementAndGet(); if (get < 2) { System.out.println("times " + get + " fail"); throw new RuntimeException("mock exception"); } else { System.out.println("times " + get + " success"); } } }; NamingFuzzyWatchContext namingFuzzyWatchContext = namingFuzzyWatchServiceListHolder.registerFuzzyWatcher( generatePattern, abstractFuzzyWatchEventWatcher); NamingFuzzyWatchChangeNotifyRequest namingFuzzyWatchChangeNotifyRequest = new NamingFuzzyWatchChangeNotifyRequest( serviceKey, ADD_SERVICE); namingFuzzyWatchNotifyRequestHandler.requestReply(namingFuzzyWatchChangeNotifyRequest, connection); //notify 1, fail namingFuzzyWatchContext.syncFuzzyWatchers(); //notify 2,success namingFuzzyWatchContext.syncFuzzyWatchers(); //notify 3 +, will not trigger watchers. namingFuzzyWatchContext.syncFuzzyWatchers(); namingFuzzyWatchContext.syncFuzzyWatchers(); namingFuzzyWatchContext.syncFuzzyWatchers(); //expect 2 times notified Assertions.assertEquals(2, watcherFlag.get()); } @Test void testFuzzyWatchNotSupport() { when(namingGrpcClientProxy.isAbilitySupportedByServer(AbilityKey.SERVER_FUZZY_WATCH)).thenReturn(false); Assertions.assertThrows(NacosRuntimeException.class, () -> { namingFuzzyWatchServiceListHolder.registerFuzzyWatcher("*", new AbstractFuzzyWatchEventWatcher() { @Override public void onEvent(FuzzyWatchChangeEvent event) { } }); }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/cache/ServiceInfoHolderTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.cache; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.monitor.MetricsMonitor; import com.alibaba.nacos.client.naming.backups.FailoverReactor; import io.prometheus.client.Gauge; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.doThrow; 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 ServiceInfoHolderTest { NacosClientProperties nacosClientProperties; ServiceInfoHolder holder; @BeforeEach void setUp() throws Exception { nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(); holder = new ServiceInfoHolder("aa", "scope-001", nacosClientProperties); } @AfterEach void tearDown() throws Exception { } @Test void testGetServiceInfoMap() throws NoSuchFieldException, IllegalAccessException { assertEquals(0, holder.getServiceInfoMap().size()); Field fieldNotifierEventScope = ServiceInfoHolder.class.getDeclaredField("notifierEventScope"); fieldNotifierEventScope.setAccessible(true); assertEquals("scope-001", fieldNotifierEventScope.get(holder)); } @Test void testProcessServiceInfo() { ServiceInfo info = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); Instance instance2 = createInstance("1.1.1.2", 2); List hosts = new ArrayList<>(); hosts.add(instance1); hosts.add(instance2); info.setHosts(hosts); ServiceInfo actual1 = holder.processServiceInfo(info); assertEquals(info, actual1); Instance newInstance1 = createInstance("1.1.1.1", 1); newInstance1.setWeight(2.0); Instance instance3 = createInstance("1.1.1.3", 3); List hosts2 = new ArrayList<>(); hosts2.add(newInstance1); hosts2.add(instance3); ServiceInfo info2 = new ServiceInfo("a@@b@@c"); info2.setHosts(hosts2); ServiceInfo actual2 = holder.processServiceInfo(info2); assertEquals(info2, actual2); } @Test void testProcessServiceInfoEnableClientMetricsTrue() { ServiceInfoHolder holder = createServiceInfoHolder(true); ServiceInfo info = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); Instance instance2 = createInstance("1.1.1.2", 2); List hosts = new ArrayList<>(); hosts.add(instance1); hosts.add(instance2); info.setHosts(hosts); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getServiceInfoMapSizeMonitor).thenReturn(mockGaugeChild); holder.processServiceInfo(info); verify(mockGaugeChild, times(1)).set(1); } } @Test void testProcessServiceInfoEnableClientMetricsFalse() { ServiceInfoHolder holder = createServiceInfoHolder(false); ServiceInfo info = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); Instance instance2 = createInstance("1.1.1.2", 2); List hosts = new ArrayList<>(); hosts.add(instance1); hosts.add(instance2); info.setHosts(hosts); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { holder.processServiceInfo(info); mockedMetricsMonitor.verify(MetricsMonitor::getServiceInfoMapSizeMonitor, never()); } } @Test void testProcessServiceInfoEnableClientMetricsNotSet() { ServiceInfoHolder holder = createServiceInfoHolder(null); ServiceInfo info = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); Instance instance2 = createInstance("1.1.1.2", 2); List hosts = new ArrayList<>(); hosts.add(instance1); hosts.add(instance2); info.setHosts(hosts); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getServiceInfoMapSizeMonitor).thenReturn(mockGaugeChild); holder.processServiceInfo(info); verify(mockGaugeChild, times(1)).set(1); } } @Test void testProcessServiceInfoSetThrowsException() { ServiceInfoHolder holder = createServiceInfoHolder(true); ServiceInfo info = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); Instance instance2 = createInstance("1.1.1.2", 2); List hosts = new ArrayList<>(); hosts.add(instance1); hosts.add(instance2); info.setHosts(hosts); Gauge.Child mockGaugeChild = mock(Gauge.Child.class); RuntimeException exception = new RuntimeException("Mocked exception"); try (MockedStatic mockedMetricsMonitor = Mockito.mockStatic(MetricsMonitor.class)) { mockedMetricsMonitor.when(MetricsMonitor::getServiceInfoMapSizeMonitor).thenReturn(mockGaugeChild); doThrow(exception).when(mockGaugeChild).set(anyInt()); ServiceInfo actual2 = holder.processServiceInfo(info); assertEquals(info, actual2); } } private ServiceInfoHolder createServiceInfoHolder(Boolean enableClientMetrics) { Properties properties = new Properties(); if (enableClientMetrics != null) { properties.put(PropertyKeyConst.ENABLE_CLIENT_METRICS, String.valueOf(enableClientMetrics)); } NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); String namespace = "test-namespace"; String notifierEventScope = "scope-001"; return new ServiceInfoHolder(namespace, notifierEventScope, clientProperties); } private Instance createInstance(String ip, int port) { Instance instance = new Instance(); instance.setIp(ip); instance.setPort(port); return instance; } @Test void testProcessServiceInfo2() { String json = "{\"groupName\":\"a\",\"name\":\"b\",\"clusters\":\"c\"}"; ServiceInfo actual = holder.processServiceInfo(json); ServiceInfo expect = new ServiceInfo("a@@b@@c"); expect.setJsonFromServer(json); assertEquals(expect.getKey(), actual.getKey()); } @Test void testProcessServiceInfoWithPushEmpty() throws NacosException { ServiceInfo oldInfo = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); Instance instance2 = createInstance("1.1.1.2", 2); List hosts = new ArrayList<>(); hosts.add(instance1); hosts.add(instance2); oldInfo.setHosts(hosts); nacosClientProperties.setProperty(PropertyKeyConst.NAMING_PUSH_EMPTY_PROTECTION, "true"); holder.shutdown(); holder = new ServiceInfoHolder("aa", "scope-001", nacosClientProperties); holder.processServiceInfo(oldInfo); ServiceInfo newInfo = new ServiceInfo("a@@b@@c"); final ServiceInfo actual = holder.processServiceInfo(newInfo); assertEquals(oldInfo.getKey(), actual.getKey()); assertEquals(2, actual.getHosts().size()); } @Test void testProcessNullServiceInfo() { assertNull(holder.processServiceInfo(new ServiceInfo())); } @Test void testProcessServiceInfoForOlder() { ServiceInfo info = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); Instance instance2 = createInstance("1.1.1.2", 2); List hosts = new ArrayList<>(); hosts.add(instance1); hosts.add(instance2); info.setHosts(hosts); info.setLastRefTime(System.currentTimeMillis()); holder.processServiceInfo(info); ServiceInfo olderInfo = new ServiceInfo("a@@b@@c"); olderInfo.setLastRefTime(0L); final ServiceInfo actual = holder.processServiceInfo(olderInfo); assertEquals(olderInfo, actual); } @Test void testGetServiceInfo() { ServiceInfo info = new ServiceInfo("a@@b@@c"); Instance instance1 = createInstance("1.1.1.1", 1); List hosts = new ArrayList<>(); hosts.add(instance1); info.setHosts(hosts); ServiceInfo expect = holder.processServiceInfo(info); String serviceName = "b"; String groupName = "a"; ServiceInfo actual = holder.getServiceInfo(serviceName, groupName); // Verify it's a clone (different object) assertNotSame(expect, actual); // Verify content is the same assertEquals(expect.getKey(), actual.getKey()); assertEquals(expect.getHosts().size(), actual.getHosts().size()); assertEquals(expect.getHosts().get(0), actual.getHosts().get(0)); // Verify hosts list is different assertNotSame(expect.getHosts(), actual.getHosts()); } @Test void testGetServiceInfoReturnsNull() { String serviceName = "nonExistent"; String groupName = "group"; ServiceInfo actual = holder.getServiceInfo(serviceName, groupName); assertNull(actual); } @Test void testShutdown() throws NacosException, NoSuchFieldException, IllegalAccessException { Field field = ServiceInfoHolder.class.getDeclaredField("failoverReactor"); field.setAccessible(true); FailoverReactor reactor = (FailoverReactor) field.get(holder); Field executorService = FailoverReactor.class.getDeclaredField("executorService"); executorService.setAccessible(true); ScheduledExecutorService pool = (ScheduledExecutorService) executorService.get(reactor); assertFalse(pool.isShutdown()); holder.shutdown(); assertTrue(pool.isShutdown()); } @Test void testConstructWithCacheLoad() throws NacosException { nacosClientProperties.setProperty(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START, "true"); nacosClientProperties.setProperty(PropertyKeyConst.NAMING_CACHE_REGISTRY_DIR, "non-exist"); holder.shutdown(); holder = new ServiceInfoHolder("aa", "scope-001", nacosClientProperties); assertEquals(System.getProperty("user.home") + "/nacos/non-exist/naming/aa", holder.getCacheDir()); assertTrue(holder.getServiceInfoMap().isEmpty()); } @Test void testIsFailoverSwitch() throws IllegalAccessException, NoSuchFieldException, NacosException { FailoverReactor mock = injectMockFailoverReactor(); when(mock.isFailoverSwitch()).thenReturn(true); assertTrue(holder.isFailoverSwitch()); } @Test void testGetFailoverServiceInfo() throws IllegalAccessException, NoSuchFieldException, NacosException { FailoverReactor mock = injectMockFailoverReactor(); ServiceInfo serviceInfo = new ServiceInfo("a@@b@@c"); when(mock.getService("a@@b")).thenReturn(serviceInfo); assertEquals(serviceInfo, holder.getFailoverServiceInfo("b", "a")); } private FailoverReactor injectMockFailoverReactor() throws NoSuchFieldException, IllegalAccessException, NacosException { Field field = ServiceInfoHolder.class.getDeclaredField("failoverReactor"); field.setAccessible(true); FailoverReactor old = (FailoverReactor) field.get(holder); old.shutdown(); FailoverReactor mock = mock(FailoverReactor.class); field.set(holder, mock); return mock; } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/core/BalancerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; 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; class BalancerTest { @Test void testGetHostByRandomWeightNull() { assertNull(Balancer.getHostByRandomWeight(null)); assertNull(Balancer.getHostByRandomWeight(new ArrayList<>())); } @Test void testGetHostByRandomWeight() { List list = new ArrayList<>(); Instance instance1 = new Instance(); list.add(instance1); final Instance actual = Balancer.getHostByRandomWeight(list); assertEquals(instance1, actual); } @Test void testSelectHost() { List hosts = new ArrayList<>(); Instance instance1 = new Instance(); hosts.add(instance1); ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.setHosts(hosts); final Instance actual = Balancer.RandomByWeight.selectHost(serviceInfo); assertEquals(instance1, actual); } @Test void testSelectHostEmpty() { Throwable exception = assertThrows(IllegalStateException.class, () -> { ServiceInfo serviceInfo = new ServiceInfo(); Balancer.RandomByWeight.selectHost(serviceInfo); }); assertTrue(exception.getMessage().contains("no host to srv for serviceInfo: null")); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/core/NamingServerListManagerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.address.EndpointServerListProvider; import com.alibaba.nacos.client.address.ServerListProvider; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; 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.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.Map; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.contains; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class NamingServerListManagerTest { @Mock NacosRestTemplate nacosRestTemplate; NacosRestTemplate cachedNacosRestTemplate; NacosClientProperties clientProperties; HttpRestResult httpRestResult; NamingServerListManager serverListManager; @BeforeEach void setUp() throws Exception { clientProperties = NacosClientProperties.PROTOTYPE.derive(); Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); cachedNacosRestTemplate = restMap.get( "com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager$NamingHttpClientFactory"); restMap.put("com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager$NamingHttpClientFactory", nacosRestTemplate); httpRestResult = new HttpRestResult<>(); httpRestResult.setData("127.0.0.1:8848"); httpRestResult.setCode(200); Mockito.when(nacosRestTemplate.get(contains("127.0.0.1"), any(), any(), any())).thenReturn(httpRestResult); } @AfterEach void tearDown() throws Exception { if (null != cachedNacosRestTemplate) { Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); restMap.put("com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager$NamingHttpClientFactory", cachedNacosRestTemplate); } if (null != serverListManager) { serverListManager.shutdown(); } } @Test void testConstructWithAddr() throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848,127.0.0.1:8849"); serverListManager = new NamingServerListManager(properties); serverListManager.start(); final List serverList = serverListManager.getServerList(); assertEquals(2, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); assertEquals("127.0.0.1:8849", serverList.get(1)); } @Test void testConstructWithAddrTryToRefresh() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848,127.0.0.1:8849"); serverListManager = new NamingServerListManager(properties); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(2, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); assertEquals("127.0.0.1:8849", serverList.get(1)); serverList = serverListManager.getServerList(); assertEquals(2, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); assertEquals("127.0.0.1:8849", serverList.get(1)); } @Test void testConstructWithEndpointAndRefresh() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.ENDPOINT, "127.0.0.1"); serverListManager = new NamingServerListManager(properties); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); httpRestResult.setData("127.0.0.1:8848\n127.0.0.1:8948"); mockThreadInvoke(serverListManager, true); serverList = serverListManager.getServerList(); assertEquals(2, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); assertEquals("127.0.0.1:8948", serverList.get(1)); } @Test void testConstructWithEndpointAndTimedNotNeedRefresh() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.ENDPOINT, "127.0.0.1"); serverListManager = new NamingServerListManager(properties); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); httpRestResult.setData("127.0.0.1:8848\n127.0.0.1:8948"); serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); } @Test void testConstructWithEndpointAndRefreshEmpty() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.ENDPOINT, "127.0.0.1"); serverListManager = new NamingServerListManager(properties); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); httpRestResult.setData(""); serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); } @Test void testConstructWithEndpointAndRefreshException() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.ENDPOINT, "127.0.0.1"); serverListManager = new NamingServerListManager(properties); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); httpRestResult.setCode(500); serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); } @Test void testConstructWithEndpointWithCustomPathAndName() throws Exception { clientProperties.setProperty(PropertyKeyConst.CONTEXT_PATH, "aaa"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME, "bbb"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT, "127.0.0.1"); Mockito.reset(nacosRestTemplate); Mockito.when(nacosRestTemplate.get(eq("http://127.0.0.1:8080/aaa/bbb"), any(), any(), any())) .thenReturn(httpRestResult); serverListManager = new NamingServerListManager(clientProperties, ""); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); } @Test void testConstructWithEndpointWithEndpointPathAndName() throws Exception { clientProperties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, "aaa"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME, "bbb"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT, "127.0.0.1"); Mockito.reset(nacosRestTemplate); Mockito.when(nacosRestTemplate.get(eq("http://127.0.0.1:8080/aaa/bbb"), any(), any(), any())) .thenReturn(httpRestResult); serverListManager = new NamingServerListManager(clientProperties, ""); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); } @Test void testConstructEndpointContextPathPriority() throws Exception { clientProperties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, "aaa"); clientProperties.setProperty(PropertyKeyConst.CONTEXT_PATH, "bbb"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME, "ccc"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT, "127.0.0.1"); Mockito.reset(nacosRestTemplate); Mockito.when(nacosRestTemplate.get(eq("http://127.0.0.1:8080/aaa/ccc"), any(), any(), any())) .thenReturn(httpRestResult); serverListManager = new NamingServerListManager(clientProperties, ""); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); } @Test void testConstructEndpointContextPathIsEmpty() throws Exception { clientProperties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, ""); clientProperties.setProperty(PropertyKeyConst.CONTEXT_PATH, "bbb"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME, "ccc"); clientProperties.setProperty(PropertyKeyConst.ENDPOINT, "127.0.0.1"); Mockito.reset(nacosRestTemplate); Mockito.when(nacosRestTemplate.get(eq("http://127.0.0.1:8080/bbb/ccc"), any(), any(), any())) .thenReturn(httpRestResult); serverListManager = new NamingServerListManager(clientProperties, ""); serverListManager.start(); List serverList = serverListManager.getServerList(); assertEquals(1, serverList.size()); assertEquals("127.0.0.1:8848", serverList.get(0)); } @Test void testIsDomain() throws Exception { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848"); serverListManager = new NamingServerListManager(properties); serverListManager.start(); assertTrue(serverListManager.isDomain()); assertEquals("127.0.0.1:8848", serverListManager.getNacosDomain()); } @Test void testGetCurrentServer() throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848"); final NamingServerListManager serverListManager = new NamingServerListManager(properties); serverListManager.start(); assertEquals("127.0.0.1:8848", serverListManager.getCurrentServer()); assertEquals("127.0.0.1:8848", serverListManager.genNextServer()); } @Test void testShutdown() throws NacosException { Properties properties = new Properties(); properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848"); final NamingServerListManager serverListManager = new NamingServerListManager(properties); serverListManager.start(); Assertions.assertDoesNotThrow(() -> { serverListManager.shutdown(); }); } @Test void testStartWithEmptyServerList() { Properties properties = new Properties(); properties.setProperty("EmptyList", "true"); properties.setProperty("MockTest", "true"); final NamingServerListManager serverListManager = new NamingServerListManager(properties); assertThrows(NacosException.class, serverListManager::start); } private void mockThreadInvoke(NamingServerListManager serverListManager, boolean expectedInvoked) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { Field providerField = AbstractServerListManager.class.getDeclaredField("serverListProvider"); providerField.setAccessible(true); ServerListProvider serverListProvider = (ServerListProvider) providerField.get(serverListManager); assertInstanceOf(EndpointServerListProvider.class, serverListProvider); Field field = EndpointServerListProvider.class.getDeclaredField("lastServerListRefreshTime"); field.setAccessible(true); field.set(serverListProvider, expectedInvoked ? 0 : System.currentTimeMillis()); Method method = EndpointServerListProvider.class.getDeclaredMethod("refreshServerListIfNeed"); method.setAccessible(true); method.invoke(serverListProvider); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/core/ProtectModeTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ProtectModeTest { @Test void testProtectThresholdDefault() { final ProtectMode protectMode = new ProtectMode(); assertEquals(0.8f, protectMode.getProtectThreshold(), 0.01f); } @Test void testSetProtectThreshold() { final ProtectMode protectMode = new ProtectMode(); float expect = 0.7f; protectMode.setProtectThreshold(expect); assertEquals(expect, protectMode.getProtectThreshold(), 0.01f); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/core/ServiceInfoUpdateServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.core; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.NamingClientProxy; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove strictness lenient @MockitoSettings(strictness = Strictness.LENIENT) class ServiceInfoUpdateServiceTest { String serviceName = "aa"; String group = "bb"; String clusters = "cc"; @Mock ServiceInfoHolder holder; @Mock NamingClientProxy proxy; @Mock InstancesChangeNotifier notifier; NacosClientProperties nacosClientProperties; ServiceInfo info; ServiceInfoUpdateService serviceInfoUpdateService; @BeforeEach void setUp() throws Exception { nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(); info = new ServiceInfo(); info.setName(serviceName); info.setGroupName(group); info.setClusters(clusters); info.setLastRefTime(System.currentTimeMillis()); when(proxy.queryInstancesOfService(serviceName, group, clusters, false)).thenReturn(info); } @AfterEach void tearDown() throws Exception { if (null != serviceInfoUpdateService) { serviceInfoUpdateService.shutdown(); } } @Test void testScheduleUpdateWithoutOpen() throws InterruptedException, NacosException { serviceInfoUpdateService = new ServiceInfoUpdateService(null, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); TimeUnit.MILLISECONDS.sleep(1500); Mockito.verify(proxy, Mockito.never()).queryInstancesOfService(serviceName, group, clusters, false); } @Test void testScheduleUpdateIfAbsent() throws InterruptedException, NacosException { info.setCacheMillis(10000L); nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); TimeUnit.MILLISECONDS.sleep(1500); Mockito.verify(proxy).queryInstancesOfService(serviceName, group, clusters, false); } @Test void testScheduleUpdateIfAbsentDuplicate() throws InterruptedException, NacosException { info.setCacheMillis(10000L); nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); TimeUnit.MILLISECONDS.sleep(1500); // Only once called Mockito.verify(proxy).queryInstancesOfService(serviceName, group, clusters, false); } @Test void testScheduleUpdateIfAbsentUpdateOlder() throws InterruptedException, NacosException { info.setCacheMillis(10000L); nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); Map map = new HashMap<>(); map.put(ServiceInfo.getKey(group + "@@" + serviceName, clusters), info); when(holder.getServiceInfoMap()).thenReturn(map); TimeUnit.MILLISECONDS.sleep(1500); Mockito.verify(proxy).queryInstancesOfService(serviceName, group, clusters, false); } @Test void testScheduleUpdateIfAbsentUpdateOlderWithInstance() throws InterruptedException, NacosException { info.setCacheMillis(10000L); nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); Map map = new HashMap<>(); map.put(ServiceInfo.getKey(group + "@@" + serviceName, clusters), info); when(holder.getServiceInfoMap()).thenReturn(map); info.setHosts(Collections.singletonList(new Instance())); TimeUnit.MILLISECONDS.sleep(1500); Mockito.verify(proxy).queryInstancesOfService(serviceName, group, clusters, false); } @Test void testScheduleUpdateIfAbsentWith403Exception() throws InterruptedException, NacosException, NoSuchFieldException, IllegalAccessException { nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); when(proxy.queryInstancesOfService(serviceName, group, clusters, false)).thenThrow( new NacosException(403, "test")); TimeUnit.MILLISECONDS.sleep(1500); assertTrue(getScheduleFuture().getDelay(TimeUnit.MILLISECONDS) > 1000); } @Test void testScheduleUpdateIfAbsentWith500Exception() throws InterruptedException, NacosException, NoSuchFieldException, IllegalAccessException { nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); when(proxy.queryInstancesOfService(serviceName, group, clusters, false)).thenThrow( new NacosException(500, "test")); TimeUnit.MILLISECONDS.sleep(1500); assertTrue(getScheduleFuture().getDelay(TimeUnit.MILLISECONDS) > 2000); } @Test void testScheduleUpdateIfAbsentWithOtherException() throws InterruptedException, NacosException, NoSuchFieldException, IllegalAccessException { nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); when(proxy.queryInstancesOfService(serviceName, group, clusters, false)).thenThrow( new RuntimeException("test")); TimeUnit.MILLISECONDS.sleep(1500); assertTrue(getScheduleFuture().getDelay(TimeUnit.MILLISECONDS) > 1000); } @Test void testStopScheduleUpdateIfAbsent() throws InterruptedException, NacosException { info.setCacheMillis(10000L); nacosClientProperties.setProperty(PropertyKeyConst.NAMING_ASYNC_QUERY_SUBSCRIBE_SERVICE, "true"); serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); serviceInfoUpdateService.stopUpdateIfContain(serviceName, group, clusters); TimeUnit.MILLISECONDS.sleep(1500); Mockito.verify(proxy, Mockito.never()).queryInstancesOfService(serviceName, group, clusters, false); } @Test void testStopUpdateIfContainWithoutOpen() throws NacosException, InterruptedException { serviceInfoUpdateService = new ServiceInfoUpdateService(nacosClientProperties, holder, proxy, notifier); serviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName, group, clusters); TimeUnit.MILLISECONDS.sleep(1500); Mockito.verify(proxy, Mockito.never()).queryInstancesOfService(serviceName, group, clusters, false); serviceInfoUpdateService.stopUpdateIfContain(serviceName, group, clusters); serviceInfoUpdateService.shutdown(); } private ScheduledFuture getScheduleFuture() throws NoSuchFieldException, IllegalAccessException { Field field = serviceInfoUpdateService.getClass().getDeclaredField("executor"); field.setAccessible(true); ScheduledThreadPoolExecutor executorService = (ScheduledThreadPoolExecutor) field.get(serviceInfoUpdateService); return (ScheduledFuture) executorService.getQueue().peek(); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/event/InstancesChangeEventTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class InstancesChangeEventTest { @Test void testGetServiceName() { String eventScope = "scope-001"; String serviceName = "a"; String groupName = "b"; String clusters = "c"; List hosts = new ArrayList<>(); Instance ins = new Instance(); hosts.add(ins); InstancesDiff diff = new InstancesDiff(); diff.setAddedInstances(hosts); InstancesChangeEvent event = new InstancesChangeEvent(eventScope, serviceName, groupName, clusters, hosts, diff); assertEquals(eventScope, event.scope()); assertEquals(serviceName, event.getServiceName()); assertEquals(clusters, event.getClusters()); assertEquals(groupName, event.getGroupName()); List hosts1 = event.getHosts(); assertEquals(hosts.size(), hosts1.size()); assertEquals(hosts.get(0), hosts1.get(0)); InstancesDiff diff1 = event.getInstancesDiff(); assertTrue(diff1.hasDifferent()); assertEquals(diff.getAddedInstances().size(), diff1.getAddedInstances().size()); assertEquals(diff.getAddedInstances().get(0), diff.getAddedInstances().get(0)); assertEquals(diff.getRemovedInstances().size(), diff1.getRemovedInstances().size()); assertEquals(diff.getModifiedInstances().size(), diff1.getModifiedInstances().size()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/event/InstancesChangeNotifierTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.api.naming.listener.AbstractEventListener; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.selector.NamingSelector; import com.alibaba.nacos.client.naming.selector.DefaultNamingSelector; import com.alibaba.nacos.client.naming.selector.NamingSelectorFactory; import com.alibaba.nacos.client.naming.selector.NamingSelectorWrapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Executor; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; class InstancesChangeNotifierTest { private static final String EVENT_SCOPE_CASE = "scope-001"; private static final String GROUP_CASE = "a"; private static final String SERVICE_NAME_CASE = "b"; private static final String CLUSTER_STR_CASE = "c"; InstancesChangeNotifier instancesChangeNotifier; @BeforeEach public void setUp() { instancesChangeNotifier = new InstancesChangeNotifier(EVENT_SCOPE_CASE); } @Test void testRegisterListener() { List clusters = Collections.singletonList(CLUSTER_STR_CASE); EventListener listener = Mockito.mock(EventListener.class); NamingSelector selector = NamingSelectorFactory.newClusterSelector(clusters); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(SERVICE_NAME_CASE, GROUP_CASE, CLUSTER_STR_CASE, selector, listener); instancesChangeNotifier.registerListener(GROUP_CASE, SERVICE_NAME_CASE, wrapper); List subscribeServices = instancesChangeNotifier.getSubscribeServices(); assertEquals(1, subscribeServices.size()); assertEquals(GROUP_CASE, subscribeServices.get(0).getGroupName()); assertEquals(SERVICE_NAME_CASE, subscribeServices.get(0).getName()); assertNull(subscribeServices.get(0).getClusters()); List hosts = new ArrayList<>(); Instance ins = new Instance(); hosts.add(ins); InstancesDiff diff = new InstancesDiff(); diff.setAddedInstances(hosts); InstancesChangeEvent event = new InstancesChangeEvent(EVENT_SCOPE_CASE, SERVICE_NAME_CASE, GROUP_CASE, CLUSTER_STR_CASE, hosts, diff); assertTrue(instancesChangeNotifier.scopeMatches(event)); } @Test void testDeregisterListener() { List clusters = Collections.singletonList(CLUSTER_STR_CASE); EventListener listener = Mockito.mock(EventListener.class); NamingSelector selector = NamingSelectorFactory.newClusterSelector(clusters); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(selector, listener); instancesChangeNotifier.registerListener(GROUP_CASE, SERVICE_NAME_CASE, wrapper); List subscribeServices = instancesChangeNotifier.getSubscribeServices(); assertEquals(1, subscribeServices.size()); instancesChangeNotifier.deregisterListener(GROUP_CASE, SERVICE_NAME_CASE, wrapper); List subscribeServices2 = instancesChangeNotifier.getSubscribeServices(); assertEquals(0, subscribeServices2.size()); } @Test void testIsSubscribed() { List clusters = Collections.singletonList(CLUSTER_STR_CASE); EventListener listener = Mockito.mock(EventListener.class); NamingSelector selector = NamingSelectorFactory.newClusterSelector(clusters); assertFalse(instancesChangeNotifier.isSubscribed(GROUP_CASE, SERVICE_NAME_CASE)); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(SERVICE_NAME_CASE, GROUP_CASE, CLUSTER_STR_CASE, selector, listener); instancesChangeNotifier.registerListener(GROUP_CASE, SERVICE_NAME_CASE, wrapper); assertTrue(instancesChangeNotifier.isSubscribed(GROUP_CASE, SERVICE_NAME_CASE)); } @Test void testOnEvent() { List clusters = Collections.singletonList(CLUSTER_STR_CASE); NamingSelector selector = NamingSelectorFactory.newClusterSelector(clusters); EventListener listener = Mockito.mock(EventListener.class); NamingSelectorWrapper wrapper = new NamingSelectorWrapper(SERVICE_NAME_CASE, GROUP_CASE, CLUSTER_STR_CASE, selector, listener); instancesChangeNotifier.registerListener(GROUP_CASE, SERVICE_NAME_CASE, wrapper); Instance instance = new Instance(); InstancesDiff diff = new InstancesDiff(null, Collections.singletonList(instance), null); instance.setClusterName(CLUSTER_STR_CASE); InstancesChangeEvent event1 = new InstancesChangeEvent(null, SERVICE_NAME_CASE, GROUP_CASE, CLUSTER_STR_CASE, Collections.emptyList(), diff); instancesChangeNotifier.onEvent(event1); Mockito.verify(listener, times(1)).onEvent(any()); } @Test void testOnEventWithoutListener() { InstancesChangeEvent event1 = Mockito.mock(InstancesChangeEvent.class); when(event1.getClusters()).thenReturn(CLUSTER_STR_CASE); when(event1.getGroupName()).thenReturn(GROUP_CASE); when(event1.getServiceName()).thenReturn(SERVICE_NAME_CASE); EventListener listener = Mockito.mock(EventListener.class); instancesChangeNotifier.registerListener(GROUP_CASE, SERVICE_NAME_CASE + "c", new NamingSelectorWrapper( NamingSelectorFactory.newClusterSelector(Collections.singletonList(CLUSTER_STR_CASE)), listener)); instancesChangeNotifier.onEvent(event1); Mockito.verify(listener, never()).onEvent(any()); } @Test void testOnEventByExecutor() { AbstractEventListener listener = Mockito.mock(AbstractEventListener.class); Executor executor = mock(Executor.class); when(listener.getExecutor()).thenReturn(executor); instancesChangeNotifier.registerListener(GROUP_CASE, SERVICE_NAME_CASE, new NamingSelectorWrapper(new DefaultNamingSelector(instance -> true), listener)); InstancesDiff instancesDiff = new InstancesDiff(); instancesDiff.setRemovedInstances(Collections.singletonList(new Instance())); InstancesChangeEvent event = new InstancesChangeEvent(EVENT_SCOPE_CASE, SERVICE_NAME_CASE, GROUP_CASE, CLUSTER_STR_CASE, new ArrayList<>(), instancesDiff); instancesChangeNotifier.onEvent(event); Mockito.verify(executor).execute(any()); } @Test void testSubscribeType() { assertEquals(InstancesChangeEvent.class, instancesChangeNotifier.subscribeType()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/event/InstancesDiffTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.event; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.builder.InstanceBuilder; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Random; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; public class InstancesDiffTest { private static List getInstanceList(int count) { ArrayList list = new ArrayList<>(count); for (int i = 0; i < count; i++) { list.add(new Instance()); } return list; } @Test public void testGetDiff() { String serviceName = "testService"; Instance addedIns = InstanceBuilder.newBuilder().setServiceName(serviceName).setClusterName("a").build(); Instance removedIns = InstanceBuilder.newBuilder().setServiceName(serviceName).setClusterName("b").build(); Instance modifiedIns = InstanceBuilder.newBuilder().setServiceName(serviceName).setClusterName("c").build(); InstancesDiff instancesDiff = new InstancesDiff(); instancesDiff.setAddedInstances(Collections.singletonList(addedIns)); instancesDiff.setRemovedInstances(Collections.singletonList(removedIns)); instancesDiff.setModifiedInstances(Collections.singletonList(modifiedIns)); assertTrue(instancesDiff.hasDifferent()); assertTrue(instancesDiff.isAdded()); assertTrue(instancesDiff.isRemoved()); assertTrue(instancesDiff.isModified()); assertEquals(addedIns, instancesDiff.getAddedInstances().get(0)); assertEquals(removedIns, instancesDiff.getRemovedInstances().get(0)); assertEquals(modifiedIns, instancesDiff.getModifiedInstances().get(0)); } @Test public void testWithFullConstructor() { Random random = new Random(); int addedCount = random.nextInt(32) + 1; int removedCount = random.nextInt(32) + 1; int modifiedCount = random.nextInt(32) + 1; InstancesDiff instancesDiff = new InstancesDiff(getInstanceList(addedCount), getInstanceList(removedCount), getInstanceList(modifiedCount)); assertTrue(instancesDiff.hasDifferent()); assertTrue(instancesDiff.isAdded()); assertTrue(instancesDiff.isRemoved()); assertTrue(instancesDiff.isModified()); assertEquals(addedCount, instancesDiff.getAddedInstances().size()); assertEquals(removedCount, instancesDiff.getRemovedInstances().size()); assertEquals(modifiedCount, instancesDiff.getModifiedInstances().size()); instancesDiff.getAddedInstances().clear(); instancesDiff.getRemovedInstances().clear(); instancesDiff.getModifiedInstances().clear(); assertFalse(instancesDiff.hasDifferent()); assertFalse(instancesDiff.hasDifferent()); assertFalse(instancesDiff.isAdded()); assertFalse(instancesDiff.isRemoved()); assertFalse(instancesDiff.isModified()); } @Test public void testWithNoConstructor() { Random random = new Random(); int addedCount = random.nextInt(32) + 1; int removedCount = random.nextInt(32) + 1; int modifiedCount = random.nextInt(32) + 1; InstancesDiff instancesDiff = new InstancesDiff(); instancesDiff.setAddedInstances(getInstanceList(addedCount)); instancesDiff.setRemovedInstances(getInstanceList(removedCount)); instancesDiff.setModifiedInstances(getInstanceList(modifiedCount)); assertTrue(instancesDiff.hasDifferent()); assertEquals(addedCount, instancesDiff.getAddedInstances().size()); assertEquals(removedCount, instancesDiff.getRemovedInstances().size()); assertEquals(modifiedCount, instancesDiff.getModifiedInstances().size()); instancesDiff.getAddedInstances().clear(); instancesDiff.getRemovedInstances().clear(); instancesDiff.getModifiedInstances().clear(); assertFalse(instancesDiff.hasDifferent()); assertFalse(instancesDiff.isAdded()); assertFalse(instancesDiff.isRemoved()); assertFalse(instancesDiff.isModified()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/listener/NamingChangeEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.listener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.naming.event.InstancesDiff; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; 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.assertFalse; public class NamingChangeEventTest { private MockNamingEventListener eventListener; private InstancesDiff instancesDiff; @BeforeEach public void setUp() throws Exception { eventListener = new MockNamingEventListener(); instancesDiff = new InstancesDiff(); instancesDiff.setAddedInstances(Arrays.asList(new Instance(), new Instance(), new Instance())); instancesDiff.setRemovedInstances(Arrays.asList(new Instance(), new Instance())); instancesDiff.setModifiedInstances(Arrays.asList(new Instance())); } @Test public void testNamingChangeEventWithSimpleConstructor() { NamingChangeEvent event = new NamingChangeEvent("serviceName", Collections.EMPTY_LIST, instancesDiff); assertEquals("serviceName", event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getClusters()); assertTrue(event.getInstances().isEmpty()); assertTrue(event.isAdded()); assertEquals(3, event.getAddedInstances().size()); assertTrue(event.isRemoved()); assertEquals(2, event.getRemovedInstances().size()); assertTrue(event.isModified()); assertEquals(1, event.getModifiedInstances().size()); eventListener.onEvent(event); assertNull(event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getClusters()); assertNull(event.getInstances()); assertFalse(event.isAdded()); assertEquals(0, event.getAddedInstances().size()); assertFalse(event.isRemoved()); assertEquals(0, event.getRemovedInstances().size()); assertFalse(event.isModified()); assertEquals(0, event.getRemovedInstances().size()); } @Test public void testNamingChangeEventWithFullConstructor() { NamingChangeEvent event = new NamingChangeEvent("serviceName", "group", "clusters", Collections.EMPTY_LIST, instancesDiff); assertEquals("serviceName", event.getServiceName()); assertEquals("group", event.getGroupName()); assertEquals("clusters", event.getClusters()); assertTrue(event.getInstances().isEmpty()); assertTrue(event.isAdded()); assertEquals(3, event.getAddedInstances().size()); assertTrue(event.isRemoved()); assertEquals(2, event.getRemovedInstances().size()); assertTrue(event.isModified()); assertEquals(1, event.getModifiedInstances().size()); eventListener.onEvent(event); assertNull(event.getServiceName()); assertNull(event.getGroupName()); assertNull(event.getClusters()); assertNull(event.getInstances()); assertFalse(event.isAdded()); assertEquals(0, event.getAddedInstances().size()); assertFalse(event.isRemoved()); assertEquals(0, event.getRemovedInstances().size()); assertFalse(event.isModified()); assertEquals(0, event.getRemovedInstances().size()); } @Test public void testGetChanges() { NamingChangeEvent event = new NamingChangeEvent("serviceName", Collections.EMPTY_LIST, instancesDiff); assertTrue(event.isAdded()); assertEquals(3, event.getAddedInstances().size()); event.getAddedInstances().clear(); assertFalse(event.isAdded()); assertEquals(0, event.getAddedInstances().size()); assertTrue(event.isRemoved()); assertEquals(2, event.getRemovedInstances().size()); event.getRemovedInstances().clear(); assertFalse(event.isRemoved()); assertEquals(0, event.getRemovedInstances().size()); assertTrue(event.isModified()); assertEquals(1, event.getModifiedInstances().size()); event.getModifiedInstances().clear(); assertFalse(event.isModified()); assertEquals(0, event.getRemovedInstances().size()); } private static class MockNamingEventListener extends AbstractNamingChangeListener { @Override public void onChange(NamingChangeEvent event) { assertNull(getExecutor()); event.setServiceName(null); event.setGroupName(null); event.setClusters(null); event.setInstances(null); event.getAddedInstances().clear(); event.getRemovedInstances().clear(); event.getModifiedInstances().clear(); } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/AbstractNamingClientProxyTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.client.utils.AppNameUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.plugin.auth.api.RequestResource; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.util.HashMap; import java.util.List; import java.util.Map; 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.when; @ExtendWith(MockitoExtension.class) class AbstractNamingClientProxyTest { @Mock private SecurityProxy sc; /** * test get security headers for accessToken. */ @Test void testGetSecurityHeadersForAccessToken() { AbstractNamingClientProxy proxy = new MockNamingClientProxy(sc); String token = "aa"; Map keyMap = new HashMap<>(); keyMap.put(Constants.ACCESS_TOKEN, token); when(sc.getIdentityContext(any(RequestResource.class))).thenReturn(keyMap); Map securityHeaders = proxy.getSecurityHeaders("", "", ""); assertEquals(2, securityHeaders.size()); assertEquals(token, securityHeaders.get(Constants.ACCESS_TOKEN)); assertEquals(AppNameUtils.getAppName(), securityHeaders.get("app")); } /** * get security headers for ram. * * @throws Exception exception */ @Test void testGetSecurityHeadersForRam() throws Exception { String ak = "aa"; String sk = "bb"; Map mockIdentityContext = new HashMap<>(); String serviceName = "aaa"; mockIdentityContext.put("ak", ak); String data = System.currentTimeMillis() + "@@" + serviceName; mockIdentityContext.put("data", data); mockIdentityContext.put("signature", SignUtil.sign(data, sk)); when(sc.getIdentityContext(any(RequestResource.class))).thenReturn(mockIdentityContext); AbstractNamingClientProxy proxy = new MockNamingClientProxy(sc); Map spasHeaders = proxy.getSecurityHeaders("", "", serviceName); assertEquals(4, spasHeaders.size()); assertEquals(AppNameUtils.getAppName(), spasHeaders.get("app")); assertEquals(ak, spasHeaders.get("ak")); assertTrue(spasHeaders.get("data").endsWith("@@" + serviceName)); String expectSign = SignUtil.sign(spasHeaders.get("data"), sk); assertEquals(expectSign, spasHeaders.get("signature")); } private class MockNamingClientProxy extends AbstractNamingClientProxy { protected MockNamingClientProxy(SecurityProxy securityProxy) { super(securityProxy); } @Override public void registerService(String serviceName, String groupName, Instance instance) throws NacosException { } @Override public void batchRegisterService(String serviceName, String groupName, List instances) throws NacosException { } @Override public void batchDeregisterService(String serviceName, String groupName, List instances) throws NacosException { } @Override public void deregisterService(String serviceName, String groupName, Instance instance) throws NacosException { } @Override public void updateInstance(String serviceName, String groupName, Instance instance) throws NacosException { } @Override public ServiceInfo queryInstancesOfService(String serviceName, String groupName, String clusters, boolean healthyOnly) throws NacosException { return null; } @Override public Service queryService(String serviceName, String groupName) throws NacosException { return null; } @Override public void createService(Service service, AbstractSelector selector) throws NacosException { } @Override public boolean deleteService(String serviceName, String groupName) throws NacosException { return false; } @Override public void updateService(Service service, AbstractSelector selector) throws NacosException { } @Override public ListView getServiceList(int pageNo, int pageSize, String groupName, AbstractSelector selector) throws NacosException { return null; } @Override public ServiceInfo subscribe(String serviceName, String groupName, String clusters) throws NacosException { return null; } @Override public void unsubscribe(String serviceName, String groupName, String clusters) throws NacosException { } @Override public boolean isSubscribed(String serviceName, String groupName, String clusters) throws NacosException { return false; } @Override public boolean serverHealthy() { return false; } @Override public void shutdown() throws NacosException { } @Override public void onEvent(ServerListChangeEvent event) { } @Override public Class subscribeType() { return null; } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/NamingClientProxyDelegateTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.event.InstancesChangeNotifier; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.http.NamingHttpClientProxy; 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.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Properties; 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; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NamingClientProxyDelegateTest { private static final String TEST_NAMESPACE = "ns1"; @Mock ServiceInfoHolder holder; @Mock NamingGrpcClientProxy mockGrpcClient; @Mock NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; NamingClientProxyDelegate delegate; InstancesChangeNotifier notifier; NacosClientProperties nacosClientProperties; @BeforeEach void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException { Properties props = new Properties(); props.setProperty("serverAddr", "localhost"); nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); notifier = new InstancesChangeNotifier(); delegate = new NamingClientProxyDelegate(TEST_NAMESPACE, holder, nacosClientProperties, notifier, namingFuzzyWatchServiceListHolder); Field grpcClientProxyField = NamingClientProxyDelegate.class.getDeclaredField("grpcClientProxy"); grpcClientProxyField.setAccessible(true); grpcClientProxyField.set(delegate, mockGrpcClient); } @AfterEach void tearDown() throws NacosException { delegate.shutdown(); } @Test void testRegisterEphemeralServiceByGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setServiceName(serviceName); instance.setClusterName(groupName); instance.setIp("1.1.1.1"); instance.setPort(1); instance.setEphemeral(true); delegate.registerService(serviceName, groupName, instance); verify(mockGrpcClient, times(1)).registerService(serviceName, groupName, instance); } @Test void testBatchRegisterServiceByGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setServiceName(serviceName); instance.setClusterName(groupName); instance.setIp("1.1.1.1"); instance.setPort(1); instance.setEphemeral(true); List instanceList = new ArrayList<>(); delegate.batchRegisterService(serviceName, groupName, instanceList); verify(mockGrpcClient, times(1)).batchRegisterService(serviceName, groupName, instanceList); } @Test void testBatchDeregisterServiceByGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; List instanceList = new ArrayList<>(); delegate.batchDeregisterService(serviceName, groupName, instanceList); verify(mockGrpcClient, times(1)).batchDeregisterService(serviceName, groupName, instanceList); reset(mockGrpcClient); instanceList.add(new Instance()); delegate.batchDeregisterService(serviceName, groupName, instanceList); verify(mockGrpcClient, times(1)).batchDeregisterService(serviceName, groupName, instanceList); } @Test void testRegisterPersistentServiceByGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setServiceName(serviceName); instance.setClusterName(groupName); instance.setIp("1.1.1.1"); instance.setPort(1); // persistent instance instance.setEphemeral(false); // when server support register persistent instance by grpc, will use grpc to register when(mockGrpcClient.isAbilitySupportedByServer( AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)).thenReturn(true); delegate.registerService(serviceName, groupName, instance); verify(mockGrpcClient, times(1)).registerService(serviceName, groupName, instance); } @Test void testRegisterPersistentServiceByHttp() throws NacosException, NoSuchFieldException, IllegalAccessException { NamingHttpClientProxy mockHttpClient = Mockito.mock(NamingHttpClientProxy.class); Field mockHttpClientField = NamingClientProxyDelegate.class.getDeclaredField("httpClientProxy"); mockHttpClientField.setAccessible(true); mockHttpClientField.set(delegate, mockHttpClient); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setServiceName(serviceName); instance.setClusterName(groupName); instance.setIp("1.1.1.1"); instance.setPort(1); // persistent instance instance.setEphemeral(false); // when server do not support register persistent instance by grpc, will use http to register delegate.registerService(serviceName, groupName, instance); verify(mockHttpClient, times(1)).registerService(serviceName, groupName, instance); } @Test void testDeregisterEphemeralServiceGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setServiceName(serviceName); instance.setClusterName(groupName); instance.setIp("1.1.1.1"); instance.setPort(1); // use grpc instance.setEphemeral(true); delegate.deregisterService(serviceName, groupName, instance); verify(mockGrpcClient, times(1)).deregisterService(serviceName, groupName, instance); } @Test void testDeregisterPersistentServiceGrpc() throws NacosException { String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setServiceName(serviceName); instance.setClusterName(groupName); instance.setIp("1.1.1.1"); instance.setPort(1); // persistent instance instance.setEphemeral(false); // when server support deregister persistent instance by grpc, will use grpc to deregister when(mockGrpcClient.isAbilitySupportedByServer( AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)).thenReturn(true); delegate.deregisterService(serviceName, groupName, instance); verify(mockGrpcClient, times(1)).deregisterService(serviceName, groupName, instance); } @Test void testDeregisterPersistentServiceHttp() throws NacosException, NoSuchFieldException, IllegalAccessException { NamingHttpClientProxy mockHttpClient = Mockito.mock(NamingHttpClientProxy.class); Field mockHttpClientField = NamingClientProxyDelegate.class.getDeclaredField("httpClientProxy"); mockHttpClientField.setAccessible(true); mockHttpClientField.set(delegate, mockHttpClient); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setServiceName(serviceName); instance.setClusterName(groupName); instance.setIp("1.1.1.1"); instance.setPort(1); // use http instance.setEphemeral(false); delegate.deregisterService(serviceName, groupName, instance); verify(mockHttpClient, times(1)).deregisterService(serviceName, groupName, instance); } @Test void testUpdateInstance() { String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); Assertions.assertDoesNotThrow(() -> { delegate.updateInstance(serviceName, groupName, instance); }); } @Test void testQueryInstancesOfService() throws NacosException { String serviceName = "service1"; String groupName = "group1"; String clusters = "cluster1"; delegate.queryInstancesOfService(serviceName, groupName, clusters, false); verify(mockGrpcClient, times(1)).queryInstancesOfService(serviceName, groupName, clusters, false); } @Test void testQueryService() throws NacosException { Service service = delegate.queryService("a", "b"); assertNull(service); } @Test void testCreateService() { Service service = new Service(); Assertions.assertDoesNotThrow(() -> { delegate.createService(service, new NoneSelector()); }); } @Test void testDeleteService() throws NacosException { assertFalse(delegate.deleteService("service", "group1")); } @Test void testUpdateService() { Service service = new Service(); Assertions.assertDoesNotThrow(() -> { delegate.updateService(service, new ExpressionSelector()); }); } @Test void testGetServiceList() throws NacosException { AbstractSelector selector = new ExpressionSelector(); int pageNo = 1; int pageSize = 10; String groupName = "group2"; delegate.getServiceList(pageNo, pageSize, groupName, selector); verify(mockGrpcClient, times(1)).getServiceList(pageNo, pageSize, groupName, selector); } @Test void testSubscribe() throws NacosException { String serviceName = "service1"; String groupName = "group1"; String clusters = "cluster1"; ServiceInfo info = new ServiceInfo(); info.setName(serviceName); info.setGroupName(groupName); info.setClusters(clusters); when(mockGrpcClient.subscribe(serviceName, groupName, clusters)).thenReturn(info); ServiceInfo actual = delegate.subscribe(serviceName, groupName, clusters); assertEquals(info, actual); verify(mockGrpcClient, times(1)).subscribe(serviceName, groupName, clusters); verify(holder, times(1)).processServiceInfo(info); } @Test void testUnsubscribe() throws NacosException { String serviceName = "service1"; String groupName = "group1"; String clusters = "cluster1"; delegate.unsubscribe(serviceName, groupName, clusters); verify(mockGrpcClient, times(1)).unsubscribe(serviceName, groupName, clusters); } @Test void testServerHealthy() { Mockito.when(mockGrpcClient.serverHealthy()).thenReturn(true); assertTrue(delegate.serverHealthy()); } @Test void testIsSubscribed() throws NacosException { String serviceName = "service1"; String groupName = "group1"; String clusters = "cluster1"; assertFalse(delegate.isSubscribed(serviceName, groupName, clusters)); when(mockGrpcClient.isSubscribed(serviceName, groupName, clusters)).thenReturn(true); assertTrue(delegate.isSubscribed(serviceName, groupName, clusters)); } @Test void testShutdown() throws NacosException { delegate.shutdown(); verify(mockGrpcClient, times(1)).shutdown(); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/TestConnection.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.RequestFuture; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; public class TestConnection extends Connection { public TestConnection(RpcClient.ServerInfo serverInfo) { super(serverInfo); } @Override public Response request(Request request, long timeoutMills) throws NacosException { return null; } @Override public RequestFuture requestFuture(Request request) throws NacosException { return null; } @Override public void asyncRequest(Request request, RequestCallBack requestCallBack) throws NacosException { } @Override public void close() { } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingGrpcClientProxyTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.remote.NamingRemoteConstants; import com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.InstanceRequest; import com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest; import com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest; import com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse; import com.alibaba.nacos.api.naming.remote.response.InstanceResponse; import com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse; import com.alibaba.nacos.api.naming.remote.response.ServiceListResponse; import com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse; import com.alibaba.nacos.api.remote.DefaultRequestFuture; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.RequestFuture; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.selector.AbstractSelector; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.remote.gprc.redo.NamingGrpcRedoService; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientConfig; import com.alibaba.nacos.common.remote.client.RpcClientFactory; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.remote.client.grpc.GrpcClient; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcConstants; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; 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.Properties; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; 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.Assertions.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove strictness lenient @MockitoSettings(strictness = Strictness.LENIENT) class NamingGrpcClientProxyTest { private static final String NAMESPACE_ID = "ns1"; private static final String SERVICE_NAME = "service1"; private static final String GROUP_NAME = "group1"; private static final String CLUSTERS = "cluster1"; private static final String ORIGIN_SERVER = "www.google.com"; @Mock private SecurityProxy proxy; @Mock private ServerListFactory factory; @Mock private ServiceInfoHolder holder; @Mock private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; @Mock private RpcClient rpcClient; private Properties prop; private NamingGrpcClientProxy client; private Response response; private Instance instance; private Instance persistentInstance; private String uuid; @BeforeEach void setUp() throws NacosException, NoSuchFieldException, IllegalAccessException { System.setProperty(GrpcConstants.GRPC_RETRY_TIMES, "1"); System.setProperty(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT, "100"); List serverList = Stream.of(ORIGIN_SERVER, "anotherServer").collect(Collectors.toList()); when(factory.getServerList()).thenReturn(serverList); when(factory.genNextServer()).thenReturn(ORIGIN_SERVER); prop = new Properties(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, namingFuzzyWatchServiceListHolder); Field uuidField = NamingGrpcClientProxy.class.getDeclaredField("uuid"); uuidField.setAccessible(true); uuid = (String) uuidField.get(client); assertNotNull(RpcClientFactory.getClient(uuid)); Field rpcClientField = NamingGrpcClientProxy.class.getDeclaredField("rpcClient"); rpcClientField.setAccessible(true); ((RpcClient) rpcClientField.get(client)).shutdown(); rpcClientField.set(client, this.rpcClient); response = new InstanceResponse(); when(this.rpcClient.request(any())).thenReturn(response); instance = new Instance(); instance.setServiceName(SERVICE_NAME); instance.setIp("1.1.1.1"); instance.setPort(1111); persistentInstance = new Instance(); persistentInstance.setServiceName(SERVICE_NAME); persistentInstance.setIp("1.1.1.1"); persistentInstance.setPort(1111); persistentInstance.setEphemeral(false); } @AfterEach void tearDown() throws NacosException { System.clearProperty(GrpcConstants.GRPC_RETRY_TIMES); System.clearProperty(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT); client.shutdown(); } @Test void testRegisterService() throws NacosException { client.registerService(SERVICE_NAME, GROUP_NAME, instance); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof InstanceRequest) { InstanceRequest request1 = (InstanceRequest) request; return request1.getType().equals(NamingRemoteConstants.REGISTER_INSTANCE); } return false; })); } @Test void testRegisterPersistentService() throws NacosException { client.registerService(SERVICE_NAME, GROUP_NAME, persistentInstance); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof PersistentInstanceRequest) { PersistentInstanceRequest request1 = (PersistentInstanceRequest) request; return request1.getType().equals(NamingRemoteConstants.REGISTER_INSTANCE); } return false; })); } @Test void testRegisterServiceThrowsNacosException() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { when(this.rpcClient.request(Mockito.any())).thenReturn(ErrorResponse.build(400, "err args")); try { client.registerService(SERVICE_NAME, GROUP_NAME, instance); } catch (NacosException ex) { assertNull(ex.getCause()); throw ex; } }); assertTrue(exception.getMessage().contains("err args")); } @Test void testRegisterServiceThrowsException() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { when(this.rpcClient.request(Mockito.any())).thenReturn(null); try { client.registerService(SERVICE_NAME, GROUP_NAME, instance); } catch (NacosException ex) { assertEquals(NullPointerException.class, ex.getCause().getClass()); throw ex; } }); assertTrue(exception.getMessage().contains("Request nacos server failed: ")); } @Test void testDeregisterService() throws NacosException { client.deregisterService(SERVICE_NAME, GROUP_NAME, instance); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof InstanceRequest) { InstanceRequest request1 = (InstanceRequest) request; return request1.getType().equals(NamingRemoteConstants.DE_REGISTER_INSTANCE); } return false; })); } @Test void testDeregisterPersistentService() throws NacosException { client.deregisterService(SERVICE_NAME, GROUP_NAME, persistentInstance); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof PersistentInstanceRequest) { PersistentInstanceRequest request1 = (PersistentInstanceRequest) request; return request1.getType().equals(NamingRemoteConstants.DE_REGISTER_INSTANCE); } return false; })); } @Test void testDeregisterServiceForBatchRegistered() throws NacosException { try { List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); instanceList.add(new Instance()); client.batchRegisterService(SERVICE_NAME, GROUP_NAME, instanceList); } catch (Exception ignored) { } response = new BatchInstanceResponse(); when(this.rpcClient.request(any())).thenReturn(response); List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); client.deregisterService(SERVICE_NAME, GROUP_NAME, instance); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof BatchInstanceRequest) { BatchInstanceRequest request1 = (BatchInstanceRequest) request; request1.setRequestId("1"); return request1.getInstances().size() == 1 && request1.getType() .equals(NamingRemoteConstants.BATCH_REGISTER_INSTANCE); } return false; })); } @Test void testBatchRegisterService() throws NacosException { List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); response = new BatchInstanceResponse(); when(this.rpcClient.request(any())).thenReturn(response); client.batchRegisterService(SERVICE_NAME, GROUP_NAME, instanceList); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof BatchInstanceRequest) { BatchInstanceRequest request1 = (BatchInstanceRequest) request; request1.setRequestId("1"); return request1.getType().equals(NamingRemoteConstants.BATCH_REGISTER_INSTANCE); } return false; })); } @Test void testBatchDeregisterServiceWithEmptyInstances() throws NacosException { assertThrows(NacosException.class, () -> { client.batchDeregisterService(SERVICE_NAME, GROUP_NAME, Collections.EMPTY_LIST); }); } @Test void testBatchDeregisterServiceWithoutCacheData() throws NacosException { assertThrows(NacosException.class, () -> { List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); client.batchDeregisterService(SERVICE_NAME, GROUP_NAME, instanceList); }); } @Test void testBatchDeregisterServiceNotBatchData() throws NacosException { assertThrows(NacosException.class, () -> { client.registerService(SERVICE_NAME, GROUP_NAME, instance); List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); client.batchDeregisterService(SERVICE_NAME, GROUP_NAME, instanceList); }); } @Test void testBatchDeregisterServiceWithEmptyBatchData() throws NacosException { assertThrows(NacosException.class, () -> { try { client.batchRegisterService(SERVICE_NAME, GROUP_NAME, Collections.EMPTY_LIST); } catch (Exception ignored) { } List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); client.batchDeregisterService(SERVICE_NAME, GROUP_NAME, instanceList); }); } @Test void testBatchDeregisterService() throws NacosException { try { List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); instanceList.add(new Instance()); client.batchRegisterService(SERVICE_NAME, GROUP_NAME, instanceList); } catch (Exception ignored) { } response = new BatchInstanceResponse(); when(this.rpcClient.request(any())).thenReturn(response); List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); client.batchDeregisterService(SERVICE_NAME, GROUP_NAME, instanceList); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof BatchInstanceRequest) { BatchInstanceRequest request1 = (BatchInstanceRequest) request; request1.setRequestId("1"); return request1.getInstances().size() == 1 && request1.getType() .equals(NamingRemoteConstants.BATCH_REGISTER_INSTANCE); } return false; })); } @Test void testBatchDeregisterServiceWithOtherPortInstance() throws NacosException { try { List instanceList = new ArrayList<>(); instance.setHealthy(true); instanceList.add(instance); instanceList.add(new Instance()); client.batchRegisterService(SERVICE_NAME, GROUP_NAME, instanceList); } catch (Exception ignored) { } response = new BatchInstanceResponse(); when(this.rpcClient.request(any())).thenReturn(response); Instance otherPortInstance = new Instance(); otherPortInstance.setServiceName(SERVICE_NAME); otherPortInstance.setIp("1.1.1.1"); otherPortInstance.setPort(2222); List instanceList = new ArrayList<>(); instanceList.add(otherPortInstance); client.batchDeregisterService(SERVICE_NAME, GROUP_NAME, instanceList); verify(this.rpcClient, times(2)).request(argThat(request -> { if (request instanceof BatchInstanceRequest) { BatchInstanceRequest request1 = (BatchInstanceRequest) request; request1.setRequestId("1"); return request1.getInstances().size() == 2 && request1.getType() .equals(NamingRemoteConstants.BATCH_REGISTER_INSTANCE); } return false; })); } @Test void testUpdateInstance() throws Exception { //TODO thrown.expect(UnsupportedOperationException.class); client.updateInstance(SERVICE_NAME, GROUP_NAME, instance); } @Test void testQueryInstancesOfService() throws Exception { QueryServiceResponse res = new QueryServiceResponse(); ServiceInfo info = new ServiceInfo(GROUP_NAME + "@@" + SERVICE_NAME + "@@" + CLUSTERS); res.setServiceInfo(info); when(this.rpcClient.request(any())).thenReturn(res); ServiceInfo actual = client.queryInstancesOfService(SERVICE_NAME, GROUP_NAME, CLUSTERS, false); assertEquals(info, actual); } @Test void testQueryService() throws Exception { Service service = client.queryService(SERVICE_NAME, GROUP_NAME); assertNull(service); } @Test void testCreateService() throws Exception { //TODO thrown.expect(UnsupportedOperationException.class); Service service = new Service(); AbstractSelector selector = new NoneSelector(); client.createService(service, selector); } @Test void testDeleteService() throws Exception { //TODO thrown.expect(UnsupportedOperationException.class); assertFalse(client.deleteService(SERVICE_NAME, GROUP_NAME)); } @Test void testUpdateService() throws NacosException { //TODO thrown.expect(UnsupportedOperationException.class); Service service = new Service(); AbstractSelector selector = new NoneSelector(); client.updateService(service, selector); } @Test void testGetServiceList() throws Exception { ServiceListResponse res = new ServiceListResponse(); List services = Arrays.asList("service1", "service2"); res.setServiceNames(services); res.setCount(5); when(this.rpcClient.request(any())).thenReturn(res); AbstractSelector selector = new NoneSelector(); ListView serviceList = client.getServiceList(1, 10, GROUP_NAME, selector); assertEquals(5, serviceList.getCount()); assertEquals(services, serviceList.getData()); } @Test void testGetServiceListForLabelSelector() throws Exception { ServiceListResponse res = new ServiceListResponse(); List services = Arrays.asList("service1", "service2"); res.setServiceNames(services); res.setCount(5); when(this.rpcClient.request(any())).thenReturn(res); AbstractSelector selector = new ExpressionSelector(); ListView serviceList = client.getServiceList(1, 10, GROUP_NAME, selector); assertEquals(5, serviceList.getCount()); assertEquals(services, serviceList.getData()); } @Test void testSubscribe() throws Exception { SubscribeServiceResponse res = new SubscribeServiceResponse(); ServiceInfo info = new ServiceInfo(GROUP_NAME + "@@" + SERVICE_NAME + "@@" + CLUSTERS); res.setServiceInfo(info); when(this.rpcClient.request(any())).thenReturn(res); ServiceInfo actual = client.subscribe(SERVICE_NAME, GROUP_NAME, CLUSTERS); assertEquals(info, actual); } @Test void testUnsubscribe() throws Exception { SubscribeServiceResponse res = new SubscribeServiceResponse(); ServiceInfo info = new ServiceInfo(GROUP_NAME + "@@" + SERVICE_NAME + "@@" + CLUSTERS); res.setServiceInfo(info); when(this.rpcClient.request(any())).thenReturn(res); client.unsubscribe(SERVICE_NAME, GROUP_NAME, CLUSTERS); verify(this.rpcClient, times(1)).request(argThat(request -> { if (request instanceof SubscribeServiceRequest) { SubscribeServiceRequest request1 = (SubscribeServiceRequest) request; // verify request fields return !request1.isSubscribe() && SERVICE_NAME.equals(request1.getServiceName()) && GROUP_NAME.equals( request1.getGroupName()) && CLUSTERS.equals(request1.getClusters()) && NAMESPACE_ID.equals( request1.getNamespace()); } return false; })); } @Test void testIsSubscribed() throws NacosException { SubscribeServiceResponse res = new SubscribeServiceResponse(); ServiceInfo info = new ServiceInfo(GROUP_NAME + "@@" + SERVICE_NAME + "@@" + CLUSTERS); res.setServiceInfo(info); when(this.rpcClient.request(any())).thenReturn(res); assertFalse(client.isSubscribed(SERVICE_NAME, GROUP_NAME, CLUSTERS)); client.subscribe(SERVICE_NAME, GROUP_NAME, CLUSTERS); assertTrue(client.isSubscribed(SERVICE_NAME, GROUP_NAME, CLUSTERS)); } @Test void testServerHealthy() { when(this.rpcClient.isRunning()).thenReturn(true); assertTrue(client.serverHealthy()); verify(this.rpcClient, times(1)).isRunning(); } @Test void testIsAbilitySupportedByServer1() { when(this.rpcClient.getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)).thenReturn( AbilityStatus.SUPPORTED); assertTrue(client.isAbilitySupportedByServer(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)); verify(this.rpcClient, times(1)).getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC); } @Test void testIsAbilitySupportedByServer2() { when(this.rpcClient.getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)).thenReturn( AbilityStatus.NOT_SUPPORTED); assertFalse(client.isAbilitySupportedByServer(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)); verify(this.rpcClient, times(1)).getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC); } @Test void testIsAbilitySupportedByServer3() { when(this.rpcClient.getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)).thenReturn( AbilityStatus.UNKNOWN); assertFalse(client.isAbilitySupportedByServer(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)); verify(this.rpcClient, times(1)).getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC); } @Test void testIsAbilitySupportedByServer4() { when(this.rpcClient.getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)).thenReturn( null); assertFalse(client.isAbilitySupportedByServer(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC)); verify(this.rpcClient, times(1)).getConnectionAbility(AbilityKey.SERVER_PERSISTENT_INSTANCE_BY_GRPC); } @Test void testShutdown() throws Exception { client.shutdown(); assertNull(RpcClientFactory.getClient(uuid)); //verify(this.rpcClient, times(1)).shutdown(); } @Test void testShutdownWithException() throws NoSuchFieldException, IllegalAccessException, NacosException { Field field = RpcClientFactory.class.getDeclaredField("CLIENT_MAP"); field.setAccessible(true); Map map = (Map) field.get(RpcClientFactory.class); RpcClient oldClient = map.get(uuid); try { oldClient.shutdown(); } catch (NacosException ignored) { } map.put(uuid, rpcClient); doThrow(new NacosException()).when(rpcClient).shutdown(); } @Test void testIsEnable() { when(this.rpcClient.isRunning()).thenReturn(true); assertTrue(client.isEnable()); verify(this.rpcClient, times(1)).isRunning(); } @Test void testServerListChanged() throws Exception { RpcClient rpc = new RpcClient(new RpcClientConfig() { @Override public String name() { return "testServerListHasChanged"; } @Override public int retryTimes() { return 3; } @Override public long timeOutMills() { return 3000L; } @Override public long connectionKeepAlive() { return 5000L; } @Override public int healthCheckRetryTimes() { return 1; } @Override public long healthCheckTimeOut() { return 3000L; } @Override public Map labels() { return new HashMap<>(); } }, factory) { @Override public ConnectionType getConnectionType() { return ConnectionType.GRPC; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) throws Exception { return new Connection(serverInfo) { @Override public Response request(Request request, long timeoutMills) throws NacosException { Response response = new Response() { }; response.setRequestId(request.getRequestId()); return response; } @Override public RequestFuture requestFuture(Request request) throws NacosException { return new DefaultRequestFuture("test", request.getRequestId()); } @Override public void asyncRequest(Request request, RequestCallBack requestCallBack) throws NacosException { } @Override public void close() { } }; } }; Field rpcClient = NamingGrpcClientProxy.class.getDeclaredField("rpcClient"); rpcClient.setAccessible(true); rpcClient.set(client, rpc); rpc.serverListFactory(factory); rpc.registerServerRequestHandler(new NamingPushRequestHandler(holder)); Field listenerField = NamingGrpcClientProxy.class.getDeclaredField("redoService"); listenerField.setAccessible(true); NamingGrpcRedoService listener = (NamingGrpcRedoService) listenerField.get(client); rpc.registerConnectionListener(listener); rpc.start(); int retry = 10; while (!rpc.isRunning()) { TimeUnit.MILLISECONDS.sleep(200); if (--retry < 0) { fail("rpc is not running"); } } assertEquals(ORIGIN_SERVER, rpc.getCurrentServer().getServerIp()); String newServer = "www.aliyun.com"; when(factory.genNextServer()).thenReturn(newServer); when(factory.getServerList()).thenReturn(Stream.of(newServer, "anotherServer").collect(Collectors.toList())); NotifyCenter.publishEvent(new ServerListChangeEvent()); retry = 10; while (ORIGIN_SERVER.equals(rpc.getCurrentServer().getServerIp())) { TimeUnit.MILLISECONDS.sleep(200); if (--retry < 0) { fail("failed to auth switch server"); } } assertEquals(newServer, rpc.getCurrentServer().getServerIp()); } @Test void testConfigAppNameLabels() throws Exception { final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); client = new NamingGrpcClientProxy(NAMESPACE_ID, proxy, factory, nacosClientProperties, holder, namingFuzzyWatchServiceListHolder); Field rpcClientField = NamingGrpcClientProxy.class.getDeclaredField("rpcClient"); rpcClientField.setAccessible(true); RpcClient rpcClient = (RpcClient) rpcClientField.get(client); Field clientConfig = GrpcClient.class.getDeclaredField("clientConfig"); clientConfig.setAccessible(true); GrpcClientConfig config = (GrpcClientConfig) clientConfig.get(rpcClient); String appName = config.labels().get(Constants.APPNAME); assertNotNull(appName); } @Test void testResponseCode403Exception() throws NacosException { Throwable exception = assertThrows(NacosException.class, () -> { when(this.rpcClient.request(Mockito.any())).thenReturn(ErrorResponse.build(403, "Invalid signature")); try { client.registerService(SERVICE_NAME, GROUP_NAME, instance); } catch (NacosException ex) { assertNull(ex.getCause()); throw ex; } }); assertTrue(exception.getMessage().contains("Invalid signature")); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/NamingPushRequestHandlerTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest; import com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse; import com.alibaba.nacos.api.remote.request.HealthCheckRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.client.naming.cache.ServiceInfoHolder; import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.common.remote.client.RpcClient; 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.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class NamingPushRequestHandlerTest { @Test void testRequestReply() { //given ServiceInfoHolder holder = mock(ServiceInfoHolder.class); NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); ServiceInfo info = new ServiceInfo("name", "cluster1"); Request req = NotifySubscriberRequest.buildNotifySubscriberRequest(info); //when Response response = handler.requestReply(req, new TestConnection(new RpcClient.ServerInfo())); //then assertTrue(response instanceof NotifySubscriberResponse); verify(holder, times(1)).processServiceInfo(info); } @Test void testRequestReplyOtherType() { ServiceInfoHolder holder = mock(ServiceInfoHolder.class); NamingPushRequestHandler handler = new NamingPushRequestHandler(holder); assertNull(handler.requestReply(new HealthCheckRequest(), new TestConnection(new RpcClient.ServerInfo()))); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/NamingGrpcRedoServiceTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.cache.NamingFuzzyWatchServiceListHolder; import com.alibaba.nacos.client.naming.remote.TestConnection; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.utils.ReflectUtils; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ScheduledExecutorService; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(MockitoExtension.class) class NamingGrpcRedoServiceTest { private static final String SERVICE = "service"; private static final String GROUP = "group"; private static final String CLUSTER = "cluster"; @Mock private NamingGrpcClientProxy clientProxy; @Mock private NamingFuzzyWatchServiceListHolder namingFuzzyWatchServiceListHolder; private NamingGrpcRedoService redoService; @BeforeEach void setUp() throws Exception { Properties prop = new Properties(); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); redoService = new NamingGrpcRedoService(clientProxy, namingFuzzyWatchServiceListHolder, nacosClientProperties); ScheduledExecutorService redoExecutor = (ScheduledExecutorService) ReflectUtils.getFieldValue(redoService, "redoExecutor"); redoExecutor.shutdownNow(); } @AfterEach void tearDown() throws Exception { redoService.shutdown(); } @Test void testDefaultProperties() throws Exception { Field redoThreadCountField = NamingGrpcRedoService.class.getDeclaredField("redoThreadCount"); redoThreadCountField.setAccessible(true); Field redoDelayTimeField = NamingGrpcRedoService.class.getDeclaredField("redoDelayTime"); redoDelayTimeField.setAccessible(true); Long redoDelayTimeValue = (Long) redoDelayTimeField.get(redoService); Integer redoThreadCountValue = (Integer) redoThreadCountField.get(redoService); assertEquals(Long.valueOf(3000L), redoDelayTimeValue); assertEquals(Integer.valueOf(1), redoThreadCountValue); } @Test void testCustomProperties() throws Exception { Properties prop = new Properties(); prop.setProperty(PropertyKeyConst.REDO_DELAY_TIME, "4000"); prop.setProperty(PropertyKeyConst.REDO_DELAY_THREAD_COUNT, "2"); NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(prop); NamingGrpcRedoService redoService = new NamingGrpcRedoService(clientProxy, namingFuzzyWatchServiceListHolder, nacosClientProperties); Field redoThreadCountField = NamingGrpcRedoService.class.getDeclaredField("redoThreadCount"); redoThreadCountField.setAccessible(true); Field redoDelayTimeField = NamingGrpcRedoService.class.getDeclaredField("redoDelayTime"); redoDelayTimeField.setAccessible(true); Long redoDelayTimeValue = (Long) redoDelayTimeField.get(redoService); Integer redoThreadCountValue = (Integer) redoThreadCountField.get(redoService); assertEquals(Long.valueOf(4000L), redoDelayTimeValue); assertEquals(Integer.valueOf(2), redoThreadCountValue); } @Test void testOnConnected() { assertFalse(redoService.isConnected()); redoService.onConnected(new TestConnection(new RpcClient.ServerInfo())); assertTrue(redoService.isConnected()); } @Test void testOnDisConnect() { redoService.onConnected(new TestConnection(new RpcClient.ServerInfo())); redoService.cacheInstanceForRedo(SERVICE, GROUP, new Instance()); redoService.instanceRegistered(SERVICE, GROUP); redoService.cacheSubscriberForRedo(SERVICE, GROUP, CLUSTER); redoService.subscriberRegistered(SERVICE, GROUP, CLUSTER); assertTrue(redoService.isConnected()); assertTrue(redoService.findInstanceRedoData().isEmpty()); assertTrue(redoService.findSubscriberRedoData().isEmpty()); redoService.onDisConnect(new TestConnection(new RpcClient.ServerInfo())); assertFalse(redoService.isConnected()); assertFalse(redoService.findInstanceRedoData().isEmpty()); assertFalse(redoService.findSubscriberRedoData().isEmpty()); } @Test void testCacheInstanceForRedo() { ConcurrentMap registeredInstances = getInstanceRedoDataMap(); assertTrue(registeredInstances.isEmpty()); Instance instance = new Instance(); redoService.cacheInstanceForRedo(SERVICE, GROUP, instance); assertFalse(registeredInstances.isEmpty()); InstanceRedoData actual = registeredInstances.entrySet().iterator().next().getValue(); assertEquals(SERVICE, actual.getServiceName()); assertEquals(GROUP, actual.getGroupName()); assertEquals(instance, actual.get()); assertFalse(actual.isRegistered()); assertFalse(actual.isUnregistering()); assertTrue(actual.isExpectedRegistered()); } @Test void testCacheInstanceForRedoByBatchInstanceRedoData() { ConcurrentMap registeredInstances = getInstanceRedoDataMap(); assertTrue(registeredInstances.isEmpty()); Instance instance = new Instance(); List instanceList = new ArrayList<>(); instanceList.add(instance); redoService.cacheInstanceForRedo(SERVICE, GROUP, instanceList); assertFalse(registeredInstances.isEmpty()); BatchInstanceRedoData actual = (BatchInstanceRedoData) registeredInstances.entrySet().iterator().next() .getValue(); assertEquals(SERVICE, actual.getServiceName()); assertEquals(GROUP, actual.getGroupName()); assertEquals(instanceList, actual.getInstances()); assertFalse(actual.isRegistered()); assertFalse(actual.isUnregistering()); } @Test void testInstanceRegistered() { ConcurrentMap registeredInstances = getInstanceRedoDataMap(); redoService.cacheInstanceForRedo(SERVICE, GROUP, new Instance()); redoService.instanceRegistered(SERVICE, GROUP); InstanceRedoData actual = registeredInstances.entrySet().iterator().next().getValue(); assertTrue(actual.isRegistered()); } @Test void testInstanceDeregister() { ConcurrentMap registeredInstances = getInstanceRedoDataMap(); redoService.cacheInstanceForRedo(SERVICE, GROUP, new Instance()); redoService.instanceDeregister(SERVICE, GROUP); InstanceRedoData actual = registeredInstances.entrySet().iterator().next().getValue(); assertTrue(actual.isUnregistering()); assertFalse(actual.isExpectedRegistered()); } @Test void testInstanceDeregistered() { ConcurrentMap registeredInstances = getInstanceRedoDataMap(); redoService.cacheInstanceForRedo(SERVICE, GROUP, new Instance()); redoService.instanceDeregistered(SERVICE, GROUP); InstanceRedoData actual = registeredInstances.entrySet().iterator().next().getValue(); assertFalse(actual.isRegistered()); assertTrue(actual.isUnregistering()); } @Test void testRemoveInstanceForRedo() { ConcurrentMap registeredInstances = getInstanceRedoDataMap(); assertTrue(registeredInstances.isEmpty()); redoService.cacheInstanceForRedo(SERVICE, GROUP, new Instance()); assertFalse(registeredInstances.isEmpty()); redoService.instanceDeregister(SERVICE, GROUP); redoService.removeInstanceForRedo(SERVICE, GROUP); assertTrue(registeredInstances.isEmpty()); } @Test void testFindInstanceRedoData() { redoService.cacheInstanceForRedo(SERVICE, GROUP, new Instance()); assertFalse(redoService.findInstanceRedoData().isEmpty()); redoService.instanceRegistered(SERVICE, GROUP); assertTrue(redoService.findInstanceRedoData().isEmpty()); redoService.instanceDeregister(SERVICE, GROUP); assertFalse(redoService.findInstanceRedoData().isEmpty()); } @SuppressWarnings("all") private ConcurrentMap getInstanceRedoDataMap() { return (ConcurrentMap) ReflectUtils.getFieldValue(redoService, "registeredInstances"); } @Test void testCacheSubscriberForRedo() { ConcurrentMap subscribes = getSubscriberRedoDataMap(); assertTrue(subscribes.isEmpty()); redoService.cacheSubscriberForRedo(SERVICE, GROUP, CLUSTER); assertFalse(subscribes.isEmpty()); SubscriberRedoData actual = subscribes.entrySet().iterator().next().getValue(); assertEquals(SERVICE, actual.getServiceName()); assertEquals(GROUP, actual.getGroupName()); assertEquals(CLUSTER, actual.get()); assertFalse(actual.isRegistered()); assertFalse(actual.isUnregistering()); } @Test void testSubscriberRegistered() { ConcurrentMap subscribes = getSubscriberRedoDataMap(); redoService.cacheSubscriberForRedo(SERVICE, GROUP, CLUSTER); redoService.subscriberRegistered(SERVICE, GROUP, CLUSTER); SubscriberRedoData actual = subscribes.entrySet().iterator().next().getValue(); assertTrue(actual.isRegistered()); } @Test void testSubscriberDeregister() { ConcurrentMap subscribes = getSubscriberRedoDataMap(); redoService.cacheSubscriberForRedo(SERVICE, GROUP, CLUSTER); redoService.subscriberDeregister(SERVICE, GROUP, CLUSTER); SubscriberRedoData actual = subscribes.entrySet().iterator().next().getValue(); assertTrue(actual.isUnregistering()); } @Test void testIsSubscriberRegistered() { assertFalse(redoService.isSubscriberRegistered(SERVICE, GROUP, CLUSTER)); redoService.cacheSubscriberForRedo(SERVICE, GROUP, CLUSTER); redoService.subscriberRegistered(SERVICE, GROUP, CLUSTER); assertTrue(redoService.isSubscriberRegistered(SERVICE, GROUP, CLUSTER)); } @Test void testRemoveSubscriberForRedo() { ConcurrentMap subscribes = getSubscriberRedoDataMap(); assertTrue(subscribes.isEmpty()); redoService.cacheSubscriberForRedo(SERVICE, GROUP, CLUSTER); assertFalse(subscribes.isEmpty()); redoService.subscriberDeregister(SERVICE, GROUP, CLUSTER); redoService.removeSubscriberForRedo(SERVICE, GROUP, CLUSTER); assertTrue(subscribes.isEmpty()); } @Test void testFindSubscriberRedoData() { redoService.cacheSubscriberForRedo(SERVICE, GROUP, CLUSTER); assertFalse(redoService.findSubscriberRedoData().isEmpty()); redoService.subscriberRegistered(SERVICE, GROUP, CLUSTER); assertTrue(redoService.findSubscriberRedoData().isEmpty()); redoService.subscriberDeregister(SERVICE, GROUP, CLUSTER); assertFalse(redoService.findSubscriberRedoData().isEmpty()); } @SuppressWarnings("all") private ConcurrentMap getSubscriberRedoDataMap() { return (ConcurrentMap) ReflectUtils.getFieldValue(redoService, "subscribes"); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/RedoScheduledTaskTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.BatchInstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.InstanceRedoData; import com.alibaba.nacos.client.naming.remote.gprc.redo.data.SubscriberRedoData; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.util.Collections; import java.util.HashSet; import java.util.Set; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove strictness lenient @MockitoSettings(strictness = Strictness.LENIENT) class RedoScheduledTaskTest { private static final String SERVICE = "service"; private static final String GROUP = "group"; private static final String CLUSTER = "cluster"; private static final Instance INSTANCE = new Instance(); @Mock private NamingGrpcClientProxy clientProxy; @Mock private NamingGrpcRedoService redoService; private RedoScheduledTask redoTask; @BeforeEach void setUp() throws Exception { redoTask = new RedoScheduledTask(clientProxy, redoService); when(clientProxy.isEnable()).thenReturn(true); when(redoService.isConnected()).thenReturn(true); } @Test void testRunRedoRegisterInstance() throws NacosException { Set mockData = generateMockInstanceData(false, false, true); when(redoService.findInstanceRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy).doRegisterService(SERVICE, GROUP, INSTANCE); } @Test void testRunRedoRegisterBatchInstance() throws NacosException { BatchInstanceRedoData redoData = BatchInstanceRedoData.build(SERVICE, GROUP, Collections.singletonList(INSTANCE)); redoData.setRegistered(false); redoData.setUnregistering(false); redoData.setExpectedRegistered(true); Set mockData = new HashSet<>(); mockData.add(redoData); when(redoService.findInstanceRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy).doBatchRegisterService(SERVICE, GROUP, redoData.getInstances()); } @Test void testRunRedoDeregisterInstance() throws NacosException { Set mockData = generateMockInstanceData(true, true, false); when(redoService.findInstanceRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy).doDeregisterService(SERVICE, GROUP, INSTANCE); } @Test void testRunRedoRemoveInstanceRedoData() throws NacosException { Set mockData = generateMockInstanceData(false, true, false); when(redoService.findInstanceRedoData()).thenReturn(mockData); redoTask.run(); verify(redoService).removeInstanceForRedo(SERVICE, GROUP); } @Test void testRunRedoRegisterInstanceWithClientDisabled() throws NacosException { when(clientProxy.isEnable()).thenReturn(false); Set mockData = generateMockInstanceData(false, false, true); when(redoService.findInstanceRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy, never()).doRegisterService(SERVICE, GROUP, INSTANCE); } @Test void testRunRedoDeregisterInstanceWithClientDisabled() throws NacosException { when(clientProxy.isEnable()).thenReturn(false); Set mockData = generateMockInstanceData(true, true, false); when(redoService.findInstanceRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy, never()).doRegisterService(SERVICE, GROUP, INSTANCE); } @Test void testRunRedoRegisterInstanceWithNacosException() throws NacosException { Set mockData = generateMockInstanceData(false, false, true); when(redoService.findInstanceRedoData()).thenReturn(mockData); doThrow(new NacosException()).when(clientProxy).doRegisterService(SERVICE, GROUP, INSTANCE); redoTask.run(); // Not any exception thrown } @Test void testRunRedoRegisterInstanceWithOtherException() throws NacosException { Set mockData = generateMockInstanceData(false, false, true); when(redoService.findInstanceRedoData()).thenReturn(mockData); doThrow(new RuntimeException("test")).when(clientProxy).doRegisterService(SERVICE, GROUP, INSTANCE); redoTask.run(); // Not any exception thrown } private Set generateMockInstanceData(boolean registered, boolean unregistering, boolean expectedRegistered) { InstanceRedoData redoData = InstanceRedoData.build(SERVICE, GROUP, INSTANCE); redoData.setRegistered(registered); redoData.setUnregistering(unregistering); redoData.setExpectedRegistered(expectedRegistered); Set result = new HashSet<>(); result.add(redoData); return result; } @Test void testRunRedoRegisterSubscriber() throws NacosException { Set mockData = generateMockSubscriberData(false, false, true); when(redoService.findSubscriberRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy).doSubscribe(SERVICE, GROUP, CLUSTER); } @Test void testRunRedoDeregisterSubscriber() throws NacosException { Set mockData = generateMockSubscriberData(true, true, false); when(redoService.findSubscriberRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy).doUnsubscribe(SERVICE, GROUP, CLUSTER); } @Test void testRunRedoRemoveSubscriberRedoData() throws NacosException { Set mockData = generateMockSubscriberData(false, true, false); when(redoService.findSubscriberRedoData()).thenReturn(mockData); redoTask.run(); verify(redoService).removeSubscriberForRedo(SERVICE, GROUP, CLUSTER); } @Test void testRunRedoRegisterSubscriberWithClientDisabled() throws NacosException { when(clientProxy.isEnable()).thenReturn(false); Set mockData = generateMockSubscriberData(false, false, true); when(redoService.findSubscriberRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy, never()).doSubscribe(SERVICE, GROUP, CLUSTER); } @Test void testRunRedoDeRegisterSubscriberWithClientDisabled() throws NacosException { when(clientProxy.isEnable()).thenReturn(false); Set mockData = generateMockSubscriberData(true, true, false); when(redoService.findSubscriberRedoData()).thenReturn(mockData); redoTask.run(); verify(clientProxy, never()).doUnsubscribe(SERVICE, GROUP, CLUSTER); } @Test void testRunRedoRegisterSubscriberWithNacosException() throws NacosException { Set mockData = generateMockSubscriberData(false, false, true); when(redoService.findSubscriberRedoData()).thenReturn(mockData); doThrow(new NacosException()).when(clientProxy).doSubscribe(SERVICE, GROUP, CLUSTER); redoTask.run(); // Not any exception thrown } private Set generateMockSubscriberData(boolean registered, boolean unregistering, boolean expectedRegistered) { SubscriberRedoData redoData = SubscriberRedoData.build(SERVICE, GROUP, CLUSTER); redoData.setRegistered(registered); redoData.setUnregistering(unregistering); redoData.setExpectedRegistered(expectedRegistered); Set result = new HashSet<>(); result.add(redoData); return result; } @Test void testRunRedoWithDisconnection() { when(redoService.isConnected()).thenReturn(false); redoTask.run(); verify(redoService, never()).findInstanceRedoData(); verify(redoService, never()).findSubscriberRedoData(); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/BatchInstanceRedoDataTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo.data; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class BatchInstanceRedoDataTest { @Test @SuppressWarnings("all") void testEquals() { BatchInstanceRedoData redoData1 = new BatchInstanceRedoData("a", "b"); redoData1.setInstances(Collections.singletonList(new Instance())); BatchInstanceRedoData redoData2 = new BatchInstanceRedoData("a", "b"); redoData2.setInstances(Collections.singletonList(new Instance())); assertEquals(redoData1, redoData1); assertEquals(redoData1, redoData2); redoData2.getInstances().get(0).setIp("1.1.1.1"); assertNotEquals(null, redoData1); assertNotEquals(redoData1, redoData2); assertNotEquals(redoData1, redoData2); BatchInstanceRedoData redoData3 = new BatchInstanceRedoData("c", "b"); assertNotEquals(redoData1, redoData3); } @Test void testHashCode() { BatchInstanceRedoData redoData1 = new BatchInstanceRedoData("a", "b"); redoData1.setInstances(Collections.singletonList(new Instance())); BatchInstanceRedoData redoData2 = new BatchInstanceRedoData("a", "b"); redoData2.setInstances(Collections.singletonList(new Instance())); assertEquals(redoData1.hashCode(), redoData2.hashCode()); redoData2.getInstances().get(0).setIp("1.1.1.1"); assertNotEquals(redoData1.hashCode(), redoData2.hashCode()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/gprc/redo/data/InstanceRedoDataTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.gprc.redo.data; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class InstanceRedoDataTest { @Test void testEquals() { InstanceRedoData redoData1 = new InstanceRedoData("a", "b"); assertEquals(redoData1, redoData1); assertNotEquals(null, redoData1); BatchInstanceRedoData redoData2 = new BatchInstanceRedoData("a", "b"); assertNotEquals(redoData1, redoData2); InstanceRedoData redoData3 = new InstanceRedoData("a", "b"); assertEquals(redoData1, redoData3); } @Test void testHashCode() { InstanceRedoData redoData1 = new InstanceRedoData("a", "b"); redoData1.set(new Instance()); InstanceRedoData redoData2 = new InstanceRedoData("a", "b"); redoData2.set(new Instance()); assertEquals(redoData1.hashCode(), redoData2.hashCode()); redoData2.get().setIp("1.1.1.1"); assertNotEquals(redoData1.hashCode(), redoData2.hashCode()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientManagerTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.http; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.client.request.HttpClientRequest; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.io.IOException; import java.lang.reflect.Field; import java.util.Map; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class NamingHttpClientManagerTest { @Test void testGetInstance() { assertNotNull(NamingHttpClientManager.getInstance()); } @Test void testGetPrefix() { assertEquals(HTTP_PREFIX, NamingHttpClientManager.getInstance().getPrefix()); } @Test void testGetNacosRestTemplate() { assertNotNull(NamingHttpClientManager.getInstance().getNacosRestTemplate()); } @Test void testShutdown() throws NoSuchFieldException, IllegalAccessException, NacosException, IOException { //given NamingHttpClientManager instance = NamingHttpClientManager.getInstance(); HttpClientRequest mockHttpClientRequest = Mockito.mock(HttpClientRequest.class); Field requestClient = NacosRestTemplate.class.getDeclaredField("requestClient"); requestClient.setAccessible(true); requestClient.set(instance.getNacosRestTemplate(), mockHttpClientRequest); // when NamingHttpClientManager.getInstance().shutdown(); // then verify(mockHttpClientRequest, times(1)).close(); } @Test void testShutdownWithException() throws Exception { String key = "com.alibaba.nacos.client.naming.remote.http.NamingHttpClientManager$NamingHttpClientFactory"; try { HttpClientBeanHolder.shutdownNacosSyncRest(key); } catch (Exception ignored) { } Field field = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); field.setAccessible(true); Map map = (Map) field.get(null); NacosRestTemplate mockRest = mock(NacosRestTemplate.class); map.put(key, mockRest); doThrow(new RuntimeException("test")).when(mockRest).close(); NamingHttpClientManager.getInstance().shutdown(); assertEquals(mockRest, map.remove(key)); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/remote/http/NamingHttpClientProxyTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.remote.http; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ListView; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.selector.ExpressionSelector; import com.alibaba.nacos.api.selector.NoneSelector; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.naming.core.NamingServerListManager; import com.alibaba.nacos.client.address.ServerListChangeEvent; import com.alibaba.nacos.client.naming.utils.UtilAndComs; import com.alibaba.nacos.client.security.SecurityProxy; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.HttpMethod; import com.alibaba.nacos.common.utils.ReflectUtils; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; 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 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.endsWith; 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; @ExtendWith(MockitoExtension.class) @MockitoSettings(strictness = Strictness.LENIENT) class NamingHttpClientProxyTest { @Mock private SecurityProxy proxy; @Mock private NamingServerListManager mgr; private Properties props; private NamingHttpClientProxy clientProxy; @BeforeEach void setUp() { when(mgr.getServerList()).thenReturn(Arrays.asList("localhost")); props = new Properties(); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); clientProxy = new NamingHttpClientProxy("namespaceId", proxy, mgr, nacosClientProperties); } @AfterEach void tearDown() throws NacosException { clientProxy.shutdown(); System.clearProperty(SystemPropertyKeyConst.NAMING_SERVER_PORT); } @Test void testOnEvent() { clientProxy.onEvent(new ServerListChangeEvent()); // Do nothing } @Test void testSubscribeType() { assertEquals(ServerListChangeEvent.class, clientProxy.subscribeType()); } @Test void testRegisterService() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("127.0.0.1:8848"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setEphemeral(false); //when clientProxy.registerService(serviceName, groupName, instance); //then verify(nacosRestTemplate, times(1)).exchangeForm(any(), any(), any(), any(), any(), any()); } @Test void testRegisterEphemeralInstance() throws NacosException { assertThrows(UnsupportedOperationException.class, () -> { Instance instance = new Instance(); clientProxy.registerService("a", "b", instance); }); } @Test void testRegisterServiceThrowsNacosException() throws Exception { Throwable exception = assertThrows(NacosException.class, () -> { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setCode(503); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setEphemeral(false); try { clientProxy.registerService(serviceName, groupName, instance); } catch (NacosException ex) { // verify the `NacosException` is directly thrown assertNull(ex.getCause()); throw ex; } }); assertTrue(exception.getMessage().contains("failed to req API")); } @Test void testRegisterServiceThrowsException() throws Exception { assertThrows(NacosException.class, () -> { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setCode(503); // makes exchangeForm failed with a NullPointerException when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(null); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setEphemeral(false); try { clientProxy.registerService(serviceName, groupName, instance); } catch (NacosException ex) { // verify the `NacosException` is directly thrown assertTrue(ex.getErrMsg().contains("java.lang.NullPointerException")); assertEquals(NacosException.SERVER_ERROR, ex.getErrCode()); throw ex; } }); } @Test void testBatchRegisterService() { assertThrows(UnsupportedOperationException.class, () -> { clientProxy.batchRegisterService("a", "b", null); }); } @Test void testBatchDeregisterService() { assertThrows(UnsupportedOperationException.class, () -> { clientProxy.batchDeregisterService("a", "b", null); }); } @Test void testDeregisterService() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("127.0.0.1:8848"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); instance.setEphemeral(false); //when clientProxy.deregisterService(serviceName, groupName, instance); //then verify(nacosRestTemplate, times(1)).exchangeForm(any(), any(), any(), any(), eq(HttpMethod.DELETE), any()); } @Test void testDeregisterServiceForEphemeral() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); Instance instance = new Instance(); clientProxy.deregisterService("serviceName", "groupName", instance); verify(nacosRestTemplate, never()).exchangeForm(any(), any(), any(), any(), eq(HttpMethod.DELETE), any()); } @Test void testUpdateInstance() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("127.0.0.1:8848"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; Instance instance = new Instance(); //when clientProxy.updateInstance(serviceName, groupName, instance); //then verify(nacosRestTemplate, times(1)).exchangeForm(any(), any(), any(), any(), eq(HttpMethod.PUT), any()); } @Test void testQueryInstancesOfServiceThrowsException() { //assert exception String serviceName = "service1"; String groupName = "group1"; String clusters = "cluster1"; assertThrows(UnsupportedOperationException.class, () -> clientProxy.queryInstancesOfService(serviceName, groupName, clusters, false)); } @Test void testQueryService() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("{\"name\":\"service1\",\"groupName\":\"group1\"}"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; //when Service service = clientProxy.queryService(serviceName, groupName); //then verify(nacosRestTemplate, times(1)).exchangeForm(endsWith(UtilAndComs.nacosUrlService), any(), any(), any(), eq(HttpMethod.GET), any()); assertEquals(serviceName, service.getName()); assertEquals(groupName, service.getGroupName()); } @Test void testCreateService() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData(""); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); //when clientProxy.createService(new Service(), new NoneSelector()); //then verify(nacosRestTemplate, times(1)).exchangeForm(endsWith(UtilAndComs.nacosUrlService), any(), any(), any(), eq(HttpMethod.POST), any()); } @Test void testDeleteService() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("{\"name\":\"service1\",\"groupName\":\"group1\"}"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; //when clientProxy.deleteService(serviceName, groupName); //then verify(nacosRestTemplate, times(1)).exchangeForm(endsWith(UtilAndComs.nacosUrlService), any(), any(), any(), eq(HttpMethod.DELETE), any()); } @Test void testUpdateService() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData(""); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String serviceName = "service1"; String groupName = "group1"; //when clientProxy.updateService(new Service(), new NoneSelector()); //then verify(nacosRestTemplate, times(1)).exchangeForm(endsWith(UtilAndComs.nacosUrlService), any(), any(), any(), eq(HttpMethod.PUT), any()); } @Test void testServerHealthy() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("{\"code\":0,\"message\":\"success\",\"data\":\"ok\"}"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); //when boolean serverHealthy = clientProxy.serverHealthy(); //then verify(nacosRestTemplate, times(1)).exchangeForm(endsWith("/v3/admin/core/state/liveness"), any(), any(), any(), eq(HttpMethod.GET), any()); assertTrue(serverHealthy); } @Test void testServerHealthyForException() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenThrow( new RuntimeException("test")); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); assertFalse(clientProxy.serverHealthy()); } @Test void testGetServiceList() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("{\"count\":2,\"doms\":[\"aaa\",\"bbb\"]}"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String groupName = "group1"; //when ListView serviceList = clientProxy.getServiceList(1, 10, groupName, new NoneSelector()); //then verify(nacosRestTemplate, times(1)).exchangeForm(endsWith("/service/list"), any(), any(), any(), eq(HttpMethod.GET), any()); assertEquals(2, serviceList.getCount()); assertEquals("aaa", serviceList.getData().get(0)); assertEquals("bbb", serviceList.getData().get(1)); } @Test void testGetServiceListWithLabelSelector() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult a = new HttpRestResult(); a.setData("{\"count\":2,\"doms\":[\"aaa\",\"bbb\"]}"); a.setCode(200); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenReturn(a); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String groupName = "group1"; //when ListView serviceList = clientProxy.getServiceList(1, 10, groupName, new ExpressionSelector()); //then verify(nacosRestTemplate, times(1)).exchangeForm(endsWith("/service/list"), any(), any(), any(), eq(HttpMethod.GET), any()); assertEquals(2, serviceList.getCount()); assertEquals("aaa", serviceList.getData().get(0)); assertEquals("bbb", serviceList.getData().get(1)); } @Test void testSubscribe() throws Exception { assertThrows(UnsupportedOperationException.class, () -> { String groupName = "group1"; String serviceName = "serviceName"; String clusters = "clusters"; //when clientProxy.subscribe(serviceName, groupName, clusters); }); } @Test void testUnsubscribe() throws Exception { String groupName = "group1"; String serviceName = "serviceName"; String clusters = "clusters"; //when clientProxy.unsubscribe(serviceName, groupName, clusters); // do nothing } @Test void testIsSubscribed() throws NacosException { assertTrue(clientProxy.isSubscribed("serviceName", "group1", "clusters")); } @Test void testReqApi() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { //return url HttpRestResult res = new HttpRestResult(); res.setData(invocationOnMock.getArgument(0)); res.setCode(200); return res; }); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String api = "/api"; Map params = new HashMap<>(); String method = HttpMethod.GET; //when String res = clientProxy.reqApi(api, params, method); //then assertEquals("http://localhost:8848/api", res); } @Test void testReqApi2() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { //return url HttpRestResult res = new HttpRestResult(); res.setData(invocationOnMock.getArgument(0)); res.setCode(200); return res; }); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String api = "/api"; Map params = new HashMap<>(); Map body = new HashMap<>(); String method = HttpMethod.GET; //when String res = clientProxy.reqApi(api, params, body, method); //then assertEquals("http://localhost:8848/api", res); } @Test void testReqApi3() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { //return url HttpRestResult res = new HttpRestResult(); res.setData(invocationOnMock.getArgument(0)); res.setCode(200); return res; }); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String api = "/api"; Map params = new HashMap<>(); Map body = new HashMap<>(); String method = HttpMethod.GET; List servers = Arrays.asList("127.0.0.1"); //when String res = clientProxy.reqApi(api, params, body, servers, method); //then assertEquals("http://127.0.0.1:8848/api", res); } @Test void testCallServerFail() throws Exception { assertThrows(NacosException.class, () -> { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenAnswer( invocationOnMock -> { //return url HttpRestResult res = new HttpRestResult(); res.setMessage("fail"); res.setCode(400); return res; }); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String api = "/api"; Map params = new HashMap<>(); Map body = new HashMap<>(); String method = HttpMethod.GET; String curServer = "127.0.0.1"; //when clientProxy.callServer(api, params, body, curServer, method); }); } @Test void testCallServerFail304() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { //return url HttpRestResult res = new HttpRestResult(); res.setMessage("redirect"); res.setCode(304); return res; }); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String api = "/api"; Map params = new HashMap<>(); Map body = new HashMap<>(); String method = HttpMethod.GET; String curServer = "127.0.0.1"; //when String s = clientProxy.callServer(api, params, body, curServer, method); //then assertEquals("", s); } @Test void testGetNamespaceId() { String namespaceId = "aaa"; final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(props); NamingHttpClientProxy clientProxy = new NamingHttpClientProxy(namespaceId, proxy, mgr, nacosClientProperties); String actualNamespaceId = clientProxy.getNamespaceId(); assertEquals(namespaceId, actualNamespaceId); } @Test void testSetServerPort() { clientProxy.setServerPort(1234); assertEquals(1234, ReflectUtils.getFieldValue(clientProxy, "serverPort")); System.setProperty(SystemPropertyKeyConst.NAMING_SERVER_PORT, "1111"); clientProxy.setServerPort(1234); assertEquals(1111, ReflectUtils.getFieldValue(clientProxy, "serverPort")); } @Test void testReqApiForEmptyServer() throws NacosException { assertThrows(NacosException.class, () -> { Map params = new HashMap<>(); clientProxy.reqApi("api", params, Collections.emptyMap(), Collections.emptyList(), HttpMethod.GET); }); } @Test void testRegApiForDomain() throws NacosException { assertThrows(NacosException.class, () -> { Map params = new HashMap<>(); clientProxy.reqApi("api", params, Collections.emptyMap(), Collections.emptyList(), HttpMethod.GET); }); } @Test void testCallServerFail403() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.exchangeForm(any(), any(), any(), any(), any(), any())).thenAnswer(invocationOnMock -> { //return url HttpRestResult res = new HttpRestResult(); res.setMessage("Invalid signature"); res.setCode(403); return res; }); final Field nacosRestTemplateField = NamingHttpClientProxy.class.getDeclaredField("nacosRestTemplate"); nacosRestTemplateField.setAccessible(true); nacosRestTemplateField.set(clientProxy, nacosRestTemplate); String api = "/api"; Map params = new HashMap<>(); Map body = new HashMap<>(); String method = HttpMethod.GET; String curServer = "127.0.0.1"; //then assertThrows(NacosException.class, () -> clientProxy.callServer(api, params, body, curServer, method)); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/selector/DefaultNamingSelectorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.selector.NamingContext; import com.alibaba.nacos.api.naming.selector.NamingResult; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import java.util.Random; 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; public class DefaultNamingSelectorTest { @Test public void testSelect() { DefaultNamingSelector namingSelector = new DefaultNamingSelector(Instance::isHealthy); Random random = new Random(); int total = random.nextInt(32) + 1; int health = random.nextInt(total); NamingContext namingContext = getMockNamingContext(total, health); NamingResult result = namingSelector.select(namingContext); assertEquals(health, result.getResult().size()); result.getResult().forEach(ins -> assertTrue(ins.isHealthy())); } private NamingContext getMockNamingContext(int total, int health) { NamingContext namingContext = mock(NamingContext.class); when(namingContext.getInstances()).thenReturn(getInstance(total, health)); return namingContext; } private List getInstance(int total, int health) { List list = new ArrayList<>(total); for (int i = 0; i < total; i++) { Instance instance = new Instance(); instance.setHealthy(false); list.add(instance); } for (int i = 0; i < health; i++) { list.get(i).setHealthy(true); } return list; } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/selector/NamingListenerInvokerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.listener.AbstractEventListener; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.listener.AbstractNamingChangeListener; import com.alibaba.nacos.client.naming.listener.NamingChangeEvent; import org.junit.jupiter.api.Test; import java.util.Collections; 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.spy; import static org.mockito.Mockito.verify; public class NamingListenerInvokerTest { @Test public void testEventListener() { EventListener listener = mock(EventListener.class); NamingListenerInvoker listenerInvoker = new NamingListenerInvoker(listener); NamingEvent event = new NamingEvent("serviceName", Collections.emptyList()); assertFalse(listenerInvoker.isInvoked()); listenerInvoker.invoke(event); verify(listener).onEvent(event); assertTrue(listenerInvoker.isInvoked()); } @Test public void testAbstractEventListener() { AbstractEventListener listener = mock(AbstractEventListener.class); NamingListenerInvoker listenerInvoker = new NamingListenerInvoker(listener); NamingEvent event = new NamingEvent("serviceName", Collections.emptyList()); assertFalse(listenerInvoker.isInvoked()); listenerInvoker.invoke(event); verify(listener).getExecutor(); assertTrue(listenerInvoker.isInvoked()); } @Test public void testAbstractNamingChaneEventListener() { AbstractNamingChangeListener listener = spy(AbstractNamingChangeListener.class); NamingListenerInvoker listenerInvoker = new NamingListenerInvoker(listener); NamingChangeEvent event = new NamingChangeEvent("serviceName", Collections.emptyList(), new InstancesDiff()); assertFalse(listenerInvoker.isInvoked()); listenerInvoker.invoke(event); verify(listener).onChange(event); assertTrue(listenerInvoker.isInvoked()); } @Test public void testEquals() { EventListener listener1 = mock(EventListener.class); EventListener listener2 = mock(EventListener.class); NamingListenerInvoker invoker1 = new NamingListenerInvoker(listener1); NamingListenerInvoker invoker2 = new NamingListenerInvoker(listener1); NamingListenerInvoker invoker3 = new NamingListenerInvoker(listener2); assertEquals(invoker1.hashCode(), invoker2.hashCode()); assertEquals(invoker1, invoker2); assertNotEquals(invoker1.hashCode(), invoker3.hashCode()); assertNotEquals(invoker1, invoker3); assertNotEquals(null, invoker1); assertEquals(invoker1, invoker1); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/selector/NamingSelectorFactoryTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.selector.NamingContext; import com.alibaba.nacos.api.naming.selector.NamingResult; import com.alibaba.nacos.api.naming.selector.NamingSelector; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; 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; public class NamingSelectorFactoryTest { @Test public void testNewClusterSelector1() { Instance ins1 = new Instance(); ins1.setClusterName("a"); Instance ins2 = new Instance(); ins2.setClusterName("b"); Instance ins3 = new Instance(); ins3.setClusterName("c"); NamingContext namingContext = mock(NamingContext.class); when(namingContext.getInstances()).thenReturn(Arrays.asList(ins1, ins2, ins3)); NamingSelector namingSelector1 = NamingSelectorFactory.newClusterSelector(Collections.singletonList("a")); NamingResult result1 = namingSelector1.select(namingContext); assertEquals("a", result1.getResult().get(0).getClusterName()); NamingSelector namingSelector2 = NamingSelectorFactory.newClusterSelector(Collections.emptyList()); NamingResult result2 = namingSelector2.select(namingContext); assertEquals(3, result2.getResult().size()); } @Test public void testNewClusterSelector2() { NamingSelector namingSelector1 = NamingSelectorFactory.newClusterSelector(Arrays.asList("a", "b", "c")); NamingSelector namingSelector2 = NamingSelectorFactory.newClusterSelector(Arrays.asList("c", "b", "a")); NamingSelector namingSelector3 = NamingSelectorFactory.newClusterSelector(Arrays.asList("a", "b", "c", "c")); NamingSelector namingSelector4 = NamingSelectorFactory.newClusterSelector(Arrays.asList("d", "e")); assertEquals(namingSelector1, namingSelector2); assertEquals(namingSelector1, namingSelector3); assertNotEquals(namingSelector1, namingSelector4); } @Test public void testNewIpSelector() { Instance ins1 = new Instance(); ins1.setIp("172.18.137.120"); Instance ins2 = new Instance(); ins2.setIp("172.18.137.121"); Instance ins3 = new Instance(); ins3.setIp("172.18.136.111"); NamingContext namingContext = mock(NamingContext.class); when(namingContext.getInstances()).thenReturn(Arrays.asList(ins1, ins2, ins3)); NamingSelector ipSelector = NamingSelectorFactory.newIpSelector("^172\\.18\\.137.*"); NamingResult result = ipSelector.select(namingContext); List list = result.getResult(); assertEquals(2, list.size()); assertEquals(ins1.getIp(), list.get(0).getIp()); assertEquals(ins2.getIp(), list.get(1).getIp()); } @Test public void testNewMetadataSelector() { Instance ins1 = new Instance(); ins1.setMetadata(new LinkedHashMap<>()); ins1.addMetadata("a", "1"); ins1.addMetadata("b", "2"); Instance ins2 = new Instance(); ins2.addMetadata("a", "1"); Instance ins3 = new Instance(); ins3.addMetadata("b", "2"); NamingContext namingContext = mock(NamingContext.class); when(namingContext.getInstances()).thenReturn(Arrays.asList(ins1, ins2, ins3)); NamingSelector metadataSelector = NamingSelectorFactory.newMetadataSelector(new LinkedHashMap() { { put("a", "1"); put("b", "2"); } }); List result = metadataSelector.select(namingContext).getResult(); assertEquals(1, result.size()); assertEquals(ins1, result.get(0)); } @Test public void testNewMetadataSelector2() { Instance ins1 = new Instance(); ins1.setMetadata(new LinkedHashMap<>()); ins1.addMetadata("a", "1"); ins1.addMetadata("c", "3"); Instance ins2 = new Instance(); ins2.addMetadata("b", "2"); Instance ins3 = new Instance(); ins3.addMetadata("c", "3"); NamingContext namingContext = mock(NamingContext.class); when(namingContext.getInstances()).thenReturn(Arrays.asList(ins1, ins2, ins3)); NamingSelector metadataSelector = NamingSelectorFactory.newMetadataSelector(new LinkedHashMap() { { put("a", "1"); put("b", "2"); } }, true); List result = metadataSelector.select(namingContext).getResult(); assertEquals(2, result.size()); assertEquals(ins1, result.get(0)); assertEquals(ins2, result.get(1)); } @Test public void testHealthSelector() { Instance ins1 = new Instance(); Instance ins2 = new Instance(); Instance ins3 = new Instance(); ins3.setHealthy(false); NamingContext namingContext = mock(NamingContext.class); when(namingContext.getInstances()).thenReturn(Arrays.asList(ins1, ins2, ins3)); List result = NamingSelectorFactory.HEALTHY_SELECTOR.select(namingContext).getResult(); assertEquals(2, result.size()); assertTrue(result.contains(ins1)); assertTrue(result.contains(ins2)); assertTrue(result.get(0).isHealthy()); assertTrue(result.get(1).isHealthy()); } @Test public void testEmptySelector() { Instance ins1 = new Instance(); Instance ins2 = new Instance(); Instance ins3 = new Instance(); NamingContext namingContext = mock(NamingContext.class); when(namingContext.getInstances()).thenReturn(Arrays.asList(ins1, ins2, ins3)); List result = NamingSelectorFactory.EMPTY_SELECTOR.select(namingContext).getResult(); assertEquals(3, result.size()); assertTrue(result.contains(ins1)); assertTrue(result.contains(ins2)); assertTrue(result.contains(ins3)); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/selector/NamingSelectorWrapperTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.selector.NamingSelector; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.event.InstancesDiff; import com.alibaba.nacos.client.naming.listener.NamingChangeEvent; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashSet; import java.util.Objects; import java.util.Set; 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.ArgumentMatchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; public class NamingSelectorWrapperTest { @Test public void testEquals() { EventListener listener = mock(EventListener.class); NamingSelector selector1 = mock(NamingSelector.class); NamingSelector selector2 = mock(NamingSelector.class); NamingSelectorWrapper sw1 = new NamingSelectorWrapper(selector1, listener); NamingSelectorWrapper sw2 = new NamingSelectorWrapper(selector2, listener); NamingSelectorWrapper sw3 = new NamingSelectorWrapper(selector1, listener); assertNotEquals(sw1.hashCode(), sw2.hashCode()); assertEquals(sw1.hashCode(), sw3.hashCode()); assertNotEquals(sw1, sw2); assertEquals(sw1, sw3); Set set = new HashSet<>(); set.add(sw1); assertFalse(set.contains(sw2)); assertTrue(set.contains(sw3)); assertTrue(set.add(sw2)); assertFalse(set.add(sw3)); assertTrue(set.remove(sw3)); assertEquals(sw1, new NamingSelectorWrapper("a", "b", "c", selector1, listener)); } @Test public void testSelectable() { NamingSelectorWrapper selectorWrapper = new NamingSelectorWrapper(null, null); assertFalse(selectorWrapper.isSelectable(null)); InstancesChangeEvent event1 = new InstancesChangeEvent(null, null, null, null, null, null); assertFalse(selectorWrapper.isSelectable(event1)); InstancesChangeEvent event2 = new InstancesChangeEvent(null, null, null, null, null, new InstancesDiff()); assertFalse(selectorWrapper.isSelectable(event2)); InstancesChangeEvent event3 = new InstancesChangeEvent(null, null, null, null, Collections.emptyList(), null); assertFalse(selectorWrapper.isSelectable(event3)); InstancesChangeEvent event4 = new InstancesChangeEvent(null, null, null, null, Collections.emptyList(), new InstancesDiff()); assertTrue(selectorWrapper.isSelectable(event4)); } @Test public void testCallable() { NamingSelectorWrapper selectorWrapper = new NamingSelectorWrapper(null, null); InstancesDiff instancesDiff = new InstancesDiff(null, Collections.singletonList(new Instance()), null); NamingChangeEvent changeEvent = new NamingChangeEvent("serviceName", Collections.emptyList(), instancesDiff); assertTrue(selectorWrapper.isCallable(changeEvent)); changeEvent.getRemovedInstances().clear(); assertFalse(selectorWrapper.isCallable(changeEvent)); } @Test public void testNotifyListener() { EventListener listener = mock(EventListener.class); NamingSelectorWrapper selectorWrapper = new NamingSelectorWrapper( new DefaultNamingSelector(Instance::isHealthy), listener); InstancesDiff diff = new InstancesDiff(null, Collections.singletonList(new Instance()), null); InstancesChangeEvent event = new InstancesChangeEvent(null, "serviceName", "groupName", "clusters", Collections.emptyList(), diff); selectorWrapper.notifyListener(event); verify(listener).onEvent(argThat(Objects::nonNull)); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/selector/ServiceInfoContextTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.selector; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class ServiceInfoContextTest { @Test void testGetAll() { ServiceInfo serviceInfo = ServiceInfo.fromKey("aaa@@bbb@@ccc,ddd"); serviceInfo.addHost(new Instance()); ServiceInfoContext context = new ServiceInfoContext(serviceInfo); assertEquals(1, context.getInstances().size()); assertEquals("aaa", context.getGroupName()); assertEquals("bbb", context.getServiceName()); assertEquals("ccc,ddd", context.getClusters()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/utils/CacheDirUtilTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.client.env.NacosClientProperties; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class CacheDirUtilTest { @AfterEach void tearDown() throws Exception { System.clearProperty("user.home"); System.clearProperty("JM.SNAPSHOT.PATH"); } @Test void testInitCacheDirWithDefaultRootAndWithoutCache() { System.setProperty("user.home", "/home/admin"); String actual = CacheDirUtil.initCacheDir("test", NacosClientProperties.PROTOTYPE.derive()); assertEquals("/home/admin/nacos/naming/test", actual); } @Test void testInitCacheDirWithDefaultRootAndWithoutCache2() { NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty("user.home", "/home/test"); String actual = CacheDirUtil.initCacheDir("test", properties); assertEquals("/home/test/nacos/naming/test", actual); } @Test void testInitCacheDirWithDefaultRootAndWithCache() { System.setProperty("user.home", "/home/admin"); NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.NAMING_CACHE_REGISTRY_DIR, "custom"); String actual = CacheDirUtil.initCacheDir("test", properties); assertEquals("/home/admin/nacos/custom/naming/test", actual); } @Test void testInitCacheDirWithJmSnapshotPathRootAndWithoutCache() { System.setProperty("JM.SNAPSHOT.PATH", "/home/snapshot"); String actual = CacheDirUtil.initCacheDir("test", NacosClientProperties.PROTOTYPE.derive()); assertEquals("/home/snapshot/nacos/naming/test", actual); } @Test void testInitCacheDirWithJmSnapshotPathRootAndWithoutCache2() { NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty("JM.SNAPSHOT.PATH", "/home/custom/snapshot"); String actual = CacheDirUtil.initCacheDir("test", properties); assertEquals("/home/custom/snapshot/nacos/naming/test", actual); } @Test void testInitCacheDirWithJmSnapshotPathRootAndWithCache() { System.setProperty("user.home", "/home/snapshot"); NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.NAMING_CACHE_REGISTRY_DIR, "custom"); String actual = CacheDirUtil.initCacheDir("test", properties); assertEquals("/home/snapshot/nacos/custom/naming/test", actual); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/utils/ChooserTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import com.alibaba.nacos.api.naming.pojo.Instance; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class ChooserTest { @Test void testChooser() { //Test the correctness of Chooser, the weight of the final selected instance must be greater than 0 List hosts = getInstanceList(); Instance target = getRandomInstance(hosts); assertTrue(hosts.contains(target) && target.getWeight() > 0); } @Test void testChooserRandomForEmptyList() { Chooser chooser = new Chooser<>("test"); assertEquals("test", chooser.getUniqueKey()); assertNull(chooser.random()); } @Test void testChooserRandomForOneSizeList() { List> list = new LinkedList<>(); list.add(new Pair<>("test", 1)); Chooser chooser = new Chooser<>("test", list); String actual = chooser.random(); assertNotNull(actual); assertEquals("test", actual); } @Test void testChooserRandom() { List> list = new LinkedList<>(); list.add(new Pair<>("test", 1)); list.add(new Pair<>("test2", 1)); Chooser chooser = new Chooser<>("test", list); String actual = chooser.random(); assertNotNull(actual); assertTrue(actual.equals("test") || actual.equals("test2")); } @Test void testOnlyOneInstanceWeightIsNotZero() { // If there is only one instance whose weight is not zero, it will be selected List hosts = getOneInstanceNotZeroList(); Instance target = getRandomInstance(hosts); assertTrue(target.getWeight() > 0); } @Test void testInstanceWeightAllZero() { // Throw an IllegalStateException when all instances have a weight of zero. List hosts = getInstanceWeightAllZero(); try { getRandomInstance(hosts); } catch (Exception e) { assertTrue(e instanceof IllegalStateException); } } @Test void testRandomWithWeightForNaNAndInfinity() { assertThrows(IllegalStateException.class, () -> { List> list = new LinkedList<>(); list.add(new Pair<>("test", Double.NaN)); list.add(new Pair<>("test2", Double.POSITIVE_INFINITY)); new Chooser<>("test", list); }); } @Test void testRefresh() { Chooser chooser = new Chooser<>("test"); assertEquals("test", chooser.getUniqueKey()); assertNull(chooser.random()); List> list = new LinkedList<>(); list.add(new Pair<>("test", 1)); chooser.refresh(list); String actual = chooser.random(); assertNotNull(actual); assertEquals("test", actual); } @Test void testEqualsHashCode() { List> list = new LinkedList<>(); list.add(new Pair<>("test", 1)); list.add(new Pair<>("test2", 1)); Chooser chooser = new Chooser<>("test", list); assertEquals("test".hashCode(), chooser.hashCode()); assertEquals(chooser, chooser); assertNotEquals(null, chooser); assertNotEquals("test", chooser); Chooser chooser1 = new Chooser<>(null, null); assertNotEquals(chooser, chooser1); assertNotEquals(chooser1, chooser); Chooser chooser2 = new Chooser<>("test", Collections.emptyList()); assertNotEquals(chooser, chooser2); assertNotEquals(chooser2, chooser); Chooser chooser3 = new Chooser<>("test1", list); assertNotEquals(chooser, chooser3); Chooser chooser4 = new Chooser<>("test", list); assertEquals(chooser, chooser4); } @Test void testRefEqualsHashCode() { List> list = new LinkedList<>(); list.add(new Pair<>("test", 1)); list.add(new Pair<>("test2", 1)); Chooser chooser = new Chooser<>("test", list); Chooser.Ref ref = chooser.getRef(); assertEquals(list.hashCode(), ref.hashCode()); assertEquals(ref, ref); assertNotEquals(null, ref); assertNotEquals(ref, chooser); Chooser.Ref ref1 = new Chooser<>("test", null).getRef(); assertNotEquals(ref, ref1); assertNotEquals(ref1, ref); Chooser.Ref ref2 = new Chooser<>("test", list).getRef(); assertEquals(ref, ref2); } private List getInstanceList() { List list = new ArrayList<>(); int size = ThreadLocalRandom.current().nextInt(0, 1000); for (int i = 0; i < size; i++) { Instance instance = new Instance(); instance.setInstanceId(String.valueOf(i)); instance.setWeight(i); list.add(instance); } return list; } private List getOneInstanceNotZeroList() { List list = new ArrayList<>(); int size = ThreadLocalRandom.current().nextInt(0, 1000); int notZeroIndex = ThreadLocalRandom.current().nextInt(0, size - 1); for (int i = 0; i < size; i++) { Instance instance = new Instance(); instance.setInstanceId(String.valueOf(i)); if (i == notZeroIndex) { instance.setWeight(notZeroIndex + 1); } else { instance.setWeight(0); } list.add(instance); } return list; } private List getInstanceWeightAllZero() { List list = new ArrayList<>(); int size = ThreadLocalRandom.current().nextInt(0, 1000); for (int i = 0; i < size; i++) { Instance instance = new Instance(); instance.setInstanceId(String.valueOf(i)); instance.setWeight(0); list.add(instance); } return list; } private Instance getRandomInstance(List hosts) { List> hostsWithWeight = new ArrayList<>(); for (Instance host : hosts) { if (host.isHealthy()) { hostsWithWeight.add(new Pair<>(host, host.getWeight())); } } Chooser vipChooser = new Chooser<>("www.taobao.com", hostsWithWeight); return vipChooser.randomWithWeight(); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/utils/ConcurrentDiskUtilTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import com.alibaba.nacos.client.utils.ConcurrentDiskUtil; import org.junit.jupiter.api.Test; import java.io.File; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.StandardCharsets; 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.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class ConcurrentDiskUtilTest { @Test void testReadAndWrite() throws IOException { File tempFile = File.createTempFile("aaa", "bbb"); String fileName = tempFile.getAbsolutePath(); String content = "hello"; String charset = "UTF-8"; ConcurrentDiskUtil.writeFileContent(fileName, content, charset); String actualContent = ConcurrentDiskUtil.getFileContent(fileName, charset); assertEquals(content, actualContent); } @Test void testReadAndWrite2() throws IOException { File tempFile = File.createTempFile("aaa", "bbb"); String content = "hello"; String charset = "UTF-8"; ConcurrentDiskUtil.writeFileContent(tempFile, content, charset); String actualContent = ConcurrentDiskUtil.getFileContent(tempFile, charset); assertEquals(content, actualContent); } @Test void testByteBufferToString() throws IOException { String msg = "test buff to string"; ByteBuffer buff = ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8)); String actual = ConcurrentDiskUtil.byteBufferToString(buff, "UTF-8"); assertEquals(msg, actual); } @Test void testWriteFileContent() throws IOException { File file = mock(File.class); assertFalse(ConcurrentDiskUtil.writeFileContent(file, "hello", "UTF-8")); } @Test void testTryLockFailure() throws Throwable { assertThrows(IOException.class, () -> { Method method = ConcurrentDiskUtil.class.getDeclaredMethod("tryLock", File.class, FileChannel.class, boolean.class); method.setAccessible(true); File file = new File("non-exist"); FileChannel channel = mock(FileChannel.class); when(channel.tryLock(anyLong(), anyLong(), anyBoolean())).thenThrow(new RuntimeException()); try { method.invoke(null, file, channel, true); } catch (InvocationTargetException e) { throw e.getCause(); } }); } @Test void testTryLockFailureForIntercept() throws Throwable { assertThrows(IOException.class, () -> { Method method = ConcurrentDiskUtil.class.getDeclaredMethod("tryLock", File.class, FileChannel.class, boolean.class); method.setAccessible(true); File file = new File("non-exist"); FileChannel channel = mock(FileChannel.class); Thread.currentThread().interrupt(); when(channel.tryLock(anyLong(), anyLong(), anyBoolean())).thenThrow(new RuntimeException()); try { method.invoke(null, file, channel, true); } catch (InvocationTargetException e) { throw e.getCause(); } }); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/utils/GenericPollerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import org.junit.jupiter.api.Test; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; class GenericPollerTest { @Test void testNext() { String item1 = "item1"; String item2 = "item2"; GenericPoller poller = new GenericPoller<>(Arrays.asList(item1, item2)); assertEquals(item1, poller.next()); assertEquals(item2, poller.next()); assertEquals(item1, poller.next()); } @Test void testRefresh() { String item1 = "item1"; String item2 = "item2"; GenericPoller poller = new GenericPoller<>(Arrays.asList(item1, item2)); Poller poller1 = poller.refresh(Arrays.asList(item2)); assertEquals(item2, poller1.next()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/utils/InitUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.client.env.NacosClientProperties; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class InitUtilsTest { @AfterEach void tearDown() { System.clearProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING); System.clearProperty(SystemPropertyKeyConst.ANS_NAMESPACE); System.clearProperty(PropertyKeyConst.NAMESPACE); System.clearProperty(SystemPropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE); System.clearProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL); System.clearProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_PORT); UtilAndComs.webContext = "/nacos"; UtilAndComs.nacosUrlBase = "/nacos/v1/ns"; UtilAndComs.nacosUrlInstance = "/nacos/v1/ns/instance"; } /** * current namespace priority 1. system.Properties 2. user.Properties 3. default value */ @Test void testInitNamespaceForDefault() { //DEFAULT final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); String actual = InitUtils.initNamespaceForNaming(properties); assertEquals(UtilAndComs.DEFAULT_NAMESPACE_ID, actual); } @Test void testInitNamespaceFromAnsWithCloudParsing() { String expect = "ans"; System.setProperty(SystemPropertyKeyConst.ANS_NAMESPACE, expect); final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, "true"); String actual = InitUtils.initNamespaceForNaming(properties); assertEquals(expect, actual); } @Test void testInitNamespaceFromAliwareWithCloudParsing() { String expect = "aliware"; System.setProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, "true"); final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE, expect); String actual = InitUtils.initNamespaceForNaming(properties); assertEquals(expect, actual); } @Test void testInitNamespaceFromJvmNamespaceWithCloudParsing() { String expect = "jvm_namespace"; System.setProperty(PropertyKeyConst.NAMESPACE, expect); final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); String ns = InitUtils.initNamespaceForNaming(properties); assertEquals(expect, ns); } @Test void testInitNamespaceFromPropNamespaceWithCloudParsing() { final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); String expect = "ns1"; properties.setProperty(PropertyKeyConst.NAMESPACE, expect); String ns = InitUtils.initNamespaceForNaming(properties); assertEquals(expect, ns); } @Test void testInitNamespaceFromDefaultNamespaceWithCloudParsing() { final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, "true"); String actual = InitUtils.initNamespaceForNaming(properties); assertEquals(UtilAndComs.DEFAULT_NAMESPACE_ID, actual); } @Test void testInitNamespaceFromJvmNamespaceWithoutCloudParsing() { System.setProperty(SystemPropertyKeyConst.ANS_NAMESPACE, "ans"); String expect = "jvm_namespace"; System.setProperty(PropertyKeyConst.NAMESPACE, expect); final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, "false"); String ns = InitUtils.initNamespaceForNaming(properties); assertEquals(expect, ns); } @Test void testInitNamespaceFromPropNamespaceWithoutCloudParsing() { System.setProperty(SystemPropertyKeyConst.ANS_NAMESPACE, "ans"); System.setProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, "false"); final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); String expect = "ns1"; properties.setProperty(PropertyKeyConst.NAMESPACE, expect); String ns = InitUtils.initNamespaceForNaming(properties); assertEquals(expect, ns); } @Test void testInitNamespaceFromDefaultNamespaceWithoutCloudParsing() { System.setProperty(SystemPropertyKeyConst.ANS_NAMESPACE, "ans"); final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, "false"); String actual = InitUtils.initNamespaceForNaming(properties); assertEquals(UtilAndComs.DEFAULT_NAMESPACE_ID, actual); } @Test void testInitWebRootContext() { String ctx = "/aaa"; final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.CONTEXT_PATH, ctx); InitUtils.initWebRootContext(properties); assertEquals(ctx, UtilAndComs.webContext); assertEquals(ctx + "/v1/ns", UtilAndComs.nacosUrlBase); assertEquals(ctx + "/v1/ns/instance", UtilAndComs.nacosUrlInstance); } @Test void testInitWebRootContextWithoutValue() { final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); InitUtils.initWebRootContext(properties); assertEquals("/nacos", UtilAndComs.webContext); assertEquals("/nacos/v1/ns", UtilAndComs.nacosUrlBase); assertEquals("/nacos/v1/ns/instance", UtilAndComs.nacosUrlInstance); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/naming/utils/PairTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.naming.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PairTest { @Test void testItem() { String item = "aa"; double weight = 1.0; Pair pair = new Pair<>(item, weight); assertEquals(weight, pair.weight(), 0.01); assertEquals(item, pair.item()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/redo/data/RedoDataTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.redo.data; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class RedoDataTest { @Test void testGetRedoType() { RedoData redoData = new RedoData() { }; // registered=true, unregistering=false, expectedRegistered=true, means data has been registered and match expected. redoData.setRegistered(true); redoData.setExpectedRegistered(true); redoData.setUnregistering(false); assertEquals(RedoData.RedoType.NONE, redoData.getRedoType()); // registered=true, unregistering=false, expectedRegistered=true, means data has been registered but not match expected. redoData.setRegistered(true); redoData.setExpectedRegistered(false); redoData.setUnregistering(false); assertEquals(RedoData.RedoType.UNREGISTER, redoData.getRedoType()); // registered=true, unregistering=true, expectedRegistered=false, means data has been registered and doing unregister. redoData.setRegistered(true); redoData.setExpectedRegistered(false); redoData.setUnregistering(true); assertEquals(RedoData.RedoType.UNREGISTER, redoData.getRedoType()); // registered=false, unregistering=false, expectedRegistered=true, means data has been not registered and doing register. redoData.setRegistered(false); redoData.setExpectedRegistered(true); redoData.setUnregistering(false); assertEquals(RedoData.RedoType.REGISTER, redoData.getRedoType()); // registered=false, unregistering=true, expectedRegistered=false, means data has been unregistered and match expected. redoData.setRegistered(false); redoData.setExpectedRegistered(false); redoData.setUnregistering(true); assertEquals(RedoData.RedoType.REMOVE, redoData.getRedoType()); // registered=false, unregistering=true, expectedRegistered=true, means data has been unregistered but not match expected. redoData.setRegistered(false); redoData.setExpectedRegistered(true); redoData.setUnregistering(true); assertEquals(RedoData.RedoType.REGISTER, redoData.getRedoType()); } @Test void testEqualsAndHashCode() { MockRedoData redoData = new MockRedoData(); redoData.set("String"); assertEquals(redoData, redoData); assertNotEquals(redoData, null); assertNotEquals(redoData, new RedoData() { }); MockRedoData redoData2 = new MockRedoData(); redoData2.set("aaa"); assertNotEquals(redoData, redoData2); redoData2.set("String"); assertEquals(redoData, redoData2); assertEquals(redoData.hashCode(), redoData2.hashCode()); redoData2.setRegistered(true); assertNotEquals(redoData, redoData2); redoData2.setRegistered(redoData.isRegistered()); redoData2.setUnregistering(!redoData.isUnregistering()); assertNotEquals(redoData, redoData2); assertEquals("String", redoData.get()); } private static class MockRedoData extends RedoData { } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/redo/service/AbstractRedoServiceTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.redo.service; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.redo.data.RedoData; 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.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Properties; import java.util.Set; 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.assertTrue; @ExtendWith(MockitoExtension.class) class AbstractRedoServiceTest { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRedoServiceTest.class); @Mock private AbstractRedoTask redoTask; MockRedoService redoService; @BeforeEach void setUp() { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.REDO_DELAY_TIME, "300"); NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); redoService = new MockRedoService(LOGGER, clientProperties); redoService.startRedoTask(); } @AfterEach void tearDown() { redoService.shutdown(); } @Test void testRemoveForNonExistedData() { assertDoesNotThrow(() -> redoService.removeRedoData("test", String.class)); } @Test void testMarkRegisteredForNonExistedData() { assertDoesNotThrow(() -> redoService.dataRegistered("test", String.class)); } @Test void testMarkUnregisterForNonExistedData() { assertDoesNotThrow(() -> redoService.dataDeregister("test", String.class)); } @Test void testMarkUnregisteredForNonExistedData() { assertDoesNotThrow(() -> redoService.dataDeregistered("test", String.class)); } @Test void testIsDataRegisteredForNonExistedData() { assertFalse(redoService.isDataRegistered("test", String.class)); } @Test void testGetNonExistedData() { assertNull(redoService.getRedoData("test", String.class)); } @Test void testFindNonExistedData() { assertTrue(redoService.findRedoData(String.class).isEmpty()); } @Test void testCacheRedoDataAndMarkRegistered() { MockRedoData redoData = new MockRedoData(); redoData.set("test"); redoService.cachedRedoData("test", redoData, String.class); RedoData redoData1 = redoService.getRedoData("test", String.class); assertEquals(redoData, redoData1); assertFalse(redoData1.isRegistered()); assertTrue(redoData1.isNeedRedo()); assertFalse(redoData1.isUnregistering()); assertFalse(redoService.isDataRegistered("test", String.class)); redoService.dataRegistered("test", String.class); assertTrue(redoData1.isRegistered()); assertFalse(redoData1.isNeedRedo()); assertFalse(redoData1.isUnregistering()); assertTrue(redoService.isDataRegistered("test", String.class)); redoService.dataDeregister("test", String.class); assertTrue(redoData1.isRegistered()); assertTrue(redoData1.isUnregistering()); assertTrue(redoService.isDataRegistered("test", String.class)); redoService.dataDeregistered("test", String.class); assertFalse(redoData1.isRegistered()); assertTrue(redoData1.isUnregistering()); assertFalse(redoService.isDataRegistered("test", String.class)); redoService.removeRedoData("test", String.class); assertNull(redoService.getRedoData("test", String.class)); } @Test void testRemoveExpectedRegisteredData() { MockRedoData redoData = new MockRedoData(); redoData.set("test"); redoService.cachedRedoData("test", redoData, String.class); redoData.setExpectedRegistered(true); redoService.removeRedoData("test", String.class); assertNotNull(redoService.getRedoData("test", String.class)); } @Test void testOnConnectedAndOnDisconnected() { assertFalse(redoService.isConnected()); redoService.onConnected(null); assertTrue(redoService.isConnected()); MockRedoData redoData = new MockRedoData(); redoData.set("test"); redoData.setRegistered(true); redoService.cachedRedoData("test", redoData, String.class); redoService.onDisConnect(null); assertFalse(redoService.isConnected()); assertFalse(redoData.isRegistered()); } @Test void testFindAllNeedRedoData() { MockRedoData noNeedRedoData = new MockRedoData(); noNeedRedoData.setRegistered(true); noNeedRedoData.setExpectedRegistered(true); MockRedoData needRedoData = new MockRedoData(); needRedoData.setRegistered(false); needRedoData.setExpectedRegistered(true); redoService.cachedRedoData("noNeedRedoData", noNeedRedoData, String.class); redoService.cachedRedoData("needRedoData", needRedoData, String.class); Set> redoDataSet = redoService.findRedoData(String.class); assertEquals(1, redoDataSet.size()); assertEquals(needRedoData, redoDataSet.iterator().next()); } private class MockRedoService extends AbstractRedoService { protected MockRedoService(Logger logger, NacosClientProperties properties) { super(logger, properties, "test"); } @Override protected AbstractRedoTask buildRedoTask() { return redoTask; } } private static class MockRedoData extends RedoData { } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/redo/service/AbstractRedoTaskTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.redo.service; 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.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.atomic.AtomicBoolean; 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.Mockito.when; @ExtendWith(MockitoExtension.class) class AbstractRedoTaskTest { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRedoTaskTest.class); @Mock private AbstractRedoService redoService; private AtomicBoolean invokedMark; private AtomicBoolean isThrowException; AbstractRedoTask redoTask; @BeforeEach void setUp() { invokedMark = new AtomicBoolean(false); isThrowException = new AtomicBoolean(false); redoTask = new AbstractRedoTask(LOGGER, redoService) { @Override protected void redoData() { invokedMark.set(true); if (isThrowException.get()) { throw new RuntimeException("test"); } } }; } @Test void testGetRedoService() { assertEquals(redoService, redoTask.getRedoService()); } @Test void runWithDisconnection() { redoTask.run(); assertFalse(invokedMark.get()); } @Test void runWithConnection() { when(redoService.isConnected()).thenReturn(true); redoTask.run(); assertTrue(invokedMark.get()); } @Test void runWithConnectionAndException() { isThrowException.set(true); when(redoService.isConnected()).thenReturn(true); redoTask.run(); assertTrue(invokedMark.get()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/security/SecurityProxyTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.security; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.address.AbstractServerListManager; import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService; import com.alibaba.nacos.plugin.auth.spi.client.ClientAuthPluginManager; import com.alibaba.nacos.plugin.auth.spi.client.ClientAuthService; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; 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.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove strictness lenient @MockitoSettings(strictness = Strictness.LENIENT) class SecurityProxyTest { private SecurityProxy securityProxy; @Mock private NacosRestTemplate nacosRestTemplate; @BeforeEach void setUp() throws Exception { //given HttpRestResult result = new HttpRestResult<>(); result.setData("{\"accessToken\":\"ttttttttttttttttt\",\"tokenTtl\":1000}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(new Properties()); AbstractServerListManager serverListManager = new AbstractServerListManager(properties) { @Override protected String getModuleName() { return "Test"; } @Override protected NacosRestTemplate getNacosRestTemplate() { return nacosRestTemplate; } @Override public String genNextServer() { return serverList.get(0); } @Override public String getCurrentServer() { return serverList.get(0); } @Override public List getServerList() { return serverList; } }; securityProxy = new SecurityProxy(serverListManager, nacosRestTemplate); } @Test void testLoginClientAuthService() throws Exception { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); securityProxy.login(properties); verify(nacosRestTemplate).postForm(any(), (Header) any(), any(), any(), any()); } @Test void testGetIdentityContext() { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); securityProxy.login(properties); //when Map keyMap = securityProxy.getIdentityContext(null); //then assertEquals("ttttttttttttttttt", keyMap.get(NacosAuthLoginConstant.ACCESSTOKEN)); } @Test void testLoginWithoutAnyPlugin() throws NoSuchFieldException, IllegalAccessException { Field clientAuthPluginManagerField = SecurityProxy.class.getDeclaredField("clientAuthPluginManager"); clientAuthPluginManagerField.setAccessible(true); ClientAuthPluginManager clientAuthPluginManager = mock(ClientAuthPluginManager.class); clientAuthPluginManagerField.set(securityProxy, clientAuthPluginManager); when(clientAuthPluginManager.getAuthServiceSpiImplSet()).thenReturn(Collections.emptySet()); securityProxy.login(new Properties()); Map header = securityProxy.getIdentityContext(new RequestResource()); assertTrue(header.isEmpty()); } @Test void testReLogin() throws NoSuchFieldException, IllegalAccessException { Field clientAuthPluginManagerField = SecurityProxy.class.getDeclaredField("clientAuthPluginManager"); clientAuthPluginManagerField.setAccessible(true); ClientAuthPluginManager clientAuthPluginManager = mock(ClientAuthPluginManager.class); clientAuthPluginManagerField.set(securityProxy, clientAuthPluginManager); when(clientAuthPluginManager.getAuthServiceSpiImplSet()).thenReturn( Collections.singleton(new AbstractClientAuthService() { private LoginIdentityContext loginIdentityContext; @Override public Boolean login(Properties properties) { return null; } @Override public LoginIdentityContext getLoginIdentityContext(RequestResource resource) { if (loginIdentityContext == null) { loginIdentityContext = new LoginIdentityContext(); } return loginIdentityContext; } @Override public void shutdown() throws NacosException { } })); securityProxy.reLogin(); Map identityContext = securityProxy.getIdentityContext(new RequestResource()); assertEquals(identityContext.get(NacosAuthLoginConstant.RELOGINFLAG), "true"); } @Test void testReLoginWithEmptyPlugin() throws NoSuchFieldException, IllegalAccessException { Field clientAuthPluginManagerField = SecurityProxy.class.getDeclaredField("clientAuthPluginManager"); clientAuthPluginManagerField.setAccessible(true); ClientAuthPluginManager clientAuthPluginManager = mock(ClientAuthPluginManager.class); clientAuthPluginManagerField.set(securityProxy, clientAuthPluginManager); when(clientAuthPluginManager.getAuthServiceSpiImplSet()).thenReturn(Collections.emptySet()); securityProxy.reLogin(); Map identityContext = securityProxy.getIdentityContext(new RequestResource()); assertFalse(identityContext.containsKey(NacosAuthLoginConstant.RELOGINFLAG)); } @Test void testReLoginWithException() throws NoSuchFieldException, IllegalAccessException { Field clientAuthPluginManagerField = SecurityProxy.class.getDeclaredField("clientAuthPluginManager"); clientAuthPluginManagerField.setAccessible(true); ClientAuthPluginManager clientAuthPluginManager = mock(ClientAuthPluginManager.class); clientAuthPluginManagerField.set(securityProxy, clientAuthPluginManager); ClientAuthService mockClientAuthService = mock(ClientAuthService.class); when(mockClientAuthService.getLoginIdentityContext(any())).thenThrow(new RuntimeException("test")); when(clientAuthPluginManager.getAuthServiceSpiImplSet()).thenReturn(Collections.singleton(mockClientAuthService)); assertDoesNotThrow(() -> securityProxy.reLogin()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/selector/AbstractSelectorWrapperTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.selector; import com.alibaba.nacos.api.naming.listener.EventListener; import com.alibaba.nacos.api.naming.listener.NamingEvent; import com.alibaba.nacos.api.naming.selector.NamingSelector; import com.alibaba.nacos.client.naming.event.InstancesChangeEvent; import com.alibaba.nacos.client.naming.selector.NamingListenerInvoker; import com.alibaba.nacos.client.naming.selector.NamingSelectorWrapper; 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.junit.jupiter.MockitoExtension; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class AbstractSelectorWrapperTest { private MockSelectorWrapper selectorWrapper; @Mock NamingSelector namingSelector; @Mock EventListener eventListener; @BeforeEach void setUp() { selectorWrapper = new MockSelectorWrapper(namingSelector, eventListener, true, true); } @AfterEach void tearDown() { } @Test void notifyListenerWithSelectableFalse() { selectorWrapper = new MockSelectorWrapper(namingSelector, eventListener, false, true); selectorWrapper.notifyListener(new InstancesChangeEvent("test", "test", "test", "test", null, null)); verify(eventListener, never()).onEvent(any()); } @Test void notifyListenerWithCallableFalse() { selectorWrapper = new MockSelectorWrapper(namingSelector, eventListener, true, false); selectorWrapper.notifyListener(new InstancesChangeEvent("test", "test", "test", "test", null, null)); verify(eventListener, never()).onEvent(any()); } @Test void notifyListener() { selectorWrapper.notifyListener(new InstancesChangeEvent("test", "test", "test", "test", null, null)); verify(eventListener).onEvent(any()); } @Test void notifyIfListenerIfNotNotifiedWithSelectableFalse() { selectorWrapper = new MockSelectorWrapper(namingSelector, eventListener, false, true); selectorWrapper.notifyIfListenerIfNotNotified( new InstancesChangeEvent("test", "test", "test", "test", null, null)); verify(eventListener, never()).onEvent(any()); } @Test void notifyIfListenerIfNotNotified() { selectorWrapper.notifyIfListenerIfNotNotified( new InstancesChangeEvent("test", "test", "test", "test", null, null)); verify(eventListener).onEvent(any()); } @Test void notifyIfListenerIfNotNotifiedTwice() { selectorWrapper.notifyIfListenerIfNotNotified( new InstancesChangeEvent("test", "test", "test", "test", null, null)); verify(eventListener).onEvent(any()); reset(eventListener); selectorWrapper.notifyIfListenerIfNotNotified( new InstancesChangeEvent("test", "test", "test", "test", null, null)); verify(eventListener, never()).onEvent(any()); } @Test void testGet() { assertEquals(namingSelector, selectorWrapper.getSelector()); assertNotEquals(eventListener, selectorWrapper.getListener()); assertEquals(new NamingListenerInvoker(eventListener), selectorWrapper.getListener()); } @Test void testEquals() { assertEquals(selectorWrapper, selectorWrapper); assertNotEquals(null, selectorWrapper); assertNotEquals(new NamingSelectorWrapper(namingSelector, eventListener), selectorWrapper); MockSelectorWrapper newSelectorWrapper = new MockSelectorWrapper(namingSelector, eventListener, true, true); assertEquals(newSelectorWrapper, selectorWrapper); } private class MockSelectorWrapper extends AbstractSelectorWrapper { private final boolean selectable; private final boolean callable; private MockSelectorWrapper(NamingSelector selector, EventListener listener, boolean selectable, boolean callable) { super(selector, new NamingListenerInvoker(listener)); this.selectable = selectable; this.callable = callable; } @Override protected boolean isSelectable(InstancesChangeEvent event) { return selectable; } @Override protected boolean isCallable(NamingEvent event) { return callable; } @Override protected NamingEvent buildListenerEvent(InstancesChangeEvent event) { return new NamingEvent(event.getServiceName(), event.getGroupName(), event.getClusters(), Collections.emptyList()); } } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/selector/SelectorManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.selector; import com.alibaba.nacos.client.naming.selector.NamingSelectorWrapper; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.mock; public class SelectorManagerTest { @Test public void testCurd() { SelectorManager selectorManager = new SelectorManager<>(); String subId = "subId"; NamingSelectorWrapper sw = mock(NamingSelectorWrapper.class); selectorManager.addSelectorWrapper(subId, sw); assertTrue(selectorManager.getSelectorWrappers(subId).contains(sw)); selectorManager.removeSelectorWrapper(subId, sw); assertTrue(selectorManager.getSelectorWrappers(subId).isEmpty()); } @Test public void testSubInfo() { SelectorManager selectorManager = new SelectorManager<>(); List list = new ArrayList<>(); for (int i = 0; i < 64; i++) { list.add(generateRandomString(2, 32)); } for (String subId : list) { selectorManager.addSelectorWrapper(subId, mock(NamingSelectorWrapper.class)); assertTrue(selectorManager.isSubscribed(subId)); } Set subsSet = selectorManager.getSubscriptions(); for (String subId : subsSet) { assertTrue(list.contains(subId)); } for (String subId : list) { selectorManager.removeSubscription(subId); assertFalse(selectorManager.isSubscribed(subId)); } } private static String generateRandomString(int minLength, int maxLength) { String characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); int length = random.nextInt(maxLength - minLength + 1) + minLength; StringBuilder sb = new StringBuilder(); for (int i = 0; i < length; i++) { int index = random.nextInt(characters.length()); char randomChar = characters.charAt(index); sb.append(randomChar); } return sb.toString(); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/utils/EnvUtilTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.common.Constants; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class EnvUtilTest { @Test void testSetSelfEnv() { Map> headers = new HashMap<>(); headers.put(Constants.AMORY_TAG, Arrays.asList("a", "1")); headers.put(Constants.VIPSERVER_TAG, Arrays.asList("b", "2")); headers.put(Constants.LOCATION_TAG, Arrays.asList("c", "3")); EnvUtil.setSelfEnv(headers); assertEquals("a,1", EnvUtil.getSelfAmoryTag()); assertEquals("b,2", EnvUtil.getSelfVipserverTag()); assertEquals("c,3", EnvUtil.getSelfLocationTag()); // reset by empty list headers.put(Constants.AMORY_TAG, Collections.emptyList()); headers.put(Constants.VIPSERVER_TAG, Collections.emptyList()); headers.put(Constants.LOCATION_TAG, Collections.emptyList()); EnvUtil.setSelfEnv(headers); assertNull(EnvUtil.getSelfAmoryTag()); assertNull(EnvUtil.getSelfVipserverTag()); assertNull(EnvUtil.getSelfLocationTag()); } @Test void testSetSelfEnv2() { Map> headers = new HashMap<>(); headers.put(Constants.AMORY_TAG, Arrays.asList("a", "1")); headers.put(Constants.VIPSERVER_TAG, Arrays.asList("b", "2")); headers.put(Constants.LOCATION_TAG, Arrays.asList("c", "3")); EnvUtil.setSelfEnv(headers); assertEquals("a,1", EnvUtil.getSelfAmoryTag()); assertEquals("b,2", EnvUtil.getSelfVipserverTag()); assertEquals("c,3", EnvUtil.getSelfLocationTag()); // reset headers.put(Constants.AMORY_TAG, null); headers.put(Constants.VIPSERVER_TAG, null); headers.put(Constants.LOCATION_TAG, null); EnvUtil.setSelfEnv(headers); assertNull(EnvUtil.getSelfAmoryTag()); assertNull(EnvUtil.getSelfVipserverTag()); assertNull(EnvUtil.getSelfLocationTag()); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/utils/LogUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import static org.junit.jupiter.api.Assertions.assertNotNull; class LogUtilsTest { @Test void testLogger() { Logger logger = LogUtils.logger(LogUtilsTest.class); assertNotNull(logger); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/utils/ParamUtilTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.common.utils.MD5Utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ParamUtilTest { private int defaultConnectTimeout; private int defaultReadTimeout; private double defaultPerTaskConfigSize; @BeforeEach void before() { defaultConnectTimeout = 1000; defaultReadTimeout = 3000; defaultPerTaskConfigSize = 3000.0; } @AfterEach void after() { ParamUtil.setConnectTimeout(defaultConnectTimeout); ParamUtil.setReadTimeout(defaultReadTimeout); ParamUtil.setPerTaskConfigSize(defaultPerTaskConfigSize); System.clearProperty("NACOS.CONNECT.TIMEOUT"); System.clearProperty("NACOS_READ_TIMEOUT"); System.clearProperty("PER_TASK_CONFIG_SIZE"); System.clearProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL); } @Test void testSetConnectTimeout() { int defaultVal = ParamUtil.getConnectTimeout(); assertEquals(defaultConnectTimeout, defaultVal); int expect = 50; ParamUtil.setConnectTimeout(expect); assertEquals(expect, ParamUtil.getConnectTimeout()); } @Test void testSetReadTimeout() { int defaultVal = ParamUtil.getReadTimeout(); assertEquals(defaultReadTimeout, defaultVal); int expect = 3000; ParamUtil.setReadTimeout(expect); assertEquals(expect, ParamUtil.getReadTimeout()); } @Test void testGetPerTaskConfigSize() { double defaultVal = ParamUtil.getPerTaskConfigSize(); assertEquals(defaultPerTaskConfigSize, defaultVal, 0.01); double expect = 50.0; ParamUtil.setPerTaskConfigSize(expect); assertEquals(expect, ParamUtil.getPerTaskConfigSize(), 0.01); } @Test void testInitConnectionTimeoutWithException() throws Throwable { assertThrows(IllegalArgumentException.class, () -> { Method method = ParamUtil.class.getDeclaredMethod("initConnectionTimeout"); method.setAccessible(true); System.setProperty("NACOS.CONNECT.TIMEOUT", "test"); try { method.invoke(null); } catch (InvocationTargetException e) { throw e.getCause(); } }); } @Test void testInitReadTimeoutWithException() throws Throwable { assertThrows(IllegalArgumentException.class, () -> { Method method = ParamUtil.class.getDeclaredMethod("initReadTimeout"); method.setAccessible(true); System.setProperty("NACOS.READ.TIMEOUT", "test"); try { method.invoke(null); } catch (InvocationTargetException e) { throw e.getCause(); } }); } @Test void testInitPerTaskConfigSizeWithException() throws Throwable { assertThrows(IllegalArgumentException.class, () -> { Method method = ParamUtil.class.getDeclaredMethod("initPerTaskConfigSize"); method.setAccessible(true); System.setProperty("PER_TASK_CONFIG_SIZE", "test"); try { method.invoke(null); } catch (InvocationTargetException e) { throw e.getCause(); } }); } @Test void testSimplyEnvNameIfOverLimit() { StringBuilder envNameOverLimitBuilder = new StringBuilder("test"); for (int i = 0; i < 50; i++) { envNameOverLimitBuilder.append(i); } String envName = envNameOverLimitBuilder.toString(); String actual = ParamUtil.simplyEnvNameIfOverLimit(envName); String expect = envName.substring(0, 50) + MD5Utils.md5Hex(envName, "UTF-8"); assertEquals(expect, actual); } @Test void testSimplyEnvNameNotOverLimit() { String expect = "test"; assertEquals(expect, ParamUtil.simplyEnvNameIfOverLimit(expect)); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/utils/PreInitUtilsTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; class PreInitUtilsTest { @Test void testAsyncPreLoadCostComponent() throws InterruptedException { // There is no things need to be assert. // The method will called when nacos-client init to async to load some components to reduce the sync load time. PreInitUtils.asyncPreLoadCostComponent(); // No exception is ok. // Let async thread run completed TimeUnit.SECONDS.sleep(2); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/utils/StringUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.common.utils.StringUtils; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collection; import static com.alibaba.nacos.common.utils.StringUtils.defaultIfEmpty; import static com.alibaba.nacos.common.utils.StringUtils.isNotBlank; import static com.alibaba.nacos.common.utils.StringUtils.isNotEmpty; import static com.alibaba.nacos.common.utils.StringUtils.join; import static com.alibaba.nacos.common.utils.StringUtils.substringBetween; 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 testisNotBlank() { assertTrue(isNotBlank("foo")); assertFalse(isNotBlank(" ")); assertFalse(isNotBlank(null)); } @Test void testIsNotEmpty() { assertFalse(isNotEmpty("")); assertTrue(isNotEmpty("foo")); } @Test void testDefaultIfEmpty() { assertEquals("foo", defaultIfEmpty("", "foo")); assertEquals("bar", defaultIfEmpty("bar", "foo")); } @Test void testEquals() { assertTrue(StringUtils.equals("foo", "foo")); assertFalse(StringUtils.equals("bar", "foo")); assertFalse(StringUtils.equals(" ", "foo")); assertFalse(StringUtils.equals("foo", null)); } @Test void testSubstringBetween() { assertNull(substringBetween(null, null, null)); assertNull(substringBetween("", "foo", "")); assertNull(substringBetween("foo", "bar", "baz")); assertEquals("", substringBetween("foo", "foo", "")); } @Test void testJoin() { assertNull(join(null, "")); Collection collection = new ArrayList(); collection.add("foo"); collection.add("bar"); assertEquals("foo,bar", join(collection, ",")); } @Test void testUuidPattern() { // match 8-4-4-4-12 uuid pattern assertTrue(StringUtils.isUuidString("123e4567-e89b-12d3-a456-426655440000")); // not match 8-4-4-4-12 uuid pattern assertFalse(StringUtils.isUuidString("123e54567-e89b5-12d35-a4565-426655440000")); // not match hexadecimal and '-' char assertFalse(StringUtils.isUuidString("@23e4567+e89b-12d3-a456-426655440000")); } } ================================================ FILE: client/src/test/java/com/alibaba/nacos/client/utils/ValidatorUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.client.env.NacosClientProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertThrows; class ValidatorUtilsTest { @Test void testContextPathLegal() { String contextPath1 = "/nacos"; ValidatorUtils.checkContextPath(contextPath1); String contextPath2 = "nacos"; ValidatorUtils.checkContextPath(contextPath2); String contextPath3 = "/"; ValidatorUtils.checkContextPath(contextPath3); String contextPath4 = ""; ValidatorUtils.checkContextPath(contextPath4); // allow null ValidatorUtils.checkContextPath(null); } @Test void testContextPathIllegal1() { assertThrows(IllegalArgumentException.class, () -> { String contextPath1 = "//nacos/"; ValidatorUtils.checkContextPath(contextPath1); }); } @Test void testContextPathIllegal2() { assertThrows(IllegalArgumentException.class, () -> { String contextPath2 = "/nacos//"; ValidatorUtils.checkContextPath(contextPath2); }); } @Test void testContextPathIllegal3() { assertThrows(IllegalArgumentException.class, () -> { String contextPath3 = "///"; ValidatorUtils.checkContextPath(contextPath3); }); } @Test void testContextPathIllegal4() { assertThrows(IllegalArgumentException.class, () -> { String contextPath4 = "//"; ValidatorUtils.checkContextPath(contextPath4); }); } @Test void testCheckInitParam() { Assertions.assertDoesNotThrow(() -> { Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.CONTEXT_PATH, "test"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); ValidatorUtils.checkInitParam(nacosClientProperties); }); } } ================================================ FILE: client/src/test/resources/disk_cache_test/error%40%40json%40%40file ================================================ {error_json} ================================================ FILE: client/src/test/resources/disk_cache_test/ignored%40%40not_json%40%40file ================================================ ignored file ================================================ FILE: client/src/test/resources/disk_cache_test/invalid_dir/invalid_file ================================================ ================================================ FILE: client/src/test/resources/disk_cache_test/legal%40%40no_name%40%40file ================================================ {"ip":"1.1.1.1","port":1234,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"file","serviceName":"no_name","metadata":{"chinese":"中文"},"instanceHeartBeatInterval":5000,"instanceHeartBeatTimeOut":15000,"ipDeleteTimeout":30000} ================================================ FILE: client/src/test/resources/disk_cache_test/legal%40%40with_name%40%40file ================================================ {"name":"legal@@with_name","clusters":"file","cacheMillis":1000,"hosts":[{"ip":"1.1.1.1","port":1234,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"file","serviceName":"with_name","metadata":{"chinese":"中文"},"instanceHeartBeatInterval":5000,"instanceHeartBeatTimeOut":15000,"ipDeleteTimeout":30000}],"lastRefTime":0,"checksum":"","allIPs":false,"reachProtectionThreshold":false,"valid":true} ================================================ FILE: client/src/test/resources/failover_test/disabled/00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 ================================================ 0 ================================================ FILE: client/src/test/resources/failover_test/enabled/00-00---000-VIPSRV_FAILOVER_SWITCH-000---00-00 ================================================ 1 ================================================ FILE: client/src/test/resources/failover_test/enabled/invalid_dir/invalid_file ================================================ ================================================ FILE: client/src/test/resources/failover_test/enabled/legal%40%40with_name%40%40file ================================================ {"name":"legal@@with_name","clusters":"file","cacheMillis":1000,"hosts":[{"ip":"1.1.1.1","port":1234,"weight":1.0,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"file","serviceName":"with_name","metadata":{"chinese":"中文"},"instanceHeartBeatInterval":5000,"instanceHeartBeatTimeOut":15000,"ipDeleteTimeout":30000}],"lastRefTime":0,"checksum":"","allIPs":false,"reachProtectionThreshold":false,"valid":true} ================================================ FILE: client-basic/pom.xml ================================================ 4.0.0 com.alibaba.nacos nacos-all ${revision} ../pom.xml nacos-client-basic jar nacos-client-basic ${project.version} https://nacos.io Nacos client basic pom.xml file ${client.java.version} ${client.java.version} ${project.groupId} nacos-api true com.alibaba.nacos nacos-common true org.slf4j slf4j-api true com.alibaba.nacos nacos-auth-plugin ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/address/AbstractServerListManager.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.JustForTest; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; /** * Server list Manager. * * @author totalo */ public abstract class AbstractServerListManager implements ServerListFactory, Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractServerListManager.class); protected ServerListProvider serverListProvider; protected NacosClientProperties properties; public AbstractServerListManager(NacosClientProperties properties) { this(properties, null); } public AbstractServerListManager(NacosClientProperties properties, String namespace) { // To avoid set operation affect the original properties. NacosClientProperties tmpProperties = properties.derive(); if (StringUtils.isNotBlank(namespace)) { tmpProperties.setProperty(PropertyKeyConst.NAMESPACE, namespace); } tmpProperties.setProperty(Constants.CLIENT_MODULE_TYPE, getModuleName()); this.properties = tmpProperties; } @Override public List getServerList() { return serverListProvider.getServerList(); } @Override public void shutdown() throws NacosException { String className = this.getClass().getName(); LOGGER.info("{} do shutdown begin", className); if (null != serverListProvider) { serverListProvider.shutdown(); } serverListProvider = null; LOGGER.info("{} do shutdown stop", className); } /** * Start server list manager. * * @throws NacosException during start and initialize. */ public void start() throws NacosException { Collection serverListProviders = NacosServiceLoader.load(ServerListProvider.class); Collection sorted = serverListProviders.stream() .sorted((a, b) -> b.getOrder() - a.getOrder()).collect(Collectors.toList()); for (ServerListProvider each : sorted) { boolean matchResult = each.match(properties); LOGGER.info("Load and match ServerListProvider {}, match result: {}", each.getClass().getCanonicalName(), matchResult); if (matchResult) { this.serverListProvider = each; LOGGER.info("Will use {} as ServerListProvider", this.serverListProvider.getClass().getCanonicalName()); break; } } if (null == serverListProvider) { LOGGER.error("No server list provider found, SPI load size: {}", sorted.size()); throw new NacosException(NacosException.CLIENT_INVALID_PARAM, "No server list provider found."); } this.serverListProvider.init(properties, getNacosRestTemplate()); } public String getServerName() { return getModuleName() + "-" + serverListProvider.getServerName(); } public String getContextPath() { return serverListProvider.getContextPath(); } public String getNamespace() { return serverListProvider.getNamespace(); } public String getAddressSource() { return serverListProvider.getAddressSource(); } public boolean isFixed() { return serverListProvider.isFixed(); } /** * get module name. * * @return module name */ protected abstract String getModuleName(); /** * get nacos rest template. * * @return nacos rest template */ protected abstract NacosRestTemplate getNacosRestTemplate(); @JustForTest NacosClientProperties getProperties() { return properties; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/address/AbstractServerListProvider.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.StringUtils; import java.util.List; /** * Address server list provider. * * @author totalo */ public abstract class AbstractServerListProvider implements ServerListProvider { protected String contextPath = ClientBasicParamUtil.getDefaultContextPath(); protected String namespace = ""; @Override public void init(final NacosClientProperties properties, final NacosRestTemplate nacosRestTemplate) throws NacosException { if (null == properties) { throw new NacosException(NacosException.INVALID_PARAM, "properties is null"); } initContextPath(properties); initNameSpace(properties); } /** * Get server list. * @return server list */ @Override public abstract List getServerList(); /** * Get server name. * @return server name */ @Override public abstract String getServerName(); /** * Get order. * @return order */ @Override public abstract int getOrder(); public String getContextPath() { return contextPath; } public String getNamespace() { return namespace; } private void initContextPath(NacosClientProperties properties) { String contentPathTmp = properties.getProperty(PropertyKeyConst.CONTEXT_PATH); if (!StringUtils.isBlank(contentPathTmp)) { this.contextPath = contentPathTmp; } } private void initNameSpace(NacosClientProperties properties) { String namespace = properties.getProperty(PropertyKeyConst.NAMESPACE); if (StringUtils.isNotBlank(namespace)) { this.namespace = namespace; } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/address/EndpointServerListProvider.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.constant.Constants.Address; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.client.utils.ContextPathUtil; import com.alibaba.nacos.client.utils.TemplateUtils; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Endpoint server list provider. * * @author totalo */ public class EndpointServerListProvider extends AbstractServerListProvider { private static final Logger LOGGER = LoggerFactory.getLogger(EndpointServerListProvider.class); private static final boolean USE_ENDPOINT_PARSING_RULE_DEFAULT_VALUE = true; private NacosRestTemplate nacosRestTemplate; private static final String CUSTOM_NAME = "custom"; private final long refreshServerListInternal = TimeUnit.SECONDS.toMillis(30); private final int initServerListRetryTimes = 5; private long lastServerListRefreshTime = 0L; private ScheduledExecutorService refreshServerListExecutor; private String endpoint; private int endpointPort = 8080; private String endpointContextPath; private String serverListName = ClientBasicParamUtil.getDefaultNodesPath(); private volatile List serversFromEndpoint = new ArrayList<>(); private String addressServerUrl; private String moduleName = "default"; @Override public void init(final NacosClientProperties properties, final NacosRestTemplate nacosRestTemplate) throws NacosException { super.init(properties, nacosRestTemplate); this.nacosRestTemplate = nacosRestTemplate; initEndpoint(properties); initEndpointPort(properties); initEndpointContextPath(properties); initServerListName(properties); initAddressServerUrl(properties); initModuleName(properties); startRefreshServerListTask(properties); } @Override public List getServerList() { return serversFromEndpoint; } @Override public String getServerName() { String contextPathTmp = StringUtils.isNotBlank(this.endpointContextPath) ? this.endpointContextPath : this.contextPath; return CUSTOM_NAME + "-" + String.join("_", endpoint, String.valueOf(endpointPort), contextPathTmp, serverListName) + (StringUtils.isNotBlank(namespace) ? ("_" + StringUtils.trim(namespace)) : ""); } @Override public int getOrder() { return Address.ENDPOINT_SERVER_LIST_PROVIDER_ORDER; } @Override public boolean match(final NacosClientProperties properties) { String endpointTmp = getEndPointTmp(properties); return StringUtils.isNotBlank(endpointTmp); } @Override public String getAddressSource() { return this.addressServerUrl; } private String getEndPointTmp(NacosClientProperties properties) { String endpointTmp = properties.getProperty(PropertyKeyConst.ENDPOINT); String isUseEndpointRuleParsing = properties.getProperty(PropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE, properties.getProperty(SystemPropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE, String.valueOf(USE_ENDPOINT_PARSING_RULE_DEFAULT_VALUE))); if (Boolean.parseBoolean(isUseEndpointRuleParsing)) { endpointTmp = ClientBasicParamUtil.parsingEndpointRule(endpointTmp); } return endpointTmp; } /** * Start refresh server list task. * * @throws NacosException nacos exception */ public void startRefreshServerListTask(NacosClientProperties properties) throws NacosException { for (int i = 0; i < initServerListRetryTimes && getServerList().isEmpty(); ++i) { refreshServerListIfNeed(); if (!serversFromEndpoint.isEmpty()) { break; } try { this.wait((i + 1) * 100L); } catch (Exception e) { LOGGER.warn("get serverlist fail,url: {}", addressServerUrl); } } if (serversFromEndpoint.isEmpty()) { LOGGER.error("[init-serverlist] fail to get NACOS-server serverlist! url: {}", addressServerUrl); throw new NacosException(NacosException.SERVER_ERROR, "fail to get NACOS-server serverlist! not connnect url:" + addressServerUrl); } refreshServerListExecutor = new ScheduledThreadPoolExecutor(1, new NameThreadFactory("com.alibaba.nacos.client.address.EndpointServerListProvider.refreshServerList")); // executor schedules the timer task long refreshInterval = Long.parseLong( properties.getProperty(PropertyKeyConst.ENDPOINT_REFRESH_INTERVAL_SECONDS, "30")); refreshServerListExecutor.scheduleWithFixedDelay(this::refreshServerListIfNeed, 0L, refreshInterval, TimeUnit.SECONDS); } private void refreshServerListIfNeed() { try { if (System.currentTimeMillis() - lastServerListRefreshTime < refreshServerListInternal) { return; } List list = getServerListFromEndpoint(); if (CollectionUtils.isEmpty(list)) { throw new Exception("Can not acquire Nacos list"); } list.sort(String::compareTo); if (!CollectionUtils.isEqualCollection(list, serversFromEndpoint)) { LOGGER.info("[SERVER-LIST] server list is updated: {}", list); serversFromEndpoint = list; lastServerListRefreshTime = System.currentTimeMillis(); NotifyCenter.publishEvent(new ServerListChangeEvent()); } } catch (Throwable e) { LOGGER.warn("failed to update server list", e); } } private List getServerListFromEndpoint() { try { HttpRestResult httpResult = nacosRestTemplate.get(addressServerUrl, HttpUtils.builderHeader(moduleName), Query.EMPTY, String.class); if (!httpResult.ok()) { LOGGER.error("[check-serverlist] error. addressServerUrl: {}, code: {}", addressServerUrl, httpResult.getCode()); return null; } List lines = IoUtils.readLines(new StringReader(httpResult.getData())); List result = new ArrayList<>(lines.size()); for (String serverAddr : lines) { String[] ipPort = InternetAddressUtil.splitIpPortStr(serverAddr); String ip = ipPort[0].trim(); if (ipPort.length == 1) { result.add(ip + InternetAddressUtil.IP_PORT_SPLITER + ClientBasicParamUtil.getDefaultServerPort()); } else { result.add(serverAddr); } } return result; } catch (Exception e) { LOGGER.error("[check-serverlist] exception. url: {}", addressServerUrl, e); return null; } } private void initEndpoint(NacosClientProperties properties) { // Endpoint should not be null or empty, because the match has return `true`. this.endpoint = getEndPointTmp(properties); } private void initEndpointPort(NacosClientProperties properties) { String endpointPortTmp = TemplateUtils.stringEmptyAndThenExecute( properties.getProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_PORT), () -> properties.getProperty(PropertyKeyConst.ENDPOINT_PORT)); if (StringUtils.isNotBlank(endpointPortTmp)) { this.endpointPort = Integer.parseInt(endpointPortTmp); } } private void initEndpointContextPath(NacosClientProperties properties) { String endpointContextPathTmp = TemplateUtils.stringEmptyAndThenExecute( properties.getProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_CONTEXT_PATH), () -> properties.getProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH)); if (StringUtils.isNotBlank(endpointContextPathTmp)) { this.endpointContextPath = endpointContextPathTmp; } } private void initServerListName(NacosClientProperties properties) { String serverListNameTmp = properties.getProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME); boolean isUseClusterName = Boolean.parseBoolean( properties.getProperty(PropertyKeyConst.IS_ADAPT_CLUSTER_NAME_USAGE)); if (StringUtils.isBlank(serverListNameTmp) && isUseClusterName) { serverListNameTmp = properties.getProperty(PropertyKeyConst.CLUSTER_NAME); } if (!StringUtils.isBlank(serverListNameTmp)) { this.serverListName = serverListNameTmp; } } private void initAddressServerUrl(NacosClientProperties properties) { String contextPathTmp = StringUtils.isNotBlank(this.endpointContextPath) ? ContextPathUtil.normalizeContextPath( this.endpointContextPath) : ContextPathUtil.normalizeContextPath(this.contextPath); StringBuilder addressServerUrlTem = new StringBuilder( String.format("http://%s:%d%s/%s", this.endpoint, this.endpointPort, contextPathTmp, this.serverListName)); boolean hasQueryString = false; if (StringUtils.isNotBlank(namespace)) { addressServerUrlTem.append("?namespace=").append(namespace); hasQueryString = true; } if (properties.containsKey(PropertyKeyConst.ENDPOINT_QUERY_PARAMS)) { addressServerUrlTem.append(hasQueryString ? "&" : "?"); addressServerUrlTem.append(properties.getProperty(PropertyKeyConst.ENDPOINT_QUERY_PARAMS)); } this.addressServerUrl = addressServerUrlTem.toString(); LOGGER.info("address server url = {}", this.addressServerUrl); } private void initModuleName(NacosClientProperties properties) { String moduleNameTmp = properties.getProperty(Constants.CLIENT_MODULE_TYPE); if (StringUtils.isNotBlank(moduleNameTmp)) { this.moduleName = moduleNameTmp; } } @Override public void shutdown() throws NacosException { if (null != refreshServerListExecutor) { refreshServerListExecutor.shutdown(); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/address/PropertiesListProvider.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.constant.Constants.Address; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.StringUtils; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * Properties server list provider. * * @author totalo */ public class PropertiesListProvider extends AbstractServerListProvider { private static final String FIXED_NAME = "fixed"; private List serverList; @Override public void init(final NacosClientProperties properties, final NacosRestTemplate nacosRestTemplate) throws NacosException { super.init(properties, nacosRestTemplate); serverList = new ArrayList<>(); String serverAddrsStr = properties.getProperty(PropertyKeyConst.SERVER_ADDR); StringTokenizer serverAddrsTokens = new StringTokenizer(serverAddrsStr, ",;"); while (serverAddrsTokens.hasMoreTokens()) { String serverAddr = serverAddrsTokens.nextToken().trim(); if (serverAddr.startsWith(HTTP_PREFIX) || serverAddr.startsWith(HTTPS_PREFIX)) { this.serverList.add(serverAddr); } else { String[] serverAddrArr = InternetAddressUtil.splitIpPortStr(serverAddr); if (serverAddrArr.length == 1) { this.serverList .add(serverAddrArr[0] + InternetAddressUtil.IP_PORT_SPLITER + ClientBasicParamUtil.getDefaultServerPort()); } else { this.serverList.add(serverAddr); } } } } @Override public List getServerList() { return serverList; } @Override public String getServerName() { return FIXED_NAME + "-" + (StringUtils.isNotBlank(namespace) ? (StringUtils.trim(namespace) + "-") : "") + ClientBasicParamUtil.getNameSuffixByServerIps(serverList.toArray(new String[0])); } @Override public int getOrder() { return Address.ADDRESS_SERVER_LIST_PROVIDER_ORDER; } @Override public boolean match(final NacosClientProperties properties) { return StringUtils.isNotBlank(properties.getProperty(PropertyKeyConst.SERVER_ADDR)); } @Override public boolean isFixed() { return true; } @Override public void shutdown() throws NacosException { } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/address/ServerListChangeEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.common.notify.SlowEvent; /** * Server List Change Event. * * @author zongtanghu */ public class ServerListChangeEvent extends SlowEvent { private static final long serialVersionUID = -1655577508567092395L; } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/address/ServerListProvider.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.lifecycle.Closeable; import java.util.List; /** * Server list provider. * * @author totalo */ public interface ServerListProvider extends Closeable { /** * Init. * @param properties nacos client properties * @param nacosRestTemplate nacos rest template * @throws NacosException nacos exception */ void init(final NacosClientProperties properties, final NacosRestTemplate nacosRestTemplate) throws NacosException; /** * Get server list. * @return server list */ List getServerList(); /** * Get server name. * @return server name */ default String getServerName() { return ""; } /** * Get namespace. * @return namespace */ default String getNamespace() { return ""; } /** * Get context path. * @return context path */ default String getContextPath() { return ClientBasicParamUtil.getDefaultContextPath(); } /** * Get order. * @return order */ int getOrder(); /** * Match. * @param properties nacos client properties * @return match */ boolean match(final NacosClientProperties properties); /** * check the server list is fixed or not. * @return true if the server list is fixed */ default boolean isFixed() { return false; } /** * Get address source. * @return address source */ default String getAddressSource() { return ""; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/impl/NacosAuthLoginConstant.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.impl; /** * Nacos auth login constants. * * @author Nacos */ public class NacosAuthLoginConstant { public static final String ACCESSTOKEN = "accessToken"; public static final String TOKENTTL = "tokenTtl"; public static final String TOKENREFRESHWINDOW = "tokenRefreshWindow"; public static final String USERNAME = "username"; public static final String PASSWORD = "password"; public static final String COLON = ":"; public static final String SERVER = "server"; public static final String RELOGINFLAG = "reLoginFlag"; } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/impl/NacosClientAuthServiceImpl.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.impl; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.auth.impl.process.HttpLoginProcessor; import com.alibaba.nacos.common.utils.RandomUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Properties; import java.util.concurrent.TimeUnit; /** * a ClientAuthService implement. * * @author wuyfee */ @SuppressWarnings("checkstyle:SummaryJavadoc") public class NacosClientAuthServiceImpl extends AbstractClientAuthService { private static final Logger SECURITY_LOGGER = LoggerFactory.getLogger(NacosClientAuthServiceImpl.class); /** * TTL of token in seconds. */ private long tokenTtl; /** * Last timestamp refresh security info from server. */ private long lastRefreshTime; /** * time window to refresh security info in seconds. */ private long tokenRefreshWindow; /** * A context to take with when sending request to Nacos server. */ private volatile LoginIdentityContext loginIdentityContext = new LoginIdentityContext(); /** * Re-login window in milliseconds. */ private final long reLoginWindow = 60000; /** * Login to servers. * * @return true if login successfully */ @Override public Boolean login(Properties properties) { try { boolean reLoginFlag = Boolean.parseBoolean(loginIdentityContext.getParameter(NacosAuthLoginConstant.RELOGINFLAG, "false")); if (reLoginFlag) { if ((System.currentTimeMillis() - lastRefreshTime) < reLoginWindow) { return true; } } else { if ((System.currentTimeMillis() - lastRefreshTime) < TimeUnit.SECONDS .toMillis(tokenTtl - tokenRefreshWindow)) { return true; } } if (StringUtils.isBlank(properties.getProperty(PropertyKeyConst.USERNAME))) { lastRefreshTime = System.currentTimeMillis(); return true; } for (String server : this.serverList) { HttpLoginProcessor httpLoginProcessor = new HttpLoginProcessor(nacosRestTemplate); properties.setProperty(NacosAuthLoginConstant.SERVER, server); LoginIdentityContext identityContext = httpLoginProcessor.getResponse(properties); if (identityContext != null) { if (identityContext.getAllKey().contains(NacosAuthLoginConstant.ACCESSTOKEN)) { tokenTtl = Long.parseLong(identityContext.getParameter(NacosAuthLoginConstant.TOKENTTL)); tokenRefreshWindow = generateTokenRefreshWindow(tokenTtl); lastRefreshTime = System.currentTimeMillis(); LoginIdentityContext newCtx = new LoginIdentityContext(); newCtx.setParameter(NacosAuthLoginConstant.ACCESSTOKEN, identityContext.getParameter(NacosAuthLoginConstant.ACCESSTOKEN)); this.loginIdentityContext = newCtx; } return true; } } } catch (Throwable throwable) { SECURITY_LOGGER.warn("[SecurityProxy] login failed, error: ", throwable); return false; } return false; } @Override public LoginIdentityContext getLoginIdentityContext(RequestResource resource) { return this.loginIdentityContext; } @Override public void shutdown() throws NacosException { } /** * Randomly generate TokenRefreshWindow, Avoid a large number of logins causing pressure on the Nacos server. * @param tokenTtl TTL of token in seconds. * @return tokenRefreshWindow, numerical range [tokenTtl/15 ~ tokenTtl/10] */ public long generateTokenRefreshWindow(long tokenTtl) { long startNumber = tokenTtl / 15; long endNumber = tokenTtl / 10; return RandomUtils.nextLong(startNumber, endNumber); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/impl/process/HttpLoginProcessor.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.impl.process; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.client.utils.ContextPathUtil; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.fasterxml.jackson.databind.JsonNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * Login processor for Http. * * @author Nacos */ public class HttpLoginProcessor implements LoginProcessor { private static final Logger SECURITY_LOGGER = LoggerFactory.getLogger(HttpLoginProcessor.class); private static final String LOGIN_V1_URL = "/v1/auth/users/login"; private static final String LOGIN_V3_URL = "/v3/auth/user/login"; public static final String DEFAULT_NACOS_WEB_CONTEXT = "/nacos"; private final NacosRestTemplate nacosRestTemplate; public HttpLoginProcessor(NacosRestTemplate nacosRestTemplate) { this.nacosRestTemplate = nacosRestTemplate; } @Override public LoginIdentityContext getResponse(Properties properties) { String contextPath = ContextPathUtil.normalizeContextPath( properties.getProperty(PropertyKeyConst.CONTEXT_PATH, DEFAULT_NACOS_WEB_CONTEXT)); String server = properties.getProperty(NacosAuthLoginConstant.SERVER, StringUtils.EMPTY); if (!server.startsWith(HTTPS_PREFIX) && !server.startsWith(HTTP_PREFIX)) { if (!InternetAddressUtil.containsPort(server)) { server = server + InternetAddressUtil.IP_PORT_SPLITER + ClientBasicParamUtil.getDefaultServerPort(); } server = HTTP_PREFIX + server; } String url = server + contextPath + LOGIN_V3_URL; Map params = new HashMap<>(2); Map bodyMap = new HashMap<>(2); params.put(PropertyKeyConst.USERNAME, properties.getProperty(PropertyKeyConst.USERNAME, StringUtils.EMPTY)); bodyMap.put(PropertyKeyConst.PASSWORD, properties.getProperty(PropertyKeyConst.PASSWORD, StringUtils.EMPTY)); try { HttpRestResult restResult = nacosRestTemplate.postForm(url, Header.EMPTY, Query.newInstance().initParams(params), bodyMap, String.class); int code = restResult.getCode(); if (code == NacosException.NOT_FOUND || code == NacosException.SERVER_NOT_IMPLEMENTED) { url = server + contextPath + LOGIN_V1_URL; restResult = nacosRestTemplate.postForm(url, Header.EMPTY, Query.newInstance().initParams(params), bodyMap, String.class); } if (!restResult.ok()) { SECURITY_LOGGER.error("login failed: {}", JacksonUtils.toJson(restResult)); return null; } JsonNode obj = JacksonUtils.toObj(restResult.getData()); LoginIdentityContext loginIdentityContext = new LoginIdentityContext(); if (obj.has(Constants.ACCESS_TOKEN)) { loginIdentityContext.setParameter(NacosAuthLoginConstant.ACCESSTOKEN, obj.get(Constants.ACCESS_TOKEN).asText()); loginIdentityContext.setParameter(NacosAuthLoginConstant.TOKENTTL, obj.get(Constants.TOKEN_TTL).asText()); } else { SECURITY_LOGGER.info("[NacosClientAuthServiceImpl] ACCESS_TOKEN is empty from response"); } return loginIdentityContext; } catch (Exception e) { Map newBodyMap = new HashMap<>(bodyMap); newBodyMap.put(PropertyKeyConst.PASSWORD, ClientBasicParamUtil.desensitiseParameter(bodyMap.get(PropertyKeyConst.PASSWORD))); SECURITY_LOGGER.error("[NacosClientAuthServiceImpl] login http request failed" + " url: {}, params: {}, bodyMap: {}, errorMsg: {}", url, params, newBodyMap, e.getMessage()); return null; } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/impl/process/LoginProcessor.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.impl.process; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import java.util.Properties; /** * Nacos login processor. * * @author Nacos */ public interface LoginProcessor { /** * send request to server and get result. * * @param properties request properties. * @return login identity context. */ LoginIdentityContext getResponse(Properties properties); } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/RamClientAuthServiceImpl.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.auth.ram.identify.StsConfig; import com.alibaba.nacos.client.auth.ram.injector.AbstractResourceInjector; import com.alibaba.nacos.client.auth.ram.injector.AiResourceInjector; import com.alibaba.nacos.client.auth.ram.injector.ConfigResourceInjector; import com.alibaba.nacos.client.auth.ram.injector.LockResourceInjector; import com.alibaba.nacos.client.auth.ram.injector.NamingResourceInjector; import com.alibaba.nacos.client.auth.ram.utils.RamUtil; import com.alibaba.nacos.client.auth.ram.utils.SpasAdapter; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * Client Auth service implementation for aliyun RAM. * * @author xiweng.yy */ public class RamClientAuthServiceImpl extends AbstractClientAuthService { private static final Logger LOGGER = LoggerFactory.getLogger(RamClientAuthServiceImpl.class); private final RamContext ramContext; private final Map resourceInjectors; public RamClientAuthServiceImpl() { ramContext = new RamContext(); resourceInjectors = new HashMap<>(); resourceInjectors.put(SignType.NAMING, new NamingResourceInjector()); resourceInjectors.put(SignType.CONFIG, new ConfigResourceInjector()); resourceInjectors.put(SignType.LOCK, new LockResourceInjector()); resourceInjectors.put(SignType.AI, new AiResourceInjector()); } @Override public Boolean login(Properties properties) { if (ramContext.validate()) { return true; } loadRoleName(properties); loadAccessKey(properties); loadSecretKey(properties); loadRegionId(properties); return true; } private void loadRoleName(Properties properties) { String ramRoleName = properties.getProperty(PropertyKeyConst.RAM_ROLE_NAME); if (!StringUtils.isBlank(ramRoleName)) { StsConfig.getInstance().setRamRoleName(ramRoleName); ramContext.setRamRoleName(ramRoleName); } } private void loadAccessKey(Properties properties) { ramContext.setAccessKey(RamUtil.getAccessKey(properties)); } private void loadSecretKey(Properties properties) { ramContext.setSecretKey(RamUtil.getSecretKey(properties)); } private void loadRegionId(Properties properties) { String regionId = properties.getProperty(PropertyKeyConst.SIGNATURE_REGION_ID); ramContext.setRegionId(regionId); } @Override public LoginIdentityContext getLoginIdentityContext(RequestResource resource) { LoginIdentityContext result = new LoginIdentityContext(); if (!ramContext.validate() || notFountInjector(resource.getType())) { return result; } resourceInjectors.get(resource.getType()).doInject(resource, ramContext, result); return result; } private boolean notFountInjector(String type) { if (!resourceInjectors.containsKey(type)) { LOGGER.warn("Injector for type {} not found, will use default ram identity context.", type); return true; } return false; } @Override public void shutdown() throws NacosException { SpasAdapter.freeCredentialInstance(); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/RamConstants.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram; /** * Ram auth plugin constants. * * @author xiweng.yy */ public class RamConstants { public static final String SIGNATURE_VERSION = "signatureVersion"; public static final String V4 = "v4"; public static final String SIGNATURE_V4_METHOD = "HmacSHA256"; public static final String SIGNATURE_V4_PRODUCE = "mse-nacos"; } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/RamContext.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram; import com.alibaba.nacos.common.utils.StringUtils; /** * Aliyun RAM context. * * @author xiweng.yy */ public class RamContext { private String accessKey; private String secretKey; private String ramRoleName; private String regionId; 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 getRamRoleName() { return ramRoleName; } public void setRamRoleName(String ramRoleName) { this.ramRoleName = ramRoleName; } public String getRegionId() { return regionId; } public void setRegionId(String regionId) { this.regionId = regionId; } public boolean validate() { return StringUtils.isNotBlank(ramRoleName) || StringUtils.isNotBlank(accessKey) && StringUtils .isNotBlank(secretKey); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/CredentialListener.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; /** * Credential Listener. * * @author Nacos */ public interface CredentialListener { /** * update Credential. */ void onUpdateCredential(); } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/CredentialService.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ConcurrentHashMap; /** * Credential Service. * * @author Nacos */ public final class CredentialService implements SpasCredentialLoader { private static final Logger LOGGER = LoggerFactory.getLogger(CredentialService.class); private static final ConcurrentHashMap INSTANCES = new ConcurrentHashMap<>(); private final String appName; private Credentials credentials = new Credentials(); private final CredentialWatcher watcher; private CredentialListener listener; private CredentialService(String appName) { if (appName == null) { String value = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.PROJECT_NAME_PROPERTY); if (StringUtils.isNotEmpty(value)) { appName = value; } } this.appName = appName; watcher = new CredentialWatcher(appName, this); } public static CredentialService getInstance() { return getInstance(null); } public static CredentialService getInstance(String appName) { String key = appName != null ? appName : IdentifyConstants.NO_APP_NAME; return INSTANCES.computeIfAbsent(key, k -> new CredentialService(appName)); } public static CredentialService freeInstance() { return freeInstance(null); } /** * Free instance. * * @param appName app name * @return {@link CredentialService} */ public static CredentialService freeInstance(String appName) { String key = appName != null ? appName : IdentifyConstants.NO_APP_NAME; CredentialService instance = INSTANCES.remove(key); if (instance != null) { instance.free(); } return instance; } /** * Free service. */ public void free() { if (watcher != null) { watcher.stop(); } LOGGER.info("[{}] {} is freed", appName, this.getClass().getSimpleName()); } @Override public Credentials getCredential() { return credentials; } public void setCredential(Credentials credential) { boolean changed = !(credentials == credential || (credentials != null && credentials.identical(credential))); credentials = credential; if (changed && listener != null) { listener.onUpdateCredential(); } } public void setStaticCredential(Credentials credential) { if (watcher != null) { watcher.stop(); } setCredential(credential); } public void registerCredentialListener(CredentialListener listener) { this.listener = listener; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/CredentialWatcher.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.Objects; import java.util.Properties; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * Credential Watcher. * * @author Nacos */ public class CredentialWatcher { private static final Logger LOGGER = LoggerFactory.getLogger(CredentialWatcher.class); private static final long REFRESH_INTERVAL = 10 * 1000L; private final CredentialService serviceInstance; private final String appName; private String propertyPath; private boolean stopped; private final ScheduledExecutorService executor; public CredentialWatcher(String appName, CredentialService serviceInstance) { this.appName = appName; this.serviceInstance = serviceInstance; loadCredential(true); executor = ExecutorFactory.newSingleScheduledExecutorService( new NameThreadFactory("com.alibaba.nacos.client.auth.ram.identify.watcher")); executor.scheduleWithFixedDelay(new Runnable() { private long modified = 0; @Override public void run() { synchronized (this) { if (stopped) { return; } boolean reload = false; if (propertyPath == null) { reload = true; } else { File file = new File(propertyPath); long lastModified = file.lastModified(); if (modified != lastModified) { reload = true; modified = lastModified; } } if (reload) { loadCredential(false); } } } }, REFRESH_INTERVAL, REFRESH_INTERVAL, TimeUnit.MILLISECONDS); } /** * Stop watcher. */ public void stop() { if (stopped) { return; } if (executor != null) { synchronized (executor) { stopped = true; executor.shutdown(); } } LOGGER.info("[{}] {} is stopped", appName, this.getClass().getSimpleName()); } private void loadCredential(boolean init) { loadPropertyPath(init); InputStream propertiesIs = loadPropertyPathToStream(); Credentials credentials = new Credentials(); boolean loadResult = Objects.isNull(propertiesIs) ? loadCredentialFromEnv(init, credentials) : loadCredentialFromProperties(propertiesIs, init, credentials); if (!loadResult) { return; } if (!credentials.valid()) { LOGGER .warn("[1] Credential file missing required property {} Credential file missing {} or {}", appName, IdentifyConstants.ACCESS_KEY, IdentifyConstants.SECRET_KEY); propertyPath = null; // return; } serviceInstance.setCredential(credentials); } private boolean loadCredentialFromProperties(InputStream propertiesIs, boolean init, Credentials credentials) { Properties properties = new Properties(); try { properties.load(propertiesIs); } catch (IOException e) { LOGGER .error("[26] Unable to load credential file, appName:" + appName + "Unable to load credential file " + propertyPath, e); propertyPath = null; return false; } finally { try { propertiesIs.close(); } catch (IOException e) { LOGGER.error("[27] Unable to close credential file, appName:" + appName + "Unable to close credential file " + propertyPath, e); } } if (init) { LOGGER.info("[{}] Load credential file {}", appName, propertyPath); } String accessKey = null; String secretKey = null; String tenantId = null; if (!IdentifyConstants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) { if (properties.containsKey(IdentifyConstants.ACCESS_KEY)) { accessKey = properties.getProperty(IdentifyConstants.ACCESS_KEY); } if (properties.containsKey(IdentifyConstants.SECRET_KEY)) { secretKey = properties.getProperty(IdentifyConstants.SECRET_KEY); } if (properties.containsKey(IdentifyConstants.TENANT_ID)) { tenantId = properties.getProperty(IdentifyConstants.TENANT_ID); } } else { if (properties.containsKey(IdentifyConstants.DOCKER_ACCESS_KEY)) { accessKey = properties.getProperty(IdentifyConstants.DOCKER_ACCESS_KEY); } if (properties.containsKey(IdentifyConstants.DOCKER_SECRET_KEY)) { secretKey = properties.getProperty(IdentifyConstants.DOCKER_SECRET_KEY); } if (properties.containsKey(IdentifyConstants.DOCKER_TENANT_ID)) { tenantId = properties.getProperty(IdentifyConstants.DOCKER_TENANT_ID); } } setAccessKey(credentials, accessKey); setSecretKey(credentials, secretKey); setTenantId(credentials, tenantId); return true; } private boolean loadCredentialFromEnv(boolean init, Credentials credentials) { propertyPath = null; String accessKey = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.ENV_ACCESS_KEY); String secretKey = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.ENV_SECRET_KEY); if (accessKey == null && secretKey == null) { if (init) { LOGGER.info("{} No credential found", appName); } return false; } setAccessKey(credentials, accessKey); setSecretKey(credentials, secretKey); return true; } private void loadPropertyPath(boolean init) { if (propertyPath == null) { URL url = ClassLoader.getSystemResource(IdentifyConstants.PROPERTIES_FILENAME); if (url != null) { propertyPath = url.getPath(); } if (propertyPath == null || propertyPath.isEmpty()) { String value = NacosClientProperties.PROTOTYPE.getProperty("spas.identity"); if (StringUtils.isNotEmpty(value)) { propertyPath = value; } if (propertyPath == null || propertyPath.isEmpty()) { propertyPath = IdentifyConstants.CREDENTIAL_PATH + (appName == null ? IdentifyConstants.CREDENTIAL_DEFAULT : appName); } else { if (init) { LOGGER.info("[{}] Defined credential file: -Dspas.identity={}", appName, propertyPath); } } } else { if (init) { LOGGER.info("[{}] Load credential file from classpath: {}", appName, IdentifyConstants.PROPERTIES_FILENAME); } } } } private InputStream loadPropertyPathToStream() { InputStream propertiesIs = null; do { try { propertiesIs = new FileInputStream(propertyPath); } catch (FileNotFoundException e) { if (appName != null && !appName.equals(IdentifyConstants.CREDENTIAL_DEFAULT) && propertyPath .equals(IdentifyConstants.CREDENTIAL_PATH + appName)) { propertyPath = IdentifyConstants.CREDENTIAL_PATH + IdentifyConstants.CREDENTIAL_DEFAULT; continue; } if (!IdentifyConstants.DOCKER_CREDENTIAL_PATH.equals(propertyPath)) { propertyPath = IdentifyConstants.DOCKER_CREDENTIAL_PATH; continue; } } break; } while (true); return propertiesIs; } private void setAccessKey(Credentials credentials, String accessKey) { if (!Objects.isNull(accessKey)) { credentials.setAccessKey(accessKey.trim()); } } private void setSecretKey(Credentials credentials, String secretKey) { if (!Objects.isNull(secretKey)) { credentials.setSecretKey(secretKey.trim()); } } private void setTenantId(Credentials credentials, String tenantId) { if (!Objects.isNull(tenantId)) { credentials.setTenantId(tenantId.trim()); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/Credentials.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; /** * Credentials. * * @author Nacos */ public class Credentials implements SpasCredential { private volatile String accessKey; private volatile String secretKey; private volatile String tenantId; public Credentials() { this(null, null, null); } public Credentials(String accessKey, String secretKey, String tenantId) { this.accessKey = accessKey; this.secretKey = secretKey; this.tenantId = tenantId; } @Override public String getAccessKey() { return accessKey; } public void setAccessKey(String accessKey) { this.accessKey = accessKey; } @Override public String getSecretKey() { return secretKey; } public void setSecretKey(String secretKey) { this.secretKey = secretKey; } public String getTenantId() { return tenantId; } public void setTenantId(String tenantId) { this.tenantId = tenantId; } public boolean valid() { return accessKey != null && !accessKey.isEmpty() && secretKey != null && !secretKey.isEmpty(); } /** * Identical. * * @param other other * @return true if identical */ public boolean identical(Credentials other) { return this == other || (other != null && (accessKey == null && other.accessKey == null || accessKey != null && accessKey.equals(other.accessKey)) && ( secretKey == null && other.secretKey == null || secretKey != null && secretKey .equals(other.secretKey))); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/IdentifyConstants.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; /** * Identify Constants. * * @author Nacos */ public class IdentifyConstants { public static final String ACCESS_KEY = "accessKey"; public static final String SECRET_KEY = "secretKey"; public static final String SECURITY_TOKEN_HEADER = "Spas-SecurityToken"; public static final String TENANT_ID = "tenantId"; public static final String PROPERTIES_FILENAME = "spas.properties"; public static final String CREDENTIAL_PATH = "/home/admin/.spas_key/"; public static final String CREDENTIAL_DEFAULT = "default"; public static final String DOCKER_CREDENTIAL_PATH = "/etc/instanceInfo"; public static final String DOCKER_ACCESS_KEY = "env_spas_accessKey"; public static final String DOCKER_SECRET_KEY = "env_spas_secretKey"; public static final String DOCKER_TENANT_ID = "ebv_spas_tenantId"; public static final String ENV_ACCESS_KEY = "spas_accessKey"; public static final String ENV_SECRET_KEY = "spas_secretKey"; public static final String ENV_TENANT_ID = "tenant.id"; public static final String NO_APP_NAME = ""; public static final String PROJECT_NAME_PROPERTY = "project.name"; public static final String RAM_ROLE_NAME_PROPERTY = "ram.role.name"; public static final String REFRESH_TIME_PROPERTY = "time.to.refresh.in.millisecond"; public static final String SECURITY_PROPERTY = "security.credentials"; public static final String SECURITY_URL_PROPERTY = "security.credentials.url"; public static final String SECURITY_CACHE_PROPERTY = "cache.security.credentials"; } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/SpasCredential.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; /** * Spas Credential Interface. * * @author Nacos */ public interface SpasCredential { /** * get AccessKey. * * @return AccessKey */ String getAccessKey(); /** * get SecretKey. * * @return SecretKey */ String getSecretKey(); } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/SpasCredentialLoader.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; /** * Spas Credential Loader. * * @author Nacos */ public interface SpasCredentialLoader { /** * get Credential. * * @return Credential */ SpasCredential getCredential(); } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/StsConfig.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.utils.StringUtils; /** * Sts config. * * @author Nacos */ public class StsConfig { private static final String RAM_SECURITY_CREDENTIALS_URL = "http://100.100.100.200/latest/meta-data/ram/security-credentials/"; private String ramRoleName; /** * The STS temporary certificate will be refreshed when the validity period of * the temporary certificate is left (allow the local time to be at most slower than the STS service time). */ private int timeToRefreshInMillisecond = 3 * 60 * 1000; /** * Metadata interface for obtaining STS temporary credentials (including role name). */ private String securityCredentialsUrl; /** * Set the STS temporary certificate and no longer obtain it through the metadata interface. */ private String securityCredentials; /** * Whether to cache. */ private boolean cacheSecurityCredentials = true; private static class Singleton { private static final StsConfig INSTANCE = new StsConfig(); } private StsConfig() { String ramRoleName = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.RAM_ROLE_NAME_PROPERTY); if (!StringUtils.isBlank(ramRoleName)) { setRamRoleName(ramRoleName); } String timeToRefreshInMillisecond = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.REFRESH_TIME_PROPERTY); if (!StringUtils.isBlank(timeToRefreshInMillisecond)) { setTimeToRefreshInMillisecond(Integer.parseInt(timeToRefreshInMillisecond)); } String securityCredentials = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.SECURITY_PROPERTY); if (!StringUtils.isBlank(securityCredentials)) { setSecurityCredentials(securityCredentials); } String securityCredentialsUrl = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.SECURITY_URL_PROPERTY); if (!StringUtils.isBlank(securityCredentialsUrl)) { setSecurityCredentialsUrl(securityCredentialsUrl); } String cacheSecurityCredentials = NacosClientProperties.PROTOTYPE.getProperty(IdentifyConstants.SECURITY_CACHE_PROPERTY); if (!StringUtils.isBlank(cacheSecurityCredentials)) { setCacheSecurityCredentials(Boolean.parseBoolean(cacheSecurityCredentials)); } } public static StsConfig getInstance() { return Singleton.INSTANCE; } public String getRamRoleName() { return ramRoleName; } public void setRamRoleName(String ramRoleName) { this.ramRoleName = ramRoleName; } public int getTimeToRefreshInMillisecond() { return timeToRefreshInMillisecond; } public void setTimeToRefreshInMillisecond(int timeToRefreshInMillisecond) { this.timeToRefreshInMillisecond = timeToRefreshInMillisecond; } public String getSecurityCredentialsUrl() { if (securityCredentialsUrl == null && ramRoleName != null) { return RAM_SECURITY_CREDENTIALS_URL + ramRoleName; } return securityCredentialsUrl; } public void setSecurityCredentialsUrl(String securityCredentialsUrl) { this.securityCredentialsUrl = securityCredentialsUrl; } public String getSecurityCredentials() { return securityCredentials; } public void setSecurityCredentials(String securityCredentials) { this.securityCredentials = securityCredentials; } public boolean isStsOn() { return StringUtils.isNotEmpty(getSecurityCredentials()) || StringUtils.isNotEmpty(getSecurityCredentialsUrl()); } public boolean isCacheSecurityCredentials() { return cacheSecurityCredentials; } public void setCacheSecurityCredentials(boolean cacheSecurityCredentials) { this.cacheSecurityCredentials = cacheSecurityCredentials; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/StsCredential.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import com.fasterxml.jackson.annotation.JsonProperty; import java.util.Date; /** * Sts credential for aliyun RAM. * * @author xiweng.yy */ public class StsCredential { @JsonProperty(value = "AccessKeyId") private String accessKeyId; @JsonProperty(value = "AccessKeySecret") private String accessKeySecret; @JsonProperty(value = "Expiration") private Date expiration; @JsonProperty(value = "SecurityToken") private String securityToken; @JsonProperty(value = "LastUpdated") private Date lastUpdated; @JsonProperty(value = "Code") private String code; public String getAccessKeyId() { return accessKeyId; } public void setAccessKeyId(String accessKeyId) { this.accessKeyId = accessKeyId; } public String getAccessKeySecret() { return accessKeySecret; } public void setAccessKeySecret(String accessKeySecret) { this.accessKeySecret = accessKeySecret; } public Date getExpiration() { return expiration; } public void setExpiration(Date expiration) { this.expiration = expiration; } public String getSecurityToken() { return securityToken; } public void setSecurityToken(String securityToken) { this.securityToken = securityToken; } public Date getLastUpdated() { return lastUpdated; } public void setLastUpdated(Date lastUpdated) { this.lastUpdated = lastUpdated; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } @Override public String toString() { return "STSCredential{" + "accessKeyId='" + accessKeyId + '\'' + ", accessKeySecret='" + accessKeySecret + '\'' + ", expiration=" + expiration + ", securityToken='" + securityToken + '\'' + ", lastUpdated=" + lastUpdated + ", code='" + code + '\'' + '}'; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/identify/StsCredentialHolder.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.client.remote.HttpClientManager; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.JacksonUtils; import com.fasterxml.jackson.core.type.TypeReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Sts credential holder. * * @author xiweng.yy */ public class StsCredentialHolder { private static final Logger LOGGER = LoggerFactory.getLogger(StsCredentialHolder.class); private static final StsCredentialHolder INSTANCE = new StsCredentialHolder(); private StsCredential stsCredential; private StsCredentialHolder() { } public static StsCredentialHolder getInstance() { return INSTANCE; } /** * Get Sts Credential. * * @return StsCredential */ public StsCredential getStsCredential() { boolean cacheSecurityCredentials = StsConfig.getInstance().isCacheSecurityCredentials(); if (cacheSecurityCredentials && stsCredential != null) { long currentTime = System.currentTimeMillis(); long expirationTime = stsCredential.getExpiration().getTime(); int timeToRefreshInMillisecond = StsConfig.getInstance().getTimeToRefreshInMillisecond(); if (expirationTime - currentTime > timeToRefreshInMillisecond) { return stsCredential; } } String stsResponse = getStsResponse(); stsCredential = JacksonUtils.toObj(stsResponse, new TypeReference() { }); LOGGER.info("[getSTSCredential] code:{}, accessKeyId:{}, lastUpdated:{}, expiration:{}", stsCredential.getCode(), stsCredential.getAccessKeyId(), stsCredential.getLastUpdated(), stsCredential.getExpiration()); return stsCredential; } private static String getStsResponse() { String securityCredentials = StsConfig.getInstance().getSecurityCredentials(); if (securityCredentials != null) { return securityCredentials; } String securityCredentialsUrl = StsConfig.getInstance().getSecurityCredentialsUrl(); try { HttpRestResult result = HttpClientManager.getInstance().getNacosRestTemplate() .get(securityCredentialsUrl, Header.EMPTY, Query.EMPTY, String.class); if (!result.ok()) { LOGGER.error( "can not get security credentials, securityCredentialsUrl: {}, responseCode: {}, response: {}", securityCredentialsUrl, result.getCode(), result.getMessage()); throw new NacosRuntimeException(NacosException.SERVER_ERROR, "can not get security credentials, responseCode: " + result.getCode() + ", response: " + result .getMessage()); } return result.getData(); } catch (Exception e) { LOGGER.error("can not get security credentials", e); throw new NacosRuntimeException(NacosException.SERVER_ERROR, e); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/injector/AbstractResourceInjector.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import com.alibaba.nacos.client.auth.ram.RamContext; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; /** * Abstract aliyun RAM resource injector. * * @author xiweng.yy */ public abstract class AbstractResourceInjector { /** * Generate and inject resource into context. Default impl will do nothing. * * @param resource request resource * @param context ram context * @param result the result identity context */ public void doInject(RequestResource resource, RamContext context, LoginIdentityContext result) { } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/injector/AiResourceInjector.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import com.alibaba.nacos.client.auth.ram.RamConstants; import com.alibaba.nacos.client.auth.ram.RamContext; import com.alibaba.nacos.client.auth.ram.identify.IdentifyConstants; import com.alibaba.nacos.client.auth.ram.identify.StsConfig; import com.alibaba.nacos.client.auth.ram.identify.StsCredential; import com.alibaba.nacos.client.auth.ram.identify.StsCredentialHolder; import com.alibaba.nacos.client.auth.ram.utils.CalculateV4SigningKeyUtil; import com.alibaba.nacos.client.auth.ram.utils.SpasAdapter; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import java.util.Map; /** * AI module aliyun ram reousce injector. * * @author xiweng.yy */ public class AiResourceInjector extends AbstractResourceInjector { private static final String ACCESS_KEY_HEADER = "Spas-AccessKey"; @Override public void doInject(RequestResource resource, RamContext context, LoginIdentityContext result) { if (!context.validate()) { return; } String accessKey = context.getAccessKey(); String secretKey = context.getSecretKey(); if (StsConfig.getInstance().isStsOn()) { StsCredential stsCredential = StsCredentialHolder.getInstance().getStsCredential(); accessKey = stsCredential.getAccessKeyId(); secretKey = stsCredential.getAccessKeySecret(); result.setParameter(IdentifyConstants.SECURITY_TOKEN_HEADER, stsCredential.getSecurityToken()); } result.setParameter(ACCESS_KEY_HEADER, accessKey); String signatureKey = secretKey; if (StringUtils.isNotEmpty(context.getRegionId())) { signatureKey = CalculateV4SigningKeyUtil.finalSigningKeyStringWithDefaultInfo(secretKey, context.getRegionId()); result.setParameter(RamConstants.SIGNATURE_VERSION, RamConstants.V4); } Map signHeaders = SpasAdapter.getSignHeaders(buildResourceString(resource), signatureKey); result.setParameters(signHeaders); } private String buildResourceString(RequestResource resource) { return resource.getNamespace() + "+" + resource.getGroup(); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/injector/ConfigResourceInjector.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import com.alibaba.nacos.client.auth.ram.RamConstants; import com.alibaba.nacos.client.auth.ram.RamContext; import com.alibaba.nacos.client.auth.ram.identify.IdentifyConstants; import com.alibaba.nacos.client.auth.ram.identify.StsConfig; import com.alibaba.nacos.client.auth.ram.identify.StsCredential; import com.alibaba.nacos.client.auth.ram.identify.StsCredentialHolder; import com.alibaba.nacos.client.auth.ram.utils.CalculateV4SigningKeyUtil; import com.alibaba.nacos.client.auth.ram.utils.SpasAdapter; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import java.util.Map; /** * Resource Injector for naming module. * * @author xiweng.yy */ public class ConfigResourceInjector extends AbstractResourceInjector { private static final String ACCESS_KEY_HEADER = "Spas-AccessKey"; private static final String DEFAULT_RESOURCE = ""; @Override public void doInject(RequestResource resource, RamContext context, LoginIdentityContext result) { String accessKey = context.getAccessKey(); String secretKey = context.getSecretKey(); // STS 临时凭证鉴权的优先级高于 AK/SK 鉴权 if (StsConfig.getInstance().isStsOn()) { StsCredential stsCredential = StsCredentialHolder.getInstance().getStsCredential(); accessKey = stsCredential.getAccessKeyId(); secretKey = stsCredential.getAccessKeySecret(); result.setParameter(IdentifyConstants.SECURITY_TOKEN_HEADER, stsCredential.getSecurityToken()); } if (StringUtils.isNotEmpty(accessKey) && StringUtils.isNotBlank(secretKey)) { result.setParameter(ACCESS_KEY_HEADER, accessKey); } String signatureKey = secretKey; if (StringUtils.isNotEmpty(context.getRegionId())) { signatureKey = CalculateV4SigningKeyUtil .finalSigningKeyStringWithDefaultInfo(secretKey, context.getRegionId()); result.setParameter(RamConstants.SIGNATURE_VERSION, RamConstants.V4); } Map signHeaders = SpasAdapter .getSignHeaders(getResource(resource.getNamespace(), resource.getGroup()), signatureKey); result.setParameters(signHeaders); } private String getResource(String tenant, String group) { if (StringUtils.isBlank(tenant)) { if (StringUtils.isBlank(group)) { return DEFAULT_RESOURCE; } else { return group; } } else { return tenant + "+" + group; } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/injector/LockResourceInjector.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import com.alibaba.nacos.client.auth.ram.RamContext; import com.alibaba.nacos.client.auth.ram.identify.IdentifyConstants; import com.alibaba.nacos.client.auth.ram.identify.StsConfig; import com.alibaba.nacos.client.auth.ram.identify.StsCredential; import com.alibaba.nacos.client.auth.ram.identify.StsCredentialHolder; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; /** * lock resource injector. * * @author 985492783@qq.com * @date 2023/9/17 1:10 */ public class LockResourceInjector extends AbstractResourceInjector { private static final String AK_FIELD = "ak"; @Override public void doInject(RequestResource resource, RamContext context, LoginIdentityContext result) { String accessKey = context.getAccessKey(); String secretKey = context.getSecretKey(); // STS 临时凭证鉴权的优先级高于 AK/SK 鉴权 if (StsConfig.getInstance().isStsOn()) { StsCredential stsCredential = StsCredentialHolder.getInstance().getStsCredential(); accessKey = stsCredential.getAccessKeyId(); secretKey = stsCredential.getAccessKeySecret(); result.setParameter(IdentifyConstants.SECURITY_TOKEN_HEADER, stsCredential.getSecurityToken()); } if (StringUtils.isNotEmpty(accessKey) && StringUtils.isNotBlank(secretKey)) { result.setParameter(AK_FIELD, accessKey); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/injector/NamingResourceInjector.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.client.auth.ram.RamConstants; import com.alibaba.nacos.client.auth.ram.RamContext; import com.alibaba.nacos.client.auth.ram.identify.IdentifyConstants; import com.alibaba.nacos.client.auth.ram.identify.StsConfig; import com.alibaba.nacos.client.auth.ram.identify.StsCredential; import com.alibaba.nacos.client.auth.ram.identify.StsCredentialHolder; import com.alibaba.nacos.client.auth.ram.utils.CalculateV4SigningKeyUtil; import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Resource Injector for naming module. * * @author xiweng.yy */ public class NamingResourceInjector extends AbstractResourceInjector { private static final Logger LOGGER = LoggerFactory.getLogger(NamingResourceInjector.class); private static final String SIGNATURE_FILED = "signature"; private static final String DATA_FILED = "data"; private static final String AK_FILED = "ak"; @Override public void doInject(RequestResource resource, RamContext context, LoginIdentityContext result) { if (context.validate()) { try { String accessKey = context.getAccessKey(); String secretKey = context.getSecretKey(); // STS 临时凭证鉴权的优先级高于 AK/SK 鉴权 if (StsConfig.getInstance().isStsOn()) { StsCredential stsCredential = StsCredentialHolder.getInstance().getStsCredential(); accessKey = stsCredential.getAccessKeyId(); secretKey = stsCredential.getAccessKeySecret(); result.setParameter(IdentifyConstants.SECURITY_TOKEN_HEADER, stsCredential.getSecurityToken()); } String signatureKey = secretKey; if (StringUtils.isNotEmpty(context.getRegionId())) { signatureKey = CalculateV4SigningKeyUtil .finalSigningKeyStringWithDefaultInfo(secretKey, context.getRegionId()); result.setParameter(RamConstants.SIGNATURE_VERSION, RamConstants.V4); } String signData = getSignData(getGroupedServiceName(resource)); String signature = SignUtil.sign(signData, signatureKey); result.setParameter(SIGNATURE_FILED, signature); result.setParameter(DATA_FILED, signData); result.setParameter(AK_FILED, accessKey); } catch (Exception e) { LOGGER.error("inject ak/sk failed.", e); } } } private String getGroupedServiceName(RequestResource resource) { if (resource.getResource().contains(Constants.SERVICE_INFO_SPLITER) || StringUtils .isBlank(resource.getGroup())) { return resource.getResource(); } return NamingUtils.getGroupedNameOptional(resource.getResource(), resource.getGroup()); } private String getSignData(String serviceName) { return StringUtils.isNotEmpty(serviceName) ? System.currentTimeMillis() + Constants.SERVICE_INFO_SPLITER + serviceName : String.valueOf(System.currentTimeMillis()); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/utils/CalculateV4SigningKeyUtil.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import com.alibaba.nacos.client.auth.ram.RamConstants; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.Base64; /** * CalculateV4SigningKeyUtil. * * @author xiweng.yy */ public class CalculateV4SigningKeyUtil { private static final String PREFIX = "aliyun_v4"; private static final String CONSTANT = "aliyun_v4_request"; private static final DateTimeFormatter V4_SIGN_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd"); private static final ZoneId UTC_0 = ZoneId.of("GMT+00:00"); private static byte[] firstSigningKey(String secret, String date, String signMethod) throws NoSuchAlgorithmException, InvalidKeyException { Mac mac = Mac.getInstance(signMethod); mac.init(new SecretKeySpec((PREFIX + secret).getBytes(StandardCharsets.UTF_8), signMethod)); return mac.doFinal(date.getBytes(StandardCharsets.UTF_8)); } private static byte[] regionSigningKey(String secret, String date, String region, String signMethod) throws NoSuchAlgorithmException, InvalidKeyException { byte[] firstSignkey = firstSigningKey(secret, date, signMethod); Mac mac = Mac.getInstance(signMethod); mac.init(new SecretKeySpec(firstSignkey, signMethod)); return mac.doFinal(region.getBytes(StandardCharsets.UTF_8)); } private static byte[] finalSigningKey(String secret, String date, String region, String productCode, String signMethod) { try { byte[] secondSignkey = regionSigningKey(secret, date, region, signMethod); Mac mac = Mac.getInstance(signMethod); mac.init(new SecretKeySpec(secondSignkey, signMethod)); byte[] thirdSigningKey = mac.doFinal(productCode.getBytes(StandardCharsets.UTF_8)); // 计算最终派生秘钥 mac = Mac.getInstance(signMethod); mac.init(new SecretKeySpec(thirdSigningKey, signMethod)); return mac.doFinal(CONSTANT.getBytes(StandardCharsets.UTF_8)); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("unsupported Algorithm:" + signMethod); } catch (InvalidKeyException e) { throw new RuntimeException("InvalidKey"); } } /** * Return V4 signature key with base64 encode. * * @param secret secret key * @param date date with utc format, like 20211222 * @param region region id * @param productCode cloud product code * @param signMethod sign method * @return V4 signature key with base64 encode */ public static String finalSigningKeyString(String secret, String date, String region, String productCode, String signMethod) { return Base64.getEncoder().encodeToString(finalSigningKey(secret, date, region, productCode, signMethod)); } /** * Return V4 signature key with base64 encode for some default information. * *
  • *
      date = current date
    *
      produceCode = mse
    *
      signMethod = HMAC-SHA256
    *
  • * * @param secret secret key * @param region region id * @return V4 signature key with base64 encode */ public static String finalSigningKeyStringWithDefaultInfo(String secret, String region) { String signDate = LocalDateTime.now(UTC_0).format(V4_SIGN_DATE_FORMATTER); return finalSigningKeyString(secret, signDate, region, RamConstants.SIGNATURE_V4_PRODUCE, RamConstants.SIGNATURE_V4_METHOD); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/utils/RamUtil.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Properties; /** * Util to get ram info, such as AK, SK and RAM role. * * @author xiweng.yy */ public class RamUtil { public static String getAccessKey(Properties properties) { boolean isUseRamInfoParsing = Boolean.parseBoolean(properties .getProperty(PropertyKeyConst.IS_USE_RAM_INFO_PARSING, System.getProperty(SystemPropertyKeyConst.IS_USE_RAM_INFO_PARSING, Constants.DEFAULT_USE_RAM_INFO_PARSING))); String result = properties.getProperty(PropertyKeyConst.ACCESS_KEY); if (isUseRamInfoParsing && StringUtils.isBlank(result)) { result = SpasAdapter.getAk(); } return result; } public static String getSecretKey(Properties properties) { boolean isUseRamInfoParsing = Boolean.parseBoolean(properties .getProperty(PropertyKeyConst.IS_USE_RAM_INFO_PARSING, System.getProperty(SystemPropertyKeyConst.IS_USE_RAM_INFO_PARSING, Constants.DEFAULT_USE_RAM_INFO_PARSING))); String result = properties.getProperty(PropertyKeyConst.SECRET_KEY); if (isUseRamInfoParsing && StringUtils.isBlank(result)) { result = SpasAdapter.getSk(); } return result; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/utils/SignUtil.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import com.alibaba.nacos.common.codec.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; /** * Sign util. * * @author pbting * @date 2019-01-22 10:20 PM */ public class SignUtil { private static final Charset UTF8 = StandardCharsets.UTF_8; /** * Sign. * * @param data data * @param key key * @return signature * @throws Exception exception */ public static String sign(String data, String key) throws Exception { try { byte[] signature = sign(data.getBytes(UTF8), key.getBytes(UTF8), SignUtil.SigningAlgorithm.HmacSHA1); return new String(Base64.encodeBase64(signature), UTF8); } catch (Exception ex) { throw new Exception("Unable to calculate a request signature: " + ex.getMessage(), ex); } } static byte[] sign(byte[] data, byte[] key, SignUtil.SigningAlgorithm algorithm) throws Exception { try { Mac mac = Mac.getInstance(algorithm.toString()); mac.init(new SecretKeySpec(key, algorithm.toString())); return mac.doFinal(data); } catch (Exception ex) { throw new Exception("Unable to calculate a request signature: " + ex.getMessage(), ex); } } public enum SigningAlgorithm { // Hmac SHA1 algorithm HmacSHA1; SigningAlgorithm() { } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/auth/ram/utils/SpasAdapter.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.client.auth.ram.identify.CredentialService; import com.alibaba.nacos.common.codec.Base64; import com.alibaba.nacos.common.utils.StringUtils; import javax.crypto.Mac; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import java.util.HashMap; import java.util.Map; /** * adapt spas interface. * * @author Nacos */ public class SpasAdapter { private static final String TIMESTAMP_HEADER = "Timestamp"; private static final String SIGNATURE_HEADER = "Spas-Signature"; private static final String GROUP_KEY = "group"; public static final String TENANT_KEY = "tenant"; private static final String SHA_ENCRYPT = "HmacSHA1"; public static Map getSignHeaders(String resource, String secretKey) { Map header = new HashMap<>(2); String timeStamp = String.valueOf(System.currentTimeMillis()); header.put(TIMESTAMP_HEADER, timeStamp); if (secretKey != null) { String signature; if (StringUtils.isBlank(resource)) { signature = signWithHmacSha1Encrypt(timeStamp, secretKey); } else { signature = signWithHmacSha1Encrypt(resource + "+" + timeStamp, secretKey); } header.put(SIGNATURE_HEADER, signature); } return header; } public static Map getSignHeaders(String groupKey, String tenant, String secretKey) { if (StringUtils.isBlank(groupKey) && StringUtils.isBlank(tenant)) { return null; } String resource = ""; if (StringUtils.isNotBlank(groupKey) && StringUtils.isNotBlank(tenant)) { resource = tenant + "+" + groupKey; } else { if (!StringUtils.isBlank(groupKey)) { resource = groupKey; } } return getSignHeaders(resource, secretKey); } public static Map getSignHeaders(Map paramValues, String secretKey) { if (null == paramValues) { return null; } String resource = ""; if (paramValues.containsKey(TENANT_KEY) && paramValues.containsKey(GROUP_KEY)) { resource = paramValues.get(TENANT_KEY) + "+" + paramValues.get(GROUP_KEY); } else { if (!StringUtils.isBlank(paramValues.get(GROUP_KEY))) { resource = paramValues.get(GROUP_KEY); } } return getSignHeaders(resource, secretKey); } public static String getSk() { return CredentialService.getInstance().getCredential().getSecretKey(); } public static String getAk() { return CredentialService.getInstance().getCredential().getAccessKey(); } public static void freeCredentialInstance() { CredentialService.freeInstance(); } /** * Sign with hmac SHA1 encrtpt. * * @param encryptText encrypt text * @param encryptKey encrypt key * @return base64 string */ public static String signWithHmacSha1Encrypt(String encryptText, String encryptKey) { try { byte[] data = encryptKey.getBytes(Constants.ENCODE); // Construct a key according to the given byte array, and the second parameter specifies the name of a key algorithm SecretKey secretKey = new SecretKeySpec(data, SHA_ENCRYPT); // Generate a Mac object specifying Mac algorithm Mac mac = Mac.getInstance(SHA_ENCRYPT); // Initialize the Mac object with the given key mac.init(secretKey); byte[] text = encryptText.getBytes(Constants.ENCODE); byte[] textFinal = mac.doFinal(text); // Complete Mac operation, base64 encoding, convert byte array to string return new String(Base64.encodeBase64(textFinal), Constants.ENCODE); } catch (Exception e) { throw new RuntimeException("signWithhmacSHA1Encrypt fail", e); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/constant/Constants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.constant; import java.util.concurrent.TimeUnit; /** * All the constants. * * @author onew */ public class Constants { public static class SysEnv { public static final String USER_HOME = "user.home"; public static final String PROJECT_NAME = "project.name"; public static final String JM_LOG_PATH = "JM.LOG.PATH"; public static final String JM_SNAPSHOT_PATH = "JM.SNAPSHOT.PATH"; public static final String NACOS_ENV_FIRST = "nacos.env.first"; } public static class Security { public static final long SECURITY_INFO_REFRESH_INTERVAL_MILLS = TimeUnit.SECONDS.toMillis(5); } public static class Address { public static final int ENDPOINT_SERVER_LIST_PROVIDER_ORDER = 500; public static final int ADDRESS_SERVER_LIST_PROVIDER_ORDER = 499; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/AbstractPropertySource.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import java.util.Properties; abstract class AbstractPropertySource { /** * get property's type. * @return name */ abstract SourceType getType(); /** * get property, if the value can not be got by the special key, the null will be returned. * @param key special key * @return value or null */ abstract String getProperty(String key); /** * Tests if the specified object is a key in this propertySource. * @param key key – possible key * @return true if and only if the specified object is a key in this propertySource, false otherwise. */ abstract boolean containsKey(String key); /** * to properties. * @return properties */ abstract Properties asProperties(); } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/JvmArgsPropertySource.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import java.util.Properties; class JvmArgsPropertySource extends AbstractPropertySource { private final Properties properties; JvmArgsPropertySource() { this.properties = System.getProperties(); } @Override SourceType getType() { return SourceType.JVM; } @Override String getProperty(String key) { return properties.getProperty(key); } @Override boolean containsKey(String key) { return properties.containsKey(key); } @Override Properties asProperties() { Properties properties = new Properties(); properties.putAll(this.properties); return properties; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/NacosClientProperties.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import java.util.Properties; /** * NacosClientProperties interface. include all the properties from jvm args, system environment, default setting. more * details you can see https://github.com/alibaba/nacos/issues/8622 * * @author onewe */ public interface NacosClientProperties { /** * all the NacosClientProperties object must be created by PROTOTYPE, so child NacosClientProperties can read * properties from the PROTOTYPE. it looks like this: |-PROTOTYPE----------------> ip=127.0.0.1 * |---|-child1---------------> port=6379 if you search key called "port" from child1, certainly you will get 6379 * if you search key called "ip" from child1, you will get 127.0.0.1. because the child can read properties from * parent NacosClientProperties */ NacosClientProperties PROTOTYPE = SearchableProperties.INSTANCE; /** * get property, if the value can not be got by the special key, the null will be returned. * * @param key special key * @return string value or null. */ String getProperty(String key); /** * get property, if the value can not be got by the special key, the default value will be returned. * * @param key special key * @param defaultValue default value * @return string value or default value. */ String getProperty(String key, String defaultValue); /** * get property from special property source. * * @param source source type * @param key special key * @return string value or null. * @see SourceType */ String getPropertyFrom(SourceType source, String key); /** * get property from special property source. * * @param source source type * @return string value or null. * @see SourceType */ Properties getProperties(SourceType source); /** * get boolean, if the value can not be got by the special key, the null will be returned. * * @param key special key * @return boolean value or null. */ Boolean getBoolean(String key); /** * get boolean, if the value can not be got by the special key, the default value will be returned. * * @param key special key * @param defaultValue default value * @return boolean value or defaultValue. */ Boolean getBoolean(String key, Boolean defaultValue); /** * get integer, if the value can not be got by the special key, the null will be returned. * * @param key special key * @return integer value or null */ Integer getInteger(String key); /** * get integer, if the value can not be got by the special key, the default value will be returned. * * @param key special key * @param defaultValue default value * @return integer value or default value */ Integer getInteger(String key, Integer defaultValue); /** * get long, if the value can not be got by the special key, the null will be returned. * * @param key special key * @return long value or null */ Long getLong(String key); /** * get long, if the value can not be got by the special key, the default value will be returned. * * @param key special key * @param defaultValue default value * @return long value or default value */ Long getLong(String key, Long defaultValue); /** * set property. * * @param key key * @param value value */ void setProperty(String key, String value); /** * add properties. * * @param properties properties */ void addProperties(Properties properties); /** * Tests if the specified object is a key in this NacosClientProperties. * * @param key key – possible key * @return true if and only if the specified object is a key in this NacosClientProperties, false otherwise. */ boolean containsKey(String key); /** * get properties from NacosClientProperties. * * @return properties */ Properties asProperties(); /** * create a new NacosClientProperties which scope is itself. * * @return NacosClientProperties */ NacosClientProperties derive(); /** * create a new NacosClientProperties from NacosClientProperties#PROTOTYPE and init. * * @param properties properties * @return NacosClientProperties */ NacosClientProperties derive(Properties properties); } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/PropertiesPropertySource.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.Properties; class PropertiesPropertySource extends AbstractPropertySource { private final Properties properties = new Properties(); private final PropertiesPropertySource parent; PropertiesPropertySource() { this.parent = null; } PropertiesPropertySource(PropertiesPropertySource parent) { this.parent = parent; } @Override SourceType getType() { return SourceType.PROPERTIES; } @Override String getProperty(String key) { return getProperty(this, key); } private String getProperty(PropertiesPropertySource propertiesPropertySource, String key) { final String value = propertiesPropertySource.properties.getProperty(key); if (value != null) { return value; } final PropertiesPropertySource parent = propertiesPropertySource.parent; if (parent == null) { return null; } return getProperty(parent, key); } @Override boolean containsKey(String key) { return containsKey(this, key); } boolean containsKey(PropertiesPropertySource propertiesPropertySource, String key) { final boolean exist = propertiesPropertySource.properties.containsKey(key); if (exist) { return true; } final PropertiesPropertySource parent = propertiesPropertySource.parent; if (parent == null) { return false; } return containsKey(parent, key); } @Override Properties asProperties() { List propertiesList = new ArrayList<>(8); propertiesList = lookForProperties(this, propertiesList); Properties ret = new Properties(); final ListIterator iterator = propertiesList.listIterator(propertiesList.size()); while (iterator.hasPrevious()) { final Properties properties = iterator.previous(); ret.putAll(properties); } return ret; } List lookForProperties(PropertiesPropertySource propertiesPropertySource, List propertiesList) { propertiesList.add(propertiesPropertySource.properties); final PropertiesPropertySource parent = propertiesPropertySource.parent; if (parent == null) { return propertiesList; } return lookForProperties(parent, propertiesList); } synchronized void setProperty(String key, String value) { properties.setProperty(key, value); } synchronized void addProperties(Properties source) { properties.putAll(source); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/SearchableProperties.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import com.alibaba.nacos.client.constant.Constants; import com.alibaba.nacos.client.env.convert.CompositeConverter; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Optional; import java.util.Properties; import java.util.stream.Collectors; /** * Searchable NacosClientProperties. the SearchableProperties that it can be specified search order by nacos.env.first * * @author onewe */ class SearchableProperties implements NacosClientProperties { private static final Logger LOGGER = LoggerFactory.getLogger(SearchableProperties.class); private static final JvmArgsPropertySource JVM_ARGS_PROPERTY_SOURCE = new JvmArgsPropertySource(); private static final SystemEnvPropertySource SYSTEM_ENV_PROPERTY_SOURCE = new SystemEnvPropertySource(); private static final List SEARCH_ORDER; private static final CompositeConverter CONVERTER = new CompositeConverter(); static { SEARCH_ORDER = init(); StringBuilder orderInfo = new StringBuilder("properties search order:"); for (int i = 0; i < SEARCH_ORDER.size(); i++) { orderInfo.append(SEARCH_ORDER.get(i).toString()); if (i < SEARCH_ORDER.size() - 1) { orderInfo.append("->"); } } LOGGER.debug(orderInfo.toString()); } private static List init() { List initOrder = Arrays.asList(SourceType.PROPERTIES, SourceType.JVM, SourceType.ENV); String firstEnv = JVM_ARGS_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); if (StringUtils.isBlank(firstEnv)) { firstEnv = SYSTEM_ENV_PROPERTY_SOURCE.getProperty(Constants.SysEnv.NACOS_ENV_FIRST); } if (StringUtils.isNotBlank(firstEnv)) { try { final SourceType sourceType = SourceType.valueOf(firstEnv.toUpperCase()); if (!sourceType.equals(SourceType.PROPERTIES)) { final int index = initOrder.indexOf(sourceType); final SourceType replacedSourceType = initOrder.set(0, sourceType); initOrder.set(index, replacedSourceType); } } catch (Exception e) { LOGGER.warn("first source type parse error, it will be used default order!", e); } } return initOrder; } static final SearchableProperties INSTANCE = new SearchableProperties(); private final List propertySources; private final PropertiesPropertySource propertiesPropertySource; private SearchableProperties() { this(new PropertiesPropertySource()); } private SearchableProperties(PropertiesPropertySource propertiesPropertySource) { this.propertiesPropertySource = propertiesPropertySource; this.propertySources = build(propertiesPropertySource, JVM_ARGS_PROPERTY_SOURCE, SYSTEM_ENV_PROPERTY_SOURCE); } @Override public String getProperty(String key) { return getProperty(key, null); } @Override public String getProperty(String key, String defaultValue) { return this.search(key, String.class).orElse(defaultValue); } @Override public String getPropertyFrom(SourceType source, String key) { if (source == null) { return this.getProperty(key); } switch (source) { case JVM: return JVM_ARGS_PROPERTY_SOURCE.getProperty(key); case ENV: return SYSTEM_ENV_PROPERTY_SOURCE.getProperty(key); case PROPERTIES: return this.propertiesPropertySource.getProperty(key); default: return this.getProperty(key); } } @Override public Properties getProperties(SourceType source) { if (source == null) { return null; } switch (source) { case JVM: return JVM_ARGS_PROPERTY_SOURCE.asProperties(); case ENV: return SYSTEM_ENV_PROPERTY_SOURCE.asProperties(); case PROPERTIES: return this.propertiesPropertySource.asProperties(); default: return null; } } @Override public Boolean getBoolean(String key) { return getBoolean(key, null); } @Override public Boolean getBoolean(String key, Boolean defaultValue) { return this.search(key, Boolean.class).orElse(defaultValue); } @Override public Integer getInteger(String key) { return getInteger(key, null); } @Override public Integer getInteger(String key, Integer defaultValue) { return this.search(key, Integer.class).orElse(defaultValue); } @Override public Long getLong(String key) { return getLong(key, null); } @Override public Long getLong(String key, Long defaultValue) { return this.search(key, Long.class).orElse(defaultValue); } @Override public void setProperty(String key, String value) { propertiesPropertySource.setProperty(key, value); } @Override public void addProperties(Properties properties) { propertiesPropertySource.addProperties(properties); } @Override public Properties asProperties() { Properties properties = new Properties(); final ListIterator iterator = propertySources.listIterator(propertySources.size()); while (iterator.hasPrevious()) { final AbstractPropertySource previous = iterator.previous(); properties.putAll(previous.asProperties()); } return properties; } @Override public boolean containsKey(String key) { for (AbstractPropertySource propertySource : propertySources) { final boolean containing = propertySource.containsKey(key); if (containing) { return true; } } return false; } private Optional search(String key, Class targetType) { for (AbstractPropertySource propertySource : propertySources) { final String value = propertySource.getProperty(key); if (value != null) { if (targetType.isAssignableFrom(String.class)) { return (Optional) Optional.of(value); } return Optional.ofNullable(CONVERTER.convert(value, targetType)); } } return Optional.empty(); } private List build(AbstractPropertySource... propertySources) { final Map sourceMap = Arrays.stream(propertySources) .collect(Collectors.toMap(AbstractPropertySource::getType, propertySource -> propertySource)); return SEARCH_ORDER.stream().map(sourceMap::get).collect(Collectors.toList()); } @Override public NacosClientProperties derive() { return new SearchableProperties(new PropertiesPropertySource(this.propertiesPropertySource)); } @Override public NacosClientProperties derive(Properties properties) { final NacosClientProperties nacosClientProperties = this.derive(); nacosClientProperties.addProperties(properties); return nacosClientProperties; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/SourceType.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; /** * properties source type enum. * @author onewe */ public enum SourceType { /** * get value from properties. */ PROPERTIES, /** * get value from jvm args. */ JVM, /** * get value from system environment. */ ENV, /** * get value from unknown environment, will be search in all properties by orders. */ UNKNOWN } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/SystemEnvPropertySource.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import java.util.Map; import java.util.Properties; class SystemEnvPropertySource extends AbstractPropertySource { private final Map env = System.getenv(); @Override SourceType getType() { return SourceType.ENV; } @Override String getProperty(String key) { String checkedKey = checkPropertyName(key); if (checkedKey == null) { final String upperCaseKey = key.toUpperCase(); if (!upperCaseKey.equals(key)) { checkedKey = checkPropertyName(upperCaseKey); } } if (checkedKey == null) { return null; } return env.get(checkedKey); } /** * copy from https://github.com/spring-projects/spring-framework.git * Copyright 2002-2021 the original author or authors. * Since: * 3.1 * Author: * Chris Beams, Juergen Hoeller */ private String checkPropertyName(String name) { // Check name as-is if (containsKey(name)) { return name; } // Check name with just dots replaced String noDotName = name.replace('.', '_'); if (!name.equals(noDotName) && containsKey(noDotName)) { return noDotName; } // Check name with just hyphens replaced String noHyphenName = name.replace('-', '_'); if (!name.equals(noHyphenName) && containsKey(noHyphenName)) { return noHyphenName; } // Check name with dots and hyphens replaced String noDotNoHyphenName = noDotName.replace('-', '_'); if (!noDotName.equals(noDotNoHyphenName) && containsKey(noDotNoHyphenName)) { return noDotNoHyphenName; } // Give up return null; } @Override boolean containsKey(String name) { return this.env.containsKey(name); } @Override Properties asProperties() { Properties properties = new Properties(); properties.putAll(this.env); return properties; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/convert/AbstractPropertyConverter.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env.convert; abstract class AbstractPropertyConverter { /** * convert property to target object. * @param property the property gets from environments * @return target object */ abstract T convert(String property); } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/convert/BooleanConverter.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env.convert; import com.alibaba.nacos.common.utils.StringUtils; import java.util.HashSet; import java.util.Set; class BooleanConverter extends AbstractPropertyConverter { private static final Set TRUE_VALUES = new HashSet<>(8); private static final Set FALSE_VALUES = new HashSet<>(8); static { TRUE_VALUES.add("true"); TRUE_VALUES.add("on"); TRUE_VALUES.add("yes"); TRUE_VALUES.add("1"); FALSE_VALUES.add("false"); FALSE_VALUES.add("off"); FALSE_VALUES.add("no"); FALSE_VALUES.add("0"); } @Override Boolean convert(String property) { if (StringUtils.isEmpty(property)) { return null; } property = property.toLowerCase(); if (TRUE_VALUES.contains(property)) { return Boolean.TRUE; } else if (FALSE_VALUES.contains(property)) { return Boolean.FALSE; } else { throw new IllegalArgumentException("Invalid boolean value '" + property + "'"); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/convert/CompositeConverter.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env.convert; import java.util.HashMap; import java.util.Map; import java.util.MissingFormatArgumentException; /** * default converters. * @author onewe */ public class CompositeConverter { private final Map, AbstractPropertyConverter> converterRegistry = new HashMap<>(); public CompositeConverter() { converterRegistry.put(Boolean.class, new BooleanConverter()); converterRegistry.put(Integer.class, new IntegerConverter()); converterRegistry.put(Long.class, new LongConverter()); } /** * convert property to target type. * @param property the property gets from environments * @param targetClass target class object * @param target type * @return the object of target type */ public T convert(String property, Class targetClass) { final AbstractPropertyConverter converter = converterRegistry.get(targetClass); if (converter == null) { throw new MissingFormatArgumentException("converter not found, can't convert from String to " + targetClass.getCanonicalName()); } return (T) converter.convert(property); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/convert/IntegerConverter.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env.convert; import com.alibaba.nacos.common.utils.StringUtils; class IntegerConverter extends AbstractPropertyConverter { @Override Integer convert(String property) { if (StringUtils.isEmpty(property)) { return null; } try { return Integer.valueOf(property); } catch (Exception e) { throw new IllegalArgumentException("Cannot convert String [" + property + "] to Integer"); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/env/convert/LongConverter.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env.convert; import com.alibaba.nacos.common.utils.StringUtils; class LongConverter extends AbstractPropertyConverter { @Override Long convert(String property) { if (StringUtils.isEmpty(property)) { return null; } try { return Long.valueOf(property); } catch (Exception e) { throw new IllegalArgumentException("Cannot convert String [" + property + "] to Long"); } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/remote/HttpClientManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.http.AbstractHttpClientFactory; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.utils.ExceptionUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * http Manager. * * @author Nacos */ public class HttpClientManager implements Closeable { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientManager.class); private static final HttpClientFactory HTTP_CLIENT_FACTORY = new HttpClientFactory(); private static final int CON_TIME_OUT_MILLIS = 1000; private static final int READ_TIME_OUT_MILLIS = 3000; private static class HttpClientManagerInstance { private static final HttpClientManager INSTANCE = new HttpClientManager(); } public static HttpClientManager getInstance() { return HttpClientManagerInstance.INSTANCE; } @Override public void shutdown() throws NacosException { LOGGER.info("[HttpClientManager] Start destroying NacosRestTemplate"); try { HttpClientBeanHolder.shutdownNacosSyncRest(HTTP_CLIENT_FACTORY.getClass().getName()); } catch (Exception ex) { LOGGER.error("[HttpClientManager] An exception occurred when the HTTP client was closed : {}", ExceptionUtil.getStackTrace(ex)); } LOGGER.info("[HttpClientManager] Completed destruction of NacosRestTemplate"); } /** * get connectTimeout. * * @param connectTimeout connectTimeout * @return int return max timeout */ public int getConnectTimeoutOrDefault(int connectTimeout) { return Math.max(CON_TIME_OUT_MILLIS, connectTimeout); } /** * get NacosRestTemplate Instance. * * @return NacosRestTemplate */ public NacosRestTemplate getNacosRestTemplate() { return HttpClientBeanHolder.getNacosRestTemplate(HTTP_CLIENT_FACTORY); } /** * HttpClientFactory. */ private static class HttpClientFactory extends AbstractHttpClientFactory { @Override protected HttpClientConfig buildHttpClientConfig() { return HttpClientConfig.builder().setConTimeOutMillis(CON_TIME_OUT_MILLIS) .setReadTimeOutMillis(READ_TIME_OUT_MILLIS).build(); } @Override protected Logger assignLogger() { return LOGGER; } } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/utils/AppNameUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.client.constant.Constants; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.utils.StringUtils; import java.io.File; /** * appName util. * * @author Nacos */ public class AppNameUtils { private static final String PARAM_MARKING_JBOSS = "jboss.server.home.dir"; private static final String PARAM_MARKING_JETTY = "jetty.home"; private static final String PARAM_MARKING_TOMCAT = "catalina.base"; private static final String LINUX_ADMIN_HOME = "/home/admin/"; private static final String SERVER_JBOSS = "jboss"; private static final String SERVER_JETTY = "jetty"; private static final String SERVER_TOMCAT = "tomcat"; private static final String SERVER_UNKNOWN = "unknown server"; private static final String DEFAULT_APP_NAME = "unknown"; public static String getAppName() { String appName; appName = getAppNameByProjectName(); if (appName != null) { return appName; } appName = getAppNameByServerHome(); if (appName != null) { return appName; } return DEFAULT_APP_NAME; } private static String getAppNameByProjectName() { return NacosClientProperties.PROTOTYPE.getProperty(Constants.SysEnv.PROJECT_NAME); } private static String getAppNameByServerHome() { String serverHome = null; if (SERVER_JBOSS.equals(getServerType())) { serverHome = NacosClientProperties.PROTOTYPE.getProperty(PARAM_MARKING_JBOSS); } else if (SERVER_JETTY.equals(getServerType())) { serverHome = NacosClientProperties.PROTOTYPE.getProperty(PARAM_MARKING_JETTY); } else if (SERVER_TOMCAT.equals(getServerType())) { serverHome = NacosClientProperties.PROTOTYPE.getProperty(PARAM_MARKING_TOMCAT); } if (serverHome != null && serverHome.startsWith(LINUX_ADMIN_HOME)) { return StringUtils.substringBetween(serverHome, LINUX_ADMIN_HOME, File.separator); } return null; } private static String getServerType() { String serverType; if (NacosClientProperties.PROTOTYPE.getProperty(PARAM_MARKING_JBOSS) != null) { serverType = SERVER_JBOSS; } else if (NacosClientProperties.PROTOTYPE.getProperty(PARAM_MARKING_JETTY) != null) { serverType = SERVER_JETTY; } else if (NacosClientProperties.PROTOTYPE.getProperty(PARAM_MARKING_TOMCAT) != null) { serverType = SERVER_TOMCAT; } else { serverType = SERVER_UNKNOWN; } return serverType; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/utils/ClientBasicParamUtil.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.VersionUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Properties; import java.util.regex.Pattern; /** * Nacos client basic parameters utils. * * @author xiweng.yy */ public class ClientBasicParamUtil { private static final Logger LOGGER = LoggerFactory.getLogger(ClientBasicParamUtil.class); private static final Pattern PATTERN = Pattern.compile("\\$\\{[^}]+\\}"); private static final int DESENSITISE_PARAMETER_MIN_LENGTH = 2; private static final int DESENSITISE_PARAMETER_KEEP_ONE_CHAR_LENGTH = 8; private static final String NACOS_CLIENT_APP_KEY = "nacos.client.appKey"; private static final String NACOS_CLIENT_CONTEXT_PATH_KEY = "nacos.client.contextPath"; private static final String DEFAULT_NACOS_CLIENT_CONTEXT_PATH = "nacos"; private static final String NACOS_SERVER_PORT_KEY = "nacos.server.port"; private static final String DEFAULT_SERVER_PORT = "8848"; private static final String BLANK_STR = ""; private static String defaultContextPath; private static String appKey; private static String clientVersion = "unknown"; private static String serverPort; private static String defaultNodesPath = "serverlist"; static { // Client identity information appKey = NacosClientProperties.PROTOTYPE.getProperty(NACOS_CLIENT_APP_KEY, BLANK_STR); defaultContextPath = NacosClientProperties.PROTOTYPE.getProperty(NACOS_CLIENT_CONTEXT_PATH_KEY, DEFAULT_NACOS_CLIENT_CONTEXT_PATH); serverPort = NacosClientProperties.PROTOTYPE.getProperty(NACOS_SERVER_PORT_KEY, DEFAULT_SERVER_PORT); LOGGER.info("[settings] [req-serv] nacos-server port:{}", serverPort); clientVersion = VersionUtils.version; } public static String getAppKey() { return appKey; } public static void setAppKey(String appKey) { ClientBasicParamUtil.appKey = appKey; } public static String getDefaultContextPath() { return defaultContextPath; } public static void setDefaultContextPath(String defaultContextPath) { ClientBasicParamUtil.defaultContextPath = defaultContextPath; } public static String getClientVersion() { return clientVersion; } public static void setClientVersion(String clientVersion) { ClientBasicParamUtil.clientVersion = clientVersion; } public static String getDefaultServerPort() { return serverPort; } public static String getDefaultNodesPath() { return defaultNodesPath; } public static void setDefaultNodesPath(String defaultNodesPath) { ClientBasicParamUtil.defaultNodesPath = defaultNodesPath; } /** * Parse namespace from properties and environment. * * @param properties properties * @return namespace */ public static String parseNamespace(NacosClientProperties properties) { String namespaceTmp = null; String isUseCloudNamespaceParsing = properties.getProperty(PropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, properties.getProperty(SystemPropertyKeyConst.IS_USE_CLOUD_NAMESPACE_PARSING, String.valueOf(Constants.DEFAULT_USE_CLOUD_NAMESPACE_PARSING))); if (Boolean.parseBoolean(isUseCloudNamespaceParsing)) { namespaceTmp = TenantUtil.getUserTenantForAcm(); namespaceTmp = TemplateUtils.stringBlankAndThenExecute(namespaceTmp, () -> { String namespace = properties.getProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_NAMESPACE); return StringUtils.isNotBlank(namespace) ? namespace : StringUtils.EMPTY; }); } if (StringUtils.isBlank(namespaceTmp)) { namespaceTmp = properties.getProperty(PropertyKeyConst.NAMESPACE); } return StringUtils.isNotBlank(namespaceTmp) ? namespaceTmp.trim() : Constants.DEFAULT_NAMESPACE_ID; } /** * Parse end point rule. * * @param endpointUrl endpoint url * @return end point rule */ public static String parsingEndpointRule(String endpointUrl) { // If entered in the configuration file, the priority in ENV will be given priority. if (endpointUrl == null || !PATTERN.matcher(endpointUrl).find()) { // skip retrieve from system property and retrieve directly from system env String endpointUrlSource = NacosClientProperties.PROTOTYPE.getProperty( PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL); if (StringUtils.isNotBlank(endpointUrlSource)) { endpointUrl = endpointUrlSource; } return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } endpointUrl = endpointUrl.substring(endpointUrl.indexOf("${") + 2, endpointUrl.lastIndexOf("}")); int defStartOf = endpointUrl.indexOf(":"); String defaultEndpointUrl = null; if (defStartOf != -1) { defaultEndpointUrl = endpointUrl.substring(defStartOf + 1); endpointUrl = endpointUrl.substring(0, defStartOf); } String endpointUrlSource = TemplateUtils.stringBlankAndThenExecute( NacosClientProperties.PROTOTYPE.getProperty(endpointUrl), () -> NacosClientProperties.PROTOTYPE.getProperty( PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL)); if (StringUtils.isBlank(endpointUrlSource)) { if (StringUtils.isNotBlank(defaultEndpointUrl)) { endpointUrl = defaultEndpointUrl; } } else { endpointUrl = endpointUrlSource; } return StringUtils.isNotBlank(endpointUrl) ? endpointUrl : ""; } public static String getInputParameters(Properties properties) { boolean logAllParameters = ConvertUtils.toBoolean(properties.getProperty(PropertyKeyConst.LOG_ALL_PROPERTIES), false); StringBuilder result = new StringBuilder(); if (logAllParameters) { result.append( "Log nacos client init properties with Full mode, This mode is only used for debugging and troubleshooting. "); result.append( "Please close this mode by removing properties `logAllProperties` after finishing debug or troubleshoot.\n"); result.append("Nacos client all init properties: \n"); properties.forEach( (key, value) -> result.append("\t").append(key.toString()).append("=").append(value.toString()) .append("\n")); } else { result.append("Nacos client key init properties: \n"); appendKeyParameters(result, properties, PropertyKeyConst.SERVER_ADDR, false); appendKeyParameters(result, properties, PropertyKeyConst.NAMESPACE, false); appendKeyParameters(result, properties, PropertyKeyConst.ENDPOINT, false); appendKeyParameters(result, properties, PropertyKeyConst.ENDPOINT_PORT, false); appendKeyParameters(result, properties, PropertyKeyConst.USERNAME, false); appendKeyParameters(result, properties, PropertyKeyConst.PASSWORD, true); appendKeyParameters(result, properties, PropertyKeyConst.ACCESS_KEY, true); appendKeyParameters(result, properties, PropertyKeyConst.SECRET_KEY, true); appendKeyParameters(result, properties, PropertyKeyConst.RAM_ROLE_NAME, false); appendKeyParameters(result, properties, PropertyKeyConst.SIGNATURE_REGION_ID, false); } return result.toString(); } private static void appendKeyParameters(StringBuilder result, Properties properties, String propertyKey, boolean needDesensitise) { String propertyValue = properties.getProperty(propertyKey); if (StringUtils.isBlank(propertyValue)) { return; } result.append("\t").append(propertyKey).append("=") .append(needDesensitise ? desensitiseParameter(propertyValue) : propertyValue).append("\n"); } /** * Do desensitise for parameters with `*` to replace inner content. * * @param parameterValue parameter value which need be desensitised. * @return desensitised parameter value. */ public static String desensitiseParameter(String parameterValue) { if (parameterValue.length() <= DESENSITISE_PARAMETER_MIN_LENGTH) { return parameterValue; } if (parameterValue.length() < DESENSITISE_PARAMETER_KEEP_ONE_CHAR_LENGTH) { return doDesensitiseParameter(parameterValue, 1); } return doDesensitiseParameter(parameterValue, 2); } private static String doDesensitiseParameter(String parameterValue, int keepCharCount) { StringBuilder result = new StringBuilder(parameterValue); for (int i = keepCharCount; i < parameterValue.length() - keepCharCount; i++) { result.setCharAt(i, '*'); } return result.toString(); } public static String getNameSuffixByServerIps(String... serverIps) { StringBuilder sb = new StringBuilder(); String split = ""; for (String serverIp : serverIps) { sb.append(split); serverIp = serverIp.replaceAll("http(s)?://", ""); sb.append(serverIp.replaceAll(":", "_")); split = "-"; } return sb.toString(); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/utils/ContextPathUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.common.utils.StringUtils; /** * Context path Util. * * @author Wei.Wang */ public class ContextPathUtil { private static final String ROOT_WEB_CONTEXT_PATH = "/"; /** * normalize context path. * * @param contextPath origin context path * @return normalized context path */ public static String normalizeContextPath(String contextPath) { if (StringUtils.isBlank(contextPath) || ROOT_WEB_CONTEXT_PATH.equals(contextPath)) { return StringUtils.EMPTY; } return contextPath.startsWith(ROOT_WEB_CONTEXT_PATH) ? contextPath : ROOT_WEB_CONTEXT_PATH + contextPath; } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/utils/TemplateUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.Callable; /** * Template Utils. * * @author Nacos */ public class TemplateUtils { private static final Logger LOGGER = LoggerFactory.getLogger(TemplateUtils.class); /** * Execute if string not empty. * * @param source source * @param runnable execute runnable */ public static void stringNotEmptyAndThenExecute(String source, Runnable runnable) { if (StringUtils.isNotEmpty(source)) { try { runnable.run(); } catch (Exception e) { LOGGER.error("string not empty and then execute cause an exception.", e); } } } /** * Execute if string empty. * * @param source empty source * @param callable execute callable * @return result */ public static String stringEmptyAndThenExecute(String source, Callable callable) { if (StringUtils.isEmpty(source)) { try { return callable.call(); } catch (Exception e) { LOGGER.error("string empty and then execute cause an exception.", e); } } return source == null ? null : source.trim(); } /** * Execute if string blank. * * @param source empty source * @param callable execute callable * @return result */ public static String stringBlankAndThenExecute(String source, Callable callable) { if (StringUtils.isBlank(source)) { try { return callable.call(); } catch (Exception e) { LOGGER.error("string empty and then execute cause an exception.", e); } } return source == null ? null : source.trim(); } } ================================================ FILE: client-basic/src/main/java/com/alibaba/nacos/client/utils/TenantUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.utils.StringUtils; /** * Tenant Util. * * @author Nacos */ public class TenantUtil { private static final String USER_TENANT; private static final String DEFAULT_ACM_NAMESPACE = ""; private static final String TENANT_ID = "tenant.id"; private static final String ACM_NAMESPACE_PROPERTY = "acm.namespace"; static { USER_TENANT = NacosClientProperties.PROTOTYPE.getProperty(TENANT_ID, ""); } /** * Adapt the way ACM gets tenant on the cloud. *

    * Note the difference between getting and getting ANS. Since the processing logic on the server side is different, * the default value returns differently. *

    * * @return user tenant for acm */ public static String getUserTenantForAcm() { String tmp = USER_TENANT; if (StringUtils.isBlank(USER_TENANT)) { tmp = NacosClientProperties.PROTOTYPE.getProperty(ACM_NAMESPACE_PROPERTY, DEFAULT_ACM_NAMESPACE); } return tmp; } /** * Adapt the way ANS gets tenant on the cloud. * * @return user tenant for ans */ public static String getUserTenantForAns() { String tmp = USER_TENANT; if (StringUtils.isBlank(USER_TENANT)) { tmp = NacosClientProperties.PROTOTYPE.getProperty(SystemPropertyKeyConst.ANS_NAMESPACE); } return tmp; } } ================================================ FILE: client-basic/src/main/resources/META-INF/services/com.alibaba.nacos.client.address.ServerListProvider ================================================ # # Copyright 1999-2024 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.client.address.EndpointServerListProvider com.alibaba.nacos.client.address.PropertiesListProvider ================================================ FILE: client-basic/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService ================================================ # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.client.auth.impl.NacosClientAuthServiceImpl com.alibaba.nacos.client.auth.ram.RamClientAuthServiceImpl ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/address/AbstractServerListManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.http.client.NacosRestTemplate; 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.junit.jupiter.MockitoExtension; 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; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class AbstractServerListManagerTest { @Mock NacosRestTemplate restTemplate; NacosClientProperties properties; AbstractServerListManager serverListManager; @BeforeEach void setUp() { properties = NacosClientProperties.PROTOTYPE.derive(); } @AfterEach void tearDown() throws NacosException { if (null != serverListManager) { serverListManager.shutdown(); } } @Test void testConstructorWithNamespace() { serverListManager = new MockServerListManager(properties, "test-namespace"); assertFalse(properties.containsKey(PropertyKeyConst.NAMESPACE)); assertFalse(properties.containsKey(Constants.CLIENT_MODULE_TYPE)); assertTrue(serverListManager.getProperties().containsKey(PropertyKeyConst.NAMESPACE)); assertEquals("testModule", serverListManager.getProperties().getProperty(Constants.CLIENT_MODULE_TYPE)); } @Test void testConstructorWithoutNamespace() { serverListManager = new MockServerListManager(properties); assertFalse(properties.containsKey(PropertyKeyConst.NAMESPACE)); assertFalse(properties.containsKey(Constants.CLIENT_MODULE_TYPE)); assertFalse(serverListManager.getProperties().containsKey(PropertyKeyConst.NAMESPACE)); assertEquals("testModule", serverListManager.getProperties().getProperty(Constants.CLIENT_MODULE_TYPE)); } @Test void testStartWithoutProvider() { serverListManager = new MockServerListManager(properties); assertThrows(NacosException.class, () -> serverListManager.start()); } @Test void testGetServerList() throws NacosException { properties.setProperty("MockTest", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); // Mock provider will call this method in init. verify(restTemplate).getInterceptors(); assertEquals(1, serverListManager.getServerList().size()); assertEquals("mock-server-list", serverListManager.getServerList().get(0)); } @Test void testGetServerNameDefault() throws NacosException { properties.setProperty("MockTest", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("testModule-", serverListManager.getServerName()); } @Test void testGetServerName() throws NacosException { properties.setProperty("MockTest", "true"); properties.setProperty("ReturnMock", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("testModule-MockServerName", serverListManager.getServerName()); } @Test void testGetContextPathDefault() throws NacosException { properties.setProperty("MockTest", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("nacos", serverListManager.getContextPath()); } @Test void testGetContextPath() throws NacosException { properties.setProperty("MockTest", "true"); properties.setProperty("ReturnMock", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("MockContextPath", serverListManager.getContextPath()); } @Test void testGetNamespaceDefault() throws NacosException { properties.setProperty("MockTest", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("", serverListManager.getNamespace()); } @Test void testGetNamespace() throws NacosException { properties.setProperty("MockTest", "true"); properties.setProperty("ReturnMock", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("MockNamespace", serverListManager.getNamespace()); } @Test void testGetAddressSourceDefault() throws NacosException { properties.setProperty("MockTest", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("", serverListManager.getAddressSource()); } @Test void testGetAddressSource() throws NacosException { properties.setProperty("MockTest", "true"); properties.setProperty("ReturnMock", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertEquals("MockAddressSource", serverListManager.getAddressSource()); } @Test void testIsFixedDefault() throws NacosException { properties.setProperty("MockTest", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertFalse(serverListManager.isFixed()); } @Test void testIsFixed() throws NacosException { properties.setProperty("MockTest", "true"); properties.setProperty("ReturnMock", "true"); serverListManager = new MockServerListManager(properties); serverListManager.start(); assertTrue(serverListManager.isFixed()); } private class MockServerListManager extends AbstractServerListManager { public MockServerListManager(NacosClientProperties properties) { super(properties); } public MockServerListManager(NacosClientProperties properties, String namespace) { super(properties, namespace); } @Override protected String getModuleName() { return "testModule"; } @Override protected NacosRestTemplate getNacosRestTemplate() { return restTemplate; } @Override public String genNextServer() { return ""; } @Override public String getCurrentServer() { return ""; } } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/address/EndpointServerListProviderTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.SystemPropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.constant.Constants; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.utils.ClientBasicParamUtil; import com.alibaba.nacos.client.utils.ContextPathUtil; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.StringUtils; 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.junit.jupiter.MockitoExtension; import java.io.IOException; import java.lang.reflect.Field; import java.util.concurrent.TimeUnit; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class EndpointServerListProviderTest { @Mock NacosRestTemplate nacosRestTemplate; private EndpointServerListProvider serverListProvider; private NacosClientProperties properties; private HttpRestResult requestSuccess; @BeforeEach void setUp() { requestSuccess = new HttpRestResult<>(Header.EMPTY, 200, "\n127.0.0.1\nlocalhost:9848", "success"); serverListProvider = new EndpointServerListProvider(); properties = NacosClientProperties.PROTOTYPE.derive(); } @AfterEach void tearDown() throws NacosException { System.clearProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL); System.clearProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_PORT); System.clearProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_CONTEXT_PATH); System.clearProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME); System.clearProperty(SystemPropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE); serverListProvider.shutdown(); } @Test void testInitWithoutProperties() throws NacosException { assertThrows(NacosException.class, () -> serverListProvider.init(null, nacosRestTemplate)); } @Test void testMatchAndInitForPropertiesEndpoint() throws Exception { assertFalse(serverListProvider.match(properties)); properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); assertTrue(serverListProvider.match(properties)); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testMatchAndInitForSystemEndpoint() throws Exception { assertFalse(serverListProvider.match(properties)); System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL, "endpointFromSystem"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); assertTrue(serverListProvider.match(properties)); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromSystem", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testMatchAndInitByParsingFalseFromProperties() throws Exception { assertFalse(serverListProvider.match(properties)); properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL, "endpointFromSystem"); properties.setProperty(PropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE, "false"); assertTrue(serverListProvider.match(properties)); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testMatchAndInitByParsingFalseFromSystem() throws Exception { assertFalse(serverListProvider.match(properties)); properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL, "endpointFromSystem"); System.setProperty(SystemPropertyKeyConst.IS_USE_ENDPOINT_PARSING_RULE, "false"); assertTrue(serverListProvider.match(properties)); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testMatchAndInitByParsingTrue() throws Exception { assertFalse(serverListProvider.match(properties)); properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL, "endpointFromSystem"); assertTrue(serverListProvider.match(properties)); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromSystem", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithPropertiesEndpointPort() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.ENDPOINT_PORT, "80"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 80, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithSystemEndpointPort() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_PORT, "443"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 443, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithPropertiesEndpointContextPath() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.ENDPOINT_CONTEXT_PATH, "address"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, "address", ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithSystemEndpointContextPath() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_CONTEXT_PATH, "addresses"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, "addresses", ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitContextPathWithFull() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.CONTEXT_PATH, "globalContextPath"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, "globalContextPath", ClientBasicParamUtil.getDefaultNodesPath(), "", "globalContextPath"); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithPropertiesEndpointClusterName() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME, "endpointClusterName"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), "endpointClusterName", "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithPropertiesEndpointClusterNameWithFull() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.CLUSTER_NAME, "clusterName"); properties.setProperty(PropertyKeyConst.ENDPOINT_CLUSTER_NAME, "endpointClusterName"); properties.setProperty(PropertyKeyConst.IS_ADAPT_CLUSTER_NAME_USAGE, "true"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), "endpointClusterName", "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithSystemEndpointClusterNameByOldWay() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.CLUSTER_NAME, "clusterName"); properties.setProperty(PropertyKeyConst.IS_ADAPT_CLUSTER_NAME_USAGE, "true"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), "clusterName", "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithSystemEndpointClusterWithoutAdapt() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.CLUSTER_NAME, "clusterName"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithNamespace() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.NAMESPACE, "customNamespace"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "customNamespace", ClientBasicParamUtil.getDefaultContextPath()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithQuery() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.ENDPOINT_QUERY_PARAMS, "nofix=1"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "", ClientBasicParamUtil.getDefaultContextPath(), "nofix=1"); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithNamespaceAndQuery() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.NAMESPACE, "customNamespace"); properties.setProperty(PropertyKeyConst.ENDPOINT_QUERY_PARAMS, "nofix=1"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertInit("endpointFromProperties", 8080, ClientBasicParamUtil.getDefaultContextPath(), ClientBasicParamUtil.getDefaultNodesPath(), "customNamespace", ClientBasicParamUtil.getDefaultContextPath(), "nofix=1"); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitWithModuleType() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(com.alibaba.nacos.api.common.Constants.CLIENT_MODULE_TYPE, "naming"); when(nacosRestTemplate.get(anyString(), argThat(header -> "naming".equals(header.getValue(HttpHeaderConsts.REQUEST_MODULE))), any(Query.class), eq(String.class))).thenReturn(requestSuccess); serverListProvider.init(properties, nacosRestTemplate); assertFalse(serverListProvider.getServerList().isEmpty()); assertEquals(2, serverListProvider.getServerList().size()); } @Test void testInitGetServerListWithException() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenThrow( new IOException("test")); assertThrows(NacosException.class, () -> serverListProvider.init(properties, nacosRestTemplate)); } @Test void testInitGetServerListWithError() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); HttpRestResult failedResult = new HttpRestResult<>(Header.EMPTY, 500, null, "test"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( failedResult); assertThrows(NacosException.class, () -> serverListProvider.init(properties, nacosRestTemplate)); } @Test void testRefreshServerList() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.ENDPOINT_REFRESH_INTERVAL_SECONDS, "1"); HttpRestResult newResult = new HttpRestResult<>(Header.EMPTY, 200, "\n1.1.1.1 \nlocalhost:9848", "success"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess, newResult); serverListProvider.init(properties, nacosRestTemplate); assertEquals(2, serverListProvider.getServerList().size()); assertEquals("127.0.0.1:8848", serverListProvider.getServerList().get(0)); assertEquals("localhost:9848", serverListProvider.getServerList().get(1)); Field field = EndpointServerListProvider.class.getDeclaredField("lastServerListRefreshTime"); field.setAccessible(true); field.set(serverListProvider, 0L); // wait refresh TimeUnit.MILLISECONDS.sleep(2000); assertEquals(2, serverListProvider.getServerList().size()); assertEquals("1.1.1.1:8848", serverListProvider.getServerList().get(0)); assertEquals("localhost:9848", serverListProvider.getServerList().get(1)); } @Test void testRefreshServerListWithDiffSort() throws Exception { properties.setProperty(PropertyKeyConst.ENDPOINT, "endpointFromProperties"); properties.setProperty(PropertyKeyConst.ENDPOINT_REFRESH_INTERVAL_SECONDS, "1"); HttpRestResult newResult = new HttpRestResult<>(Header.EMPTY, 200, "\nlocalhost:9848\n127.0.0.1", "success"); when(nacosRestTemplate.get(anyString(), any(Header.class), any(Query.class), eq(String.class))).thenReturn( requestSuccess, newResult); serverListProvider.init(properties, nacosRestTemplate); assertEquals(2, serverListProvider.getServerList().size()); assertEquals("127.0.0.1:8848", serverListProvider.getServerList().get(0)); assertEquals("localhost:9848", serverListProvider.getServerList().get(1)); Field field = EndpointServerListProvider.class.getDeclaredField("lastServerListRefreshTime"); field.setAccessible(true); field.set(serverListProvider, 0L); // wait refresh TimeUnit.MILLISECONDS.sleep(2000); assertEquals(2, serverListProvider.getServerList().size()); assertEquals("127.0.0.1:8848", serverListProvider.getServerList().get(0)); assertEquals("localhost:9848", serverListProvider.getServerList().get(1)); } private void assertInit(String expectedEndpoint, int expectEndpointPort, String expectedEndpointContext, String expectedServiceName, String expectedNamespace, String expectedContextPath) { assertInit(expectedEndpoint, expectEndpointPort, expectedEndpointContext, expectedServiceName, expectedNamespace, expectedContextPath, null); } private void assertInit(String expectedEndpoint, int expectEndpointPort, String expectedEndpointContext, String expectedServiceName, String expectedNamespace, String expectedContextPath, String expectedQuery) { String expectedAddressServerUrl = String.format("http://%s:%d%s/%s", expectedEndpoint, expectEndpointPort, ContextPathUtil.normalizeContextPath(expectedEndpointContext), expectedServiceName); assertEquals(Constants.Address.ENDPOINT_SERVER_LIST_PROVIDER_ORDER, serverListProvider.getOrder()); String expectedServerName = String.format("%s-%s_%d_%s_%s", "custom", expectedEndpoint, expectEndpointPort, expectedEndpointContext, expectedServiceName); if (StringUtils.isNotBlank(expectedNamespace)) { expectedServerName = String.format("%s_%s", expectedServerName, expectedNamespace); expectedAddressServerUrl += "?namespace=" + expectedNamespace; } if (StringUtils.isNotBlank(expectedQuery)) { String queryTag = StringUtils.isBlank(expectedNamespace) ? "?" : "&"; expectedAddressServerUrl += queryTag + expectedQuery; } assertEquals(expectedAddressServerUrl, serverListProvider.getAddressSource()); assertEquals(expectedServerName, serverListProvider.getServerName()); assertEquals(expectedNamespace, serverListProvider.getNamespace()); assertEquals(expectedContextPath, serverListProvider.getContextPath()); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/address/PropertiesListProviderTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.constant.Constants; import com.alibaba.nacos.client.env.NacosClientProperties; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class PropertiesListProviderTest { private PropertiesListProvider propertiesListProvider; @BeforeEach void setUp() { propertiesListProvider = new PropertiesListProvider(); } @AfterEach void tearDown() throws NacosException { propertiesListProvider.shutdown(); } @Test void testInitWithoutProperties() throws NacosException { assertThrows(NacosException.class, () -> propertiesListProvider.init(null, null)); } @Test void testInit() throws NacosException { NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); assertFalse(propertiesListProvider.match(properties)); properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:1111,http://127.0.0.1:2222;https://1.1.1.1:3333,2.2.2.2;http://3.3.3.3,https://4.4.4.4"); assertTrue(propertiesListProvider.match(properties)); propertiesListProvider.init(properties, null); assertEquals(6, propertiesListProvider.getServerList().size()); assertEquals("localhost:1111", propertiesListProvider.getServerList().get(0)); assertEquals("http://127.0.0.1:2222", propertiesListProvider.getServerList().get(1)); assertEquals("https://1.1.1.1:3333", propertiesListProvider.getServerList().get(2)); assertEquals("2.2.2.2:8848", propertiesListProvider.getServerList().get(3)); assertEquals("http://3.3.3.3", propertiesListProvider.getServerList().get(4)); assertEquals("https://4.4.4.4", propertiesListProvider.getServerList().get(5)); assertTrue(propertiesListProvider.isFixed()); assertEquals(Constants.Address.ADDRESS_SERVER_LIST_PROVIDER_ORDER, propertiesListProvider.getOrder()); assertEquals("fixed-localhost_1111-127.0.0.1_2222-1.1.1.1_3333-2.2.2.2_8848-3.3.3.3-4.4.4.4", propertiesListProvider.getServerName()); } @Test void testGetServerNameWithNamespace() throws NacosException { NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty(PropertyKeyConst.NAMESPACE, "test_namespace"); properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:1111"); propertiesListProvider.init(properties, null); assertEquals("fixed-test_namespace-localhost_1111", propertiesListProvider.getServerName()); assertEquals("test_namespace", propertiesListProvider.getNamespace()); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/address/mock/MockServerListProvider.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.address.mock; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.address.ServerListProvider; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import java.util.Collections; import java.util.List; public class MockServerListProvider implements ServerListProvider { private NacosClientProperties properties; @Override public void init(NacosClientProperties properties, NacosRestTemplate nacosRestTemplate) throws NacosException { this.properties = properties; nacosRestTemplate.getInterceptors(); } @Override public List getServerList() { if (properties.containsKey("EmptyList")) { return Collections.emptyList(); } return Collections.singletonList("mock-server-list"); } @Override public int getOrder() { return Integer.MIN_VALUE; } @Override public boolean match(NacosClientProperties properties) { return properties.containsKey("MockTest"); } @Override public void shutdown() throws NacosException { } @Override public String getServerName() { if (isReturnMock()) { return "MockServerName"; } return ServerListProvider.super.getServerName(); } @Override public String getNamespace() { if (isReturnMock()) { return "MockNamespace"; } return ServerListProvider.super.getNamespace(); } @Override public String getContextPath() { if (isReturnMock()) { return "MockContextPath"; } return ServerListProvider.super.getContextPath(); } @Override public boolean isFixed() { if (isReturnMock()) { return true; } return ServerListProvider.super.isFixed(); } @Override public String getAddressSource() { if (isReturnMock()) { return "MockAddressSource"; } return ServerListProvider.super.getAddressSource(); } private boolean isReturnMock() { return properties.getBoolean("ReturnMock", false); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/impl/NacosClientAuthServiceImplTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.impl; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import java.util.Properties; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class NacosClientAuthServiceImplTest { @Test void testLoginSuccess() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setData("{\"accessToken\":\"ttttttttttttttttt\",\"tokenTtl\":1000}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); //when boolean ret = nacosClientAuthService.login(properties); //then assertTrue(ret); } @Test void testTestLoginFailCode() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setCode(400); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); boolean ret = nacosClientAuthService.login(properties); assertFalse(ret); } @Test void testTestLoginFailHttp() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenThrow(new Exception()); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); boolean ret = nacosClientAuthService.login(properties); assertFalse(ret); } @Test void testTestLoginServerListSuccess() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setData("{\"accessToken\":\"ttttttttttttttttt\",\"tokenTtl\":1000}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); boolean ret = nacosClientAuthService.login(properties); assertTrue(ret); } @Test void testTestLoginServerListLoginInWindow() throws Exception { //given NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setData("{\"accessToken\":\"ttttttttttttttttt\",\"tokenTtl\":1000}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); //when nacosClientAuthService.login(properties); //then boolean ret = nacosClientAuthService.login(properties); assertTrue(ret); } @Test void testGetAccessToken() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setData("{\"accessToken\":\"abc\",\"tokenTtl\":1000}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); //when assertTrue(nacosClientAuthService.login(properties)); //then assertEquals("abc", nacosClientAuthService.getLoginIdentityContext(null).getParameter(NacosAuthLoginConstant.ACCESSTOKEN)); } @Test void testGetAccessEmptyToken() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setData("{\"accessToken\":\"\",\"tokenTtl\":1000}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); //when assertTrue(nacosClientAuthService.login(properties)); //then assertEquals("", nacosClientAuthService.getLoginIdentityContext(null).getParameter(NacosAuthLoginConstant.ACCESSTOKEN)); } @Test void testGetAccessTokenWithoutToken() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setData("{\"tokenTtl\":1000}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); //when assertTrue(nacosClientAuthService.login(properties)); //then assertNull( nacosClientAuthService.getLoginIdentityContext(null).getParameter(NacosAuthLoginConstant.ACCESSTOKEN)); } @Test void testGetAccessTokenWithInvalidTtl() throws Exception { NacosRestTemplate nacosRestTemplate = mock(NacosRestTemplate.class); HttpRestResult result = new HttpRestResult<>(); result.setData("{\"accessToken\":\"abc\",\"tokenTtl\":\"abc\"}"); result.setCode(200); when(nacosRestTemplate.postForm(any(), (Header) any(), any(), any(), any())).thenReturn(result); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.setServerList(serverList); nacosClientAuthService.setNacosRestTemplate(nacosRestTemplate); //when assertFalse(nacosClientAuthService.login(properties)); } @Test void testReLogin() { NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); nacosClientAuthService.login(new Properties()); // reLogin nacosClientAuthService.getLoginIdentityContext(null).setParameter(NacosAuthLoginConstant.RELOGINFLAG, "true"); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.USERNAME, "aaa"); properties.setProperty(PropertyKeyConst.PASSWORD, "123456"); List serverList = new ArrayList<>(); serverList.add("localhost"); //when assertTrue(nacosClientAuthService.login(properties)); } @Test void testGenerateTokenWithInvalidToken() { NacosClientAuthServiceImpl nacosClientAuthService = new NacosClientAuthServiceImpl(); long tokenTtl = 18000L; long tokenRefreshWindow = nacosClientAuthService.generateTokenRefreshWindow(tokenTtl); assertTrue(tokenRefreshWindow <= tokenTtl / 10); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/impl/process/HttpLoginProcessorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.impl.process; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.auth.impl.NacosAuthLoginConstant; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; 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.junit.jupiter.MockitoExtension; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class HttpLoginProcessorTest { @Mock NacosRestTemplate restTemplate; @Mock HttpRestResult result; Properties properties; HttpLoginProcessor loginProcessor; @BeforeEach void setUp() { loginProcessor = new HttpLoginProcessor(restTemplate); properties = new Properties(); } @Test void testGetResponseSuccess() throws Exception { properties.setProperty(NacosAuthLoginConstant.SERVER, "http://localhost:8848"); when(restTemplate.postForm(eq("http://localhost:8848/nacos/v3/auth/user/login"), eq(Header.EMPTY), any(Query.class), anyMap(), eq(String.class))).thenReturn(result); when(result.ok()).thenReturn(true); Map mockMap = new HashMap<>(); mockMap.put(Constants.ACCESS_TOKEN, "mock_access_token"); mockMap.put(Constants.TOKEN_TTL, "100L"); when(result.getData()).thenReturn(JacksonUtils.toJson(mockMap)); LoginIdentityContext actual = loginProcessor.getResponse(properties); assertEquals("mock_access_token", actual.getParameter(NacosAuthLoginConstant.ACCESSTOKEN)); assertEquals("100L", actual.getParameter(NacosAuthLoginConstant.TOKENTTL)); } @Test void testGetResponseFailed() throws Exception { properties.setProperty(NacosAuthLoginConstant.SERVER, "localhost"); when(restTemplate.postForm(eq("http://localhost:8848/nacos/v3/auth/user/login"), eq(Header.EMPTY), any(Query.class), anyMap(), eq(String.class))).thenReturn(result); assertNull(loginProcessor.getResponse(properties)); } @Test void testGetResponseException() throws Exception { properties.setProperty(NacosAuthLoginConstant.SERVER, "localhost"); when(restTemplate.postForm(eq("http://localhost:8848/nacos/v3/auth/user/login"), eq(Header.EMPTY), any(Query.class), anyMap(), eq(String.class))).thenThrow(new RuntimeException("test")); assertNull(loginProcessor.getResponse(properties)); } @Test void testGetResponseSuccessFromV1() throws Exception { properties.setProperty(NacosAuthLoginConstant.SERVER, "localhost"); HttpRestResult httpRes = new HttpRestResult<>(); httpRes.setCode(NacosException.SERVER_NOT_IMPLEMENTED); when(restTemplate.postForm(eq("http://localhost:8848/nacos/v3/auth/user/login"), eq(Header.EMPTY), any(Query.class), anyMap(), eq(String.class))).thenReturn(httpRes); when(result.ok()).thenReturn(true); Map mockMap = new HashMap<>(); mockMap.put(Constants.ACCESS_TOKEN, "mock_access_token"); mockMap.put(Constants.TOKEN_TTL, "100L"); when(result.getData()).thenReturn(JacksonUtils.toJson(mockMap)); when(restTemplate.postForm(eq("http://localhost:8848/nacos/v1/auth/users/login"), eq(Header.EMPTY), any(Query.class), anyMap(), eq(String.class))).thenReturn(result); LoginIdentityContext actual = loginProcessor.getResponse(properties); assertEquals("mock_access_token", actual.getParameter(NacosAuthLoginConstant.ACCESSTOKEN)); assertEquals("100L", actual.getParameter(NacosAuthLoginConstant.TOKENTTL)); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/RamClientAuthServiceImplTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.auth.ram.injector.AbstractResourceInjector; import com.alibaba.nacos.common.utils.ReflectUtils; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; 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.junit.jupiter.MockitoExtension; import java.util.Map; import java.util.Properties; 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.mockito.Mockito.never; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class RamClientAuthServiceImplTest { private static final String MOCK = "mock"; @Mock private AbstractResourceInjector mockResourceInjector; private RamClientAuthServiceImpl ramClientAuthService; private Properties akSkProperties; private Properties roleProperties; private RamContext ramContext; private RequestResource resource; @BeforeEach void setUp() throws Exception { ramClientAuthService = new RamClientAuthServiceImpl(); Map resourceInjectors = (Map) ReflectUtils.getFieldValue( ramClientAuthService, "resourceInjectors"); resourceInjectors.clear(); resourceInjectors.put(MOCK, mockResourceInjector); ramContext = (RamContext) ReflectUtils.getFieldValue(ramClientAuthService, "ramContext"); akSkProperties = new Properties(); roleProperties = new Properties(); akSkProperties.setProperty(PropertyKeyConst.ACCESS_KEY, PropertyKeyConst.ACCESS_KEY); akSkProperties.setProperty(PropertyKeyConst.SECRET_KEY, PropertyKeyConst.SECRET_KEY); roleProperties.setProperty(PropertyKeyConst.RAM_ROLE_NAME, PropertyKeyConst.RAM_ROLE_NAME); resource = new RequestResource(); } @AfterEach void tearDown() throws NacosException { ramClientAuthService.shutdown(); } @Test void testLoginWithAkSk() { assertTrue(ramClientAuthService.login(akSkProperties)); assertEquals(PropertyKeyConst.ACCESS_KEY, ramContext.getAccessKey()); assertEquals(PropertyKeyConst.SECRET_KEY, ramContext.getSecretKey()); assertNull(ramContext.getRamRoleName()); assertTrue(ramClientAuthService.login(roleProperties)); assertEquals(PropertyKeyConst.ACCESS_KEY, ramContext.getAccessKey()); assertEquals(PropertyKeyConst.SECRET_KEY, ramContext.getSecretKey()); assertNull(ramContext.getRamRoleName()); } @Test void testLoginWithRoleName() { assertTrue(ramClientAuthService.login(roleProperties)); assertNull(ramContext.getAccessKey(), PropertyKeyConst.ACCESS_KEY); assertNull(ramContext.getSecretKey(), PropertyKeyConst.SECRET_KEY); assertEquals(PropertyKeyConst.RAM_ROLE_NAME, ramContext.getRamRoleName()); assertTrue(ramClientAuthService.login(akSkProperties)); assertNull(ramContext.getAccessKey(), PropertyKeyConst.ACCESS_KEY); assertNull(ramContext.getSecretKey(), PropertyKeyConst.SECRET_KEY); assertEquals(PropertyKeyConst.RAM_ROLE_NAME, ramContext.getRamRoleName()); } @Test void testGetLoginIdentityContextWithoutLogin() { LoginIdentityContext actual = ramClientAuthService.getLoginIdentityContext(resource); assertTrue(actual.getAllKey().isEmpty()); verify(mockResourceInjector, never()).doInject(resource, ramContext, actual); } @Test void testGetLoginIdentityContextWithoutInjector() { ramClientAuthService.login(akSkProperties); LoginIdentityContext actual = ramClientAuthService.getLoginIdentityContext(resource); assertTrue(actual.getAllKey().isEmpty()); verify(mockResourceInjector, never()).doInject(resource, ramContext, actual); } @Test void testGetLoginIdentityContextWithInjector() { ramClientAuthService.login(akSkProperties); resource.setType(MOCK); LoginIdentityContext actual = ramClientAuthService.getLoginIdentityContext(resource); assertTrue(actual.getAllKey().isEmpty()); verify(mockResourceInjector).doInject(resource, ramContext, actual); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/identify/CredentialServiceTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; class CredentialServiceTest { private static final String APP_NAME = "app"; @BeforeEach void setUp() throws Exception { CredentialService.freeInstance(); CredentialService.freeInstance(APP_NAME); } @AfterEach void tearDown() throws Exception { System.clearProperty(IdentifyConstants.PROJECT_NAME_PROPERTY); CredentialService.freeInstance(); CredentialService.freeInstance(APP_NAME); } @Test void testGetInstance() { CredentialService credentialService1 = CredentialService.getInstance(); CredentialService credentialService2 = CredentialService.getInstance(); assertEquals(credentialService1, credentialService2); } @Test void testGetInstance2() { CredentialService credentialService1 = CredentialService.getInstance(APP_NAME); CredentialService credentialService2 = CredentialService.getInstance(APP_NAME); assertEquals(credentialService1, credentialService2); } @Test void testGetInstance3() throws NoSuchFieldException, IllegalAccessException { System.setProperty(IdentifyConstants.PROJECT_NAME_PROPERTY, APP_NAME); CredentialService credentialService1 = CredentialService.getInstance(); Field appNameField = credentialService1.getClass().getDeclaredField("appName"); appNameField.setAccessible(true); String appName = (String) appNameField.get(credentialService1); assertEquals(APP_NAME, appName); } @Test void testFreeInstance() { CredentialService credentialService1 = CredentialService.getInstance(); CredentialService credentialService2 = CredentialService.freeInstance(); assertEquals(credentialService1, credentialService2); } @Test void testFreeInstance2() { CredentialService credentialService1 = CredentialService.getInstance(); CredentialService credentialService2 = CredentialService.freeInstance(); assertEquals(credentialService1, credentialService2); } @Test void testFree() throws NoSuchFieldException, IllegalAccessException { CredentialService credentialService1 = CredentialService.getInstance(); CredentialWatcher mockWatcher = mock(CredentialWatcher.class); Field watcherField = CredentialService.class.getDeclaredField("watcher"); watcherField.setAccessible(true); watcherField.set(credentialService1, mockWatcher); //when credentialService1.free(); //then verify(mockWatcher, times(1)).stop(); } @Test void testGetCredential() { CredentialService credentialService1 = CredentialService.getInstance(); Credentials credential = credentialService1.getCredential(); assertNotNull(credential); } @Test void testSetCredential() { CredentialService credentialService1 = CredentialService.getInstance(); Credentials credential = new Credentials(); //when credentialService1.setCredential(credential); //then assertEquals(credential, credentialService1.getCredential()); } @Test void testSetStaticCredential() throws NoSuchFieldException, IllegalAccessException { CredentialService credentialService1 = CredentialService.getInstance(); CredentialWatcher mockWatcher = mock(CredentialWatcher.class); Field watcherField = CredentialService.class.getDeclaredField("watcher"); watcherField.setAccessible(true); watcherField.set(credentialService1, mockWatcher); Credentials credential = new Credentials(); //when credentialService1.setStaticCredential(credential); //then assertEquals(credential, credentialService1.getCredential()); verify(mockWatcher, times(1)).stop(); } @Test void testRegisterCredentialListener() { CredentialListener expect = mock(CredentialListener.class); CredentialService credentialService1 = CredentialService.getInstance(); credentialService1.registerCredentialListener(expect); Credentials newCredentials = new Credentials(); newCredentials.setAccessKey("ak"); credentialService1.setCredential(newCredentials); verify(expect, times(1)).onUpdateCredential(); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/identify/CredentialWatcherTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; 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.junit.jupiter.MockitoExtension; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; 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.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class CredentialWatcherTest { @Mock private CredentialService credentialService; private CredentialWatcher credentialWatcher; private Method loadCredentialMethod; private Method loadCredentialFromPropertiesMethod; @BeforeEach void setUp() throws Exception { credentialWatcher = new CredentialWatcher("testApp", credentialService); loadCredentialMethod = CredentialWatcher.class.getDeclaredMethod("loadCredential", boolean.class); loadCredentialMethod.setAccessible(true); loadCredentialFromPropertiesMethod = CredentialWatcher.class.getDeclaredMethod("loadCredentialFromProperties", InputStream.class, boolean.class, Credentials.class); loadCredentialFromPropertiesMethod.setAccessible(true); } @AfterEach void tearDown() throws Exception { credentialWatcher.stop(); System.clearProperty("spas.identity"); System.clearProperty(IdentifyConstants.ENV_ACCESS_KEY); System.clearProperty(IdentifyConstants.ENV_SECRET_KEY); CredentialService.freeInstance(); } @Test void testStop() throws NoSuchFieldException, IllegalAccessException { credentialWatcher.stop(); Field executorField = CredentialWatcher.class.getDeclaredField("executor"); executorField.setAccessible(true); ScheduledExecutorService executor = (ScheduledExecutorService) executorField.get(credentialWatcher); assertTrue(executor.isShutdown()); } @Test void testLoadCredentialByEnv() throws InvocationTargetException, IllegalAccessException { System.setProperty(IdentifyConstants.ENV_ACCESS_KEY, "testAk"); System.setProperty(IdentifyConstants.ENV_SECRET_KEY, "testSk"); final AtomicReference readAk = new AtomicReference<>(""); final AtomicReference readSK = new AtomicReference<>(""); final AtomicReference readTenantId = new AtomicReference<>(""); doAnswer(invocationOnMock -> { Credentials credentials = invocationOnMock.getArgument(0, Credentials.class); readAk.set(credentials.getAccessKey()); readSK.set(credentials.getSecretKey()); readTenantId.set(credentials.getTenantId()); return null; }).when(credentialService).setCredential(any()); loadCredentialMethod.invoke(credentialWatcher, true); assertEquals("testAk", readAk.get()); assertEquals("testSk", readSK.get()); assertNull(readTenantId.get()); } @Test void testLoadCredentialByIdentityFile() throws InvocationTargetException, IllegalAccessException { URL url = CredentialWatcherTest.class.getClassLoader().getResource("spas.identity"); System.setProperty("spas.identity", url.getPath()); final AtomicReference readAk = new AtomicReference<>(""); final AtomicReference readSK = new AtomicReference<>(""); final AtomicReference readTenantId = new AtomicReference<>(""); doAnswer(invocationOnMock -> { Credentials credentials = invocationOnMock.getArgument(0, Credentials.class); readAk.set(credentials.getAccessKey()); readSK.set(credentials.getSecretKey()); readTenantId.set(credentials.getTenantId()); return null; }).when(credentialService).setCredential(any()); loadCredentialMethod.invoke(credentialWatcher, true); assertEquals("testAk", readAk.get()); assertEquals("testSk", readSK.get()); assertEquals("testTenantId", readTenantId.get()); } @Test void testLoadCredentialByInvalidIdentityFile() throws InvocationTargetException, IllegalAccessException { URL url = CredentialWatcherTest.class.getClassLoader().getResource("spas_invalid.identity"); System.setProperty("spas.identity", url.getPath()); final AtomicReference readAk = new AtomicReference<>(""); final AtomicReference readSK = new AtomicReference<>(""); final AtomicReference readTenantId = new AtomicReference<>(""); doAnswer(invocationOnMock -> { Credentials credentials = invocationOnMock.getArgument(0, Credentials.class); readAk.set(credentials.getAccessKey()); readSK.set(credentials.getSecretKey()); readTenantId.set(credentials.getTenantId()); return null; }).when(credentialService).setCredential(any()); loadCredentialMethod.invoke(credentialWatcher, true); assertEquals("", readAk.get()); assertEquals("testSk", readSK.get()); assertEquals("testTenantId", readTenantId.get()); } /** * The docker file is need /etc permission, which depend environment. So use mock InputStream to test. */ @Test void testLoadCredentialByDockerFile() throws FileNotFoundException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { URL url = CredentialWatcherTest.class.getClassLoader().getResource("spas_docker.identity"); InputStream propertiesIS = new FileInputStream(url.getPath()); Credentials actual = new Credentials(); Field propertyPathField = CredentialWatcher.class.getDeclaredField("propertyPath"); propertyPathField.setAccessible(true); propertyPathField.set(credentialWatcher, IdentifyConstants.DOCKER_CREDENTIAL_PATH); loadCredentialFromPropertiesMethod.invoke(credentialWatcher, propertiesIS, true, actual); assertEquals("testAk", actual.getAccessKey()); assertEquals("testSk", actual.getSecretKey()); assertEquals("testTenantId", actual.getTenantId()); } @Test void testLoadCredentialByFileWithIoException() throws IOException, InvocationTargetException, IllegalAccessException { InputStream propertiesIS = mock(InputStream.class); when(propertiesIS.read(any())).thenThrow(new IOException("test")); doThrow(new IOException("test")).when(propertiesIS).close(); Credentials actual = new Credentials(); loadCredentialFromPropertiesMethod.invoke(credentialWatcher, propertiesIS, true, actual); assertNull(actual.getAccessKey()); assertNull(actual.getSecretKey()); assertNull(actual.getTenantId()); } @Test void testReLoadCredential() throws InvocationTargetException, IllegalAccessException, InterruptedException { URL url = CredentialWatcherTest.class.getClassLoader().getResource("spas_modified.identity"); modifiedFile(url, true); System.setProperty("spas.identity", url.getPath()); final AtomicReference readAk = new AtomicReference<>(""); final AtomicReference readSK = new AtomicReference<>(""); final AtomicReference readTenantId = new AtomicReference<>(""); doAnswer(invocationOnMock -> { Credentials credentials = invocationOnMock.getArgument(0, Credentials.class); readAk.set(credentials.getAccessKey()); readSK.set(credentials.getSecretKey()); readTenantId.set(credentials.getTenantId()); return null; }).when(credentialService).setCredential(any()); loadCredentialMethod.invoke(credentialWatcher, true); assertEquals("testAk", readAk.get()); assertEquals("testSk", readSK.get()); assertNull(readTenantId.get()); // waiting reload thread work modifiedFile(url, false); TimeUnit.MILLISECONDS.sleep(10500); assertEquals("testAk", readAk.get()); assertEquals("testSk", readSK.get()); assertEquals("testTenantId", readTenantId.get()); } private boolean modifiedFile(URL url, boolean init) { File file = new File(url.getPath()); boolean result; try (BufferedWriter bw = new BufferedWriter(new FileWriter(file))) { if (init) { bw.write("accessKey=testAk\nsecretKey=testSk"); } else { bw.write("accessKey=testAk\nsecretKey=testSk\ntenantId=testTenantId"); } bw.flush(); result = true; } catch (IOException ignored) { result = false; } return result; } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/identify/CredentialsTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class CredentialsTest { @Test void testGetter() { // given String ak = "ak"; String sk = "sk"; String tenantId = "100"; Credentials credentials = new Credentials(ak, sk, tenantId); // when then assertEquals(ak, credentials.getAccessKey()); assertEquals(sk, credentials.getSecretKey()); assertEquals(tenantId, credentials.getTenantId()); } @Test void testSetter() { //given String ak = "ak"; String sk = "sk"; String tenantId = "100"; Credentials credentials = new Credentials(); //when credentials.setAccessKey(ak); credentials.setSecretKey(sk); credentials.setTenantId(tenantId); //then assertEquals(ak, credentials.getAccessKey()); assertEquals(sk, credentials.getSecretKey()); assertEquals(tenantId, credentials.getTenantId()); } @Test void testValid() { //given String ak = "ak"; String sk = "sk"; String tenantId = "100"; Credentials credentials = new Credentials(ak, sk, tenantId); //when boolean actual = credentials.valid(); //then assertTrue(actual); } @Test void testIdentical() { //given String ak = "ak"; String sk = "sk"; String tenantId = "100"; Credentials credentials1 = new Credentials(ak, sk, "101"); Credentials credentials2 = new Credentials(ak, sk, "100"); //then boolean actual = credentials1.identical(credentials2); //then assertTrue(actual); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/identify/StsConfigTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; 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 StsConfigTest { private static void resetStsConfig() { StsConfig.getInstance().setRamRoleName(null); StsConfig.getInstance().setTimeToRefreshInMillisecond(3 * 60 * 1000); StsConfig.getInstance().setCacheSecurityCredentials(true); StsConfig.getInstance().setSecurityCredentials(null); StsConfig.getInstance().setSecurityCredentialsUrl(null); System.clearProperty(IdentifyConstants.RAM_ROLE_NAME_PROPERTY); System.clearProperty(IdentifyConstants.REFRESH_TIME_PROPERTY); System.clearProperty(IdentifyConstants.SECURITY_PROPERTY); System.clearProperty(IdentifyConstants.SECURITY_URL_PROPERTY); System.clearProperty(IdentifyConstants.SECURITY_CACHE_PROPERTY); } @BeforeEach void before() { resetStsConfig(); } @AfterEach void after() { resetStsConfig(); } @Test void testGetInstance() { StsConfig instance1 = StsConfig.getInstance(); StsConfig instance2 = StsConfig.getInstance(); assertEquals(instance1, instance2); } @Test void testGetRamRoleName() { StsConfig.getInstance().setRamRoleName("test"); assertEquals("test", StsConfig.getInstance().getRamRoleName()); } @Test void testGetTimeToRefreshInMillisecond() { assertEquals(3 * 60 * 1000, StsConfig.getInstance().getTimeToRefreshInMillisecond()); StsConfig.getInstance().setTimeToRefreshInMillisecond(3000); assertEquals(3000, StsConfig.getInstance().getTimeToRefreshInMillisecond()); } @Test void testGetSecurityCredentialsUrl() { assertNull(StsConfig.getInstance().getSecurityCredentialsUrl()); String expect = "localhost"; StsConfig.getInstance().setSecurityCredentialsUrl(expect); assertEquals(expect, StsConfig.getInstance().getSecurityCredentialsUrl()); } @Test void testGetSecurityCredentialsUrlDefault() { StsConfig.getInstance().setRamRoleName("test"); assertEquals("http://100.100.100.200/latest/meta-data/ram/security-credentials/test", StsConfig.getInstance().getSecurityCredentialsUrl()); } @Test void testGetSecurityCredentials() { assertNull(StsConfig.getInstance().getSecurityCredentials()); String expect = "abc"; StsConfig.getInstance().setSecurityCredentials(expect); assertEquals(expect, StsConfig.getInstance().getSecurityCredentials()); } @Test void testIsCacheSecurityCredentials() { assertTrue(StsConfig.getInstance().isCacheSecurityCredentials()); StsConfig.getInstance().setCacheSecurityCredentials(false); assertFalse(StsConfig.getInstance().isCacheSecurityCredentials()); } @Test void testIsOnFalse() { boolean stsOn = StsConfig.getInstance().isStsOn(); assertFalse(stsOn); } @Test void testIsOnTrue() { StsConfig.getInstance().setSecurityCredentials("abc"); boolean stsOn = StsConfig.getInstance().isStsOn(); assertTrue(stsOn); } @Test void testFromEnv() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor constructor = StsConfig.class.getDeclaredConstructor(); constructor.setAccessible(true); System.setProperty(IdentifyConstants.RAM_ROLE_NAME_PROPERTY, "test"); System.setProperty(IdentifyConstants.REFRESH_TIME_PROPERTY, "3000"); System.setProperty(IdentifyConstants.SECURITY_PROPERTY, "abc"); System.setProperty(IdentifyConstants.SECURITY_URL_PROPERTY, "localhost"); System.setProperty(IdentifyConstants.SECURITY_CACHE_PROPERTY, "false"); StsConfig stsConfig = constructor.newInstance(); assertEquals("test", stsConfig.getRamRoleName()); assertEquals(3000, stsConfig.getTimeToRefreshInMillisecond()); assertEquals("abc", stsConfig.getSecurityCredentials()); assertEquals("localhost", stsConfig.getSecurityCredentialsUrl()); assertFalse(stsConfig.isCacheSecurityCredentials()); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/identify/StsCredentialHolderTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.identify; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.JacksonUtils; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.util.Date; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class StsCredentialHolderTest { private String securityCredentialsUrl; private NacosRestTemplate cachedNacosRestTemplate; @Mock private HttpRestResult mockResult; @Mock private NacosRestTemplate nacosRestTemplate; @BeforeEach void setUp() throws Exception { securityCredentialsUrl = StsConfig.getInstance().getSecurityCredentialsUrl(); StsConfig.getInstance().setSecurityCredentialsUrl("url"); Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); cachedNacosRestTemplate = restMap.get( "com.alibaba.nacos.client.remote.HttpClientManager$HttpClientFactory"); restMap.put("com.alibaba.nacos.client.remote.HttpClientManager$HttpClientFactory", nacosRestTemplate); } @AfterEach void tearDown() throws Exception { StsConfig.getInstance().setSecurityCredentials(null); StsConfig.getInstance().setSecurityCredentialsUrl(securityCredentialsUrl); if (null != cachedNacosRestTemplate) { Field restMapField = HttpClientBeanHolder.class.getDeclaredField("SINGLETON_REST"); restMapField.setAccessible(true); Map restMap = (Map) restMapField.get(null); restMap.put("com.alibaba.nacos.client.remote.HttpClientManager$HttpClientFactory", cachedNacosRestTemplate); } clearForSts(); } private void clearForSts() throws NoSuchFieldException, IllegalAccessException { StsConfig.getInstance().setSecurityCredentialsUrl(null); Field field = StsCredentialHolder.class.getDeclaredField("stsCredential"); field.setAccessible(true); field.set(StsCredentialHolder.getInstance(), null); } @Test void testGetStsCredentialFromCache() throws NoSuchFieldException, IllegalAccessException { StsCredential stsCredential = buildMockStsCredential(); setStsCredential(stsCredential); assertEquals(stsCredential, StsCredentialHolder.getInstance().getStsCredential()); } private void setStsCredential(StsCredential stsCredential) throws NoSuchFieldException, IllegalAccessException { Field field = StsCredentialHolder.class.getDeclaredField("stsCredential"); field.setAccessible(true); field.set(StsCredentialHolder.getInstance(), stsCredential); } @Test void testGetStsCredentialFromStringCache() throws NoSuchFieldException, IllegalAccessException { StsCredential stsCredential = buildMockStsCredential(); StsConfig.getInstance().setSecurityCredentials(JacksonUtils.toJson(stsCredential)); assertEquals(stsCredential.toString(), StsCredentialHolder.getInstance().getStsCredential().toString()); } @Test void testGetStsCredentialFromRequest() throws Exception { StsCredential stsCredential = buildMockStsCredential(); mockResult = new HttpRestResult(); mockResult.setData(JacksonUtils.toJson(stsCredential)); mockResult.setCode(200); when(nacosRestTemplate.get(any(), any(), any(), any())).thenReturn(mockResult); assertEquals(stsCredential.toString(), StsCredentialHolder.getInstance().getStsCredential().toString()); } @Test void testGetStsCredentialFromRequestFailure() throws Exception { assertThrows(NacosRuntimeException.class, () -> { mockResult = new HttpRestResult(); mockResult.setData(""); mockResult.setCode(500); when(nacosRestTemplate.get(any(), any(), any(), any())).thenReturn(mockResult); StsCredentialHolder.getInstance().getStsCredential(); }); } @Test void testGetStsCredentialFromRequestException() throws Exception { assertThrows(NacosRuntimeException.class, () -> { when(nacosRestTemplate.get(any(), any(), any(), any())).thenThrow(new RuntimeException("test")); StsCredentialHolder.getInstance().getStsCredential(); }); } private StsCredential buildMockStsCredential() { StsCredential stsCredential = new StsCredential(); stsCredential.setAccessKeyId("test-sts-ak"); stsCredential.setAccessKeySecret("test-sts-sk"); stsCredential.setSecurityToken("test-sts-token"); stsCredential.setExpiration(new Date(System.currentTimeMillis() + 1000000)); stsCredential.setCode("200"); stsCredential.setLastUpdated(new Date()); return stsCredential; } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/injector/AbstractResourceInjectorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class AbstractResourceInjectorTest { AbstractResourceInjector injector; @BeforeEach void setUp() { injector = new AbstractResourceInjector() { }; } /** * TODO, fill test case after AbstractResourceInjector include default logic. */ @Test void testDoInject() { injector.doInject(null, null, null); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/injector/ConfigResourceInjectorTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.client.auth.ram.RamConstants; import com.alibaba.nacos.client.auth.ram.RamContext; import com.alibaba.nacos.client.auth.ram.identify.IdentifyConstants; import com.alibaba.nacos.client.auth.ram.identify.StsConfig; import com.alibaba.nacos.client.auth.ram.identify.StsCredential; import com.alibaba.nacos.client.auth.ram.identify.StsCredentialHolder; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.Date; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigResourceInjectorTest { private ConfigResourceInjector configResourceInjector; private RamContext ramContext; private RequestResource resource; private String cachedSecurityCredentialsUrl; private String cachedSecurityCredentials; private StsCredential stsCredential; @BeforeEach void setUp() throws Exception { configResourceInjector = new ConfigResourceInjector(); ramContext = new RamContext(); ramContext.setAccessKey(PropertyKeyConst.ACCESS_KEY); ramContext.setSecretKey(PropertyKeyConst.SECRET_KEY); resource = new RequestResource(); resource.setType(SignType.CONFIG); resource.setNamespace("tenant"); resource.setGroup("group"); cachedSecurityCredentialsUrl = StsConfig.getInstance().getSecurityCredentialsUrl(); cachedSecurityCredentials = StsConfig.getInstance().getSecurityCredentials(); StsConfig.getInstance().setSecurityCredentialsUrl(""); StsConfig.getInstance().setSecurityCredentials(""); stsCredential = new StsCredential(); } @AfterEach void tearDown() throws NoSuchFieldException, IllegalAccessException { StsConfig.getInstance().setSecurityCredentialsUrl(cachedSecurityCredentialsUrl); StsConfig.getInstance().setSecurityCredentials(cachedSecurityCredentials); clearForSts(); } @Test void testDoInjectWithFullResource() throws Exception { LoginIdentityContext actual = new LoginIdentityContext(); configResourceInjector.doInject(resource, ramContext, actual); assertEquals(3, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("Spas-AccessKey")); assertTrue(actual.getAllKey().contains("Timestamp")); assertTrue(actual.getAllKey().contains("Spas-Signature")); } @Test void testDoInjectWithTenant() throws Exception { resource.setGroup(""); LoginIdentityContext actual = new LoginIdentityContext(); configResourceInjector.doInject(resource, ramContext, actual); assertEquals(3, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("Spas-AccessKey")); assertTrue(actual.getAllKey().contains("Timestamp")); assertTrue(actual.getAllKey().contains("Spas-Signature")); } @Test void testDoInjectWithGroup() throws Exception { resource.setNamespace(""); LoginIdentityContext actual = new LoginIdentityContext(); configResourceInjector.doInject(resource, ramContext, actual); assertEquals(3, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("Spas-AccessKey")); assertTrue(actual.getAllKey().contains("Timestamp")); assertTrue(actual.getAllKey().contains("Spas-Signature")); } @Test void testDoInjectWithoutResource() throws Exception { resource = new RequestResource(); LoginIdentityContext actual = new LoginIdentityContext(); configResourceInjector.doInject(resource, ramContext, actual); assertEquals(3, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("Spas-AccessKey")); assertTrue(actual.getAllKey().contains("Timestamp")); assertTrue(actual.getAllKey().contains("Spas-Signature")); } @Test void testDoInjectForSts() throws NoSuchFieldException, IllegalAccessException { prepareForSts(); LoginIdentityContext actual = new LoginIdentityContext(); configResourceInjector.doInject(resource, ramContext, actual); assertEquals(4, actual.getAllKey().size()); assertEquals("test-sts-ak", actual.getParameter("Spas-AccessKey")); assertTrue(actual.getAllKey().contains("Timestamp")); assertTrue(actual.getAllKey().contains("Spas-Signature")); assertTrue(actual.getAllKey().contains(IdentifyConstants.SECURITY_TOKEN_HEADER)); } @Test void testDoInjectForV4Sign() { LoginIdentityContext actual = new LoginIdentityContext(); ramContext.setRegionId("cn-hangzhou"); configResourceInjector.doInject(resource, ramContext, actual); assertEquals(4, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("Spas-AccessKey")); assertEquals(RamConstants.V4, actual.getParameter(RamConstants.SIGNATURE_VERSION)); assertTrue(actual.getAllKey().contains("Timestamp")); assertTrue(actual.getAllKey().contains("Spas-Signature")); } private void prepareForSts() throws NoSuchFieldException, IllegalAccessException { StsConfig.getInstance().setSecurityCredentialsUrl("test"); Field field = StsCredentialHolder.class.getDeclaredField("stsCredential"); field.setAccessible(true); field.set(StsCredentialHolder.getInstance(), stsCredential); stsCredential.setAccessKeyId("test-sts-ak"); stsCredential.setAccessKeySecret("test-sts-sk"); stsCredential.setSecurityToken("test-sts-token"); stsCredential.setExpiration(new Date(System.currentTimeMillis() + 1000000)); } private void clearForSts() throws NoSuchFieldException, IllegalAccessException { StsConfig.getInstance().setSecurityCredentialsUrl(null); Field field = StsCredentialHolder.class.getDeclaredField("stsCredential"); field.setAccessible(true); field.set(StsCredentialHolder.getInstance(), null); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/injector/NamingResourceInjectorTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.injector; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.client.auth.ram.RamConstants; import com.alibaba.nacos.client.auth.ram.RamContext; import com.alibaba.nacos.client.auth.ram.identify.StsConfig; import com.alibaba.nacos.client.auth.ram.identify.StsCredential; import com.alibaba.nacos.client.auth.ram.identify.StsCredentialHolder; import com.alibaba.nacos.client.auth.ram.utils.CalculateV4SigningKeyUtil; import com.alibaba.nacos.client.auth.ram.utils.SignUtil; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.Date; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class NamingResourceInjectorTest { private NamingResourceInjector namingResourceInjector; private RamContext ramContext; private RequestResource resource; private StsCredential stsCredential; @BeforeEach void setUp() throws Exception { namingResourceInjector = new NamingResourceInjector(); ramContext = new RamContext(); ramContext.setAccessKey(PropertyKeyConst.ACCESS_KEY); ramContext.setSecretKey(PropertyKeyConst.SECRET_KEY); stsCredential = new StsCredential(); StsConfig.getInstance().setRamRoleName(null); } @AfterEach void tearDown() throws NoSuchFieldException, IllegalAccessException { clearForSts(); } @Test void testDoInjectWithEmpty() throws Exception { resource = RequestResource.namingBuilder().setResource("").build(); LoginIdentityContext actual = new LoginIdentityContext(); namingResourceInjector.doInject(resource, ramContext, actual); assertEquals(3, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("ak")); assertTrue(Long.parseLong(actual.getParameter("data")) - System.currentTimeMillis() < 100); String expectSign = SignUtil.sign(actual.getParameter("data"), PropertyKeyConst.SECRET_KEY); assertEquals(expectSign, actual.getParameter("signature")); } @Test void testDoInjectWithGroup() throws Exception { resource = RequestResource.namingBuilder().setResource("test@@aaa").setGroup("group").build(); LoginIdentityContext actual = new LoginIdentityContext(); namingResourceInjector.doInject(resource, ramContext, actual); assertEquals(3, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("ak")); assertTrue(actual.getParameter("data").endsWith("@@test@@aaa")); String expectSign = SignUtil.sign(actual.getParameter("data"), PropertyKeyConst.SECRET_KEY); assertEquals(expectSign, actual.getParameter("signature")); } @Test void testDoInjectWithoutGroup() throws Exception { resource = RequestResource.namingBuilder().setResource("aaa").setGroup("group").build(); LoginIdentityContext actual = new LoginIdentityContext(); namingResourceInjector.doInject(resource, ramContext, actual); assertTrue(actual.getParameter("data").endsWith("@@group@@aaa")); assertEquals(3, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("ak")); String expectSign = SignUtil.sign(actual.getParameter("data"), PropertyKeyConst.SECRET_KEY); assertEquals(expectSign, actual.getParameter("signature")); } @Test void testDoInjectWithGroupForSts() throws Exception { prepareForSts(); resource = RequestResource.namingBuilder().setResource("test@@aaa").setGroup("group").build(); LoginIdentityContext actual = new LoginIdentityContext(); namingResourceInjector.doInject(resource, ramContext, actual); assertEquals(4, actual.getAllKey().size()); assertEquals("test-sts-ak", actual.getParameter("ak")); assertTrue(actual.getParameter("data").endsWith("@@test@@aaa")); String expectSign = SignUtil.sign(actual.getParameter("data"), "test-sts-sk"); assertEquals(expectSign, actual.getParameter("signature")); } @Test void testDoInjectWithoutGroupForSts() throws Exception { prepareForSts(); resource = RequestResource.namingBuilder().setResource("aaa").setGroup("group").build(); LoginIdentityContext actual = new LoginIdentityContext(); namingResourceInjector.doInject(resource, ramContext, actual); assertEquals(4, actual.getAllKey().size()); assertEquals("test-sts-ak", actual.getParameter("ak")); assertTrue(actual.getParameter("data").endsWith("@@group@@aaa")); String expectSign = SignUtil.sign(actual.getParameter("data"), "test-sts-sk"); assertEquals(expectSign, actual.getParameter("signature")); } @Test void testDoInjectForStsWithException() throws Exception { prepareForSts(); stsCredential.setExpiration(new Date()); resource = RequestResource.namingBuilder().setResource("aaa").setGroup("group").build(); LoginIdentityContext actual = new LoginIdentityContext(); namingResourceInjector.doInject(resource, ramContext, actual); assertEquals(0, actual.getAllKey().size()); } @Test void testDoInjectForV4Sign() throws Exception { resource = RequestResource.namingBuilder().setResource("test@@aaa").setGroup("group").build(); LoginIdentityContext actual = new LoginIdentityContext(); ramContext.setRegionId("cn-hangzhou"); namingResourceInjector.doInject(resource, ramContext, actual); assertEquals(4, actual.getAllKey().size()); assertEquals(PropertyKeyConst.ACCESS_KEY, actual.getParameter("ak")); assertEquals(RamConstants.V4, actual.getParameter(RamConstants.SIGNATURE_VERSION)); assertTrue(actual.getParameter("data").endsWith("@@test@@aaa")); String signatureKey = CalculateV4SigningKeyUtil.finalSigningKeyStringWithDefaultInfo( PropertyKeyConst.SECRET_KEY, "cn-hangzhou"); String expectSign = SignUtil.sign(actual.getParameter("data"), signatureKey); assertEquals(expectSign, actual.getParameter("signature")); } private void prepareForSts() throws NoSuchFieldException, IllegalAccessException { StsConfig.getInstance().setSecurityCredentialsUrl("test"); Field field = StsCredentialHolder.class.getDeclaredField("stsCredential"); field.setAccessible(true); field.set(StsCredentialHolder.getInstance(), stsCredential); stsCredential.setAccessKeyId("test-sts-ak"); stsCredential.setAccessKeySecret("test-sts-sk"); stsCredential.setSecurityToken("test-sts-token"); stsCredential.setExpiration(new Date(System.currentTimeMillis() + 1000000)); } private void clearForSts() throws NoSuchFieldException, IllegalAccessException { StsConfig.getInstance().setSecurityCredentialsUrl(null); StsConfig.getInstance().setSecurityCredentials(null); Field field = StsCredentialHolder.class.getDeclaredField("stsCredential"); field.setAccessible(true); field.set(StsCredentialHolder.getInstance(), null); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/utils/CalculateV4SigningKeyUtilTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import com.alibaba.nacos.client.auth.ram.RamConstants; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class CalculateV4SigningKeyUtilTest { @Test void testFinalSigningKeyStringWithException() { assertThrows(RuntimeException.class, () -> CalculateV4SigningKeyUtil.finalSigningKeyString("", "2021-01-01", "cn-hangzhou", RamConstants.SIGNATURE_V4_PRODUCE, "non-exist")); } @Test void testFinalSigningKeyStringWithDefaultInfo() { assertNotNull(CalculateV4SigningKeyUtil.finalSigningKeyStringWithDefaultInfo("", "cn-hangzhou")); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/utils/RamUtilTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.client.auth.ram.identify.CredentialService; import com.alibaba.nacos.client.auth.ram.identify.Credentials; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; public class RamUtilTest { private Properties properties; @BeforeEach public void setUp() throws Exception { SpasAdapter.freeCredentialInstance(); Credentials credentials = new Credentials("spasAk", "spasSk", "spasNamespaceId"); CredentialService.getInstance().setStaticCredential(credentials); properties = new Properties(); properties.setProperty(PropertyKeyConst.ACCESS_KEY, "userAk"); properties.setProperty(PropertyKeyConst.SECRET_KEY, "userSk"); } @AfterEach public void tearDown() throws Exception { SpasAdapter.freeCredentialInstance(); } @Test public void testGetAccessKeyWithUserAkSk() { assertEquals("userAk", RamUtil.getAccessKey(properties)); assertEquals("userSk", RamUtil.getSecretKey(properties)); } @Test public void testGetAccessKeyWithSpasAkSk() { assertEquals("spasAk", RamUtil.getAccessKey(new Properties())); assertEquals("spasSk", RamUtil.getSecretKey(new Properties())); } @Test public void testGetAccessKeyWithoutSpasAkSk() { Properties properties1 = new Properties(); properties1.setProperty(PropertyKeyConst.IS_USE_RAM_INFO_PARSING, "false"); assertNull(RamUtil.getAccessKey(properties1)); assertNull(RamUtil.getSecretKey(properties1)); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/utils/SignUtilTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class SignUtilTest { @Test void testSign() throws Exception { String actual = SignUtil.sign("aaa", "b"); assertEquals("DxyaKScrqL26yXYOuHXE3OwfQ0Y=", actual); } @Test void testSignWithException() throws Exception { assertThrows(Exception.class, () -> { SignUtil.sign(null, "b"); }); } @Test void testSignWithException2() throws Exception { assertThrows(Exception.class, () -> { SignUtil.sign("aaa".getBytes(StandardCharsets.UTF_8), "b".getBytes(StandardCharsets.UTF_8), null); }); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/auth/ram/utils/SpasAdapterTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.auth.ram.utils; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; class SpasAdapterTest { @Test void test() { assertNull(SpasAdapter.getAk()); assertNull(SpasAdapter.getSk()); Assertions.assertDoesNotThrow(() -> { SpasAdapter.freeCredentialInstance(); }); } @Test void testSign() { assertNull(SpasAdapter.getSignHeaders("", "", "123")); final Map map1 = SpasAdapter.getSignHeaders("aa", "bb", "123"); assertEquals(2, map1.size()); assertEquals(SpasAdapter.signWithHmacSha1Encrypt("bb+aa+" + map1.get("Timestamp"), "123"), map1.get("Spas-Signature")); final Map map2 = SpasAdapter.getSignHeaders("aa", "", "123"); assertEquals(2, map2.size()); assertEquals(SpasAdapter.signWithHmacSha1Encrypt("aa" + "+" + map2.get("Timestamp"), "123"), map2.get("Spas-Signature")); final Map map3 = SpasAdapter.getSignHeaders("", "bb", "123"); assertEquals(2, map3.size()); assertEquals(SpasAdapter.signWithHmacSha1Encrypt(map3.get("Timestamp"), "123"), map3.get("Spas-Signature")); } @Test void testSign2() { assertNull(SpasAdapter.getSignHeaders((Map) null, "123")); Map param1 = new HashMap<>(); param1.put("tenant", "bb"); param1.put("group", "aa"); final Map map1 = SpasAdapter.getSignHeaders(param1, "123"); assertEquals(2, map1.size()); assertEquals(SpasAdapter.signWithHmacSha1Encrypt("bb+aa+" + map1.get("Timestamp"), "123"), map1.get("Spas-Signature")); } @Test void testGetSignHeadersWithoutTenant() { Map param1 = new HashMap<>(); param1.put("group", "aa"); final Map map1 = SpasAdapter.getSignHeaders(param1, "123"); assertEquals(2, map1.size()); assertEquals(SpasAdapter.signWithHmacSha1Encrypt("aa+" + map1.get("Timestamp"), "123"), map1.get("Spas-Signature")); } @Test void testSignWithHmacSha1EncryptWithException() { assertThrows(Exception.class, () -> { SpasAdapter.signWithHmacSha1Encrypt(null, "123"); }); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/env/NacosClientPropertiesTest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import java.util.Properties; 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; class NacosClientPropertiesTest { @BeforeAll static void init() { System.setProperty("nacos.env.first", "jvm"); } @AfterAll static void teardown() { System.clearProperty("nacos.env.first"); } @Test void testGetProperty() { NacosClientProperties.PROTOTYPE.setProperty("nacos.home", "/home/nacos"); final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home"); assertEquals("/home/nacos", value); } @Test void testGetPropertyMultiLayer() { NacosClientProperties.PROTOTYPE.setProperty("top.layer", "top"); final NacosClientProperties layerAEnv = NacosClientProperties.PROTOTYPE.derive(); layerAEnv.setProperty("a.layer", "a"); final NacosClientProperties layerBEnv = layerAEnv.derive(); layerBEnv.setProperty("b.layer", "b"); final NacosClientProperties layerCEnv = layerBEnv.derive(); layerCEnv.setProperty("c.layer", "c"); String value = layerCEnv.getProperty("c.layer"); assertEquals("c", value); value = layerCEnv.getProperty("b.layer"); assertEquals("b", value); value = layerCEnv.getProperty("a.layer"); assertEquals("a", value); value = layerCEnv.getProperty("top.layer"); assertEquals("top", value); } @Test void testGetPropertyDefaultValue() { final String value = NacosClientProperties.PROTOTYPE.getProperty("nacos.home.default", "/home/default_value"); assertEquals("/home/default_value", value); } @Test void testGetBoolean() { NacosClientProperties.PROTOTYPE.setProperty("use.cluster", "true"); final Boolean value = NacosClientProperties.PROTOTYPE.getBoolean("use.cluster"); assertTrue(value); } @Test void testGetBooleanDefaultValue() { final Boolean value = NacosClientProperties.PROTOTYPE.getBoolean("use.cluster.default", false); assertFalse(value); } @Test void testGetInteger() { NacosClientProperties.PROTOTYPE.setProperty("max.timeout", "200"); final Integer value = NacosClientProperties.PROTOTYPE.getInteger("max.timeout"); assertEquals(200, value.intValue()); } @Test void testGetIntegerDefaultValue() { final Integer value = NacosClientProperties.PROTOTYPE.getInteger("max.timeout.default", 400); assertEquals(400, value.intValue()); } @Test void testGetLong() { NacosClientProperties.PROTOTYPE.setProperty("connection.timeout", "200"); final Long value = NacosClientProperties.PROTOTYPE.getLong("connection.timeout"); assertEquals(200L, value.longValue()); } @Test void testGetLongDefault() { final Long value = NacosClientProperties.PROTOTYPE.getLong("connection.timeout.default", 400L); assertEquals(400L, value.longValue()); } @Test void setProperty() { NacosClientProperties.PROTOTYPE.setProperty("nacos.set.property", "true"); final String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.set.property"); assertEquals("true", ret); } @Test void setPropertyWithScope() { final NacosClientProperties properties = NacosClientProperties.PROTOTYPE.derive(); properties.setProperty("nacos.set.property.scope", "config"); String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.set.property.scope"); assertNull(ret); ret = properties.getProperty("nacos.set.property.scope"); assertEquals("config", ret); } @Test void testAddProperties() { Properties properties = new Properties(); properties.setProperty("nacos.add.properties", "true"); NacosClientProperties.PROTOTYPE.addProperties(properties); final String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.add.properties"); assertEquals("true", ret); } @Test void testAddPropertiesWithScope() { Properties properties = new Properties(); properties.setProperty("nacos.add.properties.scope", "config"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(); nacosClientProperties.addProperties(properties); String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.add.properties.scope"); assertNull(ret); ret = nacosClientProperties.getProperty("nacos.add.properties.scope"); assertEquals("config", ret); } @Test void testTestDerive() { Properties properties = new Properties(); properties.setProperty("nacos.derive.properties.scope", "derive"); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); final String value = nacosClientProperties.getProperty("nacos.derive.properties.scope"); assertEquals("derive", value); } @Test void testContainsKey() { NacosClientProperties.PROTOTYPE.setProperty("nacos.contains.key", "true"); boolean ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.key"); assertTrue(ret); ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.key.in.sys"); assertFalse(ret); } @Test void testContainsKeyMultiLayers() { NacosClientProperties.PROTOTYPE.setProperty("top.layer", "top"); final NacosClientProperties layerAEnv = NacosClientProperties.PROTOTYPE.derive(); layerAEnv.setProperty("a.layer", "a"); final NacosClientProperties layerBEnv = layerAEnv.derive(); layerBEnv.setProperty("b.layer", "b"); final NacosClientProperties layerCEnv = layerBEnv.derive(); layerCEnv.setProperty("c.layer", "c"); boolean exist = layerCEnv.containsKey("c.layer"); assertTrue(exist); exist = layerCEnv.containsKey("b.layer"); assertTrue(exist); exist = layerCEnv.containsKey("a.layer"); assertTrue(exist); exist = layerCEnv.containsKey("top.layer"); assertTrue(exist); } @Test void testContainsKeyWithScope() { NacosClientProperties.PROTOTYPE.setProperty("nacos.contains.global.scope", "global"); final NacosClientProperties namingProperties = NacosClientProperties.PROTOTYPE.derive(); namingProperties.setProperty("nacos.contains.naming.scope", "naming"); boolean ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.global.scope"); assertTrue(ret); ret = NacosClientProperties.PROTOTYPE.containsKey("nacos.contains.naming.scope"); assertFalse(ret); ret = namingProperties.containsKey("nacos.contains.naming.scope"); assertTrue(ret); ret = namingProperties.containsKey("nacos.contains.global.scope"); assertTrue(ret); } @Test void testAsProperties() { NacosClientProperties.PROTOTYPE.setProperty("nacos.as.properties", "true"); final Properties properties = NacosClientProperties.PROTOTYPE.asProperties(); assertNotNull(properties); assertEquals("true", properties.getProperty("nacos.as.properties")); } @Test void testAsPropertiesWithScope() { NacosClientProperties.PROTOTYPE.setProperty("nacos.as.properties.global.scope", "global"); NacosClientProperties.PROTOTYPE.setProperty("nacos.server.addr.scope", "global"); final NacosClientProperties configProperties = NacosClientProperties.PROTOTYPE.derive(); configProperties.setProperty("nacos.server.addr.scope", "config"); final Properties properties = configProperties.asProperties(); assertNotNull(properties); String ret = properties.getProperty("nacos.as.properties.global.scope"); assertEquals("global", ret); ret = properties.getProperty("nacos.server.addr.scope"); assertEquals("config", ret); } @Test void testGetPropertyWithScope() { NacosClientProperties.PROTOTYPE.setProperty("nacos.global.scope", "global"); final NacosClientProperties configProperties = NacosClientProperties.PROTOTYPE.derive(); configProperties.setProperty("nacos.config.scope", "config"); final NacosClientProperties namingProperties = NacosClientProperties.PROTOTYPE.derive(); namingProperties.setProperty("nacos.naming.scope", "naming"); String ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.global.scope"); assertEquals("global", ret); ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.config.scope"); assertNull(ret); ret = NacosClientProperties.PROTOTYPE.getProperty("nacos.naming.scope"); assertNull(ret); ret = configProperties.getProperty("nacos.config.scope"); assertEquals("config", ret); ret = configProperties.getProperty("nacos.global.scope"); assertEquals("global", ret); ret = configProperties.getProperty("nacos.naming.scope"); assertNull(ret); ret = namingProperties.getProperty("nacos.naming.scope"); assertEquals("naming", ret); ret = namingProperties.getProperty("nacos.global.scope"); assertEquals("global", ret); ret = namingProperties.getProperty("nacos.config.scope"); assertNull(ret); } @Test void testGetPropertyFrom() { System.setProperty("nacos.home.default.test", "/home/jvm_args"); NacosClientProperties.PROTOTYPE.setProperty("nacos.home.default.test", "/home/properties_args"); assertEquals("/home/jvm_args", NacosClientProperties.PROTOTYPE.getPropertyFrom(SourceType.JVM, "nacos.home.default.test")); assertEquals("/home/properties_args", NacosClientProperties.PROTOTYPE.getPropertyFrom(SourceType.PROPERTIES, "nacos.home.default.test")); assertEquals(NacosClientProperties.PROTOTYPE.getPropertyFrom(null, "nacos.home.default.test"), NacosClientProperties.PROTOTYPE.getProperty("nacos.home.default.test")); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/env/SearchablePropertiesTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import com.alibaba.nacos.client.constant.Constants; 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 java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; /** * Additional test cases for SearchableProperties. * *

    Common cases see {@link NacosClientPropertiesTest}.

    */ class SearchablePropertiesTest { Method initMethod; @BeforeAll static void init() { System.setProperty(Constants.SysEnv.NACOS_ENV_FIRST, "jvm"); } @AfterAll static void teardown() { System.clearProperty(Constants.SysEnv.NACOS_ENV_FIRST); } @BeforeEach void setUp() throws Exception { initMethod = SearchableProperties.class.getDeclaredMethod("init"); initMethod.setAccessible(true); } @AfterEach void tearDown() throws Exception { init(); initMethod.invoke(null); } @Test void testInitWithInvalidOrder() throws IllegalAccessException, InvocationTargetException { System.setProperty(Constants.SysEnv.NACOS_ENV_FIRST, "invalid"); List order = (List) initMethod.invoke(null); assertOrder(order, SourceType.PROPERTIES, SourceType.JVM, SourceType.ENV); } @Test void testInitWithoutSpecifiedOrder() throws IllegalAccessException, InvocationTargetException { System.clearProperty(Constants.SysEnv.NACOS_ENV_FIRST); List order = (List) initMethod.invoke(null); assertOrder(order, SourceType.PROPERTIES, SourceType.JVM, SourceType.ENV); } private void assertOrder(List order, SourceType... sourceTypes) { assertEquals(sourceTypes.length, order.size()); for (int i = 0; i < sourceTypes.length; i++) { assertEquals(sourceTypes[i], order.get(i)); } } @Test void testGetPropertyFromEnv() { System.setProperty("testFromSource", "jvm"); NacosClientProperties properties = SearchableProperties.INSTANCE.derive(); properties.setProperty("testFromSource", "properties"); assertNull(properties.getPropertyFrom(SourceType.ENV, "testFromSource")); } @Test void testGetPropertyFromUnknown() { System.setProperty("testFromSource", "jvm"); NacosClientProperties properties = SearchableProperties.INSTANCE.derive(); properties.setProperty("testFromSource", "properties"); assertEquals(properties.getProperty("testFromSource"), properties.getPropertyFrom(SourceType.UNKNOWN, "testFromSource")); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/env/SystemEnvPropertySourceTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; /** * Additional test cases for SystemEnvPropertySource. * *

    Common cases see {@link NacosClientPropertiesTest}.

    */ class SystemEnvPropertySourceTest { SystemEnvPropertySource systemEnvPropertySource; private Map mockEnvMap; @BeforeEach void setUp() throws Exception { systemEnvPropertySource = new SystemEnvPropertySource(); mockEnvMap = new HashMap<>(); Field envField = SystemEnvPropertySource.class.getDeclaredField("env"); envField.setAccessible(true); envField.set(systemEnvPropertySource, mockEnvMap); mockEnvMap.put("testcase1", "value1"); mockEnvMap.put("test_case_2", "value2"); mockEnvMap.put("TESTCASE3", "value3"); mockEnvMap.put("TEST_CASE_4", "value4"); } @Test void testGetEnvForLowerCaseKey() { assertEquals("value1", systemEnvPropertySource.getProperty("testcase1")); } @Test void testGetEnvForLowerCaseKeyWithDot() { assertEquals("value2", systemEnvPropertySource.getProperty("test.case.2")); } @Test void testGetEnvForLowerCaseKeyWithHyphen() { assertEquals("value2", systemEnvPropertySource.getProperty("test-case-2")); } @Test void testGetEnvForLowerCaseKeyWithHyphenAndDot() { assertEquals("value2", systemEnvPropertySource.getProperty("test.case-2")); } @Test void testGetEnvForUpperCaseKey() { assertEquals("value3", systemEnvPropertySource.getProperty("TESTCASE3")); } @Test void testGetEnvForUpperCaseKeyWithDot() { assertEquals("value4", systemEnvPropertySource.getProperty("TEST.CASE.4")); } @Test void testGetEnvForUpperCaseKeyWithHyphen() { assertEquals("value4", systemEnvPropertySource.getProperty("TEST-CASE-4")); } @Test void testGetEnvForUpperCaseKeyWithHyphenAndDot() { assertEquals("value4", systemEnvPropertySource.getProperty("TEST_CASE.4")); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/env/convert/CompositeConverterTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.env.convert; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.MissingFormatArgumentException; 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 CompositeConverterTest { CompositeConverter compositeConverter; @BeforeEach void setUp() throws Exception { compositeConverter = new CompositeConverter(); } @AfterEach void tearDown() throws Exception { } @Test void testConvertNotSupportType() { assertThrows(MissingFormatArgumentException.class, () -> { compositeConverter.convert("test", CompositeConverter.class); }); } @Test void testConvertBooleanForEmptyProperty() { assertNull(compositeConverter.convert(null, Boolean.class)); } @Test void testConvertBooleanTrue() { assertTrue(compositeConverter.convert("true", Boolean.class)); assertTrue(compositeConverter.convert("on", Boolean.class)); assertTrue(compositeConverter.convert("yes", Boolean.class)); assertTrue(compositeConverter.convert("1", Boolean.class)); } @Test void testConvertBooleanFalse() { assertFalse(compositeConverter.convert("false", Boolean.class)); assertFalse(compositeConverter.convert("off", Boolean.class)); assertFalse(compositeConverter.convert("no", Boolean.class)); assertFalse(compositeConverter.convert("0", Boolean.class)); } @Test void testConvertBooleanIllegal() { assertThrows(IllegalArgumentException.class, () -> { compositeConverter.convert("aaa", Boolean.class); }); } @Test void testConvertIntegerForEmptyProperty() { assertNull(compositeConverter.convert(null, Integer.class)); } @Test void testConvertInteger() { assertEquals(100, (int) compositeConverter.convert("100", Integer.class)); assertEquals(Integer.MAX_VALUE, (int) compositeConverter.convert(String.valueOf(Integer.MAX_VALUE), Integer.class)); assertEquals(Integer.MIN_VALUE, (int) compositeConverter.convert(String.valueOf(Integer.MIN_VALUE), Integer.class)); } @Test void testConvertIntegerIllegal() { assertThrows(IllegalArgumentException.class, () -> { compositeConverter.convert("aaa", Integer.class); }); } @Test void testConvertLongForEmptyProperty() { assertNull(compositeConverter.convert(null, Long.class)); } @Test void testConvertLong() { assertEquals(100L, (long) compositeConverter.convert("100", Long.class)); assertEquals(Long.MAX_VALUE, (long) compositeConverter.convert(String.valueOf(Long.MAX_VALUE), Long.class)); assertEquals(Long.MIN_VALUE, (long) compositeConverter.convert(String.valueOf(Long.MIN_VALUE), Long.class)); } @Test void testConvertLongIllegal() { assertThrows(IllegalArgumentException.class, () -> { compositeConverter.convert("aaa", Long.class); }); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/utils/AppNameUtilsTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.client.constant.Constants; 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; class AppNameUtilsTest { @BeforeEach void setUp() throws Exception { } @AfterEach void tearDown() throws Exception { System.clearProperty(Constants.SysEnv.PROJECT_NAME); System.clearProperty("jboss.server.home.dir"); System.clearProperty("jetty.home"); System.clearProperty("catalina.base"); } @Test void testGetAppNameByDefault() { String appName = AppNameUtils.getAppName(); assertEquals("unknown", appName); } @Test void testGetAppNameByProjectName() { System.setProperty(Constants.SysEnv.PROJECT_NAME, "testAppName"); String appName = AppNameUtils.getAppName(); assertEquals("testAppName", appName); } @Test void testGetAppNameByServerTypeForJboss() { System.setProperty("jboss.server.home.dir", "/home/admin/testAppName/"); String appName = AppNameUtils.getAppName(); assertEquals("testAppName", appName); } @Test void testGetAppNameByServerTypeForJetty() { System.setProperty("jetty.home", "/home/admin/testAppName/"); String appName = AppNameUtils.getAppName(); assertEquals("testAppName", appName); } @Test void testGetAppNameByServerTypeForTomcat() { System.setProperty("catalina.base", "/home/admin/testAppName/"); String appName = AppNameUtils.getAppName(); assertEquals("testAppName", appName); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/utils/ClientBasicParamUtilTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.client.env.NacosClientProperties; import com.alibaba.nacos.client.env.SourceType; import com.alibaba.nacos.common.utils.VersionUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ClientBasicParamUtilTest { private String defaultAppKey; private String defaultContextPath; private String defaultVersion; private String defaultNodesPath; @BeforeEach void before() { defaultAppKey = ""; defaultContextPath = "nacos"; defaultVersion = VersionUtils.version; defaultNodesPath = "serverlist"; } @AfterEach void after() { ClientBasicParamUtil.setAppKey(defaultAppKey); ClientBasicParamUtil.setDefaultContextPath(defaultContextPath); ClientBasicParamUtil.setClientVersion(defaultVersion); ClientBasicParamUtil.setDefaultNodesPath(defaultNodesPath); System.clearProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL); } @Test void testGetAppKey() { String defaultVal = ClientBasicParamUtil.getAppKey(); assertEquals(defaultAppKey, defaultVal); String expect = "test"; ClientBasicParamUtil.setAppKey(expect); assertEquals(expect, ClientBasicParamUtil.getAppKey()); } @Test void testGetDefaultContextPath() { String defaultVal = ClientBasicParamUtil.getDefaultContextPath(); assertEquals(defaultContextPath, defaultVal); String expect = "test"; ClientBasicParamUtil.setDefaultContextPath(expect); assertEquals(expect, ClientBasicParamUtil.getDefaultContextPath()); } @Test void testGetClientVersion() { String defaultVal = ClientBasicParamUtil.getClientVersion(); assertEquals(defaultVersion, defaultVal); String expect = "test"; ClientBasicParamUtil.setClientVersion(expect); assertEquals(expect, ClientBasicParamUtil.getClientVersion()); } @Test void testGetDefaultServerPort() { String actual = ClientBasicParamUtil.getDefaultServerPort(); assertEquals("8848", actual); } @Test void testGetDefaultNodesPath() { String defaultVal = ClientBasicParamUtil.getDefaultNodesPath(); assertEquals("serverlist", defaultVal); String expect = "test"; ClientBasicParamUtil.setDefaultNodesPath(expect); assertEquals(expect, ClientBasicParamUtil.getDefaultNodesPath()); } @Test void testParseNamespace() { String expect = "test"; Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.NAMESPACE, expect); final NacosClientProperties nacosClientProperties = NacosClientProperties.PROTOTYPE.derive(properties); String actual = ClientBasicParamUtil.parseNamespace(nacosClientProperties); assertEquals(expect, actual); } @Test void testParsingEndpointRule() { String url = "${test:www.example.com}"; String actual = ClientBasicParamUtil.parsingEndpointRule(url); assertEquals("www.example.com", actual); } @Test void testParsingEndpointRuleFromSystem() { System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL, "alibaba_aliware_endpoint_url"); assertEquals("alibaba_aliware_endpoint_url", ClientBasicParamUtil.parsingEndpointRule(null)); } @Test void testParsingEndpointRuleFromSystemWithParam() { System.setProperty(PropertyKeyConst.SystemEnv.ALIBABA_ALIWARE_ENDPOINT_URL, "alibaba_aliware_endpoint_url"); assertEquals("alibaba_aliware_endpoint_url", ClientBasicParamUtil.parsingEndpointRule("${abc:xxx}")); } @Test void testGetInputParametersWithFullMode() { Properties properties = new Properties(); properties.setProperty("testKey", "testValue"); properties.setProperty(PropertyKeyConst.LOG_ALL_PROPERTIES, "true"); NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); String actual = ClientBasicParamUtil.getInputParameters(clientProperties.asProperties()); assertTrue(actual.startsWith( "Log nacos client init properties with Full mode, This mode is only used for debugging and troubleshooting.")); assertTrue(actual.contains("\ttestKey=testValue\n")); Properties envProperties = clientProperties.getProperties(SourceType.ENV); String envCaseKey = envProperties.stringPropertyNames().iterator().next(); String envCaseValue = envProperties.getProperty(envCaseKey); assertTrue(actual.contains(String.format("\t%s=%s\n", envCaseKey, envCaseValue))); } @Test void testGetInputParameters() { Properties properties = new Properties(); properties.setProperty("testKey", "testValue"); properties.setProperty(PropertyKeyConst.SERVER_ADDR, "localhost:8848"); properties.setProperty(PropertyKeyConst.PASSWORD, "testPassword"); NacosClientProperties clientProperties = NacosClientProperties.PROTOTYPE.derive(properties); String actual = ClientBasicParamUtil.getInputParameters(clientProperties.asProperties()); assertEquals("Nacos client key init properties: \n\tserverAddr=localhost:8848\n\tpassword=te********rd\n", actual); } @Test void testDesensitiseParameter() { String shortParameter = "aa"; assertEquals(shortParameter, ClientBasicParamUtil.desensitiseParameter(shortParameter)); String middleParameter = "aaa"; assertEquals("a*a", ClientBasicParamUtil.desensitiseParameter(middleParameter)); middleParameter = "aaaaaaa"; assertEquals("a*****a", ClientBasicParamUtil.desensitiseParameter(middleParameter)); String longParameter = "testPass"; assertEquals("te****ss", ClientBasicParamUtil.desensitiseParameter(longParameter)); } @Test void testGetNameSuffixByServerIps() { assertEquals("1.1.1.1-2.2.2.2_8848", ClientBasicParamUtil.getNameSuffixByServerIps("http://1.1.1.1", "2.2.2.2:8848")); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/utils/ContextPathUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * ContextPathUtil test. * * @author Wei.Wang * @date 2020/11/26 3:13 PM */ class ContextPathUtilTest { @Test void testNormalizeContextPath() { assertEquals("/nacos", ContextPathUtil.normalizeContextPath("/nacos")); assertEquals("/nacos", ContextPathUtil.normalizeContextPath("nacos")); assertEquals("", ContextPathUtil.normalizeContextPath("/")); assertEquals("", ContextPathUtil.normalizeContextPath("")); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/utils/TemplateUtilsTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.concurrent.Callable; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class TemplateUtilsTest { @Test void testStringNotEmptyAndThenExecuteSuccess() { String word = "run"; Runnable task = Mockito.mock(Runnable.class); TemplateUtils.stringNotEmptyAndThenExecute(word, task); Mockito.verify(task, Mockito.times(1)).run(); } @Test void testStringNotEmptyAndThenExecuteFail() { String word = ""; Runnable task = Mockito.mock(Runnable.class); TemplateUtils.stringNotEmptyAndThenExecute(word, task); Mockito.verify(task, Mockito.times(0)).run(); } @Test void testStringNotEmptyAndThenExecuteException() { String word = "run"; Runnable task = Mockito.mock(Runnable.class); doThrow(new RuntimeException("test")).when(task).run(); TemplateUtils.stringNotEmptyAndThenExecute(word, task); Mockito.verify(task, Mockito.times(1)).run(); // NO exception thrown } @Test void testStringEmptyAndThenExecuteSuccess() { String word = " "; String actual = TemplateUtils.stringEmptyAndThenExecute(word, () -> "call"); assertEquals("", actual); } @Test void testStringEmptyAndThenExecuteFail() { String word = ""; final String expect = "call"; String actual = TemplateUtils.stringEmptyAndThenExecute(word, () -> expect); assertEquals(expect, actual); } @Test void testStringEmptyAndThenExecuteException() throws Exception { Callable callable = mock(Callable.class); when(callable.call()).thenThrow(new RuntimeException("test")); String actual = TemplateUtils.stringEmptyAndThenExecute(null, callable); assertNull(actual); } @Test void testStringBlankAndThenExecuteSuccess() { String word = "success"; String actual = TemplateUtils.stringBlankAndThenExecute(word, () -> "call"); assertEquals(word, actual); } @Test void testStringBlankAndThenExecuteFail() { String word = " "; final String expect = "call"; String actual = TemplateUtils.stringBlankAndThenExecute(word, () -> expect); assertEquals(expect, actual); } @Test void testStringBlankAndThenExecuteException() throws Exception { Callable callable = mock(Callable.class); when(callable.call()).thenThrow(new RuntimeException("test")); String actual = TemplateUtils.stringBlankAndThenExecute(null, callable); assertNull(actual); } } ================================================ FILE: client-basic/src/test/java/com/alibaba/nacos/client/utils/TenantUtilTest.java ================================================ /* * * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.client.utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class TenantUtilTest { @AfterEach void tearDown() { System.clearProperty("acm.namespace"); System.clearProperty("ans.namespace"); } @Test void testGetUserTenantForAcm() { String expect = "test"; System.setProperty("acm.namespace", expect); String actual = TenantUtil.getUserTenantForAcm(); assertEquals(expect, actual); } @Test void testGetUserTenantForAns() { String expect = "test"; System.setProperty("ans.namespace", expect); String actual = TenantUtil.getUserTenantForAns(); assertEquals(expect, actual); } } ================================================ FILE: client-basic/src/test/resources/META-INF/services/com.alibaba.nacos.client.address.ServerListProvider ================================================ # # Copyright 1999-2024 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.client.address.mock.MockServerListProvider ================================================ FILE: client-basic/src/test/resources/spas.identity ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # accessKey=testAk secretKey=testSk tenantId=testTenantId ================================================ FILE: client-basic/src/test/resources/spas_docker.identity ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # env_spas_accessKey=testAk env_spas_secretKey=testSk ebv_spas_tenantId=testTenantId ================================================ FILE: client-basic/src/test/resources/spas_invalid.identity ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # accessKey= secretKey=testSk tenantId=testTenantId ================================================ FILE: client-basic/src/test/resources/spas_modified.identity ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # ================================================ FILE: cmdb/pom.xml ================================================ nacos-all com.alibaba.nacos ${revision} ../pom.xml 4.0.0 nacos-cmdb jar nacos-cmdb ${project.version} https://nacos.io ${project.groupId} nacos-core ${project.groupId} nacos-api org.springframework.boot spring-boot org.springframework.boot spring-boot-starter-web org.apache.maven.plugins maven-surefire-plugin true -Dnacos.standalone=true org.apache.maven.plugins maven-assembly-plugin com.alibaba.nacos.cmdb.CmdbApp jar-with-dependencies src/main/resources application.properties ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/CmdbApp.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * CMDB starter. * * @author nkorange * @since 0.7.0 */ @SpringBootApplication public class CmdbApp { public static void main(String[] args) { SpringApplication.run(CmdbApp.class, args); } } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/controllers/OperationController.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.controllers; import com.alibaba.nacos.cmdb.memory.CmdbProvider; import com.alibaba.nacos.cmdb.utils.UtilsAndCommons; import com.alibaba.nacos.core.utils.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import jakarta.servlet.http.HttpServletRequest; /** * Operation controller. * * @author nkorange * @since 0.7.0 */ @RestController @RequestMapping(UtilsAndCommons.NACOS_CMDB_CONTEXT + "/ops") public class OperationController { @Autowired private CmdbProvider cmdbProvider; /** * query label. * * @param request http request * @return query result * @throws Exception exception */ @RequestMapping(value = "/label", method = RequestMethod.GET) public String queryLabel(HttpServletRequest request) throws Exception { String entry = WebUtils.required(request, "entry"); String label = WebUtils.required(request, "label"); return cmdbProvider.queryLabel(entry, "ip", label); } } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/core/SwitchAndOptions.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.core; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; /** * Switch and options. * * @author nkorange * @since 0.7.0 */ @Component public class SwitchAndOptions { @Value("${nacos.cmdb.dumpTaskInterval:3600}") private int dumpTaskInterval; @Value("${nacos.cmdb.eventTaskInterval:10}") private int eventTaskInterval; @Value("${nacos.cmdb.labelTaskInterval:300}") private int labelTaskInterval; @Value("${nacos.cmdb.loadDataAtStart:false}") private boolean loadDataAtStart; public int getDumpTaskInterval() { return dumpTaskInterval; } public int getEventTaskInterval() { return eventTaskInterval; } public int getLabelTaskInterval() { return labelTaskInterval; } public boolean isLoadDataAtStart() { return loadDataAtStart; } } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/memory/CmdbProvider.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.memory; import com.alibaba.nacos.api.cmdb.pojo.Entity; import com.alibaba.nacos.api.cmdb.pojo.EntityEvent; import com.alibaba.nacos.api.cmdb.pojo.Label; import com.alibaba.nacos.api.cmdb.spi.CmdbService; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.cmdb.core.SwitchAndOptions; import com.alibaba.nacos.cmdb.service.CmdbReader; import com.alibaba.nacos.cmdb.service.CmdbWriter; import com.alibaba.nacos.cmdb.utils.CmdbExecutor; import com.alibaba.nacos.cmdb.utils.Loggers; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.JacksonUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; 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.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; /** * CMDB provider. * * @author nkorange * @since 0.7.0 */ @Component public class CmdbProvider implements CmdbReader, CmdbWriter { @Autowired private SwitchAndOptions switches; private CmdbService cmdbService; private final Collection services = NacosServiceLoader.load(CmdbService.class); private Map> entityMap = new ConcurrentHashMap<>(); private Map labelMap = new ConcurrentHashMap<>(); private Set entityTypeSet = new HashSet<>(); private long eventTimestamp = System.currentTimeMillis(); public CmdbProvider() throws NacosException { } private void initCmdbService() throws NacosException { Iterator iterator = services.iterator(); if (iterator.hasNext()) { cmdbService = iterator.next(); } if (cmdbService == null && switches.isLoadDataAtStart()) { throw new NacosException(NacosException.SERVER_ERROR, "Cannot initialize CmdbService!"); } } /** * load data. */ public void load() { if (!switches.isLoadDataAtStart()) { return; } // init label map: Set labelNames = cmdbService.getLabelNames(); if (labelNames == null || labelNames.isEmpty()) { Loggers.MAIN.warn("[LOAD] init label names failed!"); } else { for (String labelName : labelNames) { // If get null label, it's still ok. We will try it later when we meet this label: labelMap.put(labelName, cmdbService.getLabel(labelName)); } } // init entity type set: entityTypeSet = cmdbService.getEntityTypes(); // init entity map: entityMap = cmdbService.getAllEntities(); } /** * Init, called by spring. * * @throws NacosException nacos exception */ @PostConstruct public void init() throws NacosException { initCmdbService(); load(); CmdbExecutor.scheduleCmdbTask(new CmdbDumpTask(), switches.getDumpTaskInterval(), TimeUnit.SECONDS); CmdbExecutor.scheduleCmdbTask(new CmdbLabelTask(), switches.getLabelTaskInterval(), TimeUnit.SECONDS); CmdbExecutor.scheduleCmdbTask(new CmdbEventTask(), switches.getEventTaskInterval(), TimeUnit.SECONDS); } @Override public Entity queryEntity(String entityName, String entityType) { if (!entityMap.containsKey(entityType)) { return null; } return entityMap.get(entityType).get(entityName); } @Override public String queryLabel(String entityName, String entityType, String labelName) { Entity entity = queryEntity(entityName, entityType); if (entity == null) { return null; } return entity.getLabels().get(labelName); } @Override public List queryEntitiesByLabel(String labelName, String labelValue) { throw new UnsupportedOperationException("Not available now!"); } /** * Remove CMDB entity. * * @param entityName entity name * @param entityType entity type */ public void removeEntity(String entityName, String entityType) { if (!entityMap.containsKey(entityType)) { return; } entityMap.get(entityType).remove(entityName); } /** * Update entity. * * @param entity entity */ public void updateEntity(Entity entity) { if (!entityTypeSet.contains(entity.getType())) { return; } entityMap.get(entity.getType()).put(entity.getName(), entity); } public class CmdbLabelTask implements Runnable { @Override public void run() { Loggers.MAIN.debug("LABEL-TASK {}", "start dump."); if (cmdbService == null) { return; } try { Map tmpLabelMap = new HashMap<>(16); Set labelNames = cmdbService.getLabelNames(); if (labelNames == null || labelNames.isEmpty()) { Loggers.MAIN.warn("CMDB-LABEL-TASK {}", "load label names failed!"); } else { for (String labelName : labelNames) { // If get null label, it's still ok. We will try it later when we meet this label: tmpLabelMap.put(labelName, cmdbService.getLabel(labelName)); } if (Loggers.MAIN.isDebugEnabled()) { Loggers.MAIN.debug("LABEL-TASK {}", "got label map:" + JacksonUtils.toJson(tmpLabelMap)); } labelMap = tmpLabelMap; } } catch (Exception e) { Loggers.MAIN.error("CMDB-LABEL-TASK {}", "dump failed!", e); } finally { CmdbExecutor.scheduleCmdbTask(this, switches.getLabelTaskInterval(), TimeUnit.SECONDS); } } } public class CmdbDumpTask implements Runnable { @Override public void run() { try { Loggers.MAIN.debug("DUMP-TASK {}", "start dump."); if (cmdbService == null) { return; } // refresh entity map: entityMap = cmdbService.getAllEntities(); } catch (Exception e) { Loggers.MAIN.error("DUMP-TASK {}", "dump failed!", e); } finally { CmdbExecutor.scheduleCmdbTask(this, switches.getDumpTaskInterval(), TimeUnit.SECONDS); } } } public class CmdbEventTask implements Runnable { @Override public void run() { try { Loggers.MAIN.debug("EVENT-TASK {}", "start dump."); if (cmdbService == null) { return; } long current = System.currentTimeMillis(); List events = cmdbService.getEntityEvents(eventTimestamp); eventTimestamp = current; if (Loggers.MAIN.isDebugEnabled()) { Loggers.MAIN.debug("EVENT-TASK {}", "got events size:" + ", events:" + JacksonUtils.toJson(events)); } if (events != null && !events.isEmpty()) { for (EntityEvent event : events) { switch (event.getType()) { case ENTITY_REMOVE: removeEntity(event.getEntityName(), event.getEntityType()); break; case ENTITY_ADD_OR_UPDATE: updateEntity(cmdbService.getEntity(event.getEntityName(), event.getEntityType())); break; default: break; } } } } catch (Exception e) { Loggers.MAIN.error("CMDB-EVENT {}", "event task failed!", e); } finally { CmdbExecutor.scheduleCmdbTask(this, switches.getEventTaskInterval(), TimeUnit.SECONDS); } } } } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbReader.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.service; import com.alibaba.nacos.api.cmdb.pojo.Entity; import java.util.List; /** * CMDB Reader. * * @author nkorange * @since 0.7.0 */ public interface CmdbReader { /** * Get entity. * * @param entityName name of entity * @param entityType type of entity * @return entity */ Entity queryEntity(String entityName, String entityType); /** * Get label of entity. * * @param entityName name of entity * @param entityType type of entity * @param labelName label name * @return label value */ String queryLabel(String entityName, String entityType, String labelName); /** * Get entities of selected label. * * @param labelName name of label * @param labelValue value of label * @return list of entity */ List queryEntitiesByLabel(String labelName, String labelValue); } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/service/CmdbWriter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.service; /** * CMDB writer. * * @author nkorange * @since 0.7.0 */ public interface CmdbWriter { } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/CmdbExecutor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.utils; import com.alibaba.nacos.cmdb.CmdbApp; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.core.utils.ClassUtils; import com.alibaba.nacos.sys.env.EnvUtil; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * Cmdb executor. * * @author wangweizZZ * @date 2020/7/13 1:54 PM */ public class CmdbExecutor { private static final ScheduledExecutorService GLOBAL_EXECUTOR = ExecutorFactory.Managed .newScheduledExecutorService(ClassUtils.getCanonicalName(CmdbApp.class), EnvUtil.getAvailableProcessors(), new NameThreadFactory("com.alibaba.nacos.cmdb.global.executor")); public static void scheduleCmdbTask(Runnable runnable, long delay, TimeUnit unit) { GLOBAL_EXECUTOR.schedule(runnable, delay, unit); } } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/Loggers.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Loggers holder. * * @author nacos * @since 0.7.0 */ public class Loggers { public static final Logger MAIN = LoggerFactory.getLogger("com.alibaba.nacos.cmdb.main"); } ================================================ FILE: cmdb/src/main/java/com/alibaba/nacos/cmdb/utils/UtilsAndCommons.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.cmdb.utils; /** * Utils and constants. * * @author nkorange * @since 0.7.0 */ public class UtilsAndCommons { private static final String NACOS_SERVER_VERSION = "/v1"; public static final String NACOS_CMDB_CONTEXT = NACOS_SERVER_VERSION + "/cmdb"; } ================================================ FILE: codecov.yml ================================================ comment: behavior: default layout: reach,diff,flags,files,footer require_changes: false ================================================ FILE: common/pom.xml ================================================ com.alibaba.nacos nacos-all ${revision} ../pom.xml 4.0.0 nacos-common jar nacos-common ${project.version} https://nacos.io Nacos common pom.xml file ${client.java.version} ${client.java.version} org.slf4j slf4j-api commons-io commons-io org.apache.httpcomponents.client5 httpclient5 ${project.groupId} nacos-api com.fasterxml.jackson.core jackson-core com.fasterxml.jackson.core jackson-databind commons-logging commons-logging 1.2 test com.mysql mysql-connector-j test src/main/resources true nacos-version.txt META-INF/services/* maven-compiler-plugin ${maven-compiler-plugin.version} ${client.java.version} ${client.java.version} ${client.java.version} true true -parameters maven-compiler-plugin ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/Beta.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.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; /** * Means a class or method is in beta version. * * @author wuzhiguo */ @Documented @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface Beta { } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/JustForTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common; /** * Just for test. * * @author liaochuntao */ public @interface JustForTest { } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/NotThreadSafe.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.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; /** * Annotation that marks a method as not thread safe. * * @author zongtanghu */ @Documented @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.SOURCE) public @interface NotThreadSafe { } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/ability/AbstractAbilityControlManager.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.ability; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.ability.initializer.AbilityPostProcessor; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.spi.NacosServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * It is a capability control center, manager current node abilities or other control. * * @author Daydreamer * @date 2022/7/12 19:18 **/ public abstract class AbstractAbilityControlManager { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAbilityControlManager.class); /** * current node support abilities. */ protected final Map> currentNodeAbilities = new ConcurrentHashMap<>(); protected AbstractAbilityControlManager() { NotifyCenter.registerToPublisher(AbilityUpdateEvent.class, 16384); initAbilityTable(); } /** * initialize abilities. */ private void initAbilityTable() { LOGGER.info("Ready to get current node abilities..."); // get processors Map> abilities = initCurrentNodeAbilities(); // get abilities for (AbilityMode mode : AbilityMode.values()) { Map abilitiesTable = abilities.get(mode); if (abilitiesTable == null) { continue; } // check whether exist error key // check for developer for (AbilityKey abilityKey : abilitiesTable.keySet()) { if (!mode.equals(abilityKey.getMode())) { LOGGER.error( "You should not contain a other mode: {} in a specify mode: {} abilities set, error key: {}, please check again.", abilityKey.getMode(), mode, abilityKey); throw new IllegalStateException( "Except mode: " + mode + " but " + abilityKey + " mode: " + abilityKey.getMode() + ", please check again."); } } Collection processors = NacosServiceLoader.load(AbilityPostProcessor.class); for (AbilityPostProcessor processor : processors) { processor.process(mode, abilitiesTable); } } // init Set abilityModes = abilities.keySet(); LOGGER.info("Ready to initialize current node abilities, support modes: {}", abilityModes); for (AbilityMode abilityMode : abilityModes) { this.currentNodeAbilities .put(abilityMode, new ConcurrentHashMap<>(AbilityKey.mapStr(abilities.get(abilityMode)))); } LOGGER.info("Initialize current abilities finish..."); } /** * Turn on the ability whose key is

    abilityKey

    . * * @param abilityKey ability key{@link AbilityKey} */ public void enableCurrentNodeAbility(AbilityKey abilityKey) { Map abilities = this.currentNodeAbilities.get(abilityKey.getMode()); if (abilities != null) { doTurn(abilities, abilityKey, true); } } protected void doTurn(Map abilities, AbilityKey key, boolean turn) { LOGGER.info("Turn current node ability: {}, turn: {}", key, turn); abilities.put(key.getName(), turn); // notify event AbilityUpdateEvent abilityUpdateEvent = new AbilityUpdateEvent(); abilityUpdateEvent.setTable(Collections.unmodifiableMap(abilities)); abilityUpdateEvent.setOn(turn); abilityUpdateEvent.setAbilityKey(key); NotifyCenter.publishEvent(abilityUpdateEvent); } /** * Turn off the ability whose key is

    abilityKey

    {@link AbilityKey}. * * @param abilityKey ability key */ public void disableCurrentNodeAbility(AbilityKey abilityKey) { Map abilities = this.currentNodeAbilities.get(abilityKey.getMode()); if (abilities != null) { doTurn(abilities, abilityKey, false); } } /** * . Whether current node support * * @param abilityKey ability key from {@link AbilityKey} * @return whether support */ public AbilityStatus isCurrentNodeAbilityRunning(AbilityKey abilityKey) { Map abilities = currentNodeAbilities.get(abilityKey.getMode()); if (abilities != null) { Boolean support = abilities.get(abilityKey.getName()); if (support != null) { return support ? AbilityStatus.SUPPORTED : AbilityStatus.NOT_SUPPORTED; } } return AbilityStatus.UNKNOWN; } /** * . Init current node abilities * * @return current node abilities */ protected abstract Map> initCurrentNodeAbilities(); /** * . Return the abilities current node * * @return current abilities */ public Map getCurrentNodeAbilities(AbilityMode mode) { Map abilities = currentNodeAbilities.get(mode); if (abilities != null) { return Collections.unmodifiableMap(abilities); } return Collections.emptyMap(); } /** * A legal nacos application has a ability control manager. If there are more than one, the one with higher priority * is preferred * * @return priority */ public abstract int getPriority(); /** * notify when current node ability changing. */ public static class AbilityUpdateEvent extends Event { private static final long serialVersionUID = -1232411212311111L; private AbilityKey abilityKey; private boolean isOn; private Map table; private AbilityUpdateEvent() { } public Map getAbilityTable() { return table; } public void setTable(Map abilityTable) { this.table = abilityTable; } public AbilityKey getAbilityKey() { return abilityKey; } public void setAbilityKey(AbilityKey abilityKey) { this.abilityKey = abilityKey; } public boolean isOn() { return isOn; } public void setOn(boolean on) { isOn = on; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/ability/discover/NacosAbilityManagerHolder.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.ability.discover; import com.alibaba.nacos.common.ability.AbstractAbilityControlManager; import com.alibaba.nacos.common.spi.NacosServiceLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; /** * This class is used to discover {@link AbstractAbilityControlManager} implements. All the ability operation will be * finish in this singleton. * * @author Daydreamer * @date 2022/7/14 19:58 **/ public class NacosAbilityManagerHolder { /** * . private constructor */ private NacosAbilityManagerHolder() { } private static final Logger LOGGER = LoggerFactory.getLogger(NacosAbilityManagerHolder.class); /** * . singleton */ private static AbstractAbilityControlManager abstractAbilityControlManager; /** * . get nacos ability control manager * * @return BaseAbilityControlManager */ public static synchronized AbstractAbilityControlManager getInstance() { if (null == abstractAbilityControlManager) { initAbilityControlManager(); } return abstractAbilityControlManager; } /** * . Return the target type of ability manager * * @param clazz clazz * @param target type * @return AbilityControlManager */ public static T getInstance(Class clazz) { return clazz.cast(abstractAbilityControlManager); } private static void initAbilityControlManager() { // spi discover implement Collection load = null; load = NacosServiceLoader.load(AbstractAbilityControlManager.class); // the priority of the server is higher List collect = load.stream() .sorted(Comparator.comparingInt(AbstractAbilityControlManager::getPriority)) .collect(Collectors.toList()); // get the highest priority one if (load.size() > 0) { abstractAbilityControlManager = collect.get(collect.size() - 1); LOGGER.info("[AbilityControlManager] Successfully initialize AbilityControlManager"); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/codec/Base64.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.codec; /** * From apache common codec, and remove some useless method. Provides Base64 encoding and decoding as defined by RFC 2045. * *

    This class implements section 6.8. Base64 Content-Transfer-Encoding from RFC 2045 * * Multipurpose * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies by Freed and Borenstein.

    * The class can be parameterized in the following manner with various constructors:

    • URL-safe mode: Default * off.
    • Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being * multiples of 4 in the encoded data.
    • Line separator: Default is CRLF ("\r\n")

    Since this class * operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode character * encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).

    This * class is not thread-safe. Each thread should use its own instance.

    * * @author Apache Software Foundation * @version $Revision: 1080712 $ * @see RFC 2045 * @since 1.0 */ public class Base64 { /** * BASE32 characters are 6 bits in length. They are formed by taking a block of 3 octets to form a 24-bit string, * which is converted into 4 BASE64 characters. */ private static final int BITS_PER_ENCODED_BYTE = 6; private static final int BYTES_PER_UNENCODED_BLOCK = 3; private static final int BYTES_PER_ENCODED_BLOCK = 4; /** * Chunk separator per RFC 2045 section 2.1. * *

    N.B. The next major release may break compatibility and make this field private.

    * * @see RFC 2045 section 2.1 */ static final byte[] CHUNK_SEPARATOR = {'\r', '\n'}; /** * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" * equivalents as specified in Table 1 of RFC 2045. * *

    Thanks to "commons" project in ws.apache.org for this code. * *

    http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ */ private static final byte[] STANDARD_ENCODE_TABLE = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; /** * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed to - and _ to make the encoded Base64 * results more URL-SAFE. This table is only used when the Base64's mode is set to URL-SAFE. */ private static final byte[] URL_SAFE_ENCODE_TABLE = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'}; /** * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in * Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64 * alphabet but fall within the bounds of the array are translated to -1. * *

    Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles * both URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit). * *

    Thanks to "commons" project in ws.apache.org for this code. * *

    http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ */ private static final byte[] DECODE_TABLE = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51}; /** * Base64 uses 6-bit fields. Mask used to extract 6 bits, used when encoding */ private static final int MASK_6BITS = 0x3f; // The static final fields above are used for the original static byte[] methods on Base64. // The private member fields below are used with the new streaming approach, which requires // some state be preserved between calls of encode() and decode(). /** * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch * between the two modes. */ private final byte[] encodeTable; /** * Only one decode table currently; keep for consistency with Base32 code. */ private final byte[] decodeTable = DECODE_TABLE; /** * Line separator for encoding. Not used when decoding. Only used if lineLength > 0. */ private final byte[] lineSeparator; /** * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. * decodeSize = 3 + lineSeparator.length; */ private final int decodeSize; /** * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing. * encodeSize = 4 + lineSeparator.length; */ private final int encodeSize; /** * Place holder for the bytes we're dealing with for our based logic. Bitwise operations store and extract the * encoding or decoding from this variable. */ private int bitWorkArea; /** * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.

    When encoding the line * length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE.

    * *

    When decoding all variants are supported.

    */ public Base64() { this(0, CHUNK_SEPARATOR, false); } /** * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.

    When encoding the line * length and line separator are given in the constructor, and the encoding table is STANDARD_ENCODE_TABLE.

    * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data. *

    When decoding all variants are supported.

    * * @param lineLength Each line of encoded data will be at most of the given length (rounded down to nearest * multiple of 4). If lineLength <= 0, then the output will not be divided into lines (chunks). * Ignored when decoding. * @param lineSeparator Each line of encoded data will end with this sequence of bytes. * @param urlSafe Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to * encode operations. Decoding seamlessly handles both modes. * @throws IllegalArgumentException The provided lineSeparator included some base64 characters. That's not going to * work! * @since 1.4 */ private Base64(int lineLength, byte[] lineSeparator, boolean urlSafe) { chunkSeparatorLength = lineSeparator.length; unencodedBlockSize = BYTES_PER_UNENCODED_BLOCK; encodedBlockSize = BYTES_PER_ENCODED_BLOCK; this.lineLength = (lineLength > 0 && chunkSeparatorLength > 0) ? (lineLength / encodedBlockSize) * encodedBlockSize : 0; if (lineLength > 0) { this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length; this.lineSeparator = new byte[lineSeparator.length]; System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length); } else { this.encodeSize = BYTES_PER_ENCODED_BLOCK; this.lineSeparator = null; } this.decodeSize = this.encodeSize - 1; this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE; } /** * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet. * * @param pArray a byte array containing binary data * @return A byte array containing only the basen alphabetic character data */ private byte[] encode(byte[] pArray) { reset(); encode(pArray, 0, pArray.length); encode(pArray, 0, -1); byte[] buf = new byte[pos - readPos]; readResults(buf, 0, buf.length); return buf; } /** *

    Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once * with the data to encode, and once with inAvail set to "-1" to alert encoder that EOF has been reached, so flush * last remaining bytes (if not multiple of 3).

    Thanks to "commons" project in ws.apache.org for the * bitwise operations, and general approach. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ *

    * * @param in byte[] array of binary data to base64 encode. * @param inPos Position to start reading data from. * @param inAvail Amount of bytes available from input for encoding. */ void encode(byte[] in, int inPos, int inAvail) { if (eof) { return; } if (inAvail < 0) { eof = true; if (0 == modulus && lineLength == 0) { return; } ensureBufferSize(encodeSize); int savedPos = pos; switch (modulus) { case 1: buffer[pos++] = encodeTable[(bitWorkArea >> 2) & MASK_6BITS]; buffer[pos++] = encodeTable[(bitWorkArea << 4) & MASK_6BITS]; if (encodeTable == STANDARD_ENCODE_TABLE) { buffer[pos++] = PAD; buffer[pos++] = PAD; } break; case 2: buffer[pos++] = encodeTable[(bitWorkArea >> 10) & MASK_6BITS]; buffer[pos++] = encodeTable[(bitWorkArea >> 4) & MASK_6BITS]; buffer[pos++] = encodeTable[(bitWorkArea << 2) & MASK_6BITS]; if (encodeTable == STANDARD_ENCODE_TABLE) { buffer[pos++] = PAD; } break; default: break; } currentLinePos += pos - savedPos; /* if currentPos == 0 we are at the start of a line, so don't add CRLF */ if (lineLength > 0 && currentLinePos > 0) { System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); pos += lineSeparator.length; } } else { for (int i = 0; i < inAvail; i++) { ensureBufferSize(encodeSize); modulus = (modulus + 1) % BYTES_PER_UNENCODED_BLOCK; int b = in[inPos++]; if (b < 0) { b += 256; } bitWorkArea = (bitWorkArea << 8) + b; if (0 == modulus) { buffer[pos++] = encodeTable[(bitWorkArea >> 18) & MASK_6BITS]; buffer[pos++] = encodeTable[(bitWorkArea >> 12) & MASK_6BITS]; buffer[pos++] = encodeTable[(bitWorkArea >> 6) & MASK_6BITS]; buffer[pos++] = encodeTable[bitWorkArea & MASK_6BITS]; currentLinePos += BYTES_PER_ENCODED_BLOCK; if (lineLength > 0 && lineLength <= currentLinePos) { System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length); pos += lineSeparator.length; currentLinePos = 0; } } } } } /** * Decodes a byte[] containing characters in the Base-N alphabet. * * @param pArray A byte array containing Base-N character data * @return a byte array containing binary data */ private byte[] decode(byte[] pArray) { reset(); if (pArray == null || pArray.length == 0) { return pArray; } decode(pArray, 0, pArray.length); decode(pArray, 0, -1); byte[] result = new byte[pos]; readResults(result, 0, result.length); return result; } /** *

    Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: * once with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The * "-1" call is not necessary when decoding, but it doesn't hurt, either.

    Ignores all non-base64 * characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are silently ignored, but * has implications for other bytes, too. This method subscribes to the garbage-in, garbage-out philosophy: it will * not check the provided data for validity.

    Thanks to "commons" project in ws.apache.org for the bitwise * operations, and general approach. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/

    * * @param in byte[] array of ascii data to base64 decode. * @param inPos Position to start reading data from. * @param inAvail Amount of bytes available from input for encoding. */ void decode(byte[] in, int inPos, int inAvail) { if (eof) { return; } if (inAvail < 0) { eof = true; } for (int i = 0; i < inAvail; i++) { ensureBufferSize(decodeSize); byte b = in[inPos++]; if (b == PAD) { // We're done. eof = true; break; } else { if (b >= 0 && b < DECODE_TABLE.length) { int result = DECODE_TABLE[b]; if (result >= 0) { modulus = (modulus + 1) % BYTES_PER_ENCODED_BLOCK; bitWorkArea = (bitWorkArea << BITS_PER_ENCODED_BYTE) + result; if (modulus == 0) { buffer[pos++] = (byte) ((bitWorkArea >> 16) & MASK_8BITS); buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); buffer[pos++] = (byte) (bitWorkArea & MASK_8BITS); } } } } } // Two forms of EOF as far as base64 decoder is concerned: actual // EOF (-1) and first time '=' character is encountered in stream. // This approach makes the '=' padding characters completely optional. if (eof && modulus != 0) { ensureBufferSize(decodeSize); // We have some spare bits remaining // Output all whole multiples of 8 bits and ignore the rest switch (modulus) { // case 1: // 6 bits - ignore entirely // break; case 2: bitWorkArea = bitWorkArea >> 4; buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); break; case 3: bitWorkArea = bitWorkArea >> 2; buffer[pos++] = (byte) ((bitWorkArea >> 8) & MASK_8BITS); buffer[pos++] = (byte) ((bitWorkArea) & MASK_8BITS); break; default: break; } } } /** * Encodes binary data using the base64 algorithm but does not chunk the output. * * @param binaryData binary data to encode * @return byte[] containing Base64 characters in their UTF-8 representation. */ public static byte[] encodeBase64(byte[] binaryData) { return encodeBase64(binaryData, false, false, Integer.MAX_VALUE); } /** * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks. * * @param binaryData Array containing binary data to encode. * @param isChunked if true this encoder will chunk the base64 output into 76 character blocks * @param urlSafe if true this encoder will emit - and _ instead of the usual + and / * characters. * @param maxResultSize The maximum result size to accept. * @return Base64-encoded data. * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than maxResultSize * @since 1.4 */ public static byte[] encodeBase64(byte[] binaryData, boolean isChunked, boolean urlSafe, int maxResultSize) { if (binaryData == null || binaryData.length == 0) { return binaryData; } // Create this so can use the super-class method // Also ensures that the same roundings are performed by the ctor and the code Base64 b64 = isChunked ? new Base64(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe); long len = b64.getEncodedLength(binaryData); if (len > maxResultSize) { throw new IllegalArgumentException("Input array too big, the output array would be bigger (" + len + ") than the specified maximum size of " + maxResultSize); } return b64.encode(binaryData); } /** * Decodes Base64 data into octets. * * @param base64Data Byte array containing Base64 data * @return Array containing decoded data. */ public static byte[] decodeBase64(byte[] base64Data) { return new Base64().decode(base64Data); } /** * MIME chunk size per RFC 2045 section 6.8. * *

    The {@value} character limit does not count the trailing CRLF, but counts all other characters, including * any equal signs.

    * * @see RFC 2045 section 6.8 */ private static final int MIME_CHUNK_SIZE = 76; private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2; /** * Defines the default buffer size - currently {@value} - must be large enough for at least one encoded * block+separator. */ private static final int DEFAULT_BUFFER_SIZE = 8192; /** * Mask used to extract 8 bits, used in decoding bytes. */ private static final int MASK_8BITS = 0xff; /** * Byte used to pad output. */ private static final byte PAD_DEFAULT = '='; private static final byte PAD = PAD_DEFAULT; /** * Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32 */ private final int unencodedBlockSize; /** * Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32 */ private final int encodedBlockSize; /** * Chunksize for encoding. Not used when decoding. A value of zero or less implies no chunking of the encoded data. * Rounded down to nearest multiple of encodedBlockSize. */ private final int lineLength; /** * Size of chunk separator. Not used unless {@link #lineLength} > 0. */ private final int chunkSeparatorLength; /** * Buffer for streaming. */ private byte[] buffer; /** * Position where next character should be written in the buffer. */ private int pos; /** * Position where next character should be read from the buffer. */ private int readPos; /** * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless, and * must be thrown away. */ private boolean eof; /** * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to * make sure each encoded line never goes beyond lineLength (if lineLength > 0). */ private int currentLinePos; /** * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. This * variable helps track that. */ private int modulus; /** * Ensure that the buffer has room for size bytes. * * @param size minimum spare space required */ private void ensureBufferSize(int size) { if ((buffer == null) || (buffer.length < pos + size)) { if (buffer == null) { buffer = new byte[DEFAULT_BUFFER_SIZE]; pos = 0; readPos = 0; } else { byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR]; System.arraycopy(buffer, 0, b, 0, buffer.length); buffer = b; } } } /** * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail * bytes. Returns how many bytes were actually extracted. * * @param b byte[] array to extract the buffered data into. * @param bPos position in byte[] array to start extraction at. * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are available). * @return The number of bytes successfully extracted into the provided byte[] array. */ private int readResults(byte[] b, int bPos, int bAvail) { if (buffer != null) { int len = Math.min(pos - readPos, bAvail); System.arraycopy(buffer, readPos, b, bPos, len); readPos += len; if (readPos >= pos) { buffer = null; } return len; } return eof ? -1 : 0; } /** * Resets this object to its initial newly constructed state. */ private void reset() { buffer = null; pos = 0; readPos = 0; currentLinePos = 0; modulus = 0; eof = false; } /** * Calculates the amount of space needed to encode the supplied array. * * @param pArray byte[] array which will later be encoded * @return amount of space needed to encoded the supplied array. Returns a long since a max-len array will require > * Integer.MAX_VALUE */ private long getEncodedLength(byte[] pArray) { // Calculate non-chunked size - rounded up to allow for padding // cast to long is needed to avoid possibility of overflow long len = ((pArray.length + unencodedBlockSize - 1) / unencodedBlockSize) * (long) encodedBlockSize; if (lineLength > 0) { /* Round up to nearest multiple */ len += ((len + lineLength - 1) / lineLength) * chunkSeparatorLength; } return len; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/constant/HttpHeaderConsts.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.constant; /** * Nacos header constants. * * @author ly */ public interface HttpHeaderConsts { String CLIENT_VERSION_HEADER = "Client-Version"; String USER_AGENT_HEADER = "User-Agent"; String REQUEST_SOURCE_HEADER = "Request-Source"; String CONTENT_TYPE = "Content-Type"; String CONTENT_DISPOSITION = "Content-Disposition"; String CONTENT_LENGTH = "Content-Length"; String ACCEPT_CHARSET = "Accept-Charset"; String ACCEPT_ENCODING = "Accept-Encoding"; String CONTENT_ENCODING = "Content-Encoding"; String CONNECTION = "Requester"; String REQUEST_ID = "RequestId"; String REQUEST_MODULE = "Request-Module"; String APP_FILED = "app"; String CLIENT_IP = "clientIp"; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/constant/RequestUrlConstants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.constant; /** * Nacos request url constants. * * @author chenhao26 */ public interface RequestUrlConstants { String HTTP_PREFIX = "http://"; String HTTPS_PREFIX = "https://"; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/constant/ResponseHandlerType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.constant; /** * Response Handler Type. * * @author mai.jh */ public final class ResponseHandlerType { public static final String STRING_TYPE = "java.lang.String"; public static final String RESTRESULT_TYPE = "com.alibaba.nacos.common.model.RestResult"; public static final String DEFAULT_BEAN_TYPE = "default_bean_handler"; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/constant/Symbols.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.constant; /** * Symbols. * * @author haiqi.wang * @date 2024/08/13 */ public final class Symbols { /** * Comma. */ public static final String COMMA = ","; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/event/ServerConfigChangeEvent.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.event; import com.alibaba.nacos.common.notify.Event; /** * Server configuration changed event. * *

    * When nacos server configuration file (default nacos/conf/application.properties) changed, The event should be notify * to all subscriber. *

    * * @author xiweng.yy */ public class ServerConfigChangeEvent extends Event { private static final long serialVersionUID = 289992068985663172L; public static ServerConfigChangeEvent newEvent() { return new ServerConfigChangeEvent(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/executor/ExecutorFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.executor; import com.alibaba.nacos.common.JustForTest; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Unified thread pool creation factory, and actively create thread pool resources by ThreadPoolManager for unified life * cycle management {@link ExecutorFactory.Managed}. * *

    Unified thread pool creation factory without life cycle management {@link ExecutorFactory}. * *

    two check style ignore will be removed after issue#2856 finished. * * @author liaochuntao */ @SuppressWarnings({"checkstyle:overloadmethodsdeclarationorder", "checkstyle:missingjavadocmethod"}) public final class ExecutorFactory { public static ExecutorService newSingleExecutorService() { return Executors.newFixedThreadPool(1); } public static ExecutorService newSingleExecutorService(final ThreadFactory threadFactory) { return Executors.newFixedThreadPool(1, threadFactory); } public static ExecutorService newFixedExecutorService(final int nThreads) { return Executors.newFixedThreadPool(nThreads); } public static ExecutorService newFixedExecutorService(final int nThreads, final ThreadFactory threadFactory) { return Executors.newFixedThreadPool(nThreads, threadFactory); } public static ScheduledExecutorService newSingleScheduledExecutorService(final ThreadFactory threadFactory) { return Executors.newScheduledThreadPool(1, threadFactory); } public static ScheduledExecutorService newScheduledExecutorService(final int nThreads, final ThreadFactory threadFactory) { return Executors.newScheduledThreadPool(nThreads, threadFactory); } public static ThreadPoolExecutor newCustomerThreadExecutor(final int coreThreads, final int maxThreads, final long keepAliveTimeMs, final ThreadFactory threadFactory) { return new ThreadPoolExecutor(coreThreads, maxThreads, keepAliveTimeMs, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), threadFactory); } public static final class Managed { private static final String DEFAULT_NAMESPACE = "nacos"; private static final ThreadPoolManager THREAD_POOL_MANAGER = ThreadPoolManager.getInstance(); /** * Create a new single executor service with default thread factory and register to manager. * * @param group group name * @return new single executor service */ public static ExecutorService newSingleExecutorService(final String group) { ExecutorService executorService = Executors.newFixedThreadPool(1); THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executorService); return executorService; } /** * Create a new single executor service with input thread factory and register to manager. * * @param group group name * @param threadFactory thread factory * @return new single executor service */ public static ExecutorService newSingleExecutorService(final String group, final ThreadFactory threadFactory) { ExecutorService executorService = Executors.newFixedThreadPool(1, threadFactory); THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executorService); return executorService; } /** * Create a new fixed executor service with default thread factory and register to manager. * * @param group group name * @param nThreads thread number * @return new fixed executor service */ public static ExecutorService newFixedExecutorService(final String group, final int nThreads) { ExecutorService executorService = Executors.newFixedThreadPool(nThreads); THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executorService); return executorService; } /** * Create a new fixed executor service with input thread factory and register to manager. * * @param group group name * @param nThreads thread number * @param threadFactory thread factory * @return new fixed executor service */ public static ExecutorService newFixedExecutorService(final String group, final int nThreads, final ThreadFactory threadFactory) { ExecutorService executorService = Executors.newFixedThreadPool(nThreads, threadFactory); THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executorService); return executorService; } /** * Create a new single scheduled executor service with input thread factory and register to manager. * * @param group group name * @param threadFactory thread factory * @return new single scheduled executor service */ public static ScheduledExecutorService newSingleScheduledExecutorService(final String group, final ThreadFactory threadFactory) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1, threadFactory); THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executorService); return executorService; } /** * Create a new scheduled executor service with input thread factory and register to manager. * * @param group group name * @param nThreads thread number * @param threadFactory thread factory * @return new scheduled executor service */ public static ScheduledExecutorService newScheduledExecutorService(final String group, final int nThreads, final ThreadFactory threadFactory) { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(nThreads, threadFactory); THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executorService); return executorService; } /** * Create a new custom executor service and register to manager. * * @param group group name * @param coreThreads core thread number * @param maxThreads max thread number * @param keepAliveTimeMs keep alive time milliseconds * @param threadFactory thread factory * @return new custom executor service */ public static ThreadPoolExecutor newCustomerThreadExecutor(final String group, final int coreThreads, final int maxThreads, final long keepAliveTimeMs, final ThreadFactory threadFactory) { ThreadPoolExecutor executor = new ThreadPoolExecutor(coreThreads, maxThreads, keepAliveTimeMs, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(), threadFactory); THREAD_POOL_MANAGER.register(DEFAULT_NAMESPACE, group, executor); return executor; } @JustForTest public static ThreadPoolManager getThreadPoolManager() { return THREAD_POOL_MANAGER; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/executor/NameThreadFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.executor; import com.alibaba.nacos.common.utils.StringUtils; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * Name thread factory. * * @author liaochuntao */ public class NameThreadFactory implements ThreadFactory { private final AtomicInteger id = new AtomicInteger(0); private String name; public NameThreadFactory(String name) { if (!name.endsWith(StringUtils.DOT)) { name += StringUtils.DOT; } this.name = name; } @Override public Thread newThread(Runnable r) { String threadName = name + id.getAndIncrement(); Thread thread = new Thread(r, threadName); thread.setDaemon(true); return thread; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/executor/ThreadPoolManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.executor; import com.alibaba.nacos.common.JustForTest; import com.alibaba.nacos.common.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; /** * // TODO Access Metric. * *

    For unified management of thread pool resources, the consumer can simply call the register method to {@link * ThreadPoolManager#register(String, String, ExecutorService)} the thread pool that needs to be included in the life * cycle management of the resource * * @author liaochuntao */ public final class ThreadPoolManager { private static final Logger LOGGER = LoggerFactory.getLogger(ThreadPoolManager.class); private Map>> resourcesManager; private static final ThreadPoolManager INSTANCE = new ThreadPoolManager(); private static final AtomicBoolean CLOSED = new AtomicBoolean(false); static { INSTANCE.init(); ThreadUtils.addShutdownHook(new Thread(() -> { LOGGER.info("[ThreadPoolManager] Start destroying ThreadPool"); shutdown(); LOGGER.info("[ThreadPoolManager] Completed destruction of ThreadPool"); })); } public static ThreadPoolManager getInstance() { return INSTANCE; } private ThreadPoolManager() { } private void init() { resourcesManager = new ConcurrentHashMap<>(8); } /** * Register the thread pool resources with the resource manager. * * @param namespace namespace name * @param group group name * @param executor {@link ExecutorService} */ public void register(String namespace, String group, ExecutorService executor) { resourcesManager.compute(namespace, (namespaceKey, map) -> { if (map == null) { map = new HashMap<>(8); } map.computeIfAbsent(group, groupKey -> new HashSet<>()).add(executor); return map; }); } /** * Cancel the uniform lifecycle management for all threads under this resource. * * @param namespace namespace name * @param group group name */ public void deregister(String namespace, String group) { resourcesManager.computeIfPresent(namespace, (key, map) -> { map.remove(group); return map; }); } /** * Undoing the uniform lifecycle management of {@link ExecutorService} under this resource. * * @param namespace namespace name * @param group group name * @param executor {@link ExecutorService} */ public void deregister(String namespace, String group, ExecutorService executor) { resourcesManager.computeIfPresent(namespace, (namespaceKey, map) -> { map.computeIfPresent(group, (groupKey, set) -> { set.remove(executor); return set; }); return map; }); } /** * Destroys all thread pool resources under this namespace. * * @param namespace namespace */ public void destroy(final String namespace) { Map> map = resourcesManager.remove(namespace); if (map != null) { for (Set set : map.values()) { for (ExecutorService executor : set) { ThreadUtils.shutdownThreadPool(executor); } set.clear(); } map.clear(); } } /** * This namespace destroys all thread pool resources under the grouping. * * @param namespace namespace * @param group group */ public void destroy(final String namespace, final String group) { resourcesManager.computeIfPresent(namespace, (namespaceKey, map) -> { map.computeIfPresent(group, (groupKey, set) -> { for (ExecutorService executor : set) { ThreadUtils.shutdownThreadPool(executor); } set.clear(); return null; }); return map; }); } /** * Shutdown thread pool manager. */ public static void shutdown() { if (!CLOSED.compareAndSet(false, true)) { return; } Set namespaces = INSTANCE.resourcesManager.keySet(); for (String namespace : namespaces) { INSTANCE.destroy(namespace); } } @JustForTest public Map>> getResourcesManager() { return resourcesManager; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/AbstractApacheHttpClientFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.client.request.DefaultHttpClientRequest; import org.apache.hc.client5.http.config.ConnectionConfig; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.util.Timeout; /** * apache http client factory implements. * * @author mai.jh */ public abstract class AbstractApacheHttpClientFactory extends AbstractHttpClientFactory { @Override public final NacosRestTemplate createNacosRestTemplate() { final HttpClientConfig originalRequestConfig = buildHttpClientConfig(); final RequestConfig defaultConfig = getRequestConfig(); // in latest version of Apache Http Components all client settings have been moved into manager PoolingHttpClientConnectionManager poolingManager = PoolingHttpClientConnectionManagerBuilder .create() .setMaxConnTotal(originalRequestConfig.getMaxConnTotal()) .setMaxConnPerRoute(originalRequestConfig.getMaxConnPerRoute()) .setDefaultConnectionConfig(ConnectionConfig .custom() .setTimeToLive(Timeout.of(originalRequestConfig.getConnTimeToLive(), originalRequestConfig.getConnTimeToLiveTimeUnit())) .build()) .build(); return new NacosRestTemplate(assignLogger(), new DefaultHttpClientRequest( HttpClients.custom() .addRequestInterceptorLast(new RequestContent(true)) .setDefaultRequestConfig(defaultConfig) .setUserAgent(originalRequestConfig.getUserAgent()) .setConnectionManager(poolingManager) .build(), defaultConfig)); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/AbstractHttpClientFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.client.request.DefaultAsyncHttpClientRequest; import com.alibaba.nacos.common.http.client.request.JdkHttpClientRequest; import com.alibaba.nacos.common.tls.SelfHostnameVerifier; import com.alibaba.nacos.common.tls.TlsFileWatcher; import com.alibaba.nacos.common.tls.TlsHelper; import com.alibaba.nacos.common.tls.TlsSystemConfig; import java.nio.ByteBuffer; import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.async.HttpAsyncClients; import org.apache.hc.client5.http.impl.nio.PoolingAsyncClientConnectionManagerBuilder; import org.apache.hc.client5.http.nio.AsyncClientConnectionManager; import org.apache.hc.client5.http.ssl.DefaultClientTlsStrategy; import org.apache.hc.client5.http.ssl.DefaultHostnameVerifier; import org.apache.hc.core5.http.nio.ssl.TlsStrategy; import org.apache.hc.core5.http.protocol.RequestContent; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOEventHandler; import org.apache.hc.core5.reactor.IOReactorConfig; import org.apache.hc.core5.reactor.IOSession; import org.apache.hc.core5.util.Timeout; import org.slf4j.Logger; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import java.io.IOException; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; /** * AbstractHttpClientFactory Let the creator only specify the http client config. * * @author mai.jh */ public abstract class AbstractHttpClientFactory implements HttpClientFactory { private static final String ASYNC_THREAD_NAME = "nacos-http-async-client"; private static final String ASYNC_IO_REACTOR_NAME = ASYNC_THREAD_NAME + "#I/O Reactor"; @Override public NacosRestTemplate createNacosRestTemplate() { HttpClientConfig httpClientConfig = buildHttpClientConfig(); final JdkHttpClientRequest clientRequest = new JdkHttpClientRequest(httpClientConfig); // enable ssl initTls((sslContext, hostnameVerifier) -> { clientRequest.setSslContext(loadSslContext()); clientRequest.replaceSslHostnameVerifier(hostnameVerifier); }, filePath -> clientRequest.setSslContext(loadSslContext())); return new NacosRestTemplate(assignLogger(), clientRequest); } @Override public NacosAsyncRestTemplate createNacosAsyncRestTemplate() { final IOReactorConfig ioReactorConfig = getIoReactorConfig(); final HttpClientConfig originalRequestConfig = buildHttpClientConfig(); final DefaultConnectingIOReactor ioreactor = getIoReactor(ASYNC_IO_REACTOR_NAME); final RequestConfig defaultConfig = getRequestConfig(); final AsyncClientConnectionManager connectionManager = getConnectionManager(originalRequestConfig); monitorAndExtension(connectionManager); // issue#12028 upgrade to httpclient5 return new NacosAsyncRestTemplate(assignLogger(), new DefaultAsyncHttpClientRequest( HttpAsyncClients.custom() .addRequestInterceptorLast(new RequestContent(true)) .setThreadFactory(new NameThreadFactory(ASYNC_THREAD_NAME)) .setIOReactorConfig(ioReactorConfig) // catch all exceptions here instead of in DefaultConnectingIOReactor .setIoReactorExceptionCallback((ex) -> { }) .setDefaultRequestConfig(defaultConfig) .setUserAgent(originalRequestConfig.getUserAgent()) .setConnectionManager(connectionManager) .build(), ioreactor, defaultConfig) ); } private DefaultConnectingIOReactor getIoReactor(String threadName) { return new DefaultConnectingIOReactor( (session, ojb) -> new IOEventHandler() { @Override public void connected(IOSession ioSession) throws IOException { } @Override public void inputReady(IOSession ioSession, ByteBuffer byteBuffer) throws IOException { } @Override public void outputReady(IOSession ioSession) throws IOException { } @Override public void timeout(IOSession ioSession, Timeout timeout) throws IOException { } @Override public void exception(IOSession ioSession, Exception e) { } @Override public void disconnected(IOSession ioSession) { } }, getIoReactorConfig(), new NameThreadFactory(threadName), null, // handle exception in io reactor (ex) -> { if (ex instanceof IOException) { assignLogger().warn("[AsyncClientConnectionManager] handle IOException, ignore it.", ex); } else if (ex instanceof RuntimeException) { assignLogger().warn("[AsyncClientConnectionManager] handle RuntimeException, ignore it.", ex); } else { assignLogger().error("[DefaultConnectingIOReactor] Exception! I/O Reactor error time: {}", System.currentTimeMillis(), ex.getCause()); } }, null, null ); } /** * create the {@link AsyncClientConnectionManager}, the code mainly from {@link PoolingAsyncClientConnectionManagerBuilder#build()}. we * add the {@link Callback} to handle the {@link IOException} and {@link RuntimeException} thrown * by the {@link DefaultConnectingIOReactor} when process the event of Network. Using this way * to avoid the {@link DefaultConnectingIOReactor} killed by unknown error of network. * * @param originalRequestConfig request config. * @return {@link AsyncClientConnectionManager}. */ private AsyncClientConnectionManager getConnectionManager(HttpClientConfig originalRequestConfig) { try { SSLContext sslcontext = SSLContext.getDefault(); HostnameVerifier hostnameVerifier = new DefaultHostnameVerifier(); TlsStrategy sslStrategy = new DefaultClientTlsStrategy(sslcontext, hostnameVerifier); // manager no more needs IOReactor return PoolingAsyncClientConnectionManagerBuilder // old method Registry::register("http", NoopIOSessionStrategy.INSTANCE) has been a default strategy .create() // refers to old Registry::register("https", sslStrategy) .setTlsStrategy(sslStrategy) // setMaxTotal now can be used in builder .setMaxConnTotal(originalRequestConfig.getMaxConnTotal()) // setDefaultMaxPerRoute now can be used in builder .setMaxConnPerRoute(originalRequestConfig.getMaxConnPerRoute()) .build(); } catch (NoSuchAlgorithmException e) { throw new RuntimeException(e); } } protected IOReactorConfig getIoReactorConfig() { HttpClientConfig httpClientConfig = buildHttpClientConfig(); return IOReactorConfig.custom().setIoThreadCount(httpClientConfig.getIoThreadCount()).build(); } protected RequestConfig getRequestConfig() { HttpClientConfig httpClientConfig = buildHttpClientConfig(); return RequestConfig .custom() .setConnectTimeout(httpClientConfig.getConTimeOutMillis(), TimeUnit.MILLISECONDS) .setResponseTimeout(httpClientConfig.getReadTimeOutMillis(), TimeUnit.MILLISECONDS) .setConnectionRequestTimeout(httpClientConfig.getConnectionRequestTimeout(), TimeUnit.MILLISECONDS) .setContentCompressionEnabled(httpClientConfig.getContentCompressionEnabled()) .setMaxRedirects(httpClientConfig.getMaxRedirects()).build(); } protected void initTls(BiConsumer initTlsBiFunc, TlsFileWatcher.FileChangeListener tlsChangeListener) { if (!TlsSystemConfig.tlsEnable) { return; } final HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier(); final SelfHostnameVerifier selfHostnameVerifier = new SelfHostnameVerifier(hv); initTlsBiFunc.accept(loadSslContext(), selfHostnameVerifier); if (tlsChangeListener != null) { try { TlsFileWatcher.getInstance() .addFileChangeListener(tlsChangeListener, TlsSystemConfig.tlsClientTrustCertPath, TlsSystemConfig.tlsClientKeyPath); } catch (IOException e) { assignLogger().error("add tls file listener fail", e); } } } @SuppressWarnings("checkstyle:abbreviationaswordinname") protected synchronized SSLContext loadSslContext() { try { return TlsHelper.buildSslContext(true); } catch (NoSuchAlgorithmException | KeyManagementException e) { assignLogger().error("Failed to create SSLContext", e); } return null; } /** * build http client config. * * @return HttpClientConfig */ protected abstract HttpClientConfig buildHttpClientConfig(); /** * assign Logger. * * @return Logger */ protected abstract Logger assignLogger(); /** * add some monitor and do some extension. default empty implementation, implemented by subclass */ protected void monitorAndExtension(AsyncClientConnectionManager connectionManager) { } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/BaseHttpMethod.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.utils.HttpMethod; import com.alibaba.nacos.common.utils.StringUtils; import org.apache.hc.client5.http.classic.methods.HttpDelete; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpHead; import org.apache.hc.client5.http.classic.methods.HttpPatch; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.classic.methods.HttpPut; import org.apache.hc.client5.http.classic.methods.HttpTrace; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import java.net.URI; /** * Base http method. * * @author liaochuntao */ public enum BaseHttpMethod { /** * get request. */ GET(HttpMethod.GET) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpGet(url); } }, GET_LARGE(HttpMethod.GET_LARGE) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpGetWithEntity(url); } }, /** * post request. */ POST(HttpMethod.POST) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpPost(url); } }, /** * put request. */ PUT(HttpMethod.PUT) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpPut(url); } }, /** * delete request. */ DELETE(HttpMethod.DELETE) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpDelete(url); } }, /** * delete Large request. */ DELETE_LARGE(HttpMethod.DELETE_LARGE) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpDeleteWithEntity(url); } }, /** * head request. */ HEAD(HttpMethod.HEAD) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpHead(url); } }, /** * trace request. */ TRACE(HttpMethod.TRACE) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpTrace(url); } }, /** * patch request. */ PATCH(HttpMethod.PATCH) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpPatch(url); } }, /** * options request. */ OPTIONS(HttpMethod.OPTIONS) { @Override protected HttpUriRequestBase createRequest(String url) { return new HttpTrace(url); } }; private String name; BaseHttpMethod(String name) { this.name = name; } public HttpUriRequestBase init(String url) { return createRequest(url); } protected HttpUriRequestBase createRequest(String url) { throw new UnsupportedOperationException(); } /** * Value of {@link BaseHttpMethod}. * * @param name method name * @return {@link BaseHttpMethod} */ public static BaseHttpMethod sourceOf(String name) { for (BaseHttpMethod method : BaseHttpMethod.values()) { if (StringUtils.equalsIgnoreCase(name, method.name)) { return method; } } throw new IllegalArgumentException("Unsupported http method : " + name); } /** * get Large implemented. *

    * Mainly used for GET request parameters are relatively large, can not be placed on the URL, so it needs to be * placed in the body. *

    */ public static class HttpGetWithEntity extends HttpUriRequestBase { public static final String METHOD_NAME = "GET"; public HttpGetWithEntity(String url) { super(METHOD_NAME, URI.create(url)); } } /** * delete Large implemented. *

    * Mainly used for DELETE request parameters are relatively large, can not be placed on the URL, so it needs to be * placed in the body. *

    */ public static class HttpDeleteWithEntity extends HttpUriRequestBase { public static final String METHOD_NAME = "DELETE"; public HttpDeleteWithEntity(String url) { super(METHOD_NAME, URI.create(url)); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/Callback.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.model.RestResult; /** * Http callback. * * @author liaochuntao */ public interface Callback { /** * Callback after the request is responded. * * @param result {@link RestResult} */ void onReceive(RestResult result); /** * An error occurred during the request. * * @param throwable {@link Throwable} */ void onError(Throwable throwable); /** * Callback when the request is cancelled. */ void onCancel(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/DefaultHttpClientFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import org.slf4j.Logger; /** * default http client factory. * * @author mai.jh */ public class DefaultHttpClientFactory extends AbstractHttpClientFactory { private static final int TIMEOUT = Integer.getInteger("nacos.http.timeout", 5000); private final Logger logger; public DefaultHttpClientFactory(Logger logger) { this.logger = logger; } @Override protected HttpClientConfig buildHttpClientConfig() { return HttpClientConfig.builder().setConTimeOutMillis(TIMEOUT).setReadTimeOutMillis(TIMEOUT >> 1).build(); } @Override protected Logger assignLogger() { return logger; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/HttpClientBeanHolder.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.common.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; /** * Create a rest template to ensure that each custom client config and rest template are in one-to-one correspondence. * * @author mai.jh */ public final class HttpClientBeanHolder { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientBeanHolder.class); private static final Map SINGLETON_REST = new HashMap<>(10); private static final Map SINGLETON_ASYNC_REST = new HashMap<>(10); private static final AtomicBoolean ALREADY_SHUTDOWN = new AtomicBoolean(false); static { ThreadUtils.addShutdownHook(HttpClientBeanHolder::shutdown); } public static NacosRestTemplate getNacosRestTemplate(Logger logger) { return getNacosRestTemplate(new DefaultHttpClientFactory(logger)); } public static NacosRestTemplate getNacosRestTemplate(HttpClientFactory httpClientFactory) { if (httpClientFactory == null) { throw new NullPointerException("httpClientFactory is null"); } String factoryName = httpClientFactory.getClass().getName(); NacosRestTemplate nacosRestTemplate = SINGLETON_REST.get(factoryName); if (nacosRestTemplate == null) { synchronized (SINGLETON_REST) { nacosRestTemplate = SINGLETON_REST.get(factoryName); if (nacosRestTemplate != null) { return nacosRestTemplate; } nacosRestTemplate = httpClientFactory.createNacosRestTemplate(); SINGLETON_REST.put(factoryName, nacosRestTemplate); } } return nacosRestTemplate; } public static NacosAsyncRestTemplate getNacosAsyncRestTemplate(Logger logger) { return getNacosAsyncRestTemplate(new DefaultHttpClientFactory(logger)); } public static NacosAsyncRestTemplate getNacosAsyncRestTemplate(HttpClientFactory httpClientFactory) { if (httpClientFactory == null) { throw new NullPointerException("httpClientFactory is null"); } String factoryName = httpClientFactory.getClass().getName(); NacosAsyncRestTemplate nacosAsyncRestTemplate = SINGLETON_ASYNC_REST.get(factoryName); if (nacosAsyncRestTemplate == null) { synchronized (SINGLETON_ASYNC_REST) { nacosAsyncRestTemplate = SINGLETON_ASYNC_REST.get(factoryName); if (nacosAsyncRestTemplate != null) { return nacosAsyncRestTemplate; } nacosAsyncRestTemplate = httpClientFactory.createNacosAsyncRestTemplate(); SINGLETON_ASYNC_REST.put(factoryName, nacosAsyncRestTemplate); } } return nacosAsyncRestTemplate; } /** * Shutdown common http client. */ private static void shutdown() { if (!ALREADY_SHUTDOWN.compareAndSet(false, true)) { return; } LOGGER.info("[HttpClientBeanHolder] Start destroying common HttpClient"); try { shutdown(DefaultHttpClientFactory.class.getName()); } catch (Exception ex) { LOGGER.error("An exception occurred when the common HTTP client was closed : {}", ExceptionUtil.getStackTrace(ex)); } LOGGER.info("[HttpClientBeanHolder] Completed destruction of HttpClient"); } /** * Shutdown http client holder and close remove template. * * @param className HttpClientFactory implement class name * @throws Exception ex */ public static void shutdown(String className) throws Exception { shutdownNacosSyncRest(className); shutdownNacosAsyncRest(className); } /** * Shutdown sync http client holder and remove template. * * @param className HttpClientFactory implement class name * @throws Exception ex */ public static void shutdownNacosSyncRest(String className) throws Exception { final NacosRestTemplate nacosRestTemplate = SINGLETON_REST.get(className); if (nacosRestTemplate != null) { nacosRestTemplate.close(); SINGLETON_REST.remove(className); } } /** * Shutdown async http client holder and remove template. * * @param className HttpClientFactory implement class name * @throws Exception ex */ public static void shutdownNacosAsyncRest(String className) throws Exception { final NacosAsyncRestTemplate nacosAsyncRestTemplate = SINGLETON_ASYNC_REST.get(className); if (nacosAsyncRestTemplate != null) { nacosAsyncRestTemplate.close(); SINGLETON_ASYNC_REST.remove(className); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/HttpClientConfig.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.utils.ThreadUtils; import java.util.concurrent.TimeUnit; /** * http client config build. * * @author mai.jh */ public class HttpClientConfig { /** * connect time out. */ private final int conTimeOutMillis; /** * read time out. */ private final int readTimeOutMillis; /** * connTimeToLive. */ private final long connTimeToLive; /** * connTimeToLiveTimeUnit. */ private final TimeUnit connTimeToLiveTimeUnit; /** * connectionRequestTimeout. */ private final int connectionRequestTimeout; /** * max redirect. */ private final int maxRedirects; /** * max connect total. */ private final int maxConnTotal; /** * Assigns maximum connection per route value. */ private final int maxConnPerRoute; /** * is HTTP compression enabled. */ private final boolean contentCompressionEnabled; /** * io thread count. */ private final int ioThreadCount; /** * user agent. */ private final String userAgent; public HttpClientConfig(int conTimeOutMillis, int readTimeOutMillis, long connTimeToLive, TimeUnit timeUnit, int connectionRequestTimeout, int maxRedirects, int maxConnTotal, int maxConnPerRoute, boolean contentCompressionEnabled, int ioThreadCount, String userAgent) { this.conTimeOutMillis = conTimeOutMillis; this.readTimeOutMillis = readTimeOutMillis; this.connTimeToLive = connTimeToLive; this.connTimeToLiveTimeUnit = timeUnit; this.connectionRequestTimeout = connectionRequestTimeout; this.maxRedirects = maxRedirects; this.maxConnTotal = maxConnTotal; this.maxConnPerRoute = maxConnPerRoute; this.contentCompressionEnabled = contentCompressionEnabled; this.ioThreadCount = ioThreadCount; this.userAgent = userAgent; } public int getConTimeOutMillis() { return conTimeOutMillis; } public int getReadTimeOutMillis() { return readTimeOutMillis; } public long getConnTimeToLive() { return connTimeToLive; } public TimeUnit getConnTimeToLiveTimeUnit() { return connTimeToLiveTimeUnit; } public int getConnectionRequestTimeout() { return connectionRequestTimeout; } public int getMaxRedirects() { return maxRedirects; } public int getMaxConnTotal() { return maxConnTotal; } public int getMaxConnPerRoute() { return maxConnPerRoute; } public boolean getContentCompressionEnabled() { return contentCompressionEnabled; } public int getIoThreadCount() { return ioThreadCount; } public String getUserAgent() { return userAgent; } public static HttpClientConfigBuilder builder() { return new HttpClientConfigBuilder(); } public static final class HttpClientConfigBuilder { // not allow negative number in httpclient5 private int conTimeOutMillis = 180_000; // not allow negative number in httpclient5 private int readTimeOutMillis = 180_000; // not allow negative number in httpclient5 private long connTimeToLive = 180_000; private TimeUnit connTimeToLiveTimeUnit = TimeUnit.MILLISECONDS; private int connectionRequestTimeout = 5000; private int maxRedirects = 50; private int maxConnTotal = 0; private int maxConnPerRoute = 0; private boolean contentCompressionEnabled = true; private int ioThreadCount = ThreadUtils.getSuitableThreadCount(1); private String userAgent; public HttpClientConfigBuilder setConTimeOutMillis(int conTimeOutMillis) { this.conTimeOutMillis = conTimeOutMillis; return this; } public HttpClientConfigBuilder setReadTimeOutMillis(int readTimeOutMillis) { this.readTimeOutMillis = readTimeOutMillis; return this; } public HttpClientConfigBuilder setConnectionTimeToLive(long connTimeToLive, TimeUnit connTimeToLiveTimeUnit) { this.connTimeToLive = connTimeToLive; this.connTimeToLiveTimeUnit = connTimeToLiveTimeUnit; return this; } public HttpClientConfigBuilder setConnectionRequestTimeout(int connectionRequestTimeout) { this.connectionRequestTimeout = connectionRequestTimeout; return this; } public HttpClientConfigBuilder setMaxRedirects(int maxRedirects) { this.maxRedirects = maxRedirects; return this; } public HttpClientConfigBuilder setMaxConnTotal(int maxConnTotal) { this.maxConnTotal = maxConnTotal; return this; } public HttpClientConfigBuilder setMaxConnPerRoute(int maxConnPerRoute) { this.maxConnPerRoute = maxConnPerRoute; return this; } public HttpClientConfigBuilder setContentCompressionEnabled(boolean contentCompressionEnabled) { this.contentCompressionEnabled = contentCompressionEnabled; return this; } public HttpClientConfigBuilder setIoThreadCount(int ioThreadCount) { this.ioThreadCount = ioThreadCount; return this; } public HttpClientConfigBuilder setUserAgent(String userAgent) { this.userAgent = userAgent; return this; } /** * build http client config. * * @return HttpClientConfig */ public HttpClientConfig build() { return new HttpClientConfig(conTimeOutMillis, readTimeOutMillis, connTimeToLive, connTimeToLiveTimeUnit, connectionRequestTimeout, maxRedirects, maxConnTotal, maxConnPerRoute, contentCompressionEnabled, ioThreadCount, userAgent); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/HttpClientFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; /** * http Client Factory. * * @author mai.jh */ public interface HttpClientFactory { /** * create new nacost rest. * * @return NacosRestTemplate */ NacosRestTemplate createNacosRestTemplate(); /** * create new nacos async rest. * * @return NacosAsyncRestTemplate */ NacosAsyncRestTemplate createNacosAsyncRestTemplate(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/HttpRestResult.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.model.RestResult; /** * Http RestResult. * * @author mai.jh */ public class HttpRestResult extends RestResult { private static final long serialVersionUID = 3766947816720175947L; private Header header; public HttpRestResult() { } public HttpRestResult(Header header, int code, T data, String message) { super(code, message, data); this.header = header; } public Header getHeader() { return header; } public void setHeader(Header header) { this.header = header; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/HttpUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.UuidUtils; import com.alibaba.nacos.common.utils.VersionUtils; import org.apache.hc.client5.http.ConnectTimeoutException; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; import org.apache.hc.core5.http.ClassicHttpRequest; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.HttpEntityContainer; import org.apache.hc.core5.http.NameValuePair; import org.apache.hc.core5.http.io.entity.ByteArrayEntity; import org.apache.hc.core5.http.io.entity.StringEntity; import org.apache.hc.core5.http.message.BasicNameValuePair; import java.io.UnsupportedEncodingException; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; import java.net.URLDecoder; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.TimeoutException; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTPS_PREFIX; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * Http utils. * * @author liaochuntao */ public final class HttpUtils { private static final Pattern CONTEXT_PATH_MATCH = Pattern.compile("(\\/)\\1+"); /** * Init http header. * * @param requestBase requestBase {@link HttpUriRequestBase} * @param header header */ public static void initRequestHeader(ClassicHttpRequest requestBase, Header header) { Iterator> iterator = header.iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); requestBase.setHeader(entry.getKey(), entry.getValue()); } } /** * Init http entity. * * @param requestBase requestBase {@link HttpUriRequestBase} * @param body body * @param header request header * @throws Exception exception */ public static void initRequestEntity(ClassicHttpRequest requestBase, Object body, Header header) throws Exception { if (body == null) { return; } if (requestBase instanceof HttpEntityContainer) { HttpEntityContainer request = requestBase; MediaType mediaType = MediaType.valueOf(header.getValue(HttpHeaderConsts.CONTENT_TYPE)); ContentType contentType = ContentType.create(mediaType.getType(), mediaType.getCharset()); HttpEntity entity; if (body instanceof byte[]) { entity = new ByteArrayEntity((byte[]) body, contentType); } else { entity = new StringEntity(body instanceof String ? (String) body : JacksonUtils.toJson(body), contentType); } request.setEntity(entity); } } /** * Init request from entity map. * * @param requestBase requestBase {@link HttpUriRequestBase} * @param body body map * @param charset charset of entity * @throws Exception exception */ public static void initRequestFromEntity(ClassicHttpRequest requestBase, Map body, String charset) throws Exception { if (body == null || body.isEmpty()) { return; } List params = new ArrayList<>(body.size()); for (Map.Entry entry : body.entrySet()) { params.add(new BasicNameValuePair(entry.getKey(), entry.getValue())); } if (requestBase instanceof HttpEntityContainer) { HttpEntityContainer request = requestBase; HttpEntity entity = new UrlEncodedFormEntity(params, Charset.forName(charset)); request.setEntity(entity); } } /** * Build URL. * * @param isHttps whether is https * @param serverAddr server ip/address * @param subPaths api path * @return URL string */ public static String buildUrl(boolean isHttps, String serverAddr, String... subPaths) { StringBuilder sb = new StringBuilder(); if (isHttps) { sb.append(HTTPS_PREFIX); } else { sb.append(HTTP_PREFIX); } sb.append(serverAddr); String pre = null; for (String subPath : subPaths) { if (StringUtils.isBlank(subPath)) { continue; } Matcher matcher = CONTEXT_PATH_MATCH.matcher(subPath); if (matcher.find()) { throw new IllegalArgumentException("Illegal url path expression : " + subPath); } if (pre == null || !pre.endsWith("/")) { if (subPath.startsWith("/")) { sb.append(subPath); } else { sb.append('/').append(subPath); } } else { if (subPath.startsWith("/")) { sb.append(subPath.replaceFirst("\\/", "")); } else { sb.append(subPath); } } pre = subPath; } return sb.toString(); } /** * Translate parameter map. * * @param parameterMap parameter map * @return parameter map * @throws Exception exception */ public static Map translateParameterMap(Map parameterMap) throws Exception { Map map = new HashMap<>(16); for (Map.Entry entry : parameterMap.entrySet()) { map.put(entry.getKey(), entry.getValue()[0]); } return map; } /** * Encoding parameters to url string. * * @param params parameters * @param encoding encoding charset * @return url string * @throws UnsupportedEncodingException if encoding string is illegal */ public static String encodingParams(Map params, String encoding) throws UnsupportedEncodingException { StringBuilder sb = new StringBuilder(); if (null == params || params.isEmpty()) { return null; } for (Map.Entry entry : params.entrySet()) { if (StringUtils.isEmpty(entry.getValue())) { continue; } sb.append(entry.getKey()).append('='); sb.append(URLEncoder.encode(entry.getValue(), encoding)); sb.append('&'); } return sb.toString(); } /** * Encoding KV list to url string. * * @param paramValues parameters * @param encoding encoding charset * @return url string * @throws UnsupportedEncodingException if encoding string is illegal */ public static String encodingParams(List paramValues, String encoding) throws UnsupportedEncodingException { StringBuilder sb = new StringBuilder(); if (null == paramValues) { return null; } for (Iterator iter = paramValues.iterator(); iter.hasNext(); ) { sb.append(iter.next()).append('='); sb.append(URLEncoder.encode(iter.next(), encoding)); if (iter.hasNext()) { sb.append('&'); } } return sb.toString(); } public static String decode(String str, String encode) throws UnsupportedEncodingException { return innerDecode(null, str, encode); } /** * build URI By url and query. * * @param url url * @param query query param {@link Query} * @return {@link URI} */ public static URI buildUri(String url, Query query) throws URISyntaxException { if (query != null && !query.isEmpty()) { url = url + "?" + query.toQueryUrl(); } return new URI(url); } /** * HTTP request exception is a timeout exception. * * @param throwable http request throwable * @return boolean */ public static boolean isTimeoutException(Throwable throwable) { return throwable instanceof SocketTimeoutException || throwable instanceof ConnectTimeoutException || throwable instanceof TimeoutException || throwable.getCause() instanceof TimeoutException; } /** * Build header. * * @return header */ public static Header builderHeader(String module) { Header header = Header.newInstance(); header.addParam(HttpHeaderConsts.CLIENT_VERSION_HEADER, VersionUtils.version); header.addParam(HttpHeaderConsts.USER_AGENT_HEADER, VersionUtils.getFullClientVersion()); header.addParam(HttpHeaderConsts.ACCEPT_ENCODING, "gzip,deflate,sdch"); header.addParam(HttpHeaderConsts.CONNECTION, "Keep-Alive"); header.addParam(HttpHeaderConsts.REQUEST_ID, UuidUtils.generateUuid()); header.addParam(HttpHeaderConsts.REQUEST_MODULE, module); return header; } private static String innerDecode(String pre, String now, String encode) throws UnsupportedEncodingException { // Because the data may be encoded by the URL more than once, // it needs to be decoded recursively until it is fully successful if (StringUtils.equals(pre, now)) { return pre; } pre = now; now = URLDecoder.decode(now, encode); return innerDecode(pre, now, encode); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/AbstractNacosRestTemplate.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.constant.ResponseHandlerType; import com.alibaba.nacos.common.http.client.handler.BeanResponseHandler; import com.alibaba.nacos.common.http.client.handler.ResponseHandler; import com.alibaba.nacos.common.http.client.handler.RestResultResponseHandler; import com.alibaba.nacos.common.http.client.handler.StringResponseHandler; import com.alibaba.nacos.common.utils.JacksonUtils; import com.fasterxml.jackson.databind.JavaType; import org.slf4j.Logger; import java.lang.reflect.Type; import java.util.HashMap; import java.util.Map; /** * For NacosRestTemplate and NacosAsyncRestTemplate, provide initialization and register of response converter. * * @author mai.jh */ @SuppressWarnings("all") public abstract class AbstractNacosRestTemplate { private final Map responseHandlerMap = new HashMap(); protected final Logger logger; public AbstractNacosRestTemplate(Logger logger) { this.logger = logger; initDefaultResponseHandler(); } private void initDefaultResponseHandler() { // init response handler responseHandlerMap.put(ResponseHandlerType.STRING_TYPE, new StringResponseHandler()); responseHandlerMap.put(ResponseHandlerType.RESTRESULT_TYPE, new RestResultResponseHandler()); responseHandlerMap.put(ResponseHandlerType.DEFAULT_BEAN_TYPE, new BeanResponseHandler()); } /** * register customization Response Handler. * * @param responseHandler {@link ResponseHandler} */ public void registerResponseHandler(String responseHandlerType, ResponseHandler responseHandler) { responseHandlerMap.put(responseHandlerType, responseHandler); } /** * Select a response handler by responseType. * * @param responseType responseType * @return ResponseHandler */ protected ResponseHandler selectResponseHandler(Type responseType) { ResponseHandler responseHandler = null; if (responseType == null) { responseHandler = responseHandlerMap.get(ResponseHandlerType.STRING_TYPE); } if (responseHandler == null) { JavaType javaType = JacksonUtils.constructJavaType(responseType); String name = javaType.getRawClass().getName(); responseHandler = responseHandlerMap.get(name); } // When the corresponding type of response handler cannot be obtained, // the default bean response handler is used if (responseHandler == null) { responseHandler = responseHandlerMap.get(ResponseHandlerType.DEFAULT_BEAN_TYPE); } responseHandler.setResponseType(responseType); return responseHandler; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/HttpClientRequestInterceptor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.http.client.request.HttpClientRequest; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.model.RequestHttpEntity; import java.net.URI; /** * Intercepts client-side HTTP requests. Implementations of this interface can be. * * @author mai.jh */ public interface HttpClientRequestInterceptor { /** * is intercept. * * @param uri uri * @param httpMethod http method * @param requestHttpEntity request entity * @return boolean */ boolean isIntercept(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity); /** * if isIntercept method is true Intercept the given request, and return a response Otherwise, * the {@link HttpClientRequest} will be used for execution. * * @return HttpClientResponse */ HttpClientResponse intercept(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/InterceptingHttpClientRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.http.client.request.HttpClientRequest; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.model.RequestHttpEntity; import java.io.IOException; import java.net.URI; import java.util.Iterator; /** * Wrap http client request and perform corresponding interception. * * @author mai.jh */ public class InterceptingHttpClientRequest implements HttpClientRequest { private final HttpClientRequest httpClientRequest; private final Iterator interceptors; public InterceptingHttpClientRequest(HttpClientRequest httpClientRequest, Iterator interceptors) { this.httpClientRequest = httpClientRequest; this.interceptors = interceptors; } @Override public HttpClientResponse execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity) throws Exception { while (interceptors.hasNext()) { HttpClientRequestInterceptor nextInterceptor = interceptors.next(); if (nextInterceptor.isIntercept(uri, httpMethod, requestHttpEntity)) { return nextInterceptor.intercept(); } } return httpClientRequest.execute(uri, httpMethod, requestHttpEntity); } @Override public void close() throws IOException { httpClientRequest.close(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/NacosAsyncRestTemplate.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.http.Callback; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.handler.ResponseHandler; import com.alibaba.nacos.common.http.client.request.AsyncHttpClientRequest; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RequestHttpEntity; import com.alibaba.nacos.common.utils.HttpMethod; import org.slf4j.Logger; import java.lang.reflect.Type; import java.net.URI; import java.util.Map; /** * Nacos async rest template. * * @author mai.jh * @see AsyncHttpClientRequest * @see HttpClientResponse */ public class NacosAsyncRestTemplate extends AbstractNacosRestTemplate { private final AsyncHttpClientRequest clientRequest; public NacosAsyncRestTemplate(Logger logger, AsyncHttpClientRequest clientRequest) { super(logger); this.clientRequest = clientRequest; } /** * async http get URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param responseType return type * @param header http header param * @param query http query param * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void get(String url, Header header, Query query, Type responseType, Callback callback) { execute(url, HttpMethod.GET, new RequestHttpEntity(header, query), responseType, callback); } /** * async get request, may be pulling a lot of data URL request params are expanded using the given query {@link * Query}, More request parameters can be set via body. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param body get with body * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void getLarge(String url, Header header, Query query, Object body, Type responseType, Callback callback) { execute(url, HttpMethod.GET_LARGE, new RequestHttpEntity(header, query, body), responseType, callback); } /** * async http delete URL request params are expanded using the given query {@link Query}, * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void delete(String url, Header header, Query query, Type responseType, Callback callback) { execute(url, HttpMethod.DELETE, new RequestHttpEntity(header, query), responseType, callback); } /** * async http delete large request, when the parameter exceeds the URL limit, you can use this method to put the * parameter into the body pass. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param body body * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void delete(String url, Header header, String body, Type responseType, Callback callback) { execute(url, HttpMethod.DELETE_LARGE, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), Query.EMPTY, body), responseType, callback); } /** * async http put Create a new resource by PUTting the given body to http request. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void put(String url, Header header, Query query, Object body, Type responseType, Callback callback) { execute(url, HttpMethod.PUT, new RequestHttpEntity(header, query, body), responseType, callback); } /** * async http put Json Create a new resource by PUTting the given body to http request, http header contentType * default 'application/json;charset=UTF-8'. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void putJson(String url, Header header, Query query, String body, Type responseType, Callback callback) { execute(url, HttpMethod.PUT, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), query, body), responseType, callback); } /** * async http put Json Create a new resource by PUTting the given body to http request, http header contentType * default 'application/json;charset=UTF-8'. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param body http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void putJson(String url, Header header, String body, Type responseType, Callback callback) { execute(url, HttpMethod.PUT, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), body), responseType, callback); } /** * async http put from Create a new resource by PUTting the given map {@code bodyValues} to http request, http * header contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param bodyValues http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void putForm(String url, Header header, Query query, Map bodyValues, Type responseType, Callback callback) { execute(url, HttpMethod.PUT, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), query, bodyValues), responseType, callback); } /** * async http put from Create a new resource by PUTting the given map {@code bodyValues} to http request, http * header contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param bodyValues http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void putForm(String url, Header header, Map bodyValues, Type responseType, Callback callback) { execute(url, HttpMethod.PUT, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), bodyValues), responseType, callback); } /** * async http post Create a new resource by POSTing the given object to the http request. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void post(String url, Header header, Query query, Object body, Type responseType, Callback callback) { execute(url, HttpMethod.POST, new RequestHttpEntity(header, query, body), responseType, callback); } /** * async http post Json Create a new resource by POSTing the given object to the http request, http header * contentType default 'application/json;charset=UTF-8'. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void postJson(String url, Header header, Query query, String body, Type responseType, Callback callback) { execute(url, HttpMethod.POST, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), query, body), responseType, callback); } /** * async http post Json Create a new resource by POSTing the given object to the http request, http header * contentType default 'application/json;charset=UTF-8'. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param body http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void postJson(String url, Header header, String body, Type responseType, Callback callback) { execute(url, HttpMethod.POST, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), body), responseType, callback); } /** * async http post from Create a new resource by PUTting the given map {@code bodyValues} to http request, http * header contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param query http query param * @param bodyValues http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void postForm(String url, Header header, Query query, Map bodyValues, Type responseType, Callback callback) { execute(url, HttpMethod.POST, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), query, bodyValues), responseType, callback); } /** * async http post from Create a new resource by PUTting the given map {@code bodyValues} to http request, http * header contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    {@code responseType} can be an RestResult or RestResult data {@code T} type. * *

    {@code callback} Result callback execution, * if you need response headers, you can convert the received RestResult to HttpRestResult. * * @param url url * @param header http header param * @param bodyValues http body param * @param responseType return type * @param callback callback {@link Callback#onReceive(com.alibaba.nacos.common.model.RestResult)} */ public void postForm(String url, Header header, Map bodyValues, Type responseType, Callback callback) { execute(url, HttpMethod.POST, new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), bodyValues), responseType, callback); } @SuppressWarnings("unchecked") private void execute(String url, String httpMethod, RequestHttpEntity requestEntity, Type type, Callback callback) { try { URI uri = HttpUtils.buildUri(url, requestEntity.getQuery()); if (logger.isDebugEnabled()) { logger.debug("HTTP method: {}, url: {}, body: {}", httpMethod, uri, requestEntity.getBody()); } ResponseHandler responseHandler = super.selectResponseHandler(type); clientRequest.execute(uri, httpMethod, requestEntity, responseHandler, callback); } catch (Exception e) { // When an exception occurs, use Callback to pass it instead of throw it directly. callback.onError(e); } } /** * close request client. */ public void close() throws Exception { clientRequest.close(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/NacosRestTemplate.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.handler.ResponseHandler; import com.alibaba.nacos.common.http.client.request.DefaultHttpClientRequest; import com.alibaba.nacos.common.http.client.request.HttpClientRequest; import com.alibaba.nacos.common.http.client.request.JdkHttpClientRequest; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RequestHttpEntity; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.HttpMethod; import org.slf4j.Logger; import java.io.File; import java.lang.reflect.Type; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Nacos rest template Interface specifying a basic set of RESTful operations. * * @author mai.jh * @see HttpClientRequest * @see HttpClientResponse */ public class NacosRestTemplate extends AbstractNacosRestTemplate { private final HttpClientRequest requestClient; private final List interceptors = new ArrayList<>(); public NacosRestTemplate(Logger logger, HttpClientRequest requestClient) { super(logger); this.requestClient = requestClient; } /** * http get URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult get(String url, Header header, Query query, Type responseType) throws Exception { return execute(url, HttpMethod.GET, new RequestHttpEntity(header, query), responseType); } /** * http get URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * *

    {@code config} Specify the request config via {@link HttpClientConfig} * * @param url url * @param config http config * @param header headers * @param query http query param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult get(String url, HttpClientConfig config, Header header, Query query, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(config, header, query); return execute(url, HttpMethod.GET, requestHttpEntity, responseType); } /** * get request, may be pulling a lot of data URL request params are expanded using the given query {@link Query}, * More request parameters can be set via body. * *

    This method can only be used when HttpClientRequest is implemented by {@link DefaultHttpClientRequest}, note: * {@link JdkHttpClientRequest} Implementation does not support this method. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param body get with body * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult getLarge(String url, Header header, Query query, Object body, Type responseType) throws Exception { return execute(url, HttpMethod.GET_LARGE, new RequestHttpEntity(header, query, body), responseType); } /** * http delete URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult delete(String url, Header header, Query query, Type responseType) throws Exception { return execute(url, HttpMethod.DELETE, new RequestHttpEntity(header, query), responseType); } /** * http delete URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * *

    {@code config} Specify the request config via {@link HttpClientConfig} * * @param url url * @param config http config * @param header http header param * @param query http query param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult delete(String url, HttpClientConfig config, Header header, Query query, Type responseType) throws Exception { return execute(url, HttpMethod.DELETE, new RequestHttpEntity(config, header, query), responseType); } /** * http put Create a new resource by PUTting the given body to http request. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult put(String url, Header header, Query query, Object body, Type responseType) throws Exception { return execute(url, HttpMethod.PUT, new RequestHttpEntity(header, query, body), responseType); } /** * http put json Create a new resource by PUTting the given body to http request, http header contentType default * 'application/json;charset=UTF-8'. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult putJson(String url, Header header, Query query, String body, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), query, body); return execute(url, HttpMethod.PUT, requestHttpEntity, responseType); } /** * http put json Create a new resource by PUTting the given body to http request, http header contentType default * 'application/json;charset=UTF-8'. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param body http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult putJson(String url, Header header, String body, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), body); return execute(url, HttpMethod.PUT, requestHttpEntity, responseType); } /** * http put from Create a new resource by PUTting the given map {@code bodyValues} to http request, http header * contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    URL request params are expanded using the given query {@code Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param bodyValues http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult putForm(String url, Header header, Query query, Map bodyValues, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity( header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), query, bodyValues); return execute(url, HttpMethod.PUT, requestHttpEntity, responseType); } /** * http put from Create a new resource by PUTting the given map {@code bodyValues} to http request, http header * contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param bodyValues http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult putForm(String url, Header header, Map bodyValues, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity( header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), bodyValues); return execute(url, HttpMethod.PUT, requestHttpEntity, responseType); } /** * http put from Create a new resource by PUTting the given map {@code bodyValues} to http request, http header * contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * *

    {@code config} Specify the request config via {@link HttpClientConfig} * * @param url url * @param config http config * @param header http header param * @param bodyValues http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult putForm(String url, HttpClientConfig config, Header header, Map bodyValues, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(config, header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), bodyValues); return execute(url, HttpMethod.PUT, requestHttpEntity, responseType); } /** * http post Create a new resource by POSTing the given object to the http request. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult post(String url, Header header, Query query, Object body, Type responseType) throws Exception { return execute(url, HttpMethod.POST, new RequestHttpEntity(header, query, body), responseType); } /** * http post json Create a new resource by POSTing the given object to the http request, http header contentType * default 'application/json;charset=UTF-8'. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param body http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult postJson(String url, Header header, Query query, String body, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), query, body); return execute(url, HttpMethod.POST, requestHttpEntity, responseType); } /** * http post json Create a new resource by POSTing the given object to the http request, http header contentType * default 'application/json;charset=UTF-8'. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param body http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult postJson(String url, Header header, String body, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(header.setContentType(MediaType.APPLICATION_JSON), body); return execute(url, HttpMethod.POST, requestHttpEntity, responseType); } /** * http post from Create a new resource by PUTting the given map {@code bodyValues} to http request, http header * contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    URL request params are expanded using the given query {@link Query}. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param query http query param * @param bodyValues http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult postForm(String url, Header header, Query query, Map bodyValues, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity( header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), query, bodyValues); return execute(url, HttpMethod.POST, requestHttpEntity, responseType); } /** * http post from Create a new resource by PUTting the given map {@code bodyValues} to http request, http header * contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * * @param url url * @param header http header param * @param bodyValues http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult postForm(String url, Header header, Map bodyValues, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity( header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), bodyValues); return execute(url, HttpMethod.POST, requestHttpEntity, responseType); } /** * http post from Create a new resource by PUTting the given map {@code bodyValues} to http request, http header * contentType default 'application/x-www-form-urlencoded;charset=utf-8'. * *

    {@code responseType} can be an HttpRestResult or HttpRestResult data {@code T} type. * *

    {@code config} Specify the request config via {@link HttpClientConfig} * * @param url url * @param config http config * @param header http header param * @param bodyValues http body param * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult postForm(String url, HttpClientConfig config, Header header, Map bodyValues, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(config, header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), bodyValues); return execute(url, HttpMethod.POST, requestHttpEntity, responseType); } public HttpRestResult postFile(String url, HttpClientConfig config, Header header, File file, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(config, header, file); return execute(url, HttpMethod.POST, requestHttpEntity, responseType); } /** * Execute the HTTP method to the given URI template, writing the given request entity to the request, and returns * the response as {@link HttpRestResult}. * * @param url url * @param header http header param * @param query http query param * @param bodyValues http body param * @param httpMethod http method * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult exchangeForm(String url, Header header, Query query, Map bodyValues, String httpMethod, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity( header.setContentType(MediaType.APPLICATION_FORM_URLENCODED), query, bodyValues); return execute(url, httpMethod, requestHttpEntity, responseType); } /** * Execute the HTTP method to the given URI template, writing the given request entity to the request, and returns * the response as {@link HttpRestResult}. * * @param url url * @param config HttpClientConfig * @param header http header param * @param query http query param * @param body http body param * @param httpMethod http method * @param responseType return type * @return {@link HttpRestResult} * @throws Exception ex */ public HttpRestResult exchange(String url, HttpClientConfig config, Header header, Query query, Object body, String httpMethod, Type responseType) throws Exception { RequestHttpEntity requestHttpEntity = new RequestHttpEntity(config, header, query, body); return execute(url, httpMethod, requestHttpEntity, responseType); } /** * Set the request interceptors that this accessor should use. * * @param interceptors {@link HttpClientRequestInterceptor} */ public void setInterceptors(List interceptors) { if (this.interceptors != interceptors) { this.interceptors.clear(); this.interceptors.addAll(interceptors); } } /** * Return the request interceptors that this accessor uses. * *

    The returned {@link List} is active and may get appended to. */ public List getInterceptors() { return interceptors; } @SuppressWarnings("unchecked") private HttpRestResult execute(String url, String httpMethod, RequestHttpEntity requestEntity, Type responseType) throws Exception { URI uri = HttpUtils.buildUri(url, requestEntity.getQuery()); if (logger.isDebugEnabled()) { logger.debug("HTTP method: {}, url: {}, body: {}", httpMethod, uri, requestEntity.getBody()); } ResponseHandler responseHandler = super.selectResponseHandler(responseType); HttpClientResponse response = null; try { response = this.requestClient().execute(uri, httpMethod, requestEntity); return responseHandler.handle(response); } finally { if (response != null) { response.close(); } } } private HttpClientRequest requestClient() { if (CollectionUtils.isNotEmpty(interceptors)) { if (logger.isDebugEnabled()) { logger.debug("Execute via interceptors :{}", interceptors); } return new InterceptingHttpClientRequest(requestClient, interceptors.iterator()); } return requestClient; } /** * close request client. */ public void close() throws Exception { requestClient.close(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/handler/AbstractResponseHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.handler; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.utils.IoUtils; import org.apache.hc.core5.http.HttpStatus; import java.lang.reflect.Type; /** * Abstract response handler. * * @author mai.jh */ public abstract class AbstractResponseHandler implements ResponseHandler { private Type responseType; @Override public final void setResponseType(Type responseType) { this.responseType = responseType; } @Override public final HttpRestResult handle(HttpClientResponse response) throws Exception { if (HttpStatus.SC_OK != response.getStatusCode()) { return handleError(response); } return convertResult(response, this.responseType); } private HttpRestResult handleError(HttpClientResponse response) throws Exception { Header headers = response.getHeaders(); String message = IoUtils.toString(response.getBody(), headers.getCharset()); return new HttpRestResult<>(headers, response.getStatusCode(), null, message); } /** * Abstract convertResult method, Different types of converters for expansion. * * @param response http client response * @param responseType responseType * @return HttpRestResult * @throws Exception ex */ public abstract HttpRestResult convertResult(HttpClientResponse response, Type responseType) throws Exception; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/handler/BeanResponseHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.handler; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.utils.JacksonUtils; import java.lang.reflect.Type; /** * bean response handler, * Mainly converter response type as bean type. * * @author mai.jh */ public class BeanResponseHandler extends AbstractResponseHandler { @Override public HttpRestResult convertResult(HttpClientResponse response, Type responseType) throws Exception { final Header headers = response.getHeaders(); T extractBody = JacksonUtils.toObj(response.getBody(), responseType); return new HttpRestResult<>(headers, response.getStatusCode(), extractBody, null); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/handler/ResponseHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.handler; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import java.lang.reflect.Type; /** * Response Handler abstract interface, * the actual processing of the response conversion is done by a concrete implementation class. * * @author mai.jh */ public interface ResponseHandler { /** * set response type. * * @param responseType responseType */ void setResponseType(Type responseType); /** * handle response convert to HttpRestResult. * * @param response http response * @return HttpRestResult {@link HttpRestResult} * @throws Exception ex */ HttpRestResult handle(HttpClientResponse response) throws Exception; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/handler/RestResultResponseHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.handler; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.utils.JacksonUtils; import java.lang.reflect.Type; /** * RestResult response handler, Mainly converter response type as {@link RestResult} type. * * @author mai.jh */ public class RestResultResponseHandler extends AbstractResponseHandler { @Override @SuppressWarnings("unchecked") public HttpRestResult convertResult(HttpClientResponse response, Type responseType) throws Exception { final Header headers = response.getHeaders(); T extractBody = JacksonUtils.toObj(response.getBody(), responseType); HttpRestResult httpRestResult = convert((RestResult) extractBody); httpRestResult.setHeader(headers); return httpRestResult; } private static HttpRestResult convert(RestResult restResult) { HttpRestResult httpRestResult = new HttpRestResult<>(); httpRestResult.setCode(restResult.getCode()); httpRestResult.setData(restResult.getData()); httpRestResult.setMessage(restResult.getMessage()); return httpRestResult; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/handler/StringResponseHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.handler; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.utils.IoUtils; import java.lang.reflect.Type; /** * string response handler, Mainly converter response type as string type. * * @author mai.jh */ public class StringResponseHandler extends AbstractResponseHandler { @Override public HttpRestResult convertResult(HttpClientResponse response, Type responseType) throws Exception { final Header headers = response.getHeaders(); String extractBody = IoUtils.toString(response.getBody(), headers.getCharset()); return new HttpRestResult<>(headers, response.getStatusCode(), extractBody, null); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/request/AsyncHttpClientRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.http.Callback; import com.alibaba.nacos.common.http.client.handler.ResponseHandler; import com.alibaba.nacos.common.model.RequestHttpEntity; import java.io.Closeable; import java.net.URI; /** * Represents a client-side Async HTTP request. Created via an implementation execute. * * @author mai.jh */ public interface AsyncHttpClientRequest extends Closeable { /** * execute async http request. * * @param uri http url * @param httpMethod http request method * @param requestHttpEntity http request entity * @param responseHandler http response handler * @param callback http response callback * @throws Exception ex */ void execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity, final ResponseHandler responseHandler, final Callback callback) throws Exception; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/request/DefaultAsyncHttpClientRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.http.Callback; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.handler.ResponseHandler; import com.alibaba.nacos.common.http.client.response.DefaultClientHttpResponse; import com.alibaba.nacos.common.model.RequestHttpEntity; import org.apache.hc.client5.http.async.methods.SimpleHttpRequest; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; import org.apache.hc.core5.reactor.IOReactorStatus; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.net.URI; /** * {@link AsyncHttpClientRequest} implementation that uses apache async http client to execute streaming requests. * * @author mai.jh */ public class DefaultAsyncHttpClientRequest implements AsyncHttpClientRequest { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAsyncHttpClientRequest.class); private final CloseableHttpAsyncClient asyncClient; private final RequestConfig defaultConfig; public DefaultAsyncHttpClientRequest(CloseableHttpAsyncClient asyncClient, DefaultConnectingIOReactor ioReactor, RequestConfig defaultConfig) { this.asyncClient = asyncClient; this.defaultConfig = defaultConfig; if (this.asyncClient.getStatus() != IOReactorStatus.ACTIVE) { this.asyncClient.start(); } } @Override public void execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity, final ResponseHandler responseHandler, final Callback callback) throws Exception { HttpUriRequestBase httpRequestBase = DefaultHttpClientRequest.build(uri, httpMethod, requestHttpEntity, defaultConfig); // IllegalStateException has been removed from ver.5.0, should catch it in DefaultConnectingIOReactor callback FutureCallback futureCallback = new FutureCallback() { @Override public void completed(SimpleHttpResponse result) { // SimpleHttpResponse doesn't need to close DefaultClientHttpResponse response = new DefaultClientHttpResponse(result); try { HttpRestResult httpRestResult = responseHandler.handle(response); callback.onReceive(httpRestResult); } catch (Exception e) { callback.onError(e); } } @Override public void failed(Exception ex) { callback.onError(ex); } @Override public void cancelled() { callback.onCancel(); } }; asyncClient.execute(SimpleHttpRequest.copy(httpRequestBase), futureCallback); } @Override public void close() throws IOException { this.asyncClient.close(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/request/DefaultHttpClientRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.BaseHttpMethod; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.response.DefaultClientHttpResponse; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.model.RequestHttpEntity; import com.alibaba.nacos.common.utils.IoUtils; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.util.Timeout; import java.io.IOException; import java.net.URI; import java.util.Map; /** * {@link HttpClientRequest} implementation that uses apache http client to execute streaming requests. * * @author mai.jh */ @SuppressWarnings({"unchecked", "resource"}) public class DefaultHttpClientRequest implements HttpClientRequest { private final CloseableHttpClient client; private final RequestConfig defaultConfig; public DefaultHttpClientRequest(CloseableHttpClient client, RequestConfig defaultConfig) { this.client = client; this.defaultConfig = defaultConfig; } @Override public HttpClientResponse execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity) throws Exception { HttpUriRequestBase request = build(uri, httpMethod, requestHttpEntity, defaultConfig); // copy http response to simple type SimpleHttpResponse response = client.execute(request, httpResponse -> { SimpleHttpResponse simpleHttpResponse = SimpleHttpResponse.copy(httpResponse); ContentType contentType = ContentType.parse(httpResponse.getEntity().getContentType()); String body = IoUtils.toString(httpResponse.getEntity().getContent(), null); simpleHttpResponse.setBody(body, contentType); return simpleHttpResponse; }); return new DefaultClientHttpResponse(response); } static HttpUriRequestBase build(URI uri, String method, RequestHttpEntity requestHttpEntity, RequestConfig defaultConfig) throws Exception { final Header headers = requestHttpEntity.getHeaders(); final BaseHttpMethod httpMethod = BaseHttpMethod.sourceOf(method); final HttpUriRequestBase httpRequestBase = httpMethod.init(uri.toString()); HttpUtils.initRequestHeader(httpRequestBase, headers); if (MediaType.APPLICATION_FORM_URLENCODED.equals(headers.getValue(HttpHeaderConsts.CONTENT_TYPE)) && requestHttpEntity.getBody() instanceof Map) { HttpUtils.initRequestFromEntity(httpRequestBase, (Map) requestHttpEntity.getBody(), headers.getCharset()); } else { HttpUtils.initRequestEntity(httpRequestBase, requestHttpEntity.getBody(), headers); } mergeDefaultConfig(httpRequestBase, requestHttpEntity.getHttpClientConfig(), defaultConfig); return httpRequestBase; } /** * Merge the HTTP config created by default with the HTTP config specified in the request. * * @param requestBase requestBase * @param httpClientConfig http config */ private static void mergeDefaultConfig(HttpUriRequestBase requestBase, HttpClientConfig httpClientConfig, RequestConfig defaultConfig) { if (httpClientConfig == null) { return; } requestBase.setConfig(RequestConfig.copy(defaultConfig) .setConnectionRequestTimeout(Timeout.ofMilliseconds(httpClientConfig.getConTimeOutMillis())) .setResponseTimeout(Timeout.ofMilliseconds(httpClientConfig.getReadTimeOutMillis())).build()); } @Override public void close() throws IOException { client.close(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/request/HttpClientRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.model.RequestHttpEntity; import java.io.Closeable; import java.net.URI; /** * Represents a client-side HTTP request. Created via an implementation execute. * * @author mai.jh */ public interface HttpClientRequest extends Closeable { /** * execute http request. * * @param uri http url * @param httpMethod http request method * @param requestHttpEntity http request entity * @return HttpClientResponse * @throws Exception ex */ HttpClientResponse execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity) throws Exception; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/request/JdkHttpClientRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.client.response.JdkHttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.model.RequestHttpEntity; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URI; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; import java.util.Map; /** * JDK http client request implement. * * @author mai.jh */ public class JdkHttpClientRequest implements HttpClientRequest { private static final String CONTENT_LENGTH = "Content-Length"; private static final String BOUNDARY_PREFIX = "----WebKitFormBoundary"; private static final String LINE_FEED = "\r\n"; private HttpClientConfig httpClientConfig; public JdkHttpClientRequest(HttpClientConfig httpClientConfig) { this.httpClientConfig = httpClientConfig; } /** * Use specified {@link SSLContext}. * * @param sslContext ssl context */ @SuppressWarnings("checkstyle:abbreviationaswordinname") public void setSslContext(SSLContext sslContext) { if (sslContext != null) { HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory()); } } /** * Replace the default HostnameVerifier. * * @param hostnameVerifier custom hostnameVerifier */ @SuppressWarnings("checkstyle:abbreviationaswordinname") public void replaceSslHostnameVerifier(HostnameVerifier hostnameVerifier) { if (hostnameVerifier != null) { HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); } } @Override public HttpClientResponse execute(URI uri, String httpMethod, RequestHttpEntity requestHttpEntity) throws Exception { final Object body = requestHttpEntity.getBody(); final Header headers = requestHttpEntity.getHeaders(); replaceDefaultConfig(requestHttpEntity.getHttpClientConfig()); HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); Map headerMap = headers.getHeader(); if (headerMap != null && headerMap.size() > 0) { for (Map.Entry entry : headerMap.entrySet()) { conn.setRequestProperty(entry.getKey(), entry.getValue()); } } conn.setConnectTimeout(this.httpClientConfig.getConTimeOutMillis()); conn.setReadTimeout(this.httpClientConfig.getReadTimeOutMillis()); conn.setRequestMethod(httpMethod); if (body != null && !"".equals(body)) { if (body instanceof File) { handleFileUpload(conn, (File) body); } String contentType = headers.getValue(HttpHeaderConsts.CONTENT_TYPE); String bodyStr = body instanceof String ? (String) body : JacksonUtils.toJson(body); if (MediaType.APPLICATION_FORM_URLENCODED.equals(contentType)) { Map map = JacksonUtils.toObj(bodyStr, HashMap.class); bodyStr = HttpUtils.encodingParams(map, headers.getCharset()); } if (bodyStr != null) { conn.setDoOutput(true); byte[] b = bodyStr.getBytes(StandardCharsets.UTF_8); conn.setRequestProperty(CONTENT_LENGTH, String.valueOf(b.length)); OutputStream outputStream = conn.getOutputStream(); outputStream.write(b, 0, b.length); outputStream.flush(); IoUtils.closeQuietly(outputStream); } } conn.connect(); return new JdkHttpClientResponse(conn); } private void handleFileUpload(HttpURLConnection conn, File file) throws IOException { String boundary = BOUNDARY_PREFIX + System.currentTimeMillis(); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary); StringBuilder sb = new StringBuilder(); sb.append("--").append(boundary).append(LINE_FEED); sb.append("Content-Disposition: form-data; name=\"file\"; filename=\"").append(file.getName()).append("\"") .append(LINE_FEED); sb.append("Content-Type: ").append(Files.probeContentType(file.toPath())).append(LINE_FEED).append(LINE_FEED); byte[] fileBytes = Files.readAllBytes(file.toPath()); byte[] boundaryBytes = (LINE_FEED + "--" + boundary + "--" + LINE_FEED).getBytes(StandardCharsets.UTF_8); conn.setDoOutput(true); try (OutputStream outputStream = conn.getOutputStream()) { outputStream.write(sb.toString().getBytes(StandardCharsets.UTF_8)); outputStream.write(fileBytes); outputStream.write(boundaryBytes); outputStream.flush(); } } /** * Replace the HTTP config created by default with the HTTP config specified in the request. * * @param replaceConfig http config */ private void replaceDefaultConfig(HttpClientConfig replaceConfig) { if (replaceConfig == null) { return; } this.httpClientConfig = replaceConfig; } @Override public void close() throws IOException { } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/response/DefaultClientHttpResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.response; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.utils.IoUtils; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import java.io.ByteArrayInputStream; import java.io.InputStream; /** * ApacheClientHttpResponse implementation {@link HttpClientResponse}. * * @author mai.jh */ public class DefaultClientHttpResponse implements HttpClientResponse { private SimpleHttpResponse response; private InputStream responseStream; private Header responseHeader; public DefaultClientHttpResponse(SimpleHttpResponse response) { this.response = response; } @Override public int getStatusCode() { return this.response.getCode(); } @Override public String getStatusText() { return this.response.getReasonPhrase(); } @Override public Header getHeaders() { if (this.responseHeader == null) { this.responseHeader = Header.newInstance(); org.apache.hc.core5.http.Header[] allHeaders = response.getHeaders(); for (org.apache.hc.core5.http.Header header : allHeaders) { this.responseHeader.addParam(header.getName(), header.getValue()); } } return this.responseHeader; } @Override public InputStream getBody() { byte[] bodyBytes = response.getBody().getBodyBytes(); if (bodyBytes != null) { this.responseStream = new ByteArrayInputStream(bodyBytes); } else { this.responseStream = new ByteArrayInputStream(new byte[0]); } return this.responseStream; } @Override public void close() { IoUtils.closeQuietly(this.responseStream); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/response/HttpClientResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.response; import com.alibaba.nacos.common.http.param.Header; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; /** * Represents a client-side HTTP response. * In new version of Apache Http Components, {@code HttpResponse} has been replaced by {@link SimpleHttpResponse}. * Cause in this class body content no longer be {@link InputStream} anymore, we don't need to close it anymore. * * @author mai.jh */ public interface HttpClientResponse extends Closeable { /** * Return the headers of this message. * * @return a corresponding HttpHeaders object (never {@code null}) */ Header getHeaders(); /** * Return the body of the message as an input stream. * * @return String response body * @throws IOException IOException */ InputStream getBody() throws IOException; /** * Return the HTTP status code. * * @return the HTTP status as an integer * @throws IOException IOException */ int getStatusCode() throws IOException; /** * Return the HTTP status text of the response. * * @return the HTTP status text * @throws IOException IOException */ String getStatusText() throws IOException; /** * close response InputStream. */ @Override void close(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/client/response/JdkHttpClientResponse.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.response; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.utils.IoUtils; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.util.List; import java.util.Map; /** * JDk http client response implement. * * @author mai.jh */ public class JdkHttpClientResponse implements HttpClientResponse { private final HttpURLConnection conn; private InputStream responseStream; private Header responseHeader; private static final String CONTENT_ENCODING = "gzip"; public JdkHttpClientResponse(HttpURLConnection conn) { this.conn = conn; } @Override public Header getHeaders() { if (this.responseHeader == null) { this.responseHeader = Header.newInstance(); } for (Map.Entry> entry : conn.getHeaderFields().entrySet()) { this.responseHeader.addOriginalResponseHeader(entry.getKey(), entry.getValue()); } return this.responseHeader; } @Override public InputStream getBody() throws IOException { Header headers = getHeaders(); InputStream errorStream = this.conn.getErrorStream(); this.responseStream = (errorStream != null ? errorStream : this.conn.getInputStream()); String contentEncoding = headers.getValue(HttpHeaderConsts.CONTENT_ENCODING); // Used to process http content_encoding, when content_encoding is GZIP, use GZIPInputStream if (CONTENT_ENCODING.equals(contentEncoding)) { byte[] bytes = IoUtils.tryDecompress(this.responseStream); return new ByteArrayInputStream(bytes); } return this.responseStream; } @Override public int getStatusCode() throws IOException { return this.conn.getResponseCode(); } @Override public String getStatusText() throws IOException { return this.conn.getResponseMessage(); } @Override public void close() { IoUtils.closeQuietly(this.responseStream); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/param/Header.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.param; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.utils.MapUtil; import com.alibaba.nacos.common.utils.StringUtils; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; /** * Http header. * * @author liaochuntao */ public class Header { public static final Header EMPTY = Header.newInstance(); private final Map header; private final Map> originalResponseHeader; private static final String DEFAULT_CHARSET = "UTF-8"; private Header() { header = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); originalResponseHeader = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); addParam(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_JSON); addParam(HttpHeaderConsts.ACCEPT_CHARSET, DEFAULT_CHARSET); } public static Header newInstance() { return new Header(); } /** * Add the key and value to the header. * * @param key the key * @param value the value * @return header */ public Header addParam(String key, String value) { if (StringUtils.isNotEmpty(key)) { header.put(key, value); } return this; } public Header setContentType(String contentType) { if (contentType == null) { contentType = MediaType.APPLICATION_JSON; } return addParam(HttpHeaderConsts.CONTENT_TYPE, contentType); } public String getValue(String key) { return header.get(key); } public Map getHeader() { return header; } public Iterator> iterator() { return header.entrySet().iterator(); } /** * Transfer to KV part list. The odd index is key and the even index is value. * * @return KV string list */ public List toList() { List list = new ArrayList<>(header.size() * 2); Iterator> iterator = iterator(); while (iterator.hasNext()) { Map.Entry entry = iterator.next(); list.add(entry.getKey()); list.add(entry.getValue()); } return list; } /** * Add all KV list to header. The odd index is key and the even index is value. * * @param list KV list * @return header */ public Header addAll(List list) { if ((list.size() & 1) != 0) { throw new IllegalArgumentException("list size must be a multiple of 2"); } for (int i = 0; i < list.size(); ) { String key = list.get(i++); if (StringUtils.isNotEmpty(key)) { header.put(key, list.get(i++)); } } return this; } /** * Add all parameters to header. * * @param params parameters */ public void addAll(Map params) { if (MapUtil.isNotEmpty(params)) { for (Map.Entry entry : params.entrySet()) { addParam(entry.getKey(), entry.getValue()); } } } /** * set original format response header. * *

    Currently only corresponds to the response header of JDK. * * @param key original response header key * @param values original response header values */ public void addOriginalResponseHeader(String key, List values) { if (StringUtils.isNotEmpty(key)) { this.originalResponseHeader.put(key, values); addParam(key, values.get(0)); } } /** * get original format response header. * *

    Currently only corresponds to the response header of JDK. * * @return Map original response header */ public Map> getOriginalResponseHeader() { return this.originalResponseHeader; } public String getCharset() { String acceptCharset = getValue(HttpHeaderConsts.ACCEPT_CHARSET); if (acceptCharset == null) { String contentType = getValue(HttpHeaderConsts.CONTENT_TYPE); acceptCharset = StringUtils.isNotBlank(contentType) ? analysisCharset(contentType) : Constants.ENCODE; } return acceptCharset; } private String analysisCharset(String contentType) { String[] values = contentType.split(";"); String charset = Constants.ENCODE; if (values.length == 1) { return charset; } for (String value : values) { if (value.startsWith("charset=")) { charset = value.substring("charset=".length()); } } return charset; } public void clear() { header.clear(); originalResponseHeader.clear(); } @Override public String toString() { return "Header{" + "headerToMap=" + header + '}'; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/param/MediaType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.param; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.utils.StringUtils; /** * Http Media type. * * @author liaochuntao */ public final class MediaType { public static final String APPLICATION_ATOM_XML = "application/atom+xml"; public static final String APPLICATION_FORM_URLENCODED = "application/x-www-form-urlencoded;charset=UTF-8"; public static final String APPLICATION_OCTET_STREAM = "application/octet-stream"; public static final String APPLICATION_SVG_XML = "application/svg+xml"; public static final String APPLICATION_XHTML_XML = "application/xhtml+xml"; public static final String APPLICATION_XML = "application/xml;charset=UTF-8"; public static final String APPLICATION_JSON = "application/json;charset=UTF-8"; public static final String MULTIPART_FORM_DATA = "multipart/form-data;charset=UTF-8"; public static final String TEXT_HTML = "text/html;charset=UTF-8"; public static final String TEXT_PLAIN = "text/plain;charset=UTF-8"; private MediaType(String type, String charset) { this.type = type; this.charset = charset; } /** * content type. */ private final String type; /** * content type charset. */ private final String charset; /** * Parse the given String contentType into a {@code MediaType} object. * * @param contentType mediaType * @return MediaType */ public static MediaType valueOf(String contentType) { if (StringUtils.isEmpty(contentType)) { throw new IllegalArgumentException("MediaType must not be empty"); } String[] values = contentType.split(";"); String charset = Constants.ENCODE; for (String value : values) { if (value.startsWith("charset=")) { charset = value.substring("charset=".length()); } } return new MediaType(values[0], charset); } /** * Use the given contentType and charset to assemble into a {@code MediaType} object. * * @param contentType contentType * @param charset charset * @return MediaType */ public static MediaType valueOf(String contentType, String charset) { if (StringUtils.isEmpty(contentType)) { throw new IllegalArgumentException("MediaType must not be empty"); } String[] values = contentType.split(";"); return new MediaType(values[0], StringUtils.isEmpty(charset) ? Constants.ENCODE : charset); } public String getType() { return type; } public String getCharset() { return charset; } @Override public String toString() { return type + ";charset=" + charset; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/http/param/Query.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.param; import com.alibaba.nacos.common.utils.MapUtil; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; /** * Http Query object. * * @author liaochuntao */ public class Query { private boolean isEmpty = true; public static final Query EMPTY = Query.newInstance(); private Map params; private static final String DEFAULT_ENC = "UTF-8"; public Query() { params = new LinkedHashMap<>(); } public static Query newInstance() { return new Query(); } /** * Add query parameter. * * @param key key * @param value value * @return this query */ public Query addParam(String key, Object value) { isEmpty = false; params.put(key, value); return this; } public Object getValue(String key) { return params.get(key); } /** * Add all parameters as query parameter. * * @param params parameters * @return this query */ public Query initParams(Map params) { if (MapUtil.isNotEmpty(params)) { for (Map.Entry entry : params.entrySet()) { addParam(entry.getKey(), entry.getValue()); } } return this; } /** * Print query as a http url param string. Like K=V&K=V. * * @return http url param string */ public String toQueryUrl() { StringBuilder urlBuilder = new StringBuilder(); Set> entrySet = params.entrySet(); int i = entrySet.size(); for (Map.Entry entry : entrySet) { try { if (null != entry.getValue()) { urlBuilder.append(entry.getKey()).append('=') .append(URLEncoder.encode(String.valueOf(entry.getValue()), DEFAULT_ENC)); if (i > 1) { urlBuilder.append('&'); } } i--; } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } } return urlBuilder.toString(); } public void clear() { params.clear(); isEmpty = true; } public boolean isEmpty() { return isEmpty; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/labels/LabelsCollector.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.labels; import java.util.Map; import java.util.Properties; /** * LabelsCollector. * * @author rong * @date 2024/2/4 */ public interface LabelsCollector { /** * getLabels. * * @param properties properties * @return Map labels. * @date 2024/2/4 * @description get all labels */ Map collectLabels(Properties properties); /** * getOrder. * * @return the order value * @date 2024/2/4 * @description get order value of labels in case of multiple labels */ int getOrder(); /** * get collector name. * * @return name of collector * @date 2024/2/4 * @description name of collector */ String getName(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/labels/LabelsCollectorManager.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.labels; import java.util.Map; import java.util.Properties; /** * LabelsCollectorManager. * * @author rong * @date 2024/2/4 */ public interface LabelsCollectorManager { /** * refresh all labels. * * @date 2024/3/7 * @param properties Properties. * @return all labels. */ Map getLabels(Properties properties); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/labels/impl/DefaultLabelsCollector.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.labels.impl; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.labels.LabelsCollector; import com.alibaba.nacos.common.utils.ConnLabelsUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static com.alibaba.nacos.api.common.Constants.DOT; import static com.alibaba.nacos.api.common.Constants.ENV_KEY; import static com.alibaba.nacos.api.common.Constants.JVM_KEY; /** * DefaultLabelsCollector. * * @author rong */ public class DefaultLabelsCollector implements LabelsCollector { private static final Logger LOGGER = LoggerFactory.getLogger("com.alibaba.nacos.common.labels"); private final String customName = "defaultNacosLabelsCollector"; private static final String UNDERSCORE = "_"; private static final String ESCAPE = "\\"; /** * init labels. * * @param properties Properties * @date 2024/2/4 * @description will init from properties, JVM OPTIONS, ENV by order of properties > JVM OPTIONS > ENV by * default. which will use the next level value when the current level value isn't setup (you also can set the level * by env). *

    eg: if the value of "nacos.app.conn.labels"(properties' key) is "k1=v1,k2=v2"(properties' value), the result * will be a Map with value{k1=v1,k2=v2}.

    */ @Override public Map collectLabels(Properties properties) { //properties LOGGER.info("default nacos collect properties raw labels: {}", properties.getProperty(Constants.APP_CONN_LABELS_KEY)); Map propertiesLabels = ConnLabelsUtils.parseRawLabels( properties.getProperty(Constants.APP_CONN_LABELS_KEY)); if (properties.containsKey(Constants.CONFIG_GRAY_LABEL)) { propertiesLabels.put(Constants.CONFIG_GRAY_LABEL, properties.getProperty(Constants.CONFIG_GRAY_LABEL)); } LOGGER.info("default nacos collect properties labels: {}", propertiesLabels); //jvm LOGGER.info("default nacos collect jvm raw labels: {}", System.getProperty(Constants.APP_CONN_LABELS_KEY)); Map jvmLabels = ConnLabelsUtils.parseRawLabels( System.getProperty(Constants.APP_CONN_LABELS_KEY)); if (System.getProperty(Constants.CONFIG_GRAY_LABEL) != null) { jvmLabels.put(Constants.CONFIG_GRAY_LABEL, System.getProperty((Constants.CONFIG_GRAY_LABEL))); } LOGGER.info("default nacos collect jvm labels: {}", jvmLabels); //env LOGGER.info("default nacos collect env raw labels: {}", System.getenv(Constants.APP_CONN_LABELS_KEY.replaceAll(ESCAPE + DOT, UNDERSCORE))); Map envLabels = ConnLabelsUtils.parseRawLabels( System.getenv(Constants.APP_CONN_LABELS_KEY.replaceAll(ESCAPE + DOT, UNDERSCORE))); if (System.getenv(Constants.CONFIG_GRAY_LABEL.replaceAll(ESCAPE + DOT, UNDERSCORE)) != null) { envLabels.put(Constants.CONFIG_GRAY_LABEL, System.getenv(Constants.CONFIG_GRAY_LABEL.replaceAll(ESCAPE + DOT, UNDERSCORE))); } LOGGER.info("default nacos collect env labels: {}", envLabels); Map finalLabels = new HashMap<>(4); String preferred = System.getenv(Constants.APP_CONN_LABELS_PREFERRED); boolean jvmPrefferred = false; boolean envPrefferred = false; if (StringUtils.isNotBlank(preferred)) { LOGGER.info("default nacos labels collector preferred {} labels.", preferred); if (JVM_KEY.equals(preferred)) { finalLabels.putAll(jvmLabels); jvmPrefferred = true; } else if (ENV_KEY.equals(preferred)) { finalLabels.putAll(envLabels); envPrefferred = true; } } finalLabels = ConnLabelsUtils.mergeMapByOrder(finalLabels, propertiesLabels); if (!jvmPrefferred) { finalLabels = ConnLabelsUtils.mergeMapByOrder(finalLabels, jvmLabels); } if (!envPrefferred) { finalLabels = ConnLabelsUtils.mergeMapByOrder(finalLabels, envLabels); } for (Map.Entry entry : finalLabels.entrySet()) { LOGGER.info("default nacos init labels: {}={}", entry.getKey(), entry.getValue()); } return finalLabels; } @Override public String getName() { return customName; } private static final int DEFAULT_INITIAL_ORDER = 100; @Override public int getOrder() { return DEFAULT_INITIAL_ORDER; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/labels/impl/DefaultLabelsCollectorManager.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.labels.impl; import com.alibaba.nacos.common.labels.LabelsCollector; import com.alibaba.nacos.common.labels.LabelsCollectorManager; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.ServiceLoader; /** * DefaultLabelsCollectorManager. * * @author rong */ public class DefaultLabelsCollectorManager implements LabelsCollectorManager { private static final Logger LOGGER = LoggerFactory.getLogger("com.alibaba.nacos.common.labels"); private ArrayList labelsCollectorsList = new ArrayList<>(); public DefaultLabelsCollectorManager() { labelsCollectorsList = loadLabelsCollectors(); } @Override public Map getLabels(Properties properties) { LOGGER.info("DefaultLabelsCollectorManager get labels....."); Map labels = getLabels(labelsCollectorsList, properties); LOGGER.info("DefaultLabelsCollectorManager get labels finished,labels :{}", labels); return labels; } Map getLabels(ArrayList labelsCollectorsList, Properties properties) { if (properties == null) { properties = new Properties(); } Map labels = new HashMap<>(8); for (LabelsCollector labelsCollector : labelsCollectorsList) { LOGGER.info("Process LabelsCollector with [name:{}]", labelsCollector.getName()); for (Map.Entry entry : labelsCollector.collectLabels(properties).entrySet()) { if (!checkValidLabel(entry.getKey(), entry.getValue())) { LOGGER.info(" ignore invalid label with [key:{}, value:{}] of collector [name:{}]", entry.getKey(), entry.getValue(), labelsCollector.getName()); continue; } if (innerAddLabel(labels, entry.getKey(), entry.getValue())) { LOGGER.info("pick label with [key:{}, value:{}] of collector [name:{}]", entry.getKey(), entry.getValue(), labelsCollector.getName()); } else { LOGGER.info(" ignore label with [key:{}, value:{}] of collector [name:{}]," + "already existed in LabelsCollectorManager with previous [value:{}],", entry.getKey(), entry.getValue(), labelsCollector.getName(), labels.get(entry.getKey())); } } } return labels; } private boolean checkValidLabel(String key, String value) { return isValid(key) && isValid(value); } private static boolean isValid(String param) { if (StringUtils.isBlank(param)) { return false; } int length = param.length(); if (length > maxLength) { return false; } for (int i = 0; i < length; i++) { char ch = param.charAt(i); if (!Character.isLetterOrDigit(ch) && !isValidChar(ch)) { return false; } } return true; } private static char[] validChars = new char[] {'_', '-', '.'}; private static int maxLength = 128; private static boolean isValidChar(char ch) { for (char c : validChars) { if (c == ch) { return true; } } return false; } private ArrayList loadLabelsCollectors() { ServiceLoader labelsCollectors = ServiceLoader.load(LabelsCollector.class); ArrayList labelsCollectorsList = new ArrayList<>(); for (LabelsCollector labelsCollector : labelsCollectors) { labelsCollectorsList.add(labelsCollector); } labelsCollectorsList.sort((o1, o2) -> o2.getOrder() - o1.getOrder()); return labelsCollectorsList; } private boolean innerAddLabel(Map labels, String key, String value) { return null == labels.putIfAbsent(key, value); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/lifecycle/Closeable.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.lifecycle; import com.alibaba.nacos.api.exception.NacosException; /** * An interface is used to define the resource's close and shutdown, such as IO Connection and ThreadPool. * * @author zongtanghu */ public interface Closeable { /** * Shutdown the Resources, such as Thread Pool. * * @throws NacosException exception. */ void shutdown() throws NacosException; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/logging/NacosLoggingAdapter.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.logging; /** * Nacos client logging adapter. * * @author xiweng.yy */ public interface NacosLoggingAdapter { /** * Whether current adapter is adapted for specified logger class. * * @param loggerClass {@link org.slf4j.Logger} implementation class * @return {@code true} if current adapter can adapt this {@link org.slf4j.Logger} implementation, otherwise {@code * false} */ boolean isAdaptedLogger(Class loggerClass); /** * Load Nacos logging configuration into log context. * * @param loggingProperties logging properties */ void loadConfiguration(NacosLoggingProperties loggingProperties); /** * Whether need reload configuration into log context. * * @return {@code true} when context don't contain nacos logging configuration. otherwise {@code false} */ boolean isNeedReloadConfiguration(); /** * Get current logging default config location. * * @return default config location */ String getDefaultConfigLocation(); /** * Whether current adapter enabled, design for users which want to log nacos client into app logs. * * @return {@code true} when enabled, otherwise {@code false}, default {@code true} */ default boolean isEnabled() { return true; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/logging/NacosLoggingAdapterBuilder.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.logging; /** * Builder of {@link NacosLoggingAdapter}. * *

    * Why not directly SPI {@link NacosLoggingAdapter}? *

    *

    * To avoid some {@link NacosLoggingAdapter} initialization error casue the SPI loader exit. *

    * * @author xiweng.yy */ public interface NacosLoggingAdapterBuilder { /** * Build {@link NacosLoggingAdapter} implementation. * * @return {@link NacosLoggingAdapter} */ NacosLoggingAdapter build(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/logging/NacosLoggingProperties.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.logging; import com.alibaba.nacos.common.utils.ConvertUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Properties; /** * Nacos Logging Properties, save some nacos logging properties. * * @author xiweng.yy */ public class NacosLoggingProperties { private static final String NACOS_LOGGING_CONFIG_PROPERTY = "nacos.logging.config"; private static final String NACOS_LOGGING_DEFAULT_CONFIG_ENABLED_PROPERTY = "nacos.logging.default.config.enabled"; private static final String NACOS_LOGGING_RELOAD_INTERVAL_PROPERTY = "nacos.logging.reload.interval.seconds"; private static final long DEFAULT_NACOS_LOGGING_RELOAD_INTERVAL = 10L; private final String defaultLocation; private final Properties properties; public NacosLoggingProperties(String defaultLocation, Properties properties) { this.defaultLocation = defaultLocation; this.properties = null == properties ? new Properties() : properties; } /** * Get the location of nacos logging configuration. * * @return location of nacos logging configuration */ public String getLocation() { String location = properties.getProperty(NACOS_LOGGING_CONFIG_PROPERTY); if (StringUtils.isBlank(location)) { if (isDefaultLocationEnabled()) { return defaultLocation; } return null; } return location; } /** * Is default location enabled. * *

    It is judged when user don't set the location of nacos logging configuration.

    * * @return {@code true} if default location enabled, otherwise {@code false}, default is {@code true} */ private boolean isDefaultLocationEnabled() { String property = properties.getProperty(NACOS_LOGGING_DEFAULT_CONFIG_ENABLED_PROPERTY); return property == null || ConvertUtils.toBoolean(property); } /** * Get reload internal. * * @return reload internal */ public long getReloadInternal() { String interval = properties.getProperty(NACOS_LOGGING_RELOAD_INTERVAL_PROPERTY); return ConvertUtils.toLong(interval, DEFAULT_NACOS_LOGGING_RELOAD_INTERVAL); } /** * get property value. * * @param source source * @param defaultValue defaultValue * @return value */ public String getValue(String source, String defaultValue) { return properties.getProperty(source, defaultValue); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/model/RequestHttpEntity.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.model; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import java.util.Map; /** * Represents an HTTP request , consisting of headers and body. * * @author mai.jh */ public class RequestHttpEntity { private final Header headers = Header.newInstance(); private final HttpClientConfig httpClientConfig; private final Query query; private final Object body; public RequestHttpEntity(Header header, Query query) { this(null, header, query); } public RequestHttpEntity(Header header, Object body) { this(null, header, body); } public RequestHttpEntity(Header header, Query query, Object body) { this(null, header, query, body); } public RequestHttpEntity(HttpClientConfig httpClientConfig, Header header, Query query) { this(httpClientConfig, header, query, null); } public RequestHttpEntity(HttpClientConfig httpClientConfig, Header header, Object body) { this(httpClientConfig, header, null, body); } public RequestHttpEntity(HttpClientConfig httpClientConfig, Header header, Query query, Object body) { handleHeader(header); this.httpClientConfig = httpClientConfig; this.query = query; this.body = body; } private void handleHeader(Header header) { if (header != null && !header.getHeader().isEmpty()) { Map headerMap = header.getHeader(); headers.addAll(headerMap); } } public Header getHeaders() { return headers; } public Query getQuery() { return query; } public Object getBody() { return body; } public HttpClientConfig getHttpClientConfig() { return httpClientConfig; } public boolean isEmptyBody() { return body == null; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/model/RestResult.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.model; import java.io.Serializable; /** * Rest result. * *

    TODO replaced or extend by {@link com.alibaba.nacos.api.model.v2.Result}. * * @author liaochuntao */ public class RestResult implements Serializable { private static final long serialVersionUID = 6095433538316185017L; private int code; private String message; private T data; public RestResult() { } public RestResult(int code, String message, T data) { this.code = code; this.setMessage(message); this.data = data; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; } public boolean ok() { return this.code == 0 || this.code == 200; } public boolean isNoRight() { return this.code == 403 || this.code == 401; } @Override public String toString() { return "RestResult{" + "code=" + code + ", message='" + message + '\'' + ", data=" + data + '}'; } public static ResResultBuilder builder() { return new ResResultBuilder<>(); } public static final class ResResultBuilder { private int code; private String errMsg; private T data; private ResResultBuilder() { } public ResResultBuilder withCode(int code) { this.code = code; return this; } public ResResultBuilder withMsg(String errMsg) { this.errMsg = errMsg; return this; } public ResResultBuilder withData(T data) { this.data = data; return this; } /** * Build result. * * @return result */ public RestResult build() { RestResult restResult = new RestResult<>(); restResult.setCode(code); restResult.setMessage(errMsg); restResult.setData(data); return restResult; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/model/RestResultUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.model; import com.alibaba.nacos.common.model.core.IResultCode; /** * Rest result utils. * * @author liaochuntao */ public class RestResultUtils { public static RestResult success() { return RestResult.builder().withCode(200).build(); } public static RestResult success(T data) { return RestResult.builder().withCode(200).withData(data).build(); } public static RestResult success(String msg, T data) { return RestResult.builder().withCode(200).withMsg(msg).withData(data).build(); } public static RestResult success(int code, T data) { return RestResult.builder().withCode(code).withData(data).build(); } public static RestResult failed() { return RestResult.builder().withCode(500).build(); } public static RestResult failed(String errMsg) { return RestResult.builder().withCode(500).withMsg(errMsg).build(); } public static RestResult failed(int code, T data) { return RestResult.builder().withCode(code).withData(data).build(); } public static RestResult failed(int code, T data, String errMsg) { return RestResult.builder().withCode(code).withData(data).withMsg(errMsg).build(); } public static RestResult failedWithMsg(int code, String errMsg) { return RestResult.builder().withCode(code).withMsg(errMsg).build(); } public static RestResult buildResult(IResultCode resultCode, T data) { return RestResult.builder().withCode(resultCode.getCode()).withMsg(resultCode.getCodeMsg()).withData(data).build(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/model/core/IResultCode.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.model.core; /** * IResultCode. * * @author klw * @ClassName: IResultCode * @Description: result code enum needs to be implemented this interface * @date 2019/6/28 14:44 */ @Deprecated public interface IResultCode { /** * Get the result code. * * @return code value. */ int getCode(); /** * Get the result code's message. * * @return code's message. */ String getCodeMsg(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/DefaultPublisher.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import static com.alibaba.nacos.common.notify.NotifyCenter.ringBufferSize; /** * The default event publisher implementation. * *

    Internally, use {@link ArrayBlockingQueue } as a message staging queue. * * @author liaochuntao * @author zongtanghu */ public class DefaultPublisher extends Thread implements EventPublisher { protected static final Logger LOGGER = LoggerFactory.getLogger(NotifyCenter.class); private volatile boolean initialized = false; private volatile boolean shutdown = false; private Class eventType; protected final ConcurrentHashSet subscribers = new ConcurrentHashSet<>(); private int queueMaxSize = -1; private BlockingQueue queue; protected volatile Long lastEventSequence = -1L; private static final AtomicReferenceFieldUpdater UPDATER = AtomicReferenceFieldUpdater .newUpdater(DefaultPublisher.class, Long.class, "lastEventSequence"); @Override public void init(Class type, int bufferSize) { setDaemon(true); setName("nacos.publisher-" + type.getName()); this.eventType = type; this.queueMaxSize = bufferSize; if (this.queueMaxSize == -1) { this.queueMaxSize = ringBufferSize; } this.queue = new ArrayBlockingQueue<>(this.queueMaxSize); start(); } public ConcurrentHashSet getSubscribers() { return subscribers; } @Override public synchronized void start() { if (!initialized) { // start just called once super.start(); initialized = true; } } @Override public long currentEventSize() { return queue.size(); } @Override public void run() { openEventHandler(); } void openEventHandler() { try { // This variable is defined to resolve the problem which message overstock in the queue. int waitTimes = 60; // To ensure that messages are not lost, enable EventHandler when // waiting for the first Subscriber to register while (!shutdown && !hasSubscriber() && waitTimes > 0) { ThreadUtils.sleep(1000L); waitTimes--; } while (!shutdown) { final Event event = queue.take(); receiveEvent(event); UPDATER.compareAndSet(this, lastEventSequence, Math.max(lastEventSequence, event.sequence())); } } catch (InterruptedException e) { // [issue #13752] ignore stack log } catch (Throwable ex) { LOGGER.error("Event listener exception : ", ex); } } private boolean hasSubscriber() { return CollectionUtils.isNotEmpty(subscribers); } @Override public void addSubscriber(Subscriber subscriber) { subscribers.add(subscriber); } @Override public void removeSubscriber(Subscriber subscriber) { subscribers.remove(subscriber); } @Override public boolean publish(Event event) { checkIsStart(); boolean success = this.queue.offer(event); if (!success) { LOGGER.warn("Unable to plug in due to interruption, synchronize sending time, event : {}", event); receiveEvent(event); return true; } return true; } void checkIsStart() { if (!initialized) { throw new IllegalStateException("Publisher does not start"); } } @Override public void shutdown() { this.shutdown = true; this.queue.clear(); // Interrupt the thread to stop processing events: queue.take(). this.interrupt(); } public boolean isInitialized() { return initialized; } /** * Receive and notifySubscriber to process the event. * * @param event {@link Event}. */ void receiveEvent(Event event) { final long currentEventSequence = event.sequence(); if (!hasSubscriber()) { LOGGER.warn("[NotifyCenter] the {} is lost, because there is no subscriber.", event); return; } // Notification single event listener for (Subscriber subscriber : subscribers) { if (!subscriber.scopeMatches(event)) { continue; } // Whether to ignore expiration events if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) { LOGGER.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire", event.getClass()); continue; } // Because unifying smartSubscriber and subscriber, so here need to think of compatibility. // Remove original judge part of codes. notifySubscriber(subscriber, event); } } @Override public void notifySubscriber(final Subscriber subscriber, final Event event) { LOGGER.debug("[NotifyCenter] the {} will received by {}", event, subscriber); final Runnable job = () -> subscriber.onEvent(event); final Executor executor = subscriber.executor(); if (executor != null) { executor.execute(job); } else { try { job.run(); } catch (Throwable e) { LOGGER.error("Event callback exception: ", e); } } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/DefaultSharePublisher.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * The default share event publisher implementation for slow event. * * @author zongtanghu */ public class DefaultSharePublisher extends DefaultPublisher implements ShardedEventPublisher { private final Map, Set> subMappings = new ConcurrentHashMap<>(); private final Lock lock = new ReentrantLock(); @Override public void addSubscriber(Subscriber subscriber, Class subscribeType) { // Actually, do a classification based on the slowEvent type. Class subSlowEventType = (Class) subscribeType; // For stop waiting subscriber, see {@link DefaultPublisher#openEventHandler}. subscribers.add(subscriber); lock.lock(); try { Set sets = subMappings.get(subSlowEventType); if (sets == null) { Set newSet = new ConcurrentHashSet<>(); newSet.add(subscriber); subMappings.put(subSlowEventType, newSet); return; } sets.add(subscriber); } finally { lock.unlock(); } } @Override public void removeSubscriber(Subscriber subscriber, Class subscribeType) { // Actually, do a classification based on the slowEvent type. Class subSlowEventType = (Class) subscribeType; // For removing to parent class attributes synchronization. subscribers.remove(subscriber); lock.lock(); try { Set sets = subMappings.get(subSlowEventType); if (sets != null) { sets.remove(subscriber); } } finally { lock.unlock(); } } @Override public void receiveEvent(Event event) { final long currentEventSequence = event.sequence(); // get subscriber set based on the slow EventType. final Class slowEventType = (Class) event.getClass(); // Get for Map, the algorithm is O(1). Set subscribers = subMappings.get(slowEventType); if (null == subscribers) { LOGGER.debug("[NotifyCenter] No subscribers for slow event {}", slowEventType.getName()); return; } // Notification single event subscriber for (Subscriber subscriber : subscribers) { // Whether to ignore expiration events if (subscriber.ignoreExpireEvent() && lastEventSequence > currentEventSequence) { LOGGER.debug("[NotifyCenter] the {} is unacceptable to this subscriber, because had expire", event.getClass()); continue; } // Notify single subscriber for slow event. notifySubscriber(subscriber, event); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/Event.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import java.io.Serializable; import java.util.concurrent.atomic.AtomicLong; /** * An abstract class for event. * * @author liaochuntao * @author zongtanghu */ public abstract class Event implements Serializable { private static final long serialVersionUID = -3731383194964997493L; private static final AtomicLong SEQUENCE = new AtomicLong(0); private final long sequence = SEQUENCE.getAndIncrement(); /** * Event sequence number, which can be used to handle the sequence of events. * * @return sequence num, It's best to make sure it's monotone. */ public long sequence() { return sequence; } /** * Event scope. * * @return event scope, return null if for all scope */ public String scope() { return null; } /** * Whether is plugin event. If so, the event can be dropped when no publish and subscriber without any hint. Default * false * * @return {@code true} if is plugin event, otherwise {@code false} */ public boolean isPluginEvent() { return false; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/EventPublisher.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.notify.listener.Subscriber; /** * Event publisher. * * @author liaochuntao * @author zongtanghu */ public interface EventPublisher extends Closeable { /** * Initializes the event publisher. * * @param type {@link Event >} * @param bufferSize Message staging queue size */ void init(Class type, int bufferSize); /** * The number of currently staged events. * * @return event size */ long currentEventSize(); /** * Add listener. * * @param subscriber {@link Subscriber} */ void addSubscriber(Subscriber subscriber); /** * Remove listener. * * @param subscriber {@link Subscriber} */ void removeSubscriber(Subscriber subscriber); /** * publish event. * * @param event {@link Event} * @return publish event is success */ boolean publish(Event event); /** * Notify listener. * * @param subscriber {@link Subscriber} * @param event {@link Event} */ void notifySubscriber(Subscriber subscriber, Event event); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/EventPublisherFactory.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import java.util.function.BiFunction; /** * Event publisher factory. * * @author xiweng.yy */ public interface EventPublisherFactory extends BiFunction, Integer, EventPublisher> { /** * Build an new {@link EventPublisher}. * * @param eventType eventType for {@link EventPublisher} * @param maxQueueSize max queue size for {@link EventPublisher} * @return new {@link EventPublisher} */ @Override EventPublisher apply(Class eventType, Integer maxQueueSize); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/NotifyCenter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.JustForTest; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.ClassUtils; import com.alibaba.nacos.common.utils.MapUtil; import com.alibaba.nacos.common.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; import static com.alibaba.nacos.api.exception.NacosException.SERVER_ERROR; /** * Unified Event Notify Center. * * @author liaochuntao * @author zongtanghu */ public class NotifyCenter { private static final Logger LOGGER = LoggerFactory.getLogger(NotifyCenter.class); public static int ringBufferSize; public static int shareBufferSize; private static final AtomicBoolean CLOSED = new AtomicBoolean(false); private static final EventPublisherFactory DEFAULT_PUBLISHER_FACTORY; @SuppressWarnings("checkstyle:StaticVariableName") private static NotifyCenter INSTANCE = new NotifyCenter(); private DefaultSharePublisher sharePublisher; private static Class clazz; /** * Publisher management container. */ private final Map publisherMap = new ConcurrentHashMap<>(16); static { // Internal ArrayBlockingQueue buffer size. For applications with high write throughput, // this value needs to be increased appropriately. default value is 16384 String ringBufferSizeProperty = "nacos.core.notify.ring-buffer-size"; ringBufferSize = Integer.getInteger(ringBufferSizeProperty, 16384); // The size of the public publisher's message staging queue buffer String shareBufferSizeProperty = "nacos.core.notify.share-buffer-size"; shareBufferSize = Integer.getInteger(shareBufferSizeProperty, 1024); final Collection publishers = NacosServiceLoader.load(EventPublisher.class); Iterator iterator = publishers.iterator(); if (iterator.hasNext()) { clazz = iterator.next().getClass(); } else { clazz = DefaultPublisher.class; } DEFAULT_PUBLISHER_FACTORY = (cls, buffer) -> { try { EventPublisher publisher = clazz.newInstance(); publisher.init(cls, buffer); return publisher; } catch (Throwable ex) { LOGGER.error("Service class newInstance has error : ", ex); throw new NacosRuntimeException(SERVER_ERROR, ex); } }; try { // Create and init DefaultSharePublisher instance. INSTANCE.sharePublisher = new DefaultSharePublisher(); INSTANCE.sharePublisher.init(SlowEvent.class, shareBufferSize); } catch (Throwable ex) { LOGGER.error("Service class newInstance has error : ", ex); } ThreadUtils.addShutdownHook(NotifyCenter::shutdown); } @JustForTest public static Map getPublisherMap() { return INSTANCE.publisherMap; } public static EventPublisher getPublisher(Class topic) { if (ClassUtils.isAssignableFrom(SlowEvent.class, topic)) { return INSTANCE.sharePublisher; } return INSTANCE.publisherMap.get(topic.getCanonicalName()); } public static EventPublisher getSharePublisher() { return INSTANCE.sharePublisher; } /** * Shutdown the several publisher instance which notify center has. */ public static void shutdown() { if (!CLOSED.compareAndSet(false, true)) { return; } LOGGER.info("[NotifyCenter] Start destroying Publisher"); for (Map.Entry entry : INSTANCE.publisherMap.entrySet()) { try { EventPublisher eventPublisher = entry.getValue(); eventPublisher.shutdown(); } catch (Throwable e) { LOGGER.error("[EventPublisher] shutdown has error : ", e); } } try { INSTANCE.sharePublisher.shutdown(); } catch (Throwable e) { LOGGER.error("[SharePublisher] shutdown has error : ", e); } LOGGER.info("[NotifyCenter] Completed destruction of Publisher"); } /** * Register a Subscriber. If the Publisher concerned by the Subscriber does not exist, then PublisherMap will * preempt a placeholder Publisher with default EventPublisherFactory first. * * @param consumer subscriber */ public static void registerSubscriber(final Subscriber consumer) { registerSubscriber(consumer, DEFAULT_PUBLISHER_FACTORY); } /** * Register a Subscriber. If the Publisher concerned by the Subscriber does not exist, then PublisherMap will * preempt a placeholder Publisher with specified EventPublisherFactory first. * * @param consumer subscriber * @param factory publisher factory. */ public static void registerSubscriber(final Subscriber consumer, final EventPublisherFactory factory) { // If you want to listen to multiple events, you do it separately, // based on subclass's subscribeTypes method return list, it can register to publisher. if (consumer instanceof SmartSubscriber) { for (Class subscribeType : ((SmartSubscriber) consumer).subscribeTypes()) { // For case, producer: defaultSharePublisher -> consumer: smartSubscriber. if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) { INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType); } else { // For case, producer: defaultPublisher -> consumer: subscriber. addSubscriber(consumer, subscribeType, factory); } } return; } final Class subscribeType = consumer.subscribeType(); if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) { INSTANCE.sharePublisher.addSubscriber(consumer, subscribeType); return; } addSubscriber(consumer, subscribeType, factory); } /** * Add a subscriber to publisher. * * @param consumer subscriber instance. * @param subscribeType subscribeType. * @param factory publisher factory. */ private static void addSubscriber(final Subscriber consumer, Class subscribeType, EventPublisherFactory factory) { final String topic = ClassUtils.getCanonicalName(subscribeType); synchronized (NotifyCenter.class) { // MapUtils.computeIfAbsent is a unsafe method. MapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, subscribeType, ringBufferSize); } EventPublisher publisher = INSTANCE.publisherMap.get(topic); if (publisher instanceof ShardedEventPublisher) { ((ShardedEventPublisher) publisher).addSubscriber(consumer, subscribeType); } else { publisher.addSubscriber(consumer); } } /** * Deregister subscriber. * * @param consumer subscriber instance. */ public static void deregisterSubscriber(final Subscriber consumer) { if (consumer instanceof SmartSubscriber) { for (Class subscribeType : ((SmartSubscriber) consumer).subscribeTypes()) { if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) { INSTANCE.sharePublisher.removeSubscriber(consumer, subscribeType); } else { removeSubscriber(consumer, subscribeType); } } return; } final Class subscribeType = consumer.subscribeType(); if (ClassUtils.isAssignableFrom(SlowEvent.class, subscribeType)) { INSTANCE.sharePublisher.removeSubscriber(consumer, subscribeType); return; } if (removeSubscriber(consumer, subscribeType)) { return; } throw new NoSuchElementException("The subscriber has no event publisher"); } /** * Remove subscriber. * * @param consumer subscriber instance. * @param subscribeType subscribeType. * @return whether remove subscriber successfully or not. */ private static boolean removeSubscriber(final Subscriber consumer, Class subscribeType) { final String topic = ClassUtils.getCanonicalName(subscribeType); EventPublisher eventPublisher = INSTANCE.publisherMap.get(topic); if (null == eventPublisher) { return false; } if (eventPublisher instanceof ShardedEventPublisher) { ((ShardedEventPublisher) eventPublisher).removeSubscriber(consumer, subscribeType); } else { eventPublisher.removeSubscriber(consumer); } return true; } /** * Request publisher publish event Publishers load lazily, calling publisher. Start () only when the event is * actually published. * * @param event class Instances of the event. */ public static boolean publishEvent(final Event event) { try { return publishEvent(event.getClass(), event); } catch (Throwable ex) { LOGGER.error("There was an exception to the message publishing : ", ex); return false; } } /** * Request publisher publish event Publishers load lazily, calling publisher. * * @param eventType class Instances type of the event type. * @param event event instance. */ private static boolean publishEvent(final Class eventType, final Event event) { if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) { return INSTANCE.sharePublisher.publish(event); } final String topic = ClassUtils.getCanonicalName(eventType); EventPublisher publisher = INSTANCE.publisherMap.get(topic); if (publisher != null) { return publisher.publish(event); } if (event.isPluginEvent()) { return true; } LOGGER.warn("There are no [{}] publishers for this event, please register", topic); return false; } /** * Register to share-publisher. * * @param eventType class Instances type of the event type. * @return share publisher instance. */ public static EventPublisher registerToSharePublisher(final Class eventType) { return INSTANCE.sharePublisher; } /** * Register publisher with default factory. * * @param eventType class Instances type of the event type. * @param queueMaxSize the publisher's queue max size. */ public static EventPublisher registerToPublisher(final Class eventType, final int queueMaxSize) { return registerToPublisher(eventType, DEFAULT_PUBLISHER_FACTORY, queueMaxSize); } /** * Register publisher with specified factory. * * @param eventType class Instances type of the event type. * @param factory publisher factory. * @param queueMaxSize the publisher's queue max size. */ public static EventPublisher registerToPublisher(final Class eventType, final EventPublisherFactory factory, final int queueMaxSize) { if (ClassUtils.isAssignableFrom(SlowEvent.class, eventType)) { return INSTANCE.sharePublisher; } final String topic = ClassUtils.getCanonicalName(eventType); synchronized (NotifyCenter.class) { // MapUtils.computeIfAbsent is a unsafe method. MapUtil.computeIfAbsent(INSTANCE.publisherMap, topic, factory, eventType, queueMaxSize); } return INSTANCE.publisherMap.get(topic); } /** * Register publisher. * * @param eventType class Instances type of the event type. * @param publisher the specified event publisher */ public static void registerToPublisher(final Class eventType, final EventPublisher publisher) { if (null == publisher) { return; } final String topic = ClassUtils.getCanonicalName(eventType); synchronized (NotifyCenter.class) { INSTANCE.publisherMap.putIfAbsent(topic, publisher); } } /** * Deregister publisher. * * @param eventType class Instances type of the event type. */ public static void deregisterPublisher(final Class eventType) { final String topic = ClassUtils.getCanonicalName(eventType); EventPublisher publisher = INSTANCE.publisherMap.remove(topic); try { publisher.shutdown(); } catch (Throwable ex) { LOGGER.error("There was an exception when publisher shutdown : ", ex); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/ShardedEventPublisher.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.common.notify.listener.Subscriber; /** * Sharded event publisher. * *

    To support one publisher for different events. * * @author xiweng.yy */ public interface ShardedEventPublisher extends EventPublisher { /** * Add listener for default share publisher. * * @param subscriber {@link Subscriber} * @param subscribeType subscribe event type, such as slow event or general event. */ void addSubscriber(Subscriber subscriber, Class subscribeType); /** * Remove listener for default share publisher. * * @param subscriber {@link Subscriber} * @param subscribeType subscribe event type, such as slow event or general event. */ void removeSubscriber(Subscriber subscriber, Class subscribeType); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/SlowEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; /** * This event share one event-queue. * * @author liaochuntao * @author zongtanghu */ public abstract class SlowEvent extends Event { @Override public long sequence() { return 0; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/listener/SmartSubscriber.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify.listener; import com.alibaba.nacos.common.notify.Event; import java.util.List; /** * Subscribers to multiple events can be listened to. * * @author liaochuntao * @author zongtanghu */ public abstract class SmartSubscriber extends Subscriber { /** * Returns which event type are smart subscriber interested in. * * @return The interested event types. */ public abstract List> subscribeTypes(); @Override public final Class subscribeType() { return null; } @Override public final boolean ignoreExpireEvent() { return false; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/notify/listener/Subscriber.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify.listener; import com.alibaba.nacos.common.notify.Event; import java.util.concurrent.Executor; /** * An abstract subscriber class for subscriber interface. * * @author liaochuntao * @author zongtanghu */ public abstract class Subscriber { /** * Event callback. * * @param event {@link Event} */ public abstract void onEvent(T event); /** * Type of this subscriber's subscription. * * @return Class which extends {@link Event} */ public abstract Class subscribeType(); /** * It is up to the listener to determine whether the callback is asynchronous or synchronous. * * @return {@link Executor} */ public Executor executor() { return null; } /** * Whether to ignore expired events. * * @return default value is {@link Boolean#FALSE} */ public boolean ignoreExpireEvent() { return false; } /** * Whether the event's scope matches current subscriber. Default implementation is all scopes matched. * If you override this method, it better to override related {@link com.alibaba.nacos.common.notify.Event#scope()}. * * @param event {@link Event} * @return Whether the event's scope matches current subscriber */ public boolean scopeMatches(T event) { return true; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/package-info.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common; ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/DefaultPackageScan.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan; import com.alibaba.nacos.common.packagescan.classreading.ClassReader; import com.alibaba.nacos.common.packagescan.resource.PathMatchingResourcePatternResolver; import com.alibaba.nacos.common.packagescan.resource.Resource; import com.alibaba.nacos.common.packagescan.resource.ResourcePatternResolver; import com.alibaba.nacos.common.packagescan.util.NestedIoException; import com.alibaba.nacos.common.utils.ClassUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.util.HashSet; import java.util.Set; /** * Scan all appropriate Class object through the package name. * * @author hujun */ public class DefaultPackageScan implements PackageScan { protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultPackageScan.class); private final PathMatchingResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver(); public DefaultPackageScan() { } /** * Scan all appropriate Class object through the package name and Class object. * * @param pkg package name,for example, com.alibaba.nacos.common * @param requestClass super class * @param Class type * @return a set contains Class */ @Override public Set> getSubTypesOf(String pkg, Class requestClass) { Set> set = new HashSet<>(16); String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(pkg) + '/' + "**/*.class"; try { Resource[] resources = resourcePatternResolver.getResources(packageSearchPath); for (Resource resource : resources) { Class scanClass = getClassByResource(resource); if (requestClass.isAssignableFrom(scanClass)) { set.add((Class) scanClass); } } } catch (IOException | ClassNotFoundException e) { LOGGER.error("scan path: {} failed", packageSearchPath, e); } return set; } /** * Scan all appropriate Class object through the package name and annotation. * * @param pkg package name,for example, com.alibaba.nacos.common * @param annotation annotation * @param Class type * @return a set contains Class object */ @Override public Set> getTypesAnnotatedWith(String pkg, Class annotation) { Set> set = new HashSet<>(16); String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(pkg) + '/' + "**/*.class"; try { Resource[] resources = resourcePatternResolver.getResources(packageSearchPath); for (Resource resource : resources) { Class scanClass = getClassByResource(resource); if (scanClass.isAnnotationPresent(annotation)) { set.add((Class) scanClass); } } } catch (IOException | ClassNotFoundException e) { LOGGER.error("scan path: {} failed", packageSearchPath, e); } return set; } private Class getClassByResource(Resource resource) throws IOException, ClassNotFoundException { String className = getClassReader(resource).getClassName(); return Class.forName(ClassUtils.resourcePathToConvertClassName(className)); } private static ClassReader getClassReader(Resource resource) throws IOException { try (InputStream is = resource.getInputStream()) { try { return new ClassReader(is); } catch (IllegalArgumentException ex) { throw new NestedIoException("ASM ClassReader failed to parse class file - " + "probably due to a new Java class file version that isn't supported yet: " + resource, ex); } } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/PackageScan.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan; import java.lang.annotation.Annotation; import java.util.Set; /** * Scan all appropriate Class object through the package name. * * @author hujun */ public interface PackageScan { /** * Scan all appropriate Class object through the package name and Class object. * * @param pkg package name,for example, com.alibaba.nacos.common * @param requestClass super class * @param Class type * @return a set contains Class */ Set> getSubTypesOf(String pkg, Class requestClass); /** * Scan all appropriate Class object through the package name and annotation. * * @param pkg package name,for example, com.alibaba.nacos.common * @param annotation annotation * @param Class type * @return a set contains Class object */ Set> getTypesAnnotatedWith(String pkg, Class annotation); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/classreading/ClassReader.java ================================================ // ASM: a very small and fast Java bytecode manipulation framework // Copyright (c) 2000-2011 INRIA, France Telecom // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. 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. // 3. Neither the name of the copyright holders 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 com.alibaba.nacos.common.packagescan.classreading; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; /** * Copy from ASM, with less modifications * A parser to make a visit a ClassFile structure, as defined in the Java * Virtual Machine Specification (JVMS). This class parses the ClassFile content and calls the * appropriate visit methods of a given ClassVisitor for each field, method and bytecode * instruction encountered. * * @author Eric Bruneton * @author Eugene Kuleshov * @see JVMS 4 */ public class ClassReader { /** * A flag to skip the Code attributes. If this flag is set the Code attributes are neither parsed * nor visited. */ public static final int SKIP_CODE = 1; public static final int V19 = 0 << 16 | 63; /** * A flag to skip the SourceFile. */ public static final int SKIP_DEBUG = 2; /** * A flag to skip the StackMap and StackMapTable attributes. If this flag is set these attributes * are neither parsed nor visited is not called). This flag * is useful when the option is used: it avoids visiting frames * that will be ignored and recomputed from scratch. */ public static final int SKIP_FRAMES = 4; /** * A flag to expand the stack map frames. By default stack map frames are visited in their * original format (i.e. "expanded" for classes whose version is less than V1_6, and "compressed" * for the other classes). If this flag is set, stack map frames are always visited in expanded * format (this option adds a decompression/compression step in ClassReader and ClassWriter which * degrades performance quite a lot). */ public static final int EXPAND_FRAMES = 8; /** * A flag to expand the ASM specific instructions into an equivalent sequence of standard bytecode * instructions. When resolving a forward jump it may happen that the signed 2 bytes offset * reserved for it is not sufficient to store the bytecode offset. * This internal flag is used to re-read classes containing such instructions, * in order to replace them with standard instructions. In addition, when this * flag is used, goto_w and jsr_w are not converted into goto and jsr, to make sure that * infinite loops where a goto_w is replaced with a goto in ClassReader and converted back to a * goto_w in ClassWriter cannot occur. */ static final int EXPAND_ASM_INSNS = 256; /** * The maximum size of array to allocate. */ private static final int MAX_BUFFER_SIZE = 1024 * 1024; /** * The size of the temporary byte array used to read class input streams chunk by chunk. */ private static final int INPUT_STREAM_DATA_CHUNK_SIZE = 4096; /** * A byte array containing the JVMS ClassFile structure to be parsed. * * @deprecated Use {@link #readByte(int)} and the other read methods instead. This field will * eventually be deleted. */ @Deprecated // DontCheck(MemberName): can't be renamed (for backward binary compatibility). public final byte[] b; /** * The offset in bytes of the ClassFile's access_flags field. */ public final int header; /** * A byte array containing the JVMS ClassFile structure to be parsed. The content of this array * must not be modified. This field is intended for Attribute sub classes, and is normally * not needed by class visitors. * *

    NOTE: the ClassFile structure can start at any offset within this array, i.e. it does not * necessarily start at offset 0. Use {@link #getItem} and {@link #header} to get correct * ClassFile element offsets within this byte array. */ final byte[] classFileBuffer; /** * The offset in bytes, in {@link #classFileBuffer}, of each cp_info entry of the ClassFile's * constant_pool array, plus one. In other words, the offset of constant pool entry i is * given by cpInfoOffsets[i] - 1, i.e. its cp_info's tag field is given by b[cpInfoOffsets[i] - * 1]. */ private final int[] cpInfoOffsets; /** * The String objects corresponding to the CONSTANT_Utf8 constant pool items. This cache avoids * multiple parsing of a given CONSTANT_Utf8 constant pool item. */ private final String[] constantUtf8Values; /** * A conservative estimate of the maximum length of the strings contained in the constant pool of * the class. */ private final int maxStringLength; // ----------------------------------------------------------------------------------------------- // Constructors // ----------------------------------------------------------------------------------------------- /** * Constructs a new {@link ClassReader} object. * * @param classFile the JVMS ClassFile structure to be read. */ public ClassReader(final byte[] classFile) { this(classFile, 0, classFile.length); } /** * Constructs a new {@link ClassReader} object. * * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read. * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read. * @param classFileLength the length in bytes of the ClassFile to be read. */ public ClassReader( final byte[] classFileBuffer, final int classFileOffset, final int classFileLength) { this(classFileBuffer, classFileOffset, /* checkClassVersion = */ true); } /** * Constructs a new {@link ClassReader} object. This internal constructor must not be exposed * as a public API. * * @param classFileBuffer a byte array containing the JVMS ClassFile structure to be read. * @param classFileOffset the offset in byteBuffer of the first byte of the ClassFile to be read. * @param checkClassVersion whether to check the class version or not. */ ClassReader( final byte[] classFileBuffer, final int classFileOffset, final boolean checkClassVersion) { this.classFileBuffer = classFileBuffer; this.b = classFileBuffer; // Check the class' major_version. This field is after the magic and minor_version fields, which // use 4 and 2 bytes respectively. if (checkClassVersion && readShort(classFileOffset + 6) > V19) { throw new IllegalArgumentException( "Unsupported class file major version " + readShort(classFileOffset + 6)); } // Create the constant pool arrays. The constant_pool_count field is after the magic, // minor_version and major_version fields, which use 4, 2 and 2 bytes respectively. int constantPoolCount = readUnsignedShort(classFileOffset + 8); cpInfoOffsets = new int[constantPoolCount]; constantUtf8Values = new String[constantPoolCount]; // Compute the offset of each constant pool entry, as well as a conservative estimate of the // maximum length of the constant pool strings. The first constant pool entry is after the // magic, minor_version, major_version and constant_pool_count fields, which use 4, 2, 2 and 2 // bytes respectively. int currentCpInfoIndex = 1; int currentCpInfoOffset = classFileOffset + 10; int currentMaxStringLength = 0; boolean hasBootstrapMethods = false; boolean hasConstantDynamic = false; // The offset of the other entries depend on the total size of all the previous entries. while (currentCpInfoIndex < constantPoolCount) { cpInfoOffsets[currentCpInfoIndex++] = currentCpInfoOffset + 1; int cpInfoSize; switch (classFileBuffer[currentCpInfoOffset]) { case Symbol.CONSTANT_FIELDREF_TAG: case Symbol.CONSTANT_METHODREF_TAG: case Symbol.CONSTANT_INTERFACE_METHODREF_TAG: case Symbol.CONSTANT_INTEGER_TAG: case Symbol.CONSTANT_FLOAT_TAG: case Symbol.CONSTANT_NAME_AND_TYPE_TAG: cpInfoSize = 5; break; case Symbol.CONSTANT_DYNAMIC_TAG: cpInfoSize = 5; hasBootstrapMethods = true; hasConstantDynamic = true; break; case Symbol.CONSTANT_INVOKE_DYNAMIC_TAG: cpInfoSize = 5; hasBootstrapMethods = true; break; case Symbol.CONSTANT_LONG_TAG: case Symbol.CONSTANT_DOUBLE_TAG: cpInfoSize = 9; currentCpInfoIndex++; break; case Symbol.CONSTANT_UTF8_TAG: cpInfoSize = 3 + readUnsignedShort(currentCpInfoOffset + 1); if (cpInfoSize > currentMaxStringLength) { // The size in bytes of this CONSTANT_Utf8 structure provides a conservative estimate // of the length in characters of the corresponding string, and is much cheaper to // compute than this exact length. currentMaxStringLength = cpInfoSize; } break; case Symbol.CONSTANT_METHOD_HANDLE_TAG: cpInfoSize = 4; break; case Symbol.CONSTANT_CLASS_TAG: case Symbol.CONSTANT_STRING_TAG: case Symbol.CONSTANT_METHOD_TYPE_TAG: case Symbol.CONSTANT_PACKAGE_TAG: case Symbol.CONSTANT_MODULE_TAG: cpInfoSize = 3; break; default: throw new IllegalArgumentException(); } currentCpInfoOffset += cpInfoSize; } maxStringLength = currentMaxStringLength; // The Classfile's access_flags field is just after the last constant pool entry. header = currentCpInfoOffset; } /** * Constructs a new {@link ClassReader} object. * * @param inputStream an input stream of the JVMS ClassFile structure to be read. This input * stream must contain nothing more than the ClassFile structure itself. It is read from its * current position to its end. * @throws IOException if a problem occurs during reading. */ public ClassReader(final InputStream inputStream) throws IOException { this(readStream(inputStream, false)); } /** * Constructs a new {@link ClassReader} object. * * @param className the fully qualified name of the class to be read. The ClassFile structure is * retrieved with the current class loader's {@link ClassLoader#getSystemResourceAsStream}. * @throws IOException if an exception occurs during reading. */ public ClassReader(final String className) throws IOException { this( readStream( ClassLoader.getSystemResourceAsStream(className.replace('.', '/') + ".class"), true)); } /** * Reads the given input stream and returns its content as a byte array. * * @param inputStream an input stream. * @param close true to close the input stream after reading. * @return the content of the given input stream. * @throws IOException if a problem occurs during reading. */ private static byte[] readStream(final InputStream inputStream, final boolean close) throws IOException { if (inputStream == null) { throw new IOException("Class not found"); } int bufferSize = calculateBufferSize(inputStream); try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { byte[] data = new byte[bufferSize]; int bytesRead; int readCount = 0; while ((bytesRead = inputStream.read(data, 0, bufferSize)) != -1) { outputStream.write(data, 0, bytesRead); readCount++; } outputStream.flush(); if (readCount == 1) { // SPRING PATCH: some misbehaving InputStreams return -1 but still write to buffer (gh-27429) // return data; // END OF PATCH } return outputStream.toByteArray(); } finally { if (close) { inputStream.close(); } } } private static int calculateBufferSize(final InputStream inputStream) throws IOException { int expectedLength = inputStream.available(); /* * Some implementations can return 0 while holding available data * (e.g. new FileInputStream("/proc/a_file")) * Also in some pathological cases a very small number might be returned, * and in this case we use default size */ if (expectedLength < 256) { return INPUT_STREAM_DATA_CHUNK_SIZE; } return Math.min(expectedLength, MAX_BUFFER_SIZE); } // ----------------------------------------------------------------------------------------------- // Accessors // ----------------------------------------------------------------------------------------------- /** * Returns the class's access flags . This value may not reflect Deprecated * and Synthetic flags when bytecode is before 1.5 and those flags are represented by attributes. * * @return the class access flags. */ public int getAccess() { return readUnsignedShort(header); } /** * the internal class name. */ public String getClassName() { // this_class is just after the access_flags field (using 2 bytes). return readClass(header + 2, new char[maxStringLength]); } public String getSuperName() { // super_class is after the access_flags and this_class fields (2 bytes each). return readClass(header + 4, new char[maxStringLength]); } /** * the internal names of the directly implemented interfaces. Inherited implemented * interfaces are not returned. */ public String[] getInterfaces() { // interfaces_count is after the access_flags, this_class and super_class fields (2 bytes each). int currentOffset = header + 6; int interfacesCount = readUnsignedShort(currentOffset); String[] interfaces = new String[interfacesCount]; if (interfacesCount > 0) { char[] charBuffer = new char[maxStringLength]; for (int i = 0; i < interfacesCount; ++i) { currentOffset += 2; interfaces[i] = readClass(currentOffset, charBuffer); } } return interfaces; } // ---------------------------------------------------------------------------------------------- // Methods to parse attributes // ---------------------------------------------------------------------------------------------- /** * Returns the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array * field entry. * * @return the offset in {@link #classFileBuffer} of the first ClassFile's 'attributes' array * field entry. */ final int getFirstAttributeOffset() { // Skip the access_flags, this_class, super_class, and interfaces_count fields (using 2 bytes // each), as well as the interfaces array field (2 bytes per interface). int currentOffset = header + 8 + readUnsignedShort(header + 6) * 2; // Read the fields_count field. int fieldsCount = readUnsignedShort(currentOffset); currentOffset += 2; // Skip the 'fields' array field. while (fieldsCount-- > 0) { // Invariant: currentOffset is the offset of a field_info structure. // Skip the access_flags, name_index and descriptor_index fields (2 bytes each), and read the // attributes_count field. int attributesCount = readUnsignedShort(currentOffset + 6); currentOffset += 8; // Skip the 'attributes' array field. while (attributesCount-- > 0) { // Invariant: currentOffset is the offset of an attribute_info structure. // Read the attribute_length field (2 bytes after the start of the attribute_info) and skip // this many bytes, plus 6 for the attribute_name_index and attribute_length fields // (yielding the total size of the attribute_info structure). currentOffset += 6 + readInt(currentOffset + 2); } } // Skip the methods_count and 'methods' fields, using the same method as above. int methodsCount = readUnsignedShort(currentOffset); currentOffset += 2; while (methodsCount-- > 0) { int attributesCount = readUnsignedShort(currentOffset + 6); currentOffset += 8; while (attributesCount-- > 0) { currentOffset += 6 + readInt(currentOffset + 2); } } // Skip the ClassFile's attributes_count field. return currentOffset + 2; } // ----------------------------------------------------------------------------------------------- // Utility methods: low level parsing // ----------------------------------------------------------------------------------------------- /** * Returns the number of entries in the class's constant pool table. * * @return the number of entries in the class's constant pool table. */ public int getItemCount() { return cpInfoOffsets.length; } /** * Returns the start offset in this {@link ClassReader} of a JVMS 'cp_info' structure (i.e. a * constant pool entry), plus one. This method is intended for Attribute sub classes, * and is normally not needed by class generators or adapters. * * @param constantPoolEntryIndex the index a constant pool entry in the class's constant pool * table. * @return the start offset in this {@link ClassReader} of the corresponding JVMS 'cp_info' * structure, plus one. */ public int getItem(final int constantPoolEntryIndex) { return cpInfoOffsets[constantPoolEntryIndex]; } /** * Returns a conservative estimate of the maximum length of the strings contained in the class's * constant pool table. * * @return a conservative estimate of the maximum length of the strings contained in the class's * constant pool table. */ public int getMaxStringLength() { return maxStringLength; } /** * Reads a byte value in this {@link ClassReader}. This method is intended for * Attribute sub classes, and is normally not needed by class generators or adapters. * * @param offset the start offset of the value to be read in this {@link ClassReader}. * @return the read value. */ public int readByte(final int offset) { return classFileBuffer[offset] & 0xFF; } /** * Reads an unsigned short value in this {@link ClassReader}. This method is intended for * Attribute sub classes, and is normally not needed by class generators or adapters. * * @param offset the start index of the value to be read in this {@link ClassReader}. * @return the read value. */ public int readUnsignedShort(final int offset) { byte[] classBuffer = classFileBuffer; return ((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF); } /** * Reads a signed short value in this {@link ClassReader}. This method is intended for * Attribute sub classes, and is normally not needed by class generators or adapters. * * @param offset the start offset of the value to be read in this {@link ClassReader}. * @return the read value. */ public short readShort(final int offset) { byte[] classBuffer = classFileBuffer; return (short) (((classBuffer[offset] & 0xFF) << 8) | (classBuffer[offset + 1] & 0xFF)); } /** * Reads a signed int value in this {@link ClassReader}. This method is intended for * Attribute sub classes, and is normally not needed by class generators or adapters. * * @param offset the start offset of the value to be read in this {@link ClassReader}. * @return the read value. */ public int readInt(final int offset) { byte[] classBuffer = classFileBuffer; return ((classBuffer[offset] & 0xFF) << 24) | ((classBuffer[offset + 1] & 0xFF) << 16) | ((classBuffer[offset + 2] & 0xFF) << 8) | (classBuffer[offset + 3] & 0xFF); } /** * Reads a signed long value in this {@link ClassReader}. This method is intended for * Attribute sub classes, and is normally not needed by class generators or adapters. * * @param offset the start offset of the value to be read in this {@link ClassReader}. * @return the read value. */ public long readLong(final int offset) { long l1 = readInt(offset); long l0 = readInt(offset + 4) & 0xFFFFFFFFL; return (l1 << 32) | l0; } /** * Reads a CONSTANT_Utf8 constant pool entry in this {@link ClassReader}. This method is * intended for Attribute sub classes, and is normally not needed by class generators or * adapters. * * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose * value is the index of a CONSTANT_Utf8 entry in the class's constant pool table. * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently * large. It is not automatically resized. * @return the String corresponding to the specified CONSTANT_Utf8 entry. */ // DontCheck(AbbreviationAsWordInName): can't be renamed (for backward binary compatibility). public String readUtf8(final int offset, final char[] charBuffer) { int constantPoolEntryIndex = readUnsignedShort(offset); if (offset == 0 || constantPoolEntryIndex == 0) { return null; } return readUtf(constantPoolEntryIndex, charBuffer); } /** * Reads a CONSTANT_Utf8 constant pool entry in {@link #classFileBuffer}. * * @param constantPoolEntryIndex the index of a CONSTANT_Utf8 entry in the class's constant pool * table. * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently * large. It is not automatically resized. * @return the String corresponding to the specified CONSTANT_Utf8 entry. */ final String readUtf(final int constantPoolEntryIndex, final char[] charBuffer) { String value = constantUtf8Values[constantPoolEntryIndex]; if (value != null) { return value; } int cpInfoOffset = cpInfoOffsets[constantPoolEntryIndex]; return constantUtf8Values[constantPoolEntryIndex] = readUtf(cpInfoOffset + 2, readUnsignedShort(cpInfoOffset), charBuffer); } /** * Reads an UTF8 string in {@link #classFileBuffer}. * * @param utfOffset the start offset of the UTF8 string to be read. * @param utfLength the length of the UTF8 string to be read. * @param charBuffer the buffer to be used to read the string. This buffer must be sufficiently * large. It is not automatically resized. * @return the String corresponding to the specified UTF8 string. */ private String readUtf(final int utfOffset, final int utfLength, final char[] charBuffer) { int currentOffset = utfOffset; int endOffset = currentOffset + utfLength; int strLength = 0; byte[] classBuffer = classFileBuffer; while (currentOffset < endOffset) { int currentByte = classBuffer[currentOffset++]; if ((currentByte & 0x80) == 0) { charBuffer[strLength++] = (char) (currentByte & 0x7F); } else if ((currentByte & 0xE0) == 0xC0) { charBuffer[strLength++] = (char) (((currentByte & 0x1F) << 6) + (classBuffer[currentOffset++] & 0x3F)); } else { charBuffer[strLength++] = (char) (((currentByte & 0xF) << 12) + ((classBuffer[currentOffset++] & 0x3F) << 6) + (classBuffer[currentOffset++] & 0x3F)); } } return new String(charBuffer, 0, strLength); } /** * Reads a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, CONSTANT_Module or * CONSTANT_Package constant pool entry in {@link #classFileBuffer}. This method is intended * for Attribute sub classes, and is normally not needed by class generators or * adapters. * * @param offset the start offset of an unsigned short value in {@link #classFileBuffer}, whose * value is the index of a CONSTANT_Class, CONSTANT_String, CONSTANT_MethodType, * CONSTANT_Module or CONSTANT_Package entry in class's constant pool table. * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently * large. It is not automatically resized. * @return the String corresponding to the specified constant pool entry. */ private String readStringish(final int offset, final char[] charBuffer) { // Get the start offset of the cp_info structure (plus one), and read the CONSTANT_Utf8 entry // designated by the first two bytes of this cp_info. return readUtf8(cpInfoOffsets[readUnsignedShort(offset)], charBuffer); } /** * Reads a CONSTANT_Class constant pool entry in this {@link ClassReader}. This method is * intended for Attribute sub classes, and is normally not needed by class generators or * adapters. * * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose * value is the index of a CONSTANT_Class entry in class's constant pool table. * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently * large. It is not automatically resized. * @return the String corresponding to the specified CONSTANT_Class entry. */ public String readClass(final int offset, final char[] charBuffer) { return readStringish(offset, charBuffer); } /** * Reads a CONSTANT_Module constant pool entry in this {@link ClassReader}. This method is * intended for Attribute sub classes, and is normally not needed by class generators or * adapters. * * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose * value is the index of a CONSTANT_Module entry in class's constant pool table. * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently * large. It is not automatically resized. * @return the String corresponding to the specified CONSTANT_Module entry. */ public String readModule(final int offset, final char[] charBuffer) { return readStringish(offset, charBuffer); } /** * Reads a CONSTANT_Package constant pool entry in this {@link ClassReader}. This method is * intended for Attribute sub classes, and is normally not needed by class generators or * adapters. * * @param offset the start offset of an unsigned short value in this {@link ClassReader}, whose * value is the index of a CONSTANT_Package entry in class's constant pool table. * @param charBuffer the buffer to be used to read the item. This buffer must be sufficiently * large. It is not automatically resized. * @return the String corresponding to the specified CONSTANT_Package entry. */ public String readPackage(final int offset, final char[] charBuffer) { return readStringish(offset, charBuffer); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/classreading/Symbol.java ================================================ // ASM: a very small and fast Java bytecode manipulation framework // Copyright (c) 2000-2011 INRIA, France Telecom // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // 1. Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // 2. 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. // 3. Neither the name of the copyright holders 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 com.alibaba.nacos.common.packagescan.classreading; /** * Copy from ASM, with less modifications * An entry of the constant pool, of the BootstrapMethods attribute, or of the (ASM specific) type * table of a class. * * @author Eric Bruneton * @see JVMS * 4.4 * @see JVMS * 4.7.23 */ abstract class Symbol { /** * The tag value of CONSTANT_Class_info JVMS structures. */ static final int CONSTANT_CLASS_TAG = 7; /** * The tag value of CONSTANT_Fieldref_info JVMS structures. */ static final int CONSTANT_FIELDREF_TAG = 9; /** * The tag value of CONSTANT_Methodref_info JVMS structures. */ static final int CONSTANT_METHODREF_TAG = 10; /** * The tag value of CONSTANT_InterfaceMethodref_info JVMS structures. */ static final int CONSTANT_INTERFACE_METHODREF_TAG = 11; /** * The tag value of CONSTANT_String_info JVMS structures. */ static final int CONSTANT_STRING_TAG = 8; /** * The tag value of CONSTANT_Integer_info JVMS structures. */ static final int CONSTANT_INTEGER_TAG = 3; /** * The tag value of CONSTANT_Float_info JVMS structures. */ static final int CONSTANT_FLOAT_TAG = 4; /** * The tag value of CONSTANT_Long_info JVMS structures. */ static final int CONSTANT_LONG_TAG = 5; /** * The tag value of CONSTANT_Double_info JVMS structures. */ static final int CONSTANT_DOUBLE_TAG = 6; /** * The tag value of CONSTANT_NameAndType_info JVMS structures. */ static final int CONSTANT_NAME_AND_TYPE_TAG = 12; /** * The tag value of CONSTANT_Utf8_info JVMS structures. */ static final int CONSTANT_UTF8_TAG = 1; /** * The tag value of CONSTANT_MethodHandle_info JVMS structures. */ static final int CONSTANT_METHOD_HANDLE_TAG = 15; /** * The tag value of CONSTANT_MethodType_info JVMS structures. */ static final int CONSTANT_METHOD_TYPE_TAG = 16; /** * The tag value of CONSTANT_Dynamic_info JVMS structures. */ static final int CONSTANT_DYNAMIC_TAG = 17; /** * The tag value of CONSTANT_InvokeDynamic_info JVMS structures. */ static final int CONSTANT_INVOKE_DYNAMIC_TAG = 18; /** * The tag value of CONSTANT_Module_info JVMS structures. */ static final int CONSTANT_MODULE_TAG = 19; /** * The tag value of CONSTANT_Package_info JVMS structures. */ static final int CONSTANT_PACKAGE_TAG = 20; // Tag values for the BootstrapMethods attribute entries (ASM specific tag). /** * The tag value of the BootstrapMethods attribute entries. */ static final int BOOTSTRAP_METHOD_TAG = 64; // Tag values for the type table entries (ASM specific tags). /** * The tag value of a normal type entry in the (ASM specific) type table of a class. */ static final int TYPE_TAG = 128; static final int UNINITIALIZED_TYPE_TAG = 129; /** * The tag value of a merged type entry in the (ASM specific) type table of a class. */ static final int MERGED_TYPE_TAG = 130; // Instance fields. /** * The index of this symbol in the constant pool, in the BootstrapMethods attribute, or in the * (ASM specific) type table of a class (depending on the {@link #tag} value). */ final int index; /** * A tag indicating the type of this symbol. Must be one of the static tag values defined in this * class. */ final int tag; /** * The internal name of the owner class of this symbol. Only used for {@link * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link * #CONSTANT_INTERFACE_METHODREF_TAG}, and {@link #CONSTANT_METHOD_HANDLE_TAG} symbols. */ final String owner; /** * The name of the class field or method corresponding to this symbol. Only used for {@link * #CONSTANT_FIELDREF_TAG}, {@link #CONSTANT_METHODREF_TAG}, {@link * #CONSTANT_INTERFACE_METHODREF_TAG}, {@link #CONSTANT_NAME_AND_TYPE_TAG}, {@link * #CONSTANT_METHOD_HANDLE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols. */ final String name; /** * The string value of this symbol. This is: * *

      *
    • a field or method descriptor for {@link #CONSTANT_FIELDREF_TAG}, {@link * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG}, {@link * #CONSTANT_NAME_AND_TYPE_TAG}, {@link #CONSTANT_METHOD_HANDLE_TAG}, {@link * #CONSTANT_METHOD_TYPE_TAG}, {@link #CONSTANT_DYNAMIC_TAG} and {@link * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, *
    • an arbitrary string for {@link #CONSTANT_UTF8_TAG} and {@link #CONSTANT_STRING_TAG} * symbols, *
    • an internal class name for {@link #CONSTANT_CLASS_TAG}, {@link #TYPE_TAG} and {@link * #UNINITIALIZED_TYPE_TAG} symbols, *
    • {@literal null} for the other types of symbol. *
    */ final String value; /** * The numeric value of this symbol. This is: * *
      *
    • the symbol's value for {@link #CONSTANT_INTEGER_TAG},{@link #CONSTANT_FLOAT_TAG}, {@link * #CONSTANT_LONG_TAG}, {@link #CONSTANT_DOUBLE_TAG}, *
    • the CONSTANT_MethodHandle_info reference_kind field value for {@link * #CONSTANT_METHOD_HANDLE_TAG} symbols, *
    • the CONSTANT_InvokeDynamic_info bootstrap_method_attr_index field value for {@link * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, *
    • the offset of a bootstrap method in the BootstrapMethods boostrap_methods array, for * {@link #CONSTANT_DYNAMIC_TAG} or {@link #BOOTSTRAP_METHOD_TAG} symbols, *
    • the bytecode offset of the NEW instruction that created an type for {@link #UNINITIALIZED_TYPE_TAG} symbols, *
    • the indices (in the class' type table) of two {@link #TYPE_TAG} source types for {@link * #MERGED_TYPE_TAG} symbols, *
    • 0 for the other types of symbol. *
    */ final long data; /** * Additional information about this symbol, generally computed lazily. Warning: the value of * this field is ignored when comparing Symbol instances (to avoid duplicate entries in a * SymbolTable). Therefore, this field should only contain data that can be computed from the * other fields of this class. It contains: * *
      *
    • the {@link Type#getArgumentsAndReturnSizes} of the symbol's method descriptor for {@link * #CONSTANT_METHODREF_TAG}, {@link #CONSTANT_INTERFACE_METHODREF_TAG} and {@link * #CONSTANT_INVOKE_DYNAMIC_TAG} symbols, *
    • the index in the InnerClasses_attribute 'classes' array (plus one) corresponding to this * class, for {@link #CONSTANT_CLASS_TAG} symbols, *
    • the index (in the class' type table) of the merged type of the two source types for * {@link #MERGED_TYPE_TAG} symbols, *
    • 0 for the other types of symbol, or if this field has not been computed yet. *
    */ int info; /** * Constructs a new Symbol. This constructor can't be used directly because the Symbol class is * abstract. Instead, use the factory methods of the class. * * @param index the symbol index in the constant pool, in the BootstrapMethods attribute, or in * the (ASM specific) type table of a class (depending on 'tag'). * @param tag the symbol type. Must be one of the static tag values defined in this class. * @param owner The internal name of the symbol's owner class. Maybe {@literal null}. * @param name The name of the symbol's corresponding class field or method. Maybe {@literal * null}. * @param value The string value of this symbol. Maybe {@literal null}. * @param data The numeric value of this symbol. */ Symbol( final int index, final int tag, final String owner, final String name, final String value, final long data) { this.index = index; this.tag = tag; this.owner = owner; this.name = name; this.value = value; this.data = data; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/AbstractFileResolvingResource.java ================================================ /* * Copyright 2002-2021 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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.ResourceUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.net.URLConnection; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.file.NoSuchFileException; import java.nio.file.StandardOpenOption; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Abstract base class for resources which resolve URLs into File references, * such as {@link UrlResource} or {@link ClassPathResource}. * *

    Detects the "file" protocol as well as the JBoss "vfs" protocol in URLs, * resolving file system references accordingly. * * @author Juergen Hoeller * @since 3.0 */ public abstract class AbstractFileResolvingResource extends AbstractResource { @Override public boolean exists() { try { URL url = getUrl(); if (ResourceUtils.isFileUrl(url)) { // Proceed with file system resolution return getFile().exists(); } else { // Try a URL connection content-length header URLConnection con = url.openConnection(); customizeConnection(con); HttpURLConnection httpCon = (con instanceof HttpURLConnection ? (HttpURLConnection) con : null); if (httpCon != null) { int code = httpCon.getResponseCode(); if (code == HttpURLConnection.HTTP_OK) { return true; } else if (code == HttpURLConnection.HTTP_NOT_FOUND) { return false; } } if (con.getContentLengthLong() > 0) { return true; } if (httpCon != null) { // No HTTP OK status, and no content-length header: give up httpCon.disconnect(); return false; } else { // Fall back to stream existence: can we open the stream? getInputStream().close(); return true; } } } catch (IOException ex) { return false; } } @Override public boolean isReadable() { try { return checkReadable(getUrl()); } catch (IOException ex) { return false; } } boolean checkReadable(URL url) { try { if (ResourceUtils.isFileUrl(url)) { // Proceed with file system resolution File file = getFile(); return (file.canRead() && !file.isDirectory()); } else { // Try InputStream resolution for jar resources URLConnection con = url.openConnection(); customizeConnection(con); if (con instanceof HttpURLConnection) { HttpURLConnection httpCon = (HttpURLConnection) con; int code = httpCon.getResponseCode(); if (code != HttpURLConnection.HTTP_OK) { httpCon.disconnect(); return false; } } long contentLength = con.getContentLengthLong(); if (contentLength > 0) { return true; } else if (contentLength == 0) { // Empty file or directory -> not considered readable... return false; } else { // Fall back to stream existence: can we open the stream? getInputStream().close(); return true; } } } catch (IOException ex) { return false; } } @Override public boolean isFile() { try { URL url = getUrl(); if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(url).isFile(); } return ResourceUtils.URL_PROTOCOL_FILE.equals(url.getProtocol()); } catch (IOException ex) { return false; } } /** * This implementation returns a File reference for the given URI-identified * resource, provided that it refers to a file in the file system. * * @see #getFile(URI) * @since 5.0 */ protected boolean isFile(URI uri) { try { if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(uri).isFile(); } return ResourceUtils.URL_PROTOCOL_FILE.equals(uri.getScheme()); } catch (IOException ex) { return false; } } /** * This implementation returns a File reference for the underlying class path * resource, provided that it refers to a file in the file system. * * @see ResourceUtils#getFile(URL, String) */ @Override public File getFile() throws IOException { URL url = getUrl(); if (url.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(url).getFile(); } return ResourceUtils.getFile(url, getDescription()); } /** * This implementation returns a File reference for the given URI-identified * resource, provided that it refers to a file in the file system. * * @see ResourceUtils#getFile(URI, String) */ protected File getFile(URI uri) throws IOException { if (uri.getScheme().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(uri).getFile(); } return ResourceUtils.getFile(uri, getDescription()); } /** * This implementation determines the underlying File * (or jar file, in case of a resource in a jar/zip). */ @Override protected File getFileForLastModifiedCheck() throws IOException { URL url = getUrl(); if (ResourceUtils.isJarUrl(url)) { URL actualUrl = ResourceUtils.extractArchiveUrl(url); if (actualUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { return VfsResourceDelegate.getResource(actualUrl).getFile(); } return ResourceUtils.getFile(actualUrl, "Jar URL"); } else { return getFile(); } } /** * This implementation returns a FileChannel for the given URI-identified * resource, provided that it refers to a file in the file system. * * @see #getFile() * @since 5.0 */ @Override public ReadableByteChannel readableChannel() throws IOException { try { // Try file system channel return FileChannel.open(getFile().toPath(), StandardOpenOption.READ); } catch (FileNotFoundException | NoSuchFileException ex) { // Fall back to InputStream adaptation in superclass return super.readableChannel(); } } @Override public long contentLength() throws IOException { URL url = getUrl(); if (ResourceUtils.isFileUrl(url)) { // Proceed with file system resolution File file = getFile(); long length = file.length(); if (length == 0L && !file.exists()) { throw new FileNotFoundException(getDescription() + " cannot be resolved in the file system for checking its content length"); } return length; } else { // Try a URL connection content-length header URLConnection con = url.openConnection(); customizeConnection(con); return con.getContentLengthLong(); } } @Override public long lastModified() throws IOException { URL url = getUrl(); boolean fileCheck = false; if (ResourceUtils.isFileUrl(url) || ResourceUtils.isJarUrl(url)) { // Proceed with file system resolution fileCheck = true; try { File fileToCheck = getFileForLastModifiedCheck(); long lastModified = fileToCheck.lastModified(); if (lastModified > 0L || fileToCheck.exists()) { return lastModified; } } catch (FileNotFoundException ex) { // Defensively fall back to URL connection check instead } } // Try a URL connection last-modified header URLConnection con = url.openConnection(); customizeConnection(con); long lastModified = con.getLastModified(); if (fileCheck && lastModified == 0 && con.getContentLengthLong() <= 0) { throw new FileNotFoundException(getDescription() + " cannot be resolved in the file system for checking its last-modified timestamp"); } return lastModified; } /** * Customize the given {@link URLConnection}, obtained in the course of an * {@link #exists()}, {@link #contentLength()} or {@link #lastModified()} call. * Calls {@link ResourceUtils#useCachesIfNecessary(URLConnection)} and * delegates to {@link #customizeConnection(HttpURLConnection)} if possible. * Can be overridden in subclasses. * * @param con the URLConnection to customize * @throws IOException if thrown from URLConnection methods */ protected void customizeConnection(URLConnection con) throws IOException { ResourceUtils.useCachesIfNecessary(con); if (con instanceof HttpURLConnection) { customizeConnection((HttpURLConnection) con); } } /** * Customize the given {@link HttpURLConnection}, obtained in the course of an * {@link #exists()}, {@link #contentLength()} or {@link #lastModified()} call. * Sets request method "HEAD" by default. Can be overridden in subclasses. * * @param con the HttpURLConnection to customize * @throws IOException if thrown from HttpURLConnection methods */ protected void customizeConnection(HttpURLConnection con) throws IOException { con.setRequestMethod("HEAD"); } /** * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime. */ private static class VfsResourceDelegate { public static Resource getResource(URL url) throws IOException { return new VfsResource(VfsUtils.getRoot(url)); } public static Resource getResource(URI uri) throws IOException { return new VfsResource(VfsUtils.getRoot(uri)); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/AbstractResource.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.NestedIoException; import com.alibaba.nacos.common.packagescan.util.ResourceUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Convenience base class for {@link Resource} implementations, * pre-implementing typical behavior. * *

    The "exists" method will check whether a File or InputStream can * be opened; "isOpen" will always return false; "getURL" and "getFile" * throw an exception; and "toString" will return the description. * * @author Juergen Hoeller * @author Sam Brannen * @since 28.12.2003 */ public abstract class AbstractResource implements Resource { /** * This implementation checks whether a File can be opened, * falling back to whether an InputStream can be opened. * This will cover both directories and content resources. */ @Override public boolean exists() { // Try file existence: can we find the file in the file system? if (isFile()) { try { return getFile().exists(); } catch (IOException ex) { Logger logger = LoggerFactory.getLogger(getClass()); if (logger.isDebugEnabled()) { logger.debug("Could not retrieve File for existence check of " + getDescription(), ex); } } } // Fall back to stream existence: can we open the stream? try { getInputStream().close(); return true; } catch (Throwable ex) { Logger logger = LoggerFactory.getLogger(getClass()); if (logger.isDebugEnabled()) { logger.debug("Could not retrieve InputStream for existence check of " + getDescription(), ex); } return false; } } /** * This implementation always returns {@code true} for a resource * that {@link #exists() exists} (revised as of 5.1). */ @Override public boolean isReadable() { return exists(); } /** * This implementation always returns {@code false}. */ @Override public boolean isOpen() { return false; } /** * This implementation always returns {@code false}. */ @Override public boolean isFile() { return false; } /** * This implementation throws a FileNotFoundException, assuming * that the resource cannot be resolved to a URL. */ @Override public URL getUrl() throws IOException { throw new FileNotFoundException(getDescription() + " cannot be resolved to URL"); } /** * This implementation builds a URI based on the URL returned * by {@link #getUrl()}. */ @Override public URI getUri() throws IOException { URL url = getUrl(); try { return ResourceUtils.toUri(url); } catch (URISyntaxException ex) { throw new NestedIoException("Invalid URI [" + url + "]", ex); } } /** * This implementation throws a FileNotFoundException, assuming * that the resource cannot be resolved to an absolute file path. */ @Override public File getFile() throws IOException { throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path"); } /** * This implementation returns {@link Channels#newChannel(InputStream)} * with the result of {@link #getInputStream()}. * This is the same as in {@link Resource}'s corresponding default method * but mirrored here for efficient JVM-level dispatching in a class hierarchy. */ @Override public ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } /** * This method reads the entire InputStream to determine the content length. * For a custom sub-class of {@code InputStreamResource}, we strongly * recommend overriding this method with a more optimal implementation, e.g. * checking File length, or possibly simply returning -1 if the stream can * only be read once. * * @see #getInputStream() */ @Override public long contentLength() throws IOException { InputStream is = getInputStream(); try { long size = 0; byte[] buf = new byte[256]; int read; while ((read = is.read(buf)) != -1) { size += read; } return size; } finally { try { is.close(); } catch (IOException ex) { Logger logger = LoggerFactory.getLogger(getClass()); if (logger.isDebugEnabled()) { logger.debug("Could not close content-length InputStream for " + getDescription(), ex); } } } } /** * This implementation checks the timestamp of the underlying File, * if available. * * @see #getFileForLastModifiedCheck() */ @Override public long lastModified() throws IOException { File fileToCheck = getFileForLastModifiedCheck(); long lastModified = fileToCheck.lastModified(); if (lastModified == 0L && !fileToCheck.exists()) { throw new FileNotFoundException(getDescription() + " cannot be resolved in the file system for checking its last-modified timestamp"); } return lastModified; } /** * Determine the File to use for timestamp checking. * The default implementation delegates to {@link #getFile()}. * * @return the File to use for timestamp checking (never {@code null}) * @throws FileNotFoundException if the resource cannot be resolved as * an absolute file path, i.e. is not available in a file system * @throws IOException in case of general resolution/reading failures */ protected File getFileForLastModifiedCheck() throws IOException { return getFile(); } /** * This implementation throws a FileNotFoundException, assuming * that relative resources cannot be created for this resource. */ @Override public Resource createRelative(String relativePath) throws IOException { throw new FileNotFoundException("Cannot create a relative resource for " + getDescription()); } /** * This implementation always returns {@code null}, * assuming that this resource type does not have a filename. */ @Override public String getFilename() { return null; } /** * This implementation compares description strings. * * @see #getDescription() */ @Override public boolean equals(Object other) { return (this == other || (other instanceof Resource && ((Resource) other).getDescription().equals(getDescription()))); } /** * This implementation returns the description's hash code. * * @see #getDescription() */ @Override public int hashCode() { return getDescription().hashCode(); } /** * This implementation returns the description of this resource. * * @see #getDescription() */ @Override public String toString() { return getDescription(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/AntPathMatcher.java ================================================ /* * Copyright 2002-2021 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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.PathMatcher; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import com.alibaba.nacos.common.utils.StringUtils; import java.util.ArrayList; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * {@link PathMatcher} implementation for Ant-style path patterns. * Part of this mapping code has been kindly borrowed from Apache Ant. * The mapping matches URLs using the following rules:
    *

      *
    • {@code ?} matches one character
    • *
    • {@code *} matches zero or more characters
    • *
    • {@code **} matches zero or more directories in a path
    • *
    • {@code {spring:[a-z]+}} matches the regexp {@code [a-z]+} as a path variable named "spring"
    • *
    * *

    Examples

    *
      *
    • {@code com/t?st.jsp} — matches {@code com/test.jsp} but also * {@code com/tast.jsp} or {@code com/txst.jsp}
    • *
    • {@code com/*.jsp} — matches all {@code .jsp} files in the * {@code com} directory
    • *
    • com/**/test.jsp — matches all {@code test.jsp} * files underneath the {@code com} path
    • *
    • org/springframework/**/*.jsp — matches all * {@code .jsp} files underneath the {@code org/springframework} path
    • *
    • org/**/servlet/bla.jsp — matches * {@code org/springframework/servlet/bla.jsp} but also * {@code org/springframework/testing/servlet/bla.jsp} and {@code org/servlet/bla.jsp}
    • *
    • {@code com/{filename:\\w+}.jsp} will match {@code com/test.jsp} and assign the value {@code test} * to the {@code filename} variable
    • *
    * * Note: a pattern and a path must both be absolute or must * both be relative in order for the two to match. Therefore it is recommended * that users of this implementation to sanitize patterns in order to prefix * them with "/" as it makes sense in the context in which they're used. * * @author Alef Arendsen * @author Juergen Hoeller * @author Rob Harrop * @author Arjen Poutsma * @author Rossen Stoyanchev * @author Sam Brannen * @author Vladislav Kisel * @since 16.07.2003 */ public class AntPathMatcher implements PathMatcher { /** * Default path separator: "/". */ public static final String DEFAULT_PATH_SEPARATOR = "/"; private static final int CACHE_TURNOFF_THRESHOLD = 65536; private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{[^/]+?\\}"); private static final char[] WILDCARD_CHARS = {'*', '?', '{'}; private String pathSeparator; private PathSeparatorPatternCache pathSeparatorPatternCache; private boolean caseSensitive = true; private boolean trimTokens = false; private volatile Boolean cachePatterns; private final Map tokenizedPatternCache = new ConcurrentHashMap<>(256); final Map stringMatcherCache = new ConcurrentHashMap<>(256); /** * Create a new instance with the {@link #DEFAULT_PATH_SEPARATOR}. */ public AntPathMatcher() { this.pathSeparator = DEFAULT_PATH_SEPARATOR; this.pathSeparatorPatternCache = new PathSeparatorPatternCache(DEFAULT_PATH_SEPARATOR); } /** * A convenient, alternative constructor to use with a custom path separator. * * @param pathSeparator the path separator to use, must not be {@code null}. * @since 4.1 */ public AntPathMatcher(String pathSeparator) { AbstractAssert.notNull(pathSeparator, "'pathSeparator' is required"); this.pathSeparator = pathSeparator; this.pathSeparatorPatternCache = new PathSeparatorPatternCache(pathSeparator); } /** * Set the path separator to use for pattern parsing. * Default is "/", as in Ant. */ public void setPathSeparator(String pathSeparator) { this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR); this.pathSeparatorPatternCache = new PathSeparatorPatternCache(this.pathSeparator); } /** * Specify whether to perform pattern matching in a case-sensitive fashion. * Default is {@code true}. Switch this to {@code false} for case-insensitive matching. * * @since 4.2 */ public void setCaseSensitive(boolean caseSensitive) { this.caseSensitive = caseSensitive; } /** * Specify whether to trim tokenized paths and patterns. * Default is {@code false}. */ public void setTrimTokens(boolean trimTokens) { this.trimTokens = trimTokens; } /** * Specify whether to cache parsed pattern metadata for patterns passed * into this matcher's {@link #match} method. A value of {@code true} * activates an unlimited pattern cache; a value of {@code false} turns * the pattern cache off completely. * Default is for the cache to be on, but with the variant to automatically * turn it off when encountering too many patterns to cache at runtime * (the threshold is 65536), assuming that arbitrary permutations of patterns * are coming in, with little chance for encountering a recurring pattern. * * @see #getStringMatcher(String) * @since 4.0.1 */ public void setCachePatterns(boolean cachePatterns) { this.cachePatterns = cachePatterns; } private void deactivatePatternCache() { this.cachePatterns = false; this.tokenizedPatternCache.clear(); this.stringMatcherCache.clear(); } @Override public boolean isPattern(String path) { if (path == null) { return false; } boolean uriVar = false; for (int i = 0; i < path.length(); i++) { char c = path.charAt(i); if (c == '*' || c == '?') { return true; } if (c == '{') { uriVar = true; continue; } if (c == '}' && uriVar) { return true; } } return false; } @Override public boolean match(String pattern, String path) { return doMatch(pattern, path, true, null); } @Override public boolean matchStart(String pattern, String path) { return doMatch(pattern, path, false, null); } /** * Actually match the given {@code path} against the given {@code pattern}. * * @param pattern the pattern to match against * @param path the path to test * @param fullMatch whether a full pattern match is required (else a pattern match * as far as the given base path goes is sufficient) * @return {@code true} if the supplied {@code path} matched, {@code false} if it didn't */ protected boolean doMatch(String pattern, String path, boolean fullMatch, Map uriTemplateVariables) { if (path == null || path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) { return false; } String[] pattDirs = tokenizePattern(pattern); if (fullMatch && this.caseSensitive && !isPotentialMatch(path, pattDirs)) { return false; } String[] pathDirs = tokenizePath(path); int pattIdxStart = 0; int pattIdxEnd = pattDirs.length - 1; int pathIdxStart = 0; int pathIdxEnd = pathDirs.length - 1; // Match all elements up to the first ** while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { String pattDir = pattDirs[pattIdxStart]; if ("**".equals(pattDir)) { break; } if (!matchStrings(pattDir, pathDirs[pathIdxStart], uriTemplateVariables)) { return false; } pattIdxStart++; pathIdxStart++; } if (pathIdxStart > pathIdxEnd) { // Path is exhausted, only match if rest of pattern is * or **'s if (pattIdxStart > pattIdxEnd) { return (pattern.endsWith(this.pathSeparator) == path.endsWith(this.pathSeparator)); } if (!fullMatch) { return true; } if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) { return true; } for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } else if (pattIdxStart > pattIdxEnd) { // String not exhausted, but pattern is. Failure. return false; } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) { // Path start definitely matches due to "**" part in pattern. return true; } // up to last '**' while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { String pattDir = pattDirs[pattIdxEnd]; if (pattDir.equals("**")) { break; } if (!matchStrings(pattDir, pathDirs[pathIdxEnd], uriTemplateVariables)) { return false; } pattIdxEnd--; pathIdxEnd--; } if (pathIdxStart > pathIdxEnd) { // String is exhausted for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) { int patIdxTmp = -1; for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) { if (pattDirs[i].equals("**")) { patIdxTmp = i; break; } } if (patIdxTmp == pattIdxStart + 1) { // '**/**' situation, so skip one pattIdxStart++; continue; } // Find the pattern between padIdxStart & padIdxTmp in str between // strIdxStart & strIdxEnd int patLength = (patIdxTmp - pattIdxStart - 1); int strLength = (pathIdxEnd - pathIdxStart + 1); int foundIdx = -1; strLoop: for (int i = 0; i <= strLength - patLength; i++) { for (int j = 0; j < patLength; j++) { String subPat = pattDirs[pattIdxStart + j + 1]; String subStr = pathDirs[pathIdxStart + i + j]; if (!matchStrings(subPat, subStr, uriTemplateVariables)) { continue strLoop; } } foundIdx = pathIdxStart + i; break; } if (foundIdx == -1) { return false; } pattIdxStart = patIdxTmp; pathIdxStart = foundIdx + patLength; } for (int i = pattIdxStart; i <= pattIdxEnd; i++) { if (!pattDirs[i].equals("**")) { return false; } } return true; } private boolean isPotentialMatch(String path, String[] pattDirs) { if (!this.trimTokens) { int pos = 0; for (String pattDir : pattDirs) { int skipped = skipSeparator(path, pos, this.pathSeparator); pos += skipped; skipped = skipSegment(path, pos, pattDir); if (skipped < pattDir.length()) { return (skipped > 0 || (pattDir.length() > 0 && isWildcardChar(pattDir.charAt(0)))); } pos += skipped; } } return true; } private int skipSegment(String path, int pos, String prefix) { int skipped = 0; for (int i = 0; i < prefix.length(); i++) { char c = prefix.charAt(i); if (isWildcardChar(c)) { return skipped; } int currPos = pos + skipped; if (currPos >= path.length()) { return 0; } if (c == path.charAt(currPos)) { skipped++; } } return skipped; } private int skipSeparator(String path, int pos, String separator) { int skipped = 0; while (path.startsWith(separator, pos + skipped)) { skipped += separator.length(); } return skipped; } private boolean isWildcardChar(char c) { for (char candidate : WILDCARD_CHARS) { if (c == candidate) { return true; } } return false; } /** * Tokenize the given path pattern into parts, based on this matcher's settings. * Performs caching based on {@link #setCachePatterns}, delegating to * {@link #tokenizePath(String)} for the actual tokenization algorithm. * * @param pattern the pattern to tokenize * @return the tokenized pattern parts */ protected String[] tokenizePattern(String pattern) { String[] tokenized = null; Boolean cachePatterns = this.cachePatterns; if (cachePatterns == null || cachePatterns.booleanValue()) { tokenized = this.tokenizedPatternCache.get(pattern); } if (tokenized == null) { tokenized = tokenizePath(pattern); if (cachePatterns == null && this.tokenizedPatternCache.size() >= CACHE_TURNOFF_THRESHOLD) { // Try to adapt to the runtime situation that we're encountering: // There are obviously too many different patterns coming in here... // So let's turn off the cache since the patterns are unlikely to be reoccurring. deactivatePatternCache(); return tokenized; } if (cachePatterns == null || cachePatterns.booleanValue()) { this.tokenizedPatternCache.put(pattern, tokenized); } } return tokenized; } /** * Tokenize the given path into parts, based on this matcher's settings. * * @param path the path to tokenize * @return the tokenized path parts */ protected String[] tokenizePath(String path) { return StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); } /** * Test whether or not a string matches against a pattern. * * @param pattern the pattern to match against (never {@code null}) * @param str the String which must be matched against the pattern (never {@code null}) * @return {@code true} if the string matches against the pattern, or {@code false} otherwise */ private boolean matchStrings(String pattern, String str, Map uriTemplateVariables) { return getStringMatcher(pattern).matchStrings(str, uriTemplateVariables); } /** * Build or retrieve an {@link AntPathStringMatcher} for the given pattern. * The default implementation checks this AntPathMatcher's internal cache * (see {@link #setCachePatterns}), creating a new AntPathStringMatcher instance * if no cached copy is found. * When encountering too many patterns to cache at runtime (the threshold is 65536), * it turns the default cache off, assuming that arbitrary permutations of patterns * are coming in, with little chance for encountering a recurring pattern. * This method may be overridden to implement a custom cache strategy. * * @param pattern the pattern to match against (never {@code null}) * @return a corresponding AntPathStringMatcher (never {@code null}) * @see #setCachePatterns */ protected AntPathStringMatcher getStringMatcher(String pattern) { AntPathStringMatcher matcher = null; Boolean cachePatterns = this.cachePatterns; if (cachePatterns == null || cachePatterns.booleanValue()) { matcher = this.stringMatcherCache.get(pattern); } if (matcher == null) { matcher = new AntPathStringMatcher(pattern, this.caseSensitive); if (cachePatterns == null && this.stringMatcherCache.size() >= CACHE_TURNOFF_THRESHOLD) { // Try to adapt to the runtime situation that we're encountering: // There are obviously too many different patterns coming in here... // So let's turn off the cache since the patterns are unlikely to be reoccurring. deactivatePatternCache(); return matcher; } if (cachePatterns == null || cachePatterns.booleanValue()) { this.stringMatcherCache.put(pattern, matcher); } } return matcher; } /** * Given a pattern and a full path, determine the pattern-mapped part. For example:
      *
    • '{@code /docs/cvs/commit.html}' and '{@code /docs/cvs/commit.html} → ''
    • *
    • '{@code /docs/*}' and '{@code /docs/cvs/commit} → '{@code cvs/commit}'
    • *
    • '{@code /docs/cvs/*.html}' and '{@code /docs/cvs/commit.html} → '{@code commit.html}'
    • *
    • '{@code /docs/**}' and '{@code /docs/cvs/commit} → '{@code cvs/commit}'
    • *
    • '{@code /docs/**\/*.html}' and '{@code /docs/cvs/commit.html} → '{@code cvs/commit.html}'
    • *
    • '{@code /*.html}' and '{@code /docs/cvs/commit.html} → '{@code docs/cvs/commit.html}'
    • *
    • '{@code *.html}' and '{@code /docs/cvs/commit.html} → '{@code /docs/cvs/commit.html}'
    • *
    • '{@code *}' and '{@code /docs/cvs/commit.html} → '{@code /docs/cvs/commit.html}'
    * Assumes that {@link #match} returns {@code true} for '{@code pattern}' and '{@code path}', but * does not enforce this. */ @Override public String extractPathWithinPattern(String pattern, String path) { String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator, this.trimTokens, true); String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator, this.trimTokens, true); StringBuilder builder = new StringBuilder(); boolean pathStarted = false; for (int segment = 0; segment < patternParts.length; segment++) { String patternPart = patternParts[segment]; if (patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) { for (; segment < pathParts.length; segment++) { if (pathStarted || (segment == 0 && !pattern.startsWith(this.pathSeparator))) { builder.append(this.pathSeparator); } builder.append(pathParts[segment]); pathStarted = true; } } } return builder.toString(); } @Override public Map extractUriTemplateVariables(String pattern, String path) { Map variables = new LinkedHashMap<>(); boolean result = doMatch(pattern, path, true, variables); if (!result) { throw new IllegalStateException("Pattern \"" + pattern + "\" is not a match for \"" + path + "\""); } return variables; } /** * Combine two patterns into a new pattern. * This implementation simply concatenates the two patterns, unless * the first pattern contains a file extension match (e.g., {@code *.html}). * In that case, the second pattern will be merged into the first. Otherwise, * an {@code IllegalArgumentException} will be thrown. *

    Examples

    * * * * * * * * * * * * * * * *
    Pattern 1Pattern 2Result
    {@code null}{@code null} 
    /hotels{@code null}/hotels
    {@code null}/hotels/hotels
    /hotels/bookings/hotels/bookings
    /hotelsbookings/hotels/bookings
    /hotels/*/bookings/hotels/bookings
    /hotels/**/bookings/hotels/**/bookings
    /hotels{hotel}/hotels/{hotel}
    /hotels/*{hotel}/hotels/{hotel}
    /hotels/**{hotel}/hotels/**/{hotel}
    /*.html/hotels.html/hotels.html
    /*.html/hotels/hotels.html
    /*.html/*.txt{@code IllegalArgumentException}
    * * @param pattern1 the first pattern * @param pattern2 the second pattern * @return the combination of the two patterns * @throws IllegalArgumentException if the two patterns cannot be combined */ @Override public String combine(String pattern1, String pattern2) { if (!StringUtils.hasText(pattern1) && !StringUtils.hasText(pattern2)) { return ""; } if (!StringUtils.hasText(pattern1)) { return pattern2; } if (!StringUtils.hasText(pattern2)) { return pattern1; } boolean pattern1ContainsUriVar = (pattern1.indexOf('{') != -1); if (!pattern1.equals(pattern2) && !pattern1ContainsUriVar && match(pattern1, pattern2)) { // /* + /hotel -> /hotel ; "/*.*" + "/*.html" -> /*.html // However /user + /user -> /usr/user ; /{foo} + /bar -> /{foo}/bar return pattern2; } // /hotels/* + /booking -> /hotels/booking // /hotels/* + booking -> /hotels/booking if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnWildCard())) { return concat(pattern1.substring(0, pattern1.length() - 2), pattern2); } // /hotels/** + /booking -> /hotels/**/booking // /hotels/** + booking -> /hotels/**/booking if (pattern1.endsWith(this.pathSeparatorPatternCache.getEndsOnDoubleWildCard())) { return concat(pattern1, pattern2); } int starDotPos1 = pattern1.indexOf("*."); if (pattern1ContainsUriVar || starDotPos1 == -1 || this.pathSeparator.equals(".")) { // simply concatenate the two patterns return concat(pattern1, pattern2); } String ext1 = pattern1.substring(starDotPos1 + 1); int dotPos2 = pattern2.indexOf('.'); String file2 = (dotPos2 == -1 ? pattern2 : pattern2.substring(0, dotPos2)); String ext2 = (dotPos2 == -1 ? "" : pattern2.substring(dotPos2)); boolean ext1All = (ext1.equals(".*") || ext1.isEmpty()); boolean ext2All = (ext2.equals(".*") || ext2.isEmpty()); if (!ext1All && !ext2All) { throw new IllegalArgumentException("Cannot combine patterns: " + pattern1 + " vs " + pattern2); } String ext = (ext1All ? ext2 : ext1); return file2 + ext; } private String concat(String path1, String path2) { boolean path1EndsWithSeparator = path1.endsWith(this.pathSeparator); boolean path2StartsWithSeparator = path2.startsWith(this.pathSeparator); if (path1EndsWithSeparator && path2StartsWithSeparator) { return path1 + path2.substring(1); } else if (path1EndsWithSeparator || path2StartsWithSeparator) { return path1 + path2; } else { return path1 + this.pathSeparator + path2; } } /** * Given a full path, returns a {@link Comparator} suitable for sorting patterns in order of * explicitness. * This {@code Comparator} will {@linkplain List#sort(Comparator) sort} * a list so that more specific patterns (without URI templates or wild cards) come before * generic patterns. So given a list with the following patterns, the returned comparator * will sort this list so that the order will be as indicated. *
      *
    1. {@code /hotels/new}
    2. *
    3. {@code /hotels/{hotel}}
    4. *
    5. {@code /hotels/*}
    6. *
    * The full path given as parameter is used to test for exact matches. So when the given path * is {@code /hotels/2}, the pattern {@code /hotels/2} will be sorted before {@code /hotels/1}. * * @param path the full path to use for comparison * @return a comparator capable of sorting patterns in order of explicitness */ @Override public Comparator getPatternComparator(String path) { return new AntPatternComparator(path); } /** * Tests whether or not a string matches against a pattern via a {@link Pattern}. * The pattern may contain special characters: '*' means zero or more characters; '?' means one and * only one character; '{' and '}' indicate a URI template pattern. For example /users/{user}. */ protected static class AntPathStringMatcher { private static final Pattern GLOB_PATTERN = Pattern.compile("\\?|\\*|\\{((?:\\{[^/]+?\\}|[^/{}]|\\\\[{}])+?)\\}"); private static final String DEFAULT_VARIABLE_PATTERN = "((?s).*)"; private final String rawPattern; private final boolean caseSensitive; private final boolean exactMatch; private final Pattern pattern; private final List variableNames = new ArrayList<>(); public AntPathStringMatcher(String pattern) { this(pattern, true); } public AntPathStringMatcher(String pattern, boolean caseSensitive) { this.rawPattern = pattern; this.caseSensitive = caseSensitive; StringBuilder patternBuilder = new StringBuilder(); Matcher matcher = GLOB_PATTERN.matcher(pattern); int end = 0; while (matcher.find()) { patternBuilder.append(quote(pattern, end, matcher.start())); String match = matcher.group(); if ("?".equals(match)) { patternBuilder.append('.'); } else if ("*".equals(match)) { patternBuilder.append(".*"); } else if (match.startsWith("{") && match.endsWith("}")) { int colonIdx = match.indexOf(':'); if (colonIdx == -1) { patternBuilder.append(DEFAULT_VARIABLE_PATTERN); this.variableNames.add(matcher.group(1)); } else { String variablePattern = match.substring(colonIdx + 1, match.length() - 1); patternBuilder.append('('); patternBuilder.append(variablePattern); patternBuilder.append(')'); String variableName = match.substring(1, colonIdx); this.variableNames.add(variableName); } } end = matcher.end(); } // No glob pattern was found, this is an exact String match if (end == 0) { this.exactMatch = true; this.pattern = null; } else { this.exactMatch = false; patternBuilder.append(quote(pattern, end, pattern.length())); this.pattern = (this.caseSensitive ? Pattern.compile(patternBuilder.toString()) : Pattern.compile(patternBuilder.toString(), Pattern.CASE_INSENSITIVE)); } } private String quote(String s, int start, int end) { if (start == end) { return ""; } return Pattern.quote(s.substring(start, end)); } /** * Main entry point. * * @return {@code true} if the string matches against the pattern, or {@code false} otherwise. */ public boolean matchStrings(String str, Map uriTemplateVariables) { if (this.exactMatch) { return this.caseSensitive ? this.rawPattern.equals(str) : this.rawPattern.equalsIgnoreCase(str); } else if (this.pattern != null) { Matcher matcher = this.pattern.matcher(str); if (matcher.matches()) { if (uriTemplateVariables != null) { if (this.variableNames.size() != matcher.groupCount()) { throw new IllegalArgumentException("The number of capturing groups in the pattern segment " + this.pattern + " does not match the number of URI template variables it defines, " + "which can occur if capturing groups are used in a URI template regex. " + "Use non-capturing groups instead."); } for (int i = 1; i <= matcher.groupCount(); i++) { String name = this.variableNames.get(i - 1); if (name.startsWith("*")) { throw new IllegalArgumentException("Capturing patterns (" + name + ") are not " + "supported by the AntPathMatcher. Use the PathPatternParser instead."); } String value = matcher.group(i); uriTemplateVariables.put(name, value); } } return true; } } return false; } } /** * The default {@link Comparator} implementation returned by * {@link #getPatternComparator(String)}. * In order, the most "generic" pattern is determined by the following: *
      *
    • if it's null or a capture all pattern (i.e. it is equal to "/**")
    • *
    • if the other pattern is an actual match
    • *
    • if it's a catch-all pattern (i.e. it ends with "**"
    • *
    • if it's got more "*" than the other pattern
    • *
    • if it's got more "{foo}" than the other pattern
    • *
    • if it's shorter than the other pattern
    • *
    */ protected static class AntPatternComparator implements Comparator { private final String path; public AntPatternComparator(String path) { this.path = path; } /** * Compare two patterns to determine which should match first, i.e. which * is the most specific regarding the current path. * * @return a negative integer, zero, or a positive integer as pattern1 is * more specific, equally specific, or less specific than pattern2. */ @Override public int compare(String pattern1, String pattern2) { PatternInfo info1 = new PatternInfo(pattern1); PatternInfo info2 = new PatternInfo(pattern2); if (info1.isLeastSpecific() && info2.isLeastSpecific()) { return 0; } else if (info1.isLeastSpecific()) { return 1; } else if (info2.isLeastSpecific()) { return -1; } boolean pattern1EqualsPath = pattern1.equals(this.path); boolean pattern2EqualsPath = pattern2.equals(this.path); if (pattern1EqualsPath && pattern2EqualsPath) { return 0; } else if (pattern1EqualsPath) { return -1; } else if (pattern2EqualsPath) { return 1; } if (info1.isPrefixPattern() && info2.isPrefixPattern()) { return info2.getLength() - info1.getLength(); } else if (info1.isPrefixPattern() && info2.getDoubleWildcards() == 0) { return 1; } else if (info2.isPrefixPattern() && info1.getDoubleWildcards() == 0) { return -1; } if (info1.getTotalCount() != info2.getTotalCount()) { return info1.getTotalCount() - info2.getTotalCount(); } if (info1.getLength() != info2.getLength()) { return info2.getLength() - info1.getLength(); } if (info1.getSingleWildcards() < info2.getSingleWildcards()) { return -1; } else if (info2.getSingleWildcards() < info1.getSingleWildcards()) { return 1; } if (info1.getUriVars() < info2.getUriVars()) { return -1; } else if (info2.getUriVars() < info1.getUriVars()) { return 1; } return 0; } /** * Value class that holds information about the pattern, e.g. number of * occurrences of "*", "**", and "{" pattern elements. */ private static class PatternInfo { private final String pattern; private int uriVars; private int singleWildcards; private int doubleWildcards; private boolean catchAllPattern; private boolean prefixPattern; private Integer length; public PatternInfo(String pattern) { this.pattern = pattern; if (this.pattern != null) { initCounters(); this.catchAllPattern = this.pattern.equals("/**"); this.prefixPattern = !this.catchAllPattern && this.pattern.endsWith("/**"); } if (this.uriVars == 0) { this.length = (this.pattern != null ? this.pattern.length() : 0); } } protected void initCounters() { int pos = 0; if (this.pattern != null) { while (pos < this.pattern.length()) { if (this.pattern.charAt(pos) == '{') { this.uriVars++; pos++; } else if (this.pattern.charAt(pos) == '*') { if (pos + 1 < this.pattern.length() && this.pattern.charAt(pos + 1) == '*') { this.doubleWildcards++; pos += 2; } else if (pos > 0 && !this.pattern.substring(pos - 1).equals(".*")) { this.singleWildcards++; pos++; } else { pos++; } } else { pos++; } } } } public int getUriVars() { return this.uriVars; } public int getSingleWildcards() { return this.singleWildcards; } public int getDoubleWildcards() { return this.doubleWildcards; } public boolean isLeastSpecific() { return (this.pattern == null || this.catchAllPattern); } public boolean isPrefixPattern() { return this.prefixPattern; } public int getTotalCount() { return this.uriVars + this.singleWildcards + (2 * this.doubleWildcards); } /** * Returns the length of the given pattern, where template variables are considered to be 1 long. */ public int getLength() { if (this.length == null) { this.length = (this.pattern != null ? VARIABLE_PATTERN.matcher(this.pattern).replaceAll("#").length() : 0); } return this.length; } } } /** * A simple cache for patterns that depend on the configured path separator. */ private static class PathSeparatorPatternCache { private final String endsOnWildCard; private final String endsOnDoubleWildCard; public PathSeparatorPatternCache(String pathSeparator) { this.endsOnWildCard = pathSeparator + "*"; this.endsOnDoubleWildCard = pathSeparator + "**"; } public String getEndsOnWildCard() { return this.endsOnWildCard; } public String getEndsOnDoubleWildCard() { return this.endsOnDoubleWildCard; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/ByteArrayResource.java ================================================ /* * Copyright 2002-2021 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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.Arrays; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * {@link Resource} implementation for a given byte array. * *

    Creates a {@link ByteArrayInputStream} for the given byte array. * *

    Useful for loading content from any given byte array, * without having to resort to a single-use {@link InputStreamResource}. * Particularly useful for creating mail attachments from local content, * where JavaMail needs to be able to read the stream multiple times. * * @author Juergen Hoeller * @author Sam Brannen * @see ByteArrayInputStream * @see InputStreamResource * @since 1.2.3 */ public class ByteArrayResource extends AbstractResource { private final byte[] byteArray; private final String description; /** * Create a new {@code ByteArrayResource}. * * @param byteArray the byte array to wrap */ public ByteArrayResource(byte[] byteArray) { this(byteArray, "resource loaded from byte array"); } /** * Create a new {@code ByteArrayResource} with a description. * * @param byteArray the byte array to wrap * @param description where the byte array comes from */ public ByteArrayResource(byte[] byteArray, String description) { AbstractAssert.notNull(byteArray, "Byte array must not be null"); this.byteArray = byteArray; this.description = (description != null ? description : ""); } /** * Return the underlying byte array. */ public final byte[] getByteArray() { return this.byteArray; } /** * This implementation always returns {@code true}. */ @Override public boolean exists() { return true; } /** * This implementation returns the length of the underlying byte array. */ @Override public long contentLength() { return this.byteArray.length; } /** * This implementation returns a ByteArrayInputStream for the * underlying byte array. * * @see ByteArrayInputStream */ @Override public InputStream getInputStream() throws IOException { return new ByteArrayInputStream(this.byteArray); } /** * This implementation returns a description that includes the passed-in * {@code description}, if any. */ @Override public String getDescription() { return "Byte array resource [" + this.description + "]"; } /** * This implementation compares the underlying byte array. * * @see Arrays#equals(byte[], byte[]) */ @Override public boolean equals(Object other) { return (this == other || (other instanceof ByteArrayResource && Arrays.equals(((ByteArrayResource) other).byteArray, this.byteArray))); } /** * This implementation returns the hash code based on the * underlying byte array. */ @Override public int hashCode() { return Arrays.hashCode(this.byteArray); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/ClassPathResource.java ================================================ /* * Copyright 2002-2021 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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.AbstractObjectUtils; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import com.alibaba.nacos.common.utils.ClassUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * {@link Resource} implementation for class path resources. Uses either a * given {@link ClassLoader} or a given {@link Class} for loading resources. * *

    Supports resolution as {@code java.io.File} if the class path * resource resides in the file system, but not for resources in a JAR. * Always supports resolution as URL. * * @author Juergen Hoeller * @author Sam Brannen * @see ClassLoader#getResourceAsStream(String) * @see Class#getResourceAsStream(String) * @since 28.12.2003 */ public class ClassPathResource extends AbstractFileResolvingResource { private final String path; private ClassLoader classLoader; private Class clazz; /** * Create a new {@code ClassPathResource} for {@code ClassLoader} usage. * A leading slash will be removed, as the ClassLoader resource access * methods will not accept it. * *

    The thread context class loader will be used for * loading the resource. * * @param path the absolute path within the class path * @see ClassLoader#getResourceAsStream(String) * @see ClassUtils#getDefaultClassLoader() */ public ClassPathResource(String path) { this(path, (ClassLoader) null); } /** * Create a new {@code ClassPathResource} for {@code ClassLoader} usage. * A leading slash will be removed, as the ClassLoader resource access * methods will not accept it. * * @param path the absolute path within the classpath * @param classLoader the class loader to load the resource with, * or {@code null} for the thread context class loader * @see ClassLoader#getResourceAsStream(String) */ public ClassPathResource(String path, ClassLoader classLoader) { AbstractAssert.notNull(path, "Path must not be null"); String pathToUse = StringUtils.cleanPath(path); if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } this.path = pathToUse; this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); } /** * Create a new {@code ClassPathResource} for {@code Class} usage. * The path can be relative to the given class, or absolute within * the classpath via a leading slash. * * @param path relative or absolute path within the class path * @param clazz the class to load resources with * @see Class#getResourceAsStream */ public ClassPathResource(String path, Class clazz) { AbstractAssert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); this.clazz = clazz; } /** * Create a new {@code ClassPathResource} with optional {@code ClassLoader} * and {@code Class}. Only for internal usage. * * @param path relative or absolute path within the classpath * @param classLoader the class loader to load the resource with, if any * @param clazz the class to load resources with, if any * @deprecated as of 4.3.13, in favor of selective use of * {@link #ClassPathResource(String, ClassLoader)} vs {@link #ClassPathResource(String, Class)} */ @Deprecated protected ClassPathResource(String path, ClassLoader classLoader, Class clazz) { this.path = StringUtils.cleanPath(path); this.classLoader = classLoader; this.clazz = clazz; } /** * Return the path for this resource (as resource path within the class path). */ public final String getPath() { return this.path; } /** * Return the ClassLoader that this resource will be obtained from. */ public final ClassLoader getClassLoader() { return (this.clazz != null ? this.clazz.getClassLoader() : this.classLoader); } /** * This implementation checks for the resolution of a resource URL. * * @see ClassLoader#getResource(String) * @see Class#getResource(String) */ @Override public boolean exists() { return (resolveUrl() != null); } /** * This implementation checks for the resolution of a resource URL upfront, * then proceeding with {@link AbstractFileResolvingResource}'s length check. * * @see ClassLoader#getResource(String) * @see Class#getResource(String) */ @Override public boolean isReadable() { URL url = resolveUrl(); return (url != null && checkReadable(url)); } /** * Resolves a URL for the underlying class path resource. * * @return the resolved URL, or {@code null} if not resolvable */ protected URL resolveUrl() { try { if (this.clazz != null) { return this.clazz.getResource(this.path); } else if (this.classLoader != null) { return this.classLoader.getResource(this.path); } else { return ClassLoader.getSystemResource(this.path); } } catch (IllegalArgumentException ex) { // Should not happen according to the JDK's contract: // see https://github.com/openjdk/jdk/pull/2662 return null; } } /** * This implementation opens an InputStream for the given class path resource. * * @see ClassLoader#getResourceAsStream(String) * @see Class#getResourceAsStream(String) */ @Override public InputStream getInputStream() throws IOException { InputStream is; if (this.clazz != null) { is = this.clazz.getResourceAsStream(this.path); } else if (this.classLoader != null) { is = this.classLoader.getResourceAsStream(this.path); } else { is = ClassLoader.getSystemResourceAsStream(this.path); } if (is == null) { throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist"); } return is; } /** * This implementation returns a URL for the underlying class path resource, * if available. * * @see ClassLoader#getResource(String) * @see Class#getResource(String) */ @Override public URL getUrl() throws IOException { URL url = resolveUrl(); if (url == null) { throw new FileNotFoundException(getDescription() + " cannot be resolved to URL because it does not exist"); } return url; } /** * This implementation creates a ClassPathResource, applying the given path * relative to the path of the underlying resource of this descriptor. * * @see StringUtils#applyRelativePath(String, String) */ @Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); return (this.clazz != null ? new ClassPathResource(pathToUse, this.clazz) : new ClassPathResource(pathToUse, this.classLoader)); } /** * This implementation returns the name of the file that this class path * resource refers to. * * @see StringUtils#getFilename(String) */ @Override public String getFilename() { return StringUtils.getFilename(this.path); } /** * This implementation returns a description that includes the class path location. */ @Override public String getDescription() { StringBuilder builder = new StringBuilder("class path resource ["); String pathToUse = this.path; if (this.clazz != null && !pathToUse.startsWith("/")) { builder.append(ClassUtils.classPackageAsResourcePath(this.clazz)); builder.append('/'); } if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } builder.append(pathToUse); builder.append(']'); return builder.toString(); } /** * This implementation compares the underlying class path locations. */ @Override public boolean equals(Object other) { if (this == other) { return true; } if (!(other instanceof ClassPathResource)) { return false; } ClassPathResource otherRes = (ClassPathResource) other; return (this.path.equals(otherRes.path) && AbstractObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) && AbstractObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz)); } /** * This implementation returns the hash code of the underlying * class path location. */ @Override public int hashCode() { return this.path.hashCode(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/ContextResource.java ================================================ /* * Copyright 2002-2007 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 com.alibaba.nacos.common.packagescan.resource; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Extended interface for a resource that is loaded from an enclosing * 'context', e.g. from a {@link jakarta.servlet.ServletContext} but also * from plain classpath paths or relative file system paths (specified * without an explicit prefix, hence applying relative to the local * {@link ResourceLoader}'s context). * * @author Juergen Hoeller * @since 2.5 */ public interface ContextResource extends Resource { /** * Return the path within the enclosing 'context'. * *

    This is typically path relative to a context-specific root directory, * e.g. a ServletContext root or a PortletContext root. */ String getPathWithinContext(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/DefaultResourceLoader.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.ResourceUtils; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import com.alibaba.nacos.common.utils.ClassUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.net.MalformedURLException; import java.net.URL; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Default implementation of the {@link ResourceLoader} interface. * Can also be used standalone. * *

    Will return a {@link UrlResource} if the location value is a URL, * and a {@link ClassPathResource} if it is a non-URL path or a * "classpath:" pseudo-URL. * * @author Juergen Hoeller * @since 10.03.2004 */ public class DefaultResourceLoader implements ResourceLoader { private ClassLoader classLoader; private final Set protocolResolvers = new LinkedHashSet<>(4); private final Map, Map> resourceCaches = new ConcurrentHashMap<>(4); /** * Create a new DefaultResourceLoader. * *

    ClassLoader access will happen using the thread context class loader * at the time of actual resource access (since 5.3). For more control, pass * a specific ClassLoader to {@link #DefaultResourceLoader(ClassLoader)}. * * @see Thread#getContextClassLoader() */ public DefaultResourceLoader() { } /** * Create a new DefaultResourceLoader. * * @param classLoader the ClassLoader to load class path resources with, or {@code null} * for using the thread context class loader at the time of actual resource access */ public DefaultResourceLoader(ClassLoader classLoader) { this.classLoader = classLoader; } /** * Specify the ClassLoader to load class path resources with, or {@code null} * for using the thread context class loader at the time of actual resource access. * *

    The default is that ClassLoader access will happen using the thread context * class loader at the time of actual resource access (since 5.3). */ public void setClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; } /** * Return the ClassLoader to load class path resources with. * *

    Will get passed to ClassPathResource's constructor for all * ClassPathResource objects created by this resource loader. * * @see ClassPathResource */ @Override public ClassLoader getClassLoader() { return (this.classLoader != null ? this.classLoader : ClassUtils.getDefaultClassLoader()); } /** * Register the given resolver with this resource loader, allowing for * additional protocols to be handled. * *

    Any such resolver will be invoked ahead of this loader's standard * resolution rules. It may therefore also override any default rules. * * @see #getProtocolResolvers() * @since 4.3 */ public void addProtocolResolver(ProtocolResolver resolver) { AbstractAssert.notNull(resolver, "ProtocolResolver must not be null"); this.protocolResolvers.add(resolver); } /** * Return the collection of currently registered protocol resolvers, * allowing for introspection as well as modification. * * @since 4.3 */ public Collection getProtocolResolvers() { return this.protocolResolvers; } /** * Obtain a cache for the given value type, keyed by {@link Resource}. * * @param valueType the value type, e.g. an ASM {@code MetadataReader} * @return the cache {@link Map}, shared at the {@code ResourceLoader} level * @since 5.0 */ @SuppressWarnings("unchecked") public Map getResourceCache(Class valueType) { return (Map) this.resourceCaches.computeIfAbsent(valueType, key -> new ConcurrentHashMap<>()); } /** * Clear all resource caches in this resource loader. * * @see #getResourceCache * @since 5.0 */ public void clearResourceCaches() { this.resourceCaches.clear(); } @Override public Resource getResource(String location) { AbstractAssert.notNull(location, "Location must not be null"); for (ProtocolResolver protocolResolver : getProtocolResolvers()) { Resource resource = protocolResolver.resolve(location, this); if (resource != null) { return resource; } } if (location.startsWith("/")) { return getResourceByPath(location); } else if (location.startsWith(CLASSPATH_URL_PREFIX)) { return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader()); } else { try { // Try to parse the location as a URL... URL url = new URL(location); return (ResourceUtils.isFileUrl(url) ? new FileUrlResource(url) : new UrlResource(url)); } catch (MalformedURLException ex) { // No URL -> resolve as resource path. return getResourceByPath(location); } } } /** * Return a Resource handle for the resource at the given path. * *

    The default implementation supports class path locations. This should * be appropriate for standalone implementations but can be overridden, * e.g. for implementations targeted at a Servlet container. * * @param path the path to the resource * @return the corresponding Resource handle * @see ClassPathResource */ protected Resource getResourceByPath(String path) { return new ClassPathContextResource(path, getClassLoader()); } /** * ClassPathResource that explicitly expresses a context-relative path * through implementing the ContextResource interface. */ protected static class ClassPathContextResource extends ClassPathResource implements ContextResource { public ClassPathContextResource(String path, ClassLoader classLoader) { super(path, classLoader); } @Override public String getPathWithinContext() { return getPath(); } @Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath); return new ClassPathContextResource(pathToUse, getClassLoader()); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/FileSystemResource.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import com.alibaba.nacos.common.utils.StringUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URL; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.FileSystem; import java.nio.file.Files; import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.StandardOpenOption; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * {@link Resource} implementation for {@code java.io.File} and * {@code java.nio.file.Path} handles with a file system target. * Supports resolution as a {@code File} and also as a {@code URL}. * Implements the extended {@link WritableResource} interface. * *

    Note: As of Spring Framework 5.0, this {@link Resource} implementation uses * NIO.2 API for read/write interactions. As of 5.1, it may be constructed with a * {@link Path} handle in which case it will perform all file system * interactions via NIO.2, only resorting to {@link File} on {@link #getFile()}. * * @author Juergen Hoeller * @see #FileSystemResource(String) * @see #FileSystemResource(File) * @see #FileSystemResource(Path) * @see File * @see Files * @since 28.12.2003 */ public class FileSystemResource extends AbstractResource implements WritableResource { private final String path; private final File file; private final Path filePath; /** * Create a new {@code FileSystemResource} from a file path. * *

    Note: When building relative resources via {@link #createRelative}, * it makes a difference whether the specified resource base path here * ends with a slash or not. In the case of "C:/dir1/", relative paths * will be built underneath that root: e.g. relative path "dir2" → * "C:/dir1/dir2". In the case of "C:/dir1", relative paths will apply * at the same directory level: relative path "dir2" → "C:/dir2". * * @param path a file path * @see #FileSystemResource(Path) */ public FileSystemResource(String path) { AbstractAssert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); this.file = new File(path); this.filePath = this.file.toPath(); } /** * Create a new {@code FileSystemResource} from a {@link File} handle. * *

    Note: When building relative resources via {@link #createRelative}, * the relative path will apply at the same directory level: * e.g. new File("C:/dir1"), relative path "dir2" → "C:/dir2"! * If you prefer to have relative paths built underneath the given root directory, * use the {@link #FileSystemResource(String) constructor with a file path} * to append a trailing slash to the root path: "C:/dir1/", which indicates * this directory as root for all relative paths. * * @param file a File handle * @see #FileSystemResource(Path) * @see #getFile() */ public FileSystemResource(File file) { AbstractAssert.notNull(file, "File must not be null"); this.path = StringUtils.cleanPath(file.getPath()); this.file = file; this.filePath = file.toPath(); } /** * Create a new {@code FileSystemResource} from a {@link Path} handle, * performing all file system interactions via NIO.2 instead of {@link File}. * *

    In contrast to {@link PathResource}, this variant strictly follows the * general {@link FileSystemResource} conventions, in particular in terms of * path cleaning and {@link #createRelative(String)} handling. * *

    Note: When building relative resources via {@link #createRelative}, * the relative path will apply at the same directory level: * e.g. Paths.get("C:/dir1"), relative path "dir2" → "C:/dir2"! * If you prefer to have relative paths built underneath the given root directory, * use the {@link #FileSystemResource(String) constructor with a file path} * to append a trailing slash to the root path: "C:/dir1/", which indicates * this directory as root for all relative paths. Alternatively, consider * using {@link PathResource#PathResource(Path)} for {@code java.nio.path.Path} * resolution in {@code createRelative}, always nesting relative paths. * * @param filePath a Path handle to a file * @see #FileSystemResource(File) * @since 5.1 */ public FileSystemResource(Path filePath) { AbstractAssert.notNull(filePath, "Path must not be null"); this.path = StringUtils.cleanPath(filePath.toString()); this.file = null; this.filePath = filePath; } /** * Create a new {@code FileSystemResource} from a {@link FileSystem} handle, * locating the specified path. * *

    This is an alternative to {@link #FileSystemResource(String)}, * performing all file system interactions via NIO.2 instead of {@link File}. * * @param fileSystem the FileSystem to locate the path within * @param path a file path * @see #FileSystemResource(File) * @since 5.1.1 */ public FileSystemResource(FileSystem fileSystem, String path) { AbstractAssert.notNull(fileSystem, "FileSystem must not be null"); AbstractAssert.notNull(path, "Path must not be null"); this.path = StringUtils.cleanPath(path); this.file = null; this.filePath = fileSystem.getPath(this.path).normalize(); } /** * Return the file path for this resource. */ public final String getPath() { return this.path; } /** * This implementation returns whether the underlying file exists. * * @see File#exists() */ @Override public boolean exists() { return (this.file != null ? this.file.exists() : Files.exists(this.filePath)); } /** * This implementation checks whether the underlying file is marked as readable * (and corresponds to an actual file with content, not to a directory). * * @see File#canRead() * @see File#isDirectory() */ @Override public boolean isReadable() { return (this.file != null ? this.file.canRead() && !this.file.isDirectory() : Files.isReadable(this.filePath) && !Files.isDirectory(this.filePath)); } /** * This implementation opens a NIO file stream for the underlying file. * * @see java.io.FileInputStream */ @Override public InputStream getInputStream() throws IOException { try { return Files.newInputStream(this.filePath); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } } /** * This implementation checks whether the underlying file is marked as writable * (and corresponds to an actual file with content, not to a directory). * * @see File#canWrite() * @see File#isDirectory() */ @Override public boolean isWritable() { return (this.file != null ? this.file.canWrite() && !this.file.isDirectory() : Files.isWritable(this.filePath) && !Files.isDirectory(this.filePath)); } /** * This implementation opens a FileOutputStream for the underlying file. * * @see java.io.FileOutputStream */ @Override public OutputStream getOutputStream() throws IOException { return Files.newOutputStream(this.filePath); } /** * This implementation returns a URL for the underlying file. * * @see File#toURI() */ @Override public URL getUrl() throws IOException { return (this.file != null ? this.file.toURI().toURL() : this.filePath.toUri().toURL()); } /** * This implementation returns a URI for the underlying file. * * @see File#toURI() */ @Override public URI getUri() throws IOException { return (this.file != null ? this.file.toURI() : this.filePath.toUri()); } /** * This implementation always indicates a file. */ @Override public boolean isFile() { return true; } /** * This implementation returns the underlying File reference. */ @Override public File getFile() { return (this.file != null ? this.file : this.filePath.toFile()); } /** * This implementation opens a FileChannel for the underlying file. * * @see FileChannel */ @Override public ReadableByteChannel readableChannel() throws IOException { try { return FileChannel.open(this.filePath, StandardOpenOption.READ); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } } /** * This implementation opens a FileChannel for the underlying file. * * @see FileChannel */ @Override public WritableByteChannel writableChannel() throws IOException { return FileChannel.open(this.filePath, StandardOpenOption.WRITE); } /** * This implementation returns the underlying File/Path length. */ @Override public long contentLength() throws IOException { if (this.file != null) { long length = this.file.length(); if (length == 0L && !this.file.exists()) { throw new FileNotFoundException(getDescription() + " cannot be resolved in the file system for checking its content length"); } return length; } else { try { return Files.size(this.filePath); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } } } /** * This implementation returns the underlying File/Path last-modified time. */ @Override public long lastModified() throws IOException { if (this.file != null) { return super.lastModified(); } else { try { return Files.getLastModifiedTime(this.filePath).toMillis(); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } } } /** * This implementation creates a FileSystemResource, applying the given path * relative to the path of the underlying file of this resource descriptor. * * @see StringUtils#applyRelativePath(String, String) */ @Override public Resource createRelative(String relativePath) { String pathToUse = StringUtils.applyRelativePath(this.path, relativePath); return (this.file != null ? new FileSystemResource(pathToUse) : new FileSystemResource(this.filePath.getFileSystem(), pathToUse)); } /** * This implementation returns the name of the file. * * @see File#getName() */ @Override public String getFilename() { return (this.file != null ? this.file.getName() : this.filePath.getFileName().toString()); } /** * This implementation returns a description that includes the absolute * path of the file. * * @see File#getAbsolutePath() */ @Override public String getDescription() { return "file [" + (this.file != null ? this.file.getAbsolutePath() : this.filePath.toAbsolutePath()) + "]"; } /** * This implementation compares the underlying File references. */ @Override public boolean equals(Object other) { return (this == other || (other instanceof FileSystemResource && this.path.equals(((FileSystemResource) other).path))); } /** * This implementation returns the hash code of the underlying File reference. */ @Override public int hashCode() { return this.path.hashCode(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/FileUrlResource.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.ResourceUtils; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.channels.FileChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.Files; import java.nio.file.StandardOpenOption; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Subclass of {@link UrlResource} which assumes file resolution, to the degree * of implementing the {@link WritableResource} interface for it. This resource * variant also caches resolved {@link File} handles from {@link #getFile()}. * *

    This is the class resolved by {@link DefaultResourceLoader} for a "file:..." * URL location, allowing a downcast to {@link WritableResource} for it. * *

    Alternatively, for direct construction from a {@link File} handle * or NIO {@link java.nio.file.Path}, consider using {@link FileSystemResource}. * * @author Juergen Hoeller * @since 5.0.2 */ public class FileUrlResource extends UrlResource implements WritableResource { private volatile File file; /** * Create a new {@code FileUrlResource} based on the given URL object. * *

    Note that this does not enforce "file" as URL protocol. If a protocol * is known to be resolvable to a file, it is acceptable for this purpose. * * @param url a URL * @see ResourceUtils#isFileUrl(URL) * @see #getFile() */ public FileUrlResource(URL url) { super(url); } /** * Create a new {@code FileUrlResource} based on the given file location, * using the URL protocol "file". * *

    The given parts will automatically get encoded if necessary. * * @param location the location (i.e. the file path within that protocol) * @throws MalformedURLException if the given URL specification is not valid * @see UrlResource#UrlResource(String, String) * @see ResourceUtils#URL_PROTOCOL_FILE */ public FileUrlResource(String location) throws MalformedURLException { super(ResourceUtils.URL_PROTOCOL_FILE, location); } @Override public File getFile() throws IOException { File file = this.file; if (file != null) { return file; } file = super.getFile(); this.file = file; return file; } @Override public boolean isWritable() { try { File file = getFile(); return (file.canWrite() && !file.isDirectory()); } catch (IOException ex) { return false; } } @Override public OutputStream getOutputStream() throws IOException { return Files.newOutputStream(getFile().toPath()); } @Override public WritableByteChannel writableChannel() throws IOException { return FileChannel.open(getFile().toPath(), StandardOpenOption.WRITE); } @Override public Resource createRelative(String relativePath) throws MalformedURLException { return new FileUrlResource(createRelativeUrl(relativePath)); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/InputStreamResource.java ================================================ /* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import java.io.IOException; import java.io.InputStream; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * {@link Resource} implementation for a given {@link InputStream}. * *

    Should only be used if no other specific {@code Resource} implementation * is applicable. In particular, prefer {@link ByteArrayResource} or any of the * file-based {@code Resource} implementations where possible. * *

    In contrast to other {@code Resource} implementations, this is a descriptor * for an already opened resource - therefore returning {@code true} from * {@link #isOpen()}. Do not use an {@code InputStreamResource} if you need to * keep the resource descriptor somewhere, or if you need to read from a stream * multiple times. * * @author Juergen Hoeller * @author Sam Brannen * @see ByteArrayResource * @see ClassPathResource * @see FileSystemResource * @see UrlResource * @since 28.12.2003 */ public class InputStreamResource extends AbstractResource { private final InputStream inputStream; private final String description; private boolean read = false; /** * Create a new InputStreamResource. * * @param inputStream the InputStream to use */ public InputStreamResource(InputStream inputStream) { this(inputStream, "resource loaded through InputStream"); } /** * Create a new InputStreamResource. * * @param inputStream the InputStream to use * @param description where the InputStream comes from */ public InputStreamResource(InputStream inputStream, String description) { AbstractAssert.notNull(inputStream, "InputStream must not be null"); this.inputStream = inputStream; this.description = (description != null ? description : ""); } /** * This implementation always returns {@code true}. */ @Override public boolean exists() { return true; } /** * This implementation always returns {@code true}. */ @Override public boolean isOpen() { return true; } /** * This implementation throws IllegalStateException if attempting to * read the underlying stream multiple times. */ @Override public InputStream getInputStream() throws IOException, IllegalStateException { if (this.read) { throw new IllegalStateException("InputStream has already been read - " + "do not use InputStreamResource if a stream needs to be read multiple times"); } this.read = true; return this.inputStream; } /** * This implementation returns a description that includes the passed-in * description, if any. */ @Override public String getDescription() { return "InputStream resource [" + this.description + "]"; } /** * This implementation compares the underlying InputStream. */ @Override public boolean equals(Object other) { return (this == other || (other instanceof InputStreamResource && ((InputStreamResource) other).inputStream.equals(this.inputStream))); } /** * This implementation returns the hash code of the underlying InputStream. */ @Override public int hashCode() { return this.inputStream.hashCode(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/InputStreamSource.java ================================================ /* * Copyright 2002-2021 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 com.alibaba.nacos.common.packagescan.resource; import java.io.IOException; import java.io.InputStream; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Simple interface for objects that are sources for an {@link InputStream}. * *

    This is the base interface for Spring's more extensive {@link Resource} interface. * *

    For single-use streams, {@link InputStreamResource} can be used for any * given {@code InputStream}. Spring's {@link ByteArrayResource} or any * file-based {@code Resource} implementation can be used as a concrete * instance, allowing one to read the underlying content stream multiple times. * This makes this interface useful as an abstract content source for mail * attachments, for example. * * @author Juergen Hoeller * @see InputStream * @see Resource * @see InputStreamResource * @see ByteArrayResource * @since 20.01.2004 */ public interface InputStreamSource { /** * Return an {@link InputStream} for the content of an underlying resource. * *

    It is expected that each call creates a fresh stream. * *

    This requirement is particularly important when you consider an API such * as JavaMail, which needs to be able to read the stream multiple times when * creating mail attachments. For such a use case, it is required * that each {@code getInputStream()} call returns a fresh stream. * * @return the input stream for the underlying resource (must not be {@code null}) * @throws java.io.FileNotFoundException if the underlying resource does not exist * @throws IOException if the content stream could not be opened * @see Resource#isReadable() */ InputStream getInputStream() throws IOException; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/PathMatchingResourcePatternResolver.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.PathMatcher; import com.alibaba.nacos.common.packagescan.util.ResourceUtils; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import com.alibaba.nacos.common.utils.ClassUtils; import com.alibaba.nacos.common.utils.ReflectUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.net.JarURLConnection; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.LinkedHashSet; import java.util.Set; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.zip.ZipException; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * A {@link ResourcePatternResolver} implementation that is able to resolve a * specified resource location path into one or more matching Resources. * The source path may be a simple path which has a one-to-one mapping to a * target {@link Resource}, or alternatively * may contain the special "{@code classpath*:}" prefix and/or * internal Ant-style regular expressions (matched using Spring's * {@link AntPathMatcher} utility). * Both of the latter are effectively wildcards. * *

    No Wildcards: * *

    In the simple case, if the specified location path does not start with the * {@code "classpath*:}" prefix, and does not contain a PathMatcher pattern, * this resolver will simply return a single resource via a * {@code getResource()} call on the underlying {@code ResourceLoader}. * Examples are real URLs such as "{@code file:C:/context.xml}", pseudo-URLs * such as "{@code classpath:/context.xml}", and simple unprefixed paths * such as "{@code /WEB-INF/context.xml}". The latter will resolve in a * fashion specific to the underlying {@code ResourceLoader} (e.g. * {@code ServletContextResource} for a {@code WebApplicationContext}). * *

    Ant-style Patterns: * *

    When the path location contains an Ant-style pattern, e.g.: *

     * /WEB-INF/*-context.xml
     * com/mycompany/**/applicationContext.xml
     * file:C:/some/path/*-context.xml
     * classpath:com/mycompany/**/applicationContext.xml
    * the resolver follows a more complex but defined procedure to try to resolve * the wildcard. It produces a {@code Resource} for the path up to the last * non-wildcard segment and obtains a {@code URL} from it. If this URL is * not a "{@code jar:}" URL or container-specific variant (e.g. * "{@code zip:}" in WebLogic, "{@code wsjar}" in WebSphere", etc.), * then a {@code java.io.File} is obtained from it, and used to resolve the * wildcard by walking the filesystem. In the case of a jar URL, the resolver * either gets a {@code java.net.JarURLConnection} from it, or manually parses * the jar URL, and then traverses the contents of the jar file, to resolve the * wildcards. * *

    Implications on portability: * *

    If the specified path is already a file URL (either explicitly, or * implicitly because the base {@code ResourceLoader} is a filesystem one, * then wildcarding is guaranteed to work in a completely portable fashion. * *

    If the specified path is a classpath location, then the resolver must * obtain the last non-wildcard path segment URL via a * {@code Classloader.getResource()} call. Since this is just a * node of the path (not the file at the end) it is actually undefined * (in the ClassLoader Javadocs) exactly what sort of a URL is returned in * this case. In practice, it is usually a {@code java.io.File} representing * the directory, where the classpath resource resolves to a filesystem * location, or a jar URL of some sort, where the classpath resource resolves * to a jar location. Still, there is a portability concern on this operation. * *

    If a jar URL is obtained for the last non-wildcard segment, the resolver * must be able to get a {@code java.net.JarURLConnection} from it, or * manually parse the jar URL, to be able to walk the contents of the jar, * and resolve the wildcard. This will work in most environments, but will * fail in others, and it is strongly recommended that the wildcard * resolution of resources coming from jars be thoroughly tested in your * specific environment before you rely on it. * *

    {@code classpath*:} Prefix: * *

    There is special support for retrieving multiple class path resources with * the same name, via the "{@code classpath*:}" prefix. For example, * "{@code classpath*:META-INF/beans.xml}" will find all "beans.xml" * files in the class path, be it in "classes" directories or in JAR files. * This is particularly useful for autodetecting config files of the same name * at the same location within each jar file. Internally, this happens via a * {@code ClassLoader.getResources()} call, and is completely portable. * *

    The "classpath*:" prefix can also be combined with a PathMatcher pattern in * the rest of the location path, for example "classpath*:META-INF/*-beans.xml". * In this case, the resolution strategy is fairly simple: a * {@code ClassLoader.getResources()} call is used on the last non-wildcard * path segment to get all the matching resources in the class loader hierarchy, * and then off each resource the same PathMatcher resolution strategy described * above is used for the wildcard subpath. * *

    Other notes: * *

    WARNING: Note that "{@code classpath*:}" when combined with * Ant-style patterns will only work reliably with at least one root directory * before the pattern starts, unless the actual target files reside in the file * system. This means that a pattern like "{@code classpath*:*.xml}" will * not retrieve files from the root of jar files but rather only from the * root of expanded directories. This originates from a limitation in the JDK's * {@code ClassLoader.getResources()} method which only returns file system * locations for a passed-in empty String (indicating potential roots to search). * This {@code ResourcePatternResolver} implementation is trying to mitigate the * jar root lookup limitation through {@link URLClassLoader} introspection and * "java.class.path" manifest evaluation; however, without portability guarantees. * *

    WARNING: Ant-style patterns with "classpath:" resources are not * guaranteed to find matching resources if the root package to search is available * in multiple class path locations. This is because a resource such as *

     *     com/mycompany/package1/service-context.xml
     * 
    * may be in only one location, but when a path such as *
     *     classpath:com/mycompany/**/service-context.xml
     * 
    * is used to try to resolve it, the resolver will work off the (first) URL * returned by {@code getResource("com/mycompany");}. If this base package node * exists in multiple classloader locations, the actual end resource may not be * underneath. Therefore, preferably, use "{@code classpath*:}" with the same * Ant-style pattern in such a case, which will search all class path * locations that contain the root package. * * @author Juergen Hoeller * @author Colin Sampaleanu * @author Marius Bogoevici * @author Costin Leau * @author Phillip Webb * @see #CLASSPATH_ALL_URL_PREFIX * @see AntPathMatcher * @see ResourceLoader#getResource(String) * @see ClassLoader#getResources(String) * @since 1.0.2 */ public class PathMatchingResourcePatternResolver implements ResourcePatternResolver { private static final Logger LOGGER = LoggerFactory.getLogger(PathMatchingResourcePatternResolver.class); private static Method equinoxResolveMethod; static { try { // Detect Equinox OSGi (e.g. on WebSphere 6.1) Class fileLocatorClass = ClassUtils.forName("org.eclipse.core.runtime.FileLocator", PathMatchingResourcePatternResolver.class.getClassLoader()); equinoxResolveMethod = fileLocatorClass.getMethod("resolve", URL.class); LOGGER.trace("Found Equinox FileLocator for OSGi bundle URL resolution"); } catch (Throwable ex) { equinoxResolveMethod = null; } } private final ResourceLoader resourceLoader; private PathMatcher pathMatcher = new AntPathMatcher(); /** * Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader. * *

    ClassLoader access will happen via the thread context class loader. * * @see DefaultResourceLoader */ public PathMatchingResourcePatternResolver() { this.resourceLoader = new DefaultResourceLoader(); } /** * Create a new PathMatchingResourcePatternResolver. * *

    ClassLoader access will happen via the thread context class loader. * * @param resourceLoader the ResourceLoader to load root directories and * actual resources with */ public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) { AbstractAssert.notNull(resourceLoader, "ResourceLoader must not be null"); this.resourceLoader = resourceLoader; } /** * Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader. * * @param classLoader the ClassLoader to load classpath resources with, * or {@code null} for using the thread context class loader * at the time of actual resource access * @see DefaultResourceLoader */ public PathMatchingResourcePatternResolver(ClassLoader classLoader) { this.resourceLoader = new DefaultResourceLoader(classLoader); } /** * Return the ResourceLoader that this pattern resolver works with. */ public ResourceLoader getResourceLoader() { return this.resourceLoader; } @Override public ClassLoader getClassLoader() { return getResourceLoader().getClassLoader(); } /** * Set the PathMatcher implementation to use for this * resource pattern resolver. Default is AntPathMatcher. * * @see AntPathMatcher */ public void setPathMatcher(PathMatcher pathMatcher) { AbstractAssert.notNull(pathMatcher, "PathMatcher must not be null"); this.pathMatcher = pathMatcher; } /** * Return the PathMatcher that this resource pattern resolver uses. */ public PathMatcher getPathMatcher() { return this.pathMatcher; } @Override public Resource getResource(String location) { return getResourceLoader().getResource(location); } @Override public Resource[] getResources(String locationPattern) throws IOException { AbstractAssert.notNull(locationPattern, "Location pattern must not be null"); if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) { // a class path resource (multiple resources for same name possible) if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) { // a class path resource pattern return findPathMatchingResources(locationPattern); } else { // all class path resources with the given name return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length())); } } else { // Generally only look for a pattern after a prefix here, // and on Tomcat only after the "*/" separator for its "war:" protocol. int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(':') + 1); if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); } else { // a single resource with the given name return new Resource[]{getResourceLoader().getResource(locationPattern)}; } } } /** * Find all class location resources with the given location via the ClassLoader. * Delegates to {@link #doFindAllClassPathResources(String)}. * * @param location the absolute path within the classpath * @return the result as Resource array * @throws IOException in case of I/O errors * @see ClassLoader#getResources * @see #convertClassLoaderUrl */ protected Resource[] findAllClassPathResources(String location) throws IOException { String path = location; if (path.startsWith("/")) { path = path.substring(1); } Set result = doFindAllClassPathResources(path); if (LOGGER.isTraceEnabled()) { LOGGER.trace("Resolved classpath location [" + location + "] to resources " + result); } return result.toArray(new Resource[0]); } /** * Find all class location resources with the given path via the ClassLoader. * Called by {@link #findAllClassPathResources(String)}. * * @param path the absolute path within the classpath (never a leading slash) * @return a mutable Set of matching Resource instances * @since 4.1.1 */ protected Set doFindAllClassPathResources(String path) throws IOException { Set result = new LinkedHashSet<>(16); ClassLoader cl = getClassLoader(); Enumeration resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path)); while (resourceUrls.hasMoreElements()) { URL url = resourceUrls.nextElement(); result.add(convertClassLoaderUrl(url)); } if (!StringUtils.hasLength(path)) { // The above result is likely to be incomplete, i.e. only containing file system references. // We need to have pointers to each of the jar files on the classpath as well... addAllClassLoaderJarRoots(cl, result); } return result; } /** * Convert the given URL as returned from the ClassLoader into a {@link Resource}. * *

    The default implementation simply creates a {@link UrlResource} instance. * * @param url a URL as returned from the ClassLoader * @return the corresponding Resource object * @see ClassLoader#getResources * @see Resource */ protected Resource convertClassLoaderUrl(URL url) { return new UrlResource(url); } /** * Search all {@link URLClassLoader} URLs for jar file references and add them to the * given set of resources in the form of pointers to the root of the jar file content. * * @param classLoader the ClassLoader to search (including its ancestors) * @param result the set of resources to add jar roots to * @since 4.1.1 */ protected void addAllClassLoaderJarRoots(ClassLoader classLoader, Set result) { if (classLoader instanceof URLClassLoader) { try { for (URL url : ((URLClassLoader) classLoader).getURLs()) { try { UrlResource jarResource = (ResourceUtils.URL_PROTOCOL_JAR.equals(url.getProtocol()) ? new UrlResource(url) : new UrlResource(ResourceUtils.JAR_URL_PREFIX + url + ResourceUtils.JAR_URL_SEPARATOR)); if (jarResource.exists()) { result.add(jarResource); } } catch (MalformedURLException ex) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot search for matching files underneath [" + url + "] because it cannot be converted to a valid 'jar:' URL: " + ex.getMessage()); } } } } catch (Exception ex) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot introspect jar files since ClassLoader [" + classLoader + "] does not support 'getURLs()': " + ex); } } } if (classLoader == ClassLoader.getSystemClassLoader()) { // "java.class.path" manifest evaluation... addClassPathManifestEntries(result); } if (classLoader != null) { try { // Hierarchy traversal... addAllClassLoaderJarRoots(classLoader.getParent(), result); } catch (Exception ex) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot introspect jar files in parent ClassLoader since [" + classLoader + "] does not support 'getParent()': " + ex); } } } } /** * Determine jar file references from the "java.class.path." manifest property and add them * to the given set of resources in the form of pointers to the root of the jar file content. * * @param result the set of resources to add jar roots to * @since 4.3 */ protected void addClassPathManifestEntries(Set result) { try { String javaClassPathProperty = System.getProperty("java.class.path"); for (String path : StringUtils.delimitedListToStringArray( javaClassPathProperty, System.getProperty("path.separator"))) { try { String filePath = new File(path).getAbsolutePath(); int prefixIndex = filePath.indexOf(':'); if (prefixIndex == 1) { // Possibly "c:" drive prefix on Windows, to be upper-cased for proper duplicate detection filePath = StringUtils.capitalize(filePath); } // # can appear in directories/filenames, java.net.URL should not treat it as a fragment filePath = StringUtils.replace(filePath, "#", "%23"); // Build URL that points to the root of the jar file UrlResource jarResource = new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + filePath + ResourceUtils.JAR_URL_SEPARATOR); // Potentially overlapping with URLClassLoader.getURLs() result above! if (!result.contains(jarResource) && !hasDuplicate(filePath, result) && jarResource.exists()) { result.add(jarResource); } } catch (MalformedURLException ex) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot search for matching files underneath [" + path + "] because it cannot be converted to a valid 'jar:' URL: " + ex.getMessage()); } } } } catch (Exception ex) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Failed to evaluate 'java.class.path' manifest entries: " + ex); } } } /** * Check whether the given file path has a duplicate but differently structured entry * in the existing result, i.e. with or without a leading slash. * * @param filePath the file path (with or without a leading slash) * @param result the current result * @return {@code true} if there is a duplicate (i.e. to ignore the given file path), * {@code false} to proceed with adding a corresponding resource to the current result */ private boolean hasDuplicate(String filePath, Set result) { if (result.isEmpty()) { return false; } String duplicatePath = (filePath.startsWith("/") ? filePath.substring(1) : "/" + filePath); try { return result.contains(new UrlResource(ResourceUtils.JAR_URL_PREFIX + ResourceUtils.FILE_URL_PREFIX + duplicatePath + ResourceUtils.JAR_URL_SEPARATOR)); } catch (MalformedURLException ex) { // Ignore: just for testing against duplicate. return false; } } /** * Find all resources that match the given location pattern via the * Ant-style PathMatcher. Supports resources in jar files and zip files * and in the file system. * * @param locationPattern the location pattern to match * @return the result as Resource array * @throws IOException in case of I/O errors * @see #doFindPathMatchingJarResources * @see #doFindPathMatchingFileResources * @see PathMatcher */ protected Resource[] findPathMatchingResources(String locationPattern) throws IOException { String rootDirPath = determineRootDir(locationPattern); String subPattern = locationPattern.substring(rootDirPath.length()); Resource[] rootDirResources = getResources(rootDirPath); Set result = new LinkedHashSet<>(16); for (Resource rootDirResource : rootDirResources) { rootDirResource = resolveRootDirResource(rootDirResource); URL rootDirUrl = rootDirResource.getUrl(); if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) { URL resolvedUrl = (URL) ReflectUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl); if (resolvedUrl != null) { rootDirUrl = resolvedUrl; } rootDirResource = new UrlResource(rootDirUrl); } if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) { result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher())); } else if (ResourceUtils.isJarUrl(rootDirUrl) || isJarResource(rootDirResource)) { result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern)); } else { result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern)); } } if (LOGGER.isTraceEnabled()) { LOGGER.trace("Resolved location pattern [" + locationPattern + "] to resources " + result); } return result.toArray(new Resource[0]); } /** * Determine the root directory for the given location. * *

    Used for determining the starting point for file matching, * resolving the root directory location to a {@code java.io.File} * and passing it into {@code retrieveMatchingFiles}, with the * remainder of the location as pattern. * *

    Will return "/WEB-INF/" for the pattern "/WEB-INF/*.xml", * for example. * * @param location the location to check * @return the part of the location that denotes the root directory * @see #retrieveMatchingFiles */ protected String determineRootDir(String location) { int prefixEnd = location.indexOf(':') + 1; int rootDirEnd = location.length(); while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) { rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1; } if (rootDirEnd == 0) { rootDirEnd = prefixEnd; } return location.substring(0, rootDirEnd); } /** * Resolve the specified resource for path matching. * *

    By default, Equinox OSGi "bundleresource:" / "bundleentry:" URL will be * resolved into a standard jar file URL that be traversed using Spring's * standard jar file traversal algorithm. For any preceding custom resolution, * override this method and replace the resource handle accordingly. * * @param original the resource to resolve * @return the resolved resource (may be identical to the passed-in resource) * @throws IOException in case of resolution failure */ protected Resource resolveRootDirResource(Resource original) throws IOException { return original; } /** * Return whether the given resource handle indicates a jar resource * that the {@code doFindPathMatchingJarResources} method can handle. * *

    By default, the URL protocols "jar", "zip", "vfszip and "wsjar" * will be treated as jar resources. This template method allows for * detecting further kinds of jar-like resources, e.g. through * {@code instanceof} checks on the resource handle type. * * @param resource the resource handle to check * (usually the root directory to start path matching from) * @see #doFindPathMatchingJarResources * @see ResourceUtils#isJarUrl */ protected boolean isJarResource(Resource resource) throws IOException { return false; } /** * Find all resources in jar files that match the given location pattern * via the Ant-style PathMatcher. * * @param rootDirResource the root directory as Resource * @param rootDirUrl the pre-resolved root directory URL * @param subPattern the sub pattern to match (below the root directory) * @return a mutable Set of matching Resource instances * @throws IOException in case of I/O errors * @see PathMatcher * @since 4.3 */ protected Set doFindPathMatchingJarResources(Resource rootDirResource, URL rootDirUrl, String subPattern) throws IOException { URLConnection con = rootDirUrl.openConnection(); JarFile jarFile; String jarFileUrl; String rootEntryPath; boolean closeJarFile; if (con instanceof JarURLConnection) { // Should usually be the case for traditional JAR files. JarURLConnection jarCon = (JarURLConnection) con; ResourceUtils.useCachesIfNecessary(jarCon); jarFile = jarCon.getJarFile(); jarFileUrl = jarCon.getJarFileURL().toExternalForm(); JarEntry jarEntry = jarCon.getJarEntry(); rootEntryPath = (jarEntry != null ? jarEntry.getName() : ""); closeJarFile = !jarCon.getUseCaches(); } else { // No JarURLConnection -> need to resort to URL file parsing. // We'll assume URLs of the format "jar:path!/entry", with the protocol // being arbitrary as long as following the entry format. // We'll also handle paths with and without leading "file:" prefix. String urlFile = rootDirUrl.getFile(); try { int separatorIndex = urlFile.indexOf(ResourceUtils.WAR_URL_SEPARATOR); if (separatorIndex == -1) { separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR); } if (separatorIndex != -1) { jarFileUrl = urlFile.substring(0, separatorIndex); rootEntryPath = urlFile.substring(separatorIndex + 2); // both separators are 2 chars jarFile = getJarFile(jarFileUrl); } else { jarFile = new JarFile(urlFile); jarFileUrl = urlFile; rootEntryPath = ""; } closeJarFile = true; } catch (ZipException ex) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skipping invalid jar classpath entry [" + urlFile + "]"); } return Collections.emptySet(); } } try { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Looking for matching resources in jar file [" + jarFileUrl + "]"); } if (StringUtils.hasLength(rootEntryPath) && !rootEntryPath.endsWith("/")) { // Root entry path must end with slash to allow for proper matching. // The Sun JRE does not return a slash here, but BEA JRockit does. rootEntryPath = rootEntryPath + "/"; } Set result = new LinkedHashSet<>(8); for (Enumeration entries = jarFile.entries(); entries.hasMoreElements(); ) { JarEntry entry = entries.nextElement(); String entryPath = entry.getName(); if (entryPath.startsWith(rootEntryPath)) { String relativePath = entryPath.substring(rootEntryPath.length()); if (getPathMatcher().match(subPattern, relativePath)) { result.add(rootDirResource.createRelative(relativePath)); } } } return result; } finally { if (closeJarFile) { jarFile.close(); } } } /** * Resolve the given jar file URL into a JarFile object. */ protected JarFile getJarFile(String jarFileUrl) throws IOException { if (jarFileUrl.startsWith(ResourceUtils.FILE_URL_PREFIX)) { try { return new JarFile(ResourceUtils.toUri(jarFileUrl).getSchemeSpecificPart()); } catch (URISyntaxException ex) { // Fallback for URLs that are not valid URIs (should hardly ever happen). return new JarFile(jarFileUrl.substring(ResourceUtils.FILE_URL_PREFIX.length())); } } else { return new JarFile(jarFileUrl); } } /** * Find all resources in the file system that match the given location pattern * via the Ant-style PathMatcher. * * @param rootDirResource the root directory as Resource * @param subPattern the sub pattern to match (below the root directory) * @return a mutable Set of matching Resource instances * @throws IOException in case of I/O errors * @see #retrieveMatchingFiles * @see PathMatcher */ protected Set doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException { File rootDir; try { rootDir = rootDirResource.getFile().getAbsoluteFile(); } catch (FileNotFoundException ex) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Cannot search for matching files underneath " + rootDirResource + " in the file system: " + ex.getMessage()); } return Collections.emptySet(); } catch (Exception ex) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Failed to resolve " + rootDirResource + " in the file system: " + ex); } return Collections.emptySet(); } return doFindMatchingFileSystemResources(rootDir, subPattern); } /** * Find all resources in the file system that match the given location pattern * via the Ant-style PathMatcher. * * @param rootDir the root directory in the file system * @param subPattern the sub pattern to match (below the root directory) * @return a mutable Set of matching Resource instances * @throws IOException in case of I/O errors * @see #retrieveMatchingFiles * @see PathMatcher */ protected Set doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Looking for matching resources in directory tree [" + rootDir.getPath() + "]"); } Set matchingFiles = retrieveMatchingFiles(rootDir, subPattern); Set result = new LinkedHashSet<>(matchingFiles.size()); for (File file : matchingFiles) { result.add(new FileSystemResource(file)); } return result; } /** * Retrieve files that match the given path pattern, * checking the given directory and its subdirectories. * * @param rootDir the directory to start from * @param pattern the pattern to match against, * relative to the root directory * @return a mutable Set of matching Resource instances * @throws IOException if directory contents could not be retrieved */ protected Set retrieveMatchingFiles(File rootDir, String pattern) throws IOException { if (!rootDir.exists()) { // Silently skip non-existing directories. if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skipping [" + rootDir.getAbsolutePath() + "] because it does not exist"); } return Collections.emptySet(); } if (!rootDir.isDirectory()) { // Complain louder if it exists but is no directory. if (LOGGER.isInfoEnabled()) { LOGGER.info("Skipping [" + rootDir.getAbsolutePath() + "] because it does not denote a directory"); } return Collections.emptySet(); } if (!rootDir.canRead()) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Skipping search for matching files underneath directory [" + rootDir.getAbsolutePath() + "] because the application is not allowed to read the directory"); } return Collections.emptySet(); } String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/"); if (!pattern.startsWith("/")) { fullPattern += "/"; } fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/"); Set result = new LinkedHashSet<>(8); doRetrieveMatchingFiles(fullPattern, rootDir, result); return result; } /** * Recursively retrieve files that match the given pattern, * adding them to the given result list. * * @param fullPattern the pattern to match against, * with prepended root directory path * @param dir the current directory * @param result the Set of matching File instances to add to * @throws IOException if directory contents could not be retrieved */ protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set result) throws IOException { if (LOGGER.isTraceEnabled()) { LOGGER.trace("Searching directory [" + dir.getAbsolutePath() + "] for files matching pattern [" + fullPattern + "]"); } for (File content : listDirectory(dir)) { String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/"); if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) { if (!content.canRead()) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("Skipping subdirectory [" + dir.getAbsolutePath() + "] because the application is not allowed to read the directory"); } } else { doRetrieveMatchingFiles(fullPattern, content, result); } } if (getPathMatcher().match(fullPattern, currPath)) { result.add(content); } } } /** * Determine a sorted list of files in the given directory. * * @param dir the directory to introspect * @return the sorted list of files (by default in alphabetical order) * @see File#listFiles() * @since 5.1 */ protected File[] listDirectory(File dir) { File[] files = dir.listFiles(); if (files == null) { if (LOGGER.isInfoEnabled()) { LOGGER.info("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]"); } return new File[0]; } Arrays.sort(files, Comparator.comparing(File::getName)); return files; } /** * Inner delegate class, avoiding a hard JBoss VFS API dependency at runtime. */ private static class VfsResourceMatchingDelegate { public static Set findMatchingResources( URL rootDirUrl, String locationPattern, PathMatcher pathMatcher) throws IOException { Object root = VfsPatternUtils.findRoot(rootDirUrl); PatternVirtualFileVisitor visitor = new PatternVirtualFileVisitor(VfsPatternUtils.getPath(root), locationPattern, pathMatcher); VfsPatternUtils.visit(root, visitor); return visitor.getResources(); } } /** * VFS visitor for path matching purposes. */ @SuppressWarnings("unused") private static class PatternVirtualFileVisitor implements InvocationHandler { private final String subPattern; private final PathMatcher pathMatcher; private final String rootPath; private final Set resources = new LinkedHashSet<>(); public PatternVirtualFileVisitor(String rootPath, String subPattern, PathMatcher pathMatcher) { this.subPattern = subPattern; this.pathMatcher = pathMatcher; this.rootPath = (rootPath.isEmpty() || rootPath.endsWith("/") ? rootPath : rootPath + "/"); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); if (Object.class == method.getDeclaringClass()) { if ("equals".equals(methodName)) { // Only consider equal when proxies are identical. return (proxy == args[0]); } else if ("hashCode".equals(methodName)) { return System.identityHashCode(proxy); } } else if ("getAttributes".equals(methodName)) { return getAttributes(); } else if ("visit".equals(methodName)) { visit(args[0]); return null; } else if ("toString".equals(methodName)) { return toString(); } throw new IllegalStateException("Unexpected method invocation: " + method); } public void visit(Object vfsResource) { if (this.pathMatcher.match(this.subPattern, VfsPatternUtils.getPath(vfsResource).substring(this.rootPath.length()))) { this.resources.add(new VfsResource(vfsResource)); } } public Object getAttributes() { return VfsPatternUtils.getVisitorAttributes(); } public Set getResources() { return this.resources; } public int size() { return this.resources.size(); } @Override public String toString() { return "sub-pattern: " + this.subPattern + ", resources: " + this.resources; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/PathResource.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URL; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.NoSuchFileException; import java.nio.file.OpenOption; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * {@link Resource} implementation for {@link Path} handles, * performing all operations and transformations via the {@code Path} API. * Supports resolution as a {@link File} and also as a {@link URL}. * Implements the extended {@link WritableResource} interface. * *

    Note: As of 5.1, {@link Path} support is also available * in {@link FileSystemResource#FileSystemResource(Path) FileSystemResource}, * applying Spring's standard String-based path transformations but * performing all operations via the {@link Files} API. * This {@code PathResource} is effectively a pure {@code java.nio.path.Path} * based alternative with different {@code createRelative} behavior. * * @author Philippe Marschall * @author Juergen Hoeller * @see Path * @see Files * @see FileSystemResource * @since 4.0 */ public class PathResource extends AbstractResource implements WritableResource { private final Path path; /** * Create a new PathResource from a Path handle. * *

    Note: Unlike {@link FileSystemResource}, when building relative resources * via {@link #createRelative}, the relative path will be built underneath * the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" → "C:/dir1/dir2"! * * @param path a Path handle */ public PathResource(Path path) { AbstractAssert.notNull(path, "Path must not be null"); this.path = path.normalize(); } /** * Create a new PathResource from a Path handle. * *

    Note: Unlike {@link FileSystemResource}, when building relative resources * via {@link #createRelative}, the relative path will be built underneath * the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" → "C:/dir1/dir2"! * * @param path a path * @see Paths#get(String, String...) */ public PathResource(String path) { AbstractAssert.notNull(path, "Path must not be null"); this.path = Paths.get(path).normalize(); } /** * Create a new PathResource from a Path handle. * *

    Note: Unlike {@link FileSystemResource}, when building relative resources * via {@link #createRelative}, the relative path will be built underneath * the given root: e.g. Paths.get("C:/dir1/"), relative path "dir2" → "C:/dir1/dir2"! * * @param uri a path URI * @see Paths#get(URI) */ public PathResource(URI uri) { AbstractAssert.notNull(uri, "URI must not be null"); this.path = Paths.get(uri).normalize(); } /** * Return the file path for this resource. */ public final String getPath() { return this.path.toString(); } /** * This implementation returns whether the underlying file exists. * * @see Files#exists(Path, LinkOption...) */ @Override public boolean exists() { return Files.exists(this.path); } /** * This implementation checks whether the underlying file is marked as readable * (and corresponds to an actual file with content, not to a directory). * * @see Files#isReadable(Path) * @see Files#isDirectory(Path, LinkOption...) */ @Override public boolean isReadable() { return (Files.isReadable(this.path) && !Files.isDirectory(this.path)); } /** * This implementation opens a InputStream for the underlying file. * * @see java.nio.file.spi.FileSystemProvider#newInputStream(Path, OpenOption...) */ @Override public InputStream getInputStream() throws IOException { if (!exists()) { throw new FileNotFoundException(getPath() + " (no such file or directory)"); } if (Files.isDirectory(this.path)) { throw new FileNotFoundException(getPath() + " (is a directory)"); } return Files.newInputStream(this.path); } /** * This implementation checks whether the underlying file is marked as writable * (and corresponds to an actual file with content, not to a directory). * * @see Files#isWritable(Path) * @see Files#isDirectory(Path, LinkOption...) */ @Override public boolean isWritable() { return (Files.isWritable(this.path) && !Files.isDirectory(this.path)); } /** * This implementation opens a OutputStream for the underlying file. * * @see java.nio.file.spi.FileSystemProvider#newOutputStream(Path, OpenOption...) */ @Override public OutputStream getOutputStream() throws IOException { if (Files.isDirectory(this.path)) { throw new FileNotFoundException(getPath() + " (is a directory)"); } return Files.newOutputStream(this.path); } /** * This implementation returns a URL for the underlying file. * * @see Path#toUri() * @see URI#toURL() */ @Override public URL getUrl() throws IOException { return this.path.toUri().toURL(); } /** * This implementation returns a URI for the underlying file. * * @see Path#toUri() */ @Override public URI getUri() throws IOException { return this.path.toUri(); } /** * This implementation always indicates a file. */ @Override public boolean isFile() { return true; } /** * This implementation returns the underlying File reference. */ @Override public File getFile() throws IOException { try { return this.path.toFile(); } catch (UnsupportedOperationException ex) { // Only paths on the default file system can be converted to a File: // Do exception translation for cases where conversion is not possible. throw new FileNotFoundException(this.path + " cannot be resolved to absolute file path"); } } /** * This implementation opens a Channel for the underlying file. * * @see Files#newByteChannel(Path, OpenOption...) */ @Override public ReadableByteChannel readableChannel() throws IOException { try { return Files.newByteChannel(this.path, StandardOpenOption.READ); } catch (NoSuchFileException ex) { throw new FileNotFoundException(ex.getMessage()); } } /** * This implementation opens a Channel for the underlying file. * * @see Files#newByteChannel(Path, OpenOption...) */ @Override public WritableByteChannel writableChannel() throws IOException { return Files.newByteChannel(this.path, StandardOpenOption.WRITE); } /** * This implementation returns the underlying file's length. */ @Override public long contentLength() throws IOException { return Files.size(this.path); } /** * This implementation returns the underlying File's timestamp. * * @see Files#getLastModifiedTime(Path, LinkOption...) */ @Override public long lastModified() throws IOException { // We can not use the superclass method since it uses conversion to a File and // only a Path on the default file system can be converted to a File... return Files.getLastModifiedTime(this.path).toMillis(); } /** * This implementation creates a PathResource, applying the given path * relative to the path of the underlying file of this resource descriptor. * * @see Path#resolve(String) */ @Override public Resource createRelative(String relativePath) { return new PathResource(this.path.resolve(relativePath)); } /** * This implementation returns the name of the file. * * @see Path#getFileName() */ @Override public String getFilename() { return this.path.getFileName().toString(); } @Override public String getDescription() { return "path [" + this.path.toAbsolutePath() + "]"; } /** * This implementation compares the underlying Path references. */ @Override public boolean equals(Object other) { return (this == other || (other instanceof PathResource && this.path.equals(((PathResource) other).path))); } /** * This implementation returns the hash code of the underlying Path reference. */ @Override public int hashCode() { return this.path.hashCode(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/ProtocolResolver.java ================================================ /* * Copyright 2002-2016 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 com.alibaba.nacos.common.packagescan.resource; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * A resolution strategy for protocol-specific resource handles. * *

    Used as an SPI for {@link DefaultResourceLoader}, allowing for * custom protocols to be handled without subclassing the loader * implementation (or application context implementation). * * @author Juergen Hoeller * @see DefaultResourceLoader#addProtocolResolver * @since 4.3 */ @FunctionalInterface public interface ProtocolResolver { /** * Resolve the given location against the given resource loader * if this implementation's protocol matches. * * @param location the user-specified resource location * @param resourceLoader the associated resource loader * @return a corresponding {@code Resource} handle if the given location * matches this resolver's protocol, or {@code null} otherwise */ Resource resolve(String location, ResourceLoader resourceLoader); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/Resource.java ================================================ /* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.common.packagescan.resource; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URL; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Interface for a resource descriptor that abstracts from the actual * type of underlying resource, such as a file or class path resource. * *

    An InputStream can be opened for every resource if it exists in * physical form, but a URL or File handle can just be returned for * certain resources. The actual behavior is implementation-specific. * * @author Juergen Hoeller * @see #getInputStream() * @see #getUrl() * @see #getUri() * @see #getFile() * @see WritableResource * @see ContextResource * @see UrlResource * @see FileUrlResource * @see FileSystemResource * @see ClassPathResource * @see ByteArrayResource * @see InputStreamResource * @since 28.12.2003 */ public interface Resource extends InputStreamSource { /** * Determine whether this resource actually exists in physical form. * *

    This method performs a definitive existence check, whereas the * existence of a {@code Resource} handle only guarantees a valid * descriptor handle. */ boolean exists(); /** * Indicate whether non-empty contents of this resource can be read via * {@link #getInputStream()}. * *

    Will be {@code true} for typical resource descriptors that exist * since it strictly implies {@link #exists()} semantics as of 5.1. * Note that actual content reading may still fail when attempted. * However, a value of {@code false} is a definitive indication * that the resource content cannot be read. * * @see #getInputStream() * @see #exists() */ default boolean isReadable() { return exists(); } /** * Indicate whether this resource represents a handle with an open stream. * If {@code true}, the InputStream cannot be read multiple times, * and must be read and closed to avoid resource leaks. * *

    Will be {@code false} for typical resource descriptors. */ default boolean isOpen() { return false; } /** * Determine whether this resource represents a file in a file system. * A value of {@code true} strongly suggests (but does not guarantee) * that a {@link #getFile()} call will succeed. * *

    This is conservatively {@code false} by default. * * @see #getFile() * @since 5.0 */ default boolean isFile() { return false; } /** * Return a URL handle for this resource. * * @throws IOException if the resource cannot be resolved as URL, * i.e. if the resource is not available as descriptor */ URL getUrl() throws IOException; /** * Return a URI handle for this resource. * * @throws IOException if the resource cannot be resolved as URI, * i.e. if the resource is not available as descriptor * @since 2.5 */ URI getUri() throws IOException; /** * Return a File handle for this resource. * * @throws java.io.FileNotFoundException if the resource cannot be resolved as * absolute file path, i.e. if the resource is not available in a file system * @throws IOException in case of general resolution/reading failures * @see #getInputStream() */ File getFile() throws IOException; /** * Return a {@link ReadableByteChannel}. * *

    It is expected that each call creates a fresh channel. * *

    The default implementation returns {@link Channels#newChannel(InputStream)} * with the result of {@link #getInputStream()}. * * @return the byte channel for the underlying resource (must not be {@code null}) * @throws java.io.FileNotFoundException if the underlying resource doesn't exist * @throws IOException if the content channel could not be opened * @see #getInputStream() * @since 5.0 */ default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); } /** * Determine the content length for this resource. * * @throws IOException if the resource cannot be resolved * (in the file system or as some other known physical resource type) */ long contentLength() throws IOException; /** * Determine the last-modified timestamp for this resource. * * @throws IOException if the resource cannot be resolved * (in the file system or as some other known physical resource type) */ long lastModified() throws IOException; /** * Create a resource relative to this resource. * * @param relativePath the relative path (relative to this resource) * @return the resource handle for the relative resource * @throws IOException if the relative resource cannot be determined */ Resource createRelative(String relativePath) throws IOException; /** * Determine a filename for this resource, i.e. typically the last * part of the path: for example, "myfile.txt". * *

    Returns {@code null} if this type of resource does not * have a filename. */ String getFilename(); /** * Return a description for this resource, * to be used for error output when working with the resource. * *

    Implementations are also encouraged to return this value * from their {@code toString} method. * * @see Object#toString() */ String getDescription(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/ResourceLoader.java ================================================ /* * Copyright 2002-2021 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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.ResourceUtils; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Strategy interface for loading resources (e.g., class path or file system * resources). * *

    {@link DefaultResourceLoader} is a standalone implementation * *

    Bean properties of type {@code Resource} and {@code Resource[]} can be populated * from Strings when running in an ApplicationContext, using the particular * context's resource loading strategy. * * @author Juergen Hoeller * @see Resource * @see ResourcePatternResolver * @since 10.03.2004 */ public interface ResourceLoader { /** * Pseudo URL prefix for loading from the class path: "classpath:". */ String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX; /** * Return a {@code Resource} handle for the specified resource location. * *

    The handle should always be a reusable resource descriptor, * allowing for multiple {@link Resource#getInputStream()} calls. * *

      *
    • Must support fully qualified URLs, e.g. "file:C:/test.dat". *
    • Must support classpath pseudo-URLs, e.g. "classpath:test.dat". *
    • Should support relative file paths, e.g. "WEB-INF/test.dat". * (This will be implementation-specific, typically provided by an * ApplicationContext implementation.) *
    * *

    Note that a {@code Resource} handle does not imply an existing resource; * you need to invoke {@link Resource#exists} to check for existence. * * @param location the resource location * @return a corresponding {@code Resource} handle (never {@code null}) * @see #CLASSPATH_URL_PREFIX * @see Resource#exists() * @see Resource#getInputStream() */ Resource getResource(String location); /** * Expose the {@link ClassLoader} used by this {@code ResourceLoader}. * *

    Clients which need to access the {@code ClassLoader} directly can do so * in a uniform manner with the {@code ResourceLoader}, rather than relying * on the thread context {@code ClassLoader}. * * @return the {@code ClassLoader} * (only {@code null} if even the system {@code ClassLoader} isn't accessible) */ ClassLoader getClassLoader(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/ResourcePatternResolver.java ================================================ /* * Copyright 2002-2021 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 com.alibaba.nacos.common.packagescan.resource; import java.io.IOException; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Strategy interface for resolving a location pattern (for example, * an Ant-style path pattern) into {@link Resource} objects. * *

    This is an extension to the {@link ResourceLoader} * interface. A passed-in {@code ResourceLoader} (for example, an * can be checked whether it implements this extended interface too. * *

    {@link PathMatchingResourcePatternResolver} is a standalone implementation * that is usable outside an {@code ApplicationContext}, also used by * properties. * *

    Can be used with any sort of location pattern (e.g. "/WEB-INF/*-context.xml"): * Input patterns have to match the strategy implementation. This interface just * specifies the conversion method rather than a specific pattern format. * *

    This interface also suggests a new resource prefix "classpath*:" for all * matching resources from the class path. Note that the resource location is * expected to be a path without placeholders in this case (e.g. "/beans.xml"); * JAR files or different directories in the class path can contain multiple files * of the same name. * * @author Juergen Hoeller * @see Resource * @see ResourceLoader * @since 1.0.2 */ public interface ResourcePatternResolver extends ResourceLoader { /** * Pseudo URL prefix for all matching resources from the class path: "classpath*:" * *

    This differs from ResourceLoader's classpath URL prefix in that it * retrieves all matching resources for a given name (e.g. "/beans.xml"), * for example in the root of all deployed JAR files. * * @see ResourceLoader#CLASSPATH_URL_PREFIX */ String CLASSPATH_ALL_URL_PREFIX = "classpath*:"; /** * Resolve the given location pattern into {@code Resource} objects. * *

    Overlapping resource entries that point to the same physical * resource should be avoided, as far as possible. The result should * have set semantics. * * @param locationPattern the location pattern to resolve * @return the corresponding {@code Resource} objects * @throws IOException in case of I/O errors */ Resource[] getResources(String locationPattern) throws IOException; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/UrlResource.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.ResourceUtils; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import com.alibaba.nacos.common.utils.StringUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * {@link Resource} implementation for {@code java.net.URL} locators. * Supports resolution as a {@code URL} and also as a {@code File} in * case of the {@code "file:"} protocol. * * @author Juergen Hoeller * @see URL * @since 28.12.2003 */ public class UrlResource extends AbstractFileResolvingResource { /** * Original URI, if available; used for URI and File access. */ private final URI uri; /** * Original URL, used for actual access. */ private final URL url; /** * Cleaned URL (with normalized path), used for comparisons. */ private volatile URL cleanedUrl; /** * Create a new {@code UrlResource} based on the given URI object. * * @param uri a URI * @throws MalformedURLException if the given URL path is not valid * @since 2.5 */ public UrlResource(URI uri) throws MalformedURLException { AbstractAssert.notNull(uri, "URI must not be null"); this.uri = uri; this.url = uri.toURL(); } /** * Create a new {@code UrlResource} based on the given URL object. * * @param url a URL */ public UrlResource(URL url) { AbstractAssert.notNull(url, "URL must not be null"); this.uri = null; this.url = url; } /** * Create a new {@code UrlResource} based on a URL path. * *

    Note: The given path needs to be pre-encoded if necessary. * * @param path a URL path * @throws MalformedURLException if the given URL path is not valid * @see URL#URL(String) */ public UrlResource(String path) throws MalformedURLException { AbstractAssert.notNull(path, "Path must not be null"); this.uri = null; this.url = new URL(path); this.cleanedUrl = getCleanedUrl(this.url, path); } /** * Create a new {@code UrlResource} based on a URI specification. * *

    The given parts will automatically get encoded if necessary. * * @param protocol the URL protocol to use (e.g. "jar" or "file" - without colon); * also known as "scheme" * @param location the location (e.g. the file path within that protocol); * also known as "scheme-specific part" * @throws MalformedURLException if the given URL specification is not valid * @see URI#URI(String, String, String) */ public UrlResource(String protocol, String location) throws MalformedURLException { this(protocol, location, null); } /** * Create a new {@code UrlResource} based on a URI specification. * *

    The given parts will automatically get encoded if necessary. * * @param protocol the URL protocol to use (e.g. "jar" or "file" - without colon); * also known as "scheme" * @param location the location (e.g. the file path within that protocol); * also known as "scheme-specific part" * @param fragment the fragment within that location (e.g. anchor on an HTML page, * as following after a "#" separator) * @throws MalformedURLException if the given URL specification is not valid * @see URI#URI(String, String, String) */ public UrlResource(String protocol, String location, String fragment) throws MalformedURLException { try { this.uri = new URI(protocol, location, fragment); this.url = this.uri.toURL(); } catch (URISyntaxException ex) { MalformedURLException exToThrow = new MalformedURLException(ex.getMessage()); exToThrow.initCause(ex); throw exToThrow; } } /** * Determine a cleaned URL for the given original URL. * * @param originalUrl the original URL * @param originalPath the original URL path * @return the cleaned URL (possibly the original URL as-is) * @see StringUtils#cleanPath */ private static URL getCleanedUrl(URL originalUrl, String originalPath) { String cleanedPath = StringUtils.cleanPath(originalPath); if (!cleanedPath.equals(originalPath)) { try { return new URL(cleanedPath); } catch (MalformedURLException ex) { // Cleaned URL path cannot be converted to URL -> take original URL. } } return originalUrl; } /** * Lazily determine a cleaned URL for the given original URL. * * @see #getCleanedUrl(URL, String) */ private URL getCleanedUrl() { URL cleanedUrl = this.cleanedUrl; if (cleanedUrl != null) { return cleanedUrl; } cleanedUrl = getCleanedUrl(this.url, (this.uri != null ? this.uri : this.url).toString()); this.cleanedUrl = cleanedUrl; return cleanedUrl; } /** * This implementation opens an InputStream for the given URL. * *

    It sets the {@code useCaches} flag to {@code false}, * mainly to avoid jar file locking on Windows. * * @see URL#openConnection() * @see URLConnection#setUseCaches(boolean) * @see URLConnection#getInputStream() */ @Override public InputStream getInputStream() throws IOException { URLConnection con = this.url.openConnection(); ResourceUtils.useCachesIfNecessary(con); try { return con.getInputStream(); } catch (IOException ex) { // Close the HTTP connection (if applicable). if (con instanceof HttpURLConnection) { ((HttpURLConnection) con).disconnect(); } throw ex; } } /** * This implementation returns the underlying URL reference. */ @Override public URL getUrl() { return this.url; } /** * This implementation returns the underlying URI directly, * if possible. */ @Override public URI getUri() throws IOException { if (this.uri != null) { return this.uri; } else { return super.getUri(); } } @Override public boolean isFile() { if (this.uri != null) { return super.isFile(this.uri); } else { return super.isFile(); } } /** * This implementation returns a File reference for the underlying URL/URI, * provided that it refers to a file in the file system. * * @see ResourceUtils#getFile(URL, String) */ @Override public File getFile() throws IOException { if (this.uri != null) { return super.getFile(this.uri); } else { return super.getFile(); } } /** * This implementation creates a {@code UrlResource}, delegating to * {@link #createRelativeUrl(String)} for adapting the relative path. * * @see #createRelativeUrl(String) */ @Override public Resource createRelative(String relativePath) throws MalformedURLException { return new UrlResource(createRelativeUrl(relativePath)); } /** * This delegate creates a {@code java.net.URL}, applying the given path * relative to the path of the underlying URL of this resource descriptor. * A leading slash will get dropped; a "#" symbol will get encoded. * * @see #createRelative(String) * @since 5.2 */ protected URL createRelativeUrl(String relativePath) throws MalformedURLException { if (relativePath.startsWith("/")) { relativePath = relativePath.substring(1); } // # can appear in filenames, java.net.URL should not treat it as a fragment relativePath = StringUtils.replace(relativePath, "#", "%23"); // Use the URL constructor for applying the relative path as a URL spec return new URL(this.url, relativePath); } /** * This implementation returns the name of the file that this URL refers to. * * @see URL#getPath() */ @Override public String getFilename() { return StringUtils.getFilename(getCleanedUrl().getPath()); } /** * This implementation returns a description that includes the URL. */ @Override public String getDescription() { return "URL [" + this.url + "]"; } /** * This implementation compares the underlying URL references. */ @Override public boolean equals(Object other) { return (this == other || (other instanceof UrlResource && getCleanedUrl().equals(((UrlResource) other).getCleanedUrl()))); } /** * This implementation returns the hash code of the underlying URL reference. */ @Override public int hashCode() { return getCleanedUrl().hashCode(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/VfsPatternUtils.java ================================================ /* * Copyright 2002-2017 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 com.alibaba.nacos.common.packagescan.resource; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.net.URL; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Artificial class used for accessing the {@link VfsUtils} methods * without exposing them to the entire world. * * @author Costin Leau * @since 3.0.3 */ abstract class VfsPatternUtils extends VfsUtils { static Object getVisitorAttributes() { return doGetVisitorAttributes(); } static String getPath(Object resource) { String path = doGetPath(resource); return (path != null ? path : ""); } static Object findRoot(URL url) throws IOException { return getRoot(url); } static void visit(Object resource, InvocationHandler visitor) throws IOException { Object visitorProxy = Proxy.newProxyInstance( VIRTUAL_FILE_VISITOR_INTERFACE.getClassLoader(), new Class[]{VIRTUAL_FILE_VISITOR_INTERFACE}, visitor); invokeVfsMethod(VIRTUAL_FILE_METHOD_VISIT, resource, visitorProxy); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/VfsResource.java ================================================ /* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.packagescan.util.AbstractAssert; import com.alibaba.nacos.common.packagescan.util.NestedIoException; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URL; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * JBoss VFS based {@link Resource} implementation. * *

    As of Spring 4.0, this class supports VFS 3.x on JBoss AS 6+ * (package {@code org.jboss.vfs}) and is in particular compatible with * JBoss AS 7 and WildFly 8+. * * @author Ales Justin * @author Juergen Hoeller * @author Costin Leau * @author Sam Brannen * @see org.jboss.vfs.VirtualFile * @since 3.0 */ public class VfsResource extends AbstractResource { private final Object resource; /** * Create a new {@code VfsResource} wrapping the given resource handle. * * @param resource a {@code org.jboss.vfs.VirtualFile} instance * (untyped in order to avoid a static dependency on the VFS API) */ public VfsResource(Object resource) { AbstractAssert.notNull(resource, "VirtualFile must not be null"); this.resource = resource; } @Override public InputStream getInputStream() throws IOException { return VfsUtils.getInputStream(this.resource); } @Override public boolean exists() { return VfsUtils.exists(this.resource); } @Override public boolean isReadable() { return VfsUtils.isReadable(this.resource); } @Override public URL getUrl() throws IOException { try { return VfsUtils.getUrl(this.resource); } catch (Exception ex) { throw new NestedIoException("Failed to obtain URL for file " + this.resource, ex); } } @Override public URI getUri() throws IOException { try { return VfsUtils.getUri(this.resource); } catch (Exception ex) { throw new NestedIoException("Failed to obtain URI for " + this.resource, ex); } } @Override public File getFile() throws IOException { return VfsUtils.getFile(this.resource); } @Override public long contentLength() throws IOException { return VfsUtils.getSize(this.resource); } @Override public long lastModified() throws IOException { return VfsUtils.getLastModified(this.resource); } @Override public Resource createRelative(String relativePath) throws IOException { if (!relativePath.startsWith(".") && relativePath.contains("/")) { try { return new VfsResource(VfsUtils.getChild(this.resource, relativePath)); } catch (IOException ex) { // fall back to getRelative } } return new VfsResource(VfsUtils.getRelative(new URL(getUrl(), relativePath))); } @Override public String getFilename() { return VfsUtils.getName(this.resource); } @Override public String getDescription() { return "VFS resource [" + this.resource + "]"; } @Override public boolean equals(Object other) { return (this == other || (other instanceof VfsResource && this.resource.equals(((VfsResource) other).resource))); } @Override public int hashCode() { return this.resource.hashCode(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/VfsUtils.java ================================================ /* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.common.packagescan.resource; import com.alibaba.nacos.common.utils.ReflectUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Utility for detecting and accessing JBoss VFS in the classpath. * *

    As of Spring 4.0, this class supports VFS 3.x on JBoss AS 6+ * (package {@code org.jboss.vfs}) and is in particular compatible with * JBoss AS 7 and WildFly 8+. * *

    Thanks go to Marius Bogoevici for the initial patch. * Note: This is an internal class and should not be used outside the framework. * * @author Costin Leau * @author Juergen Hoeller * @since 3.0.3 */ public abstract class VfsUtils { private static final String VFS3_PKG = "org.jboss.vfs."; private static final String VFS_NAME = "VFS"; private static final Method VFS_METHOD_GET_ROOT_URL; private static final Method VFS_METHOD_GET_ROOT_URI; private static final Method VIRTUAL_FILE_METHOD_EXISTS; private static final Method VIRTUAL_FILE_METHOD_GET_INPUT_STREAM; private static final Method VIRTUAL_FILE_METHOD_GET_SIZE; private static final Method VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED; private static final Method VIRTUAL_FILE_METHOD_TO_URL; private static final Method VIRTUAL_FILE_METHOD_TO_URI; private static final Method VIRTUAL_FILE_METHOD_GET_NAME; private static final Method VIRTUAL_FILE_METHOD_GET_PATH_NAME; private static final Method VIRTUAL_FILE_METHOD_GET_PHYSICAL_FILE; private static final Method VIRTUAL_FILE_METHOD_GET_CHILD; protected static final Class VIRTUAL_FILE_VISITOR_INTERFACE; protected static final Method VIRTUAL_FILE_METHOD_VISIT; private static final Field VISITOR_ATTRIBUTES_FIELD_RECURSE; static { ClassLoader loader = VfsUtils.class.getClassLoader(); try { Class vfsClass = loader.loadClass(VFS3_PKG + VFS_NAME); VFS_METHOD_GET_ROOT_URL = vfsClass.getMethod("getChild", URL.class); VFS_METHOD_GET_ROOT_URI = vfsClass.getMethod("getChild", URI.class); Class virtualFile = loader.loadClass(VFS3_PKG + "VirtualFile"); VIRTUAL_FILE_METHOD_EXISTS = virtualFile.getMethod("exists"); VIRTUAL_FILE_METHOD_GET_INPUT_STREAM = virtualFile.getMethod("openStream"); VIRTUAL_FILE_METHOD_GET_SIZE = virtualFile.getMethod("getSize"); VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED = virtualFile.getMethod("getLastModified"); VIRTUAL_FILE_METHOD_TO_URI = virtualFile.getMethod("toURI"); VIRTUAL_FILE_METHOD_TO_URL = virtualFile.getMethod("toURL"); VIRTUAL_FILE_METHOD_GET_NAME = virtualFile.getMethod("getName"); VIRTUAL_FILE_METHOD_GET_PATH_NAME = virtualFile.getMethod("getPathName"); VIRTUAL_FILE_METHOD_GET_PHYSICAL_FILE = virtualFile.getMethod("getPhysicalFile"); VIRTUAL_FILE_METHOD_GET_CHILD = virtualFile.getMethod("getChild", String.class); VIRTUAL_FILE_VISITOR_INTERFACE = loader.loadClass(VFS3_PKG + "VirtualFileVisitor"); VIRTUAL_FILE_METHOD_VISIT = virtualFile.getMethod("visit", VIRTUAL_FILE_VISITOR_INTERFACE); Class visitorAttributesClass = loader.loadClass(VFS3_PKG + "VisitorAttributes"); VISITOR_ATTRIBUTES_FIELD_RECURSE = visitorAttributesClass.getField("RECURSE"); } catch (Throwable ex) { throw new IllegalStateException("Could not detect JBoss VFS infrastructure", ex); } } protected static Object invokeVfsMethod(Method method, Object target, Object... args) throws IOException { try { return method.invoke(target, args); } catch (InvocationTargetException ex) { Throwable targetEx = ex.getTargetException(); if (targetEx instanceof IOException) { throw (IOException) targetEx; } ReflectUtils.handleInvocationTargetException(ex); } catch (Exception ex) { ReflectUtils.handleReflectionException(ex); } throw new IllegalStateException("Invalid code path reached"); } static boolean exists(Object vfsResource) { try { return (Boolean) invokeVfsMethod(VIRTUAL_FILE_METHOD_EXISTS, vfsResource); } catch (IOException ex) { return false; } } static boolean isReadable(Object vfsResource) { try { return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_SIZE, vfsResource) > 0; } catch (IOException ex) { return false; } } static long getSize(Object vfsResource) throws IOException { return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_SIZE, vfsResource); } static long getLastModified(Object vfsResource) throws IOException { return (Long) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED, vfsResource); } static InputStream getInputStream(Object vfsResource) throws IOException { return (InputStream) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_INPUT_STREAM, vfsResource); } static URL getUrl(Object vfsResource) throws IOException { return (URL) invokeVfsMethod(VIRTUAL_FILE_METHOD_TO_URL, vfsResource); } static URI getUri(Object vfsResource) throws IOException { return (URI) invokeVfsMethod(VIRTUAL_FILE_METHOD_TO_URI, vfsResource); } static String getName(Object vfsResource) { try { return (String) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_NAME, vfsResource); } catch (IOException ex) { throw new IllegalStateException("Cannot get resource name", ex); } } static Object getRelative(URL url) throws IOException { return invokeVfsMethod(VFS_METHOD_GET_ROOT_URL, null, url); } static Object getChild(Object vfsResource, String path) throws IOException { return invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_CHILD, vfsResource, path); } static File getFile(Object vfsResource) throws IOException { return (File) invokeVfsMethod(VIRTUAL_FILE_METHOD_GET_PHYSICAL_FILE, vfsResource); } static Object getRoot(URI url) throws IOException { return invokeVfsMethod(VFS_METHOD_GET_ROOT_URI, null, url); } // protected methods used by the support sub-package protected static Object getRoot(URL url) throws IOException { return invokeVfsMethod(VFS_METHOD_GET_ROOT_URL, null, url); } protected static Object doGetVisitorAttributes() { return ReflectUtils.getField(VISITOR_ATTRIBUTES_FIELD_RECURSE, null); } protected static String doGetPath(Object resource) { return (String) ReflectUtils.invokeMethod(VIRTUAL_FILE_METHOD_GET_PATH_NAME, resource); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/resource/WritableResource.java ================================================ /* * Copyright 2002-2017 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 com.alibaba.nacos.common.packagescan.resource; import java.io.IOException; import java.io.OutputStream; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Extended interface for a resource that supports writing to it. * Provides an {@link #getOutputStream() OutputStream accessor}. * * @author Juergen Hoeller * @see OutputStream * @since 3.1 */ public interface WritableResource extends Resource { /** * Indicate whether the contents of this resource can be written * via {@link #getOutputStream()}. * *

    Will be {@code true} for typical resource descriptors; * note that actual content writing may still fail when attempted. * However, a value of {@code false} is a definitive indication * that the resource content cannot be modified. * * @see #getOutputStream() * @see #isReadable() */ default boolean isWritable() { return true; } /** * Return an {@link OutputStream} for the underlying resource, * allowing to (over-)write its content. * * @throws IOException if the stream could not be opened * @see #getInputStream() */ OutputStream getOutputStream() throws IOException; /** * Return a {@link WritableByteChannel}. * *

    It is expected that each call creates a fresh channel. * *

    The default implementation returns {@link Channels#newChannel(OutputStream)} * with the result of {@link #getOutputStream()}. * * @return the byte channel for the underlying resource (must not be {@code null}) * @throws java.io.FileNotFoundException if the underlying resource doesn't exist * @throws IOException if the content channel could not be opened * @see #getOutputStream() * @since 5.0 */ default WritableByteChannel writableChannel() throws IOException { return Channels.newChannel(getOutputStream()); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/util/AbstractAssert.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan.util; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Collection; import java.util.Map; import java.util.function.Supplier; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Assertion utility class that assists in validating arguments. * *

    Useful for identifying programmer errors early and clearly at runtime. * For example, if the contract of a public method states it does not * allow {@code null} arguments, {@code Assert} can be used to validate that * contract. Doing this clearly indicates a contract violation when it * occurs and protects the class's invariants. * *

    Typically used to validate method arguments rather than configuration * properties, to check for cases that are usually programmer errors rather * than configuration errors. In contrast to configuration initialization * code, there is usually no point in falling back to defaults in such methods. * This class is similar to JUnit's assertion library. If an argument value is * deemed invalid, an {@link IllegalArgumentException} is thrown (typically). * For example: * *

     * Assert.notNull(clazz, "The class must not be null");
     * Assert.isTrue(i > 0, "The value must be greater than zero");
    * *

    Mainly for internal use within the framework; for a more comprehensive suite * of assertion utilities consider {@code org.apache.commons.lang3.Validate} from * Apache Commons Lang, * Google Guava's * Preconditions, * or similar third-party libraries. * * @author Keith Donald * @author Juergen Hoeller * @author Sam Brannen * @author Colin Sampaleanu * @author Rob Harrop * @since 1.1.2 */ public abstract class AbstractAssert { private AbstractAssert() { } /** * Assert a boolean expression, throwing an {@code IllegalStateException} * if the expression evaluates to {@code false}. * Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException} * on an assertion failure. *

    Assert.state(id == null, "The id property must not already be initialized");
    * * @param expression a boolean expression * @param message the exception message to use if the assertion fails * @throws IllegalStateException if {@code expression} is {@code false} */ public static void state(boolean expression, String message) { if (!expression) { throw new IllegalStateException(message); } } /** * Assert a boolean expression, throwing an {@code IllegalStateException} * if the expression evaluates to {@code false}. * Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException} * on an assertion failure. *
         * Assert.state(entity.getId() == null,
         *     () -> "ID for entity " + entity.getName() + " must not already be initialized");
         * 
    * * @param expression a boolean expression * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalStateException if {@code expression} is {@code false} * @since 5.0 */ public static void state(boolean expression, Supplier messageSupplier) { if (!expression) { throw new IllegalStateException(nullSafeGet(messageSupplier)); } } /** * Assert a boolean expression, throwing an {@code IllegalStateException} * if the expression evaluates to {@code false}. * * @deprecated as of 4.3.7, in favor of {@link #state(boolean, String)} */ @Deprecated public static void state(boolean expression) { state(expression, "[Assertion failed] - this state invariant must be true"); } /** * Assert a boolean expression, throwing an {@code IllegalArgumentException} * if the expression evaluates to {@code false}. *
    Assert.isTrue(i > 0, "The value must be greater than zero");
    * * @param expression a boolean expression * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if {@code expression} is {@code false} */ public static void isTrue(boolean expression, String message) { if (!expression) { throw new IllegalArgumentException(message); } } /** * Assert a boolean expression, throwing an {@code IllegalArgumentException} * if the expression evaluates to {@code false}. *
         * Assert.isTrue(i > 0, () -> "The value '" + i + "' must be greater than zero");
         * 
    * * @param expression a boolean expression * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if {@code expression} is {@code false} * @since 5.0 */ public static void isTrue(boolean expression, Supplier messageSupplier) { if (!expression) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert a boolean expression, throwing an {@code IllegalArgumentException} * if the expression evaluates to {@code false}. * * @deprecated as of 4.3.7, in favor of {@link #isTrue(boolean, String)} */ @Deprecated public static void isTrue(boolean expression) { isTrue(expression, "[Assertion failed] - this expression must be true"); } /** * Assert that an object is {@code null}. *
    Assert.isNull(value, "The value must be null");
    * * @param object the object to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object is not {@code null} */ public static void isNull(Object object, String message) { if (object != null) { throw new IllegalArgumentException(message); } } /** * Assert that an object is {@code null}. *
         * Assert.isNull(value, () -> "The value '" + value + "' must be null");
         * 
    * * @param object the object to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the object is not {@code null} * @since 5.0 */ public static void isNull(Object object, Supplier messageSupplier) { if (object != null) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that an object is {@code null}. * * @deprecated as of 4.3.7, in favor of {@link #isNull(Object, String)} */ @Deprecated public static void isNull(Object object) { isNull(object, "[Assertion failed] - the object argument must be null"); } /** * Assert that an object is not {@code null}. *
    Assert.notNull(clazz, "The class must not be null");
    * * @param object the object to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object is {@code null} */ public static void notNull(Object object, String message) { if (object == null) { throw new IllegalArgumentException(message); } } /** * Assert that an object is not {@code null}. *
         * Assert.notNull(entity.getId(),
         *     () -> "ID for entity " + entity.getName() + " must not be null");
         * 
    * * @param object the object to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the object is {@code null} * @since 5.0 */ public static void notNull(Object object, Supplier messageSupplier) { if (object == null) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that an object is not {@code null}. * * @deprecated as of 4.3.7, in favor of {@link #notNull(Object, String)} */ @Deprecated public static void notNull(Object object) { notNull(object, "[Assertion failed] - this argument is required; it must not be null"); } /** * Assert that the given String is not empty; that is, * it must not be {@code null} and not the empty String. *
    Assert.hasLength(name, "Name must not be empty");
    * * @param text the String to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the text is empty */ public static void hasLength(String text, String message) { if (!StringUtils.hasLength(text)) { throw new IllegalArgumentException(message); } } /** * Assert that the given String is not empty; that is, * it must not be {@code null} and not the empty String. *
         * Assert.hasLength(account.getName(),
         *     () -> "Name for account '" + account.getId() + "' must not be empty");
         * 
    * * @param text the String to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the text is empty * @since 5.0 */ public static void hasLength(String text, Supplier messageSupplier) { if (!StringUtils.hasLength(text)) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that the given String is not empty; that is, * it must not be {@code null} and not the empty String. * * @deprecated as of 4.3.7, in favor of {@link #hasLength(String, String)} */ @Deprecated public static void hasLength(String text) { hasLength(text, "[Assertion failed] - this String argument must have length; it must not be null or empty"); } /** * Assert that the given String contains valid text content; that is, it must not * be {@code null} and must contain at least one non-whitespace character. *
    Assert.hasText(name, "'name' must not be empty");
    * * @param text the String to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the text does not contain valid text content */ public static void hasText(String text, String message) { if (!StringUtils.hasText(text)) { throw new IllegalArgumentException(message); } } /** * Assert that the given String contains valid text content; that is, it must not * be {@code null} and must contain at least one non-whitespace character. *
         * Assert.hasText(account.getName(),
         *     () -> "Name for account '" + account.getId() + "' must not be empty");
         * 
    * * @param text the String to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the text does not contain valid text content * @since 5.0 */ public static void hasText(String text, Supplier messageSupplier) { if (!StringUtils.hasText(text)) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that the given String contains valid text content; that is, it must not * be {@code null} and must contain at least one non-whitespace character. * * @deprecated as of 4.3.7, in favor of {@link #hasText(String, String)} */ @Deprecated public static void hasText(String text) { hasText(text, "[Assertion failed] - this String argument must have text; it must not be null, empty, or blank"); } /** * Assert that the given text does not contain the given substring. *
         * Assert.doesNotContain(name, forbidden, () -> "Name must not contain '" + forbidden + "'");
         * 
    * * @param textToSearch the text to search * @param substring the substring to find within the text * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the text contains the substring * @since 5.0 */ public static void doesNotContain(String textToSearch, String substring, Supplier messageSupplier) { if (StringUtils.hasLength(textToSearch) && StringUtils.hasLength(substring) && textToSearch.contains(substring)) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that the given text does not contain the given substring. * * @deprecated as of 4.3.7, in favor of {@link #doesNotContain(String, String, String)} */ @Deprecated public static void doesNotContain(String textToSearch, String substring) { doesNotContain(textToSearch, substring, () -> "[Assertion failed] - this String argument must not contain the substring [" + substring + "]"); } /** * Assert that an array contains elements; that is, it must not be * {@code null} and must contain at least one element. *
    Assert.notEmpty(array, "The array must contain elements");
    * * @param array the array to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object array is {@code null} or contains no elements */ public static void notEmpty(Object[] array, String message) { if (AbstractObjectUtils.isEmpty(array)) { throw new IllegalArgumentException(message); } } /** * Assert that an array contains elements; that is, it must not be * {@code null} and must contain at least one element. *
         * Assert.notEmpty(array, () -> "The " + arrayType + " array must contain elements");
         * 
    * * @param array the array to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the object array is {@code null} or contains no elements * @since 5.0 */ public static void notEmpty(Object[] array, Supplier messageSupplier) { if (AbstractObjectUtils.isEmpty(array)) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that an array contains elements; that is, it must not be * {@code null} and must contain at least one element. * * @deprecated as of 4.3.7, in favor of {@link #notEmpty(Object[], String)} */ @Deprecated public static void notEmpty(Object[] array) { notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element"); } /** * Assert that a collection contains elements; that is, it must not be * {@code null} and must contain at least one element. *
         * Assert.notEmpty(collection, () -> "The " + collectionType + " collection must contain elements");
         * 
    * * @param collection the collection to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the collection is {@code null} or * contains no elements * @since 5.0 */ public static void notEmpty(Collection collection, Supplier messageSupplier) { if (CollectionUtils.isEmpty(collection)) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that a collection contains elements; that is, it must not be * {@code null} and must contain at least one element. *
    Assert.notEmpty(collection, "Collection must contain elements");
    * * @param collection the collection to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the collection is {@code null} or * contains no elements */ public static void notEmpty(Collection collection, String message) { if (CollectionUtils.isEmpty(collection)) { throw new IllegalArgumentException(message); } } /** * Assert that a collection contains elements; that is, it must not be * {@code null} and must contain at least one element. * * @deprecated as of 4.3.7, in favor of {@link #notEmpty(Collection, String)} */ @Deprecated public static void notEmpty(Collection collection) { notEmpty(collection, "[Assertion failed] - this collection must not be empty: it must contain at least 1 element"); } /** * Assert that a Map contains entries; that is, it must not be {@code null} * and must contain at least one entry. *
    Assert.notEmpty(map, "Map must contain entries");
    * * @param map the map to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the map is {@code null} or contains no entries */ public static void notEmpty(Map map, String message) { if (CollectionUtils.isMapEmpty(map)) { throw new IllegalArgumentException(message); } } /** * Assert that a Map contains entries; that is, it must not be {@code null} * and must contain at least one entry. *
         * Assert.notEmpty(map, () -> "The " + mapType + " map must contain entries");
         * 
    * * @param map the map to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the map is {@code null} or contains no entries * @since 5.0 */ public static void notEmpty(Map map, Supplier messageSupplier) { if (CollectionUtils.isMapEmpty(map)) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } /** * Assert that a Map contains entries; that is, it must not be {@code null} * and must contain at least one entry. * * @deprecated as of 4.3.7, in favor of {@link #notEmpty(Map, String)} */ @Deprecated public static void notEmpty(Map map) { notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry"); } /** * Assert that an array contains no {@code null} elements. * Note: Does not complain if the array is empty! *
    Assert.noNullElements(array, "The array must contain non-null elements");
    * * @param array the array to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the object array contains a {@code null} element */ public static void noNullElements(Object[] array, String message) { if (array != null) { for (Object element : array) { if (element == null) { throw new IllegalArgumentException(message); } } } } /** * Assert that an array contains no {@code null} elements. * Note: Does not complain if the array is empty! *
         * Assert.noNullElements(array, () -> "The " + arrayType + " array must contain non-null elements");
         * 
    * * @param array the array to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the object array contains a {@code null} element * @since 5.0 */ public static void noNullElements(Object[] array, Supplier messageSupplier) { if (array != null) { for (Object element : array) { if (element == null) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } } } /** * Assert that an array contains no {@code null} elements. * * @deprecated as of 4.3.7, in favor of {@link #noNullElements(Object[], String)} */ @Deprecated public static void noNullElements(Object[] array) { noNullElements(array, "[Assertion failed] - this array must not contain any null elements"); } /** * Assert that a collection contains no {@code null} elements. * Note: Does not complain if the collection is empty! *
    Assert.noNullElements(collection, "Collection must contain non-null elements");
    * * @param collection the collection to check * @param message the exception message to use if the assertion fails * @throws IllegalArgumentException if the collection contains a {@code null} element * @since 5.2 */ public static void noNullElements(Collection collection, String message) { if (collection != null) { for (Object element : collection) { if (element == null) { throw new IllegalArgumentException(message); } } } } /** * Assert that a collection contains no {@code null} elements. * Note: Does not complain if the collection is empty! *
         * Assert.noNullElements(collection, () -> "Collection " + collectionName + " must contain non-null elements");
         * 
    * * @param collection the collection to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails * @throws IllegalArgumentException if the collection contains a {@code null} element * @since 5.2 */ public static void noNullElements(Collection collection, Supplier messageSupplier) { if (collection != null) { for (Object element : collection) { if (element == null) { throw new IllegalArgumentException(nullSafeGet(messageSupplier)); } } } } /** * Assert that the provided object is an instance of the provided class. *
    Assert.instanceOf(Foo.class, foo, "Foo expected");
    * * @param type the type to check against * @param obj the object to check * @param message a message which will be prepended to provide further context. * If it is empty or ends in ":" or ";" or "," or ".", a full exception message * will be appended. If it ends in a space, the name of the offending object's * type will be appended. In any other case, a ":" with a space and the name * of the offending object's type will be appended. * @throws IllegalArgumentException if the object is not an instance of type */ public static void isInstanceOf(Class type, Object obj, String message) { notNull(type, "Type to check against must not be null"); if (!type.isInstance(obj)) { instanceCheckFailed(type, obj, message); } } /** * Assert that the provided object is an instance of the provided class. *
         * Assert.instanceOf(Foo.class, foo, () -> "Processing " + Foo.class.getSimpleName() + ":");
         * 
    * * @param type the type to check against * @param obj the object to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails. See {@link #isInstanceOf(Class, Object, String)} for details. * @throws IllegalArgumentException if the object is not an instance of type * @since 5.0 */ public static void isInstanceOf(Class type, Object obj, Supplier messageSupplier) { notNull(type, "Type to check against must not be null"); if (!type.isInstance(obj)) { instanceCheckFailed(type, obj, nullSafeGet(messageSupplier)); } } /** * Assert that the provided object is an instance of the provided class. *
    Assert.instanceOf(Foo.class, foo);
    * * @param type the type to check against * @param obj the object to check * @throws IllegalArgumentException if the object is not an instance of type */ public static void isInstanceOf(Class type, Object obj) { isInstanceOf(type, obj, ""); } /** * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}. *
    Assert.isAssignable(Number.class, myClass, "Number expected");
    * * @param superType the super type to check against * @param subType the sub type to check * @param message a message which will be prepended to provide further context. * If it is empty or ends in ":" or ";" or "," or ".", a full exception message * will be appended. If it ends in a space, the name of the offending sub type * will be appended. In any other case, a ":" with a space and the name of the * offending sub type will be appended. * @throws IllegalArgumentException if the classes are not assignable */ public static void isAssignable(Class superType, Class subType, String message) { notNull(superType, "Super type to check against must not be null"); if (subType == null || !superType.isAssignableFrom(subType)) { assignableCheckFailed(superType, subType, message); } } /** * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}. *
         * Assert.isAssignable(Number.class, myClass, () -> "Processing " + myAttributeName + ":");
         * 
    * * @param superType the super type to check against * @param subType the sub type to check * @param messageSupplier a supplier for the exception message to use if the * assertion fails. See {@link #isAssignable(Class, Class, String)} for details. * @throws IllegalArgumentException if the classes are not assignable * @since 5.0 */ public static void isAssignable(Class superType, Class subType, Supplier messageSupplier) { notNull(superType, "Super type to check against must not be null"); if (subType == null || !superType.isAssignableFrom(subType)) { assignableCheckFailed(superType, subType, nullSafeGet(messageSupplier)); } } /** * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}. *
    Assert.isAssignable(Number.class, myClass);
    * * @param superType the super type to check * @param subType the sub type to check * @throws IllegalArgumentException if the classes are not assignable */ public static void isAssignable(Class superType, Class subType) { isAssignable(superType, subType, ""); } private static void instanceCheckFailed(Class type, Object obj, String msg) { String className = (obj != null ? obj.getClass().getName() : "null"); String result = ""; boolean defaultMessage = true; if (StringUtils.hasLength(msg)) { if (endsWithSeparator(msg)) { result = msg + " "; } else { result = messageWithTypeName(msg, className); defaultMessage = false; } } if (defaultMessage) { result = result + ("Object of class [" + className + "] must be an instance of " + type); } throw new IllegalArgumentException(result); } private static void assignableCheckFailed(Class superType, Class subType, String msg) { String result = ""; boolean defaultMessage = true; if (StringUtils.hasLength(msg)) { if (endsWithSeparator(msg)) { result = msg + " "; } else { result = messageWithTypeName(msg, subType); defaultMessage = false; } } if (defaultMessage) { result = result + (subType + " is not assignable to " + superType); } throw new IllegalArgumentException(result); } private static boolean endsWithSeparator(String msg) { return (msg.endsWith(":") || msg.endsWith(";") || msg.endsWith(",") || msg.endsWith(".")); } private static String messageWithTypeName(String msg, Object typeName) { return msg + (msg.endsWith(" ") ? "" : ": ") + typeName; } private static String nullSafeGet(Supplier messageSupplier) { return (messageSupplier != null ? messageSupplier.get() : null); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/util/AbstractObjectUtils.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan.util; import java.lang.reflect.Array; import java.util.Arrays; import java.util.Collection; import java.util.Map; import java.util.Optional; import java.util.StringJoiner; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Miscellaneous object utility methods. * *

    Mainly for internal use within the framework. * *

    Thanks to Alex Ruiz for contributing several enhancements to this class! * * @author Juergen Hoeller * @author Keith Donald * @author Rod Johnson * @author Rob Harrop * @author Chris Beams * @author Sam Brannen * @since 19.03.2004 */ public abstract class AbstractObjectUtils { private AbstractObjectUtils() { } private static final int INITIAL_HASH = 7; private static final int MULTIPLIER = 31; private static final String EMPTY_STRING = ""; private static final String NULL_STRING = "null"; private static final String ARRAY_START = "{"; private static final String ARRAY_END = "}"; private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END; private static final String ARRAY_ELEMENT_SEPARATOR = ", "; private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; /** * Return whether the given throwable is a checked exception: * that is, neither a RuntimeException nor an Error. * * @param ex the throwable to check * @return whether the throwable is a checked exception * @see Exception * @see RuntimeException * @see Error */ public static boolean isCheckedException(Throwable ex) { return !(ex instanceof RuntimeException || ex instanceof Error); } /** * Check whether the given exception is compatible with the specified * exception types, as declared in a throws clause. * * @param ex the exception to check * @param declaredExceptions the exception types declared in the throws clause * @return whether the given exception is compatible */ public static boolean isCompatibleWithThrowsClause(Throwable ex, Class... declaredExceptions) { if (!isCheckedException(ex)) { return true; } if (declaredExceptions != null) { for (Class declaredException : declaredExceptions) { if (declaredException.isInstance(ex)) { return true; } } } return false; } /** * Determine whether the given object is an array: * either an Object array or a primitive array. * * @param obj the object to check */ public static boolean isArray(Object obj) { return (obj != null && obj.getClass().isArray()); } /** * Determine whether the given array is empty: * i.e. {@code null} or of zero length. * * @param array the array to check * @see #isEmpty(Object) */ public static boolean isEmpty(Object[] array) { return (array == null || array.length == 0); } /** * Determine whether the given object is empty. * *

    This method supports the following object types. *

      *
    • {@code Optional}: considered empty if not {@link Optional#isPresent()}
    • *
    • {@code Array}: considered empty if its length is zero
    • *
    • {@link CharSequence}: considered empty if its length is zero
    • *
    * *

    If the given object is non-null and not one of the aforementioned * supported types, this method returns {@code false}. * * @param obj the object to check * @return {@code true} if the object is {@code null} or empty * @see Optional#isPresent() * @see AbstractObjectUtils#isEmpty(Object[]) * @since 4.2 */ public static boolean isEmpty(Object obj) { if (obj == null) { return true; } if (obj instanceof Optional) { return !((Optional) obj).isPresent(); } if (obj instanceof CharSequence) { return ((CharSequence) obj).length() == 0; } if (obj.getClass().isArray()) { return Array.getLength(obj) == 0; } if (obj instanceof Collection) { return ((Collection) obj).isEmpty(); } if (obj instanceof Map) { return ((Map) obj).isEmpty(); } // else return false; } /** * Unwrap the given object which is potentially a {@link Optional}. * * @param obj the candidate object * @return either the value held within the {@code Optional}, {@code null} * if the {@code Optional} is empty, or simply the given object as-is * @since 5.0 */ public static Object unwrapOptional(Object obj) { if (obj instanceof Optional) { Optional optional = (Optional) obj; if (!optional.isPresent()) { return null; } Object result = optional.get(); AbstractAssert.isTrue(!(result instanceof Optional), "Multi-level Optional usage not supported"); return result; } return obj; } /** * Check whether the given array contains the given element. * * @param array the array to check (may be {@code null}, * in which case the return value will always be {@code false}) * @param element the element to check for * @return whether the element has been found in the given array */ public static boolean containsElement(Object[] array, Object element) { if (array == null) { return false; } for (Object arrayEle : array) { if (nullSafeEquals(arrayEle, element)) { return true; } } return false; } /** * Check whether the given array of enum constants contains a constant with the given name, * ignoring case when determining a match. * * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()} * @param constant the constant name to find (must not be null or empty string) * @return whether the constant has been found in the given array */ public static boolean containsConstant(Enum[] enumValues, String constant) { return containsConstant(enumValues, constant, false); } /** * Check whether the given array of enum constants contains a constant with the given name. * * @param enumValues the enum values to check, typically obtained via {@code MyEnum.values()} * @param constant the constant name to find (must not be null or empty string) * @param caseSensitive whether case is significant in determining a match * @return whether the constant has been found in the given array */ public static boolean containsConstant(Enum[] enumValues, String constant, boolean caseSensitive) { for (Enum candidate : enumValues) { if (caseSensitive ? candidate.toString().equals(constant) : candidate.toString().equalsIgnoreCase(constant)) { return true; } } return false; } /** * Case insensitive alternative to {@link Enum#valueOf(Class, String)}. * * @param the concrete Enum type * @param enumValues the array of all Enum constants in question, usually per {@code Enum.values()} * @param constant the constant to get the enum value of * @throws IllegalArgumentException if the given constant is not found in the given array * of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception. */ public static > E caseInsensitiveValueOf(E[] enumValues, String constant) { for (E candidate : enumValues) { if (candidate.toString().equalsIgnoreCase(constant)) { return candidate; } } throw new IllegalArgumentException("Constant [" + constant + "] does not exist in enum type " + enumValues.getClass().getComponentType().getName()); } /** * Append the given object to the given array, returning a new array * consisting of the input array contents plus the given object. * * @param array the array to append to (can be {@code null}) * @param obj the object to append * @return the new array (of the same component type; never {@code null}) */ public static A[] addObjectToArray(A[] array, O obj) { Class compType = Object.class; if (array != null) { compType = array.getClass().getComponentType(); } else if (obj != null) { compType = obj.getClass(); } int newArrLength = (array != null ? array.length + 1 : 1); @SuppressWarnings("unchecked") A[] newArr = (A[]) Array.newInstance(compType, newArrLength); if (array != null) { System.arraycopy(array, 0, newArr, 0, array.length); } newArr[newArr.length - 1] = obj; return newArr; } /** * Convert the given array (which may be a primitive array) to an * object array (if necessary of primitive wrapper objects). * *

    A {@code null} source value will be converted to an * empty Object array. * * @param source the (potentially primitive) array * @return the corresponding object array (never {@code null}) * @throws IllegalArgumentException if the parameter is not an array */ public static Object[] toObjectArray(Object source) { if (source instanceof Object[]) { return (Object[]) source; } if (source == null) { return EMPTY_OBJECT_ARRAY; } if (!source.getClass().isArray()) { throw new IllegalArgumentException("Source is not an array: " + source); } int length = Array.getLength(source); if (length == 0) { return EMPTY_OBJECT_ARRAY; } Class wrapperType = Array.get(source, 0).getClass(); Object[] newArray = (Object[]) Array.newInstance(wrapperType, length); for (int i = 0; i < length; i++) { newArray[i] = Array.get(source, i); } return newArray; } //--------------------------------------------------------------------- // Convenience methods for content-based equality/hash-code handling //--------------------------------------------------------------------- /** * Determine if the given objects are equal, returning {@code true} if * both are {@code null} or {@code false} if only one is {@code null}. * *

    Compares arrays with {@code Arrays.equals}, performing an equality * check based on the array elements rather than the array reference. * * @param o1 first Object to compare * @param o2 second Object to compare * @return whether the given objects are equal * @see Object#equals(Object) * @see Arrays#equals */ public static boolean nullSafeEquals(Object o1, Object o2) { if (o1 == o2) { return true; } if (o1 == null || o2 == null) { return false; } if (o1.equals(o2)) { return true; } if (o1.getClass().isArray() && o2.getClass().isArray()) { return arrayEquals(o1, o2); } return false; } /** * Compare the given arrays with {@code Arrays.equals}, performing an equality * check based on the array elements rather than the array reference. * * @param o1 first array to compare * @param o2 second array to compare * @return whether the given objects are equal * @see #nullSafeEquals(Object, Object) * @see Arrays#equals */ private static boolean arrayEquals(Object o1, Object o2) { if (o1 instanceof Object[] && o2 instanceof Object[]) { return Arrays.equals((Object[]) o1, (Object[]) o2); } if (o1 instanceof boolean[] && o2 instanceof boolean[]) { return Arrays.equals((boolean[]) o1, (boolean[]) o2); } if (o1 instanceof byte[] && o2 instanceof byte[]) { return Arrays.equals((byte[]) o1, (byte[]) o2); } if (o1 instanceof char[] && o2 instanceof char[]) { return Arrays.equals((char[]) o1, (char[]) o2); } if (o1 instanceof double[] && o2 instanceof double[]) { return Arrays.equals((double[]) o1, (double[]) o2); } if (o1 instanceof float[] && o2 instanceof float[]) { return Arrays.equals((float[]) o1, (float[]) o2); } if (o1 instanceof int[] && o2 instanceof int[]) { return Arrays.equals((int[]) o1, (int[]) o2); } if (o1 instanceof long[] && o2 instanceof long[]) { return Arrays.equals((long[]) o1, (long[]) o2); } if (o1 instanceof short[] && o2 instanceof short[]) { return Arrays.equals((short[]) o1, (short[]) o2); } return false; } /** * Return as hash code for the given object; typically the value of * {@code Object#hashCode()}}. If the object is an array, * this method will delegate to any of the {@code nullSafeHashCode} * methods for arrays in this class. If the object is {@code null}, * this method returns 0. * * @see Object#hashCode() * @see #nullSafeHashCode(Object[]) * @see #nullSafeHashCode(boolean[]) * @see #nullSafeHashCode(byte[]) * @see #nullSafeHashCode(char[]) * @see #nullSafeHashCode(double[]) * @see #nullSafeHashCode(float[]) * @see #nullSafeHashCode(int[]) * @see #nullSafeHashCode(long[]) * @see #nullSafeHashCode(short[]) */ public static int nullSafeHashCode(Object obj) { if (obj == null) { return 0; } if (obj.getClass().isArray()) { if (obj instanceof Object[]) { return nullSafeHashCode((Object[]) obj); } if (obj instanceof boolean[]) { return nullSafeHashCode((boolean[]) obj); } if (obj instanceof byte[]) { return nullSafeHashCode((byte[]) obj); } if (obj instanceof char[]) { return nullSafeHashCode((char[]) obj); } if (obj instanceof double[]) { return nullSafeHashCode((double[]) obj); } if (obj instanceof float[]) { return nullSafeHashCode((float[]) obj); } if (obj instanceof int[]) { return nullSafeHashCode((int[]) obj); } if (obj instanceof long[]) { return nullSafeHashCode((long[]) obj); } if (obj instanceof short[]) { return nullSafeHashCode((short[]) obj); } } return obj.hashCode(); } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(Object[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (Object element : array) { hash = MULTIPLIER * hash + nullSafeHashCode(element); } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(boolean[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (boolean element : array) { hash = MULTIPLIER * hash + Boolean.hashCode(element); } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(byte[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (byte element : array) { hash = MULTIPLIER * hash + element; } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(char[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (char element : array) { hash = MULTIPLIER * hash + element; } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(double[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (double element : array) { hash = MULTIPLIER * hash + Double.hashCode(element); } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(float[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (float element : array) { hash = MULTIPLIER * hash + Float.hashCode(element); } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(int[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (int element : array) { hash = MULTIPLIER * hash + element; } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(long[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (long element : array) { hash = MULTIPLIER * hash + Long.hashCode(element); } return hash; } /** * Return a hash code based on the contents of the specified array. * If {@code array} is {@code null}, this method returns 0. */ public static int nullSafeHashCode(short[] array) { if (array == null) { return 0; } int hash = INITIAL_HASH; for (short element : array) { hash = MULTIPLIER * hash + element; } return hash; } /** * Return the same value as {@link Boolean#hashCode(boolean)}}. * * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant */ @Deprecated public static int hashCode(boolean bool) { return Boolean.hashCode(bool); } /** * Return the same value as {@link Double#hashCode(double)}}. * * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant */ @Deprecated public static int hashCode(double dbl) { return Double.hashCode(dbl); } /** * Return the same value as {@link Float#hashCode(float)}}. * * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant */ @Deprecated public static int hashCode(float flt) { return Float.hashCode(flt); } /** * Return the same value as {@link Long#hashCode(long)}}. * * @deprecated as of Spring Framework 5.0, in favor of the native JDK 8 variant */ @Deprecated public static int hashCode(long lng) { return Long.hashCode(lng); } //--------------------------------------------------------------------- // Convenience methods for toString output //--------------------------------------------------------------------- /** * Return a String representation of an object's overall identity. * * @param obj the object (may be {@code null}) * @return the object's identity as String representation, * or an empty String if the object was {@code null} */ public static String identityToString(Object obj) { if (obj == null) { return EMPTY_STRING; } return obj.getClass().getName() + "@" + getIdentityHexString(obj); } /** * Return a hex String form of an object's identity hash code. * * @param obj the object * @return the object's identity code in hex notation */ public static String getIdentityHexString(Object obj) { return Integer.toHexString(System.identityHashCode(obj)); } /** * Return a content-based String representation if {@code obj} is * not {@code null}; otherwise returns an empty String. * *

    Differs from {@link #nullSafeToString(Object)} in that it returns * an empty String rather than "null" for a {@code null} value. * * @param obj the object to build a display String for * @return a display String representation of {@code obj} * @see #nullSafeToString(Object) */ public static String getDisplayString(Object obj) { if (obj == null) { return EMPTY_STRING; } return nullSafeToString(obj); } /** * Determine the class name for the given object. * *

    Returns a {@code "null"} String if {@code obj} is {@code null}. * * @param obj the object to introspect (may be {@code null}) * @return the corresponding class name */ public static String nullSafeClassName(Object obj) { return (obj != null ? obj.getClass().getName() : NULL_STRING); } /** * Return a String representation of the specified Object. * *

    Builds a String representation of the contents in case of an array. * Returns a {@code "null"} String if {@code obj} is {@code null}. * * @param obj the object to build a String representation for * @return a String representation of {@code obj} */ public static String nullSafeToString(Object obj) { if (obj == null) { return NULL_STRING; } if (obj instanceof String) { return (String) obj; } if (obj instanceof Object[]) { return nullSafeToString((Object[]) obj); } if (obj instanceof boolean[]) { return nullSafeToString((boolean[]) obj); } if (obj instanceof byte[]) { return nullSafeToString((byte[]) obj); } if (obj instanceof char[]) { return nullSafeToString((char[]) obj); } if (obj instanceof double[]) { return nullSafeToString((double[]) obj); } if (obj instanceof float[]) { return nullSafeToString((float[]) obj); } if (obj instanceof int[]) { return nullSafeToString((int[]) obj); } if (obj instanceof long[]) { return nullSafeToString((long[]) obj); } if (obj instanceof short[]) { return nullSafeToString((short[]) obj); } String str = obj.toString(); return (str != null ? str : EMPTY_STRING); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(Object[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (Object o : array) { stringJoiner.add(String.valueOf(o)); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(boolean[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (boolean b : array) { stringJoiner.add(String.valueOf(b)); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(byte[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (byte b : array) { stringJoiner.add(String.valueOf(b)); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(char[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (char c : array) { stringJoiner.add('\'' + String.valueOf(c) + '\''); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(double[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (double d : array) { stringJoiner.add(String.valueOf(d)); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(float[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (float f : array) { stringJoiner.add(String.valueOf(f)); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(int[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (int i : array) { stringJoiner.add(String.valueOf(i)); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(long[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (long l : array) { stringJoiner.add(String.valueOf(l)); } return stringJoiner.toString(); } /** * Return a String representation of the contents of the specified array. * *

    The String representation consists of a list of the array's elements, * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated * by the characters {@code ", "} (a comma followed by a space). * Returns a {@code "null"} String if {@code array} is {@code null}. * * @param array the array to build a String representation for * @return a String representation of {@code array} */ public static String nullSafeToString(short[] array) { if (array == null) { return NULL_STRING; } int length = array.length; if (length == 0) { return EMPTY_ARRAY; } StringJoiner stringJoiner = new StringJoiner(ARRAY_ELEMENT_SEPARATOR, ARRAY_START, ARRAY_END); for (short s : array) { stringJoiner.add(String.valueOf(s)); } return stringJoiner.toString(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/util/NestedExceptionUtils.java ================================================ /* * Copyright 2002-2017 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 com.alibaba.nacos.common.packagescan.util; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Helper class for implementing exception classes which are capable of * holding nested exceptions. Necessary because we can't share a base * class among different exception types. * *

    Mainly for use within the framework. * * @author Juergen Hoeller * @see NestedIoException * @since 2.0 */ public abstract class NestedExceptionUtils { /** * Build a message for the given base message and root cause. * * @param message the base message * @param cause the root cause * @return the full exception message */ public static String buildMessage(String message, Throwable cause) { if (cause == null) { return message; } StringBuilder sb = new StringBuilder(64); if (message != null) { sb.append(message).append("; "); } sb.append("nested exception is ").append(cause); return sb.toString(); } /** * Retrieve the innermost cause of the given exception, if any. * * @param original the original exception to introspect * @return the innermost exception, or {@code null} if none * @since 4.3.9 */ public static Throwable getRootCause(Throwable original) { if (original == null) { return null; } Throwable rootCause = null; Throwable cause = original.getCause(); while (cause != null && cause != rootCause) { rootCause = cause; cause = cause.getCause(); } return rootCause; } /** * Retrieve the most specific cause of the given exception, that is, * either the innermost cause (root cause) or the exception itself. * *

    Differs from {@link #getRootCause} in that it falls back * to the original exception if there is no root cause. * * @param original the original exception to introspect * @return the most specific cause (never {@code null}) * @since 4.3.9 */ public static Throwable getMostSpecificCause(Throwable original) { Throwable rootCause = getRootCause(original); return (rootCause != null ? rootCause : original); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/util/NestedIoException.java ================================================ /* * Copyright 2002-2017 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 com.alibaba.nacos.common.packagescan.util; import java.io.IOException; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Subclass of {@link IOException} that properly handles a root cause, * exposing the root cause just like NestedChecked/RuntimeException does. * *

    Proper root cause handling has not been added to standard IOException before * Java 6, which is why we need to do it ourselves for Java 5 compatibility purposes. * *

    The similarity between this class and the NestedChecked/RuntimeException * class is unavoidable, as this class needs to derive from IOException. * * @author Juergen Hoeller * @see #getMessage * @since 2.0 */ @SuppressWarnings("serial") public class NestedIoException extends IOException { static { // Eagerly load the NestedExceptionUtils class to avoid classloader deadlock // issues on OSGi when calling getMessage(). Reported by Don Brown; SPR-5607. NestedExceptionUtils.class.getName(); } /** * Construct a {@code NestedIOException} with the specified detail message. * * @param msg the detail message */ public NestedIoException(String msg) { super(msg); } /** * Construct a {@code NestedIOException} with the specified detail message * and nested exception. * * @param msg the detail message * @param cause the nested exception */ public NestedIoException(String msg, Throwable cause) { super(msg, cause); } /** * Return the detail message, including the message from the nested exception * if there is one. */ @Override public String getMessage() { return NestedExceptionUtils.buildMessage(super.getMessage(), getCause()); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/util/PathMatcher.java ================================================ /* * Copyright 2002-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 com.alibaba.nacos.common.packagescan.util; import com.alibaba.nacos.common.packagescan.resource.AntPathMatcher; import com.alibaba.nacos.common.packagescan.resource.PathMatchingResourcePatternResolver; import java.util.Comparator; import java.util.Map; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Strategy interface for {@code String}-based path matching. * *

    Used by {@link PathMatchingResourcePatternResolver}, * *

    The default implementation is {@link AntPathMatcher}, supporting the * Ant-style pattern syntax. * * @author Juergen Hoeller * @see AntPathMatcher * @since 1.2 */ public interface PathMatcher { /** * Does the given {@code path} represent a pattern that can be matched * by an implementation of this interface? * *

    If the return value is {@code false}, then the {@link #match} * method does not have to be used because direct equality comparisons * on the static path Strings will lead to the same result. * * @param path the path to check * @return {@code true} if the given {@code path} represents a pattern */ boolean isPattern(String path); /** * Match the given {@code path} against the given {@code pattern}, * according to this PathMatcher's matching strategy. * * @param pattern the pattern to match against * @param path the path to test * @return {@code true} if the supplied {@code path} matched, * {@code false} if it didn't */ boolean match(String pattern, String path); /** * Match the given {@code path} against the corresponding part of the given * {@code pattern}, according to this PathMatcher's matching strategy. * *

    Determines whether the pattern at least matches as far as the given base * path goes, assuming that a full path may then match as well. * * @param pattern the pattern to match against * @param path the path to test * @return {@code true} if the supplied {@code path} matched, * {@code false} if it didn't */ boolean matchStart(String pattern, String path); /** * Given a pattern and a full path, determine the pattern-mapped part. * *

    This method is supposed to find out which part of the path is matched * dynamically through an actual pattern, that is, it strips off a statically * defined leading path from the given full path, returning only the actually * pattern-matched part of the path. * *

    For example: For "myroot/*.html" as pattern and "myroot/myfile.html" * as full path, this method should return "myfile.html". The detailed * determination rules are specified to this PathMatcher's matching strategy. * *

    A simple implementation may return the given full path as-is in case * of an actual pattern, and the empty String in case of the pattern not * containing any dynamic parts (i.e. the {@code pattern} parameter being * a static path that wouldn't qualify as an actual {@link #isPattern pattern}). * A sophisticated implementation will differentiate between the static parts * and the dynamic parts of the given path pattern. * * @param pattern the path pattern * @param path the full path to introspect * @return the pattern-mapped part of the given {@code path} * (never {@code null}) */ String extractPathWithinPattern(String pattern, String path); /** * Given a pattern and a full path, extract the URI template variables. URI template * variables are expressed through curly brackets ('{' and '}'). * *

    For example: For pattern "/hotels/{hotel}" and path "/hotels/1", this method will * return a map containing "hotel" → "1". * * @param pattern the path pattern, possibly containing URI templates * @param path the full path to extract template variables from * @return a map, containing variable names as keys; variables values as values */ Map extractUriTemplateVariables(String pattern, String path); /** * Given a full path, returns a {@link Comparator} suitable for sorting patterns * in order of explicitness for that path. * *

    The full algorithm used depends on the underlying implementation, * but generally, the returned {@code Comparator} will * {@linkplain java.util.List#sort(Comparator) sort} * a list so that more specific patterns come before generic patterns. * * @param path the full path to use for comparison * @return a comparator capable of sorting patterns in order of explicitness */ Comparator getPatternComparator(String path); /** * Combines two patterns into a new pattern that is returned. * *

    The full algorithm used for combining the two pattern depends on the underlying implementation. * * @param pattern1 the first pattern * @param pattern2 the second pattern * @return the combination of the two patterns * @throws IllegalArgumentException when the two patterns cannot be combined */ String combine(String pattern1, String pattern2); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/packagescan/util/ResourceUtils.java ================================================ /* * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.common.packagescan.util; import com.alibaba.nacos.common.packagescan.resource.Resource; import com.alibaba.nacos.common.utils.ClassUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.io.File; import java.io.FileNotFoundException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.net.URLConnection; /** * Copy from https://github.com/spring-projects/spring-framework.git, with less modifications * Utility methods for resolving resource locations to files in the * file system. Mainly for internal use within the framework. * *

    Consider using Spring's Resource abstraction in the core package * for handling all kinds of file resources in a uniform manner. * method can resolve any location to a {@link Resource} * object, which in turn allows one to obtain a {@code java.io.File} in the * file system through its {@code getFile()} method. * * @author Juergen Hoeller * @since 1.1.5 */ public abstract class ResourceUtils { /** * Pseudo URL prefix for loading from the class path: "classpath:". */ public static final String CLASSPATH_URL_PREFIX = "classpath:"; /** * URL prefix for loading from the file system: "file:". */ public static final String FILE_URL_PREFIX = "file:"; /** * URL prefix for loading from a jar file: "jar:". */ public static final String JAR_URL_PREFIX = "jar:"; /** * URL prefix for loading from a war file on Tomcat: "war:". */ public static final String WAR_URL_PREFIX = "war:"; /** * URL protocol for a file in the file system: "file". */ public static final String URL_PROTOCOL_FILE = "file"; /** * URL protocol for an entry from a jar file: "jar". */ public static final String URL_PROTOCOL_JAR = "jar"; /** * URL protocol for an entry from a war file: "war". */ public static final String URL_PROTOCOL_WAR = "war"; /** * URL protocol for an entry from a zip file: "zip". */ public static final String URL_PROTOCOL_ZIP = "zip"; /** * URL protocol for an entry from a WebSphere jar file: "wsjar". */ public static final String URL_PROTOCOL_WSJAR = "wsjar"; /** * URL protocol for an entry from a JBoss jar file: "vfszip". */ public static final String URL_PROTOCOL_VFSZIP = "vfszip"; /** * URL protocol for a JBoss file system resource: "vfsfile". */ public static final String URL_PROTOCOL_VFSFILE = "vfsfile"; /** * URL protocol for a general JBoss VFS resource: "vfs". */ public static final String URL_PROTOCOL_VFS = "vfs"; /** * File extension for a regular jar file: ".jar". */ public static final String JAR_FILE_EXTENSION = ".jar"; /** * Separator between JAR URL and file path within the JAR: "!/". */ public static final String JAR_URL_SEPARATOR = "!/"; /** * Special separator between WAR URL and jar part on Tomcat. */ public static final String WAR_URL_SEPARATOR = "*/"; /** * Return whether the given resource location is a URL: * either a special "classpath" pseudo URL or a standard URL. * * @param resourceLocation the location String to check * @return whether the location qualifies as a URL * @see #CLASSPATH_URL_PREFIX * @see URL */ public static boolean isUrl(String resourceLocation) { if (resourceLocation == null) { return false; } if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { return true; } try { new URL(resourceLocation); return true; } catch (MalformedURLException ex) { return false; } } /** * Resolve the given resource location to a {@code java.io.File}, * i.e. to a file in the file system. * *

    Does not check whether the file actually exists; simply returns * the File that the given location would correspond to. * * @param resourceLocation the resource location to resolve: either a * "classpath:" pseudo URL, a "file:" URL, or a plain file path * @return a corresponding File object * @throws FileNotFoundException if the resource cannot be resolved to * a file in the file system */ public static File getFile(String resourceLocation) throws FileNotFoundException { AbstractAssert.notNull(resourceLocation, "Resource location must not be null"); if (resourceLocation.startsWith(CLASSPATH_URL_PREFIX)) { String path = resourceLocation.substring(CLASSPATH_URL_PREFIX.length()); String description = "class path resource [" + path + "]"; ClassLoader cl = ClassUtils.getDefaultClassLoader(); URL url = (cl != null ? cl.getResource(path) : ClassLoader.getSystemResource(path)); if (url == null) { throw new FileNotFoundException(description + " cannot be resolved to absolute file path because it does not exist"); } return getFile(url, description); } try { // try URL return getFile(new URL(resourceLocation)); } catch (MalformedURLException ex) { // no URL -> treat as file path return new File(resourceLocation); } } /** * Resolve the given resource URL to a {@code java.io.File}, * i.e. to a file in the file system. * * @param resourceUrl the resource URL to resolve * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system */ public static File getFile(URL resourceUrl) throws FileNotFoundException { return getFile(resourceUrl, "URL"); } /** * Resolve the given resource URL to a {@code java.io.File}, * i.e. to a file in the file system. * * @param resourceUrl the resource URL to resolve * @param description a description of the original resource that * the URL was created for (for example, a class path location) * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system */ public static File getFile(URL resourceUrl, String description) throws FileNotFoundException { AbstractAssert.notNull(resourceUrl, "Resource URL must not be null"); if (!URL_PROTOCOL_FILE.equals(resourceUrl.getProtocol())) { throw new FileNotFoundException( description + " cannot be resolved to absolute file path " + "because it does not reside in the file system: " + resourceUrl); } try { return new File(toUri(resourceUrl).getSchemeSpecificPart()); } catch (URISyntaxException ex) { // Fallback for URLs that are not valid URIs (should hardly ever happen). return new File(resourceUrl.getFile()); } } /** * Resolve the given resource URI to a {@code java.io.File}, * i.e. to a file in the file system. * * @param resourceUri the resource URI to resolve * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system * @since 2.5 */ public static File getFile(URI resourceUri) throws FileNotFoundException { return getFile(resourceUri, "URI"); } /** * Resolve the given resource URI to a {@code java.io.File}, * i.e. to a file in the file system. * * @param resourceUri the resource URI to resolve * @param description a description of the original resource that * the URI was created for (for example, a class path location) * @return a corresponding File object * @throws FileNotFoundException if the URL cannot be resolved to * a file in the file system * @since 2.5 */ public static File getFile(URI resourceUri, String description) throws FileNotFoundException { AbstractAssert.notNull(resourceUri, "Resource URI must not be null"); if (!URL_PROTOCOL_FILE.equals(resourceUri.getScheme())) { throw new FileNotFoundException( description + " cannot be resolved to absolute file path " + "because it does not reside in the file system: " + resourceUri); } return new File(resourceUri.getSchemeSpecificPart()); } /** * Determine whether the given URL points to a resource in the file system, * i.e. has protocol "file", "vfsfile" or "vfs". * * @param url the URL to check * @return whether the URL has been identified as a file system URL */ public static boolean isFileUrl(URL url) { String protocol = url.getProtocol(); return (URL_PROTOCOL_FILE.equals(protocol) || URL_PROTOCOL_VFSFILE.equals(protocol) || URL_PROTOCOL_VFS.equals(protocol)); } /** * Determine whether the given URL points to a resource in a jar file. * i.e. has protocol "jar", "war, ""zip", "vfszip" or "wsjar". * * @param url the URL to check * @return whether the URL has been identified as a JAR URL */ public static boolean isJarUrl(URL url) { String protocol = url.getProtocol(); return (URL_PROTOCOL_JAR.equals(protocol) || URL_PROTOCOL_WAR.equals(protocol) || URL_PROTOCOL_ZIP.equals(protocol) || URL_PROTOCOL_VFSZIP.equals(protocol) || URL_PROTOCOL_WSJAR.equals(protocol)); } /** * Determine whether the given URL points to a jar file itself, * that is, has protocol "file" and ends with the ".jar" extension. * * @param url the URL to check * @return whether the URL has been identified as a JAR file URL * @since 4.1 */ public static boolean isJarFileUrl(URL url) { return (URL_PROTOCOL_FILE.equals(url.getProtocol()) && url.getPath().toLowerCase().endsWith(JAR_FILE_EXTENSION)); } /** * Extract the URL for the actual jar file from the given URL * (which may point to a resource in a jar file or to a jar file itself). * * @param jarUrl the original URL * @return the URL for the actual jar file * @throws MalformedURLException if no valid jar file URL could be extracted */ public static URL extractJarFileUrl(URL jarUrl) throws MalformedURLException { String urlFile = jarUrl.getFile(); int separatorIndex = urlFile.indexOf(JAR_URL_SEPARATOR); if (separatorIndex != -1) { String jarFile = urlFile.substring(0, separatorIndex); try { return new URL(jarFile); } catch (MalformedURLException ex) { // Probably no protocol in original jar URL, like "jar:C:/mypath/myjar.jar". // This usually indicates that the jar file resides in the file system. if (!jarFile.startsWith("/")) { jarFile = "/" + jarFile; } return new URL(FILE_URL_PREFIX + jarFile); } } else { return jarUrl; } } /** * Extract the URL for the outermost archive from the given jar/war URL * (which may point to a resource in a jar file or to a jar file itself). * *

    In the case of a jar file nested within a war file, this will return * a URL to the war file since that is the one resolvable in the file system. * * @param jarUrl the original URL * @return the URL for the actual jar file * @throws MalformedURLException if no valid jar file URL could be extracted * @see #extractJarFileUrl(URL) * @since 4.1.8 */ public static URL extractArchiveUrl(URL jarUrl) throws MalformedURLException { String urlFile = jarUrl.getFile(); int endIndex = urlFile.indexOf(WAR_URL_SEPARATOR); if (endIndex != -1) { // Tomcat's "war:file:...mywar.war*/WEB-INF/lib/myjar.jar!/myentry.txt" String warFile = urlFile.substring(0, endIndex); if (URL_PROTOCOL_WAR.equals(jarUrl.getProtocol())) { return new URL(warFile); } int startIndex = warFile.indexOf(WAR_URL_PREFIX); if (startIndex != -1) { return new URL(warFile.substring(startIndex + WAR_URL_PREFIX.length())); } } // Regular "jar:file:...myjar.jar!/myentry.txt" return extractJarFileUrl(jarUrl); } /** * Create a URI instance for the given URL, * replacing spaces with "%20" URI encoding first. * * @param url the URL to convert into a URI instance * @return the URI instance * @throws URISyntaxException if the URL wasn't a valid URI * @see URL#toURI() */ public static URI toUri(URL url) throws URISyntaxException { return toUri(url.toString()); } /** * Create a URI instance for the given location String, * replacing spaces with "%20" URI encoding first. * * @param location the location String to convert into a URI instance * @return the URI instance * @throws URISyntaxException if the location wasn't a valid URI */ public static URI toUri(String location) throws URISyntaxException { return new URI(StringUtils.replace(location, " ", "%20")); } /** * Set the {@link URLConnection#setUseCaches "useCaches"} flag on the * given connection, preferring {@code false} but leaving the * flag at {@code true} for JNLP based resources. * * @param con the URLConnection to set the flag on */ public static void useCachesIfNecessary(URLConnection con) { con.setUseCaches(con.getClass().getSimpleName().startsWith("JNLP")); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/paramcheck/AbstractParamChecker.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; import java.util.List; /** * The type Abstract param checker. * * @author zhuoguang */ public abstract class AbstractParamChecker { protected ParamCheckRule paramCheckRule; public AbstractParamChecker() { initParamCheckRule(); } /** * Gets checker type. * * @return the checker type */ public abstract String getCheckerType(); /** * Check param info list param check response. * * @param paramInfos the param infos * @return the param check response */ public abstract ParamCheckResponse checkParamInfoList(List paramInfos); /** * Init param check rule. */ public abstract void initParamCheckRule(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/paramcheck/DefaultParamChecker.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; import com.alibaba.nacos.common.utils.NumberUtils; import com.alibaba.nacos.common.utils.PropertyUtils; import com.alibaba.nacos.common.utils.StringUtils; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** * The type Default param checker. * * @author zhuoguang */ public class DefaultParamChecker extends AbstractParamChecker { private Pattern namespaceShowNamePattern; private Pattern namespaceIdPattern; private Pattern dataIdPattern; private Pattern serviceNamePattern; private Pattern groupPattern; private Pattern clusterPattern; private Pattern ipPattern; private Pattern mcpNamePattern; private Pattern agentNamePattern; private static final String CHECKER_TYPE = "default"; private static final String MAX_METADATA_LENGTH_PROP_NAME = "nacos.naming.service.metadata.length"; private static final String MAX_METADATA_LENGTH_ENV_NAME = "NACOS_NAMING_SERVICE_METADATA_LENGTH"; @Override public String getCheckerType() { return CHECKER_TYPE; } @Override public ParamCheckResponse checkParamInfoList(List paramInfos) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (paramInfos == null) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } for (ParamInfo paramInfo : paramInfos) { paramCheckResponse = checkParamInfoFormat(paramInfo); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } } paramCheckResponse.setSuccess(true); return paramCheckResponse; } @Override public void initParamCheckRule() { this.paramCheckRule = new ParamCheckRule(); initFormatPattern(); replaceParamCheckRuleByEnv(); } private void initFormatPattern() { this.namespaceShowNamePattern = Pattern.compile(this.paramCheckRule.namespaceShowNamePatternString); this.namespaceIdPattern = Pattern.compile(this.paramCheckRule.namespaceIdPatternString); this.dataIdPattern = Pattern.compile(this.paramCheckRule.dataIdPatternString); this.serviceNamePattern = Pattern.compile(this.paramCheckRule.serviceNamePatternString); this.groupPattern = Pattern.compile(this.paramCheckRule.groupPatternString); this.clusterPattern = Pattern.compile(this.paramCheckRule.clusterPatternString); this.ipPattern = Pattern.compile(this.paramCheckRule.ipPatternString); this.mcpNamePattern = Pattern.compile(this.paramCheckRule.mcpNamePatternString); this.agentNamePattern = Pattern.compile(this.paramCheckRule.agentNamePatternString); } /** * if environment variables exists, it will be replaced. */ private void replaceParamCheckRuleByEnv() { String maxMetadataLength = PropertyUtils.getProperty(MAX_METADATA_LENGTH_PROP_NAME, MAX_METADATA_LENGTH_ENV_NAME); if (StringUtils.isNotBlank(maxMetadataLength)) { this.paramCheckRule.maxMetadataLength = NumberUtils.toInt(maxMetadataLength); } } /** * Check param info format. * * @param paramInfo the param info * @return the param check response */ public ParamCheckResponse checkParamInfoFormat(ParamInfo paramInfo) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (paramInfo == null) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } paramCheckResponse = checkNamespaceShowNameFormat(paramInfo.getNamespaceShowName()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkNamespaceIdFormat(paramInfo.getNamespaceId()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkDataIdFormat(paramInfo.getDataId()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkServiceNameFormat(paramInfo.getServiceName()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkGroupFormat(paramInfo.getGroup()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkClusterFormat(paramInfo.getClusters()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkSingleClusterFormat(paramInfo.getCluster()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkIpFormat(paramInfo.getIp()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkPortFormat(paramInfo.getPort()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkMetadataFormat(paramInfo.getMetadata()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkMcpNameFormat(paramInfo.getMcpName()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse = checkAgentNameFormat(paramInfo.getAgentName()); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check namespace show name format. * * @param namespaceShowName the namespace show name * @return the param check response */ public ParamCheckResponse checkNamespaceShowNameFormat(String namespaceShowName) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(namespaceShowName)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (namespaceShowName.length() > paramCheckRule.maxNamespaceShowNameLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage(String.format("Param 'namespaceShowName' is illegal, the param length should not exceed %d.", paramCheckRule.maxNamespaceShowNameLength)); return paramCheckResponse; } if (!namespaceShowNamePattern.matcher(namespaceShowName).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'namespaceShowName' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check namespace id format. * * @param namespaceId the namespace id * @return the param check response */ public ParamCheckResponse checkNamespaceIdFormat(String namespaceId) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(namespaceId)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (namespaceId.length() > paramCheckRule.maxNamespaceIdLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage(String.format("Param 'namespaceId/tenant' is illegal, the param length should not exceed %d.", paramCheckRule.maxNamespaceIdLength)); return paramCheckResponse; } if (!namespaceIdPattern.matcher(namespaceId).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'namespaceId/tenant' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check data id format. * * @param dataId the data id * @return the param check response */ public ParamCheckResponse checkDataIdFormat(String dataId) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(dataId)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (dataId.length() > paramCheckRule.maxDataIdLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'dataId' is illegal, the param length should not exceed %d.", paramCheckRule.maxDataIdLength)); return paramCheckResponse; } if (!dataIdPattern.matcher(dataId).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'dataId' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check service name format. * * @param serviceName the service name * @return the param check response */ public ParamCheckResponse checkServiceNameFormat(String serviceName) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(serviceName)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (serviceName.length() > paramCheckRule.maxServiceNameLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'serviceName' is illegal, the param length should not exceed %d.", paramCheckRule.maxServiceNameLength)); return paramCheckResponse; } if (!serviceNamePattern.matcher(serviceName).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'serviceName' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check group format. * * @param group the group * @return the param check response */ public ParamCheckResponse checkGroupFormat(String group) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(group)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (group.length() > paramCheckRule.maxGroupLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'group' is illegal, the param length should not exceed %d.", paramCheckRule.maxGroupLength)); return paramCheckResponse; } if (!groupPattern.matcher(group).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'group' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check cluster format. * * @param clusterString the cluster string * @return the param check response */ public ParamCheckResponse checkClusterFormat(String clusterString) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(clusterString)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } String[] clusters = clusterString.split(","); for (String cluster : clusters) { paramCheckResponse = checkSingleClusterFormat(cluster); if (!paramCheckResponse.isSuccess()) { return paramCheckResponse; } } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check single cluster format. * * @param cluster the cluster * @return the param check response */ public ParamCheckResponse checkSingleClusterFormat(String cluster) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(cluster)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (cluster.length() > paramCheckRule.maxClusterLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'cluster' is illegal, the param length should not exceed %d.", paramCheckRule.maxClusterLength)); return paramCheckResponse; } if (!clusterPattern.matcher(cluster).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'cluster' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check ip format. * * @param ip the ip * @return the param check response */ public ParamCheckResponse checkIpFormat(String ip) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(ip)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (ip.length() > paramCheckRule.maxIpLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage(String.format("Param 'ip' is illegal, the param length should not exceed %d.", paramCheckRule.maxIpLength)); return paramCheckResponse; } if (!ipPattern.matcher(ip).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'ip' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check port format. * * @param port the port * @return the param check response */ public ParamCheckResponse checkPortFormat(String port) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(port)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } int portInt = 0; try { portInt = Integer.parseInt(port); } catch (Exception e) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'port' is illegal, the value should be between %d and %d.", paramCheckRule.minPort, paramCheckRule.maxPort)); return paramCheckResponse; } if (portInt > paramCheckRule.maxPort || portInt < paramCheckRule.minPort) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'port' is illegal, the value should be between %d and %d.", paramCheckRule.minPort, paramCheckRule.maxPort)); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check metadata format. * * @param metadata the metadata * @return the param check response */ public ParamCheckResponse checkMetadataFormat(Map metadata) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (metadata == null || metadata.isEmpty()) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } int totalLength = 0; for (Map.Entry entry : metadata.entrySet()) { if (StringUtils.isNotBlank(entry.getKey())) { totalLength = totalLength + entry.getKey().length(); } if (StringUtils.isNotBlank(entry.getValue())) { totalLength = totalLength + entry.getValue().length(); } } if (totalLength > paramCheckRule.maxMetadataLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'Metadata' is illegal, the param length should not exceed %d.", paramCheckRule.maxMetadataLength)); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check mcp name format. * * @param mcpName the mcp name * @return the param check response */ public ParamCheckResponse checkMcpNameFormat(String mcpName) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(mcpName)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (mcpName.length() > paramCheckRule.maxMcpNameLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'mcpName' is illegal, the param length should not exceed %d.", paramCheckRule.maxClusterLength)); return paramCheckResponse; } if (!mcpNamePattern.matcher(mcpName).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'mcpName' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } /** * Check agent name format. * * @param agentName agent name * @return the param check response */ public ParamCheckResponse checkAgentNameFormat(String agentName) { ParamCheckResponse paramCheckResponse = new ParamCheckResponse(); if (StringUtils.isBlank(agentName)) { paramCheckResponse.setSuccess(true); return paramCheckResponse; } if (agentName.length() > paramCheckRule.maxAgentNameLength) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage( String.format("Param 'agentName' is illegal, the param length should not exceed %d.", paramCheckRule.maxAgentNameLength)); return paramCheckResponse; } if (!agentNamePattern.matcher(agentName).matches()) { paramCheckResponse.setSuccess(false); paramCheckResponse.setMessage("Param 'agentName' is illegal, illegal characters should not appear in the param."); return paramCheckResponse; } paramCheckResponse.setSuccess(true); return paramCheckResponse; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/paramcheck/ParamCheckResponse.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; /** * The type Param check response. * * @author zhuoguang */ public class ParamCheckResponse { private boolean success; private String message; public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/paramcheck/ParamCheckRule.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; /** * Param check rules. * * @author zhuoguang */ public class ParamCheckRule { public int maxNamespaceShowNameLength = 256; public String namespaceShowNamePatternString = "^[^@#$%^&*]+$"; public int maxNamespaceIdLength = 64; public String namespaceIdPatternString = "^[\\w-]+"; public int maxDataIdLength = 256; public String dataIdPatternString = "^[a-zA-Z0-9-_:\\.]*$"; public int maxServiceNameLength = 512; public String serviceNamePatternString = "^(?!@).((?!@@)[^\\u4E00-\\u9FA5\\s])*$"; public int maxGroupLength = 128; public String groupPatternString = "^[a-zA-Z0-9-_:\\.]*$"; public int maxClusterLength = 64; public String clusterPatternString = "^[0-9a-zA-Z-_]+$"; public int maxIpLength = 128; public String ipPatternString = "^[^\\u4E00-\\u9FA5\\s]*$"; public int maxPort = 65535; public int minPort = 0; public int maxMetadataLength = 1024; public String agentNamePatternString = "^[\\x20-\\x7E]+$"; public int maxAgentNameLength = 64; public String mcpNamePatternString = "^[a-zA-Z0-9-_\\/\\.]+$"; public int maxMcpNameLength = 128; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/paramcheck/ParamCheckerManager.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * The type Param checker manager. * * @author zhuoguang */ public class ParamCheckerManager { private static final ParamCheckerManager INSTANCE = new ParamCheckerManager(); private static final AbstractParamChecker DEFAULT_PARAM_CHECKER = new DefaultParamChecker(); private final Map paramCheckerMap = new ConcurrentHashMap<>(); private ParamCheckerManager() { Collection paramCheckers = NacosServiceLoader.load(AbstractParamChecker.class); for (AbstractParamChecker paramChecker : paramCheckers) { String checkerType = paramChecker.getCheckerType(); paramCheckerMap.put(checkerType, paramChecker); } } public static ParamCheckerManager getInstance() { return INSTANCE; } public AbstractParamChecker getParamChecker(String checkerType) { if (StringUtils.isBlank(checkerType)) { return DEFAULT_PARAM_CHECKER; } AbstractParamChecker paramChecker = paramCheckerMap.get(checkerType); if (paramChecker == null) { paramChecker = DEFAULT_PARAM_CHECKER; } return paramChecker; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/paramcheck/ParamInfo.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; import java.util.Map; /** * Param info. * * @author zhuoguang */ public class ParamInfo { private String namespaceShowName; private String namespaceId; private String dataId; private String serviceName; private String group; private String cluster; private String clusters; private String ip; private String port; private Map metadata; private String mcpName; private String mcpId; private String agentName; public String getNamespaceShowName() { return namespaceShowName; } public void setNamespaceShowName(String namespaceShowName) { this.namespaceShowName = namespaceShowName; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getServiceName() { return serviceName; } public void setServiceName(String serviceName) { this.serviceName = serviceName; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getCluster() { return cluster; } public void setCluster(String cluster) { this.cluster = cluster; } public String getClusters() { return clusters; } public void setClusters(String clusters) { this.clusters = clusters; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public String getPort() { return port; } public void setPort(String port) { this.port = port; } public Map getMetadata() { return metadata; } public void setMetadata(Map metadata) { this.metadata = metadata; } public String getMcpName() { return mcpName; } public String getMcpId() { return mcpId; } public void setMcpId(String mcpId) { this.mcpId = mcpId; } public void setMcpName(String mcpName) { this.mcpName = mcpName; } public String getAgentName() { return agentName; } public void setAgentName(String agentName) { this.agentName = agentName; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/pathencoder/PathEncoder.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.pathencoder; /** * To encode path if illegal,an os may have a PathEncoder. * * @author daydreamer-ia */ public interface PathEncoder { /** * encode path. * * @param str origin * @param charset charset * @return new path */ String encode(String str, String charset); /** * decode path. * * @param str new path * @param charset charset * @return origin path */ String decode(String str, String charset); /** * return simple lowercase os name. * * @return simple lowercase os name */ String name(); /** * whether to encode. * * @param key key * @return whether to encode. */ boolean needEncode(String key); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/pathencoder/PathEncoderManager.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.pathencoder; import com.alibaba.nacos.common.spi.NacosServiceLoader; import java.nio.charset.Charset; import java.util.Collection; /** * To expose interface from {@link PathEncoder}. * * @author daydreamer-ia */ public class PathEncoderManager { /** * singleton. */ private static final PathEncoderManager INSTANCE = new PathEncoderManager(); /** * encoder. */ private PathEncoder targetEncoder = null; private PathEncoderManager() { // load path encoder Collection load = NacosServiceLoader.load(PathEncoder.class); if (!load.isEmpty()) { String currentOs = System.getProperty("os.name").toLowerCase(); for (PathEncoder pathEncoder : load) { // match first if (currentOs.contains(pathEncoder.name())) { targetEncoder = pathEncoder; break; } } } } /** * encode path if necessary. * * @param path origin path * @param charset charset of origin path * @return encoded path */ public String encode(String path, String charset) { if (path == null || charset == null) { return path; } if (targetEncoder != null && targetEncoder.needEncode(path)) { return targetEncoder.encode(path, charset); } return path; } /** * encode path if necessary. * * @param path origin path * @return encoded path */ public String encode(String path) { return encode(path, Charset.defaultCharset().name()); } /** * decode path. * * @param path encoded path * @param charset charset of encoded path * @return origin path */ public String decode(String path, String charset) { if (path == null || charset == null) { return path; } if (targetEncoder != null) { return targetEncoder.decode(path, charset); } return path; } /** * decode path. * * @param path encoded path * @return origin path */ public String decode(String path) { return decode(path, Charset.defaultCharset().name()); } /** * get singleton. * * @return singleton. */ public static PathEncoderManager getInstance() { return INSTANCE; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/pathencoder/impl/WindowsEncoder.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.pathencoder.impl; import com.alibaba.nacos.common.pathencoder.PathEncoder; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; /** * Encode path if illegal char reach in Windows. * * @author daydreamer-ia */ public class WindowsEncoder implements PathEncoder { /** * 不应该含有 / : ? " < > | \. */ private static final String PATTERN_EXP = "[^/:*?\"<>|\\\\]+"; private static final Map REG_MAPPING = new HashMap<>(); private static final Map CHAR_MAPPING = new HashMap<>(); private static final Pattern PATTERN = Pattern.compile(PATTERN_EXP); static { // reg REG_MAPPING.put("\\\\", "%A1%"); REG_MAPPING.put("/", "%A2%"); REG_MAPPING.put(":", "%A3%"); REG_MAPPING.put("\\*", "%A4%"); REG_MAPPING.put("\\?", "%A5%"); REG_MAPPING.put("\"", "%A6%"); REG_MAPPING.put("<", "%A7%"); REG_MAPPING.put(">", "%A8%"); REG_MAPPING.put("\\|", "%A9%"); // char CHAR_MAPPING.put("%A1%", "\\\\"); CHAR_MAPPING.put("%A2%", "/"); CHAR_MAPPING.put("%A3%", ":"); CHAR_MAPPING.put("%A4%", "*"); CHAR_MAPPING.put("%A5%", "?"); CHAR_MAPPING.put("%A6%", "\""); CHAR_MAPPING.put("%A7%", "<"); CHAR_MAPPING.put("%A8%", ">"); CHAR_MAPPING.put("%A9%", "|"); } @Override public String encode(String str, String charset) { for (Map.Entry entry : REG_MAPPING.entrySet()) { str = str.replaceAll(entry.getKey(), entry.getValue()); } return str; } @Override public String decode(String str, String charset) { for (Map.Entry entry : CHAR_MAPPING.entrySet()) { str = str.replaceAll(entry.getKey(), entry.getValue()); } return str; } @Override public String name() { return "window"; } @Override public boolean needEncode(String key) { if (key == null) { return false; } return !PATTERN.matcher(key).matches(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/ConnectionType.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote; /** * ConnectionType. * * @author liuzunfei * @version $Id: ConnectionType.java, v 0.1 2020年07月13日 7:15 PM liuzunfei Exp $ */ public enum ConnectionType { /** * gRPC connection. */ GRPC("GRPC", "Grpc Connection"); final String type; final String name; public static ConnectionType getByType(String type) { ConnectionType[] values = ConnectionType.values(); for (ConnectionType connectionType : values) { if (connectionType.getType().equals(type)) { return connectionType; } } return null; } ConnectionType(String type, String name) { this.type = type; this.name = name; } /** * Getter method for property type. * * @return property value of type */ public String getType() { return type; } /** * Getter method for property name. * * @return property value of name */ public String getName() { return name; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/PayloadRegistry.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote; import com.alibaba.nacos.api.remote.Payload; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import java.util.ServiceLoader; /** * payload registry,Define basic scan behavior request and response. * * @author liuzunfei * @author hujun * @version $Id: PayloadRegistry.java, v 0.1 2020年09月01日 10:56 AM liuzunfei Exp $ */ public class PayloadRegistry { private static final Map> REGISTRY_REQUEST = new HashMap<>(); static boolean initialized = false; public static void init() { scan(); } private static synchronized void scan() { if (initialized) { return; } ServiceLoader payloads = ServiceLoader.load(Payload.class); for (Payload payload : payloads) { register(payload.getClass().getSimpleName(), payload.getClass()); } initialized = true; } static void register(String type, Class clazz) { if (Modifier.isAbstract(clazz.getModifiers())) { return; } if (REGISTRY_REQUEST.containsKey(type)) { throw new RuntimeException(String.format("Fail to register, type: %s, clazz: %s", type, clazz.getName())); } REGISTRY_REQUEST.put(type, clazz); } public static Class getClassByType(String type) { return REGISTRY_REQUEST.get(type); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/TlsConfig.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote; /** * gRPC config. * * @author githubcheng2978 */ public class TlsConfig { /** * ssl provider,default OPENSSL,JDK,OPENSSL_REFCNT. */ private String sslProvider = ""; /** * enable tls. */ private Boolean enableTls = false; /** * tls version: TLSv1.1,TLSv1.2,TLSv1.3 * if want to support multi protocol, use comma seperated. like TLSv1.1,TLSv1.2,TLSv1.3 */ private String protocols; /** * cipherList, same of usage protocols. */ private String ciphers; /** * private key. */ private String certPrivateKey; /** * certificate file. */ private String certChainFile; /** * read certPrivateKey file when need password. */ private String certPrivateKeyPassword; /** * mutualAuth,if true,need provider certPrivateKey and certChainFile. */ private Boolean mutualAuthEnable = false; /** * ignore certificate valid. */ private Boolean trustAll = false; /** * collection of trust certificate file. */ private String trustCollectionCertFile; public Boolean getEnableTls() { return enableTls; } public void setEnableTls(Boolean enableTls) { this.enableTls = enableTls; } public Boolean getMutualAuthEnable() { return mutualAuthEnable; } public void setMutualAuthEnable(Boolean mutualAuthEnable) { this.mutualAuthEnable = mutualAuthEnable; } public String getProtocols() { return protocols; } public void setProtocols(String protocols) { this.protocols = protocols; } public Boolean getTrustAll() { return trustAll; } public void setTrustAll(Boolean trustAll) { this.trustAll = trustAll; } public String getCiphers() { return ciphers; } public void setCiphers(String ciphers) { this.ciphers = ciphers; } public String getTrustCollectionCertFile() { return trustCollectionCertFile; } public void setTrustCollectionCertFile(String trustCollectionCertFile) { this.trustCollectionCertFile = trustCollectionCertFile; } public String getCertPrivateKeyPassword() { return certPrivateKeyPassword; } public void setCertPrivateKeyPassword(String certPrivateKeyPassword) { this.certPrivateKeyPassword = certPrivateKeyPassword; } public String getCertPrivateKey() { return certPrivateKey; } public void setCertPrivateKey(String certPrivateKey) { this.certPrivateKey = certPrivateKey; } public String getCertChainFile() { return certChainFile; } public void setCertChainFile(String certChainFile) { this.certChainFile = certChainFile; } public String getSslProvider() { return sslProvider; } public void setSslProvider(String sslProvider) { this.sslProvider = sslProvider; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/Connection.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.remote.Requester; import java.util.Map; /** * connection on client side. * * @author liuzunfei * @version $Id: Connection.java, v 0.1 2020年08月09日 1:32 PM liuzunfei Exp $ */ public abstract class Connection implements Requester { private String connectionId; private boolean abandon = false; protected RpcClient.ServerInfo serverInfo; protected Map abilityTable; public Connection(RpcClient.ServerInfo serverInfo) { this.serverInfo = serverInfo; } public String getConnectionId() { return connectionId; } public void setConnectionId(String connectionId) { this.connectionId = connectionId; } public AbilityStatus getConnectionAbility(AbilityKey abilityKey) { if (abilityTable == null || !abilityTable.containsKey(abilityKey.getName())) { return AbilityStatus.UNKNOWN; } return abilityTable.get(abilityKey.getName()) ? AbilityStatus.SUPPORTED : AbilityStatus.NOT_SUPPORTED; } public boolean isAbilitiesSet() { return abilityTable != null; } public void setAbilityTable(Map abilityTable) { this.abilityTable = abilityTable; } /** * Getter method for property abandon. * * @return property value of abandon */ public boolean isAbandon() { return abandon; } /** * Setter method for property abandon. connection event will be ignored if connection is abandoned. * * @param abandon value to be assigned to property abandon */ public void setAbandon(boolean abandon) { this.abandon = abandon; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/ConnectionEventListener.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; /** * connection event listener of client side. * @author liuzunfei * @version $Id: ConnectionEventListener.java, v 0.1 2020年07月14日 10:59 AM liuzunfei Exp $ */ public interface ConnectionEventListener { /** * notify when connected to server. * * @param connection connection has connected */ void onConnected(Connection connection); /** * notify when disconnected to server. * * @param connection connection has disconnected */ void onDisConnect(Connection connection); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClient.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.RequestFuture; import com.alibaba.nacos.api.remote.request.ClientDetectionRequest; import com.alibaba.nacos.api.remote.request.ConnectResetRequest; import com.alibaba.nacos.api.remote.request.HealthCheckRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.ClientDetectionResponse; import com.alibaba.nacos.api.remote.response.ConnectResetResponse; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.packagescan.resource.DefaultResourceLoader; import com.alibaba.nacos.common.packagescan.resource.ResourceLoader; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.PayloadRegistry; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.LoggerUtils; import com.alibaba.nacos.common.utils.NumberUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Matcher; import java.util.regex.Pattern; import static com.alibaba.nacos.api.exception.NacosException.SERVER_ERROR; /** * abstract remote client to connect to server. * * @author liuzunfei * @version $Id: RpcClient.java, v 0.1 2020年07月13日 9:15 PM liuzunfei Exp $ */ public abstract class RpcClient implements Closeable { private static final Logger LOGGER = LoggerFactory.getLogger("com.alibaba.nacos.common.remote.client"); private ServerListFactory serverListFactory; protected BlockingQueue eventLinkedBlockingQueue = new LinkedBlockingQueue<>(); protected volatile AtomicReference rpcClientStatus = new AtomicReference<>( RpcClientStatus.WAIT_INIT); protected ScheduledExecutorService clientEventExecutor; private final BlockingQueue reconnectionSignal = new ArrayBlockingQueue<>(1); protected volatile Connection currentConnection; private String tenant; private long lastActiveTimeStamp = System.currentTimeMillis(); /** * listener called where connection's status changed. */ protected List connectionEventListeners = new ArrayList<>(); /** * handlers to process server push request. */ protected List serverRequestHandlers = new ArrayList<>(); private static final Pattern EXCLUDE_PROTOCOL_PATTERN = Pattern.compile("(?<=\\w{1,5}://)(.*)"); protected RpcClientConfig rpcClientConfig; protected final ResourceLoader resourceLoader = new DefaultResourceLoader(); static { PayloadRegistry.init(); } public RpcClient(RpcClientConfig rpcClientConfig) { this(rpcClientConfig, null); } public RpcClient(RpcClientConfig rpcClientConfig, ServerListFactory serverListFactory) { this.rpcClientConfig = rpcClientConfig; this.serverListFactory = serverListFactory; init(); } protected void init() { if (this.serverListFactory != null) { rpcClientStatus.compareAndSet(RpcClientStatus.WAIT_INIT, RpcClientStatus.INITIALIZED); LoggerUtils.printIfInfoEnabled(LOGGER, "RpcClient init in constructor, ServerListFactory = {}", serverListFactory.getClass().getName()); } } /** * init server list factory. only can init once. * * @param serverListFactory serverListFactory */ public RpcClient serverListFactory(ServerListFactory serverListFactory) { if (!isWaitInitiated()) { return this; } this.serverListFactory = serverListFactory; rpcClientStatus.compareAndSet(RpcClientStatus.WAIT_INIT, RpcClientStatus.INITIALIZED); LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] RpcClient init, ServerListFactory = {}", rpcClientConfig.name(), serverListFactory.getClass().getName()); return this; } /** * Notify when client disconnected. * * @param connection connection has disconnected */ protected void notifyDisConnected(Connection connection) { if (connectionEventListeners.isEmpty()) { return; } LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Notify disconnected event to listeners", rpcClientConfig.name()); for (ConnectionEventListener connectionEventListener : connectionEventListeners) { try { connectionEventListener.onDisConnect(connection); } catch (Throwable throwable) { LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Notify disconnect listener error, listener = {}", rpcClientConfig.name(), connectionEventListener.getClass().getName()); } } } /** * Notify when client new connected. * * @param connection connection has connected */ protected void notifyConnected(Connection connection) { if (connectionEventListeners.isEmpty()) { return; } LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Notify connected event to listeners.", rpcClientConfig.name()); for (ConnectionEventListener connectionEventListener : connectionEventListeners) { try { connectionEventListener.onConnected(connection); } catch (Throwable throwable) { LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Notify connect listener error, listener = {}", rpcClientConfig.name(), connectionEventListener.getClass().getName()); } } } /** * check is this client is initiated. * * @return is wait initiated or not. */ public boolean isWaitInitiated() { return this.rpcClientStatus.get() == RpcClientStatus.WAIT_INIT; } /** * check is this client is running. * * @return is running or not. */ public boolean isRunning() { return this.rpcClientStatus.get() == RpcClientStatus.RUNNING; } /** * check is this client is shutdown. * * @return is shutdown or not. */ public boolean isShutdown() { return this.rpcClientStatus.get() == RpcClientStatus.SHUTDOWN; } /** * check if current connected server is in server list, if not switch server. */ public void onServerListChange() { if (currentConnection != null && currentConnection.serverInfo != null) { ServerInfo serverInfo = currentConnection.serverInfo; boolean found = false; for (String serverAddress : serverListFactory.getServerList()) { if (resolveServerInfo(serverAddress).getAddress().equalsIgnoreCase(serverInfo.getAddress())) { found = true; break; } } if (!found) { LoggerUtils.printIfInfoEnabled(LOGGER, "Current connected server {} is not in latest server list, switch switchServerAsync", serverInfo.getAddress()); switchServerAsync(); } } } /** * Start this client. */ public final void start() throws NacosException { boolean success = rpcClientStatus.compareAndSet(RpcClientStatus.INITIALIZED, RpcClientStatus.STARTING); if (!success) { return; } clientEventExecutor = new ScheduledThreadPoolExecutor(2, new NameThreadFactory("com.alibaba.nacos.client.remote.worker")); // connection event consumer. clientEventExecutor.submit(() -> { while (!clientEventExecutor.isTerminated() && !clientEventExecutor.isShutdown()) { ConnectionEvent take; try { take = eventLinkedBlockingQueue.take(); if (take.isConnected()) { notifyConnected(take.connection); } else if (take.isDisConnected()) { notifyDisConnected(take.connection); } } catch (Throwable e) { // Do nothing } } }); clientEventExecutor.submit(() -> { while (true) { try { if (isShutdown()) { break; } ReconnectContext reconnectContext = reconnectionSignal .poll(rpcClientConfig.connectionKeepAlive(), TimeUnit.MILLISECONDS); if (reconnectContext == null) { // check alive time. if (System.currentTimeMillis() - lastActiveTimeStamp >= rpcClientConfig.connectionKeepAlive()) { boolean isHealthy = healthCheck(); if (!isHealthy) { if (currentConnection == null) { continue; } LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Server healthy check fail, currentConnection = {}", rpcClientConfig.name(), currentConnection.getConnectionId()); RpcClientStatus rpcClientStatus = RpcClient.this.rpcClientStatus.get(); if (RpcClientStatus.SHUTDOWN.equals(rpcClientStatus)) { break; } boolean statusFlowSuccess = RpcClient.this.rpcClientStatus .compareAndSet(rpcClientStatus, RpcClientStatus.UNHEALTHY); if (statusFlowSuccess) { reconnectContext = new ReconnectContext(null, false); } else { continue; } } else { lastActiveTimeStamp = System.currentTimeMillis(); continue; } } else { continue; } } if (reconnectContext.serverInfo != null) { // clear recommend server if server is not in server list. boolean serverExist = false; for (String server : getServerListFactory().getServerList()) { ServerInfo serverInfo = resolveServerInfo(server); if (serverInfo.getServerIp().equals(reconnectContext.serverInfo.getServerIp())) { serverExist = true; reconnectContext.serverInfo.serverPort = serverInfo.serverPort; break; } } if (!serverExist) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Recommend server is not in server list, ignore recommend server {}", rpcClientConfig.name(), reconnectContext.serverInfo.getAddress()); reconnectContext.serverInfo = null; } } reconnect(reconnectContext.serverInfo, reconnectContext.onRequestFail); } catch (Throwable throwable) { // Do nothing } } }); // connect to server, try to connect to server sync retryTimes times, async starting if failed. Connection connectToServer = null; rpcClientStatus.set(RpcClientStatus.STARTING); int startUpRetryTimes = rpcClientConfig.retryTimes(); while (startUpRetryTimes >= 0 && connectToServer == null) { try { startUpRetryTimes--; ServerInfo serverInfo = nextRpcServer(); LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to connect to server on start up, server: {}", rpcClientConfig.name(), serverInfo); connectToServer = connectToServer(serverInfo); } catch (Throwable e) { LoggerUtils.printIfWarnEnabled(LOGGER, "[{}] Fail to connect to server on start up, error message = {}, start up retry times left: {}", rpcClientConfig.name(), e.getMessage(), startUpRetryTimes, e); } } if (connectToServer != null) { LoggerUtils .printIfInfoEnabled(LOGGER, "[{}] Success to connect to server [{}] on start up, connectionId = {}", rpcClientConfig.name(), connectToServer.serverInfo.getAddress(), connectToServer.getConnectionId()); this.currentConnection = connectToServer; rpcClientStatus.set(RpcClientStatus.RUNNING); eventLinkedBlockingQueue.offer(new ConnectionEvent(ConnectionEvent.CONNECTED, currentConnection)); } else { switchServerAsync(); } registerServerRequestHandler(new ConnectResetRequestHandler()); // register client detection request. registerServerRequestHandler((request, connection) -> { if (request instanceof ClientDetectionRequest) { return new ClientDetectionResponse(); } return null; }); } class ConnectResetRequestHandler implements ServerRequestHandler { @Override public Response requestReply(Request request, Connection connection) { if (request instanceof ConnectResetRequest) { try { synchronized (RpcClient.this) { if (isRunning()) { ConnectResetRequest connectResetRequest = (ConnectResetRequest) request; if (StringUtils.isNotBlank(connectResetRequest.getServerIp())) { ServerInfo serverInfo = resolveServerInfo( connectResetRequest.getServerIp() + Constants.COLON + connectResetRequest .getServerPort()); switchServerAsync(serverInfo, false); } else { switchServerAsync(); } afterReset(connectResetRequest); } } } catch (Exception e) { LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Switch server error, {}", rpcClientConfig.name(), e); } return new ConnectResetResponse(); } return null; } } /** * . invoke after receiving reset request * * @param request request for resetting */ protected void afterReset(ConnectResetRequest request) { // hook for GrpcClient } @Override public void shutdown() throws NacosException { LOGGER.info("Shutdown rpc client, set status to shutdown"); rpcClientStatus.set(RpcClientStatus.SHUTDOWN); LOGGER.info("Shutdown client event executor " + clientEventExecutor); if (clientEventExecutor != null) { clientEventExecutor.shutdownNow(); } closeConnection(currentConnection); } private boolean healthCheck() { HealthCheckRequest healthCheckRequest = new HealthCheckRequest(); if (this.currentConnection == null) { return false; } int reTryTimes = rpcClientConfig.healthCheckRetryTimes(); while (reTryTimes >= 0) { reTryTimes--; try { if (reTryTimes > 1) { Thread.sleep(ThreadLocalRandom.current().nextInt(500)); } Response response = this.currentConnection .request(healthCheckRequest, rpcClientConfig.healthCheckTimeOut()); // not only check server is ok, also check connection is register. return response != null && response.isSuccess(); } catch (Exception e) { // ignore } } return false; } public void switchServerAsyncOnRequestFail() { switchServerAsync(null, true); } public void switchServerAsync() { switchServerAsync(null, false); } protected void switchServerAsync(final ServerInfo recommendServerInfo, boolean onRequestFail) { reconnectionSignal.offer(new ReconnectContext(recommendServerInfo, onRequestFail)); } /** * switch server . */ protected void reconnect(final ServerInfo recommendServerInfo, boolean onRequestFail) { try { AtomicReference recommendServer = new AtomicReference<>(recommendServerInfo); if (onRequestFail && healthCheck()) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Server check success, currentServer is {} ", rpcClientConfig.name(), currentConnection.serverInfo.getAddress()); rpcClientStatus.set(RpcClientStatus.RUNNING); return; } LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Try to reconnect to a new server, server is {}", rpcClientConfig.name(), recommendServerInfo == null ? " not appointed, will choose a random server." : (recommendServerInfo.getAddress() + ", will try it once.")); // loop until start client success. boolean switchSuccess = false; int reConnectTimes = 0; int retryTurns = 0; Exception lastException; while (!switchSuccess && !isShutdown()) { // 1.get a new server ServerInfo serverInfo = null; try { serverInfo = recommendServer.get() == null ? nextRpcServer() : recommendServer.get(); // 2.create a new channel to new server Connection connectionNew = connectToServer(serverInfo); if (connectionNew != null) { LoggerUtils .printIfInfoEnabled(LOGGER, "[{}] Success to connect a server [{}], connectionId = {}", rpcClientConfig.name(), serverInfo.getAddress(), connectionNew.getConnectionId()); // successfully create a new connect. if (currentConnection != null) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Abandon prev connection, server is {}, connectionId is {}", rpcClientConfig.name(), currentConnection.serverInfo.getAddress(), currentConnection.getConnectionId()); // set current connection to enable connection event. currentConnection.setAbandon(true); closeConnection(currentConnection); } currentConnection = connectionNew; rpcClientStatus.set(RpcClientStatus.RUNNING); switchSuccess = true; eventLinkedBlockingQueue.add(new ConnectionEvent(ConnectionEvent.CONNECTED, currentConnection)); return; } // close connection if client is already shutdown. if (isShutdown()) { closeConnection(currentConnection); } lastException = null; } catch (Throwable throwable) { LoggerUtils.printIfErrorEnabled(LOGGER, "Fail to connect server, error = {}", throwable.getMessage()); lastException = new Exception(throwable); } finally { recommendServer.set(null); } if (CollectionUtils.isEmpty(RpcClient.this.serverListFactory.getServerList())) { throw new Exception("server list is empty"); } if (reConnectTimes > 0 && reConnectTimes % RpcClient.this.serverListFactory.getServerList().size() == 0) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Fail to connect server, after trying {} times, last try server is {}, error = {}", rpcClientConfig.name(), reConnectTimes, serverInfo, lastException == null ? "unknown" : lastException); if (Integer.MAX_VALUE == retryTurns) { retryTurns = 50; } else { retryTurns++; } } reConnectTimes++; try { // sleep x milliseconds to switch next server. if (!isRunning()) { // first round, try servers at a delay 100ms;second round, 200ms; max delays 5s. to be reconsidered. Thread.sleep(Math.min(retryTurns + 1, 50) * 100L); } } catch (InterruptedException e) { // Do nothing. // set the interrupted flag Thread.currentThread().interrupt(); } } if (isShutdown()) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Client is shutdown, stop reconnect to server", rpcClientConfig.name()); } } catch (Exception e) { LoggerUtils .printIfWarnEnabled(LOGGER, "[{}] Fail to reconnect to server, error is {}", rpcClientConfig.name(), e); } } private void closeConnection(Connection connection) { if (connection != null) { LOGGER.info("Close current connection " + connection.getConnectionId()); connection.close(); eventLinkedBlockingQueue.add(new ConnectionEvent(ConnectionEvent.DISCONNECTED, connection)); } } /** * get connection type of this client. * * @return ConnectionType. */ public abstract ConnectionType getConnectionType(); /** * increase offset of the nacos server port for the rpc server port. * * @return rpc port offset */ public abstract int rpcPortOffset(); /** * get current server. * * @return server info. */ public ServerInfo getCurrentServer() { if (this.currentConnection != null) { return currentConnection.serverInfo; } return null; } /** * send request. * * @param request request. * @return response from server. */ public Response request(Request request) throws NacosException { return request(request, rpcClientConfig.timeOutMills()); } /** * send request. * * @param request request. * @return response from server. */ public Response request(Request request, long timeoutMills) throws NacosException { int retryTimes = 0; Response response; Throwable exceptionThrow = null; long start = System.currentTimeMillis(); while (retryTimes <= rpcClientConfig.retryTimes() && (timeoutMills <= 0 || System.currentTimeMillis() < timeoutMills + start)) { boolean waitReconnect = false; try { if (this.currentConnection == null || !isRunning()) { waitReconnect = true; throw new NacosException(NacosException.CLIENT_DISCONNECT, "Client not connected, current status:" + rpcClientStatus.get()); } response = this.currentConnection.request(request, timeoutMills); if (response == null) { throw new NacosException(SERVER_ERROR, "Unknown Exception."); } if (response instanceof ErrorResponse) { if (response.getErrorCode() == NacosException.UN_REGISTER) { synchronized (this) { waitReconnect = true; if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) { LoggerUtils.printIfErrorEnabled(LOGGER, "Connection is unregistered, switch server, connectionId = {}, request = {}", currentConnection.getConnectionId(), request.getClass().getSimpleName()); switchServerAsync(); } } } throw new NacosException(response.getErrorCode(), response.getMessage()); } // return response. lastActiveTimeStamp = System.currentTimeMillis(); return response; } catch (Throwable e) { if (waitReconnect) { try { // wait client to reconnect. Thread.sleep(Math.min(100, timeoutMills / 3)); } catch (Exception exception) { // Do nothing. } } LoggerUtils.printIfErrorEnabled(LOGGER, "Send request fail, request = {}, retryTimes = {}, errorMessage = {}", request, retryTimes, e.getMessage()); exceptionThrow = e; } retryTimes++; } if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) { switchServerAsyncOnRequestFail(); } if (exceptionThrow != null) { throw (exceptionThrow instanceof NacosException) ? (NacosException) exceptionThrow : new NacosException(SERVER_ERROR, exceptionThrow); } else { throw new NacosException(SERVER_ERROR, "Request fail, unknown Error"); } } /** * send async request. * * @param request request. */ public void asyncRequest(Request request, RequestCallBack callback) throws NacosException { int retryTimes = 0; Throwable exceptionToThrow = null; long start = System.currentTimeMillis(); while (retryTimes <= rpcClientConfig.retryTimes() && System.currentTimeMillis() < start + callback .getTimeout()) { boolean waitReconnect = false; try { if (this.currentConnection == null || !isRunning()) { waitReconnect = true; throw new NacosException(NacosException.CLIENT_DISCONNECT, "Client not connected."); } this.currentConnection.asyncRequest(request, callback); return; } catch (Throwable e) { if (waitReconnect) { try { // wait client to reconnect. Thread.sleep(Math.min(100, callback.getTimeout() / 3)); } catch (Exception exception) { // Do nothing. } } LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Send request fail, request = {}, retryTimes = {}, errorMessage = {}", rpcClientConfig.name(), request, retryTimes, e.getMessage()); exceptionToThrow = e; } retryTimes++; } if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) { switchServerAsyncOnRequestFail(); } if (exceptionToThrow != null) { throw (exceptionToThrow instanceof NacosException) ? (NacosException) exceptionToThrow : new NacosException(SERVER_ERROR, exceptionToThrow); } else { throw new NacosException(SERVER_ERROR, "AsyncRequest fail, unknown error"); } } /** * send async request. * * @param request request. * @return request future. */ public RequestFuture requestFuture(Request request) throws NacosException { int retryTimes = 0; long start = System.currentTimeMillis(); Exception exceptionToThrow = null; while (retryTimes <= rpcClientConfig.retryTimes() && System.currentTimeMillis() < start + rpcClientConfig .timeOutMills()) { boolean waitReconnect = false; try { if (this.currentConnection == null || !isRunning()) { waitReconnect = true; throw new NacosException(NacosException.CLIENT_DISCONNECT, "Client not connected."); } return this.currentConnection.requestFuture(request); } catch (Exception e) { if (waitReconnect) { try { // wait client to reconnect. Thread.sleep(100L); } catch (Exception exception) { // Do nothing. } } LoggerUtils.printIfErrorEnabled(LOGGER, "[{}] Send request fail, request = {}, retryTimes = {}, errorMessage = {}", rpcClientConfig.name(), request, retryTimes, e.getMessage()); exceptionToThrow = e; } retryTimes++; } if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) { switchServerAsyncOnRequestFail(); } if (exceptionToThrow != null) { throw (exceptionToThrow instanceof NacosException) ? (NacosException) exceptionToThrow : new NacosException(SERVER_ERROR, exceptionToThrow); } else { throw new NacosException(SERVER_ERROR, "Request future fail, unknown error"); } } /** * connect to server. * * @param serverInfo server address to connect. * @return return connection when successfully connect to server, or null if failed. * @throws Exception exception when fail to connect to server. */ public abstract Connection connectToServer(ServerInfo serverInfo) throws Exception; /** * handle server request. * * @param request request. * @return response. */ protected Response handleServerRequest(final Request request) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Receive server push request, request = {}, requestId = {}", rpcClientConfig.name(), request.getClass().getSimpleName(), request.getRequestId()); lastActiveTimeStamp = System.currentTimeMillis(); for (ServerRequestHandler serverRequestHandler : serverRequestHandlers) { try { Response response = serverRequestHandler.requestReply(request, currentConnection); if (response != null) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Ack server push request, request = {}, requestId = {}", rpcClientConfig.name(), request.getClass().getSimpleName(), request.getRequestId()); return response; } } catch (Exception e) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] HandleServerRequest:{}, errorMessage = {}", rpcClientConfig.name(), serverRequestHandler.getClass().getName(), e.getMessage()); throw e; } } return null; } /** * Register connection handler. Will be notified when inner connection's state changed. * * @param connectionEventListener connectionEventListener */ public synchronized void registerConnectionListener(ConnectionEventListener connectionEventListener) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Registry connection listener to current client:{}", rpcClientConfig.name(), connectionEventListener.getClass().getName()); this.connectionEventListeners.add(connectionEventListener); } /** * Register serverRequestHandler, the handler will handle the request from server side. * * @param serverRequestHandler serverRequestHandler */ public synchronized void registerServerRequestHandler(ServerRequestHandler serverRequestHandler) { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}] Register server push request handler:{}", rpcClientConfig.name(), serverRequestHandler.getClass().getName()); this.serverRequestHandlers.add(serverRequestHandler); } /** * Getter method for property name. * * @return property value of name */ public String getName() { return rpcClientConfig.name(); } /** * Getter method for property serverListFactory. * * @return property value of serverListFactory */ public ServerListFactory getServerListFactory() { return serverListFactory; } protected ServerInfo nextRpcServer() { String serverAddress = getServerListFactory().genNextServer(); return resolveServerInfo(serverAddress); } protected ServerInfo currentRpcServer() { String serverAddress = getServerListFactory().getCurrentServer(); return resolveServerInfo(serverAddress); } /** * resolve server info. * * @param serverAddress address. * @return */ private ServerInfo resolveServerInfo(String serverAddress) { Matcher matcher = EXCLUDE_PROTOCOL_PATTERN.matcher(serverAddress); if (matcher.find()) { serverAddress = matcher.group(1); } String[] ipPortTuple = InternetAddressUtil.splitIpPortStr(serverAddress); int defaultPort = Integer.parseInt(System.getProperty("nacos.server.port", "8848")); String serverPort = CollectionUtils.getOrDefault(ipPortTuple, 1, Integer.toString(defaultPort)); return new ServerInfo(ipPortTuple[0], NumberUtils.toInt(serverPort, defaultPort)); } public static class ServerInfo { protected String serverIp; protected int serverPort; public ServerInfo() { } public ServerInfo(String serverIp, int serverPort) { this.serverPort = serverPort; this.serverIp = serverIp; } /** * get address, ip:port. * * @return address. */ public String getAddress() { return serverIp + Constants.COLON + serverPort; } /** * Setter method for property serverIp. * * @param serverIp value to be assigned to property serverIp */ public void setServerIp(String serverIp) { this.serverIp = serverIp; } /** * Setter method for property serverPort. * * @param serverPort value to be assigned to property serverPort */ public void setServerPort(int serverPort) { this.serverPort = serverPort; } /** * Getter method for property serverIp. * * @return property value of serverIp */ public String getServerIp() { return serverIp; } /** * Getter method for property serverPort. * * @return property value of serverPort */ public int getServerPort() { return serverPort; } @Override public String toString() { return "{serverIp = '" + serverIp + '\'' + ", server main port = " + serverPort + '}'; } } public static class ConnectionEvent { public static final int CONNECTED = 1; public static final int DISCONNECTED = 0; int eventType; Connection connection; public ConnectionEvent(int eventType, Connection connection) { this.eventType = eventType; this.connection = connection; } public boolean isConnected() { return eventType == CONNECTED; } public boolean isDisConnected() { return eventType == DISCONNECTED; } } /** * Getter method for property labels. * * @return property value of labels */ public Map getLabels() { return rpcClientConfig.labels(); } static class ReconnectContext { public ReconnectContext(ServerInfo serverInfo, boolean onRequestFail) { this.onRequestFail = onRequestFail; this.serverInfo = serverInfo; } boolean onRequestFail; ServerInfo serverInfo; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } /** * Return ability of current connection. * * @param abilityKey ability key * @return whether support, return null if connection is not ready */ public AbilityStatus getConnectionAbility(AbilityKey abilityKey) { if (currentConnection != null) { return currentConnection.getConnectionAbility(abilityKey); } // return null if connection is not ready return null; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientConfig.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import java.util.Map; /** * RpcClientConfig. * * @author karsonto */ public interface RpcClientConfig { /** * get name. * * @return name. */ String name(); /** * get request retry times. * * @return retryTimes. */ int retryTimes(); /** * get time out mills. * * @return timeOutMills. */ long timeOutMills(); /** * get connection keep alive time. * * @return connectionKeepAlive. */ long connectionKeepAlive(); /** * get health check retry times. * * @return healthCheckRetryTimes. */ int healthCheckRetryTimes(); /** * get health check time out. * * @return healthCheckTimeOut. */ long healthCheckTimeOut(); /** * get map of labels. * * @return labels. */ Map labels(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientConfigFactory.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.common.remote.client.grpc.DefaultGrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import java.util.Map; import java.util.Properties; /** * RpcClientConfigFactory. * * @author Nacos */ public class RpcClientConfigFactory implements RpcConfigFactory { private static volatile RpcClientConfigFactory rpcClientConfigFactory; private RpcClientConfigFactory() { } public static RpcClientConfigFactory getInstance() { if (rpcClientConfigFactory == null) { synchronized (RpcClientConfigFactory.class) { if (rpcClientConfigFactory == null) { rpcClientConfigFactory = new RpcClientConfigFactory(); } } } return rpcClientConfigFactory; } @Override public GrpcClientConfig createGrpcClientConfig(Properties properties, Map labels) { return DefaultGrpcClientConfig.newBuilder().setLabels(labels).buildSdkFromProperties(properties).build(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientFactory.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcClusterClient; import com.alibaba.nacos.common.remote.client.grpc.GrpcSdkClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * RpcClientFactory.to support multi client for different modules of usage. * * @author liuzunfei * @version $Id: RpcClientFactory.java, v 0.1 2020年07月14日 3:41 PM liuzunfei Exp $ */ public class RpcClientFactory { private static final Logger LOGGER = LoggerFactory.getLogger("com.alibaba.nacos.common.remote.client"); private static final Map CLIENT_MAP = new ConcurrentHashMap<>(); /** * get all client. * * @return client collection. */ public static Set> getAllClientEntries() { return CLIENT_MAP.entrySet(); } /** * shut down client. * * @param clientName client name. */ public static void destroyClient(String clientName) throws NacosException { RpcClient rpcClient = CLIENT_MAP.remove(clientName); if (rpcClient != null) { rpcClient.shutdown(); } } public static RpcClient getClient(String clientName) { return CLIENT_MAP.get(clientName); } /** * create a rpc client. * * @param clientName client name. * @param connectionType client type. * @return rpc client. */ public static RpcClient createClient(String clientName, ConnectionType connectionType, Map labels) { return createClient(clientName, connectionType, null, null, labels); } public static RpcClient createClient(String clientName, ConnectionType connectionType, Map labels, RpcClientTlsConfig tlsConfig) { return createClient(clientName, connectionType, null, null, labels, tlsConfig); } /** * create client with properties. * * @return rpc client. * @date 2024/3/7 */ public static RpcClient createClient(String clientName, ConnectionType connectionType, Map labels, Properties properties, RpcClientTlsConfig tlsConfig) { return createClient(clientName, connectionType, null, null, labels, tlsConfig); } public static RpcClient createClient(String clientName, ConnectionType connectionType, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels) { return createClient(clientName, connectionType, threadPoolCoreSize, threadPoolMaxSize, labels, null); } /** * create a rpc client. * * @param clientName client name. * @param connectionType client type. * @param threadPoolCoreSize grpc thread pool core size * @param threadPoolMaxSize grpc thread pool max size * @param tlsConfig tlsconfig * @return rpc client. */ public static RpcClient createClient(String clientName, ConnectionType connectionType, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels, RpcClientTlsConfig tlsConfig) { if (!ConnectionType.GRPC.equals(connectionType)) { throw new UnsupportedOperationException("unsupported connection type :" + connectionType.getType()); } return CLIENT_MAP.computeIfAbsent(clientName, clientNameInner -> { LOGGER.info("[RpcClientFactory] create a new rpc client of " + clientName); return new GrpcSdkClient(clientNameInner, threadPoolCoreSize, threadPoolMaxSize, labels, tlsConfig); }); } /** * create a rpc client. * * @param clientName client name. * @param connectionType client type. * @param grpcClientConfig grpc client config. * @return rpc client. */ public static RpcClient createClient(String clientName, ConnectionType connectionType, GrpcClientConfig grpcClientConfig) { if (!ConnectionType.GRPC.equals(connectionType)) { throw new UnsupportedOperationException("unsupported connection type :" + connectionType.getType()); } return CLIENT_MAP.computeIfAbsent(clientName, clientNameInner -> { LOGGER.info("[RpcClientFactory] create a new rpc client of " + clientName); grpcClientConfig.setName(clientNameInner); return new GrpcSdkClient(grpcClientConfig); }); } /** * Creates an RPC client for cluster communication with default thread pool settings. * * @param clientName The name of the client. * @param connectionType The type of client connection. * @param labels Additional labels for RPC-related attributes. * @return An RPC client for cluster communication. */ public static RpcClient createClusterClient(String clientName, ConnectionType connectionType, Map labels) { return createClusterClient(clientName, connectionType, null, null, labels); } /** * Creates an RPC client for cluster communication with TLS configuration. * * @param clientName The name of the client. * @param connectionType The type of client connection. * @param labels Additional labels for RPC-related attributes. * @param tlsConfig TLS configuration for secure communication. * @return An RPC client for cluster communication with TLS configuration. */ public static RpcClient createClusterClient(String clientName, ConnectionType connectionType, Map labels, RpcClientTlsConfig tlsConfig) { return createClusterClient(clientName, connectionType, null, null, labels, tlsConfig); } /** * Creates an RPC client for cluster communication with custom thread pool settings. * * @param clientName The name of the client. * @param connectionType The type of client connection. * @param threadPoolCoreSize The core size of the gRPC thread pool. * @param threadPoolMaxSize The maximum size of the gRPC thread pool. * @param labels Additional labels for RPC-related attributes. * @return An RPC client for cluster communication with custom thread pool settings. */ public static RpcClient createClusterClient(String clientName, ConnectionType connectionType, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels) { return createClusterClient(clientName, connectionType, threadPoolCoreSize, threadPoolMaxSize, labels, null); } /** * createClusterClient. * * @param clientName client name. * @param connectionType connectionType. * @param threadPoolCoreSize coreSize. * @param threadPoolMaxSize threadPoolSize. * @param labels tables. * @param tlsConfig tlsConfig. * @return */ public static RpcClient createClusterClient(String clientName, ConnectionType connectionType, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels, RpcClientTlsConfig tlsConfig) { if (!ConnectionType.GRPC.equals(connectionType)) { throw new UnsupportedOperationException("unsupported connection type :" + connectionType.getType()); } return CLIENT_MAP.computeIfAbsent(clientName, clientNameInner -> new GrpcClusterClient(clientNameInner, threadPoolCoreSize, threadPoolMaxSize, labels, tlsConfig)); } /** * create a cluster rpc client. * * @param clientName client name. * @param connectionType client type. * @param grpcClientConfig grpc client config. * @return rpc client. */ public static RpcClient createClusterClient(String clientName, ConnectionType connectionType, GrpcClientConfig grpcClientConfig) { if (!ConnectionType.GRPC.equals(connectionType)) { throw new UnsupportedOperationException("unsupported connection type :" + connectionType.getType()); } return CLIENT_MAP.computeIfAbsent(clientName, clientNameInner -> { LOGGER.info("[RpcClientFactory] create a new cluster rpc client of " + clientName); grpcClientConfig.setName(clientNameInner); return new GrpcClusterClient(grpcClientConfig); }); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientStatus.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; /** * status of rpc client. * * @author liuzunfei * @version $Id: RpcClientStatus.java, v 0.1 2020年07月14日 3:49 PM liuzunfei Exp $ */ public enum RpcClientStatus { /** * wait to init. */ WAIT_INIT(0, "Wait to init server list factory..."), /** * already init. */ INITIALIZED(1, "Server list factory is ready, wait to starting..."), /** * in starting. */ STARTING(2, "Client already staring, wait to connect with server..."), /** * unhealthy. */ UNHEALTHY(3, "Client unhealthy, may closed by server, in reconnecting"), /** * in running. */ RUNNING(4, "Client is running"), /** * shutdown. */ SHUTDOWN(5, "Client is shutdown"); int status; String desc; RpcClientStatus(int status, String desc) { this.status = status; this.desc = desc; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientTlsConfig.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.common.remote.TlsConfig; /** * gRPC config for sdk. * * @author githubcheng2978 */ public class RpcClientTlsConfig extends TlsConfig { } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcClientTlsConfigFactory.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import java.util.Properties; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.MUTUAL_AUTH; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_CERT_CHAIN_PATH; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_CERT_KEY; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_CIPHERS; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_ENABLE; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_PROTOCOLS; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_PROVIDER; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_TRUST_ALL; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_TRUST_COLLECTION_CHAIN_PATH; import static com.alibaba.nacos.common.remote.client.RpcConstants.ClientSuffix.TLS_TRUST_PWD; import static com.alibaba.nacos.common.remote.client.RpcConstants.NACOS_CLIENT_RPC; import static com.alibaba.nacos.common.remote.client.RpcConstants.NACOS_PEER_RPC; /** * TlsConfigFactory. * * @author stone-98 */ public class RpcClientTlsConfigFactory implements RpcTlsConfigFactory { private static RpcClientTlsConfigFactory instance; private RpcClientTlsConfigFactory() { } public static synchronized RpcClientTlsConfigFactory getInstance() { if (instance == null) { instance = new RpcClientTlsConfigFactory(); } return instance; } /** * Create SDK client TLS config. * * @param properties Properties containing TLS configuration * @return RpcClientTlsConfig object representing the TLS configuration */ @Override public RpcClientTlsConfig createSdkConfig(Properties properties) { RpcClientTlsConfig tlsConfig = new RpcClientTlsConfig(); tlsConfig.setEnableTls(getBooleanProperty(properties, NACOS_CLIENT_RPC + TLS_ENABLE, false)); tlsConfig.setMutualAuthEnable(getBooleanProperty(properties, NACOS_CLIENT_RPC + MUTUAL_AUTH, false)); tlsConfig.setProtocols(properties.getProperty(NACOS_CLIENT_RPC + TLS_PROTOCOLS)); tlsConfig.setCiphers(properties.getProperty(NACOS_CLIENT_RPC + TLS_CIPHERS)); tlsConfig.setTrustCollectionCertFile(properties.getProperty(NACOS_CLIENT_RPC + TLS_TRUST_COLLECTION_CHAIN_PATH)); tlsConfig.setCertChainFile(properties.getProperty(NACOS_CLIENT_RPC + TLS_CERT_CHAIN_PATH)); tlsConfig.setCertPrivateKey(properties.getProperty(NACOS_CLIENT_RPC + TLS_CERT_KEY)); tlsConfig.setTrustAll(getBooleanProperty(properties, NACOS_CLIENT_RPC + TLS_TRUST_ALL, true)); tlsConfig.setCertPrivateKeyPassword(properties.getProperty(NACOS_CLIENT_RPC + TLS_TRUST_PWD)); tlsConfig.setSslProvider(properties.getProperty(NACOS_CLIENT_RPC + TLS_PROVIDER)); return tlsConfig; } /** * Create cluster client TLS config. * * @param properties Properties containing TLS configuration * @return RpcClientTlsConfig object representing the TLS configuration */ @Override public RpcClientTlsConfig createClusterConfig(Properties properties) { RpcClientTlsConfig tlsConfig = new RpcClientTlsConfig(); tlsConfig.setEnableTls(getBooleanProperty(properties, NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_ENABLE, false)); tlsConfig.setMutualAuthEnable(getBooleanProperty(properties, NACOS_PEER_RPC + RpcConstants.ServerSuffix.MUTUAL_AUTH, false)); tlsConfig.setProtocols(properties.getProperty(NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_PROTOCOLS)); tlsConfig.setCiphers(properties.getProperty(NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_CIPHERS)); tlsConfig.setTrustCollectionCertFile(properties.getProperty(NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_TRUST_COLLECTION_CHAIN_PATH)); tlsConfig.setCertChainFile(properties.getProperty(NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_CERT_CHAIN_PATH)); tlsConfig.setCertPrivateKey(properties.getProperty(NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_CERT_KEY)); tlsConfig.setTrustAll(getBooleanProperty(properties, NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_TRUST_ALL, true)); tlsConfig.setCertPrivateKeyPassword(properties.getProperty(NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_TRUST_PWD)); tlsConfig.setSslProvider(properties.getProperty(NACOS_PEER_RPC + RpcConstants.ServerSuffix.TLS_PROVIDER)); return tlsConfig; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcConfigFactory.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import java.util.Map; import java.util.Properties; /** * RpcConfigFactory. * * @author Nacos */ public interface RpcConfigFactory { /** * createGrpcClientConfig. * * @param properties properties * @param labels labels * @return GrpcClientConfig */ GrpcClientConfig createGrpcClientConfig(Properties properties, Map labels); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcConstants.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; 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 java.lang.reflect.Field; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * RpcConstants. * * @author githubcheng2978. */ public class RpcConstants { public static final String NACOS_CLIENT_RPC = "nacos.remote.client.rpc"; public static final String NACOS_SERVER_RPC = "nacos.remote.server.rpc.tls"; public static final String NACOS_PEER_RPC = "nacos.remote.peer.rpc.tls"; @RpcConfigLabel public static final String RPC_CLIENT_TLS_ENABLE = NACOS_CLIENT_RPC + ClientSuffix.TLS_ENABLE; @RpcConfigLabel public static final String RPC_CLIENT_TLS_PROVIDER = NACOS_CLIENT_RPC + ClientSuffix.TLS_PROVIDER; @RpcConfigLabel public static final String RPC_CLIENT_MUTUAL_AUTH = NACOS_CLIENT_RPC + ClientSuffix.MUTUAL_AUTH; @RpcConfigLabel public static final String RPC_CLIENT_TLS_PROTOCOLS = NACOS_CLIENT_RPC + ClientSuffix.TLS_PROTOCOLS; @RpcConfigLabel public static final String RPC_CLIENT_TLS_CIPHERS = NACOS_CLIENT_RPC + ClientSuffix.TLS_CIPHERS; @RpcConfigLabel public static final String RPC_CLIENT_TLS_CERT_CHAIN_PATH = NACOS_CLIENT_RPC + ClientSuffix.TLS_CERT_CHAIN_PATH; @RpcConfigLabel public static final String RPC_CLIENT_TLS_CERT_KEY = NACOS_CLIENT_RPC + ClientSuffix.TLS_CERT_KEY; @RpcConfigLabel public static final String RPC_CLIENT_TLS_TRUST_PWD = NACOS_CLIENT_RPC + ClientSuffix.TLS_TRUST_PWD; @RpcConfigLabel public static final String RPC_CLIENT_TLS_TRUST_COLLECTION_CHAIN_PATH = NACOS_CLIENT_RPC + ClientSuffix.TLS_TRUST_COLLECTION_CHAIN_PATH; @RpcConfigLabel public static final String RPC_CLIENT_TLS_TRUST_ALL = NACOS_CLIENT_RPC + ClientSuffix.TLS_TRUST_ALL; private static final Set CONFIG_NAMES = new HashSet<>(); static { Class clazz = RpcConstants.class; Field[] declaredFields = clazz.getDeclaredFields(); for (Field declaredField : declaredFields) { declaredField.setAccessible(true); if (declaredField.getType().equals(String.class) && null != declaredField.getAnnotation( RpcConfigLabel.class)) { try { CONFIG_NAMES.add((String) declaredField.get(null)); } catch (IllegalAccessException ignored) { } } } } /** * Enumeration of common suffixes for RPC configuration properties. Each enum constant represents a specific * configuration attribute suffix. This allows for the construction of complete configuration property keys. */ public class ClientSuffix { /** * Suffix for 'tls.enable' configuration property. */ public static final String TLS_ENABLE = ".tls.enable"; /** * Suffix for 'tls.provider' configuration property. */ public static final String TLS_PROVIDER = ".tls.provider"; /** * Suffix for 'tls.mutualAuth' configuration property. */ public static final String MUTUAL_AUTH = ".tls.mutualAuth"; /** * Suffix for 'tls.protocols' configuration property. */ public static final String TLS_PROTOCOLS = ".tls.protocols"; /** * Suffix for 'tls.ciphers' configuration property. */ public static final String TLS_CIPHERS = ".tls.ciphers"; /** * Suffix for 'tls.certChainFile' configuration property. */ public static final String TLS_CERT_CHAIN_PATH = ".tls.certChainFile"; /** * Suffix for 'tls.certPrivateKey' configuration property. */ public static final String TLS_CERT_KEY = ".tls.certPrivateKey"; /** * Suffix for 'tls.certPrivateKeyPassword' configuration property. */ public static final String TLS_TRUST_PWD = ".tls.certPrivateKeyPassword"; /** * Suffix for 'tls.trustCollectionChainPath' configuration property. */ public static final String TLS_TRUST_COLLECTION_CHAIN_PATH = ".tls.trustCollectionChainPath"; /** * Suffix for 'tls.trustAll' configuration property. */ public static final String TLS_TRUST_ALL = ".tls.trustAll"; } /** * Enumeration of common suffixes for RPC configuration properties. Each enum constant represents a specific * configuration attribute suffix. This allows for the construction of complete configuration property keys. */ public class ServerSuffix { /** * Suffix for 'tls.enable' configuration property. */ public static final String TLS_ENABLE = ".enableTls"; /** * Suffix for 'tls.provider' configuration property. */ public static final String TLS_PROVIDER = ".sslProvider"; /** * Suffix for 'tls.mutualAuth' configuration property. */ public static final String MUTUAL_AUTH = ".mutualAuthEnable"; /** * Suffix for 'tls.protocols' configuration property. */ public static final String TLS_PROTOCOLS = ".protocols"; /** * Suffix for 'tls.ciphers' configuration property. */ public static final String TLS_CIPHERS = ".ciphers"; /** * Suffix for 'tls.certChainFile' configuration property. */ public static final String TLS_CERT_CHAIN_PATH = ".certChainFile"; /** * Suffix for 'tls.certPrivateKey' configuration property. */ public static final String TLS_CERT_KEY = ".certPrivateKey"; /** * Suffix for 'tls.certPrivateKeyPassword' configuration property. */ public static final String TLS_TRUST_PWD = ".certPrivateKeyPassword"; /** * Suffix for 'tls.trustCollectionChainPath' configuration property. */ public static final String TLS_TRUST_COLLECTION_CHAIN_PATH = ".trustCollectionCertFile"; /** * Suffix for 'tls.trustAll' configuration property. */ public static final String TLS_TRUST_ALL = ".trustAll"; /** * Suffix for '.sslContextRefresher' configuration property. */ public static final String SSL_CONTEXT_REFRESHER = ".sslContextRefresher"; /** * Suffix for '.compatibility' configuration property. */ public static final String COMPATIBILITY = ".compatibility"; } @Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) protected @interface RpcConfigLabel { } public static Set getRpcParams() { return Collections.unmodifiableSet(CONFIG_NAMES); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/RpcTlsConfigFactory.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.common.remote.TlsConfig; import java.util.Properties; /** * RpcTlsConfigFactory. * * @author stone-98 * @date 2024/4/8 */ public interface RpcTlsConfigFactory { /** * Create a TlsConfig for SDK connections based on the provided properties. * * @param properties Properties containing configuration * @return TlsConfig instance for SDK connections */ TlsConfig createSdkConfig(Properties properties); /** * Create a TlsConfig for cluster connections based on the provided properties. * * @param properties Properties containing configuration * @return TlsConfig instance for cluster connections */ TlsConfig createClusterConfig(Properties properties); /** * Get boolean property from properties. * * @param properties Properties containing configuration * @param key Key of the property * @param defaultValue Default value to return if the property is not found or is invalid * @return Boolean value of the property, or the provided defaultValue if not found or invalid */ default Boolean getBooleanProperty(Properties properties, String key, Boolean defaultValue) { String value = properties.getProperty(key); if (value != null) { return Boolean.parseBoolean(value); } return defaultValue; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/ServerListFactory.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import java.util.List; /** * server list factory . use to inner client to connecte and switch servers. * @author liuzunfei * @version $Id: ServerListFactory.java, v 0.1 2020年07月14日 1:11 PM liuzunfei Exp $ */ public interface ServerListFactory { /** * switch to a new server and get it. * * @return server " ip:port". */ String genNextServer(); /** * get current server. * @return server " ip:port". */ String getCurrentServer(); /** * get current server. * * @return servers. */ List getServerList(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/ServerRequestHandler.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; /** * ServerRequestHandler, to process the request from server side. * * @author liuzunfei * @version $Id: ServerRequestHandler.java, v 0.1 2020年07月14日 11:41 AM liuzunfei Exp $ */ public interface ServerRequestHandler { /** * Handle request from server. * * @param request request * @param connection current connection, it can be used to know server ability * @return response. */ Response requestReply(Request request, Connection connection); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/DefaultGrpcClientConfig.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.common.remote.TlsConfig; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfigFactory; import com.alibaba.nacos.common.utils.ThreadUtils; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.concurrent.TimeUnit; /** * Default grpc client config. * * @author karsonto */ public class DefaultGrpcClientConfig implements GrpcClientConfig { private String name; private final int retryTimes; private final long timeOutMills; private final long connectionKeepAlive; private final long channelKeepAliveTimeout; private final long threadPoolKeepAlive; private final int threadPoolCoreSize; private final int threadPoolMaxSize; private final long serverCheckTimeOut; private final int threadPoolQueueSize; private final int maxInboundMessageSize; private final int channelKeepAlive; private final int healthCheckRetryTimes; private final long healthCheckTimeOut; private final long capabilityNegotiationTimeout; private final boolean allowCoreThreadTimeOut; private final Map labels; private RpcClientTlsConfig tlsConfig = new RpcClientTlsConfig(); /** * constructor. * * @param builder builder of DefaultGrpcClientConfig builder. */ private DefaultGrpcClientConfig(Builder builder) { this.name = builder.name; this.retryTimes = builder.retryTimes; this.timeOutMills = builder.timeOutMills; this.connectionKeepAlive = builder.connectionKeepAlive; this.threadPoolKeepAlive = builder.threadPoolKeepAlive; this.threadPoolCoreSize = builder.threadPoolCoreSize; this.threadPoolMaxSize = builder.threadPoolMaxSize; this.serverCheckTimeOut = builder.serverCheckTimeOut; this.threadPoolQueueSize = builder.threadPoolQueueSize; this.maxInboundMessageSize = builder.maxInboundMessageSize; this.channelKeepAlive = builder.channelKeepAlive; this.healthCheckRetryTimes = builder.healthCheckRetryTimes; this.healthCheckTimeOut = builder.healthCheckTimeOut; this.channelKeepAliveTimeout = builder.channelKeepAliveTimeout; this.capabilityNegotiationTimeout = builder.capabilityNegotiationTimeout; this.allowCoreThreadTimeOut = builder.allowCoreThreadTimeOut; this.labels = builder.labels; this.labels.put("tls.enable", "false"); if (Objects.nonNull(builder.tlsConfig)) { this.tlsConfig = builder.tlsConfig; if (Objects.nonNull(builder.tlsConfig.getEnableTls()) && builder.tlsConfig.getEnableTls()) { this.labels.put("tls.enable", "true"); } } } @Override public String name() { return this.name; } @Override public int retryTimes() { return retryTimes; } @Override public long timeOutMills() { return timeOutMills; } @Override public long connectionKeepAlive() { return connectionKeepAlive; } @Override public int threadPoolCoreSize() { return threadPoolCoreSize; } @Override public int threadPoolMaxSize() { return threadPoolMaxSize; } @Override public long threadPoolKeepAlive() { return threadPoolKeepAlive; } @Override public long serverCheckTimeOut() { return serverCheckTimeOut; } @Override public int threadPoolQueueSize() { return threadPoolQueueSize; } @Override public int maxInboundMessageSize() { return maxInboundMessageSize; } @Override public int channelKeepAlive() { return channelKeepAlive; } @Override public long channelKeepAliveTimeout() { return channelKeepAliveTimeout; } @Override public TlsConfig tlsConfig() { return tlsConfig; } public void setTlsConfig(RpcClientTlsConfig tlsConfig) { this.tlsConfig = tlsConfig; } public void setName(String name) { this.name = name; } @Override public long capabilityNegotiationTimeout() { return this.capabilityNegotiationTimeout; } @Override public boolean allowCoreThreadTimeOut() { return this.allowCoreThreadTimeOut; } @Override public int healthCheckRetryTimes() { return healthCheckRetryTimes; } @Override public long healthCheckTimeOut() { return healthCheckTimeOut; } @Override public Map labels() { return this.labels; } public static Builder newBuilder() { return new Builder(); } public static class Builder { private String name; private int retryTimes = 3; private long timeOutMills = 3000L; private long connectionKeepAlive = 5000L; private long threadPoolKeepAlive = 10000L; private int threadPoolCoreSize = ThreadUtils.getSuitableThreadCount(2); private int threadPoolMaxSize = ThreadUtils.getSuitableThreadCount(8); private long serverCheckTimeOut = 3000L; private int threadPoolQueueSize = 10000; private int maxInboundMessageSize = 10 * 1024 * 1024; private int channelKeepAlive = 6 * 60 * 1000; private long channelKeepAliveTimeout = TimeUnit.SECONDS.toMillis(20L); private int healthCheckRetryTimes = 3; private long healthCheckTimeOut = 3000L; private long capabilityNegotiationTimeout = 5000L; private boolean allowCoreThreadTimeOut = false; private final Map labels = new HashMap<>(); private RpcClientTlsConfig tlsConfig = new RpcClientTlsConfig(); private Builder() { } public Builder buildSdkFromProperties(Properties properties) { RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); return fromProperties(properties, tlsConfig); } public Builder buildClusterFromProperties(Properties properties) { RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); return fromProperties(properties, tlsConfig); } /** * Set config from properties. * * @param properties properties * @return Builder */ public Builder fromProperties(Properties properties, RpcClientTlsConfig tlsConfig) { if (properties.containsKey(GrpcConstants.GRPC_NAME)) { this.name = properties.getProperty(GrpcConstants.GRPC_NAME); } if (properties.containsKey(GrpcConstants.GRPC_RETRY_TIMES)) { this.retryTimes = Integer.parseInt(properties.getProperty(GrpcConstants.GRPC_RETRY_TIMES)); } if (properties.containsKey(GrpcConstants.GRPC_TIMEOUT_MILLS)) { this.timeOutMills = Long.parseLong(properties.getProperty(GrpcConstants.GRPC_TIMEOUT_MILLS)); } if (properties.containsKey(GrpcConstants.GRPC_CONNECT_KEEP_ALIVE_TIME)) { this.connectionKeepAlive = Long.parseLong( properties.getProperty(GrpcConstants.GRPC_CONNECT_KEEP_ALIVE_TIME)); } if (properties.containsKey(GrpcConstants.GRPC_THREADPOOL_KEEPALIVETIME)) { this.threadPoolKeepAlive = Long.parseLong( properties.getProperty(GrpcConstants.GRPC_THREADPOOL_KEEPALIVETIME)); } if (properties.containsKey(GrpcConstants.GRPC_THREADPOOL_CORE_SIZE)) { this.threadPoolCoreSize = Integer.parseInt( properties.getProperty(GrpcConstants.GRPC_THREADPOOL_CORE_SIZE)); } if (properties.containsKey(GrpcConstants.GRPC_THREADPOOL_MAX_SIZE)) { this.threadPoolMaxSize = Integer.parseInt( properties.getProperty(GrpcConstants.GRPC_THREADPOOL_MAX_SIZE)); } if (properties.containsKey(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT)) { this.serverCheckTimeOut = Long.parseLong( properties.getProperty(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT)); } if (properties.containsKey(GrpcConstants.GRPC_QUEUESIZE)) { this.threadPoolQueueSize = Integer.parseInt(properties.getProperty(GrpcConstants.GRPC_QUEUESIZE)); } if (properties.containsKey(GrpcConstants.GRPC_MAX_INBOUND_MESSAGE_SIZE)) { this.maxInboundMessageSize = Integer.parseInt( properties.getProperty(GrpcConstants.GRPC_MAX_INBOUND_MESSAGE_SIZE)); } if (properties.containsKey(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIME)) { this.channelKeepAlive = Integer.parseInt( properties.getProperty(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIME)); } if (properties.containsKey(GrpcConstants.GRPC_CHANNEL_CAPABILITY_NEGOTIATION_TIMEOUT)) { this.capabilityNegotiationTimeout = Integer.parseInt( properties.getProperty(GrpcConstants.GRPC_CHANNEL_CAPABILITY_NEGOTIATION_TIMEOUT)); } if (properties.containsKey(GrpcConstants.GRPC_HEALTHCHECK_RETRY_TIMES)) { this.healthCheckRetryTimes = Integer.parseInt( properties.getProperty(GrpcConstants.GRPC_HEALTHCHECK_RETRY_TIMES)); } if (properties.containsKey(GrpcConstants.GRPC_HEALTHCHECK_TIMEOUT)) { this.healthCheckTimeOut = Long.parseLong( properties.getProperty(GrpcConstants.GRPC_HEALTHCHECK_TIMEOUT)); } if (properties.containsKey(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIMEOUT)) { this.channelKeepAliveTimeout = Integer.parseInt( properties.getProperty(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIMEOUT)); } if (properties.containsKey(GrpcConstants.GRPC_THREADPOOL_ALLOW_CORE_THREAD_TIMEOUT)) { this.allowCoreThreadTimeOut = Boolean.parseBoolean( properties.getProperty(GrpcConstants.GRPC_THREADPOOL_ALLOW_CORE_THREAD_TIMEOUT)); } this.tlsConfig = tlsConfig; return this; } /** * set client name. */ public Builder setName(String name) { this.name = name; return this; } /** * set retryTimes. */ public Builder setRetryTimes(int retryTimes) { this.retryTimes = retryTimes; return this; } /** * set timeOutMills. */ public Builder setTimeOutMills(long timeOutMills) { this.timeOutMills = timeOutMills; return this; } /** * set connectionKeepAlive. */ public Builder setConnectionKeepAlive(long connectionKeepAlive) { this.connectionKeepAlive = connectionKeepAlive; return this; } /** * set threadPoolKeepAlive. */ public Builder setThreadPoolKeepAlive(Long threadPoolKeepAlive) { this.threadPoolKeepAlive = threadPoolKeepAlive; return this; } /** * set threadPoolCoreSize. */ public Builder setThreadPoolCoreSize(Integer threadPoolCoreSize) { if (!Objects.isNull(threadPoolCoreSize)) { this.threadPoolCoreSize = threadPoolCoreSize; } return this; } /** * set threadPoolMaxSize. */ public Builder setThreadPoolMaxSize(Integer threadPoolMaxSize) { if (!Objects.isNull(threadPoolMaxSize)) { this.threadPoolMaxSize = threadPoolMaxSize; } return this; } /** * set serverCheckTimeOut. */ public Builder setServerCheckTimeOut(Long serverCheckTimeOut) { this.serverCheckTimeOut = serverCheckTimeOut; return this; } /** * set threadPoolQueueSize. */ public Builder setThreadPoolQueueSize(int threadPoolQueueSize) { this.threadPoolQueueSize = threadPoolQueueSize; return this; } /** * set maxInboundMessageSize. */ public Builder setMaxInboundMessageSize(int maxInboundMessageSize) { this.maxInboundMessageSize = maxInboundMessageSize; return this; } /** * set channelKeepAlive. */ public Builder setChannelKeepAlive(int channelKeepAlive) { this.channelKeepAlive = channelKeepAlive; return this; } /** * set channelKeepAlive. * * @param channelKeepAliveTimeout milliseconds * @return builder */ public Builder setChannelKeepAliveTimeout(int channelKeepAliveTimeout) { this.channelKeepAliveTimeout = channelKeepAliveTimeout; return this; } public Builder setCapabilityNegotiationTimeout(long capabilityNegotiationTimeout) { this.capabilityNegotiationTimeout = capabilityNegotiationTimeout; return this; } /** * set allowCoreThreadTimeOut. * * @param allowCoreThreadTimeOut allowCoreThreadTimeOut flag * @return builder */ public Builder setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; return this; } /** * set healthCheckRetryTimes. */ public Builder setHealthCheckRetryTimes(int healthCheckRetryTimes) { this.healthCheckRetryTimes = healthCheckRetryTimes; return this; } /** * set healthCheckTimeOut. */ public Builder setHealthCheckTimeOut(long healthCheckTimeOut) { this.healthCheckTimeOut = healthCheckTimeOut; return this; } /** * set labels. */ public Builder setLabels(Map labels) { this.labels.putAll(labels); return this; } /** * set tlsConfig. * * @param tlsConfig tls of client. * @return */ public Builder setTlsConfig(RpcClientTlsConfig tlsConfig) { this.tlsConfig = tlsConfig; return this; } /** * build GrpcClientConfig. */ public GrpcClientConfig build() { return new DefaultGrpcClientConfig(this); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClient.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc; import com.alibaba.nacos.api.grpc.auto.Payload; import com.alibaba.nacos.api.grpc.auto.RequestGrpc; import com.alibaba.nacos.api.remote.request.ConnectResetRequest; import com.alibaba.nacos.api.remote.request.ConnectionSetupRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.request.ServerCheckRequest; import com.alibaba.nacos.api.remote.request.SetupAckRequest; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ServerCheckResponse; import com.alibaba.nacos.api.remote.response.SetupAckResponse; import com.alibaba.nacos.common.ability.discover.NacosAbilityManagerHolder; import com.alibaba.nacos.common.packagescan.resource.Resource; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.TlsConfig; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientStatus; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.alibaba.nacos.common.remote.client.ServerRequestHandler; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.LoggerUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadFactoryBuilder; import com.alibaba.nacos.common.utils.TlsTypeResolve; import com.alibaba.nacos.common.utils.VersionUtils; import com.google.common.util.concurrent.ListenableFuture; import io.grpc.CompressorRegistry; import io.grpc.DecompressorRegistry; import io.grpc.ManagedChannel; import io.grpc.ManagedChannelBuilder; import io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts; import io.grpc.netty.shaded.io.grpc.netty.NegotiationType; import io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder; import io.grpc.netty.shaded.io.netty.handler.ssl.SslContext; import io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder; import io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.grpc.stub.StreamObserver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Optional; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * gRPC Client. * * @author liuzunfei * @version $Id: GrpcClient.java, v 0.1 2020年07月13日 9:16 PM liuzunfei Exp $ */ public abstract class GrpcClient extends RpcClient { private static final Logger LOGGER = LoggerFactory.getLogger(GrpcClient.class); private final GrpcClientConfig clientConfig; private ThreadPoolExecutor grpcExecutor; /** * Block to wait setup success response. */ private final RecAbilityContext recAbilityContext = new RecAbilityContext(null); /** * for receiving server abilities. */ private SetupRequestHandler setupRequestHandler; @Override public ConnectionType getConnectionType() { return ConnectionType.GRPC; } /** * constructor. * * @param name . */ public GrpcClient(String name) { this(DefaultGrpcClientConfig.newBuilder().setName(name).build()); } /** * constructor. * * @param clientConfig . */ public GrpcClient(GrpcClientConfig clientConfig) { super(clientConfig); this.clientConfig = clientConfig; initSetupHandler(); } /** * constructor. * * @param clientConfig . * @param serverListFactory . */ public GrpcClient(GrpcClientConfig clientConfig, ServerListFactory serverListFactory) { super(clientConfig, serverListFactory); this.clientConfig = clientConfig; initSetupHandler(); } /** * setup handler. */ private void initSetupHandler() { // register to handler setup request setupRequestHandler = new SetupRequestHandler(this.recAbilityContext); } /** * constructor. * * @param name . * @param threadPoolCoreSize . * @param threadPoolMaxSize . * @param labels . */ public GrpcClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels) { this(DefaultGrpcClientConfig.newBuilder().setName(name).setThreadPoolCoreSize(threadPoolCoreSize) .setThreadPoolMaxSize(threadPoolMaxSize).setLabels(labels).build()); } public GrpcClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels, RpcClientTlsConfig tlsConfig) { this(DefaultGrpcClientConfig.newBuilder().setName(name).setThreadPoolCoreSize(threadPoolCoreSize) .setTlsConfig(tlsConfig).setThreadPoolMaxSize(threadPoolMaxSize).setLabels(labels).build()); } protected ThreadPoolExecutor createGrpcExecutor(String serverIp) { // Thread name will use String.format, ipv6 maybe contain special word %, so handle it first. serverIp = serverIp.replaceAll("%", "-"); ThreadPoolExecutor grpcExecutor = new ThreadPoolExecutor(clientConfig.threadPoolCoreSize(), clientConfig.threadPoolMaxSize(), clientConfig.threadPoolKeepAlive(), TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>(clientConfig.threadPoolQueueSize()), new ThreadFactoryBuilder().daemon(true).nameFormat("nacos-grpc-client-executor-" + serverIp + "-%d") .build()); grpcExecutor.allowCoreThreadTimeOut(clientConfig.allowCoreThreadTimeOut()); return grpcExecutor; } @Override public void shutdown() throws NacosException { super.shutdown(); if (grpcExecutor != null) { LOGGER.info("Shutdown grpc executor " + grpcExecutor); grpcExecutor.shutdown(); } } /** * Create a stub using a channel. * * @param managedChannelTemp channel. * @return if server check success,return a non-null stub. */ protected RequestGrpc.RequestFutureStub createNewChannelStub(ManagedChannel managedChannelTemp) { return RequestGrpc.newFutureStub(managedChannelTemp); } /** * create a new channel with specific server address. * * @param serverIp serverIp. * @param serverPort serverPort. * @return if server check success,return a non-null channel. */ private ManagedChannel createNewManagedChannel(String serverIp, int serverPort) { LOGGER.info("grpc client connection server: {} ip, serverPort: {}, grpcTslConfig: {}", serverIp, serverPort, JacksonUtils.toJson(clientConfig.tlsConfig())); ManagedChannelBuilder managedChannelBuilder = buildChannel(serverIp, serverPort, buildSslContext()).executor( grpcExecutor).compressorRegistry(CompressorRegistry.getDefaultInstance()) .decompressorRegistry(DecompressorRegistry.getDefaultInstance()) .maxInboundMessageSize(clientConfig.maxInboundMessageSize()) .keepAliveTime(clientConfig.channelKeepAlive(), TimeUnit.MILLISECONDS) .keepAliveTimeout(clientConfig.channelKeepAliveTimeout(), TimeUnit.MILLISECONDS); return managedChannelBuilder.build(); } /** * shutdown a channel. * * @param managedChannel channel to be shutdown. */ private void shuntDownChannel(ManagedChannel managedChannel) { if (managedChannel != null && !managedChannel.isShutdown()) { managedChannel.shutdownNow(); } } /** * check server if success. * * @param requestBlockingStub requestBlockingStub used to check server. * @return success or not */ private Response serverCheck(String ip, int port, RequestGrpc.RequestFutureStub requestBlockingStub) { try { ServerCheckRequest serverCheckRequest = new ServerCheckRequest(); Payload grpcRequest = GrpcUtils.convert(serverCheckRequest); ListenableFuture responseFuture = requestBlockingStub.request(grpcRequest); Payload response = responseFuture.get(clientConfig.serverCheckTimeOut(), TimeUnit.MILLISECONDS); // receive connection unregister response here,not check response is success. return (Response) GrpcUtils.parse(response); } catch (Exception e) { LoggerUtils.printIfErrorEnabled(LOGGER, "Server check fail, please check server {}, port {} is available, error ={}", ip, port, e); if (this.clientConfig != null && this.clientConfig.tlsConfig() != null && this.clientConfig.tlsConfig() .getEnableTls()) { LoggerUtils.printIfErrorEnabled(LOGGER, "current client is require tls encrypted, server must support tls ,please check"); } return null; } } private StreamObserver bindRequestStream(final BiRequestStreamGrpc.BiRequestStreamStub streamStub, final GrpcConnection grpcConn) { return streamStub.requestBiStream(new StreamObserver() { @Override public void onNext(Payload payload) { LoggerUtils.printIfDebugEnabled(LOGGER, "[{}]Stream server request receive, original info: {}", grpcConn.getConnectionId(), payload.toString()); try { Object parseBody = GrpcUtils.parse(payload); final Request request = (Request) parseBody; if (request != null) { try { if (request instanceof SetupAckRequest) { // there is no connection ready this time setupRequestHandler.requestReply(request, null); return; } Response response = handleServerRequest(request); if (response != null) { response.setRequestId(request.getRequestId()); sendResponse(response); } else { LOGGER.warn("[{}]Fail to process server request, ackId->{}", grpcConn.getConnectionId(), request.getRequestId()); } } catch (Exception e) { LoggerUtils.printIfErrorEnabled(LOGGER, "[{}]Handle server request exception: {}", grpcConn.getConnectionId(), payload.toString(), e.getMessage()); Response errResponse = ErrorResponse.build(NacosException.CLIENT_ERROR, "Handle server request error"); errResponse.setRequestId(request.getRequestId()); sendResponse(errResponse); } } } catch (Exception e) { LoggerUtils.printIfErrorEnabled(LOGGER, "[{}]Error to process server push response: {}", grpcConn.getConnectionId(), payload.getBody().getValue().toStringUtf8()); // remove and notify recAbilityContext.release(null); } } @Override public void onError(Throwable throwable) { boolean isRunning = isRunning(); boolean isAbandon = grpcConn.isAbandon(); if (isRunning && !isAbandon) { LoggerUtils.printIfErrorEnabled(LOGGER, "[{}]Request stream error, switch server,error={}", grpcConn.getConnectionId(), throwable); if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) { switchServerAsync(); } } else { LoggerUtils.printIfWarnEnabled(LOGGER, "[{}]Ignore error event,isRunning:{},isAbandon={}", grpcConn.getConnectionId(), isRunning, isAbandon); } } @Override public void onCompleted() { boolean isRunning = isRunning(); boolean isAbandon = grpcConn.isAbandon(); if (isRunning && !isAbandon) { LoggerUtils.printIfErrorEnabled(LOGGER, "[{}]Request stream onCompleted, switch server", grpcConn.getConnectionId()); if (rpcClientStatus.compareAndSet(RpcClientStatus.RUNNING, RpcClientStatus.UNHEALTHY)) { switchServerAsync(); } } else { LoggerUtils.printIfInfoEnabled(LOGGER, "[{}]Ignore complete event,isRunning:{},isAbandon={}", grpcConn.getConnectionId(), isRunning, isAbandon); } } }); } private void sendResponse(Response response) { try { ((GrpcConnection) this.currentConnection).sendResponse(response); } catch (Exception e) { LOGGER.error("[{}]Error to send ack response, ackId->{}", this.currentConnection.getConnectionId(), response.getRequestId()); } } @Override public Connection connectToServer(ServerInfo serverInfo) { // the newest connection id String connectionId = ""; try { if (grpcExecutor == null) { this.grpcExecutor = createGrpcExecutor(serverInfo.getServerIp()); } int port = serverInfo.getServerPort() + rpcPortOffset(); ManagedChannel managedChannel = createNewManagedChannel(serverInfo.getServerIp(), port); RequestGrpc.RequestFutureStub newChannelStubTemp = createNewChannelStub(managedChannel); Response response = serverCheck(serverInfo.getServerIp(), port, newChannelStubTemp); if (!(response instanceof ServerCheckResponse)) { shuntDownChannel(managedChannel); return null; } // submit ability table as soon as possible // ability table will be null if server doesn't support ability table ServerCheckResponse serverCheckResponse = (ServerCheckResponse) response; connectionId = serverCheckResponse.getConnectionId(); BiRequestStreamGrpc.BiRequestStreamStub biRequestStreamStub = BiRequestStreamGrpc.newStub( newChannelStubTemp.getChannel()); GrpcConnection grpcConn = new GrpcConnection(serverInfo, grpcExecutor); grpcConn.setConnectionId(connectionId); // if not supported, it will be false if (serverCheckResponse.isSupportAbilityNegotiation()) { // mark this.recAbilityContext.reset(grpcConn); // promise null if no abilities receive grpcConn.setAbilityTable(null); } //create stream request and bind connection event to this connection. StreamObserver payloadStreamObserver = bindRequestStream(biRequestStreamStub, grpcConn); // stream observer to send response to server grpcConn.setPayloadStreamObserver(payloadStreamObserver); grpcConn.setGrpcFutureServiceStub(newChannelStubTemp); grpcConn.setChannel(managedChannel); //send a setup request. ConnectionSetupRequest conSetupRequest = new ConnectionSetupRequest(); conSetupRequest.setClientVersion(getClientVersion()); conSetupRequest.setLabels(super.getLabels()); // set ability table conSetupRequest.setAbilityTable( NacosAbilityManagerHolder.getInstance().getCurrentNodeAbilities(abilityMode())); conSetupRequest.setTenant(super.getTenant()); grpcConn.sendRequest(conSetupRequest); // wait for response if (recAbilityContext.isNeedToSync()) { // try to wait for notify response recAbilityContext.await(this.clientConfig.capabilityNegotiationTimeout(), TimeUnit.MILLISECONDS); // if no server abilities receiving, then reconnect if (!recAbilityContext.check(grpcConn)) { return null; } } else { // leave for adapting old version server // registration is considered successful by default after 100ms // wait to register connection setup Thread.sleep(100L); } return grpcConn; } catch (Exception e) { LOGGER.error("[{}]Fail to connect to server!,error={}", GrpcClient.this.getName(), e); // remove and notify recAbilityContext.release(null); } return null; } protected String getClientVersion() { return VersionUtils.getFullClientVersion(); } /** * ability mode: sdk client or cluster client. * * @return mode */ protected abstract AbilityMode abilityMode(); @Override protected void afterReset(ConnectResetRequest request) { recAbilityContext.release(null); } /** * This is for receiving server abilities. */ static class RecAbilityContext { /** * connection waiting for server abilities. */ private volatile Connection connection; /** * way to block client. */ private volatile CountDownLatch blocker; private volatile boolean needToSync = false; public RecAbilityContext(Connection connection) { this.connection = connection; this.blocker = new CountDownLatch(1); } /** * whether to sync for ability table. * * @return whether to sync for ability table. */ public boolean isNeedToSync() { return this.needToSync; } /** * reset with new connection which is waiting for ability table. * * @param connection new connection which is waiting for ability table. */ public void reset(Connection connection) { this.connection = connection; this.blocker = new CountDownLatch(1); this.needToSync = true; } /** * notify sync by abilities. * * @param abilities abilities. */ public void release(Map abilities) { if (this.connection != null) { this.connection.setAbilityTable(abilities); // avoid repeat setting this.connection = null; } if (this.blocker != null) { blocker.countDown(); } this.needToSync = false; } /** * await for abilities. * * @param timeout timeout. * @param unit unit. * @throws InterruptedException by blocker. */ public void await(long timeout, TimeUnit unit) throws InterruptedException { if (this.blocker != null) { this.blocker.await(timeout, unit); } this.needToSync = false; } /** * check whether receive abilities. * * @param connection conn. * @return whether receive abilities. */ public boolean check(Connection connection) { if (!connection.isAbilitiesSet()) { LOGGER.error( "Client don't receive server abilities table even empty table but server supports ability negotiation." + " You can check if it is need to adjust the timeout of ability negotiation by property: {}" + " if always fail to connect.", GrpcConstants.GRPC_CHANNEL_CAPABILITY_NEGOTIATION_TIMEOUT); connection.setAbandon(true); connection.close(); return false; } return true; } } private Optional buildSslContext() { TlsConfig tlsConfig = clientConfig.tlsConfig(); if (!tlsConfig.getEnableTls()) { return Optional.empty(); } try { SslContextBuilder builder = GrpcSslContexts.forClient(); if (StringUtils.isNotBlank(tlsConfig.getSslProvider())) { builder.sslProvider(TlsTypeResolve.getSslProvider(tlsConfig.getSslProvider())); } if (StringUtils.isNotBlank(tlsConfig.getProtocols())) { builder.protocols(tlsConfig.getProtocols().split(",")); } if (StringUtils.isNotBlank(tlsConfig.getCiphers())) { builder.ciphers(Arrays.asList(tlsConfig.getCiphers().split(","))); } if (tlsConfig.getTrustAll()) { builder.trustManager(InsecureTrustManagerFactory.INSTANCE); } else { if (StringUtils.isBlank(tlsConfig.getTrustCollectionCertFile())) { throw new IllegalArgumentException("trustCollectionCertFile must be not null"); } Resource resource = resourceLoader.getResource(tlsConfig.getTrustCollectionCertFile()); builder.trustManager(resource.getInputStream()); } if (tlsConfig.getMutualAuthEnable()) { if (StringUtils.isBlank(tlsConfig.getCertChainFile()) || StringUtils.isBlank( tlsConfig.getCertPrivateKey())) { throw new IllegalArgumentException("client certChainFile or certPrivateKey must be not null"); } Resource certChainFile = resourceLoader.getResource(tlsConfig.getCertChainFile()); Resource privateKey = resourceLoader.getResource(tlsConfig.getCertPrivateKey()); builder.keyManager(certChainFile.getInputStream(), privateKey.getInputStream(), tlsConfig.getCertPrivateKeyPassword()); } return Optional.of(builder.build()); } catch (Exception e) { throw new RuntimeException("Unable to build SslContext", e); } } private ManagedChannelBuilder buildChannel(String serverIp, int port, Optional sslContext) { if (sslContext.isPresent()) { return NettyChannelBuilder.forAddress(serverIp, port).negotiationType(NegotiationType.TLS) .sslContext(sslContext.get()); } else { return ManagedChannelBuilder.forAddress(serverIp, port).usePlaintext(); } } /** * Setup response handler. */ class SetupRequestHandler implements ServerRequestHandler { private final RecAbilityContext abilityContext; public SetupRequestHandler(RecAbilityContext abilityContext) { this.abilityContext = abilityContext; } @Override public Response requestReply(Request request, Connection connection) { // if finish setup if (request instanceof SetupAckRequest) { SetupAckRequest setupAckRequest = (SetupAckRequest) request; // remove and count down recAbilityContext.release( Optional.ofNullable(setupAckRequest.getAbilityTable()).orElse(new HashMap<>(0))); return new SetupAckResponse(); } return null; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientConfig.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.common.remote.TlsConfig; import com.alibaba.nacos.common.remote.client.RpcClientConfig; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; /** * GrpcClient config. Use to collect and init Grpc client configuration. * * @author karsonto */ public interface GrpcClientConfig extends RpcClientConfig { /** * get threadPoolCoreSize. * * @return threadPoolCoreSize. */ int threadPoolCoreSize(); /** * get threadPoolMaxSize. * * @return threadPoolMaxSize. */ int threadPoolMaxSize(); /** * get thread pool keep alive time. * * @return threadPoolKeepAlive. */ long threadPoolKeepAlive(); /** * get server check time out. * * @return serverCheckTimeOut. */ long serverCheckTimeOut(); /** * get thread pool queue size. * * @return threadPoolQueueSize. */ int threadPoolQueueSize(); /** * get maxInboundMessage size. * * @return maxInboundMessageSize. */ int maxInboundMessageSize(); /** * get channelKeepAlive time. * * @return channelKeepAlive. */ int channelKeepAlive(); /** * get channelKeepAliveTimeout. * * @return channelKeepAliveTimeout. */ long channelKeepAliveTimeout(); /** * getTlsConfig. * * @return TlsConfig. */ TlsConfig tlsConfig(); /** * Set TlsConfig. * * @param tlsConfig tlsConfig of client. */ void setTlsConfig(RpcClientTlsConfig tlsConfig); /** * Set name of client. * * @param name name of client. */ void setName(String name); /** * get timeout of connection setup(TimeUnit.MILLISECONDS). * * @return timeout of connection setup */ long capabilityNegotiationTimeout(); /** * get allowCoreThreadTimeOut flag for thread pool. * * @return allowCoreThreadTimeOut flag */ boolean allowCoreThreadTimeOut(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClusterClient.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; import com.alibaba.nacos.common.utils.VersionUtils; import java.util.Map; /** * gRPC client for cluster. * * @author liuzunfei * @version $Id: GrpcClusterClient.java, v 0.1 2020年09月07日 11:05 AM liuzunfei Exp $ */ public class GrpcClusterClient extends GrpcClient { private static final String CLUSTER_CLIENT_VERSION_PREFIX = "Nacos-Server:v"; /** * Empty constructor. * * @param name name of client. */ public GrpcClusterClient(String name) { super(name); } /** * Empty constructor. * * @param config of GrpcClientConfig. */ public GrpcClusterClient(GrpcClientConfig config) { super(config); } /** * Constructor. * * @param name name of client. * @param threadPoolCoreSize . * @param threadPoolMaxSize . * @param labels . */ public GrpcClusterClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels) { this(name, threadPoolCoreSize, threadPoolMaxSize, labels, null); } public GrpcClusterClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels, RpcClientTlsConfig tlsConfig) { super(name, threadPoolCoreSize, threadPoolMaxSize, labels, tlsConfig); } @Override protected AbilityMode abilityMode() { return AbilityMode.CLUSTER_CLIENT; } @Override protected String getClientVersion() { return CLUSTER_CLIENT_VERSION_PREFIX + VersionUtils.version; } @Override public int rpcPortOffset() { return Integer.parseInt(System.getProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY, String.valueOf(Constants.CLUSTER_GRPC_PORT_DEFAULT_OFFSET))); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConnection.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.grpc.auto.Payload; import com.alibaba.nacos.api.grpc.auto.RequestGrpc; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.RequestFuture; import com.alibaba.nacos.api.remote.RpcScheduledExecutor; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import io.grpc.ManagedChannel; import io.grpc.stub.StreamObserver; import org.checkerframework.checker.nullness.qual.Nullable; import java.util.concurrent.CancellationException; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; /** * gRPC connection. * * @author liuzunfei * @version $Id: GrpcConnection.java, v 0.1 2020年08月09日 1:36 PM liuzunfei Exp $ */ public class GrpcConnection extends Connection { /** * grpc channel. */ protected ManagedChannel channel; Executor executor; /** * stub to send request. */ protected RequestGrpc.RequestFutureStub grpcFutureServiceStub; protected StreamObserver payloadStreamObserver; public GrpcConnection(RpcClient.ServerInfo serverInfo, Executor executor) { super(serverInfo); this.executor = executor; } @Override public Response request(Request request, long timeouts) throws NacosException { Payload grpcRequest = GrpcUtils.convert(request); ListenableFuture requestFuture = grpcFutureServiceStub.request(grpcRequest); Payload grpcResponse; try { if (timeouts <= 0) { grpcResponse = requestFuture.get(); } else { grpcResponse = requestFuture.get(timeouts, TimeUnit.MILLISECONDS); } } catch (Exception e) { throw new NacosException(NacosException.SERVER_ERROR, e); } return (Response) GrpcUtils.parse(grpcResponse); } @Override public RequestFuture requestFuture(Request request) throws NacosException { Payload grpcRequest = GrpcUtils.convert(request); final ListenableFuture requestFuture = grpcFutureServiceStub.request(grpcRequest); return new RequestFuture() { @Override public boolean isDone() { return requestFuture.isDone(); } @Override public Response get() throws Exception { Payload grpcResponse = requestFuture.get(); Response response = (Response) GrpcUtils.parse(grpcResponse); if (response instanceof ErrorResponse) { throw new NacosException(response.getErrorCode(), response.getMessage()); } return response; } @Override public Response get(long timeout) throws Exception { Payload grpcResponse = requestFuture.get(timeout, TimeUnit.MILLISECONDS); Response response = (Response) GrpcUtils.parse(grpcResponse); if (response instanceof ErrorResponse) { throw new NacosException(response.getErrorCode(), response.getMessage()); } return response; } }; } public void sendResponse(Response response) { Payload convert = GrpcUtils.convert(response); payloadStreamObserver.onNext(convert); } public void sendRequest(Request request) { Payload convert = GrpcUtils.convert(request); payloadStreamObserver.onNext(convert); } @Override public void asyncRequest(Request request, final RequestCallBack requestCallBack) throws NacosException { Payload grpcRequest = GrpcUtils.convert(request); ListenableFuture requestFuture = grpcFutureServiceStub.request(grpcRequest); //set callback . Futures.addCallback(requestFuture, new FutureCallback() { @Override public void onSuccess(@Nullable Payload grpcResponse) { if (grpcResponse == null) { requestCallBack.onException(new NacosException(ResponseCode.FAIL.getCode(), "grpc response is null")); return; } Response response = (Response) GrpcUtils.parse(grpcResponse); if (response != null) { if (response instanceof ErrorResponse) { requestCallBack.onException(new NacosException(response.getErrorCode(), response.getMessage())); } else { requestCallBack.onResponse(response); } } else { requestCallBack.onException(new NacosException(ResponseCode.FAIL.getCode(), "response is null")); } } @Override public void onFailure(Throwable throwable) { if (throwable instanceof CancellationException) { requestCallBack.onException( new TimeoutException("Timeout after " + requestCallBack.getTimeout() + " milliseconds.")); } else { requestCallBack.onException(throwable); } } }, requestCallBack.getExecutor() != null ? requestCallBack.getExecutor() : this.executor); // set timeout future. ListenableFuture payloadListenableFuture = Futures.withTimeout(requestFuture, requestCallBack.getTimeout(), TimeUnit.MILLISECONDS, RpcScheduledExecutor.TIMEOUT_SCHEDULER); } @Override public void close() { if (this.payloadStreamObserver != null) { try { payloadStreamObserver.onCompleted(); } catch (Throwable ignored) { } } if (this.channel != null && !channel.isShutdown()) { try { this.channel.shutdownNow(); } catch (Throwable ignored) { } } } /** * Getter method for property channel. * * @return property value of channel */ public ManagedChannel getChannel() { return channel; } /** * Setter method for property channel. * * @param channel value to be assigned to property channel */ public void setChannel(ManagedChannel channel) { this.channel = channel; } /** * Getter method for property grpcFutureServiceStub. * * @return property value of grpcFutureServiceStub */ public RequestGrpc.RequestFutureStub getGrpcFutureServiceStub() { return grpcFutureServiceStub; } /** * Setter method for property grpcFutureServiceStub. * * @param grpcFutureServiceStub value to be assigned to property grpcFutureServiceStub */ public void setGrpcFutureServiceStub(RequestGrpc.RequestFutureStub grpcFutureServiceStub) { this.grpcFutureServiceStub = grpcFutureServiceStub; } /** * Getter method for property payloadStreamObserver. * * @return property value of payloadStreamObserver */ public StreamObserver getPayloadStreamObserver() { return payloadStreamObserver; } /** * Setter method for property payloadStreamObserver. * * @param payloadStreamObserver value to be assigned to property payloadStreamObserver */ public void setPayloadStreamObserver(StreamObserver payloadStreamObserver) { this.payloadStreamObserver = payloadStreamObserver; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstants.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; 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 java.lang.reflect.Field; import java.util.Collections; import java.util.HashSet; import java.util.Set; /** * GrpcConsts. * * @author karsonto */ public class GrpcConstants { public static final String NACOS_SERVER_GRPC_PORT_OFFSET_KEY = "nacos.server.grpc.port.offset"; public static final String NACOS_CLIENT_GRPC = "nacos.remote.client.grpc"; @GRpcConfigLabel public static final String GRPC_NAME = NACOS_CLIENT_GRPC + ".name"; @GRpcConfigLabel public static final String GRPC_THREADPOOL_KEEPALIVETIME = NACOS_CLIENT_GRPC + ".pool.alive"; @GRpcConfigLabel public static final String GRPC_THREADPOOL_CORE_SIZE = NACOS_CLIENT_GRPC + ".pool.core.size"; @GRpcConfigLabel public static final String GRPC_RETRY_TIMES = NACOS_CLIENT_GRPC + ".retry.times"; @GRpcConfigLabel public static final String GRPC_TIMEOUT_MILLS = NACOS_CLIENT_GRPC + ".timeout"; @GRpcConfigLabel public static final String GRPC_CONNECT_KEEP_ALIVE_TIME = NACOS_CLIENT_GRPC + ".connect.keep.alive"; @GRpcConfigLabel public static final String GRPC_THREADPOOL_MAX_SIZE = NACOS_CLIENT_GRPC + ".pool.max.size"; @GRpcConfigLabel public static final String GRPC_SERVER_CHECK_TIMEOUT = NACOS_CLIENT_GRPC + ".server.check.timeout"; @GRpcConfigLabel public static final String GRPC_QUEUESIZE = NACOS_CLIENT_GRPC + ".queue.size"; @GRpcConfigLabel public static final String GRPC_HEALTHCHECK_RETRY_TIMES = NACOS_CLIENT_GRPC + ".health.retry"; @GRpcConfigLabel public static final String GRPC_HEALTHCHECK_TIMEOUT = NACOS_CLIENT_GRPC + ".health.timeout"; @GRpcConfigLabel public static final String GRPC_MAX_INBOUND_MESSAGE_SIZE = NACOS_CLIENT_GRPC + ".maxinbound.message.size"; @GRpcConfigLabel public static final String GRPC_CHANNEL_KEEP_ALIVE_TIME = NACOS_CLIENT_GRPC + ".channel.keep.alive"; @GRpcConfigLabel public static final String GRPC_CHANNEL_KEEP_ALIVE_TIMEOUT = NACOS_CLIENT_GRPC + ".channel.keep.alive.timeout"; @GRpcConfigLabel public static final String GRPC_CHANNEL_CAPABILITY_NEGOTIATION_TIMEOUT = NACOS_CLIENT_GRPC + ".channel.capability.negotiation.timeout"; @GRpcConfigLabel public static final String GRPC_THREADPOOL_ALLOW_CORE_THREAD_TIMEOUT = NACOS_CLIENT_GRPC + ".pool.core.timeout"; private static final Set CONFIG_NAMES = new HashSet<>(); @Documented @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) protected @interface GRpcConfigLabel { } static { Class clazz = GrpcConstants.class; Field[] declaredFields = clazz.getDeclaredFields(); for (Field declaredField : declaredFields) { declaredField.setAccessible(true); if (declaredField.getType().equals(String.class) && null != declaredField.getAnnotation( GRpcConfigLabel.class)) { try { CONFIG_NAMES.add((String) declaredField.get(null)); } catch (IllegalAccessException ignored) { } } } } public static Set getRpcParams() { return Collections.unmodifiableSet(CONFIG_NAMES); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcSdkClient.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; import java.util.Map; /** * gRPC client for sdk. * * @author liuzunfei * @version $Id: GrpcSdkClient.java, v 0.1 2020年09月07日 11:05 AM liuzunfei Exp $ */ public class GrpcSdkClient extends GrpcClient { /** * Constructor. * * @param name name of client. */ public GrpcSdkClient(String name) { super(name); } /** * Constructor. * * @param name name of client. * @param threadPoolCoreSize . * @param threadPoolMaxSize . * @param labels . */ public GrpcSdkClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels) { this(name, threadPoolCoreSize, threadPoolMaxSize, labels, null); } public GrpcSdkClient(String name, Integer threadPoolCoreSize, Integer threadPoolMaxSize, Map labels, RpcClientTlsConfig tlsConfig) { super(name, threadPoolCoreSize, threadPoolMaxSize, labels, tlsConfig); } @Override protected AbilityMode abilityMode() { return AbilityMode.SDK_CLIENT; } /** * constructor. * * @param config of GrpcClientConfig. */ public GrpcSdkClient(GrpcClientConfig config) { super(config); } @Override public int rpcPortOffset() { return Integer.parseInt(System.getProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY, String.valueOf(Constants.SDK_GRPC_PORT_DEFAULT_OFFSET))); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/client/grpc/GrpcUtils.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.grpc.auto.Metadata; import com.alibaba.nacos.api.grpc.auto.Payload; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.utils.NetUtils; import com.alibaba.nacos.common.remote.PayloadRegistry; import com.alibaba.nacos.common.remote.exception.RemoteException; import com.alibaba.nacos.common.utils.JacksonUtils; import com.fasterxml.jackson.databind.util.ByteBufferBackedInputStream; import com.google.protobuf.Any; import com.google.protobuf.ByteString; import com.google.protobuf.UnsafeByteOperations; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.Map; /** * gRPC utils, use to parse request and response. * * @author liuzunfei * @version $Id: GrpcUtils.java, v 0.1 2020年08月09日 1:43 PM liuzunfei Exp $ */ public class GrpcUtils { /** * convert request to payload. * * @param request request. * @param meta request meta. * @return payload. */ public static Payload convert(Request request, RequestMeta meta) { //meta. Payload.Builder payloadBuilder = Payload.newBuilder(); Metadata.Builder metaBuilder = Metadata.newBuilder(); if (meta != null) { metaBuilder.putAllHeaders(request.getHeaders()).setType(request.getClass().getSimpleName()); } metaBuilder.setClientIp(NetUtils.localIp()); payloadBuilder.setMetadata(metaBuilder.build()); // request body . byte[] jsonBytes = convertRequestToByte(request); return payloadBuilder.setBody(Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes))).build(); } /** * convert request to payload. * * @param request request. * @return payload. */ public static Payload convert(Request request) { Metadata newMeta = Metadata.newBuilder().setType(request.getClass().getSimpleName()) .setClientIp(NetUtils.localIp()).putAllHeaders(request.getHeaders()).build(); byte[] jsonBytes = convertRequestToByte(request); Payload.Builder builder = Payload.newBuilder(); return builder.setBody(Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes))) .setMetadata(newMeta).build(); } /** * convert response to payload. * * @param response response. * @return payload. */ public static Payload convert(Response response) { byte[] jsonBytes = JacksonUtils.toJsonBytes(response); Metadata.Builder metaBuilder = Metadata.newBuilder().setType(response.getClass().getSimpleName()); return Payload.newBuilder().setBody(Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes))) .setMetadata(metaBuilder.build()).build(); } private static byte[] convertRequestToByte(Request request) { Map requestHeaders = new HashMap<>(request.getHeaders()); request.clearHeaders(); byte[] jsonBytes = JacksonUtils.toJsonBytes(request); request.putAllHeader(requestHeaders); return jsonBytes; } /** * parse payload to request/response model. * * @param payload payload to be parsed. * @return payload */ public static Object parse(Payload payload) { Class classType = PayloadRegistry.getClassByType(payload.getMetadata().getType()); if (classType != null) { ByteString byteString = payload.getBody().getValue(); ByteBuffer byteBuffer = byteString.asReadOnlyByteBuffer(); Object obj = JacksonUtils.toObj(new ByteBufferBackedInputStream(byteBuffer), classType); if (obj instanceof Request) { ((Request) obj).putAllHeader(payload.getMetadata().getHeadersMap()); } return obj; } else { throw new RemoteException(NacosException.SERVER_ERROR, "Unknown payload type:" + payload.getMetadata().getType()); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/exception/ConnectionAlreadyClosedException.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.exception; /** * connection already closed exception. * * @author liuzunfei * @version $Id: ConnectionAlreadyClosedException.java, v 0.1 2020年07月22日 7:28 PM liuzunfei Exp $ */ public class ConnectionAlreadyClosedException extends RemoteException { private static final int CONNECTION_ALREADY_CLOSED = 600; public ConnectionAlreadyClosedException(String msg) { super(CONNECTION_ALREADY_CLOSED, msg); } public ConnectionAlreadyClosedException() { super(CONNECTION_ALREADY_CLOSED); } public ConnectionAlreadyClosedException(Throwable throwable) { super(CONNECTION_ALREADY_CLOSED, throwable); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/exception/ConnectionBusyException.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.exception; /** * connection is busy exception. * * @author liuzunfei * @version $Id: ConnectionBusyException.java, v 0.1 2020年11月30日 7:28 PM liuzunfei Exp $ */ public class ConnectionBusyException extends RemoteException { private static final int CONNECTION_BUSY = 601; public ConnectionBusyException(String msg) { super(CONNECTION_BUSY, msg); } public ConnectionBusyException(Throwable throwable) { super(CONNECTION_BUSY, throwable); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/remote/exception/RemoteException.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.exception; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; /** * super exception in remote module. * * @author liuzunfei * @version $Id: RemoteException.java, v 0.1 2020年07月22日 7:24 PM liuzunfei Exp $ */ public class RemoteException extends NacosRuntimeException { public RemoteException(int errorCode) { super(errorCode); } public RemoteException(int errorCode, String msg) { super(errorCode, msg); } public RemoteException(int errorCode, Throwable throwable) { super(errorCode, throwable); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/spi/NacosServiceLoader.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.spi; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.Map; import java.util.ServiceLoader; import java.util.concurrent.ConcurrentHashMap; /** * Nacos SPI Service Loader. * * @author xiweng.yy */ public class NacosServiceLoader { private static final Map, Collection>> SERVICES = new ConcurrentHashMap<>(); /** * Load service. * *

    Load service by SPI and cache the classes for reducing cost when load second time. * * @param service service class * @param type of service * @return service instances */ public static Collection load(final Class service) { if (SERVICES.containsKey(service)) { return newServiceInstances(service); } Collection result = new LinkedHashSet<>(); for (T each : ServiceLoader.load(service)) { result.add(each); cacheServiceClass(service, each); } return result; } private static void cacheServiceClass(final Class service, final T instance) { SERVICES.computeIfAbsent(service, k -> new LinkedHashSet<>()).add(instance.getClass()); } /** * New service instances. * * @param service service class * @param type of service * @return service instances */ public static Collection newServiceInstances(final Class service) { return SERVICES.containsKey(service) ? newServiceInstancesFromCache(service) : Collections.emptyList(); } @SuppressWarnings("unchecked") private static Collection newServiceInstancesFromCache(Class service) { Collection result = new LinkedHashSet<>(); for (Class each : SERVICES.get(service)) { result.add((T) newServiceInstance(each)); } return result; } private static Object newServiceInstance(final Class clazz) { try { return clazz.newInstance(); } catch (IllegalAccessException | InstantiationException e) { throw new ServiceLoaderException(clazz, e); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/spi/ServiceLoaderException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.spi; /** * Nacos service loader exception. * * @author xiweng.yy */ public class ServiceLoaderException extends RuntimeException { private static final long serialVersionUID = -4133484884875183141L; private final Class clazz; public ServiceLoaderException(Class clazz, Exception caused) { super(String.format("Can not load class `%s` by SPI ", clazz.getName()), caused); this.clazz = clazz; } public Class getClazz() { return clazz; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/AbstractDelayTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task; /** * Abstract task which can delay and merge. * * @author huali * @author xiweng.yy */ public abstract class AbstractDelayTask implements NacosTask { /** * Task time interval between twice processing, unit is millisecond. */ private long taskInterval; /** * The time which was processed at last time, unit is millisecond. */ private long lastProcessTime; /** * The default time interval, in milliseconds, between tasks. */ protected static final long INTERVAL = 1000L; /** * merge task. * * @param task task */ public abstract void merge(AbstractDelayTask task); public void setTaskInterval(long interval) { this.taskInterval = interval; } public long getTaskInterval() { return this.taskInterval; } public void setLastProcessTime(long lastProcessTime) { this.lastProcessTime = lastProcessTime; } public long getLastProcessTime() { return this.lastProcessTime; } @Override public boolean shouldProcess() { return (System.currentTimeMillis() - this.lastProcessTime >= this.taskInterval); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/AbstractExecuteTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task; /** * Abstract task which should be executed immediately. * * @author xiweng.yy */ public abstract class AbstractExecuteTask implements NacosTask, Runnable { protected static final long INTERVAL = 3000L; @Override public boolean shouldProcess() { return true; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/BatchTaskCounter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** * batch task counter. * * @author shiyiyue */ public class BatchTaskCounter { List batchCounter; public BatchTaskCounter(int totalBatch) { initBatchCounter(totalBatch); } /** * init counter. * @param totalBatch totalBatch. */ private void initBatchCounter(int totalBatch) { batchCounter = new ArrayList<>(totalBatch); for (int i = 0; i < totalBatch; i++) { batchCounter.add(i, new AtomicBoolean(false)); } } /** * set bath succeed. * @param batch succeed batch. */ public void batchSuccess(int batch) { if (batch <= batchCounter.size()) { batchCounter.get(batch - 1).set(true); } } /** * check all completed. * @return */ public boolean batchCompleted() { for (AtomicBoolean atomicBoolean : batchCounter) { if (!atomicBoolean.get()) { return false; } } return true; } public int getTotalBatch() { return batchCounter.size(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/NacosTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task; /** * Nacos task. * * @author xiweng.yy */ public interface NacosTask { /** * Judge Whether this nacos task should do. * * @return true means the nacos task should be done, otherwise false */ boolean shouldProcess(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/NacosTaskProcessor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task; /** * Task processor. * * @author Nacos */ public interface NacosTaskProcessor { /** * Process task. * * @param task task. * @return process task result. */ boolean process(NacosTask task); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/engine/AbstractNacosTaskExecuteEngine.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task.engine; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.concurrent.ConcurrentHashMap; /** * Abstract nacos task execute engine. * * @author xiweng.yy */ public abstract class AbstractNacosTaskExecuteEngine implements NacosTaskExecuteEngine { private final Logger log; private final ConcurrentHashMap taskProcessors = new ConcurrentHashMap<>(); private NacosTaskProcessor defaultTaskProcessor; public AbstractNacosTaskExecuteEngine(Logger logger) { this.log = null != logger ? logger : LoggerFactory.getLogger(AbstractNacosTaskExecuteEngine.class.getName()); } @Override public void addProcessor(Object key, NacosTaskProcessor taskProcessor) { taskProcessors.putIfAbsent(key, taskProcessor); } @Override public void removeProcessor(Object key) { taskProcessors.remove(key); } @Override public NacosTaskProcessor getProcessor(Object key) { return taskProcessors.containsKey(key) ? taskProcessors.get(key) : defaultTaskProcessor; } @Override public Collection getAllProcessorKey() { return taskProcessors.keySet(); } @Override public void setDefaultTaskProcessor(NacosTaskProcessor defaultTaskProcessor) { this.defaultTaskProcessor = defaultTaskProcessor; } protected Logger getEngineLog() { return log; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/engine/NacosDelayTaskExecuteEngine.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task.engine; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.task.AbstractDelayTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import org.slf4j.Logger; import java.util.Collection; import java.util.HashSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; /** * Nacos delay task execute engine. * * @author xiweng.yy */ public class NacosDelayTaskExecuteEngine extends AbstractNacosTaskExecuteEngine { private final ScheduledExecutorService processingExecutor; protected final ConcurrentHashMap tasks; protected final ReentrantLock lock = new ReentrantLock(); public NacosDelayTaskExecuteEngine(String name) { this(name, null); } public NacosDelayTaskExecuteEngine(String name, Logger logger) { this(name, 32, logger, 100L); } public NacosDelayTaskExecuteEngine(String name, int initCapacity, Logger logger, long processInterval) { super(logger); tasks = new ConcurrentHashMap<>(initCapacity); processingExecutor = ExecutorFactory.newSingleScheduledExecutorService(new NameThreadFactory(name)); processingExecutor .scheduleWithFixedDelay(new ProcessRunnable(), processInterval, processInterval, TimeUnit.MILLISECONDS); } @Override public int size() { lock.lock(); try { return tasks.size(); } finally { lock.unlock(); } } @Override public boolean isEmpty() { lock.lock(); try { return tasks.isEmpty(); } finally { lock.unlock(); } } @Override public AbstractDelayTask removeTask(Object key) { lock.lock(); try { AbstractDelayTask task = tasks.get(key); if (null != task && task.shouldProcess()) { return tasks.remove(key); } else { return null; } } finally { lock.unlock(); } } @Override public Collection getAllTaskKeys() { Collection keys = new HashSet<>(); lock.lock(); try { keys.addAll(tasks.keySet()); } finally { lock.unlock(); } return keys; } @Override public void shutdown() throws NacosException { tasks.clear(); processingExecutor.shutdown(); } @Override public void addTask(Object key, AbstractDelayTask newTask) { lock.lock(); try { AbstractDelayTask existTask = tasks.get(key); if (null != existTask) { newTask.merge(existTask); } tasks.put(key, newTask); } finally { lock.unlock(); } } /** * process tasks in execute engine. */ protected void processTasks() { Collection keys = getAllTaskKeys(); for (Object taskKey : keys) { AbstractDelayTask task = removeTask(taskKey); if (null == task) { continue; } NacosTaskProcessor processor = getProcessor(taskKey); try { // ReAdd task if process failed if (!processor.process(task)) { retryFailedTask(taskKey, task); } } catch (Throwable e) { getEngineLog().error("Nacos task execute error ", e); retryFailedTask(taskKey, task); } } } private void retryFailedTask(Object key, AbstractDelayTask task) { task.setLastProcessTime(System.currentTimeMillis()); addTask(key, task); } private class ProcessRunnable implements Runnable { @Override public void run() { try { processTasks(); } catch (Throwable e) { getEngineLog().error(e.toString(), e); } } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/engine/NacosExecuteTaskExecuteEngine.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task.engine; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.task.AbstractExecuteTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.utils.ThreadUtils; import org.slf4j.Logger; import java.util.Collection; /** * Nacos execute task execute engine. * * @author xiweng.yy */ public class NacosExecuteTaskExecuteEngine extends AbstractNacosTaskExecuteEngine { private final TaskExecuteWorker[] executeWorkers; public NacosExecuteTaskExecuteEngine(String name, Logger logger) { this(name, logger, ThreadUtils.getSuitableThreadCount(1)); } public NacosExecuteTaskExecuteEngine(String name, Logger logger, int dispatchWorkerCount) { super(logger); executeWorkers = new TaskExecuteWorker[dispatchWorkerCount]; for (int mod = 0; mod < dispatchWorkerCount; ++mod) { executeWorkers[mod] = new TaskExecuteWorker(name, mod, dispatchWorkerCount, getEngineLog()); } } @Override public int size() { int result = 0; for (TaskExecuteWorker each : executeWorkers) { result += each.pendingTaskCount(); } return result; } @Override public boolean isEmpty() { return 0 == size(); } @Override public void addTask(Object tag, AbstractExecuteTask task) { NacosTaskProcessor processor = getProcessor(tag); if (null != processor) { processor.process(task); return; } TaskExecuteWorker worker = getWorker(tag); worker.process(task); } private TaskExecuteWorker getWorker(Object tag) { int idx = (tag.hashCode() & Integer.MAX_VALUE) % workersCount(); return executeWorkers[idx]; } private int workersCount() { return executeWorkers.length; } @Override public AbstractExecuteTask removeTask(Object key) { throw new UnsupportedOperationException("ExecuteTaskEngine do not support remove task"); } @Override public Collection getAllTaskKeys() { throw new UnsupportedOperationException("ExecuteTaskEngine do not support get all task keys"); } @Override public void shutdown() throws NacosException { for (TaskExecuteWorker each : executeWorkers) { each.shutdown(); } } /** * Get workers status. * * @return workers status string */ public String workersStatus() { StringBuilder sb = new StringBuilder(); for (TaskExecuteWorker worker : executeWorkers) { sb.append(worker.status()).append('\n'); } return sb.toString(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/engine/NacosTaskExecuteEngine.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task.engine; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import java.util.Collection; /** * Nacos task execute engine. * * @author xiweng.yy */ public interface NacosTaskExecuteEngine extends Closeable { /** * Get Task size in execute engine. * * @return size of task */ int size(); /** * Whether the execute engine is empty. * * @return true if the execute engine has no task to do, otherwise false */ boolean isEmpty(); /** * Add task processor {@link NacosTaskProcessor} for execute engine. * * @param key key of task * @param taskProcessor task processor */ void addProcessor(Object key, NacosTaskProcessor taskProcessor); /** * Remove task processor {@link NacosTaskProcessor} form execute engine for key. * * @param key key of task */ void removeProcessor(Object key); /** * Try to get {@link NacosTaskProcessor} by key, if non-exist, will return default processor. * * @param key key of task * @return task processor for task key or default processor if task processor for task key non-exist */ NacosTaskProcessor getProcessor(Object key); /** * Get all processor key. * * @return collection of processors */ Collection getAllProcessorKey(); /** * Set default task processor. If do not find task processor by task key, use this default processor to process * task. * * @param defaultTaskProcessor default task processor */ void setDefaultTaskProcessor(NacosTaskProcessor defaultTaskProcessor); /** * Add task into execute pool. * * @param key key of task * @param task task */ void addTask(Object key, T task); /** * Remove task. * * @param key key of task * @return nacos task */ T removeTask(Object key); /** * Get all task keys. * * @return collection of task keys. */ Collection getAllTaskKeys(); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/task/engine/TaskExecuteWorker.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task.engine; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.lifecycle.Closeable; import com.alibaba.nacos.common.task.AbstractExecuteTask; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.atomic.AtomicBoolean; /** * Nacos execute task execute worker. * * @author xiweng.yy */ public final class TaskExecuteWorker implements NacosTaskProcessor, Closeable { /** * Max task queue size 32768. */ private static final int QUEUE_CAPACITY = 1 << 15; private final Logger log; private final String name; private final BlockingQueue queue; private final AtomicBoolean closed; private final InnerWorker realWorker; public TaskExecuteWorker(final String name, final int mod, final int total) { this(name, mod, total, null); } public TaskExecuteWorker(final String name, final int mod, final int total, final Logger logger) { this.name = name + "_" + mod + "%" + total; this.queue = new ArrayBlockingQueue<>(QUEUE_CAPACITY); this.closed = new AtomicBoolean(false); this.log = null == logger ? LoggerFactory.getLogger(TaskExecuteWorker.class) : logger; realWorker = new InnerWorker(this.name); realWorker.start(); } public String getName() { return name; } @Override public boolean process(NacosTask task) { if (task instanceof AbstractExecuteTask) { putTask((Runnable) task); } return true; } private void putTask(Runnable task) { try { queue.put(task); } catch (InterruptedException ire) { log.error(ire.toString(), ire); } } public int pendingTaskCount() { return queue.size(); } /** * Worker status. */ public String status() { return getName() + ", pending tasks: " + pendingTaskCount(); } @Override public void shutdown() throws NacosException { queue.clear(); closed.compareAndSet(false, true); realWorker.interrupt(); } /** * Inner execute worker. */ private class InnerWorker extends Thread { InnerWorker(String name) { setDaemon(false); setName(name); } @Override public void run() { while (!closed.get()) { try { Runnable task = queue.take(); long begin = System.currentTimeMillis(); task.run(); long duration = System.currentTimeMillis() - begin; if (duration > 1000L) { log.warn("task {} takes {}ms", task, duration); } } catch (InterruptedException e) { // [issue #13752] ignore stack log } catch (Throwable e) { log.error("[TASK-FAILED] " + e, e); } } } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/tls/SelfHostnameVerifier.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; import com.alibaba.nacos.common.utils.InternetAddressUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; import java.util.concurrent.ConcurrentHashMap; /** * A HostnameVerifier verify ipv4 and localhost. * * @author wangwei */ public final class SelfHostnameVerifier implements HostnameVerifier { private static final Logger LOGGER = LoggerFactory.getLogger(SelfHostnameVerifier.class); private final HostnameVerifier hv; private static final ConcurrentHashMap HOSTS = new ConcurrentHashMap<>(); private static final String[] LOCALHOST_HOSTNAME = new String[] {InternetAddressUtil.LOCAL_HOST, InternetAddressUtil.localHostIp()}; public SelfHostnameVerifier(HostnameVerifier hv) { this.hv = hv; } @Override public boolean verify(String hostname, SSLSession session) { if (LOCALHOST_HOSTNAME[0].equalsIgnoreCase(hostname) || LOCALHOST_HOSTNAME[1].equals(hostname)) { return true; } if (isIp(hostname)) { return true; } return hv.verify(hostname, session); } private static boolean isIp(String host) { if (host == null || host.isEmpty()) { LOGGER.warn("host is empty, isIp = false"); return false; } Boolean cacheHostVerify = HOSTS.get(host); if (cacheHostVerify != null) { return cacheHostVerify; } boolean isIp = InternetAddressUtil.isIp(host); HOSTS.putIfAbsent(host, isIp); return isIp; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/tls/SelfTrustManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; import com.alibaba.nacos.common.utils.IoUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.net.ssl.SSLException; import javax.net.ssl.TrustManager; import javax.net.ssl.TrustManagerFactory; import javax.net.ssl.X509TrustManager; import java.io.FileInputStream; import java.io.InputStream; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.Collection; /** * A TrustManager tool returns the specified TrustManager. * * @author wangwei */ public final class SelfTrustManager { private static final Logger LOGGER = LoggerFactory.getLogger(SelfTrustManager.class); @SuppressWarnings("checkstyle:WhitespaceAround") static TrustManager[] trustAll = new TrustManager[] {new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }}; /** * Returns the result of calling {@link #buildSecureTrustManager} if {@code needAuth} is enable and {@code * trustCertPath} exists. Returns the {@link #trustAll} otherwise. * * @param needAuth whether need client auth * @param trustCertPath trust certificate path * @return Array of {@link TrustManager } */ public static TrustManager[] trustManager(boolean needAuth, String trustCertPath) { if (needAuth) { try { return trustCertPath == null ? null : buildSecureTrustManager(trustCertPath); } catch (SSLException e) { LOGGER.warn("degrade trust manager as build failed, " + "will trust all certs."); return trustAll; } } else { return trustAll; } } private static TrustManager[] buildSecureTrustManager(String trustCertPath) throws SSLException { TrustManagerFactory selfTmf; InputStream in = null; try { String algorithm = TrustManagerFactory.getDefaultAlgorithm(); selfTmf = TrustManagerFactory.getInstance(algorithm); KeyStore trustKeyStore = KeyStore.getInstance("JKS"); trustKeyStore.load(null, null); in = new FileInputStream(trustCertPath); CertificateFactory cf = CertificateFactory.getInstance("X.509"); Collection certs = (Collection) cf.generateCertificates(in); int count = 0; for (Certificate cert : certs) { trustKeyStore.setCertificateEntry("cert-" + (count++), cert); } selfTmf.init(trustKeyStore); return selfTmf.getTrustManagers(); } catch (Exception e) { LOGGER.error("build client trustManagerFactory failed", e); throw new SSLException(e); } finally { IoUtils.closeQuietly(in); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/tls/TlsFileWatcher.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.utils.ClassUtils; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.MD5Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; /** * Certificate file update monitoring * *

    Considering that the current client needs to support jdk 1.6 and module dependencies , * the WatchFileCenter in the core module is not used * * @author wangwei */ public final class TlsFileWatcher { private static final Logger LOGGER = LoggerFactory.getLogger(TlsFileWatcher.class); private AtomicBoolean started = new AtomicBoolean(false); private final int checkInterval = TlsSystemConfig.tlsFileCheckInterval; private Map fileMd5Map = new HashMap<>(); private ConcurrentHashMap watchFilesMap = new ConcurrentHashMap<>(); private final ScheduledExecutorService service = ExecutorFactory.Managed .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(TlsFileWatcher.class), new NameThreadFactory("com.alibaba.nacos.core.common.tls")); private static TlsFileWatcher tlsFileWatcher = new TlsFileWatcher(); private TlsFileWatcher() { start(); } public static TlsFileWatcher getInstance() { return tlsFileWatcher; } /** * Add file change listener for specified path. * * @param fileChangeListener listener * @param filePaths file paths * @throws IOException If an I/O error occurs */ public void addFileChangeListener(FileChangeListener fileChangeListener, String... filePaths) throws IOException { for (String filePath : filePaths) { if (filePath != null && new File(filePath).exists()) { watchFilesMap.put(filePath, fileChangeListener); InputStream in = null; try { in = new FileInputStream(filePath); fileMd5Map.put(filePath, MD5Utils.md5Hex(IoUtils.toString(in, Constants.ENCODE), Constants.ENCODE)); } finally { IoUtils.closeQuietly(in); } } } } /** * start file watch task. Notify when the MD5 of file changed */ public void start() { if (started.compareAndSet(false, true)) { service.scheduleAtFixedRate(() -> { for (Map.Entry item : watchFilesMap.entrySet()) { String filePath = item.getKey(); String newHash; InputStream in = null; try { in = new FileInputStream(filePath); newHash = MD5Utils.md5Hex(IoUtils.toString(in, Constants.ENCODE), Constants.ENCODE); } catch (Exception exception) { LOGGER.warn(" service has exception when calculate the file MD5. " + exception); continue; } finally { IoUtils.closeQuietly(in); } if (!newHash.equals(fileMd5Map.get(filePath))) { LOGGER.info(filePath + " file hash changed, need reload ssl context"); fileMd5Map.put(filePath, newHash); item.getValue().onChanged(filePath); LOGGER.info(filePath + " onChanged success!"); } } }, 1, checkInterval, TimeUnit.MINUTES); } } public interface FileChangeListener { /** * listener onChanged event. * * @param filePath Path of changed file */ void onChanged(String filePath); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/tls/TlsHelper.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; import javax.net.ssl.SSLContext; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; /** * Utils for build {@link SSLContext}. * *

    Currently only supports client-side * *

    Making your client support TLS without authentication

    *
     * System.setProperty({@link TlsSystemConfig#TLS_ENABLE}, "true");
     * 
    * *

    Making your client support TLS one-way authentication

    * *
     * System.setProperty({@link TlsSystemConfig#TLS_ENABLE}, "true");
     * System.setProperty({@link TlsSystemConfig#CLIENT_AUTH}, "true");
     * System.setProperty({@link TlsSystemConfig#CLIENT_TRUST_CERT}, "trustCert");
     * 
    * * @author wangwei * @date 2020/8/19 2:59 PM */ public final class TlsHelper { /** * Returns a {@link org.apache.http.ssl.SSLContexts}. * *

    For example

    * HttpsURLConnection.setDefaultSSLSocketFactory(TlsHelper.buildSslContext(true).getSocketFactory()); * * @param forClient whether for client * @return {@link SSLContext} * @throws NoSuchAlgorithmException Not support the specified algorithm * @throws KeyManagementException KeyManagement exception */ public static SSLContext buildSslContext(boolean forClient) throws NoSuchAlgorithmException, KeyManagementException { SSLContext sslcontext = SSLContext.getInstance("TLS"); sslcontext.init(null, SelfTrustManager .trustManager(TlsSystemConfig.tlsClientAuthServer, TlsSystemConfig.tlsClientTrustCertPath), new java.security.SecureRandom()); return sslcontext; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/tls/TlsSystemConfig.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; /** * tls system config. * * @author wangwei */ public final class TlsSystemConfig { public static final String TLS_TEST_MODE_ENABLE = "tls.test"; public static final String TLS_ENABLE = "tls.enable"; public static final String CLIENT_AUTH = "tls.client.authServer"; public static final String CLIENT_KEYPATH = "tls.client.keyPath"; public static final String CLIENT_KEYPASSWORD = "tls.client.keyPassword"; public static final String CLIENT_CERTPATH = "tls.client.certPath"; public static final String CLIENT_TRUST_CERT = "tls.client.trustCertPath"; public static final String SERVER_AUTH = "tls.server.authClient"; public static final String SERVER_KEYPATH = "tls.server.keyPath"; public static final String SERVER_KEYPASSWORD = "tls.server.keyPassword"; public static final String SERVER_CERTPATH = "tls.server.certPath"; public static final String SERVER_TRUST_CERT = "tls.server.trustCertPath"; public static final String CHECK_INTERVAL = "checkIntervalTlsFile"; /** * To determine whether use SSL in client-side. */ public static boolean tlsEnable = Boolean.parseBoolean(System.getProperty(TLS_ENABLE, "false")); /** * To determine whether use test mode when initialize TLS context. */ public static boolean tlsTestModeEnable = Boolean.parseBoolean(System.getProperty(TLS_TEST_MODE_ENABLE, "false")); /** * To determine whether verify the server endpoint's certificate strictly. */ public static boolean tlsClientAuthServer = Boolean.parseBoolean(System.getProperty(CLIENT_AUTH, "false")); /** * To determine whether verify the client endpoint's certificate strictly. */ public static boolean tlsServerAuthClient = Boolean.parseBoolean(System.getProperty(SERVER_AUTH, "false")); /** * The store path of client-side private key. */ public static String tlsClientKeyPath = System.getProperty(CLIENT_KEYPATH, null); /** * The password of the client-side private key. */ public static String tlsClientKeyPassword = System.getProperty(CLIENT_KEYPASSWORD, null); /** * The store path of client-side X.509 certificate chain in PEM format. */ public static String tlsClientCertPath = System.getProperty(CLIENT_CERTPATH, null); /** * The store path of trusted certificates for verifying the server endpoint's certificate. */ public static String tlsClientTrustCertPath = System.getProperty(CLIENT_TRUST_CERT, null); /** * The store path of server-side private key. */ public static String tlsServerKeyPath = System.getProperty(SERVER_KEYPATH, null); /** * The password of the server-side private key. */ public static String tlsServerKeyPassword = System.getProperty(SERVER_KEYPASSWORD, null); /** * The store path of server-side X.509 certificate chain in PEM format. */ public static String tlsServerCertPath = System.getProperty(SERVER_CERTPATH, null); /** * The store path of trusted certificates for verifying the client endpoint's certificate. */ public static String tlsServerTrustCertPath = System.getProperty(SERVER_TRUST_CERT, null); /** * tls file check interval , default is 10 min. */ public static int tlsFileCheckInterval = Integer.parseInt(System.getProperty(CHECK_INTERVAL, "10")); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/DeregisterInstanceReason.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace; /** * The reasons of deregister instance. * * @author yanda */ public enum DeregisterInstanceReason { /** * client initiates request. */ REQUEST, /** * Instance native disconnected. */ NATIVE_DISCONNECTED, /** * Instance synced disconnected. */ SYNCED_DISCONNECTED, /** * Instance heart beat timeout expire. */ HEARTBEAT_EXPIRE, } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/HealthCheckType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace; /** * The types of health check. * * @author yanda */ public enum HealthCheckType { /** * Instance heart beat timeout. */ CLIENT_BEAT("client_beat"), /** * Http health check. */ HTTP_HEALTH_CHECK("http"), /** * Mysql health check. */ MYSQL_HEALTH_CHECK("mysql"), /** * Tcp super sense health check . */ TCP_SUPER_SENSE("tcp"); private String prefix; HealthCheckType(String prefix) { this.prefix = prefix; } public String getPrefix() { return prefix; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/TraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event; import com.alibaba.nacos.common.notify.Event; /** * Trace event. * * @author yanda */ public class TraceEvent extends Event { private static final long serialVersionUID = -3065900892505697062L; private final String type; private final long eventTime; private final String namespace; private final String group; private final String name; public String getType() { return type; } public long getEventTime() { return eventTime; } public String getNamespace() { return namespace; } public String getGroup() { return group; } public String getName() { return name; } public TraceEvent(String eventType, long eventTime, String namespace, String group, String name) { this.type = eventType; this.eventTime = eventTime; this.namespace = namespace; this.group = group; this.name = name; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/BatchRegisterInstanceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; /** * Naming register instance trace event. * * @author xiweng.yy */ public class BatchRegisterInstanceTraceEvent extends RegisterInstanceTraceEvent { public BatchRegisterInstanceTraceEvent(long eventTime, String clientIp, boolean rpc, String serviceNamespace, String serviceGroup, String serviceName, String instanceIp, int instancePort) { super("BATCH_REGISTER_INSTANCE_TRACE_EVENT", eventTime, clientIp, rpc, serviceNamespace, serviceGroup, serviceName, instanceIp, instancePort); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/DeregisterInstanceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import com.alibaba.nacos.common.trace.DeregisterInstanceReason; /** * Naming deregister instance trace event. * * @author yanda */ public class DeregisterInstanceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = 3850573686472190256L; private final String clientIp; private final boolean rpc; private final String instanceIp; private final int instancePort; public final DeregisterInstanceReason reason; public String getClientIp() { return clientIp; } public boolean isRpc() { return rpc; } public String getInstanceIp() { return instanceIp; } public int getInstancePort() { return instancePort; } public String toInetAddr() { return instanceIp + ":" + instancePort; } public DeregisterInstanceReason getReason() { return reason; } public DeregisterInstanceTraceEvent(long eventTime, String clientIp, boolean rpc, DeregisterInstanceReason reason, String serviceNamespace, String serviceGroup, String serviceName, String instanceIp, int instancePort) { super("DEREGISTER_INSTANCE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); this.clientIp = clientIp; this.reason = reason; this.rpc = rpc; this.instanceIp = instanceIp; this.instancePort = instancePort; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/DeregisterServiceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; /** * Naming deregister service trace event. * * @author yanda */ public class DeregisterServiceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = 7358195336881398548L; public DeregisterServiceTraceEvent(long eventTime, String serviceNamespace, String serviceGroup, String serviceName) { super("DEREGISTER_SERVICE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/HealthStateChangeTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import com.alibaba.nacos.common.trace.HealthCheckType; /** * Naming instance health state change trace event. * * @author yanda */ public class HealthStateChangeTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = 6966396191118694597L; private final String instanceIp; private final int instancePort; private final boolean isHealthy; private final HealthCheckType healthCheckType; private final String healthStateChangeReason; public String getInstanceIp() { return instanceIp; } public int getInstancePort() { return instancePort; } public String toInetAddr() { return instanceIp + ":" + instancePort; } public boolean isHealthy() { return isHealthy; } public HealthCheckType getHealthCheckType() { return healthCheckType; } public String getHealthStateChangeReason() { return healthStateChangeReason; } public HealthStateChangeTraceEvent(long eventTime, String serviceNamespace, String serviceGroup, String serviceName, String instanceIp, int instancePort, boolean isHealthy, String healthStateChangeReason) { super("HEALTH_STATE_CHANGE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); this.instanceIp = instanceIp; this.instancePort = instancePort; this.isHealthy = isHealthy; this.healthCheckType = getHealthCheckTypeFromReason(healthStateChangeReason); this.healthStateChangeReason = healthStateChangeReason; } public HealthCheckType getHealthCheckTypeFromReason(String reason) { if (reason.startsWith(HealthCheckType.HTTP_HEALTH_CHECK.getPrefix())) { return HealthCheckType.HTTP_HEALTH_CHECK; } else if (reason.startsWith(HealthCheckType.TCP_SUPER_SENSE.getPrefix())) { return HealthCheckType.TCP_SUPER_SENSE; } else if (reason.startsWith(HealthCheckType.MYSQL_HEALTH_CHECK.getPrefix())) { return HealthCheckType.MYSQL_HEALTH_CHECK; } return HealthCheckType.CLIENT_BEAT; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/NamingTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import com.alibaba.nacos.common.trace.event.TraceEvent; /** * Naming trace event. * * @author yanda */ public class NamingTraceEvent extends TraceEvent { private static final long serialVersionUID = 2923077640400851816L; public NamingTraceEvent(String eventType, long eventTime, String serviceNamespace, String serviceGroup, String name) { super(eventType, eventTime, serviceNamespace, serviceGroup, name); } @Override public boolean isPluginEvent() { return true; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/PushServiceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; /** * Naming push service trace event. * * @author yanda */ public class PushServiceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = 787915741281241877L; private final String clientIp; private final int instanceSize; private final long pushCostTimeForNetWork; private final long pushCostTimeForAll; private final long serviceLevelAgreementTime; public String getClientIp() { return clientIp; } public int getInstanceSize() { return instanceSize; } public long getPushCostTimeForNetWork() { return pushCostTimeForNetWork; } public long getPushCostTimeForAll() { return pushCostTimeForAll; } public long getServiceLevelAgreementTime() { return serviceLevelAgreementTime; } public PushServiceTraceEvent(long eventTime, long pushCostTimeForNetWork, long pushCostTimeForAll, long serviceLevelAgreementTime, String clientIp, String serviceNamespace, String serviceGroup, String serviceName, int instanceSize) { super("PUSH_SERVICE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); this.clientIp = clientIp; this.instanceSize = instanceSize; this.pushCostTimeForAll = pushCostTimeForAll; this.pushCostTimeForNetWork = pushCostTimeForNetWork; this.serviceLevelAgreementTime = serviceLevelAgreementTime; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/RegisterInstanceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; /** * Naming register instance trace event. * * @author yanda */ public class RegisterInstanceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = -8283438151444483864L; private final String clientIp; private final boolean rpc; private final String instanceIp; private final int instancePort; public String getClientIp() { return clientIp; } public boolean isRpc() { return rpc; } public String getInstanceIp() { return instanceIp; } public int getInstancePort() { return instancePort; } public String toInetAddr() { return instanceIp + ":" + instancePort; } public RegisterInstanceTraceEvent(long eventTime, String clientIp, boolean rpc, String serviceNamespace, String serviceGroup, String serviceName, String instanceIp, int instancePort) { this("REGISTER_INSTANCE_TRACE_EVENT", eventTime, clientIp, rpc, serviceNamespace, serviceGroup, serviceName, instanceIp, instancePort); } public RegisterInstanceTraceEvent(String eventType, long eventTime, String clientIp, boolean rpc, String serviceNamespace, String serviceGroup, String serviceName, String instanceIp, int instancePort) { super(eventType, eventTime, serviceNamespace, serviceGroup, serviceName); this.clientIp = clientIp; this.rpc = rpc; this.instanceIp = instanceIp; this.instancePort = instancePort; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/RegisterServiceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; /** * Naming deregister service trace event. * * @author yanda */ public class RegisterServiceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = -8568231862586636388L; public RegisterServiceTraceEvent(long eventTime, String serviceNamespace, String serviceGroup, String serviceName) { super("REGISTER_SERVICE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/SubscribeServiceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; /** * Naming subscribe service trace event. * * @author yanda */ public class SubscribeServiceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = -8856834879168816801L; private final String clientIp; public String getClientIp() { return clientIp; } public SubscribeServiceTraceEvent(long eventTime, String clientIp, String serviceNamespace, String serviceGroup, String serviceName) { super("SUBSCRIBE_SERVICE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); this.clientIp = clientIp; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/UnsubscribeServiceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; /** * Naming unsubscribe service trace event. * * @author yanda */ public class UnsubscribeServiceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = -7461808613817897106L; private final String clientIp; public String getClientIp() { return clientIp; } public UnsubscribeServiceTraceEvent(long eventTime, String clientIp, String serviceNamespace, String serviceGroup, String serviceName) { super("UNSUBSCRIBE_SERVICE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); this.clientIp = clientIp; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/UpdateInstanceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import java.util.Map; /** * Naming update instance trace event. * * @author stone-98 * @date 2023/8/31 */ public class UpdateInstanceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = -6995370254824508523L; private final Map metadata; private final String clientIp; private final String instanceIp; private final int instancePort; public Map getMetadata() { return metadata; } public String getClientIp() { return clientIp; } public String getInstanceIp() { return instanceIp; } public int getInstancePort() { return instancePort; } public String toInetAddr() { return instanceIp + ":" + instancePort; } public UpdateInstanceTraceEvent(long eventTime, String clientIp, String serviceNamespace, String serviceGroup, String serviceName, String instanceIp, int instancePort, Map metadata) { super("UPDATE_INSTANCE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); this.clientIp = clientIp; this.instanceIp = instanceIp; this.instancePort = instancePort; this.metadata = metadata; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/event/naming/UpdateServiceTraceEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import java.util.Map; /** * Naming update service trace event. * * @author stone-98 * @date 2023/8/31 */ public class UpdateServiceTraceEvent extends NamingTraceEvent { private static final long serialVersionUID = -6792054530665003857L; private final Map metadata; public Map getMetadata() { return metadata; } public UpdateServiceTraceEvent(long eventTime, String serviceNamespace, String serviceGroup, String serviceName, Map metadata) { super("UPDATE_SERVICE_TRACE_EVENT", eventTime, serviceNamespace, serviceGroup, serviceName); this.metadata = metadata; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisher.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.publisher; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.ShardedEventPublisher; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import com.alibaba.nacos.common.utils.ThreadUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Map; import java.util.Set; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; /** * Event publisher for trace event. * * @author yanda */ public class TraceEventPublisher extends Thread implements ShardedEventPublisher { private static final String THREAD_NAME = "trace.publisher-"; private static final Logger LOGGER = LoggerFactory.getLogger("com.alibaba.nacos.common.trace.publisher"); private static final int DEFAULT_WAIT_TIME = 60; private final Map, Set>> subscribes = new ConcurrentHashMap<>(); private volatile boolean initialized = false; private volatile boolean shutdown = false; private int queueMaxSize = -1; private BlockingQueue queue; private String publisherName; @Override public void init(Class type, int bufferSize) { this.queueMaxSize = bufferSize; this.queue = new ArrayBlockingQueue<>(bufferSize); this.publisherName = type.getSimpleName(); super.setName(THREAD_NAME + this.publisherName); super.setDaemon(true); super.start(); initialized = true; } @Override public long currentEventSize() { return this.queue.size(); } @Override public void addSubscriber(Subscriber subscriber) { addSubscriber(subscriber, subscriber.subscribeType()); } @Override public void addSubscriber(Subscriber subscriber, Class subscribeType) { subscribes.computeIfAbsent(subscribeType, inputType -> new ConcurrentHashSet<>()).add(subscriber); } @Override public void removeSubscriber(Subscriber subscriber) { removeSubscriber(subscriber, subscriber.subscribeType()); } @Override public void removeSubscriber(Subscriber subscriber, Class subscribeType) { subscribes.computeIfPresent(subscribeType, (inputType, subscribers) -> { subscribers.remove(subscriber); return subscribers.isEmpty() ? null : subscribers; }); } @Override public boolean publish(Event event) { checkIsStart(); boolean success = this.queue.offer(event); if (!success) { LOGGER.warn("Trace Event Publish failed, event : {}, publish queue size : {}", event, currentEventSize()); } return true; } @Override public void notifySubscriber(Subscriber subscriber, Event event) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("[NotifyCenter] the {} will received by {}", event, subscriber); } final Runnable job = () -> subscriber.onEvent(event); final Executor executor = subscriber.executor(); if (executor != null) { executor.execute(job); } else { try { job.run(); } catch (Throwable e) { LOGGER.error("Event callback exception: ", e); } } } @Override public void shutdown() throws NacosException { this.shutdown = true; this.queue.clear(); } @Override public void run() { try { waitSubscriberForInit(); handleEvents(); } catch (Exception e) { LOGGER.error("Trace Event Publisher {}, stop to handle event due to unexpected exception: ", this.publisherName, e); } } private void waitSubscriberForInit() { // To ensure that messages are not lost, enable EventHandler when // waiting for the first Subscriber to register for (int waitTimes = DEFAULT_WAIT_TIME; waitTimes > 0; waitTimes--) { if (shutdown || !subscribes.isEmpty()) { break; } ThreadUtils.sleep(1000L); } } private void handleEvents() { while (!shutdown) { try { final Event event = queue.take(); handleEvent(event); } catch (InterruptedException e) { LOGGER.warn("Trace Event Publisher {} take event from queue failed:", this.publisherName, e); // set the interrupted flag Thread.currentThread().interrupt(); } } } private void handleEvent(Event event) { Class eventType = event.getClass(); Set> subscribers = subscribes.get(eventType); if (null == subscribers) { if (LOGGER.isDebugEnabled()) { LOGGER.debug("[NotifyCenter] No subscribers for slow event {}", eventType.getName()); } return; } for (Subscriber subscriber : subscribers) { notifySubscriber(subscriber, event); } } void checkIsStart() { if (!initialized) { throw new IllegalStateException("Publisher does not start"); } } public String getStatus() { return String.format("Publisher %-30s: shutdown=%5s, queue=%7d/%-7d", publisherName, shutdown, currentEventSize(), queueMaxSize); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisherFactory.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.publisher; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.EventPublisher; import com.alibaba.nacos.common.notify.EventPublisherFactory; import com.alibaba.nacos.common.trace.event.TraceEvent; import com.alibaba.nacos.common.utils.ConcurrentHashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * event publisher factory for trace event. * * @author yanda */ public class TraceEventPublisherFactory implements EventPublisherFactory { private static final TraceEventPublisherFactory INSTANCE = new TraceEventPublisherFactory(); private final Map, TraceEventPublisher> publisher; private final Set> publisherEvents; private TraceEventPublisherFactory() { publisher = new ConcurrentHashMap<>(); publisherEvents = new ConcurrentHashSet<>(); } public static TraceEventPublisherFactory getInstance() { return INSTANCE; } @Override public EventPublisher apply(final Class eventType, final Integer maxQueueSize) { Class cachedEventType = TraceEvent.class; for (Class publisherEvent : publisherEvents) { if (publisherEvent.isAssignableFrom(eventType)) { cachedEventType = publisherEvent; break; } } return publisher.computeIfAbsent(cachedEventType, eventClass -> { TraceEventPublisher result = new TraceEventPublisher(); result.init(eventClass, maxQueueSize); return result; }); } public String getAllPublisherStatues() { StringBuilder result = new StringBuilder("Trace event publisher statues:\n"); for (TraceEventPublisher each : publisher.values()) { result.append('\t').append(each.getStatus()).append('\n'); } return result.toString(); } public void addPublisherEvent(Class event) { this.publisherEvents.add(event); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ArrayUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.Arrays; /** * Array utils. * * @author zzq */ public class ArrayUtils { private ArrayUtils() { } /** *

    Checks if an array of Objects is empty or {@code null}.

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

    Checks if the object is in the given array.

    * *

    The method returns {@code false} if a {@code null} array is passed in.

    * * @param array the array to search through * @param objectToFind the object to find * @return {@code true} if the array contains the object */ public static boolean contains(final Object[] array, final Object objectToFind) { if (array == null) { return false; } return Arrays.asList(array).contains(objectToFind); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ByteUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.nio.charset.StandardCharsets; /** * ByteUtils. * * @author liaochuntao */ public final class ByteUtils { private ByteUtils() { } public static final byte[] EMPTY = new byte[0]; /** * String to byte array. * * @param input input string * @return byte array of string */ public static byte[] toBytes(String input) { if (input == null) { return EMPTY; } return input.getBytes(StandardCharsets.UTF_8); } /** * Object to byte array. * * @param obj input obj * @return byte array of object */ public static byte[] toBytes(Object obj) { if (obj == null) { return EMPTY; } return toBytes(String.valueOf(obj)); } /** * Byte array to string. * * @param bytes byte array * @return string */ public static String toString(byte[] bytes) { if (bytes == null) { return StringUtils.EMPTY; } return new String(bytes, StandardCharsets.UTF_8); } public static boolean isEmpty(byte[] data) { return data == null || data.length == 0; } public static boolean isNotEmpty(byte[] data) { return !isEmpty(data); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ClassUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import java.io.Closeable; import java.io.Externalizable; import java.io.Serializable; import java.lang.reflect.Array; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import static com.alibaba.nacos.api.exception.NacosException.SERVER_ERROR; /** * Utils for Class. * * @author liaochuntao */ public final class ClassUtils { private ClassUtils() { } public static final String ARRAY_SUFFIX = "[]"; private static final String INTERNAL_ARRAY_PREFIX = "["; private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L"; private static final char PACKAGE_SEPARATOR = '.'; private static final char PATH_SEPARATOR = '/'; private static final char NESTED_CLASS_SEPARATOR = '$'; private static final String SEMICOLON_SEPARATOR = ";"; /** * 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 IdentityHashMap<>(9); /** * Map with primitive type as key and corresponding wrapper type as value, for example: int.class -> Integer.class. */ private static final Map, Class> PRIMITIVE_TYPE_TO_WRAPPER_MAP = new IdentityHashMap<>(9); /** * 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); private static final int PRIMITIVE_TYPE_NAME_MAP_LENGTH = 7; /** * Map with common Java language class name as key and corresponding Class as value. Primarily for efficient * deserialization of remote invocations. */ private static final Map> COMMON_CLASS_CACHE = new HashMap<>(64); 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); // Map entry iteration is less expensive to initialize than forEach with lambdas for (Map.Entry, Class> entry : PRIMITIVE_WRAPPER_TYPE_MAP.entrySet()) { PRIMITIVE_TYPE_TO_WRAPPER_MAP.put(entry.getValue(), entry.getKey()); registerCommonClasses(entry.getKey()); } Set> primitiveTypes = new HashSet<>(32); primitiveTypes.addAll(PRIMITIVE_WRAPPER_TYPE_MAP.values()); Collections.addAll(primitiveTypes, boolean[].class, byte[].class, char[].class, double[].class, float[].class, int[].class, long[].class, short[].class); for (Class primitiveType : primitiveTypes) { PRIMITIVE_TYPE_NAME_MAP.put(primitiveType.getName(), primitiveType); } registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class, Float[].class, Integer[].class, Long[].class, Short[].class); registerCommonClasses(Number.class, Number[].class, String.class, String[].class, Class.class, Class[].class, Object.class, Object[].class); registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class, Error.class, StackTraceElement.class, StackTraceElement[].class); registerCommonClasses(Enum.class, Iterable.class, Iterator.class, Enumeration.class, Collection.class, List.class, Set.class, Map.class, Map.Entry.class, Optional.class); Class[] javaLanguageInterfaceArray = {Serializable.class, Externalizable.class, Closeable.class, AutoCloseable.class, Cloneable.class, Comparable.class}; registerCommonClasses(javaLanguageInterfaceArray); } /** * Register the given common classes with the ClassUtils cache. */ private static void registerCommonClasses(Class... commonClasses) { for (Class clazz : commonClasses) { COMMON_CLASS_CACHE.put(clazz.getName(), clazz); } } /** * Finds and returns class by className. * * @param className String value for className. * @return class Instances of the class represent classes and interfaces. */ public static Class findClassByName(String className) { try { return Class.forName(className); } catch (Exception e) { throw new NacosRuntimeException(SERVER_ERROR, "this class name not found"); } } /** * Determines if the class or interface represented by this object is either the same as, or is a superclass or * superinterface of, the class or interface represented by the specified parameter. * * @param clazz Instances of the class represent classes and interfaces. * @param cls Instances of the class represent classes and interfaces. * @return the value indicating whether objects of the type can be assigned to objects of this class. */ public static boolean isAssignableFrom(Class clazz, Class cls) { Objects.requireNonNull(cls, "cls"); return clazz.isAssignableFrom(cls); } /** * Gets and returns the class name. * * @param cls Instances of the class represent classes and interfaces. * @return the name of the class or interface represented by this object. */ public static String getName(Class cls) { Objects.requireNonNull(cls, "cls"); return cls.getName(); } /** * Gets and returns className. * * @param obj Object instance. * @return className. */ public static String getName(Object obj) { Objects.requireNonNull(obj, "obj"); return obj.getClass().getName(); } /** * Gets and returns the canonical name of the underlying class. * * @param cls Instances of the class represent classes and interfaces. * @return The canonical name of the underlying class. */ public static String getCanonicalName(Class cls) { Objects.requireNonNull(cls, "cls"); return cls.getCanonicalName(); } /** * Gets and returns the canonical name of the underlying class. * * @param obj Object instance. * @return The canonical name of the underlying class. */ public static String getCanonicalName(Object obj) { Objects.requireNonNull(obj, "obj"); return obj.getClass().getCanonicalName(); } /** * Gets and returns the simple name of the underlying class. * * @param cls Instances of the class represent classes and interfaces. * @return the simple name of the underlying class. */ public static String getSimpleName(Class cls) { Objects.requireNonNull(cls, "cls"); return cls.getSimpleName(); } /** * Gets and returns the simple name of the underlying class as given in the source code. * * @param obj Object instance. * @return the simple name of the underlying class. */ public static String getSimpleName(Object obj) { Objects.requireNonNull(obj, "obj"); return obj.getClass().getSimpleName(); } /** * Replacement for {@code Class.forName()} that also returns Class instances for primitives (e.g. "int") and array * class names (e.g. "String[]"). Furthermore, it is also capable of resolving nested class names in Java source * style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State"). * * @param name the name of the Class * @param classLoader the class loader to use (may be {@code null}, which indicates the default class loader) * @return a 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 { Objects.requireNonNull(name, "Name must not be null"); Class clazz = resolvePrimitiveClassName(name); if (clazz == null) { clazz = COMMON_CLASS_CACHE.get(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 if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(SEMICOLON_SEPARATOR)) { String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); Class elementClass = forName(elementName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } // "[[I" or "[[Ljava.lang.String;" style arrays if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); Class elementClass = forName(elementName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } ClassLoader clToUse = classLoader; if (clToUse == null) { clToUse = getDefaultClassLoader(); } try { return Class.forName(name, false, clToUse); } catch (ClassNotFoundException ex) { int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR); if (lastDotIndex != -1) { String nestedClassName = name.substring(0, lastDotIndex) + NESTED_CLASS_SEPARATOR + name.substring(lastDotIndex + 1); try { return Class.forName(nestedClassName, false, clToUse); } catch (ClassNotFoundException e) { // Swallow - let original exception get through } } throw ex; } } /** * 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(String, ClassLoader)}. * * @param name the name of the potentially primitive class * @return the primitive class, or {@code 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() <= PRIMITIVE_TYPE_NAME_MAP_LENGTH) { // Could be a primitive - likely. result = PRIMITIVE_TYPE_NAME_MAP.get(name); } return result; } /** * 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 clearly prefer a non-null ClassLoader reference: for example, for class path resource * loading (but not necessarily for {@code Class.forName}, which accepts a {@code null} ClassLoader reference as * well). * * @return the default ClassLoader (only {@code null} if even the system ClassLoader isn't accessible) * @see Thread#getContextClassLoader() * @see ClassLoader#getSystemClassLoader() */ public static ClassLoader getDefaultClassLoader() { ClassLoader cl = null; try { cl = Thread.currentThread().getContextClassLoader(); } catch (Throwable ex) { // Cannot access thread context ClassLoader - falling back... } if (cl == null) { // No thread context class loader -> use class loader of this class. cl = ClassUtils.class.getClassLoader(); if (cl == null) { // getClassLoader() returning null indicates the bootstrap ClassLoader try { cl = ClassLoader.getSystemClassLoader(); } catch (Throwable ex) { // Cannot access system ClassLoader - oh well, maybe the caller can live with null... } } } return cl; } /** * Given an input class object, return a string which consists of the class's package name as a pathname, i.e., all * dots ('.') are replaced by slashes ('/'). Neither a leading nor trailing slash is added. The result could be * concatenated with a slash and the name of a resource and fed directly to {@code ClassLoader.getResource()}. For * it to be fed to {@code Class.getResource} instead, a leading slash would also have to be prepended to the * returned value. * * @param clazz the input class. A {@code null} value or the default (empty) package will result in an empty string * ("") being returned. * @return a path which represents the package name * @see ClassLoader#getResource * @see Class#getResource */ public static String classPackageAsResourcePath(Class clazz) { if (clazz == null) { return ""; } String className = clazz.getName(); int packageEndIndex = className.lastIndexOf(PACKAGE_SEPARATOR); if (packageEndIndex == -1) { return ""; } String packageName = className.substring(0, packageEndIndex); return packageName.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR); } /** * Convert a "."-based fully qualified class name to a "/"-based resource path. * * @param className the fully qualified class name * @return the corresponding resource path, pointing to the class */ public static String convertClassNameToResourcePath(String className) { Objects.requireNonNull(className, "Class name must not be null"); return className.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR); } /** * Convert a "."-based fully qualified class name to a "/"-based resource path. * * @param className the fully qualified class name * @return the corresponding resource path, pointing to the class */ public static String resourcePathToConvertClassName(String className) { Objects.requireNonNull(className, "Class name must not be null"); return className.replace(PATH_SEPARATOR, PACKAGE_SEPARATOR); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/CollectionUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /** * Copy from {@link org.apache.commons.collections}. * * @author liaochuntao */ public final class CollectionUtils { /** * Constant to avoid repeated object creation. */ private static final Integer INTEGER_ONE = 1; private CollectionUtils() { } /** * Returns the index-th value in object, throwing * IndexOutOfBoundsException if there is no such element or * IllegalArgumentException if object is not an * instance of one of the supported types. * *

    The supported types, and associated semantics are: *

      *
    • Map -- the value returned is the Map.Entry in position * index in the map's entrySet iterator, * if there is such an entry.
    • *
    • List -- this method is equivalent to the list's get method.
    • *
    • Array -- the index-th array entry is returned, * if there is such an entry; otherwise an IndexOutOfBoundsException * is thrown.
    • *
    • Collection -- the value returned is the index-th object * returned by the collection's default iterator, if there is such an element.
    • *
    • Iterator or Enumeration -- the value returned is the * index-th object in the Iterator/Enumeration, if there * is such an element. The Iterator/Enumeration is advanced to * index (or to the end, if index exceeds the * number of entries) as a side effect of this method.
    • *
    * * @param object the object to get a value from * @param index the index to get * @return the object at the specified index * @throws IndexOutOfBoundsException if the index is invalid * @throws IllegalArgumentException if the object type is invalid */ public static Object get(Object object, int index) { if (index < 0) { throw new IndexOutOfBoundsException("Index cannot be negative: " + index); } if (object instanceof Map) { Map map = (Map) object; Iterator iterator = map.entrySet().iterator(); return get(iterator, index); } else if (object instanceof List) { return ((List) object).get(index); } else if (object instanceof Object[]) { return ((Object[]) object)[index]; } else if (object instanceof Iterator) { Iterator it = (Iterator) object; while (it.hasNext()) { index--; if (index == -1) { return it.next(); } else { it.next(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + index); } else if (object instanceof Collection) { Iterator iterator = ((Collection) object).iterator(); return get(iterator, index); } else if (object instanceof Enumeration) { Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { index--; if (index == -1) { return it.nextElement(); } else { it.nextElement(); } } throw new IndexOutOfBoundsException("Entry does not exist: " + index); } else if (object == null) { throw new IllegalArgumentException("Unsupported object type: null"); } else { try { return Array.get(object, index); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Gets the size of the collection/iterator specified. * *

    This method can handles objects as follows *

      *
    • Collection - the collection size *
    • Map - the map size *
    • Array - the array size *
    • Iterator - the number of elements remaining in the iterator *
    • Enumeration - the number of elements remaining in the enumeration *
    * * @param object the object to get the size of * @return the size of the specified collection * @throws IllegalArgumentException thrown if object is not recognised or null * @since Commons Collections 3.1 */ public static int size(Object object) { int total = 0; if (object instanceof Map) { total = ((Map) object).size(); } else if (object instanceof Collection) { total = ((Collection) object).size(); } else if (object instanceof Object[]) { total = ((Object[]) object).length; } else if (object instanceof Iterator) { Iterator it = (Iterator) object; while (it.hasNext()) { total++; it.next(); } } else if (object instanceof Enumeration) { Enumeration it = (Enumeration) object; while (it.hasMoreElements()) { total++; it.nextElement(); } } else if (object == null) { throw new IllegalArgumentException("Unsupported object type: null"); } else { try { total = Array.getLength(object); } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } return total; } /** * Judge whether object is empty. * * @param object object * @return true if object is empty, otherwise false * @throws IllegalArgumentException if object has no length or size */ public static boolean sizeIsEmpty(Object object) { if (object instanceof Collection) { return ((Collection) object).isEmpty(); } else if (object instanceof Map) { return ((Map) object).isEmpty(); } else if (object instanceof Object[]) { return ((Object[]) object).length == 0; } else if (object instanceof Iterator) { return ((Iterator) object).hasNext() == false; } else if (object instanceof Enumeration) { return ((Enumeration) object).hasMoreElements() == false; } else if (object == null) { throw new IllegalArgumentException("Unsupported object type: null"); } else { try { return Array.getLength(object) == 0; } catch (IllegalArgumentException ex) { throw new IllegalArgumentException("Unsupported object type: " + object.getClass().getName()); } } } /** * Whether contain item in collection. * * @param coll collection * @param target target value * @param General Type * @return true if contain, otherwise false */ public static boolean contains(Collection coll, T target) { if (isEmpty(coll)) { return false; } return coll.contains(target); } /** * Null-safe check if the specified collection is empty. * *

    Null returns true. * * @param coll the collection to check, may be null * @return true if empty or null * @since Commons Collections 3.2 */ public static boolean isEmpty(Collection coll) { return (coll == null || coll.isEmpty()); } /** * Null-safe check if the specified collection is not empty. * *

    Null returns false. * * @param coll the collection to check, may be null * @return true if non-null and non-empty * @since Commons Collections 3.2 */ public static boolean isNotEmpty(Collection coll) { return !CollectionUtils.isEmpty(coll); } /** * Returns the value to which the specified index , or {@code defaultValue} if this collection contains no value for * the index. * * @param obj the object to get a value from * @param index the index to get * @param defaultValue default value * @param General Type * @return the value to which the specified index , or {@code defaultValue} if this collection contains no value for * the index. */ public static T getOrDefault(Object obj, int index, T defaultValue) { try { return (T) get(obj, index); } catch (IndexOutOfBoundsException e) { return defaultValue; } } /** * return an arraylist containing all input parameters. * * @param elements element array * @return arraylist containing all input parameters * @author zzq */ public static List list(T... elements) { if (elements == null) { throw new IllegalArgumentException("Expected an array of elements (or empty array) but received a null."); } ArrayList list = new ArrayList<>(elements.length); Collections.addAll(list, elements); return list; } /** * Return a set containing all input parameters. * * @param elements elements element array * @return set containing all input parameters */ public static Set set(T... elements) { if (elements == null) { throw new IllegalArgumentException("Expected an array of elements (or empty array) but received a null."); } else { return new LinkedHashSet<>(Arrays.asList(elements)); } } /** * return the first element, if the iterator contains multiple elements, will throw {@code * IllegalArgumentException}. * * @throws NoSuchElementException if the iterator is empty * @throws IllegalArgumentException if the iterator contains multiple elements. The state of the iterator is * unspecified. */ public static T getOnlyElement(Iterable iterable) { if (iterable == null) { throw new IllegalArgumentException("iterable cannot be null."); } Iterator iterator = iterable.iterator(); T first = iterator.next(); if (!iterator.hasNext()) { return first; } throw new IllegalArgumentException(buildExceptionMessage(iterator, first)); } private static String buildExceptionMessage(Iterator iterator, T first) { StringBuilder msg = new StringBuilder(); msg.append("expected one element but was: <"); msg.append(first); for (int i = 0; i < 4 && iterator.hasNext(); i++) { msg.append(", "); msg.append(iterator.next()); } if (iterator.hasNext()) { msg.append(", ..."); } msg.append('>'); return msg.toString(); } /** * 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 isMapEmpty(Map map) { return (map == null || map.isEmpty()); } /** * Returns a {@link Map} mapping each unique element in the given {@link Collection} to an {@link Integer} * representing the number of occurrences of that element in the {@link Collection}. * *

    Only those elements present in the collection will appear as keys in the map. * * @param coll the collection to get the cardinality map for, must not be null * @return the populated cardinality map */ public static Map getCardinalityMap(final Collection coll) { Map count = new HashMap(coll.size()); for (Iterator it = coll.iterator(); it.hasNext(); ) { Object obj = it.next(); Integer c = (Integer) (count.get(obj)); if (c == null) { count.put(obj, INTEGER_ONE); } else { count.put(obj, c + 1); } } return count; } /** * Returns true iff the given {@link Collection}s contain exactly the same elements with exactly the same * cardinalities. * *

    That is, iff the cardinality of e in a is equal to the cardinality of e in b, for * each element e in a or b. * * @param a the first collection, must not be null * @param b the second collection, must not be null * @return true iff the collections contain the same elements with the same cardinalities. */ public static boolean isEqualCollection(final Collection a, final Collection b) { if (a.size() != b.size()) { return false; } else { Map mapa = getCardinalityMap(a); Map mapb = getCardinalityMap(b); if (mapa.size() != mapb.size()) { return false; } else { Iterator it = mapa.keySet().iterator(); while (it.hasNext()) { Object obj = it.next(); if (getFreq(obj, mapa) != getFreq(obj, mapb)) { return false; } } return true; } } } private static int getFreq(final Object obj, final Map freqMap) { Integer count = (Integer) freqMap.get(obj); if (count != null) { return count.intValue(); } return 0; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ConcurrentHashSet.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.AbstractSet; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; /** * Concurrent Hash Set implement by {@link ConcurrentHashMap}. * * @author liaochuntao */ public class ConcurrentHashSet extends AbstractSet { private ConcurrentHashMap map; public ConcurrentHashSet() { super(); map = new ConcurrentHashMap<>(); } @Override public int size() { return map.size(); } @Override public boolean contains(Object o) { return map.containsKey(o); } @Override public Iterator iterator() { return map.keySet().iterator(); } @Override public boolean add(E o) { return map.putIfAbsent(o, Boolean.TRUE) == null; } @Override public boolean remove(Object o) { return map.remove(o) != null; } @Override public void clear() { map.clear(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ConnLabelsUtils.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Properties; import java.util.stream.Collectors; /** * ConnLabelsUtils. * * @author rong */ public class ConnLabelsUtils { private static final Logger LOGGER = LoggerFactory.getLogger(ConnLabelsUtils.class); public static final String LABEL_EQUALS_OPERATOR = "="; public static final String LABEL_SPLIT_OPERATOR = ","; public static final int TAG_V2_LABEL_KEY_VALUE_SPLIT_LENGTH = 2; /** * parse property value to map. * * @param properties Properties * @param propertyName which key to get * @return (String)key-(String)value map * @date 2024/1/29 * @description will get a key-value map from properties, JVM OPTIONS, ENV by order of properties > JVM OPTIONS * > ENV which will use the next level value when the current level value isn't setup. *

    eg: if the value of "nacos.app.conn.labels"(properties' key) is "k1=v1,k2=v2"(properties' value), the result * will be * a Map with value{k1=v1,k2=v2}.

    */ public static Map parsePropertyValue2Map(Properties properties, String propertyName) { String rawLabels = properties.getProperty(propertyName, System.getProperty(propertyName, System.getenv(propertyName))); if (StringUtils.isBlank(rawLabels)) { LOGGER.info("no value found for property key: {}", propertyName); return new HashMap<>(2); } return parseRawLabels(rawLabels); } /** * parse raw json labels into a key-value map. * * @param rawLabels rawLabels to parse * @return map parsed from rawLabels * @date 2024/1/29 * @description */ public static Map parseRawLabels(String rawLabels) { if (StringUtils.isBlank(rawLabels)) { return new HashMap<>(2); } HashMap resultMap = new HashMap<>(2); try { Arrays.stream(rawLabels.split(LABEL_SPLIT_OPERATOR)).filter(Objects::nonNull).map(String::trim) .filter(StringUtils::isNotBlank).forEach(label -> { String[] kv = label.split(LABEL_EQUALS_OPERATOR); if (kv.length == TAG_V2_LABEL_KEY_VALUE_SPLIT_LENGTH) { resultMap.put(kv[0].trim(), kv[1].trim()); } else { LOGGER.error("unknown label format: {}", label); } }); } catch (Exception e) { LOGGER.error("unknown label format: {}", rawLabels); } return resultMap; } /** * merge two map into one by using the former value when key is duplicated. * * @param preferredMap preferredMap * @param backwardMap backwardMap * @date 2024/1/29 * @description merge two map into one preferring using the first one when key is duplicated */ public static Map mergeMapByOrder(Map preferredMap, Map backwardMap) { if (preferredMap == null || preferredMap.isEmpty()) { return new HashMap(8) { { putAll(backwardMap); } }; } if (backwardMap == null || backwardMap.isEmpty()) { return new HashMap(8) { { putAll(preferredMap); } }; } HashMap resultMap = new HashMap(8) { { putAll(preferredMap); } }; backwardMap.forEach((key, value) -> { if (!resultMap.containsKey(key)) { resultMap.put(key, value); } }); return resultMap; } /** * add prefix for each key in map. * * @param map map to add prefix * @param prefix prefix * @date 2024/1/29 * @description add prefix for each key in map */ public static Map addPrefixForEachKey(Map map, String prefix) { if (map == null || map.isEmpty()) { return map; } return map.entrySet().stream().filter(Objects::nonNull).filter(elem -> !elem.getKey().trim().isEmpty()) .collect(Collectors.toMap(elem -> prefix + elem.getKey(), Map.Entry::getValue)); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ConvertUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.Set; /** * Value Convert Utils. * * @author liaochuntao */ public final class ConvertUtils { private ConvertUtils() { } private static final String NULL_STR = "null"; public static final Set TRUE_SET = CollectionUtils.set("y", "yes", "on", "true", "t"); public static final Set FALSE_SET = CollectionUtils.set("n", "no", "off", "false", "f"); /** * Convert String value to int value if parameter value is legal. And it automatically defaults to 0 if parameter * value is null or blank str. * * @param val String value which need to be converted to int value. * @return Converted int value and its default value is 0. */ public static int toInt(String val) { return toInt(val, 0); } /** * Convert String value to int value if parameter value is legal. And return default value if parameter value is * null or blank str. * * @param val value * @param defaultValue default value * @return int value if input value is legal, otherwise default value */ public static int toInt(String val, int defaultValue) { if (StringUtils.equalsIgnoreCase(val, NULL_STR)) { return defaultValue; } if (StringUtils.isBlank(val)) { return defaultValue; } try { return Integer.parseInt(val); } catch (NumberFormatException exception) { return defaultValue; } } /** * Convert Object value to long value if parameter value is legal. * And it automatically defaults to 0 if parameter value is null or other object. * * @param val object value * @return Converted long value and its default value is 0. */ public static long toLong(Object val) { if (val instanceof Long) { return (Long) val; } return toLong(val.toString()); } /** * Convert String value to long value if parameter value is legal. And it automatically defaults to 0 if parameter * value is null or blank str. * * @param val String value which need to be converted to int value. * @return Converted long value and its default value is 0. */ public static long toLong(String val) { return toLong(val, 0L); } /** * Convert String value to long value if parameter value is legal. And return default value if parameter value is * null or blank str. * * @param val value * @param defaultValue default value * @return long value if input value is legal, otherwise default value */ public static long toLong(String val, long defaultValue) { if (StringUtils.isBlank(val)) { return defaultValue; } try { return Long.parseLong(val); } catch (NumberFormatException exception) { return defaultValue; } } /** * Convert String value to boolean value if parameter value is legal. And return default value if parameter value is * null or blank str. * * @param val value * @param defaultValue default value * @return boolean value if input value is legal, otherwise default value */ public static boolean toBoolean(String val, boolean defaultValue) { if (StringUtils.isBlank(val)) { return defaultValue; } return Boolean.parseBoolean(val); } // The following utility functions are extracted from org.apache.commons.lang3 // start /** *

    Converts a String to a boolean (optimised for performance).

    * *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} * (case insensitive) will return {@code true}. Otherwise, {@code false} is returned.

    * *

    This method performs 4 times faster (JDK1.4) than * {@code Boolean.valueOf(String)}. However, this method accepts 'on' and 'yes', 't', 'y' as true values. * *

         *   BooleanUtils.toBoolean(null)    = false
         *   BooleanUtils.toBoolean("true")  = true
         *   BooleanUtils.toBoolean("TRUE")  = true
         *   BooleanUtils.toBoolean("tRUe")  = true
         *   BooleanUtils.toBoolean("on")    = true
         *   BooleanUtils.toBoolean("yes")   = true
         *   BooleanUtils.toBoolean("false") = false
         *   BooleanUtils.toBoolean("x gti") = false
         *   BooleanUtils.toBooleanObject("y") = true
         *   BooleanUtils.toBooleanObject("n") = false
         *   BooleanUtils.toBooleanObject("t") = true
         *   BooleanUtils.toBooleanObject("f") = false
         * 
    * * @param str the String to check * @return the boolean value of the string, {@code false} if no match or the String is null */ public static boolean toBoolean(final String str) { return Boolean.TRUE.equals(toBooleanObject(str)); } /** *

    Converts a String to a Boolean.

    * *

    {@code 'true'}, {@code 'on'}, {@code 'y'}, {@code 't'} or {@code 'yes'} * (case insensitive) will return {@code true}. {@code 'false'}, {@code 'off'}, {@code 'n'}, {@code 'f'} or {@code * 'no'} (case insensitive) will return {@code false}. Otherwise, {@code null} is returned.

    * *

    NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

    * *
         *   // N.B. case is not significant
         *   BooleanUtils.toBooleanObject(null)    = null
         *   BooleanUtils.toBooleanObject("true")  = Boolean.TRUE
         *   BooleanUtils.toBooleanObject("T")     = Boolean.TRUE // i.e. T[RUE]
         *   BooleanUtils.toBooleanObject("false") = Boolean.FALSE
         *   BooleanUtils.toBooleanObject("f")     = Boolean.FALSE // i.e. f[alse]
         *   BooleanUtils.toBooleanObject("No")    = Boolean.FALSE
         *   BooleanUtils.toBooleanObject("n")     = Boolean.FALSE // i.e. n[o]
         *   BooleanUtils.toBooleanObject("on")    = Boolean.TRUE
         *   BooleanUtils.toBooleanObject("ON")    = Boolean.TRUE
         *   BooleanUtils.toBooleanObject("off")   = Boolean.FALSE
         *   BooleanUtils.toBooleanObject("oFf")   = Boolean.FALSE
         *   BooleanUtils.toBooleanObject("yes")   = Boolean.TRUE
         *   BooleanUtils.toBooleanObject("Y")     = Boolean.TRUE // i.e. Y[ES]
         *   BooleanUtils.toBooleanObject("blue")  = null
         *   BooleanUtils.toBooleanObject("true ") = null // trailing space (too long)
         *   BooleanUtils.toBooleanObject("ono")   = null // does not match on or no
         * 
    * * @param str the String to check; upper and lower case are treated as the same * @return the Boolean value of the string, {@code null} if no match or {@code null} input */ @SuppressWarnings("all") public static Boolean toBooleanObject(String str) { String formatStr = (str == null ? StringUtils.EMPTY : str).toLowerCase(); if (TRUE_SET.contains(formatStr)) { return true; } else if (FALSE_SET.contains(formatStr)) { return false; } else { return null; } } // end } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/DateFormatUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.text.SimpleDateFormat; import java.util.Date; /** * Date and time formatting utilities. * @author zzq */ public class DateFormatUtils { private DateFormatUtils() { } public static final String YYYYMMDD = "yyyyMMdd"; public static final String YYMMDD = "yyMMdd"; public static final String HHMMSS = "HHmmss"; public static final String YYYYMM = "yyyyMM"; public static final String YYYYMMDDHHMMSS = "yyyyMMddHHmmss"; public static final String YYYY = "yyyy"; public static final String MM = "MM"; public static final String DD = "dd"; public static final String YYYYMMDDSLASH = "yyyy/MM/dd"; public static final String YYYYMMDDHHMMSSNOMARK = "yyyyMMddHHmmss"; /** * Formats a date/time into a specific pattern. * * @param date the date to format, not null * @param pattern the pattern to use to format the date, not null * @return the formatted date */ public static String format(final Date date, final String pattern) { if (date == null) { throw new NullPointerException("date must not be null"); } if (pattern == null) { throw new NullPointerException("pattern must not be null"); } SimpleDateFormat sdf = new SimpleDateFormat(pattern); return sdf.format(date); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ExceptionUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.nio.charset.StandardCharsets; import java.util.Objects; /** * Common methods for exception. * * @author nkorange * @since 1.2.0 */ public class ExceptionUtil { private ExceptionUtil() { } /** * Represents an empty exception, that is, no exception occurs, only a constant. */ public static final Exception NONE_EXCEPTION = new RuntimeException(""); public static String getAllExceptionMsg(Throwable e) { Throwable cause = e; StringBuilder strBuilder = new StringBuilder(); while (cause != null && !StringUtils.isEmpty(cause.getMessage())) { strBuilder.append("caused: ").append(cause.getMessage()).append(';'); cause = cause.getCause(); } return strBuilder.toString(); } public static Throwable getCause(final Throwable t) { final Throwable cause = t.getCause(); if (Objects.isNull(cause)) { return t; } return cause; } public static String getStackTrace(final Throwable t) { if (t == null) { return ""; } final ByteArrayOutputStream out = new ByteArrayOutputStream(); try { final PrintStream ps = new PrintStream(out, false, StandardCharsets.UTF_8.name()); t.printStackTrace(ps); ps.flush(); return new String(out.toByteArray(), StandardCharsets.UTF_8); } catch (UnsupportedEncodingException e) { // Should never happen since UTF-8 is always supported throw new IllegalStateException(e); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPattern.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; import static com.alibaba.nacos.api.common.Constants.ALL_PATTERN; import static com.alibaba.nacos.api.common.Constants.DEFAULT_NAMESPACE_ID; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_PATTERN_SPLITTER; /** * Utility class for matching group keys against a given pattern. * *

    This class provides methods to match group keys based on a pattern specified. It supports matching based on * dataId, group, and namespace components of the group key. * * @author stone-98 * @date 2024/3/14 */ public class FuzzyGroupKeyPattern { /** * Generates a fuzzy listen group key pattern based on the given dataId pattern, group, and optional tenant. pattern * result as: fixNamespace>>groupPattern>>dataIdPattern * * @param resourcePattern The pattern for matching dataIds or service names. * @param groupPattern The groupPattern associated with the groups. * @param fixNamespace (Optional) The tenant associated with the dataIds (can be null or empty). * @return A unique group key pattern for fuzzy listen. * @throws IllegalArgumentException If the dataId pattern or group is blank. */ public static String generatePattern(final String resourcePattern, final String groupPattern, String fixNamespace) { if (StringUtils.isBlank(resourcePattern)) { throw new IllegalArgumentException("Param 'resourcePattern' is illegal, resourcePattern is blank"); } if (StringUtils.isBlank(groupPattern)) { throw new IllegalArgumentException("Param 'groupPattern' is illegal, group is blank"); } if (StringUtils.isBlank(fixNamespace)) { fixNamespace = DEFAULT_NAMESPACE_ID; } StringBuilder sb = new StringBuilder(); sb.append(fixNamespace); sb.append(FUZZY_WATCH_PATTERN_SPLITTER); sb.append(groupPattern); sb.append(FUZZY_WATCH_PATTERN_SPLITTER); sb.append(resourcePattern); return sb.toString().intern(); } /** * Given a dataId, group, and a collection of completed group key patterns, returns the patterns that match. * * @param resourceName The dataId or service name to match. * @param group The group to match. * @param namespace The group to match. * @param groupKeyPatterns The collection of completed group key patterns to match against. * @return A set of patterns that match the dataId and group. */ public static Set filterMatchedPatterns(Collection groupKeyPatterns, String resourceName, String group, String namespace) { if (CollectionUtils.isEmpty(groupKeyPatterns)) { return new HashSet<>(1); } Set matchedPatternList = new HashSet<>(); for (String keyPattern : groupKeyPatterns) { if (matchPattern(keyPattern, resourceName, group, namespace)) { matchedPatternList.add(keyPattern); } } return matchedPatternList; } /** * check if the resource match the groupKeyPattern. * @param resourceName The dataId or service name to match. * @param group The group to match. * @param namespace The group to match. * @param groupKeyPattern The pattern to match. * @return matched or not. */ public static boolean matchPattern(String groupKeyPattern, String resourceName, String group, String namespace) { if (StringUtils.isBlank(namespace)) { namespace = DEFAULT_NAMESPACE_ID; } String[] splitPatterns = groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER); return splitPatterns[0].equals(namespace) && itemMatched(splitPatterns[1], group) && itemMatched( splitPatterns[2], resourceName); } public static String getNamespaceFromPattern(String groupKeyPattern) { return groupKeyPattern.split(FUZZY_WATCH_PATTERN_SPLITTER)[0]; } /** * check pattern matched the resource. * @param pattern pattern contain *. * @param resource resource to check. * @return */ private static boolean itemMatched(String pattern, String resource) { //accurate match without * if (!pattern.contains(ALL_PATTERN)) { return pattern.equals(resource); } //match for '*' pattern if (pattern.equals(ALL_PATTERN)) { return true; } //match for *{string}* if (pattern.startsWith(ALL_PATTERN) && pattern.endsWith(ALL_PATTERN)) { String pureString = pattern.replace(ALL_PATTERN, ""); return resource.contains(pureString); } //match for postfix match *{string} if (pattern.startsWith(ALL_PATTERN)) { String pureString = pattern.replace(ALL_PATTERN, ""); return resource.endsWith(pureString); } //match for prefix match {string}* if (pattern.endsWith(ALL_PATTERN)) { String pureString = pattern.replace(ALL_PATTERN, ""); return resource.startsWith(pureString); } return false; } /** * Calculates and merges the differences between the matched group keys and the client's existing group keys into a * list of ConfigState objects. * * @param basedGroupKeys The matched group keys set * @param followedGroupKeys The followed existing group keys set * @return a different list of GroupKeyState objects representing the states which the followed sets should be added * or removed GroupKeyState#exist true presents follow set should add,GroupKeyState#exist false presents follow set * should removed. */ public static List diffGroupKeys(Set basedGroupKeys, Set followedGroupKeys) { // Calculate the set of group keys to be added and removed Set addGroupKeys = new HashSet<>(); if (CollectionUtils.isNotEmpty(basedGroupKeys)) { addGroupKeys.addAll(basedGroupKeys); } if (CollectionUtils.isNotEmpty(followedGroupKeys)) { addGroupKeys.removeAll(followedGroupKeys); } Set removeGroupKeys = new HashSet<>(); if (CollectionUtils.isNotEmpty(followedGroupKeys)) { removeGroupKeys.addAll(followedGroupKeys); } if (CollectionUtils.isNotEmpty(basedGroupKeys)) { removeGroupKeys.removeAll(basedGroupKeys); } // Convert the group keys to be added and removed into corresponding ConfigState objects and merge them into a list return Stream.concat(addGroupKeys.stream().map(groupKey -> new GroupKeyState(groupKey, true)), removeGroupKeys.stream().map(groupKey -> new GroupKeyState(groupKey, false))) .collect(Collectors.toList()); } public static class GroupKeyState { String groupKey; boolean exist; /** * Constructs a new ConfigState instance with the given group key and existence flag. * * @param groupKey The group key associated with the configuration. * @param exist {@code true} if the configuration exists, {@code false} otherwise. */ public GroupKeyState(String groupKey, boolean exist) { this.groupKey = groupKey; this.exist = exist; } /** * Retrieves the group key associated with the configuration. * * @return The group key. */ public String getGroupKey() { return groupKey; } /** * Sets the group key associated with the configuration. * * @param groupKey The group key to set. */ public void setGroupKey(String groupKey) { this.groupKey = groupKey; } /** * Checks whether the configuration exists or not. * * @return {@code true} if the configuration exists, {@code false} otherwise. */ public boolean isExist() { return exist; } /** * Sets the existence flag of the configuration. * * @param exist {@code true} if the configuration exists, {@code false} otherwise. */ public void setExist(boolean exist) { this.exist = exist; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/HttpMethod.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; /** * Http method constants. * * @author nkorange * @since 0.8.0 */ public class HttpMethod { private HttpMethod() { } public static final String GET = "GET"; /** * this is only use in nacos, Custom request type, essentially a GET request, Mainly used for GET request parameters * are relatively large,can not be placed on the URL, so it needs to be placed in the body. */ public static final String GET_LARGE = "GET-LARGE"; public static final String HEAD = "HEAD"; public static final String POST = "POST"; public static final String PUT = "PUT"; public static final String PATCH = "PATCH"; public static final String DELETE = "DELETE"; /** * this is only use in nacos, Custom request type, essentially a DELETE request, Mainly used for DELETE request * parameters are relatively large, can not be placed on the URL, so it needs to be placed in the body. */ public static final String DELETE_LARGE = "DELETE_LARGE"; public static final String OPTIONS = "OPTIONS"; public static final String TRACE = "TRACE"; } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/InetAddressValidator.java ================================================ /** * Copyright 2018-2021 Dynatrace LLC 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 com.alibaba.nacos.common.utils; import java.util.regex.Pattern; /** * ipv4 ipv6 check util. * * @author Dynatrace LLC */ @SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class InetAddressValidator { private InetAddressValidator() { } private static final String PERCENT = "%"; private static final String DOUBLE_COLON = "::"; private static final String DOUBLE_COLON_FFFF = "::ffff:"; private static final String FE80 = "fe80:"; private static final int ZERO = 0; private static final int SEVEN = 7; private static final int FIVE = 5; private static final Pattern IPV4_PATTERN = Pattern .compile("^" + "(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)" + "(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}" + "$"); private static final Pattern IPV6_STD_PATTERN = Pattern .compile("^" + "(?:[0-9a-fA-F]{1,4}:){7}" + "[0-9a-fA-F]{1,4}" + "$"); private static final Pattern IPV6_HEX_COMPRESSED_PATTERN = Pattern .compile("^" + "(" + "(?:[0-9A-Fa-f]{1,4}" + "(?::[0-9A-Fa-f]{1,4})*)?" + ")" + "::" + "(" + "(?:[0-9A-Fa-f]{1,4}" + "(?::[0-9A-Fa-f]{1,4})*)?" + ")" + "$"); private static final Pattern IPV6_MIXED_COMPRESSED_REGEX = Pattern.compile( "^" + "(" + "(?:[0-9A-Fa-f]{1,4}" + "(?::[0-9A-Fa-f]{1,4})*)?" + ")" + "::" + "(" + "(?:[0-9A-Fa-f]{1,4}:" + "(?:[0-9A-Fa-f]{1,4}:)*)?" + ")" + "$"); private static final Pattern IPV6_MIXED_UNCOMPRESSED_REGEX = Pattern .compile("^" + "(?:[0-9a-fA-F]{1,4}:){6}" + "$"); /** * Check if input is a valid IPv4 address. The format is 'xxx.xxx.xxx.xxx'. Four blocks of integer * numbers ranging from 0 to 255 are required. Letters are not allowed. * * @param input ip-address to check * @return true if input is in correct IPv4 notation. */ public static boolean isIpv4Address(final String input) { return IPV4_PATTERN.matcher(input).matches(); } /** * Check if the given address is a valid IPv6 address in the standard format The format is * 'xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx'. Eight blocks of hexadecimal digits are required. * * @param input ip-address to check * @return true if input is in correct IPv6 notation. */ public static boolean isIpv6StdAddress(final String input) { return IPV6_STD_PATTERN.matcher(input).matches(); } /** * Check if the given address is a valid IPv6 address in the hex-compressed notation The format is * 'xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx'. If all digits in a block are '0' the block can be left empty. * * @param input ip-address to check * @return true if input is in correct IPv6 (hex-compressed) notation. */ public static boolean isIpv6HexCompressedAddress(final String input) { return IPV6_HEX_COMPRESSED_PATTERN.matcher(input).matches(); } /** * Check if input is a IPv6 address. Possible notations for valid IPv6 are: - Standard IPv6 address - * Hex-compressed IPv6 address - Link-local IPv6 address - IPv4-mapped-to-IPV6 address - IPv6 mixed address * * @param input ip-address to check * @return true if input is in correct IPv6 notation. */ public static boolean isIpv6Address(final String input) { return isIpv6StdAddress(input) || isIpv6HexCompressedAddress(input) || isLinkLocalIpv6WithZoneIndex(input) || isIpv6Ipv4MappedAddress(input) || isIpv6MixedAddress(input); } /** * Check if the given address is a valid IPv6 address in the mixed-standard or mixed-compressed notation. IPV6 Mixed * mode consists of two parts, the first 96 bits (up to 6 blocks of 4 hex digits) are IPv6 the IPV6 part can be * either compressed or uncompressed the second block is a full IPv4 address e.g. '0:0:0:0:0:0:172.12.55.18' * * @param input ip-address to check * @return true if input is in correct IPv6 (mixed-standard or mixed-compressed) notation. */ public static boolean isIpv6MixedAddress(final String input) { int splitIndex = input.lastIndexOf(':'); if (splitIndex == -1) { return false; } //the last part is a ipv4 address boolean ipv4PartValid = isIpv4Address(input.substring(splitIndex + 1)); String ipV6Part = input.substring(ZERO, splitIndex + 1); if (DOUBLE_COLON.equals(ipV6Part)) { return ipv4PartValid; } boolean ipV6UncompressedDetected = IPV6_MIXED_UNCOMPRESSED_REGEX.matcher(ipV6Part).matches(); boolean ipV6CompressedDetected = IPV6_MIXED_COMPRESSED_REGEX.matcher(ipV6Part).matches(); return ipv4PartValid && (ipV6UncompressedDetected || ipV6CompressedDetected); } /** * Check if input is an IPv4 address mapped into a IPv6 address. These are starting with "::ffff:" * followed by the IPv4 address in a dot-seperated notation. The format is '::ffff:d.d.d.d' * * @param input ip-address to check * @return true if input is in correct IPv6 notation containing an IPv4 address */ public static boolean isIpv6Ipv4MappedAddress(final String input) { if (input.length() > SEVEN && input.substring(ZERO, SEVEN).equalsIgnoreCase(DOUBLE_COLON_FFFF)) { String lowerPart = input.substring(SEVEN); return isIpv4Address(lowerPart); } return false; } /** * Check if input is a link local IPv6 address starting with "fe80:" and containing a zone index with * "%xxx". The zone index will not be checked. * * @param input ip-address to check * @return true if address part of input is in correct IPv6 notation. */ public static boolean isLinkLocalIpv6WithZoneIndex(String input) { if (input.length() > FIVE && input.substring(ZERO, FIVE).equalsIgnoreCase(FE80)) { int lastIndex = input.lastIndexOf(PERCENT); if (lastIndex > ZERO && lastIndex < (input.length() - 1)) { String ipPart = input.substring(ZERO, lastIndex); return isIpv6StdAddress(ipPart) || isIpv6HexCompressedAddress(ipPart); } } return false; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/InternetAddressUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.net.InetAddress; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * ip tool. * * @author Nacos */ @SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class InternetAddressUtil { private InternetAddressUtil() { } public static final boolean PREFER_IPV6_ADDRESSES = Boolean.parseBoolean( System.getProperty("java.net.preferIPv6Addresses")); public static final String IPV6_START_MARK = "["; public static final String IPV6_END_MARK = "]"; public static final String ILLEGAL_IP_PREFIX = "illegal ip: "; public static final String IP_PORT_SPLITER = ":"; public static final int SPLIT_IP_PORT_RESULT_LENGTH = 2; public static final String PERCENT_SIGN_IN_IPV6 = "%"; public static final String LOCAL_HOST = "localhost"; private static final String LOCAL_HOST_IP_V4 = "127.0.0.1"; private static final String LOCAL_HOST_IP_V6 = "[::1]"; private static final String CHECK_OK = "ok"; private static final Pattern DOMAIN_PATTERN = Pattern.compile( "[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\\.?"); private static final String IPV4_TUPLE = "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])"; private static final Pattern IPV4_PATTERN = Pattern.compile( "(? readLines(Reader input) throws IOException { BufferedReader reader = toBufferedReader(input); List list = new ArrayList<>(); while (true) { String line = reader.readLine(); if (null != line) { if (StringUtils.isNotEmpty(line)) { list.add(line.trim()); } } else { break; } } return list; } /** * To string from stream. * * @param input stream * @param encoding charset of stream * @return string * @throws IOException io exception */ public static String toString(InputStream input, String encoding) throws IOException { if (input == null) { return StringUtils.EMPTY; } return (null == encoding) ? toString(new InputStreamReader(input, Constants.ENCODE)) : toString(new InputStreamReader(input, encoding)); } /** * To string from reader. * * @param reader reader * @return string * @throws IOException io exception */ public static String toString(Reader reader) throws IOException { CharArrayWriter sw = new CharArrayWriter(); copy(reader, sw); return sw.toString(); } /** * Copy data. * * @param input source * @param output target * @return copy size * @throws IOException io exception */ public static long copy(Reader input, Writer output) throws IOException { char[] buffer = new char[1 << 12]; long count = 0; for (int n = 0; (n = input.read(buffer)) >= 0; ) { output.write(buffer, 0, n); count += n; } return count; } /** * Copy data. * * @param input source * @param output target * @return copy size * @throws IOException io exception */ public static long copy(InputStream input, OutputStream output) throws IOException { byte[] buffer = new byte[1024]; int bytesRead; int totalBytes = 0; while ((bytesRead = input.read(buffer)) != -1) { output.write(buffer, 0, bytesRead); totalBytes += bytesRead; } return totalBytes; } /** * Delete file or dir. * *

    If is dir, clean directory, do not delete dir. * *

    If is file, delete file. * * @param fileOrDir file or dir * @throws IOException io exception */ public static void delete(File fileOrDir) throws IOException { if (fileOrDir == null) { return; } if (fileOrDir.isDirectory()) { cleanDirectory(fileOrDir); } else { if (fileOrDir.exists()) { boolean isDeleteOk = fileOrDir.delete(); if (!isDeleteOk) { throw new IOException("delete fail"); } } } } /** * 清理目录下的内容. Clean content under directory. * * @param directory directory * @throws IOException io exception */ public static void cleanDirectory(File directory) throws IOException { if (!directory.exists()) { String message = directory + " does not exist"; throw new IllegalArgumentException(message); } if (!directory.isDirectory()) { String message = directory + " is not a directory"; throw new IllegalArgumentException(message); } File[] files = directory.listFiles(); // null if security restricted if (files == null) { throw new IOException("Failed to list contents of " + directory); } IOException exception = null; for (File file : files) { try { delete(file); } catch (IOException ioe) { exception = ioe; } } if (null != exception) { throw exception; } } /** * Judge whether is Gzip stream. * * @param bytes byte array * @return true if is gzip, otherwise false */ public static boolean isGzipStream(byte[] bytes) { int minByteArraySize = 2; if (bytes == null || bytes.length < minByteArraySize) { return false; } return GZIPInputStream.GZIP_MAGIC == ((bytes[1] << 8 | bytes[0]) & 0xFFFF); } /** * Close http connection quietly. * * @param connection http connection */ public static void closeQuietly(HttpURLConnection connection) { if (connection != null) { try { closeQuietly(connection.getInputStream()); } catch (Exception ignore) { } } } /** * Close closable object quietly. * * @param closeable http connection */ public static void closeQuietly(Closeable closeable) { try { if (closeable != null) { closeable.close(); } } catch (IOException ignored) { } } public static void closeQuietly(Closeable... closeable) { Arrays.stream(closeable).forEach(IoUtils::closeQuietly); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/JacksonUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.api.exception.runtime.NacosSerializationException; import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.jsontype.NamedType; import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import java.io.InputStream; import java.io.IOException; import java.lang.reflect.Type; /** * Json utils implement by Jackson. * * @author liaochuntao */ public final class JacksonUtils { private JacksonUtils() { } static ObjectMapper mapper = new ObjectMapper(); static { mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); mapper.setSerializationInclusion(Include.NON_NULL); } /** * Object to json string. * * @param obj obj * @return json string * @throws NacosSerializationException if transfer failed */ public static String toJson(Object obj) { try { return mapper.writeValueAsString(obj); } catch (JsonProcessingException e) { throw new NacosSerializationException(obj.getClass(), e); } } /** * Object to json string byte array. * * @param obj obj * @return json string byte array * @throws NacosSerializationException if transfer failed */ public static byte[] toJsonBytes(Object obj) { try { return mapper.writeValueAsBytes(obj); } catch (JsonProcessingException e) { throw new NacosSerializationException(obj.getClass(), e); } } /** * Json string deserialize to Object. * * @param json json string * @param cls class of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(byte[] json, Class cls) { try { return mapper.readValue(json, cls); } catch (Exception e) { throw new NacosDeserializationException(cls, e); } } /** * Json string deserialize to Object. * * @param json json string * @param cls {@link Type} of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(byte[] json, Type cls) { try { return mapper.readValue(json, mapper.constructType(cls)); } catch (Exception e) { throw new NacosDeserializationException(e); } } /** * Json string deserialize to Object. * * @param inputStream json string input stream * @param cls class of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(InputStream inputStream, Class cls) { try { return mapper.readValue(inputStream, cls); } catch (IOException e) { throw new NacosDeserializationException(e); } } /** * Json string deserialize to Object. * * @param json json string byte array * @param typeReference {@link TypeReference} of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(byte[] json, TypeReference typeReference) { try { return mapper.readValue(json, typeReference); } catch (Exception e) { throw new NacosDeserializationException(e); } } /** * Json string deserialize to Object. * * @param json json string * @param cls class of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(String json, Class cls) { try { return mapper.readValue(json, cls); } catch (IOException e) { throw new NacosDeserializationException(cls, e); } } /** * Json string deserialize to Object. * * @param json json string * @param type {@link Type} of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(String json, Type type) { try { return mapper.readValue(json, mapper.constructType(type)); } catch (IOException e) { throw new NacosDeserializationException(e); } } /** * Json string deserialize to Object. * * @param json json string * @param typeReference {@link TypeReference} of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(String json, TypeReference typeReference) { try { return mapper.readValue(json, typeReference); } catch (IOException e) { throw new NacosDeserializationException(typeReference.getClass(), e); } } /** * Json string deserialize to Object. * * @param inputStream json string input stream * @param type {@link Type} of object * @param General type * @return object * @throws NacosDeserializationException if deserialize failed */ public static T toObj(InputStream inputStream, Type type) { try { return mapper.readValue(inputStream, mapper.constructType(type)); } catch (IOException e) { throw new NacosDeserializationException(type, e); } } /** * Json string deserialize to Jackson {@link JsonNode}. * * @param json json string * @return {@link JsonNode} * @throws NacosDeserializationException if deserialize failed */ public static JsonNode toObj(String json) { try { return mapper.readTree(json); } catch (IOException e) { throw new NacosDeserializationException(e); } } /** * Register sub type for child class. * * @param clz child class * @param type type name of child class */ public static void registerSubtype(Class clz, String type) { mapper.registerSubtypes(new NamedType(clz, type)); } /** * Create a new empty Jackson {@link ObjectNode}. * * @return {@link ObjectNode} */ public static ObjectNode createEmptyJsonNode() { return new ObjectNode(mapper.getNodeFactory()); } /** * Create a new empty Jackson {@link ArrayNode}. * * @return {@link ArrayNode} */ public static ArrayNode createEmptyArrayNode() { return new ArrayNode(mapper.getNodeFactory()); } /** * Parse object to Jackson {@link JsonNode}. * * @param obj object * @return {@link JsonNode} */ public static JsonNode transferToJsonNode(Object obj) { return mapper.valueToTree(obj); } /** * construct java type -> Jackson Java Type. * * @param type java type * @return JavaType {@link JavaType} */ public static JavaType constructJavaType(Type type) { return mapper.constructType(type); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/LoggerUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.slf4j.Logger; /** * Logger utils. * * @author liaochuntao */ public final class LoggerUtils { private LoggerUtils() { } public static final String TRACE = "TRACE"; public static final String INFO = "INFO"; public static final String DEBUG = "DEBUG"; public static final String WARN = "WARN"; public static final String ERROR = "ERROR"; /** * Print if log debug level is enabled. * * @param logger logger * @param s log message * @param args arguments */ public static void printIfDebugEnabled(Logger logger, String s, Object... args) { if (logger.isDebugEnabled()) { logger.debug(s, args); } } /** * Print if log info level is enabled. * * @param logger logger * @param s log message * @param args arguments */ public static void printIfInfoEnabled(Logger logger, String s, Object... args) { if (logger.isInfoEnabled()) { logger.info(s, args); } } /** * Print if log trace level is enabled. * * @param logger logger * @param s log message * @param args arguments */ public static void printIfTraceEnabled(Logger logger, String s, Object... args) { if (logger.isTraceEnabled()) { logger.trace(s, args); } } /** * Print if log warn level is enabled. * * @param logger logger * @param s log message * @param args arguments */ public static void printIfWarnEnabled(Logger logger, String s, Object... args) { if (logger.isWarnEnabled()) { logger.warn(s, args); } } /** * Print if log error level is enabled. * * @param logger logger * @param s log message * @param args arguments */ public static void printIfErrorEnabled(Logger logger, String s, Object... args) { if (logger.isErrorEnabled()) { logger.error(s, args); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/MD5Utils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** * MD5 util. * * @author nacos */ public class MD5Utils { private MD5Utils() { } private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** * Calculate MD5 hex string. * * @param bytes byte arrays * @return MD5 hex string of input * @throws NoSuchAlgorithmException if can't load md5 digest spi. */ public static String md5Hex(byte[] bytes) throws NoSuchAlgorithmException { MessageDigest messageDigest = MessageDigest.getInstance("MD5"); return encodeHexString(messageDigest.digest(bytes)); } /** * Calculate MD5 hex string with encode charset. * * @param value value * @param encode encode charset of input * @return MD5 hex string of input */ public static String md5Hex(String value, String encode) { try { return md5Hex(value.getBytes(encode)); } catch (Exception e) { throw new RuntimeException(e); } } /** * Convert a byte array into a visible string. */ public static String encodeHexString(byte[] bytes) { int l = bytes.length; char[] out = new char[l << 1]; for (int i = 0, j = 0; i < l; i++) { out[j++] = DIGITS_LOWER[(0xF0 & bytes[i]) >>> 4]; out[j++] = DIGITS_LOWER[0x0F & bytes[i]]; } return new String(out); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/MapUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.common.NotThreadSafe; import java.util.Collection; import java.util.Dictionary; import java.util.Map; import java.util.Objects; import java.util.function.Predicate; import java.util.function.BiFunction; /** * Map utils. * * @author liaochuntao */ public class MapUtil { private MapUtil() { } /** * Null-safe check if the specified Dictionary is empty. * *

    Null returns true. * * @param map the collection to check, may be null * @return true if empty or null */ public static boolean isEmpty(Map map) { return (map == null || map.isEmpty()); } /** * Null-safe check if the specified Dictionary is empty. * *

    Null returns true. * * @param coll the collection to check, may be null * @return true if empty or null */ public static boolean isEmpty(Dictionary coll) { return (coll == null || coll.isEmpty()); } /** * Null-safe check if the specified Dictionary is not empty. * *

    Null returns false. * * @param map the collection to check, may be null * @return true if non-null and non-empty */ public static boolean isNotEmpty(Map map) { return !isEmpty(map); } /** * Null-safe check if the specified Dictionary is not empty. * *

    Null returns false. * * @param coll the collection to check, may be null * @return true if non-null and non-empty */ public static boolean isNotEmpty(Dictionary coll) { return !isEmpty(coll); } /** * Put into map if value is not null. * * @param target target map * @param key key * @param value value */ public static void putIfValNoNull(Map target, Object key, Object value) { Objects.requireNonNull(key, "key"); if (value != null) { target.put(key, value); } } /** * Put into map if value is not empty. * * @param target target map * @param key key * @param value value */ public static void putIfValNoEmpty(Map target, Object key, Object value) { Objects.requireNonNull(key, "key"); if (value instanceof String) { if (StringUtils.isNotEmpty((String) value)) { target.put(key, value); } return; } if (value instanceof Collection) { if (CollectionUtils.isNotEmpty((Collection) value)) { target.put(key, value); } return; } if (value instanceof Map) { if (isNotEmpty((Map) value)) { target.put(key, value); } return; } if (value instanceof Dictionary) { if (isNotEmpty((Dictionary) value)) { target.put(key, value); } } } /** * ComputeIfAbsent lazy load. * * @param target target Map data. * @param key map key. * @param mappingFunction function which is need to be executed. * @param param1 function's parameter value1. * @param param2 function's parameter value1. * @return */ @NotThreadSafe public static V computeIfAbsent(Map target, K key, BiFunction mappingFunction, C param1, T param2) { Objects.requireNonNull(target, "target"); Objects.requireNonNull(key, "key"); Objects.requireNonNull(mappingFunction, "mappingFunction"); Objects.requireNonNull(param1, "param1"); Objects.requireNonNull(param2, "param2"); return target.computeIfAbsent(key, (keyInner) -> mappingFunction.apply(param1, param2)); } /** * remove value, Thread safety depends on whether the Map is a thread-safe Map. * * @param map map * @param key key * @param removeJudge judge this key can be remove * @param key type * @param value type * @return value */ public static V removeKey(Map map, K key, Predicate removeJudge) { return map.computeIfPresent(key, (k, v) -> removeJudge.test(v) ? null : v); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/NamespaceUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.common.Constants; /** * namespace(tenant) util. Because config and naming treat namespace(tenant) differently, this tool class can only be * used by the config module. * * @author klw(213539 @ qq.com) * @date 2020/10/12 17:56 */ public class NamespaceUtil { private NamespaceUtil() { } /** * public id,默认值为 "public". */ public static String namespaceDefaultId = Constants.DEFAULT_NAMESPACE_ID; /** * Treat the namespace(tenant) parameters with values of empty string as "public". * * @param tenant namespace(tenant) id * @return java.lang.String A namespace(tenant) string processed */ public static String processNamespaceParameter(String tenant) { if (isNeedTransferNamespace(tenant)) { return getNamespaceDefaultId(); } return tenant.trim(); } public static boolean isNeedTransferNamespace(String tenant) { return StringUtils.isBlank(tenant); } /** * Set default namespace id. Invoke settings at system startup. * * @param namespaceDefaultId 参数不能为 null */ public static void setNamespaceDefaultId(String namespaceDefaultId) { NamespaceUtil.namespaceDefaultId = namespaceDefaultId; } /** * Get default namespace id. * * @return namespace id */ public static String getNamespaceDefaultId() { return NamespaceUtil.namespaceDefaultId; } /** * Judge whether is default namespaceId. * * @param namespaceId to check namespaceId * @return {@code true} if equals default namespaceId, otherwise {@code false}. */ public static boolean isDefaultNamespaceId(String namespaceId) { return StringUtils.equals(namespaceId, getNamespaceDefaultId()); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/NumberUtils.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; /** * Number utils. * @author zzq */ public class NumberUtils { private NumberUtils() { } /** * Convert a String to an int, returning * zero if the conversion fails. * * @param str the string to convert, may be null * @return the int represented by the string, or zero if * conversion fails */ public static int toInt(String str) { return toInt(str, 0); } /** * Convert a String to an int, returning a * default value if the conversion fails. * * @param str the string to convert, may be null * @param defaultValue the default value * @return the int represented by the string, or the default if conversion fails */ public static int toInt(String str, int defaultValue) { if (str == null) { return defaultValue; } try { return Integer.parseInt(str); } catch (NumberFormatException nfe) { return defaultValue; } } /** * Convert a String to a long, returning a * default value if the conversion fails. * * @param str the string to convert, may be null * @param defaultValue the default value * @return the long represented by the string, or the default if conversion fails */ public static long toLong(String str, long defaultValue) { if (str == null) { return defaultValue; } try { return Long.parseLong(str); } catch (NumberFormatException nfe) { return defaultValue; } } /** * Convert a String to a double, returning a * default value if the conversion fails. * * @param str the string to convert, may be null * @param defaultValue the default value * @return the double represented by the string, or defaultValue * if conversion fails */ public static double toDouble(String str, double defaultValue) { if (str == null) { return defaultValue; } try { return Double.parseDouble(str); } catch (NumberFormatException nfe) { return defaultValue; } } /** * Checks whether the String contains only * digit characters. * * @param str the String to check * @return true if str contains only unicode numeric */ public static boolean isDigits(String str) { if (StringUtils.isEmpty(str)) { return false; } for (int i = 0; i < str.length(); i++) { if (!Character.isDigit(str.charAt(i))) { return false; } } return true; } /** * Convert a String to a float, returning * 0.0f if the conversion fails. * * @param str the string to convert, may be null * @return the float represented by the string, or 0.0f * if conversion fails */ public static float toFloat(final String str) { return toFloat(str, 0.0f); } /** * Convert a String to a float, returning a * default value if the conversion fails. * * @param str the string to convert, may be null * @param defaultValue the default value * @return the float represented by the string, or defaultValue * if conversion fails */ public static float toFloat(final String str, final float defaultValue) { if (str == null) { return defaultValue; } try { return Float.parseFloat(str); } catch (final NumberFormatException nfe) { return defaultValue; } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/Observable.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.Objects; import java.util.Set; /** * Observable utils. * * @author liaochuntao */ public class Observable { private transient boolean changed = false; private transient Set obs = new ConcurrentHashSet<>(); private volatile int observerCnt = 0; /** * Adds an observer to the set of observers for this object, provided that it is not the same as some observer * already in the set. The order in which notifications will be delivered to multiple observers is not specified. * See the class comment. * * @param o an observer to be added. * @throws NullPointerException if the parameter o is null. */ public synchronized void addObserver(Observer o) { Objects.requireNonNull(o, "Observer"); obs.add(o); observerCnt++; o.update(this); } /** * Deletes an observer from the set of observers of this object. Passing {@code null} to this method will have no * effect. * * @param o the observer to be deleted. */ public synchronized void deleteObserver(Observer o) { obs.remove(o); observerCnt--; } /** * If this object has changed, as indicated by the {@code hasChanged} method, then notify all of its observers and * then call the {@code clearChanged} method to indicate that this object has no longer changed. * *

    Each observer has its {@code update} method called with one argument: this observable object. */ public void notifyObservers() { synchronized (this) { /* We don't want the Observer doing callbacks into * arbitrary code while holding its own Monitor. * The code where we extract each Observable from * the Vector and store the state of the Observer * needs synchronization, but notifying observers * does not (should not). The worst result of any * potential race-condition here is that: * 1) a newly-added Observer will miss a * notification in progress * 2) a recently unregistered Observer will be * wrongly notified when it doesn't care */ if (!changed) { return; } clearChanged(); } for (Observer observer : obs) { observer.update(this); } } /** * Clears the observer list so that this object no longer has any observers. */ public void deleteObservers() { obs.clear(); } /** * Marks this {@code Observable} object as having been changed; the {@code hasChanged} method will now return {@code * true}. */ protected synchronized void setChanged() { changed = true; } /** * Indicates that this object has no longer changed, or that it has already notified all of its observers of its * most recent change, so that the {@code hasChanged} method will now return {@code false}. This method is called * automatically by the {@code notifyObservers} methods. * * @see java.util.Observable#notifyObservers() * @see java.util.Observable#notifyObservers(java.lang.Object) */ protected synchronized void clearChanged() { changed = false; } /** * Tests if this object has changed. * * @return {@code true} if and only if the {@code setChanged} method has been called more recently than the {@code * clearChanged} method on this object; {@code false} otherwise. */ public synchronized boolean hasChanged() { return changed; } /** * Returns the number of observers of this {@code Observable} object. * * @return the number of observers of this object. */ public int countObservers() { return observerCnt; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/Observer.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; /** * Obeserver. * * @author liaochuntao * @author xiweng.yy */ public interface Observer { /** * This method is called whenever the observed object is changed. An application calls an {@code Observable} * object's {@code notifyObservers} method to have all the object's observers notified of the change. * * @param o the observable object. */ void update(Observable o); } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/Pair.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; /** * Pair. * * @author nacos */ public class Pair { private final A first; private final B second; Pair(A first, B second) { this.first = first; this.second = second; } public static Pair with(A first, B second) { return new Pair<>(first, second); } public A getFirst() { return first; } public B getSecond() { return second; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/Preconditions.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.Objects; /** * Check precondition, throws an {@code IllegalArgumentException} If the conditions are not met. * @author zzq * @date 2021/7/29 */ public class Preconditions { private Preconditions() { } /** * check precondition. * @param expression a boolean expression * @param errorMessage the exception message to use if the check fails * @throws IllegalArgumentException if {@code expression} is false */ public static void checkArgument(boolean expression, Object errorMessage) { if (Objects.isNull(errorMessage)) { throw new IllegalArgumentException("errorMessage cannot be null."); } if (!expression) { throw new IllegalArgumentException(String.valueOf(errorMessage)); } } /** * check precondition. * @param expression a boolean expression * @param errorMessageTemplate the exception message template to use if the check fails * @param errorMessageArgs the arguments to be substituted into the message template. * @throws IllegalArgumentException if {@code expression} is false */ public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { if (Objects.isNull(errorMessageArgs) || Objects.isNull(errorMessageTemplate)) { throw new IllegalArgumentException("errorMessageTemplate or errorMessage cannot be null."); } if (!expression) { throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/PropertyUtils.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; /** * A convenient tool to get property or env value. * * @author Pixy Yuan on 2022/3/24 */ public class PropertyUtils { private PropertyUtils() { } private static final String PROCESSORS_ENV_NAME = "NACOS_COMMON_PROCESSORS"; private static final String PROCESSORS_PROP_NAME = "nacos.common.processors"; /** * Get system env or property value. * *

    If {@link System#getenv()} has no value for {@code envName}, * return {@link System#getProperty(String)}. */ public static String getProperty(String propertyName, String envName) { return System.getenv().getOrDefault(envName, System.getProperty(propertyName)); } /** * Get system env or property value. * *

    If {@link System#getenv()} has no value for {@code envName}, * return {@link System#getProperty(String, String)} or {@code defaultValue}. */ public static String getProperty(String propertyName, String envName, String defaultValue) { return System.getenv().getOrDefault(envName, System.getProperty(propertyName, defaultValue)); } /** * Get processors count maybe preset by env or property. */ public static int getProcessorsCount() { int processorsCount = 0; String processorsCountPreSet = getProperty(PROCESSORS_PROP_NAME, PROCESSORS_ENV_NAME); if (processorsCountPreSet != null) { try { processorsCount = Integer.parseInt(processorsCountPreSet); } catch (NumberFormatException ignored) { } } if (processorsCount <= 0) { processorsCount = Runtime.getRuntime().availableProcessors(); } return processorsCount; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/RandomUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.Random; /** * Random utils. * * @author zzq */ public class RandomUtils { private RandomUtils() { } /** * Random Object for random method. */ private static final Random RANDOM = new Random(); /** * Returns a random long within the specified range. * * @param startInclusive the smallest value that can be returned, must be non-negative * @param endExclusive the upper bound (not included) * @return the random long * @throws IllegalArgumentException if startInclusive or endExclusive illegal */ public static long nextLong(final long startInclusive, final long endExclusive) { checkParameters(startInclusive, endExclusive); long diff = endExclusive - startInclusive; if (diff == 0) { return startInclusive; } return (long) (startInclusive + (diff * RANDOM.nextDouble())); } /** * Returns a random integer within the specified range. * * @param startInclusive lower limit, must be non-negative * @param endExclusive the upper bound (not included) * @return the random integer * @throws IllegalArgumentException if startInclusive or endExclusive illegal */ public static int nextInt(final int startInclusive, final int endExclusive) { checkParameters(startInclusive, endExclusive); int diff = endExclusive - startInclusive; if (diff == 0) { return startInclusive; } return startInclusive + RANDOM.nextInt(diff); } /** * Check input parameters. * * @param startInclusive lower limit, must be non-negative * @param endExclusive the upper bound (not included) */ private static void checkParameters(final long startInclusive, final long endExclusive) { if (endExclusive < startInclusive) { throw new IllegalArgumentException("startInclusive must be less than or equal to the endExclusive."); } if (startInclusive < 0) { throw new IllegalArgumentException("Both parameters must be non-negative"); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ReflectUtils.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; /** * reflect utils. * * @author liuzunfei * @version $Id: ReflectUtils.java, v 0.1 2020年08月20日 12:57 PM liuzunfei Exp $ */ public class ReflectUtils { private ReflectUtils() { } /** * get filed value of obj. * * @param obj obj. * @param fieldName file name to get value. * @return field value. */ public static Object getFieldValue(Object obj, String fieldName) { try { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); return field.get(obj); } catch (Exception e) { throw new RuntimeException(e); } } /** * get filed value of obj. * * @param obj obj. * @param fieldName file name to get value. * @return field value. */ public static Object getFieldValue(Object obj, String fieldName, Object defaultValue) { try { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true); return field.get(obj); } catch (Exception e) { return defaultValue; } } /** * Get the field represented by the supplied {@link Field field object} on the specified {@link Object target * object}. In accordance with {@link Field#get(Object)} semantics, the returned value is automatically wrapped if * the underlying field has a primitive type. * *

    Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}. * * @param field the field to get * @param target the target object from which to get the field (or {@code null} for a static field) * @return the field's current value */ public static Object getField(Field field, Object target) { try { return field.get(target); } catch (IllegalAccessException ex) { handleReflectionException(ex); } throw new IllegalStateException("Should never get here"); } /** * Handle the given reflection exception. * *

    Should only be called if no checked exception is expected to be thrown * by a target method, or if an error occurs while accessing a method or field. * *

    Throws the underlying RuntimeException or Error in case of an * InvocationTargetException with such a root cause. Throws an IllegalStateException with an appropriate message or * UndeclaredThrowableException otherwise. * * @param ex the reflection exception to handle */ public static void handleReflectionException(Exception ex) { if (ex instanceof NoSuchMethodException) { throw new IllegalStateException("Method not found: " + ex.getMessage()); } if (ex instanceof IllegalAccessException) { throw new IllegalStateException("Could not access method or field: " + ex.getMessage()); } if (ex instanceof InvocationTargetException) { handleInvocationTargetException((InvocationTargetException) ex); } if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } throw new UndeclaredThrowableException(ex); } /** * Handle the given invocation target exception. Should only be called if no checked exception is expected to be * thrown by the target method. * *

    Throws the underlying RuntimeException or Error in case of such a root * cause. Throws an UndeclaredThrowableException otherwise. * * @param ex the invocation target exception to handle */ public static void handleInvocationTargetException(InvocationTargetException ex) { rethrowRuntimeException(ex.getTargetException()); } /** * Rethrow the given {@link Throwable exception}, which is presumably the * target exception of an {@link InvocationTargetException}. * Should only be called if no checked exception is expected to be thrown by the target method. * *

    Rethrows the underlying exception cast to a {@link RuntimeException} or * {@link Error} if appropriate; otherwise, throws an {@link UndeclaredThrowableException}. * * @param ex the exception to rethrow * @throws RuntimeException the rethrown exception */ public static void rethrowRuntimeException(Throwable ex) { if (ex instanceof RuntimeException) { throw (RuntimeException) ex; } if (ex instanceof Error) { throw (Error) ex; } throw new UndeclaredThrowableException(ex); } /** * Invoke the specified {@link Method} against the supplied target object with the supplied arguments. The target * object can be {@code null} when invoking a static {@link Method}. * *

    Thrown exceptions are handled via a call to {@link #handleReflectionException}. * * @param method the method to invoke * @param target the target object to invoke the method on * @param args the invocation arguments (may be {@code null}) * @return the invocation result, if any */ public static Object invokeMethod(Method method, Object target, Object... args) { try { return method.invoke(target, args); } catch (Exception ex) { handleReflectionException(ex); } throw new IllegalStateException("Should never get here"); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ResourceUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; /** * resource util. * * @author boyan */ public class ResourceUtils { private ResourceUtils() { } private static final String CLASSPATH_PREFIX = "classpath:"; /** * Returns the URL of the resource on the classpath. * * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static URL getResourceUrl(String resource) throws IOException { if (resource.startsWith(CLASSPATH_PREFIX)) { String path = resource.substring(CLASSPATH_PREFIX.length()); ClassLoader classLoader = ResourceUtils.class.getClassLoader(); URL url = (classLoader != null ? classLoader.getResource(path) : ClassLoader.getSystemResource(path)); if (url == null) { throw new FileNotFoundException("Resource [" + resource + "] does not exist"); } return url; } try { return new URL(resource); } catch (MalformedURLException ex) { return new File(resource).toURI().toURL(); } } /** * Returns the URL of the resource on the classpath. * * @param loader The classloader used to load the resource * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static URL getResourceUrl(ClassLoader loader, String resource) throws IOException { URL url = null; if (loader != null) { url = loader.getResource(resource); } if (url == null) { url = ClassLoader.getSystemResource(resource); } if (url == null) { throw new IOException("Could not find resource " + resource); } return url; } /** * Returns a resource on the classpath as a Stream object. * * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static InputStream getResourceAsStream(String resource) throws IOException { ClassLoader loader = ResourceUtils.class.getClassLoader(); return getResourceAsStream(loader, resource); } /** * Returns a resource on the classpath as a Stream object. * * @param loader The classloader used to load the resource * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException { InputStream in = null; if (loader != null) { in = loader.getResourceAsStream(resource); } if (in == null) { in = ClassLoader.getSystemResourceAsStream(resource); } if (in == null) { throw new IOException("Could not find resource " + resource); } return in; } /** * Returns a resource on the classpath as a Properties object. * * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static Properties getResourceAsProperties(String resource) throws IOException { ClassLoader loader = ResourceUtils.class.getClassLoader(); return getResourceAsProperties(loader, resource); } /** * Returns a resource on the classpath as a Properties object. * * @param loader The classloader used to load the resource * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static Properties getResourceAsProperties(ClassLoader loader, String resource) throws IOException { Properties props = new Properties(); InputStream in = getResourceAsStream(loader, resource); props.load(in); IoUtils.closeQuietly(in); return props; } /** * Returns a resource on the classpath as a Reader object. * * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static InputStreamReader getResourceAsReader(String resource, String charsetName) throws IOException { return new InputStreamReader(getResourceAsStream(resource), charsetName); } /** * Returns a resource on the classpath as a Reader object. * * @param loader The classloader used to load the resource * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static Reader getResourceAsReader(ClassLoader loader, String resource, String charsetName) throws IOException { return new InputStreamReader(getResourceAsStream(loader, resource), charsetName); } /** * Returns a resource on the classpath as a File object. * * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static File getResourceAsFile(String resource) throws IOException { return new File(getResourceUrl(resource).getFile()); } /** * Returns a resource on the classpath as a File object. * * @param url The resource url to find * @return The resource */ public static File getResourceAsFile(URL url) { return new File(url.getFile()); } /** * Returns a resource on the classpath as a File object. * * @param loader The classloader used to load the resource * @param resource The resource to find * @return The resource * @throws IOException If the resource cannot be found or read */ public static File getResourceAsFile(ClassLoader loader, String resource) throws IOException { return new File(getResourceUrl(loader, resource).getFile()); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/StringUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.nio.charset.StandardCharsets; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Deque; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; import java.util.regex.Pattern; /** * string util. * * @author Nacos * @author zzq */ public class StringUtils { private StringUtils() { } public static final String DOT = "."; private static final int INDEX_NOT_FOUND = -1; public static final String COMMA = ","; public static final String EMPTY = ""; public static final String LF = "\n"; private static final String[] EMPTY_STRING_ARRAY = {}; public static final String TOP_PATH = ".."; public static final String FOLDER_SEPARATOR = "/"; public static final String WINDOWS_FOLDER_SEPARATOR = "\\"; public static final Pattern UUID_PATTERN = Pattern.compile( "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"); /** *

    Create a string with encoding format as utf8.

    * * @param bytes the bytes that make up the string * @return created string */ public static String newStringForUtf8(byte[] bytes) { return new String(bytes, StandardCharsets.UTF_8); } /** *

    Checks if a string is empty (""), null and whitespace only.

    * * @param cs the string to check * @return {@code true} if the string is empty and null and whitespace */ public static boolean isBlank(final 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; } /** *

    Checks if a string is not empty (""), not null and not whitespace only.

    * * @param str the string to check, may be null * @return {@code true} if the string is not empty and not null and not whitespace */ public static boolean isNotBlank(String str) { return !isBlank(str); } /** *

    Checks if a str is not empty ("") or not null.

    * * @param str the str to check, may be null * @return {@code true} if the str is not empty or not null */ public static boolean isNotEmpty(String str) { return !isEmpty(str); } /** *

    Checks if a str is empty ("") or null.

    * * @param str the str to check, may be null * @return {@code true} if the str is empty or null */ public static boolean isEmpty(String str) { return str == null || str.length() == 0; } /** *

    Returns either the passed in CharSequence, or if the CharSequence is * empty or {@code null}, the value of {@code defaultStr}.

    * * @param str the CharSequence to check, may be null * @param defaultStr the default CharSequence to return if the input is empty ("") or {@code null}, may be null * @return the passed in CharSequence, or the default */ public static String defaultIfEmpty(String str, String defaultStr) { return isEmpty(str) ? defaultStr : str; } /** *

    Returns either the passed in CharSequence, or if the CharSequence is * empty or {@code null} or whitespace only, the value of {@code defaultStr}.

    * * @param str the CharSequence to check, may be null, may be whitespace only * @param defaultStr the default CharSequence to return if the input is empty ("") or {@code null}, may be null * @return the passed in CharSequence, or the default */ public static String defaultIfBlank(String str, String defaultStr) { return isBlank(str) ? defaultStr : str; } /** *

    Returns either the passed in CharSequence, or if the CharSequence is * empty or {@code null} or whitespace only, the value of {@code EmptyString}.

    * * @param str the CharSequence to check, may be null, may be whitespace only * @return the passed in CharSequence, or the empty string */ public static String defaultEmptyIfBlank(String str) { return defaultIfBlank(str, EMPTY); } /** *

    Compares two CharSequences, returning {@code true} if they represent * equal sequences of characters.

    * * @param str1 the first string, may be {@code null} * @param str2 the second string, may be {@code null} * @return {@code true} if the string are equal (case-sensitive), or both {@code null} * @see Object#equals(Object) */ public static boolean equals(String str1, String str2) { return str1 == null ? str2 == null : str1.equals(str2); } /** *

    Removes control characters (char <= 32) from both * ends of this String, handling {@code null} by returning {@code null}.

    * * @param str the String to be trimmed, may be null * @return the trimmed string, {@code null} if null String input */ public static String trim(final String str) { return str == null ? null : str.trim(); } /** * Substring between two index. * * @param str string * @param open start index to sub * @param close end index to sub * @return substring */ public static String substringBetween(String str, String open, String close) { if (str == null || open == null || close == null) { return null; } int start = str.indexOf(open); if (start != INDEX_NOT_FOUND) { int end = str.indexOf(close, start + open.length()); if (end != INDEX_NOT_FOUND) { return str.substring(start + open.length(), end); } } return null; } /** *

    Joins the elements of the provided array into a single String * containing the provided list of elements.

    * * @param collection the Collection of values to join together, may be null * @param separator the separator string to use * @return the joined String, {@code null} if null array input */ public static String join(Collection collection, String separator) { if (collection == null) { return null; } StringBuilder stringBuilder = new StringBuilder(); Object[] objects = collection.toArray(); for (int i = 0; i < collection.size(); i++) { if (objects[i] != null) { stringBuilder.append(objects[i]); if (i != collection.size() - 1 && separator != null) { stringBuilder.append(separator); } } } return stringBuilder.toString(); } /** * Checks if CharSequence contains a search CharSequence irrespective of case, handling {@code null}. * Case-insensitivity is defined as by {@link String#equalsIgnoreCase(String)}. * *

    A {@code null} CharSequence will return {@code false}.

    * * @param str the CharSequence to check, may be null * @param searchStr the CharSequence to find, may be null * @return true if the CharSequence contains the search CharSequence irrespective of case or false if not or {@code * null} string input */ public static boolean containsIgnoreCase(final CharSequence str, final CharSequence searchStr) { if (str == null || searchStr == null) { return false; } String str1 = str.toString().toLowerCase(); String str2 = searchStr.toString().toLowerCase(); return str1.contains(str2); } /** * Checks if CharSequence contains a search CharSequence. * * @param str the CharSequence to check, may be null * @param searchStr the CharSequence to find, may be null * @return true if the CharSequence contains the search CharSequence */ public static boolean contains(final CharSequence str, final CharSequence searchStr) { if (str == null || searchStr == null) { return false; } return str.toString().contains(searchStr); } /** *

    Checks if none of the CharSequences are blank ("") or null and whitespace only..

    * * @param css the CharSequences to check, may be null or empty * @return {@code true} if none of the CharSequences are blank or null or whitespace only */ public static boolean isNoneBlank(final CharSequence... css) { return !isAnyBlank(css); } /** *

    Checks if any one of the CharSequences are blank ("") or null and not whitespace only..

    * * @param css the CharSequences to check, may be null or empty * @return {@code true} if any of the CharSequences are blank or null or whitespace only */ public static boolean isAnyBlank(final CharSequence... css) { if (ArrayUtils.isEmpty(css)) { return true; } for (final CharSequence cs : css) { if (isBlank(cs)) { return true; } } return false; } /** *

    Check if a CharSequence starts with a specified prefix.

    * *

    {@code null}s are handled without exceptions. Two {@code null} * references are considered to be equal. The comparison is case sensitive.

    * * @param str the CharSequence to check, may be null * @param prefix the prefix to find, may be null * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or both {@code null} * @see java.lang.String#startsWith(String) */ public static boolean startsWith(final CharSequence str, final CharSequence prefix) { return startsWith(str, prefix, false); } /** *

    Check if a CharSequence starts with a specified prefix (optionally case insensitive).

    * * @param str the CharSequence to check, may be null * @param prefix the prefix to find, may be null * @param ignoreCase indicates whether the compare should ignore case (case insensitive) or not. * @return {@code true} if the CharSequence starts with the prefix or both {@code null} * @see java.lang.String#startsWith(String) */ private static boolean startsWith(final CharSequence str, final CharSequence prefix, final boolean ignoreCase) { if (str == null || prefix == null) { return str == null && prefix == null; } if (prefix.length() > str.length()) { return false; } if (ignoreCase) { String lowerCaseStr = str.toString().toLowerCase(); String lowerCasePrefix = prefix.toString().toLowerCase(); return lowerCaseStr.startsWith(lowerCasePrefix); } else { return str.toString().startsWith(prefix.toString()); } } /** *

    Case insensitive check if a CharSequence starts with a specified prefix.

    * *

    {@code null}s are handled without exceptions. Two {@code null} * references are considered to be equal. The comparison is case insensitive.

    * * @param str the CharSequence to check, may be null * @param prefix the prefix to find, may be null * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or both {@code null} * @see java.lang.String#startsWith(String) */ public static boolean startsWithIgnoreCase(final CharSequence str, final CharSequence prefix) { return startsWith(str, prefix, true); } /** *

    Deletes all whitespaces from a String as defined by * {@link Character#isWhitespace(char)}.

    * * @param str the String to delete whitespace from, may be null * @return the String without whitespaces, null if null String input */ public static String deleteWhitespace(String str) { if (isEmpty(str)) { return str; } int sz = str.length(); char[] chs = new char[sz]; int count = 0; for (int i = 0; i < sz; i++) { if (!Character.isWhitespace(str.charAt(i))) { chs[count++] = str.charAt(i); } } if (count == sz) { return str; } return new String(chs, 0, count); } /** *

    Compares two CharSequences, returning {@code true} if they represent * equal sequences of characters, ignoring case.

    * * @param str1 the first string, may be null * @param str2 the second string, may be null * @return {@code true} if the string are equal, case insensitive, or both {@code null} */ public static boolean equalsIgnoreCase(String str1, String str2) { return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2); } /** * Splits the provided text into an array with a maximum length, separators specified. If separatorChars is empty, * divide by blank. * * @param str the String to parse, may be null * @return an array of parsed Strings */ @SuppressWarnings("checkstyle:WhitespaceAround") public static String[] split(final String str, String separatorChars) { if (str == null) { return null; } if (str.length() == 0) { return new String[0]; } if (separatorChars == null) { separatorChars = " +"; } return str.split(separatorChars); } /** * Tokenize the given {@code String} into a {@code String} array via a {@link StringTokenizer}. * *

    The given {@code delimiters} string can consist of any number of * delimiter characters. Each of those characters can be used to separate tokens. A delimiter is always a single * character; * * @param str the {@code String} to tokenize (potentially {@code null} or empty) * @param delimiters the delimiter characters, assembled as a {@code String} (each of the characters is * individually considered as a delimiter) * @param trimTokens trim the tokens via {@link String#trim()} * @param ignoreEmptyTokens omit empty tokens from the result array (only applies to tokens that are empty after * trimming; StringTokenizer will not consider subsequent delimiters as token in the first * place). * @return an array of the tokens * @see java.util.StringTokenizer * @see String#trim() */ public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { if (str == null) { return EMPTY_STRING_ARRAY; } StringTokenizer st = new StringTokenizer(str, delimiters); List tokens = new ArrayList<>(); while (st.hasMoreTokens()) { String token = st.nextToken(); if (trimTokens) { token = token.trim(); } if (!ignoreEmptyTokens || token.length() > 0) { tokens.add(token); } } return toStringArray(tokens); } /** * Copy the given {@link Collection} into a {@code String} array. * *

    The {@code Collection} must contain {@code String} elements only. * * @param collection the {@code Collection} to copy (potentially {@code null} or empty) * @return the resulting {@code String} array */ public static String[] toStringArray(Collection collection) { return (!CollectionUtils.isEmpty(collection) ? collection.toArray(EMPTY_STRING_ARRAY) : EMPTY_STRING_ARRAY); } /** * Check whether the given {@code String} contains actual text. * *

    More specifically, this method returns {@code true} if the * {@code String} is not {@code null}, its length is greater than 0, and it contains at least one non-whitespace * character. * * @param str the {@code String} to check (maybe {@code null}) * @return {@code true} if the {@code String} is not {@code null}, its length is greater than 0, and it does not * contain whitespace only * @see Character#isWhitespace */ public static boolean hasText(String str) { return (str != null && !str.isEmpty() && containsText(str)); } private static boolean containsText(CharSequence str) { int strLen = str.length(); for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(str.charAt(i))) { return true; } } return false; } /** * Normalize the path by suppressing sequences like "path/.." and inner simple dots. * *

    The result is convenient for path comparison. For other uses, * notice that Windows separators ("\") are replaced by simple slashes. * *

    NOTE that {@code cleanPath} should not be depended * upon in a security context. Other mechanisms should be used to prevent path-traversal issues. * * @param path the original path * @return the normalized path */ public static String cleanPath(String path) { if (!hasLength(path)) { return path; } String normalizedPath = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR); String pathToUse = normalizedPath; // Shortcut if there is no work to do if (pathToUse.indexOf(DOT) == -1) { return pathToUse; } // Strip prefix from path to analyze, to not treat it as part of the // first path element. This is necessary to correctly parse paths like // "file:core/../core/io/Resource.class", where the ".." should just // strip the first "core" directory while keeping the "file:" prefix. int prefixIndex = pathToUse.indexOf(':'); String prefix = ""; if (prefixIndex != -1) { prefix = pathToUse.substring(0, prefixIndex + 1); if (prefix.contains(FOLDER_SEPARATOR)) { prefix = ""; } else { pathToUse = pathToUse.substring(prefixIndex + 1); } } if (pathToUse.startsWith(FOLDER_SEPARATOR)) { prefix = prefix + FOLDER_SEPARATOR; pathToUse = pathToUse.substring(1); } String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR); // we never require more elements than pathArray and in the common case the same number Deque pathElements = new ArrayDeque<>(pathArray.length); int tops = 0; for (int i = pathArray.length - 1; i >= 0; i--) { String element = pathArray[i]; if (DOT.equals(element)) { // Points to current directory - drop it. } else if (TOP_PATH.equals(element)) { // Registering top path found. tops++; } else { if (tops > 0) { // Merging path element with element corresponding to top path. tops--; } else { // Normal path element found. pathElements.addFirst(element); } } } // All path elements stayed the same - shortcut if (pathArray.length == pathElements.size()) { return normalizedPath; } // Remaining top paths need to be retained. for (int i = 0; i < tops; i++) { pathElements.addFirst(TOP_PATH); } // If nothing else left, at least explicitly point to current path. if (pathElements.size() == 1 && pathElements.getLast().isEmpty() && !prefix.endsWith(FOLDER_SEPARATOR)) { pathElements.addFirst(DOT); } final String joined = collectionToDelimitedString(pathElements, FOLDER_SEPARATOR); // avoid string concatenation with empty prefix return prefix.isEmpty() ? joined : prefix + joined; } /** * Convert a {@code Collection} into a delimited {@code String} (e.g. CSV). * *

    Useful for {@code toString()} implementations. * * @param coll the {@code Collection} to convert (potentially {@code null} or empty) * @param delim the delimiter to use (typically a ",") * @return the delimited {@code String} */ public static String collectionToDelimitedString(Collection coll, String delim) { return collectionToDelimitedString(coll, delim, "", ""); } /** * Convert a {@link Collection} to a delimited {@code String} (e.g. CSV). * *

    Useful for {@code toString()} implementations. * * @param coll the {@code Collection} to convert (potentially {@code null} or empty) * @param delim the delimiter to use (typically a ",") * @param prefix the {@code String} to start each element with * @param suffix the {@code String} to end each element with * @return the delimited {@code String} */ public static String collectionToDelimitedString(Collection coll, String delim, String prefix, String suffix) { if (CollectionUtils.isEmpty(coll)) { return ""; } int totalLength = coll.size() * (prefix.length() + suffix.length()) + (coll.size() - 1) * delim.length(); for (Object element : coll) { totalLength += String.valueOf(element).length(); } StringBuilder sb = new StringBuilder(totalLength); Iterator it = coll.iterator(); while (it.hasNext()) { sb.append(prefix).append(it.next()).append(suffix); if (it.hasNext()) { sb.append(delim); } } return sb.toString(); } /** * Check that the given {@code String} is neither {@code null} nor of length 0. * *

    Note: this method returns {@code true} for a {@code String} that * purely consists of whitespace. * * @param str the {@code String} to check (maybe {@code null}) * @return {@code true} if the {@code String} is not {@code null} and has length * @see #hasText(String) */ public static boolean hasLength(String str) { return (str != null && !str.isEmpty()); } /** * Take a {@code String} that is a delimited list and convert it into a {@code String} array. * *

    A single {@code delimiter} may consist of more than one character, * but it will still be considered as a single delimiter string, rather than as a bunch of potential delimiter * characters, in contrast to {@link #tokenizeToStringArray}. * * @param str the input {@code String} (potentially {@code null} or empty) * @param delimiter the delimiter between elements (this is a single delimiter, rather than a bunch individual * delimiter characters) * @return an array of the tokens in the list * @see #tokenizeToStringArray */ public static String[] delimitedListToStringArray(String str, String delimiter) { return delimitedListToStringArray(str, delimiter, null); } /** * Take a {@code String} that is a delimited list and convert it into a {@code String} array. * *

    A single {@code delimiter} may consist of more than one character, * but it will still be considered as a single delimiter string, rather than as a bunch of potential delimiter * characters, in contrast to {@link #tokenizeToStringArray}. * * @param str the input {@code String} (potentially {@code null} or empty) * @param delimiter the delimiter between elements (this is a single delimiter, rather than a bunch individual * delimiter characters) * @param charsToDelete a set of characters to delete; useful for deleting unwanted line breaks: e.g. "\r\n\f" will * delete all new lines and line feeds in a {@code String} * @return an array of the tokens in the list * @see #tokenizeToStringArray */ public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) { if (str == null) { return EMPTY_STRING_ARRAY; } if (delimiter == null) { return new String[] {str}; } List result = new ArrayList<>(); if (delimiter.isEmpty()) { for (int i = 0; i < str.length(); i++) { result.add(deleteAny(str.substring(i, i + 1), charsToDelete)); } } else { int pos = 0; int delPos; while ((delPos = str.indexOf(delimiter, pos)) != -1) { result.add(deleteAny(str.substring(pos, delPos), charsToDelete)); pos = delPos + delimiter.length(); } if (str.length() > 0 && pos <= str.length()) { // Add rest of String, but not in case of empty input. result.add(deleteAny(str.substring(pos), charsToDelete)); } } return toStringArray(result); } /** * Delete any character in a given {@code String}. * * @param inString the original {@code String} * @param charsToDelete a set of characters to delete. E.g. "az\n" will delete 'a's, 'z's and new lines. * @return the resulting {@code String} */ public static String deleteAny(String inString, String charsToDelete) { if (!hasLength(inString) || !hasLength(charsToDelete)) { return inString; } int lastCharIndex = 0; char[] result = new char[inString.length()]; for (int i = 0; i < inString.length(); i++) { char c = inString.charAt(i); if (charsToDelete.indexOf(c) == -1) { result[lastCharIndex++] = c; } } if (lastCharIndex == inString.length()) { return inString; } return new String(result, 0, lastCharIndex); } /** * Replace all occurrences of a substring within a string with another string. * * @param inString {@code String} to examine * @param oldPattern {@code String} to replace * @param newPattern {@code String} to insert * @return a {@code String} with the replacements */ public static String replace(String inString, String oldPattern, String newPattern) { if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) { return inString; } int index = inString.indexOf(oldPattern); if (index == -1) { // no occurrence -> can return input as-is return inString; } int capacity = inString.length(); if (newPattern.length() > oldPattern.length()) { capacity += 16; } StringBuilder sb = new StringBuilder(capacity); int pos = 0; int patLen = oldPattern.length(); while (index >= 0) { sb.append(inString, pos, index); sb.append(newPattern); pos = index + patLen; index = inString.indexOf(oldPattern, pos); } // append any characters to the right of a match sb.append(inString, pos, inString.length()); return sb.toString(); } /** * Apply the given relative path to the given Java resource path, assuming standard Java folder separation (i.e. "/" * separators). * * @param path the path to start from (usually a full file path) * @param relativePath the relative path to apply (relative to the full file path above) * @return the full file path that results from applying the relative path */ public static String applyRelativePath(String path, String relativePath) { int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); if (separatorIndex != -1) { String newPath = path.substring(0, separatorIndex); if (!relativePath.startsWith(FOLDER_SEPARATOR)) { newPath += FOLDER_SEPARATOR; } return newPath + relativePath; } else { return relativePath; } } /** * Extract the filename from the given Java resource path, e.g. {@code "myPath/myFile.txt" → "myFile.txt"}. * * @param path the file path (maybe {@code null}) * @return the extracted filename, or {@code null} if none */ public static String getFilename(String path) { if (path == null) { return null; } int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR); return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path); } /** * Capitalize a {@code String}, changing the first letter to upper case as per {@link Character#toUpperCase(char)}. * No other letters are changed. * * @param str the {@code String} to capitalize * @return the capitalized {@code String} */ public static String capitalize(String str) { return changeFirstCharacterCase(str); } private static String changeFirstCharacterCase(String str) { if (!hasLength(str)) { return str; } char baseChar = str.charAt(0); char updatedChar; updatedChar = Character.toUpperCase(baseChar); if (baseChar == updatedChar) { return str; } char[] chars = str.toCharArray(); chars[0] = updatedChar; return new String(chars); } public static boolean isUuidString(String str) { return UUID_PATTERN.matcher(str).matches(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ThreadFactoryBuilder.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.Locale; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; /** * build thread factory. * @author zzq * @date 2021/8/3 */ public class ThreadFactoryBuilder { /** * Whether it is a daemon thread. */ private Boolean daemon = false; /** * Thread priority. */ private Integer priority = null; /** * Thread name template. */ private String nameFormat = null; /** * Uncaught exception handler. */ private Thread.UncaughtExceptionHandler uncaughtExceptionHandler = null; /** * Customize thread factory. */ private ThreadFactory customizeFactory = null; /** * set nameFormat property. */ public ThreadFactoryBuilder nameFormat(String nameFormat) { checkNullParameter(nameFormat, "nameFormat cannot be null."); this.nameFormat = nameFormat; return this; } /** * set priority property. */ public ThreadFactoryBuilder priority(int priority) { if (priority > Thread.MAX_PRIORITY || priority < Thread.MIN_PRIORITY) { throw new IllegalArgumentException( String.format("The value of priority should be between %s and %s", Thread.MIN_PRIORITY + 1, Thread.MAX_PRIORITY + 1) ); } this.priority = priority; return this; } /** * set uncaughtExceptionHandler property. */ public ThreadFactoryBuilder uncaughtExceptionHandler( Thread.UncaughtExceptionHandler uncaughtExceptionHandler) { checkNullParameter(uncaughtExceptionHandler, "uncaughtExceptionHandler cannot be null."); this.uncaughtExceptionHandler = uncaughtExceptionHandler; return this; } /** * set daemon property. */ public ThreadFactoryBuilder daemon(boolean daemon) { this.daemon = daemon; return this; } /** * set customizeFactory property. */ public ThreadFactoryBuilder customizeFactory(ThreadFactory factory) { checkNullParameter(factory, "factory cannot be null."); this.customizeFactory = factory; return this; } /** * build thread factory. */ public ThreadFactory build() { ThreadFactory factory = customizeFactory == null ? Executors.defaultThreadFactory() : customizeFactory; final AtomicLong count = (nameFormat != null) ? new AtomicLong(0) : null; return r -> { Thread thread = factory.newThread(r); if (nameFormat != null) { thread.setName(format(nameFormat, count.getAndIncrement())); } if (priority != null) { thread.setPriority(priority); } if (uncaughtExceptionHandler != null) { thread.setUncaughtExceptionHandler(uncaughtExceptionHandler); } thread.setDaemon(daemon); return thread; }; } private String format(String format, Object... args) { return String.format(Locale.ROOT, format, args); } private void checkNullParameter(Object obj, String msg) { if (obj == null) { throw new IllegalArgumentException(msg); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/ThreadUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.slf4j.Logger; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; /** * Thread utils. * * @author liaochuntao */ public final class ThreadUtils { private ThreadUtils() { } private static final int THREAD_MULTIPLER = 2; /** * Sleep. * * @param millis sleep millisecond */ public static void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } public static void countDown(CountDownLatch latch) { Objects.requireNonNull(latch, "latch"); latch.countDown(); } /** * Await count down latch. * * @param latch count down latch */ public static void latchAwait(CountDownLatch latch) { try { latch.await(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /** * Await count down latch with timeout. * * @param latch count down latch * @param time timeout time * @param unit time unit */ public static void latchAwait(CountDownLatch latch, long time, TimeUnit unit) { try { latch.await(time, unit); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } /** * Through the number of cores, calculate the appropriate number of threads; 1.5-2 times the number of CPU cores. * * @return thread count */ public static int getSuitableThreadCount() { return getSuitableThreadCount(THREAD_MULTIPLER); } /** * Through the number of cores, calculate the appropriate number of threads. * * @param threadMultiple multiple time of cores * @return thread count */ public static int getSuitableThreadCount(int threadMultiple) { final int coreCount = PropertyUtils.getProcessorsCount(); int workerCount = 1; while (workerCount < coreCount * threadMultiple) { workerCount <<= 1; } return workerCount; } public static void shutdownThreadPool(ExecutorService executor) { shutdownThreadPool(executor, null); } /** * Shutdown thread pool. * * @param executor thread pool * @param logger logger */ public static void shutdownThreadPool(ExecutorService executor, Logger logger) { executor.shutdown(); int retry = 3; while (retry > 0) { retry--; try { if (executor.awaitTermination(100, TimeUnit.MILLISECONDS)) { return; } } catch (InterruptedException e) { executor.shutdownNow(); Thread.interrupted(); } catch (Throwable ex) { if (logger != null) { logger.error("ThreadPoolManager shutdown executor has error : ", ex); } } } executor.shutdownNow(); } public static void addShutdownHook(Runnable runnable) { Runtime.getRuntime().addShutdownHook(new Thread(runnable)); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/TlsTypeResolve.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import io.grpc.netty.shaded.io.netty.handler.ssl.SslProvider; /** * gRPC config for sdk. * * @author githubcheng2978 */ public class TlsTypeResolve { /** * JDK SSL is very slower than OPENSSL, recommend use openSSl. * * @param provider name of ssl provider. * @return SslProvider. */ public static SslProvider getSslProvider(String provider) { if (SslProvider.OPENSSL.name().equalsIgnoreCase(provider)) { return SslProvider.OPENSSL; } if (SslProvider.JDK.name().equalsIgnoreCase(provider)) { return SslProvider.JDK; } if (SslProvider.OPENSSL_REFCNT.name().equalsIgnoreCase(provider)) { return SslProvider.OPENSSL_REFCNT; } return SslProvider.OPENSSL; } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/TypeUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * type utils. * * @author zzq */ public class TypeUtils { private TypeUtils() { } /** * Create a parameterized type instance. * * @param raw raw class * @param typeArguments the types used for parameterization * @return {@link ParameterizedType} */ public static ParameterizedType parameterize(final Class raw, final Type... typeArguments) { checkParameterizeMethodParameter(raw, typeArguments); return new ParameterizedTypeImpl(raw, raw.getEnclosingClass(), typeArguments); } /** * Check parameterize method parameter. * * @param raw raw class * @param typeArguments the types used for parameterization */ private static void checkParameterizeMethodParameter(Class raw, final Type... typeArguments) { if (raw == null) { throw new NullPointerException("raw cannot be null"); } if (typeArguments == null) { throw new NullPointerException("typeArguments cannot be null"); } if (typeArguments.length != raw.getTypeParameters().length) { throw new IllegalArgumentException( String.format("invalid number of type parameters specified: expected %s, got %s", raw.getTypeParameters().length, typeArguments.length)); } for (int i = 0; i < typeArguments.length; i++) { if (typeArguments[i] == null) { throw new IllegalArgumentException("There can be no null in typeArguments"); } } } /** * ParameterizedType implementation class. */ private static final class ParameterizedTypeImpl implements ParameterizedType { /** * type. */ private final Class raw; /** * owner type to use, if any. */ private final Type useOwner; /** * formal type arguments.typeArguments */ private final Type[] typeArguments; private ParameterizedTypeImpl(final Class raw, final Type useOwner, final Type[] typeArguments) { this.raw = raw; this.useOwner = useOwner; this.typeArguments = typeArguments; } @Override public Type getRawType() { return raw; } @Override public Type getOwnerType() { return useOwner; } @Override public Type[] getActualTypeArguments() { return typeArguments.clone(); } @Override public String toString() { final StringBuilder buf = new StringBuilder(); buf.append(raw.getName()); buf.append('<'); buf.append(typeArguments[0].getTypeName()); for (int i = 1; i < typeArguments.length; i++) { buf.append(", "); buf.append(typeArguments[i].getTypeName()); } buf.append('>'); return buf.toString(); } } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/UuidUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.util.UUID; /** * UUID utils. * * @author nkorange */ public class UuidUtils { private UuidUtils() { } public static String generateUuid() { return UUID.randomUUID().toString(); } } ================================================ FILE: common/src/main/java/com/alibaba/nacos/common/utils/VersionUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import java.io.InputStream; import java.util.Comparator; import java.util.Objects; import java.util.Properties; /** * Version utils. * * @author xingxuechao on:2019/2/27 12:32 PM */ public class VersionUtils { private VersionUtils() { } public static String version; private static String clientVersion; /** * current version. */ public static final String VERSION_PLACEHOLDER = "${project.version}"; private static final String NACOS_VERSION_FILE = "nacos-version.txt"; static { try (InputStream in = VersionUtils.class.getClassLoader().getResourceAsStream(NACOS_VERSION_FILE)) { Properties props = new Properties(); props.load(in); String val = props.getProperty("version"); if (val != null && !VERSION_PLACEHOLDER.equals(val)) { version = val; clientVersion = "Nacos-Java-Client:v" + VersionUtils.version; } } catch (Exception e) { e.printStackTrace(); } } private static final Comparator STRING_COMPARATOR = String::compareTo; /** * compare two version who is latest. * * @param versionA version A, like x.y.z(-beta) * @param versionB version B, like x.y.z(-beta) * @return compare result */ public static int compareVersion(final String versionA, final String versionB) { final String[] sA = versionA.split("\\."); final String[] sB = versionB.split("\\."); int expectSize = 3; if (sA.length != expectSize || sB.length != expectSize) { throw new IllegalArgumentException("version must be like x.y.z(-beta)"); } int first = Objects.compare(sA[0], sB[0], STRING_COMPARATOR); if (first != 0) { return first; } int second = Objects.compare(sA[1], sB[1], STRING_COMPARATOR); if (second != 0) { return second; } return Objects.compare(sA[2].split("-")[0], sB[2].split("-")[0], STRING_COMPARATOR); } public static String getFullClientVersion() { return clientVersion; } } ================================================ FILE: common/src/main/resources/META-INF/services/com.alibaba.nacos.common.labels.LabelsCollector ================================================ # # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.common.labels.impl.DefaultLabelsCollector ================================================ FILE: common/src/main/resources/META-INF/services/com.alibaba.nacos.common.paramcheck.AbstractParamChecker ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.common.paramcheck.DefaultParamChecker ================================================ FILE: common/src/main/resources/META-INF/services/com.alibaba.nacos.common.pathencoder.PathEncoder ================================================ # # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.common.pathencoder.impl.WindowsEncoder ================================================ FILE: common/src/main/resources/nacos-version.txt ================================================ version=${project.version} ================================================ FILE: common/src/test/java/ClassUtilsTestMockClass.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 class ClassUtilsTestMockClass { } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/AppTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; public class AppTest { @Test void testApp() { assertTrue(true); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/ability/AbstractAbilityControlManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.ability; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class AbstractAbilityControlManagerTest { private AbstractAbilityControlManager abilityControlManager; private Subscriber mockSubscriber; private boolean isOn = true; private AssertionError assertionError; private boolean notified = false; @BeforeEach void setUp() throws Exception { mockSubscriber = new Subscriber() { @Override public void onEvent(AbstractAbilityControlManager.AbilityUpdateEvent event) { notified = true; try { assertEquals(AbilityKey.SERVER_FUZZY_WATCH, event.getAbilityKey()); assertEquals(isOn, event.isOn()); assertEquals(2, event.getAbilityTable().size()); assertEquals(isOn, event.getAbilityTable().get(AbilityKey.SERVER_FUZZY_WATCH.getName())); } catch (AssertionError error) { assertionError = error; } } @Override public Class subscribeType() { return AbstractAbilityControlManager.AbilityUpdateEvent.class; } }; abilityControlManager = new MockAbilityControlManager(); NotifyCenter.registerSubscriber(mockSubscriber); } @AfterEach void tearDown() throws Exception { NotifyCenter.deregisterSubscriber(mockSubscriber); assertionError = null; notified = false; } @Test void testEnableCurrentNodeAbility() throws InterruptedException { isOn = true; abilityControlManager.enableCurrentNodeAbility(AbilityKey.SERVER_FUZZY_WATCH); TimeUnit.MILLISECONDS.sleep(1100); assertTrue(notified); if (null != assertionError) { throw assertionError; } } @Test void testDisableCurrentNodeAbility() throws InterruptedException { isOn = false; abilityControlManager.disableCurrentNodeAbility(AbilityKey.SERVER_FUZZY_WATCH); TimeUnit.MILLISECONDS.sleep(1100); assertTrue(notified); if (null != assertionError) { throw assertionError; } } @Test void testIsCurrentNodeAbilityRunning() { assertEquals(AbilityStatus.SUPPORTED, abilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_FUZZY_WATCH)); assertEquals(AbilityStatus.NOT_SUPPORTED, abilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SERVER_DISTRIBUTED_LOCK)); assertEquals(AbilityStatus.UNKNOWN, abilityControlManager.isCurrentNodeAbilityRunning(AbilityKey.SDK_CLIENT_FUZZY_WATCH)); } @Test void testGetCurrentNodeAbilities() { Map actual = abilityControlManager.getCurrentNodeAbilities(AbilityMode.SERVER); assertEquals(2, actual.size()); assertTrue(actual.containsKey(AbilityKey.SERVER_FUZZY_WATCH.getName())); assertTrue(actual.containsKey(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName())); actual = abilityControlManager.getCurrentNodeAbilities(AbilityMode.SDK_CLIENT); assertTrue(actual.isEmpty()); } @Test void testGetPriority() { assertEquals(Integer.MIN_VALUE, abilityControlManager.getPriority()); } @Test void testInitFailed() { assertThrows(IllegalStateException.class, () -> { abilityControlManager = new AbstractAbilityControlManager() { @Override protected Map> initCurrentNodeAbilities() { Map abilities = Collections.singletonMap(AbilityKey.SDK_CLIENT_FUZZY_WATCH, true); return Collections.singletonMap(AbilityMode.SERVER, abilities); } @Override public int getPriority() { return 0; } }; }); } private static final class MockAbilityControlManager extends AbstractAbilityControlManager { @Override protected Map> initCurrentNodeAbilities() { Map abilities = new HashMap<>(2); abilities.put(AbilityKey.SERVER_FUZZY_WATCH, true); abilities.put(AbilityKey.SERVER_DISTRIBUTED_LOCK, false); return Collections.singletonMap(AbilityMode.SERVER, abilities); } @Override public int getPriority() { return Integer.MIN_VALUE; } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/ability/MockAbilityPostProcessor.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.ability; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.ability.initializer.AbilityPostProcessor; import java.util.Map; public class MockAbilityPostProcessor implements AbilityPostProcessor { @Override public void process(AbilityMode mode, Map abilities) { // do nothing. } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/ability/discover/HigherMockAbilityManager.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.ability.discover; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.common.ability.AbstractAbilityControlManager; import java.util.HashMap; import java.util.Map; public class HigherMockAbilityManager extends AbstractAbilityControlManager { @Override protected Map> initCurrentNodeAbilities() { Map abilities = new HashMap<>(); abilities.put(AbilityKey.SERVER_FUZZY_WATCH, true); Map> result = new HashMap<>(); result.put(AbilityMode.SERVER, abilities); return result; } @Override public int getPriority() { return 100; } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/ability/discover/LowerMockAbilityManager.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.ability.discover; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.common.ability.AbstractAbilityControlManager; import java.util.HashMap; import java.util.Map; public class LowerMockAbilityManager extends AbstractAbilityControlManager { @Override protected Map> initCurrentNodeAbilities() { Map abilities = new HashMap<>(); abilities.put(AbilityKey.SDK_CLIENT_FUZZY_WATCH, true); Map> result = new HashMap<>(); result.put(AbilityMode.SDK_CLIENT, abilities); return result; } @Override public int getPriority() { return 0; } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/ability/discover/NacosAbilityManagerHolderTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.ability.discover; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; @ExtendWith(MockitoExtension.class) class NacosAbilityManagerHolderTest { @BeforeEach void setUp() throws Exception { NacosAbilityManagerHolder.getInstance(); } @AfterEach void tearDown() throws Exception { Field abilityControlManagerField = NacosAbilityManagerHolder.class.getDeclaredField("abstractAbilityControlManager"); abilityControlManagerField.setAccessible(true); abilityControlManagerField.set(null, null); } @Test void testGetInstance() { assertNotNull(NacosAbilityManagerHolder.getInstance()); } @Test void testGetInstanceByType() { assertNotNull(NacosAbilityManagerHolder.getInstance(HigherMockAbilityManager.class)); } @Test void testGetInstanceByWrongType() { assertThrows(ClassCastException.class, () -> { assertNotNull(NacosAbilityManagerHolder.getInstance(LowerMockAbilityManager.class)); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/codec/Base64Test.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.codec; import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; class Base64Test { @Test void test() { String origin = "nacos"; String encoded = "bmFjb3M="; byte[] encodeBase64 = Base64.encodeBase64(origin.getBytes(StandardCharsets.UTF_8)); assertEquals(encoded, new String(encodeBase64)); byte[] decodeBase64 = Base64.decodeBase64(encoded.getBytes(StandardCharsets.UTF_8)); assertEquals(origin, new String(decodeBase64)); } @Test void testEncodeNullOrEmpty() { byte[] b1 = Base64.encodeBase64(null); assertNull(b1); byte[] b2 = Base64.encodeBase64(new byte[] {}); assertEquals(0, b2.length); } @Test void testDecodeNullOrEmpty() { byte[] b1 = Base64.decodeBase64(null); assertNull(b1); byte[] b2 = Base64.decodeBase64(new byte[] {}); assertEquals(0, b2.length); } @Test void testChunk() { String a = "very large characters to test chunk encoding and see if the result is expected or not"; byte[] b1 = Base64.encodeBase64(a.getBytes(StandardCharsets.UTF_8), false, false, Integer.MAX_VALUE); byte[] b2 = Base64.encodeBase64(a.getBytes(StandardCharsets.UTF_8), true, false, Integer.MAX_VALUE); String s1 = new String(b1); String s2 = new String(b2); assertEquals(s1, "dmVyeSBsYXJnZSBjaGFyYWN0ZXJzIHRvIHRlc3QgY2h1bmsgZW5jb2RpbmcgYW5kIHNlZSBpZiB0" + "aGUgcmVzdWx0IGlzIGV4cGVjdGVkIG9yIG5vdA=="); assertEquals(s2, "dmVyeSBsYXJnZSBjaGFyYWN0ZXJzIHRvIHRlc3QgY2h1bmsgZW5jb2RpbmcgYW5kIHNlZSBpZiB0" + "\r\n" + "aGUgcmVzdWx0IGlzIGV4cGVjdGVkIG9yIG5vdA==" + "\r\n"); byte[] c1 = Base64.decodeBase64(b1); byte[] c2 = Base64.decodeBase64(b2); String s3 = new String(c1); String s4 = new String(c2); assertEquals(a, s3); assertEquals(a, s4); } @Test void testUrlSafe() { String a = "aa~aa?"; byte[] b1 = Base64.encodeBase64(a.getBytes(StandardCharsets.UTF_8), false, false, Integer.MAX_VALUE); byte[] b2 = Base64.encodeBase64(a.getBytes(StandardCharsets.UTF_8), false, true, Integer.MAX_VALUE); String s1 = new String(b1); String s2 = new String(b2); assertEquals("YWF+YWE/", s1); assertEquals("YWF-YWE_", s2); byte[] c1 = Base64.decodeBase64(b1); byte[] c2 = Base64.decodeBase64(b2); String s3 = new String(c1); String s4 = new String(c2); assertEquals("aa~aa?", s3); assertEquals("aa~aa?", s4); } @Test void testEncodeOverMaxLength() { assertThrows(IllegalArgumentException.class, () -> { String a = "very large characters to test chunk encoding and see if the result is expected or not"; Base64.encodeBase64(a.getBytes(StandardCharsets.UTF_8), false, false, 10); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/event/ServerConfigChangeEventTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.event; import com.alibaba.nacos.common.notify.Event; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class ServerConfigChangeEventTest { @Test void test() { Event event = ServerConfigChangeEvent.newEvent(); assertTrue(event instanceof ServerConfigChangeEvent); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/executor/ExecutorFactoryTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.executor; import org.junit.jupiter.api.Test; import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadPoolExecutor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ExecutorFactoryTest { private final NameThreadFactory threadFactory = new NameThreadFactory("test"); @Test void test() { ExecutorService executorService; ThreadPoolExecutor threadPoolExecutor; executorService = ExecutorFactory.newSingleExecutorService(); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(1, threadPoolExecutor.getCorePoolSize()); assertEquals(1, threadPoolExecutor.getMaximumPoolSize()); assertNotEquals(threadFactory, threadPoolExecutor.getThreadFactory()); executorService = ExecutorFactory.newFixedExecutorService(10); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(10, threadPoolExecutor.getCorePoolSize()); assertEquals(10, threadPoolExecutor.getMaximumPoolSize()); assertNotEquals(threadFactory, threadPoolExecutor.getThreadFactory()); executorService = ExecutorFactory.newSingleExecutorService(threadFactory); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(1, threadPoolExecutor.getCorePoolSize()); assertEquals(1, threadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); executorService = ExecutorFactory.newFixedExecutorService(10, threadFactory); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(10, threadPoolExecutor.getCorePoolSize()); assertEquals(10, threadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; executorService = ExecutorFactory.newSingleScheduledExecutorService(threadFactory); assertTrue(executorService instanceof ScheduledThreadPoolExecutor); scheduledThreadPoolExecutor = (ScheduledThreadPoolExecutor) executorService; assertEquals(1, scheduledThreadPoolExecutor.getCorePoolSize()); assertEquals(Integer.MAX_VALUE, scheduledThreadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); executorService = ExecutorFactory.newScheduledExecutorService(10, threadFactory); assertTrue(executorService instanceof ScheduledThreadPoolExecutor); scheduledThreadPoolExecutor = (ScheduledThreadPoolExecutor) executorService; assertEquals(10, scheduledThreadPoolExecutor.getCorePoolSize()); assertEquals(Integer.MAX_VALUE, scheduledThreadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); threadPoolExecutor = ExecutorFactory.newCustomerThreadExecutor(10, 20, 1000, threadFactory); assertEquals(10, threadPoolExecutor.getCorePoolSize()); assertEquals(20, threadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); } @Test void testManaged() { String testGroup = "test"; ExecutorService executorService; ThreadPoolExecutor threadPoolExecutor; ThreadPoolManager manager = ExecutorFactory.Managed.getThreadPoolManager(); final Map>> resourcesManager = manager.getResourcesManager(); executorService = ExecutorFactory.Managed.newSingleExecutorService(testGroup); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(1, threadPoolExecutor.getCorePoolSize()); assertEquals(1, threadPoolExecutor.getMaximumPoolSize()); assertNotEquals(threadFactory, threadPoolExecutor.getThreadFactory()); assertEquals(1, resourcesManager.get("nacos").get(testGroup).size()); executorService = ExecutorFactory.Managed.newFixedExecutorService(testGroup, 10); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(10, threadPoolExecutor.getCorePoolSize()); assertEquals(10, threadPoolExecutor.getMaximumPoolSize()); assertNotEquals(threadFactory, threadPoolExecutor.getThreadFactory()); assertEquals(2, resourcesManager.get("nacos").get(testGroup).size()); executorService = ExecutorFactory.Managed.newSingleExecutorService(testGroup, threadFactory); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(1, threadPoolExecutor.getCorePoolSize()); assertEquals(1, threadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); assertEquals(3, resourcesManager.get("nacos").get(testGroup).size()); executorService = ExecutorFactory.Managed.newFixedExecutorService(testGroup, 10, threadFactory); assertTrue(executorService instanceof ThreadPoolExecutor); threadPoolExecutor = (ThreadPoolExecutor) executorService; assertEquals(10, threadPoolExecutor.getCorePoolSize()); assertEquals(10, threadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); assertEquals(4, resourcesManager.get("nacos").get(testGroup).size()); ScheduledThreadPoolExecutor scheduledThreadPoolExecutor; executorService = ExecutorFactory.Managed.newSingleScheduledExecutorService(testGroup, threadFactory); assertTrue(executorService instanceof ScheduledThreadPoolExecutor); scheduledThreadPoolExecutor = (ScheduledThreadPoolExecutor) executorService; assertEquals(1, scheduledThreadPoolExecutor.getCorePoolSize()); assertEquals(Integer.MAX_VALUE, scheduledThreadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); assertEquals(5, resourcesManager.get("nacos").get(testGroup).size()); executorService = ExecutorFactory.Managed.newScheduledExecutorService(testGroup, 10, threadFactory); assertTrue(executorService instanceof ScheduledThreadPoolExecutor); scheduledThreadPoolExecutor = (ScheduledThreadPoolExecutor) executorService; assertEquals(10, scheduledThreadPoolExecutor.getCorePoolSize()); assertEquals(Integer.MAX_VALUE, scheduledThreadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); assertEquals(6, resourcesManager.get("nacos").get(testGroup).size()); threadPoolExecutor = ExecutorFactory.Managed.newCustomerThreadExecutor(testGroup, 10, 20, 1000, threadFactory); assertEquals(10, threadPoolExecutor.getCorePoolSize()); assertEquals(20, threadPoolExecutor.getMaximumPoolSize()); assertEquals(threadFactory, threadPoolExecutor.getThreadFactory()); assertEquals(7, resourcesManager.get("nacos").get(testGroup).size()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/executor/NameThreadFactoryTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.executor; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class NameThreadFactoryTest { @Test void test() { NameThreadFactory threadFactory = new NameThreadFactory("test"); Thread t1 = threadFactory.newThread(() -> { }); Thread t2 = threadFactory.newThread(() -> { }); assertEquals("test.0", t1.getName()); assertEquals("test.1", t2.getName()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/executor/ThreadPoolManagerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.executor; import org.junit.jupiter.api.Test; import java.util.concurrent.ExecutorService; 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 ThreadPoolManagerTest { @Test void test() { ThreadPoolManager manager = ThreadPoolManager.getInstance(); ExecutorService executor = ExecutorFactory.newSingleExecutorService(); String namespace = "test"; String group = "test"; manager.register(namespace, group, executor); assertTrue(manager.getResourcesManager().containsKey(namespace)); assertEquals(1, manager.getResourcesManager().get(namespace).get(group).size()); manager.register(namespace, group, ExecutorFactory.newSingleExecutorService()); assertEquals(2, manager.getResourcesManager().get(namespace).get(group).size()); manager.destroy(namespace, group); assertFalse(manager.getResourcesManager().get(namespace).containsKey(group)); manager.register(namespace, group, executor); manager.destroy(namespace); assertFalse(manager.getResourcesManager().containsKey(namespace)); manager.register(namespace, group, executor); manager.deregister(namespace, group, ExecutorFactory.newSingleExecutorService()); assertEquals(1, manager.getResourcesManager().get(namespace).get(group).size()); manager.deregister(namespace, group, executor); assertEquals(0, manager.getResourcesManager().get(namespace).get(group).size()); manager.register(namespace, group, executor); manager.deregister(namespace, group); assertFalse(manager.getResourcesManager().get(namespace).containsKey(group)); manager.register(namespace, group, executor); manager.register(namespace, group, ExecutorFactory.newSingleExecutorService()); ThreadPoolManager.shutdown(); assertFalse(manager.getResourcesManager().containsKey(namespace)); manager.destroy(namespace); manager.destroy(namespace, group); assertFalse(manager.getResourcesManager().containsKey(namespace)); } @Test void testDestroyWithNull() { ThreadPoolManager.getInstance().register("t", "g", ExecutorFactory.newFixedExecutorService(1)); try { ThreadPoolManager.getInstance().destroy("null"); assertTrue(ThreadPoolManager.getInstance().getResourcesManager().containsKey("t")); ThreadPoolManager.getInstance().destroy("null", "g"); assertTrue(ThreadPoolManager.getInstance().getResourcesManager().containsKey("t")); } finally { ThreadPoolManager.getInstance().destroy("t", "g"); } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/AbstractApacheHttpClientFactoryTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.client.request.DefaultHttpClientRequest; import com.alibaba.nacos.common.http.client.request.HttpClientRequest; 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.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(MockitoExtension.class) class AbstractApacheHttpClientFactoryTest { @Mock private Logger logger; @BeforeEach void setUp() throws Exception { } @AfterEach void tearDown() throws Exception { } @Test void testCreateNacosRestTemplate() throws NoSuchFieldException, IllegalAccessException { HttpClientFactory factory = new AbstractApacheHttpClientFactory() { @Override protected HttpClientConfig buildHttpClientConfig() { return HttpClientConfig.builder().build(); } @Override protected Logger assignLogger() { return logger; } }; NacosRestTemplate template = factory.createNacosRestTemplate(); assertNotNull(template); Field field = NacosRestTemplate.class.getDeclaredField("requestClient"); field.setAccessible(true); HttpClientRequest requestClient = (HttpClientRequest) field.get(template); assertTrue(requestClient instanceof DefaultHttpClientRequest); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/AbstractHttpClientFactoryTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.tls.TlsSystemConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import static org.junit.jupiter.api.Assertions.assertNotNull; @ExtendWith(MockitoExtension.class) class AbstractHttpClientFactoryTest { @Mock private Logger logger; @AfterEach void tearDown() throws Exception { TlsSystemConfig.tlsEnable = false; } @Test void testCreateNacosRestTemplateWithSsl() throws Exception { TlsSystemConfig.tlsEnable = true; HttpClientFactory httpClientFactory = new DefaultHttpClientFactory(logger); NacosRestTemplate nacosRestTemplate = httpClientFactory.createNacosRestTemplate(); assertNotNull(nacosRestTemplate); } @Test void testCreateNacosAsyncRestTemplate() { HttpClientFactory httpClientFactory = new AbstractHttpClientFactory() { @Override protected HttpClientConfig buildHttpClientConfig() { return HttpClientConfig.builder().setMaxConnTotal(10).setMaxConnPerRoute(10).build(); } @Override protected Logger assignLogger() { return logger; } }; NacosAsyncRestTemplate nacosRestTemplate = httpClientFactory.createNacosAsyncRestTemplate(); assertNotNull(nacosRestTemplate); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/BaseHttpMethodTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class BaseHttpMethodTest { @Test void testHttpGet() { BaseHttpMethod method = BaseHttpMethod.GET; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("GET", request.getMethod()); } @Test void testHttpGetLarge() { BaseHttpMethod method = BaseHttpMethod.GET_LARGE; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("GET", request.getMethod()); } @Test void testHttpPost() { BaseHttpMethod method = BaseHttpMethod.POST; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("POST", request.getMethod()); } @Test void testHttpPut() { BaseHttpMethod method = BaseHttpMethod.PUT; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("PUT", request.getMethod()); } @Test void testHttpDelete() { BaseHttpMethod method = BaseHttpMethod.DELETE; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("DELETE", request.getMethod()); } @Test void testHttpDeleteLarge() { BaseHttpMethod method = BaseHttpMethod.DELETE_LARGE; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("DELETE", request.getMethod()); } @Test void testHttpHead() { BaseHttpMethod method = BaseHttpMethod.HEAD; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("HEAD", request.getMethod()); } @Test void testHttpTrace() { BaseHttpMethod method = BaseHttpMethod.TRACE; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("TRACE", request.getMethod()); } @Test void testHttpPatch() { BaseHttpMethod method = BaseHttpMethod.PATCH; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("PATCH", request.getMethod()); } @Test void testHttpOptions() { BaseHttpMethod method = BaseHttpMethod.OPTIONS; HttpUriRequestBase request = method.init("http://example.com"); assertEquals("TRACE", request.getMethod()); } @Test void testSourceOf() { BaseHttpMethod method = BaseHttpMethod.sourceOf("GET"); assertEquals(BaseHttpMethod.GET, method); } @Test void testSourceOfNotFound() { assertThrows(IllegalArgumentException.class, () -> { BaseHttpMethod.sourceOf("Not Found"); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/HttpClientBeanHolderTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.slf4j.Logger; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class HttpClientBeanHolderTest { private Map cachedRestTemplateMap; private Map restMap; private Map cachedAsyncRestTemplateMap; private Map restAsyncMap; @Mock private NacosRestTemplate mockRestTemplate; @Mock private NacosAsyncRestTemplate mockAsyncRestTemplate; @Mock private HttpClientFactory mockFactory; @BeforeEach void setUp() throws Exception { cachedRestTemplateMap = new HashMap<>(); cachedAsyncRestTemplateMap = new HashMap<>(); restMap = (Map) getCachedMap("SINGLETON_REST"); restAsyncMap = (Map) getCachedMap("SINGLETON_ASYNC_REST"); cachedRestTemplateMap.putAll(restMap); cachedAsyncRestTemplateMap.putAll(restAsyncMap); restMap.clear(); restAsyncMap.clear(); when(mockFactory.createNacosRestTemplate()).thenReturn(mockRestTemplate); when(mockFactory.createNacosAsyncRestTemplate()).thenReturn(mockAsyncRestTemplate); } @AfterEach void tearDown() throws Exception { restMap.putAll(cachedRestTemplateMap); restAsyncMap.putAll(cachedAsyncRestTemplateMap); cachedRestTemplateMap.clear(); cachedAsyncRestTemplateMap.clear(); } private Object getCachedMap(String mapName) throws NoSuchFieldException, IllegalAccessException { Field field = HttpClientBeanHolder.class.getDeclaredField(mapName); field.setAccessible(true); return field.get(HttpClientBeanHolder.class); } @Test void testGetNacosRestTemplateWithDefault() { assertTrue(restMap.isEmpty()); NacosRestTemplate actual = HttpClientBeanHolder.getNacosRestTemplate((Logger) null); assertEquals(1, restMap.size()); NacosRestTemplate duplicateGet = HttpClientBeanHolder.getNacosRestTemplate((Logger) null); assertEquals(1, restMap.size()); assertEquals(actual, duplicateGet); } @Test void testGetNacosRestTemplateForNullFactory() { assertThrows(NullPointerException.class, () -> { HttpClientBeanHolder.getNacosRestTemplate((HttpClientFactory) null); }); } @Test void testGetNacosRestTemplateWithCustomFactory() { assertTrue(restMap.isEmpty()); HttpClientBeanHolder.getNacosRestTemplate((Logger) null); assertEquals(1, restMap.size()); NacosRestTemplate actual = HttpClientBeanHolder.getNacosRestTemplate(mockFactory); assertEquals(2, restMap.size()); assertEquals(mockRestTemplate, actual); } @Test void testGetNacosAsyncRestTemplateWithDefault() { assertTrue(restAsyncMap.isEmpty()); NacosAsyncRestTemplate actual = HttpClientBeanHolder.getNacosAsyncRestTemplate((Logger) null); assertEquals(1, restAsyncMap.size()); NacosAsyncRestTemplate duplicateGet = HttpClientBeanHolder.getNacosAsyncRestTemplate((Logger) null); assertEquals(1, restAsyncMap.size()); assertEquals(actual, duplicateGet); } @Test void testGetNacosAsyncRestTemplateForNullFactory() { assertThrows(NullPointerException.class, () -> { HttpClientBeanHolder.getNacosAsyncRestTemplate((HttpClientFactory) null); }); } @Test void testGetNacosAsyncRestTemplateWithCustomFactory() { assertTrue(restAsyncMap.isEmpty()); HttpClientBeanHolder.getNacosAsyncRestTemplate((Logger) null); assertEquals(1, restAsyncMap.size()); NacosAsyncRestTemplate actual = HttpClientBeanHolder.getNacosAsyncRestTemplate(mockFactory); assertEquals(2, restAsyncMap.size()); assertEquals(mockAsyncRestTemplate, actual); } @Test void shutdown() throws Exception { HttpClientBeanHolder.getNacosRestTemplate((Logger) null); HttpClientBeanHolder.getNacosAsyncRestTemplate((Logger) null); assertEquals(1, restMap.size()); assertEquals(1, restAsyncMap.size()); HttpClientBeanHolder.shutdown(DefaultHttpClientFactory.class.getName()); assertEquals(0, restMap.size()); assertEquals(0, restAsyncMap.size()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/HttpClientConfigTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; class HttpClientConfigTest { @Test void testGetConTimeOutMillis() { HttpClientConfig config = HttpClientConfig.builder().setConTimeOutMillis(1000).build(); assertEquals(1000, config.getConTimeOutMillis()); } @Test void testGetReadTimeOutMillis() { HttpClientConfig config = HttpClientConfig.builder().setReadTimeOutMillis(2000).build(); assertEquals(2000, config.getReadTimeOutMillis()); } @Test void testGetConnTimeToLive() { HttpClientConfig config = HttpClientConfig.builder().setConnectionTimeToLive(3000, TimeUnit.MILLISECONDS).build(); assertEquals(3000, config.getConnTimeToLive()); } @Test void testGetConnTimeToLiveTimeUnit() { HttpClientConfig config = HttpClientConfig.builder().setConnectionTimeToLive(4000, TimeUnit.SECONDS).build(); assertEquals(TimeUnit.SECONDS, config.getConnTimeToLiveTimeUnit()); } @Test void testGetConnectionRequestTimeout() { HttpClientConfig config = HttpClientConfig.builder().setConnectionRequestTimeout(5000).build(); assertEquals(5000, config.getConnectionRequestTimeout()); } @Test void testGetMaxRedirects() { HttpClientConfig config = HttpClientConfig.builder().setMaxRedirects(60).build(); assertEquals(60, config.getMaxRedirects()); } @Test void testGetMaxConnTotal() { HttpClientConfig config = HttpClientConfig.builder().setMaxConnTotal(70).build(); assertEquals(70, config.getMaxConnTotal()); } @Test void testGetMaxConnPerRoute() { HttpClientConfig config = HttpClientConfig.builder().setMaxConnPerRoute(80).build(); assertEquals(80, config.getMaxConnPerRoute()); } @Test void testGetContentCompressionEnabled() { HttpClientConfig config = HttpClientConfig.builder().setContentCompressionEnabled(false).build(); assertFalse(config.getContentCompressionEnabled()); } @Test void testGetIoThreadCount() { HttpClientConfig config = HttpClientConfig.builder().setIoThreadCount(90).build(); assertEquals(90, config.getIoThreadCount()); } @Test void testGetUserAgent() { HttpClientConfig config = HttpClientConfig.builder().setUserAgent("testUserAgent").build(); assertEquals("testUserAgent", config.getUserAgent()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/HttpRestResultTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.common.http.param.Header; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class HttpRestResultTest { @Test void testSetHeader() { HttpRestResult result = new HttpRestResult<>(); result.setData("test data"); Header header = Header.newInstance(); result.setHeader(header); assertEquals(header, result.getHeader()); assertEquals("test data", result.getData()); } @Test void testFullConstructor() { Header header = Header.newInstance(); HttpRestResult result = new HttpRestResult<>(header, 200, "test data", "message"); assertEquals(header, result.getHeader()); assertEquals("test data", result.getData()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/HttpUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.VersionUtils; import org.apache.hc.client5.http.ConnectTimeoutException; import org.apache.hc.client5.http.classic.methods.HttpDelete; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.core5.http.HttpEntity; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeoutException; 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.assertNotEquals; 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.mockito.Mockito.mock; @ExtendWith(MockitoExtension.class) class HttpUtilsTest { String exceptUrl = "http://127.0.0.1:8080/v1/api/test"; @Test void testBuildHttpUrl1() { String targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1/api/test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "v1/api/test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1", "/api/test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1", "/api", "/test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1", "/api/", "/test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1", "", "/api/", "/test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1", "", null, "/api/", "/test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1", "/api/", "test"); assertEquals(exceptUrl, targetUrl); targetUrl = HttpUtils.buildUrl(true, "127.0.0.1:8080", "/v1", "", null, "/api/", "/test"); assertEquals("https://127.0.0.1:8080/v1/api/test", targetUrl); } @Test void testBuildHttpUrl2() { assertThrows(IllegalArgumentException.class, () -> { String targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "//v1/api/test"); assertNotEquals(exceptUrl, targetUrl); }); } @Test void testBuildHttpUrl3() { assertThrows(IllegalArgumentException.class, () -> { String targetUrl = HttpUtils.buildUrl(false, "127.0.0.1:8080", "/v1", "/api//", "test"); assertNotEquals(exceptUrl, targetUrl); }); } @Test void testInitRequestHeader() { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); Header header = Header.newInstance(); header.addParam("k", "v"); HttpUtils.initRequestHeader(httpRequest, header); org.apache.hc.core5.http.Header[] headers = httpRequest.getHeaders("k"); assertEquals(1, headers.length); assertEquals("k", headers[0].getName()); assertEquals("v", headers[0].getValue()); } @Test void testInitRequestEntity1() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); Header header = Header.newInstance(); header.addParam(HttpHeaderConsts.CONTENT_TYPE, "text/html"); HttpUtils.initRequestEntity(httpRequest, new byte[] {0, 1, 0, 1}, header); HttpEntity entity = httpRequest.getEntity(); InputStream contentStream = entity.getContent(); byte[] bytes = new byte[contentStream.available()]; contentStream.read(bytes); assertArrayEquals(new byte[] {0, 1, 0, 1}, bytes); assertEquals("text/html; charset=UTF-8", entity.getContentType()); } @Test void testInitRequestEntity2() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); Header header = Header.newInstance(); header.addParam(HttpHeaderConsts.CONTENT_TYPE, "text/html"); HttpUtils.initRequestEntity(httpRequest, Collections.singletonMap("k", "v"), header); HttpEntity entity = httpRequest.getEntity(); InputStream contentStream = entity.getContent(); byte[] bytes = new byte[contentStream.available()]; contentStream.read(bytes); assertEquals("{\"k\":\"v\"}", new String(bytes, Constants.ENCODE)); assertEquals("text/html; charset=UTF-8", entity.getContentType()); } @Test void testInitRequestEntity3() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); Header header = Header.newInstance(); header.addParam(HttpHeaderConsts.CONTENT_TYPE, "text/html"); HttpUtils.initRequestEntity(httpRequest, "common text", header); HttpEntity entity = httpRequest.getEntity(); InputStream contentStream = entity.getContent(); byte[] bytes = new byte[contentStream.available()]; contentStream.read(bytes); assertEquals("common text", new String(bytes, Constants.ENCODE)); assertEquals("text/html; charset=UTF-8", entity.getContentType()); } @Test void testInitRequestEntity4() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); HttpUtils.initRequestEntity(httpRequest, null, null); // nothing change assertEquals(new BaseHttpMethod.HttpGetWithEntity("").getEntity(), httpRequest.getEntity()); // assertArrayEquals(new BaseHttpMethod.HttpGetWithEntity("").getAllHeaders(), httpRequest.getAllHeaders()); } @Test void testInitRequestEntity5() throws Exception { HttpDelete httpDelete = new HttpDelete(""); HttpUtils.initRequestEntity(httpDelete, null, null); // nothing change assertEquals(new HttpDelete("").getMethod(), httpDelete.getMethod()); // assertArrayEquals(new HttpDelete("").getAllHeaders(), httpDelete.getAllHeaders()); } @Test void testInitRequestFromEntity1() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); HttpUtils.initRequestFromEntity(httpRequest, Collections.singletonMap("k", "v"), "UTF-8"); HttpEntity entity = httpRequest.getEntity(); InputStream contentStream = entity.getContent(); byte[] bytes = new byte[contentStream.available()]; contentStream.read(bytes); assertEquals("k=v", new String(bytes, StandardCharsets.UTF_8)); } @Test void testInitRequestFromEntity2() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); HttpUtils.initRequestFromEntity(httpRequest, null, "UTF-8"); // nothing change assertEquals(new BaseHttpMethod.HttpGetWithEntity("").getEntity(), httpRequest.getEntity()); } @Test void testInitRequestFromEntity3() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); HttpUtils.initRequestFromEntity(httpRequest, Collections.emptyMap(), "UTF-8"); // nothing change assertEquals(new BaseHttpMethod.HttpGetWithEntity("").getEntity(), httpRequest.getEntity()); } @Test void testInitRequestFromEntity4() throws Exception { BaseHttpMethod.HttpGetWithEntity httpRequest = new BaseHttpMethod.HttpGetWithEntity(""); HttpUtils.initRequestFromEntity(mock(HttpUriRequestBase.class), Collections.emptyMap(), "UTF-8"); // nothing change assertEquals(new BaseHttpMethod.HttpGetWithEntity("").getEntity(), httpRequest.getEntity()); } @Test void testInitRequestFromEntity5() throws Exception { HttpDelete httpDelete = new HttpDelete(""); HttpUtils.initRequestFromEntity(httpDelete, Collections.singletonMap("k", "v"), "UTF-8"); // nothing change assertEquals(new HttpDelete("").getMethod(), httpDelete.getMethod()); assertArrayEquals(new HttpDelete("").getHeaders(), httpDelete.getHeaders()); } @Test void testTranslateParameterMap() throws Exception { Map map = Collections.singletonMap("K", new String[] {"V1", "V2"}); Map resultMap = HttpUtils.translateParameterMap(map); assertEquals(Collections.singletonMap("K", "V1"), resultMap); } @Test void testDecode() throws UnsupportedEncodingException { // % - %25, { - %7B, } - %7D assertEquals("{k,v}", HttpUtils.decode("%7Bk,v%7D", "UTF-8")); assertEquals("{k,v}", HttpUtils.decode("%257Bk,v%257D", "UTF-8")); } @Test void testEncodingParamsMapWithNullOrEmpty() throws UnsupportedEncodingException { assertNull(HttpUtils.encodingParams((Map) null, "UTF-8")); assertNull(HttpUtils.encodingParams(Collections.emptyMap(), "UTF-8")); } @Test void testEncodingParamsMap() throws UnsupportedEncodingException { Map params = new LinkedHashMap<>(); params.put("a", ""); params.put("b", "x"); params.put("uriChar", "="); params.put("chinese", "测试"); assertEquals("b=x&uriChar=%3D&chinese=%E6%B5%8B%E8%AF%95&", HttpUtils.encodingParams(params, "UTF-8")); } @Test void testEncodingParamsListWithNull() throws UnsupportedEncodingException { assertNull(HttpUtils.encodingParams((List) null, "UTF-8")); } @Test void testEncodingParamsList() throws UnsupportedEncodingException { List params = new LinkedList<>(); params.add("a"); params.add(""); params.add("b"); params.add("x"); params.add("uriChar"); params.add("="); params.add("chinese"); params.add("测试"); assertEquals("a=&b=x&uriChar=%3D&chinese=%E6%B5%8B%E8%AF%95", HttpUtils.encodingParams(params, "UTF-8")); } @Test void testBuildUriForEmptyQuery() throws URISyntaxException { URI actual = HttpUtils.buildUri("www.aliyun.com", null); assertEquals("www.aliyun.com", actual.toString()); actual = HttpUtils.buildUri("www.aliyun.com", new Query()); assertEquals("www.aliyun.com", actual.toString()); } @Test void testBuildUri() throws URISyntaxException { Query query = new Query(); query.addParam("a", ""); query.addParam("b", "x"); query.addParam("uriChar", "="); query.addParam("chinese", "测试"); URI actual = HttpUtils.buildUri("www.aliyun.com", query); assertEquals("www.aliyun.com?" + query.toQueryUrl(), actual.toString()); } @Test void testIsTimeoutException() { assertFalse(HttpUtils.isTimeoutException(new NacosRuntimeException(0))); assertTrue(HttpUtils.isTimeoutException(new TimeoutException())); assertTrue(HttpUtils.isTimeoutException(new SocketTimeoutException())); assertTrue(HttpUtils.isTimeoutException(new ConnectTimeoutException(""))); assertTrue(HttpUtils.isTimeoutException(new NacosRuntimeException(0, new TimeoutException()))); } @Test void testBuilderHeader() { Header header = HttpUtils.builderHeader("Test"); assertNotNull(header); assertEquals(header.getValue(HttpHeaderConsts.CLIENT_VERSION_HEADER), VersionUtils.version); assertEquals(header.getValue(HttpHeaderConsts.USER_AGENT_HEADER), VersionUtils.getFullClientVersion()); assertEquals("gzip,deflate,sdch", header.getValue(HttpHeaderConsts.ACCEPT_ENCODING)); assertEquals("Keep-Alive", header.getValue(HttpHeaderConsts.CONNECTION)); assertNotNull(header.getValue(HttpHeaderConsts.REQUEST_ID)); assertEquals("Test", header.getValue(HttpHeaderConsts.REQUEST_MODULE)); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/AbstractNacosRestTemplateTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.http.client.handler.BeanResponseHandler; import com.alibaba.nacos.common.http.client.handler.ResponseHandler; import com.alibaba.nacos.common.http.client.handler.RestResultResponseHandler; import com.alibaba.nacos.common.http.client.handler.StringResponseHandler; import com.alibaba.nacos.common.model.RestResult; 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.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import java.lang.reflect.Type; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(MockitoExtension.class) class AbstractNacosRestTemplateTest { MockNacosRestTemplate restTemplate; @Mock private ResponseHandler mockResponseHandler; @BeforeEach void setUp() throws Exception { restTemplate = new MockNacosRestTemplate(null); restTemplate.registerResponseHandler(MockNacosRestTemplate.class.getName(), mockResponseHandler); } @Test void testSelectResponseHandlerForNull() { assertTrue(restTemplate.testFindResponseHandler(null) instanceof StringResponseHandler); } @Test void testSelectResponseHandlerForRestResult() { assertTrue(restTemplate.testFindResponseHandler(RestResult.class) instanceof RestResultResponseHandler); } @Test void testSelectResponseHandlerForDefault() { assertTrue(restTemplate.testFindResponseHandler(AbstractNacosRestTemplateTest.class) instanceof BeanResponseHandler); } @Test void testSelectResponseHandlerForCustom() { assertEquals(mockResponseHandler, restTemplate.testFindResponseHandler(MockNacosRestTemplate.class)); } private static class MockNacosRestTemplate extends AbstractNacosRestTemplate { public MockNacosRestTemplate(Logger logger) { super(logger); } private ResponseHandler testFindResponseHandler(Type responseType) { return super.selectResponseHandler(responseType); } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/InterceptingHttpClientRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.http.client.request.HttpClientRequest; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RequestHttpEntity; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.net.URI; import java.util.LinkedList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class InterceptingHttpClientRequestTest { InterceptingHttpClientRequest clientRequest; @Mock private HttpClientRequest httpClientRequest; @Mock private HttpClientRequestInterceptor interceptor; @Mock private HttpClientResponse interceptorResponse; @Mock private HttpClientResponse httpClientResponse; @BeforeEach void setUp() throws Exception { List interceptorList = new LinkedList<>(); interceptorList.add(interceptor); clientRequest = new InterceptingHttpClientRequest(httpClientRequest, interceptorList.listIterator()); when(interceptor.intercept()).thenReturn(interceptorResponse); when(httpClientRequest.execute(any(), any(), any())).thenReturn(httpClientResponse); } @AfterEach void tearDown() throws Exception { clientRequest.close(); } @Test void testExecuteIntercepted() throws Exception { when(interceptor.isIntercept(any(), any(), any())).thenReturn(true); HttpClientResponse response = clientRequest.execute(URI.create("http://example.com"), "GET", new RequestHttpEntity(Header.EMPTY, Query.EMPTY)); assertEquals(interceptorResponse, response); } @Test void testExecuteNotIntercepted() throws Exception { HttpClientResponse response = clientRequest.execute(URI.create("http://example.com"), "GET", new RequestHttpEntity(Header.EMPTY, Query.EMPTY)); assertEquals(httpClientResponse, response); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/NacosAsyncRestTemplateTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.Callback; import com.alibaba.nacos.common.http.client.request.AsyncHttpClientRequest; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.http.param.Query; 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.junit.jupiter.MockitoExtension; import org.slf4j.Logger; import java.util.HashMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NacosAsyncRestTemplateTest { private static final String TEST_URL = "http://127.0.0.1:8848/nacos/test"; @Mock private AsyncHttpClientRequest requestClient; @Mock private Logger logger; @Mock private Callback mockCallback; private NacosAsyncRestTemplate restTemplate; @BeforeEach void setUp() throws Exception { restTemplate = new NacosAsyncRestTemplate(logger, requestClient); when(logger.isDebugEnabled()).thenReturn(true); } @AfterEach void tearDown() throws Exception { restTemplate.close(); } @Test void testGet() throws Exception { restTemplate.get(TEST_URL, Header.EMPTY, Query.EMPTY, String.class, mockCallback); verify(requestClient).execute(any(), eq("GET"), any(), any(), eq(mockCallback)); } @Test void testGetWithException() throws Exception { doThrow(new RuntimeException("test")).when(requestClient).execute(any(), any(), any(), any(), any()); restTemplate.get(TEST_URL, Header.EMPTY, Query.EMPTY, String.class, mockCallback); verify(requestClient).execute(any(), eq("GET"), any(), any(), eq(mockCallback)); verify(mockCallback).onError(any(RuntimeException.class)); } @Test void testGetLarge() throws Exception { restTemplate.getLarge(TEST_URL, Header.EMPTY, Query.EMPTY, new Object(), String.class, mockCallback); verify(requestClient).execute(any(), eq("GET-LARGE"), any(), any(), eq(mockCallback)); } @Test void testDeleteWithBody() throws Exception { restTemplate.delete(TEST_URL, Header.EMPTY, Query.EMPTY, String.class, mockCallback); verify(requestClient).execute(any(), eq("DELETE"), any(), any(), eq(mockCallback)); } @Test void testDeleteLarge() throws Exception { restTemplate.delete(TEST_URL, Header.EMPTY, "body", String.class, mockCallback); verify(requestClient).execute(any(), eq("DELETE_LARGE"), any(), any(), eq(mockCallback)); } @Test void testPut() throws Exception { restTemplate.put(TEST_URL, Header.EMPTY, Query.EMPTY, "body", String.class, mockCallback); verify(requestClient).execute(any(), eq("PUT"), any(), any(), eq(mockCallback)); } @Test void testPutJson() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.putJson(TEST_URL, header, "body", String.class, mockCallback); verify(requestClient).execute(any(), eq("PUT"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPutJsonWithQuery() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.putJson(TEST_URL, header, Query.EMPTY, "body", String.class, mockCallback); verify(requestClient).execute(any(), eq("PUT"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPutForm() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.putForm(TEST_URL, header, new HashMap<>(), String.class, mockCallback); verify(requestClient).execute(any(), eq("PUT"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPutFormWithQuery() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.putForm(TEST_URL, header, Query.EMPTY, new HashMap<>(), String.class, mockCallback); verify(requestClient).execute(any(), eq("PUT"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPost() throws Exception { restTemplate.post(TEST_URL, Header.EMPTY, Query.EMPTY, "body", String.class, mockCallback); verify(requestClient).execute(any(), eq("POST"), any(), any(), eq(mockCallback)); } @Test void testPostJson() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.postJson(TEST_URL, header, "body", String.class, mockCallback); verify(requestClient).execute(any(), eq("POST"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPostJsonWithQuery() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.postJson(TEST_URL, header, Query.EMPTY, "body", String.class, mockCallback); verify(requestClient).execute(any(), eq("POST"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPostForm() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.postForm(TEST_URL, header, new HashMap<>(), String.class, mockCallback); verify(requestClient).execute(any(), eq("POST"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPostFormWithQuery() throws Exception { Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); restTemplate.postForm(TEST_URL, header, Query.EMPTY, new HashMap<>(), String.class, mockCallback); verify(requestClient).execute(any(), eq("POST"), any(), any(), eq(mockCallback)); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/NacosRestTemplateTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.request.HttpClientRequest; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.http.param.Query; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.slf4j.Logger; import java.io.ByteArrayInputStream; import java.util.Collections; import java.util.HashMap; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class NacosRestTemplateTest { @Mock private HttpClientRequest requestClient; @Mock private Logger logger; @Mock private HttpClientResponse mockResponse; @Mock private HttpClientRequestInterceptor interceptor; private NacosRestTemplate restTemplate; @BeforeEach void setUp() throws Exception { restTemplate = new NacosRestTemplate(logger, requestClient); when(logger.isDebugEnabled()).thenReturn(true); when(mockResponse.getHeaders()).thenReturn(Header.EMPTY); when(interceptor.isIntercept(any(), any(), any())).thenReturn(true); when(interceptor.intercept()).thenReturn(mockResponse); } @AfterEach void tearDown() throws Exception { restTemplate.close(); } @Test void testGetWithDefaultConfig() throws Exception { when(requestClient.execute(any(), eq("GET"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpRestResult result = restTemplate.get("http://127.0.0.1:8848/nacos/test", Header.EMPTY, Query.EMPTY, String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testGetWithCustomConfig() throws Exception { when(requestClient.execute(any(), eq("GET"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpClientConfig config = HttpClientConfig.builder().setConTimeOutMillis(1000).build(); HttpRestResult result = restTemplate.get("http://127.0.0.1:8848/nacos/test", config, Header.EMPTY, Query.EMPTY, String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testGetWithInterceptor() throws Exception { when(mockResponse.getStatusCode()).thenReturn(300); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test interceptor".getBytes())); restTemplate.setInterceptors(Collections.singletonList(interceptor)); HttpRestResult result = restTemplate.get("http://127.0.0.1:8848/nacos/test", Header.EMPTY, Query.EMPTY, String.class); assertFalse(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test interceptor", result.getMessage()); } @Test void testGetWithException() throws Exception { assertThrows(RuntimeException.class, () -> { when(requestClient.execute(any(), eq("GET"), any())).thenThrow(new RuntimeException("test")); restTemplate.get("http://127.0.0.1:8848/nacos/test", Header.EMPTY, Query.EMPTY, String.class); }); } @Test void testGetLarge() throws Exception { when(requestClient.execute(any(), eq("GET-LARGE"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpRestResult result = restTemplate.getLarge("http://127.0.0.1:8848/nacos/test", Header.EMPTY, Query.EMPTY, new Object(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testDeleteWithDefaultConfig() throws Exception { when(requestClient.execute(any(), eq("DELETE"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpRestResult result = restTemplate.delete("http://127.0.0.1:8848/nacos/test", Header.EMPTY, Query.EMPTY, String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testDeleteWithCustomConfig() throws Exception { when(requestClient.execute(any(), eq("DELETE"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpClientConfig config = HttpClientConfig.builder().setConTimeOutMillis(1000).build(); HttpRestResult result = restTemplate.delete("http://127.0.0.1:8848/nacos/test", config, Header.EMPTY, Query.EMPTY, String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testPut() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpRestResult result = restTemplate.put("http://127.0.0.1:8848/nacos/test", Header.EMPTY, Query.EMPTY, new Object(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testPutJson() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.putJson("http://127.0.0.1:8848/nacos/test", header, "body", String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPutJsonWithQuery() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.putJson("http://127.0.0.1:8848/nacos/test", header, Query.EMPTY, "body", String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPutForm() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.putForm("http://127.0.0.1:8848/nacos/test", header, new HashMap<>(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPutFormWithQuery() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.putForm("http://127.0.0.1:8848/nacos/test", header, Query.EMPTY, new HashMap<>(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPutFormWithConfig() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpClientConfig config = HttpClientConfig.builder().setConTimeOutMillis(1000).build(); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.putForm("http://127.0.0.1:8848/nacos/test", config, header, new HashMap<>(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPost() throws Exception { when(requestClient.execute(any(), eq("POST"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpRestResult result = restTemplate.post("http://127.0.0.1:8848/nacos/test", Header.EMPTY, Query.EMPTY, new Object(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testPostJson() throws Exception { when(requestClient.execute(any(), eq("POST"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.postJson("http://127.0.0.1:8848/nacos/test", header, "body", String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPostJsonWithQuery() throws Exception { when(requestClient.execute(any(), eq("POST"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.postJson("http://127.0.0.1:8848/nacos/test", header, Query.EMPTY, "body", String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPostForm() throws Exception { when(requestClient.execute(any(), eq("POST"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.postForm("http://127.0.0.1:8848/nacos/test", header, new HashMap<>(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPostFormWithQuery() throws Exception { when(requestClient.execute(any(), eq("POST"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.postForm("http://127.0.0.1:8848/nacos/test", header, Query.EMPTY, new HashMap<>(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testPostFormWithConfig() throws Exception { when(requestClient.execute(any(), eq("POST"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpClientConfig config = HttpClientConfig.builder().setConTimeOutMillis(1000).build(); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.postForm("http://127.0.0.1:8848/nacos/test", config, header, new HashMap<>(), String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testExchangeForm() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); Header header = Header.newInstance().setContentType(MediaType.APPLICATION_XML); HttpRestResult result = restTemplate.exchangeForm("http://127.0.0.1:8848/nacos/test", header, Query.EMPTY, new HashMap<>(), "PUT", String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testExchange() throws Exception { when(requestClient.execute(any(), eq("PUT"), any())).thenReturn(mockResponse); when(mockResponse.getStatusCode()).thenReturn(200); when(mockResponse.getBody()).thenReturn(new ByteArrayInputStream("test".getBytes())); HttpClientConfig config = HttpClientConfig.builder().setConTimeOutMillis(1000).build(); HttpRestResult result = restTemplate.exchange("http://127.0.0.1:8848/nacos/test", config, Header.EMPTY, Query.EMPTY, new Object(), "PUT", String.class); assertTrue(result.ok()); assertEquals(Header.EMPTY, result.getHeader()); assertEquals("test", result.getData()); } @Test void testGetInterceptors() { assertTrue(restTemplate.getInterceptors().isEmpty()); restTemplate.setInterceptors(Collections.singletonList(interceptor)); assertEquals(1, restTemplate.getInterceptors().size()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/handler/BeanResponseHandlerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.handler; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.utils.JacksonUtils; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.LinkedList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class BeanResponseHandlerTest { @Test void testConvertResult() throws Exception { List testCase = new LinkedList<>(); for (int i = 0; i < 10; i++) { testCase.add(i); } byte[] bytes = JacksonUtils.toJsonBytes(testCase); InputStream inputStream = new ByteArrayInputStream(bytes); HttpClientResponse response = mock(HttpClientResponse.class); when(response.getBody()).thenReturn(inputStream); when(response.getHeaders()).thenReturn(Header.EMPTY); when(response.getStatusCode()).thenReturn(200); BeanResponseHandler> beanResponseHandler = new BeanResponseHandler<>(); beanResponseHandler.setResponseType(List.class); HttpRestResult> actual = beanResponseHandler.handle(response); assertEquals(200, actual.getCode()); assertEquals(testCase, actual.getData()); assertNull(actual.getMessage()); assertEquals(Header.EMPTY, actual.getHeader()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/handler/RestResultResponseHandlerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.handler; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.utils.JacksonUtils; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.InputStream; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class RestResultResponseHandlerTest { @Test void testConvertResult() throws Exception { RestResult testCase = RestResult.builder().withCode(200).withData("ok").withMsg("msg").build(); InputStream inputStream = new ByteArrayInputStream(JacksonUtils.toJsonBytes(testCase)); HttpClientResponse response = mock(HttpClientResponse.class); when(response.getBody()).thenReturn(inputStream); when(response.getHeaders()).thenReturn(Header.EMPTY); when(response.getStatusCode()).thenReturn(200); RestResultResponseHandler handler = new RestResultResponseHandler<>(); handler.setResponseType(RestResult.class); HttpRestResult actual = handler.handle(response); assertEquals(testCase.getCode(), actual.getCode()); assertEquals(testCase.getData(), actual.getData()); assertEquals(testCase.getMessage(), actual.getMessage()); assertEquals(Header.EMPTY, actual.getHeader()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/request/DefaultAsyncHttpClientRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.http.Callback; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.handler.ResponseHandler; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RequestHttpEntity; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; import org.apache.hc.core5.concurrent.FutureCallback; import org.apache.hc.core5.reactor.DefaultConnectingIOReactor; 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.junit.jupiter.MockitoExtension; import java.net.URI; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DefaultAsyncHttpClientRequestTest { DefaultAsyncHttpClientRequest httpClientRequest; @Mock private CloseableHttpAsyncClient client; @Mock private DefaultConnectingIOReactor ioReactor; @Mock private Callback callback; @Mock private ResponseHandler responseHandler; private RequestConfig defaultConfig; private URI uri; @BeforeEach void setUp() throws Exception { defaultConfig = RequestConfig.DEFAULT; httpClientRequest = new DefaultAsyncHttpClientRequest(client, ioReactor, defaultConfig); uri = URI.create("http://127.0.0.1:8080"); } @AfterEach void tearDown() throws Exception { httpClientRequest.close(); } @Test void testExecuteOnFail() throws Exception { Header header = Header.newInstance(); Map body = new HashMap<>(); body.put("test", "test"); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY, body); RuntimeException exception = new RuntimeException("test"); when(client.execute(any(), any())).thenAnswer(invocationOnMock -> { ((FutureCallback) invocationOnMock.getArgument(1)).failed(exception); return null; }); httpClientRequest.execute(uri, "PUT", httpEntity, responseHandler, callback); verify(callback).onError(exception); } @Test void testExecuteOnCancel() throws Exception { Header header = Header.newInstance(); Map body = new HashMap<>(); body.put("test", "test"); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY, body); when(client.execute(any(), any())).thenAnswer(invocationOnMock -> { ((FutureCallback) invocationOnMock.getArgument(1)).cancelled(); return null; }); httpClientRequest.execute(uri, "PUT", httpEntity, responseHandler, callback); verify(callback).onCancel(); } @Test void testExecuteOnComplete() throws Exception { Header header = Header.newInstance(); Map body = new HashMap<>(); body.put("test", "test"); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY, body); SimpleHttpResponse response = mock(SimpleHttpResponse.class); HttpRestResult restResult = new HttpRestResult(); when(responseHandler.handle(any())).thenReturn(restResult); when(client.execute(any(), any())).thenAnswer(invocationOnMock -> { ((FutureCallback) invocationOnMock.getArgument(1)).completed(response); return null; }); httpClientRequest.execute(uri, "PUT", httpEntity, responseHandler, callback); verify(callback).onReceive(restResult); } @Test void testExecuteOnCompleteWithException() throws Exception { Header header = Header.newInstance(); Map body = new HashMap<>(); body.put("test", "test"); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY, body); SimpleHttpResponse response = mock(SimpleHttpResponse.class); RuntimeException exception = new RuntimeException("test"); when(responseHandler.handle(any())).thenThrow(exception); when(client.execute(any(), any())).thenAnswer(invocationOnMock -> { ((FutureCallback) invocationOnMock.getArgument(1)).completed(response); return null; }); httpClientRequest.execute(uri, "PUT", httpEntity, responseHandler, callback); verify(callback).onError(exception); } @Test void testExecuteException() throws Exception { Header header = Header.newInstance(); Map body = new HashMap<>(); body.put("test", "test"); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY, body); IllegalStateException exception = new IllegalStateException("test"); when(client.execute(any(), any())).thenThrow(exception); // when(ioReactor.getAuditLog()).thenReturn(Collections.singletonList(new ExceptionEvent(exception, new Date()))); try { httpClientRequest.execute(uri, "PUT", httpEntity, responseHandler, callback); } catch (Exception e) { assertEquals(exception, e); } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/request/DefaultHttpClientRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RequestHttpEntity; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.client5.http.classic.methods.HttpUriRequestBase; import org.apache.hc.client5.http.config.RequestConfig; import org.apache.hc.client5.http.entity.UrlEncodedFormEntity; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.core5.http.io.HttpClientResponseHandler; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.net.URI; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DefaultHttpClientRequestTest { DefaultHttpClientRequest httpClientRequest; @Mock private CloseableHttpClient client; @Mock private SimpleHttpResponse response; private RequestConfig defaultConfig; private boolean isForm; private boolean withConfig; private URI uri; @BeforeEach void setUp() throws Exception { defaultConfig = RequestConfig.DEFAULT; httpClientRequest = new DefaultHttpClientRequest(client, defaultConfig); when(client.execute(argThat(httpUriRequest -> { boolean result = isForm == (httpUriRequest.getEntity() instanceof UrlEncodedFormEntity); HttpUriRequestBase baseHttpRequest = (HttpUriRequestBase) httpUriRequest; if (withConfig) { result &= null != baseHttpRequest.getConfig(); } return result; }), any(HttpClientResponseHandler.class))).thenReturn(response); uri = URI.create("http://127.0.0.1:8080"); } @AfterEach void tearDown() throws Exception { isForm = false; withConfig = false; httpClientRequest.close(); } @Test void testExecuteForFormWithoutConfig() throws Exception { isForm = true; Header header = Header.newInstance().addParam(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); Map body = new HashMap<>(); body.put("test", "test"); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY, body); HttpClientResponse actual = httpClientRequest.execute(uri, "PUT", httpEntity); assertEquals(response, getActualResponse(actual)); } @Test void testExecuteForFormWithConfig() throws Exception { isForm = true; withConfig = true; Header header = Header.newInstance().addParam(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); Map body = new HashMap<>(); body.put("test", "test"); RequestHttpEntity httpEntity = new RequestHttpEntity(HttpClientConfig.builder().build(), header, Query.EMPTY, body); HttpClientResponse actual = httpClientRequest.execute(uri, "PUT", httpEntity); assertEquals(response, getActualResponse(actual)); } @Test void testExecuteForOther() throws Exception { Header header = Header.newInstance(); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY, "body"); HttpClientResponse actual = httpClientRequest.execute(uri, "PUT", httpEntity); assertEquals(response, getActualResponse(actual)); } private SimpleHttpResponse getActualResponse(HttpClientResponse actual) throws IllegalAccessException, NoSuchFieldException { Field field = actual.getClass().getDeclaredField("response"); field.setAccessible(true); return (SimpleHttpResponse) field.get(actual); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/request/JdkHttpClientRequestTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.request; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.response.HttpClientResponse; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RequestHttpEntity; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.io.OutputStream; import java.lang.reflect.Field; import java.net.HttpURLConnection; import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class JdkHttpClientRequestTest { JdkHttpClientRequest httpClientRequest; @Mock private HttpURLConnection connection; @Mock private URI uri; @Mock private URL url; @Mock private OutputStream outputStream; private HttpClientConfig httpClientConfig; @BeforeEach void setUp() throws Exception { when(uri.toURL()).thenReturn(url); when(url.openConnection()).thenReturn(connection); when(connection.getOutputStream()).thenReturn(outputStream); httpClientConfig = HttpClientConfig.builder().build(); httpClientRequest = new JdkHttpClientRequest(httpClientConfig); } @AfterEach void tearDown() throws Exception { httpClientRequest.close(); } @Test void testExecuteNormal() throws Exception { Header header = Header.newInstance(); HttpClientConfig config = HttpClientConfig.builder().build(); RequestHttpEntity httpEntity = new RequestHttpEntity(config, header, Query.EMPTY, "a=bo&dy"); HttpClientResponse response = httpClientRequest.execute(uri, "GET", httpEntity); byte[] writeBytes = "a=bo&dy".getBytes(StandardCharsets.UTF_8); verify(outputStream).write(writeBytes, 0, writeBytes.length); assertEquals(connection, getActualConnection(response)); } @Test void testExecuteForm() throws Exception { Header header = Header.newInstance(); header.setContentType(MediaType.APPLICATION_FORM_URLENCODED); HttpClientConfig config = HttpClientConfig.builder().build(); Map body = new HashMap<>(); body.put("a", "bo&dy"); RequestHttpEntity httpEntity = new RequestHttpEntity(config, header, Query.EMPTY, body); HttpClientResponse response = httpClientRequest.execute(uri, "GET", httpEntity); byte[] writeBytes = HttpUtils.encodingParams(body, StandardCharsets.UTF_8.name()).getBytes(StandardCharsets.UTF_8); verify(outputStream).write(writeBytes, 0, writeBytes.length); assertEquals(connection, getActualConnection(response)); } @Test void testExecuteEmptyBody() throws Exception { Header header = Header.newInstance(); RequestHttpEntity httpEntity = new RequestHttpEntity(header, Query.EMPTY); HttpClientResponse response = httpClientRequest.execute(uri, "GET", httpEntity); verify(outputStream, never()).write(any(), eq(0), anyInt()); assertEquals(connection, getActualConnection(response)); } private HttpURLConnection getActualConnection(HttpClientResponse actual) throws IllegalAccessException, NoSuchFieldException { Field field = actual.getClass().getDeclaredField("conn"); field.setAccessible(true); return (HttpURLConnection) field.get(actual); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/response/DefaultClientHttpResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.response; import org.apache.hc.client5.http.async.methods.SimpleBody; import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; import org.apache.hc.core5.http.Header; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.io.ByteArrayInputStream; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class DefaultClientHttpResponseTest { DefaultClientHttpResponse clientHttpResponse; @Mock private SimpleHttpResponse response; @Mock private SimpleBody simpleBody; @Mock private Header header; @BeforeEach void setUp() throws Exception { when(response.getBody()).thenReturn(simpleBody); when(response.getHeaders()).thenReturn(new Header[] {header}); when(response.getReasonPhrase()).thenReturn("test"); when(header.getName()).thenReturn("testName"); when(header.getValue()).thenReturn("testValue"); clientHttpResponse = new DefaultClientHttpResponse(response); } @AfterEach void tearDown() throws Exception { clientHttpResponse.close(); } @Test void testGetStatusCode() { assertEquals(0, clientHttpResponse.getStatusCode()); } @Test void testGetStatusText() { assertEquals("test", clientHttpResponse.getStatusText()); } @Test void testGetHeaders() { assertEquals(3, clientHttpResponse.getHeaders().getHeader().size()); assertEquals("testValue", clientHttpResponse.getHeaders().getValue("testName")); } @Test void testGetBody() throws IOException { assertTrue(clientHttpResponse.getBody() instanceof ByteArrayInputStream); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/client/response/JdkClientHttpResponseTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.client.response; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.utils.IoUtils; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class JdkClientHttpResponseTest { JdkHttpClientResponse clientHttpResponse; @Mock private HttpURLConnection connection; @Mock private InputStream inputStream; private Map> headers; @BeforeEach void setUp() throws Exception { headers = new HashMap<>(); headers.put("testName", Collections.singletonList("testValue")); when(connection.getHeaderFields()).thenReturn(headers); clientHttpResponse = new JdkHttpClientResponse(connection); } @AfterEach void tearDown() throws Exception { clientHttpResponse.close(); } @Test void testGetStatusCode() throws IOException { when(connection.getResponseCode()).thenReturn(200); assertEquals(200, clientHttpResponse.getStatusCode()); } @Test void testGetStatusText() throws IOException { when(connection.getResponseMessage()).thenReturn("test"); assertEquals("test", clientHttpResponse.getStatusText()); } @Test void testGetHeaders() { assertEquals(3, clientHttpResponse.getHeaders().getHeader().size()); assertEquals("testValue", clientHttpResponse.getHeaders().getValue("testName")); } @Test void testGetBody() throws IOException { when(connection.getInputStream()).thenReturn(inputStream); assertEquals(inputStream, clientHttpResponse.getBody()); } @Test void testGetBodyWithGzip() throws IOException { byte[] testCase = IoUtils.tryCompress("test", StandardCharsets.UTF_8.name()); ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(testCase); when(connection.getInputStream()).thenReturn(byteArrayInputStream); headers.put(HttpHeaderConsts.CONTENT_ENCODING, Collections.singletonList("gzip")); assertEquals("test", IoUtils.toString(clientHttpResponse.getBody(), StandardCharsets.UTF_8.name())); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/param/HeaderTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.param; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class HeaderTest { @Test void testSetContentType() { Header header = Header.newInstance(); header.setContentType(null); assertEquals(MediaType.APPLICATION_JSON, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); header.setContentType(MediaType.MULTIPART_FORM_DATA); assertEquals(MediaType.MULTIPART_FORM_DATA, header.getValue(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testHeaderKyeIgnoreCase() { Header header = Header.newInstance(); header.addParam("Content-Encoding", "gzip"); assertEquals("gzip", header.getValue("content-encoding")); } @Test void testToList() { Header header = Header.newInstance(); List list = header.toList(); assertTrue(list.contains(HttpHeaderConsts.CONTENT_TYPE)); assertTrue(list.contains(MediaType.APPLICATION_JSON)); assertEquals(1, list.indexOf(MediaType.APPLICATION_JSON) - list.indexOf(HttpHeaderConsts.CONTENT_TYPE)); assertTrue(list.contains(HttpHeaderConsts.ACCEPT_CHARSET)); assertTrue(list.contains("UTF-8")); assertEquals(1, list.indexOf("UTF-8") - list.indexOf(HttpHeaderConsts.ACCEPT_CHARSET)); } @Test void testAddAllForMap() { Map map = new HashMap<>(); map.put("test1", "test2"); map.put("test3", "test4"); Header header = Header.newInstance(); header.addAll(map); assertEquals("test2", header.getValue("test1")); assertEquals("test4", header.getValue("test3")); assertEquals(4, header.getHeader().size()); } @Test void testAddAllForList() { List list = new ArrayList<>(4); list.add("test1"); list.add("test2"); list.add("test3"); list.add("test4"); Header header = Header.newInstance(); header.addAll(list); assertEquals("test2", header.getValue("test1")); assertEquals("test4", header.getValue("test3")); assertEquals(4, header.getHeader().size()); } @Test void testAddAllForListWithWrongLength() { assertThrows(IllegalArgumentException.class, () -> { List list = new ArrayList<>(3); list.add("test1"); list.add("test2"); list.add("test3"); Header header = Header.newInstance(); header.addAll(list); }); } @Test void testAddOriginalResponseHeader() { List list = new ArrayList<>(4); list.add("test1"); list.add("test2"); list.add("test3"); list.add("test4"); Header header = Header.newInstance(); header.addOriginalResponseHeader("test", list); assertEquals("test1", header.getValue("test")); assertEquals(1, header.getOriginalResponseHeader().size()); assertEquals(list, header.getOriginalResponseHeader().get("test")); } @Test void testGetCharset() { Header header = Header.newInstance(); assertEquals("UTF-8", header.getCharset()); header.addParam(HttpHeaderConsts.ACCEPT_CHARSET, null); header.setContentType(MediaType.APPLICATION_JSON); assertEquals("UTF-8", header.getCharset()); header.setContentType("application/json;charset=GBK"); assertEquals("GBK", header.getCharset()); header.setContentType("application/json"); assertEquals("UTF-8", header.getCharset()); header.setContentType(""); assertEquals("UTF-8", header.getCharset()); } @Test void testClear() { Header header = Header.newInstance(); header.addOriginalResponseHeader("test", Collections.singletonList("test")); assertEquals(3, header.getHeader().size()); assertEquals(1, header.getOriginalResponseHeader().size()); header.clear(); assertEquals(0, header.getHeader().size()); assertEquals(0, header.getOriginalResponseHeader().size()); assertEquals("Header{headerToMap={}}", header.toString()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/param/MediaTypeTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.param; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; /** * MediaTypeTest. * * @author mai.jh */ class MediaTypeTest { @Test void testValueOf() { MediaType mediaType = MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED); String type = "application/x-www-form-urlencoded"; String charset = "UTF-8"; assertEquals(type, mediaType.getType()); assertEquals(charset, mediaType.getCharset()); assertEquals(MediaType.APPLICATION_FORM_URLENCODED, mediaType.toString()); } @Test void testValueOf2() { MediaType mediaType = MediaType.valueOf(MediaType.APPLICATION_FORM_URLENCODED, "ISO-8859-1"); String type = "application/x-www-form-urlencoded"; String charset = "ISO-8859-1"; String excepted = "application/x-www-form-urlencoded;charset=ISO-8859-1"; assertEquals(type, mediaType.getType()); assertEquals(charset, mediaType.getCharset()); assertEquals(excepted, mediaType.toString()); } @Test void testValueOf3() { MediaType mediaType = MediaType.valueOf("application/x-www-form-urlencoded", "ISO-8859-1"); String type = "application/x-www-form-urlencoded"; String charset = "ISO-8859-1"; String excepted = "application/x-www-form-urlencoded;charset=ISO-8859-1"; assertEquals(type, mediaType.getType()); assertEquals(charset, mediaType.getCharset()); assertEquals(excepted, mediaType.toString()); } @Test void testValueOfWithEmpty() { assertThrows(IllegalArgumentException.class, () -> { MediaType.valueOf(""); }); } @Test void testValueOfWithEmpty2() { assertThrows(IllegalArgumentException.class, () -> { MediaType.valueOf("", "UTF-8"); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/http/param/QueryTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.http.param; import com.alibaba.nacos.api.naming.CommonParams; import org.junit.jupiter.api.Test; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.LinkedHashMap; import java.util.Map; 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 QueryTest { @Test void testInitParams() { Map parameters = new LinkedHashMap(); parameters.put(CommonParams.NAMESPACE_ID, "namespace"); parameters.put(CommonParams.SERVICE_NAME, "service"); parameters.put(CommonParams.GROUP_NAME, "group"); parameters.put(CommonParams.CLUSTER_NAME, null); parameters.put("ip", "1.1.1.1"); parameters.put("port", String.valueOf(9999)); parameters.put("weight", String.valueOf(1.0)); parameters.put("ephemeral", String.valueOf(true)); String excepted = "namespaceId=namespace&serviceName=service&groupName=group&ip=1.1.1.1&port=9999&weight=1.0&ephemeral=true"; Query actual = Query.newInstance().initParams(parameters); assertEquals(excepted, actual.toQueryUrl()); assertEquals("namespace", actual.getValue(CommonParams.NAMESPACE_ID)); } @Test void testAddParams() throws Exception { Query query = Query.newInstance().addParam("key-1", "value-1").addParam("key-2", "value-2"); String s1 = query.toQueryUrl(); String s2 = "key-1=" + URLEncoder.encode("value-1", StandardCharsets.UTF_8.name()) + "&key-2=" + URLEncoder.encode("value-2", StandardCharsets.UTF_8.name()); assertEquals(s2, s1); assertEquals("value-1", query.getValue("key-1")); } @Test void testClear() { Query query = Query.newInstance().addParam("key-1", "value-1").addParam("key-2", "value-2"); assertFalse(query.isEmpty()); assertEquals("value-1", query.getValue("key-1")); query.clear(); assertTrue(query.isEmpty()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/labels/impl/DefaultLabelsCollectorManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.labels.impl; import com.alibaba.nacos.api.common.Constants; import org.junit.jupiter.api.Test; import java.util.Map; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; /** * description. * * @author rong * @date 2024-02-29 20:13 */ class DefaultLabelsCollectorManagerTest { @Test void tagV2LabelsCollectorTest() { Properties properties = new Properties(); properties.put(Constants.APP_CONN_LABELS_KEY, "k1=v1,gray=properties_pre"); properties.put(Constants.CONFIG_GRAY_LABEL, "properties_after"); DefaultLabelsCollectorManager defaultLabelsCollectorManager = new DefaultLabelsCollectorManager(); Map labels = defaultLabelsCollectorManager.getLabels(properties); assertEquals("properties_after", labels.get(Constants.CONFIG_GRAY_LABEL)); assertEquals("v1", labels.get("k1")); } @Test void tagV2LabelsCollectorOrderTest() { Properties properties = new Properties(); DefaultLabelsCollectorManager defaultLabelsCollectorManager = new DefaultLabelsCollectorManager(); Map labels = defaultLabelsCollectorManager.getLabels(properties); String test = labels.get("test"); assertEquals("test2", test); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/labels/impl/Test1LabelsCollector.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.labels.impl; import com.alibaba.nacos.common.labels.LabelsCollector; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * Test1LabelsCollector. * * @author rong */ public class Test1LabelsCollector implements LabelsCollector { @Override public String getName() { return "test1"; } @Override public Map collectLabels(Properties properties) { Map labels = new HashMap<>(); labels.put("test", getName()); return labels; } @Override public int getOrder() { return 1; } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/labels/impl/Test2LabelsCollector.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.labels.impl; import com.alibaba.nacos.common.labels.LabelsCollector; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * TagV1LabelsCollector. * * @author rong */ public class Test2LabelsCollector implements LabelsCollector { @Override public String getName() { return "test2"; } @Override public Map collectLabels(Properties properties) { Map labels = new HashMap<>(); labels.put("test", getName()); return labels; } @Override public int getOrder() { return 2; } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/logging/NacosLoggingPropertiesTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.logging; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class NacosLoggingPropertiesTest { NacosLoggingProperties loggingProperties; Properties properties; @BeforeEach void setUp() throws Exception { properties = new Properties(); loggingProperties = new NacosLoggingProperties("classpath:test.xml", properties); } @Test void testGetLocationWithDefault() { assertEquals("classpath:test.xml", loggingProperties.getLocation()); } @Test void testGetLocationWithoutDefault() { properties.setProperty("nacos.logging.default.config.enabled", "false"); assertNull(loggingProperties.getLocation()); } @Test void testGetLocationForSpecified() { properties.setProperty("nacos.logging.config", "classpath:specified-test.xml"); properties.setProperty("nacos.logging.default.config.enabled", "false"); assertEquals("classpath:specified-test.xml", loggingProperties.getLocation()); } @Test void testGetLocationForSpecifiedWithDefault() { properties.setProperty("nacos.logging.config", "classpath:specified-test.xml"); assertEquals("classpath:specified-test.xml", loggingProperties.getLocation()); } @Test void testGetReloadInternal() { properties.setProperty("nacos.logging.reload.interval.seconds", "50000"); assertEquals(50000L, loggingProperties.getReloadInternal()); } @Test void testGetValue() { properties.setProperty("test.key", "test.value"); assertEquals("test.value", loggingProperties.getValue("test.key", "default.value")); properties.clear(); assertEquals("default.value", loggingProperties.getValue("test.key", "default.value")); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/model/RequestHttpEntityTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.model; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import java.util.HashMap; 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; @ExtendWith(MockitoExtension.class) class RequestHttpEntityTest { Header header; Query query; HttpClientConfig clientConfig; Object body; @BeforeEach void setUp() throws Exception { header = Header.newInstance(); header.addParam("testHeader", "test"); query = Query.newInstance(); query.addParam("testQuery", "test"); clientConfig = HttpClientConfig.builder().build(); body = new HashMap<>(); } @Test void testConstructWithoutConfigAndBody() { RequestHttpEntity entity = new RequestHttpEntity(header, query); assertTrue(entity.isEmptyBody()); assertNull(entity.getHttpClientConfig()); assertNull(entity.getBody()); assertEquals(header.toString(), entity.getHeaders().toString()); assertEquals(query.toString(), entity.getQuery().toString()); } @Test void testConstructWithoutConfigAndQuery() { RequestHttpEntity entity = new RequestHttpEntity(header, body); assertFalse(entity.isEmptyBody()); assertNull(entity.getHttpClientConfig()); assertNull(entity.getQuery()); assertEquals(header.toString(), entity.getHeaders().toString()); assertEquals(body, entity.getBody()); } @Test void testConstructWithoutConfig() { RequestHttpEntity entity = new RequestHttpEntity(header, query, body); assertFalse(entity.isEmptyBody()); assertNull(entity.getHttpClientConfig()); assertEquals(query.toString(), entity.getQuery().toString()); assertEquals(header.toString(), entity.getHeaders().toString()); assertEquals(body, entity.getBody()); } @Test void testConstructFull() { RequestHttpEntity entity = new RequestHttpEntity(clientConfig, header, query, body); assertFalse(entity.isEmptyBody()); assertEquals(clientConfig, entity.getHttpClientConfig()); assertEquals(query.toString(), entity.getQuery().toString()); assertEquals(header.toString(), entity.getHeaders().toString()); assertEquals(body, entity.getBody()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/model/RestResultTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.model; import com.alibaba.nacos.common.utils.JacksonUtils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class RestResultTest { @Test void testSerialization() { RestResult result = new RestResult<>(200, "test", "content"); String json = JacksonUtils.toJson(result); assertTrue(json.contains("\"code\":200")); assertTrue(json.contains("\"message\":\"test\"")); assertTrue(json.contains("\"data\":\"content\"")); } @Test void testDeserialization() { String json = "{\"code\":200,\"message\":\"test\",\"data\":\"content\"}"; RestResult restResult = JacksonUtils.toObj(json, RestResult.class); assertEquals(200, restResult.getCode()); assertEquals("test", restResult.getMessage()); assertEquals("content", restResult.getData()); assertEquals("RestResult{code=200, message='test', data=content}", restResult.toString()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/model/RestResultUtilsTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.model; import com.alibaba.nacos.common.model.core.IResultCode; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class RestResultUtilsTest { @Test void testSuccessWithDefault() { RestResult restResult = RestResultUtils.success(); assertRestResult(restResult, 200, null, null, true); } @Test void testSuccessWithData() { RestResult restResult = RestResultUtils.success("content"); assertRestResult(restResult, 200, null, "content", true); } @Test void testSuccessWithMsg() { RestResult restResult = RestResultUtils.success("test", "content"); assertRestResult(restResult, 200, "test", "content", true); } @Test void testSuccessWithCode() { RestResult restResult = RestResultUtils.success(203, "content"); assertRestResult(restResult, 203, null, "content", false); } @Test void testFailedWithDefault() { RestResult restResult = RestResultUtils.failed(); assertRestResult(restResult, 500, null, null, false); } @Test void testFailedWithMsg() { RestResult restResult = RestResultUtils.failed("test"); assertRestResult(restResult, 500, "test", null, false); } @Test void testFailedWithCode() { RestResult restResult = RestResultUtils.failed(400, "content"); assertRestResult(restResult, 400, null, "content", false); } @Test void testSuccessWithFull() { RestResult restResult = RestResultUtils.failed(400, "content", "test"); assertRestResult(restResult, 400, "test", "content", false); } @Test void testFailedWithMsgMethod() { RestResult restResult = RestResultUtils.failedWithMsg(400, "content"); assertRestResult(restResult, 400, "content", null, false); } @Test void testBuildResult() { IResultCode mockCode = new IResultCode() { @Override public int getCode() { return 503; } @Override public String getCodeMsg() { return "limited"; } }; RestResult restResult = RestResultUtils.buildResult(mockCode, "content"); assertRestResult(restResult, 503, "limited", "content", false); } private void assertRestResult(RestResult restResult, int code, String message, Object data, boolean isOk) { assertEquals(code, restResult.getCode()); assertEquals(message, restResult.getMessage()); assertEquals(data, restResult.getData()); assertEquals(isOk, restResult.ok()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/notify/DefaultPublisherTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.common.notify.listener.Subscriber; 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.junit.jupiter.MockitoExtension; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DefaultPublisherTest { private DefaultPublisher publisher; @Mock private Subscriber subscriber; @BeforeEach void setUp() throws Exception { publisher = new DefaultPublisher(); publisher.init(MockEvent.class, 1); } @AfterEach void tearDown() throws Exception { try { publisher.shutdown(); } catch (Exception ignored) { } } @Test void testInitWithIllegalSize() { publisher.shutdown(); publisher = new DefaultPublisher(); publisher.init(MockEvent.class, -1); assertTrue(publisher.isInitialized()); } @Test void testCheckIsStart() { assertThrows(IllegalStateException.class, () -> { publisher.shutdown(); publisher = new DefaultPublisher(); publisher.checkIsStart(); }); } @Test void testCurrentEventSize() { assertEquals(0, publisher.currentEventSize()); publisher.publish(new MockEvent()); assertEquals(1, publisher.currentEventSize()); } @Test void testRemoveSubscriber() { publisher.addSubscriber(subscriber); assertEquals(1, publisher.getSubscribers().size()); publisher.removeSubscriber(subscriber); assertEquals(0, publisher.getSubscribers().size()); } @Test void publishEventWhenQueueFull() { // Stop the publisher thread to mock queue full. publisher.shutdown(); publisher.publish(new MockEvent()); // Test throw event when no subscribers. publisher.publish(new MockEvent()); verify(subscriber, never()).onEvent(any(MockEvent.class)); // Add subscriber to test publisher.addSubscriber(subscriber); publisher.publish(new MockEvent()); // Test scopeMatches not pass verify(subscriber, never()).onEvent(any(MockEvent.class)); // Test scopeMatches pass when(subscriber.scopeMatches(any(MockEvent.class))).thenReturn(true); publisher.publish(new MockEvent()); verify(subscriber).onEvent(any(MockEvent.class)); } @Test void publishEventQueueNotFull() throws InterruptedException { when(subscriber.scopeMatches(any(MockEvent.class))).thenReturn(true); MockEvent mockEvent = new MockEvent(); // Make sure Publisher entry waiting subscribers. TimeUnit.MILLISECONDS.sleep(500); publisher.addSubscriber(subscriber); publisher.publish(mockEvent); // Make sure Publisher find the subscribers. TimeUnit.MILLISECONDS.sleep(600); verify(subscriber).onEvent(mockEvent); // Test subscriber ignore expired event. publisher.publish(new MockEvent()); TimeUnit.MILLISECONDS.sleep(100); reset(subscriber); when(subscriber.scopeMatches(any(MockEvent.class))).thenReturn(true); when(subscriber.ignoreExpireEvent()).thenReturn(true); publisher.publish(mockEvent); TimeUnit.MILLISECONDS.sleep(100); verify(subscriber, never()).onEvent(mockEvent); } @Test void testHandleEventWithThrowable() throws InterruptedException { when(subscriber.scopeMatches(any(MockEvent.class))).thenReturn(true); doThrow(new RuntimeException("test")).when(subscriber).onEvent(any(MockEvent.class)); publisher.addSubscriber(subscriber); publisher.publish(new MockEvent()); TimeUnit.MILLISECONDS.sleep(1100); verify(subscriber).onEvent(any(MockEvent.class)); } @Test void testHandleEventWithExecutor() throws InterruptedException { Executor executor = mock(Executor.class); when(subscriber.scopeMatches(any(MockEvent.class))).thenReturn(true); when(subscriber.executor()).thenReturn(executor); publisher.addSubscriber(subscriber); publisher.publish(new MockEvent()); TimeUnit.MILLISECONDS.sleep(1100); verify(executor).execute(any(Runnable.class)); } @Test void testReceiveEventWithException() throws InterruptedException { Executor executor = mock(Executor.class); when(subscriber.scopeMatches(any(MockEvent.class))).thenReturn(true); when(subscriber.executor()).thenThrow(new RuntimeException("test")); publisher.addSubscriber(subscriber); publisher.publish(new MockEvent()); TimeUnit.MILLISECONDS.sleep(1100); verify(executor, never()).execute(any(Runnable.class)); } private static class MockEvent extends Event { private static final long serialVersionUID = -4081244883427311461L; } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/notify/DefaultSharePublisherTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; 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.junit.jupiter.MockitoExtension; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DefaultSharePublisherTest { private static final AtomicLong TEST_SEQUENCE = new AtomicLong(); DefaultSharePublisher defaultSharePublisher; @Mock SmartSubscriber smartSubscriber1; @Mock SmartSubscriber smartSubscriber2; @BeforeEach void setUp() throws Exception { defaultSharePublisher = new DefaultSharePublisher(); defaultSharePublisher.init(SlowEvent.class, 2); } @AfterEach void tearDown() throws Exception { defaultSharePublisher.shutdown(); } @Test void testRemoveSubscribers() { defaultSharePublisher.addSubscriber(smartSubscriber1, MockSlowEvent1.class); defaultSharePublisher.addSubscriber(smartSubscriber1, MockSlowEvent2.class); defaultSharePublisher.addSubscriber(smartSubscriber2, MockSlowEvent2.class); assertEquals(2, defaultSharePublisher.getSubscribers().size()); defaultSharePublisher.removeSubscriber(smartSubscriber1, MockSlowEvent1.class); defaultSharePublisher.removeSubscriber(smartSubscriber1, MockSlowEvent2.class); defaultSharePublisher.removeSubscriber(smartSubscriber2, MockSlowEvent2.class); assertEquals(0, defaultSharePublisher.getSubscribers().size()); } @Test void testReceiveEventWithoutSubscriber() { defaultSharePublisher.addSubscriber(smartSubscriber1, MockSlowEvent1.class); defaultSharePublisher.addSubscriber(smartSubscriber2, MockSlowEvent2.class); defaultSharePublisher.receiveEvent(new SlowEvent() { private static final long serialVersionUID = 5996336354563933789L; @Override public long sequence() { return super.sequence(); } }); verify(smartSubscriber1, never()).onEvent(any(SlowEvent.class)); verify(smartSubscriber2, never()).onEvent(any(SlowEvent.class)); } @Test void testReceiveEventWithSubscriber() { defaultSharePublisher.addSubscriber(smartSubscriber1, MockSlowEvent1.class); defaultSharePublisher.addSubscriber(smartSubscriber2, MockSlowEvent2.class); defaultSharePublisher.receiveEvent(new MockSlowEvent1()); verify(smartSubscriber1).onEvent(any(MockSlowEvent1.class)); verify(smartSubscriber2, never()).onEvent(any(MockSlowEvent1.class)); defaultSharePublisher.receiveEvent(new MockSlowEvent2()); verify(smartSubscriber1, never()).onEvent(any(MockSlowEvent2.class)); verify(smartSubscriber2).onEvent(any(MockSlowEvent2.class)); } @Test void testIgnoreExpiredEvent() throws InterruptedException { MockSlowEvent1 mockSlowEvent1 = new MockSlowEvent1(); MockSlowEvent2 mockSlowEvent2 = new MockSlowEvent2(); defaultSharePublisher.addSubscriber(smartSubscriber1, MockSlowEvent1.class); defaultSharePublisher.addSubscriber(smartSubscriber2, MockSlowEvent2.class); defaultSharePublisher.publish(mockSlowEvent1); defaultSharePublisher.publish(mockSlowEvent2); TimeUnit.MILLISECONDS.sleep(1500); verify(smartSubscriber1).onEvent(mockSlowEvent1); verify(smartSubscriber2).onEvent(mockSlowEvent2); reset(smartSubscriber1); when(smartSubscriber1.ignoreExpireEvent()).thenReturn(true); defaultSharePublisher.publish(mockSlowEvent1); TimeUnit.MILLISECONDS.sleep(100); verify(smartSubscriber1, never()).onEvent(mockSlowEvent1); } private static class MockSlowEvent1 extends SlowEvent { private static final long serialVersionUID = -951177705152304999L; private final long sequence = TEST_SEQUENCE.incrementAndGet(); @Override public long sequence() { return sequence; } } private static class MockSlowEvent2 extends SlowEvent { private static final long serialVersionUID = -951177705152304999L; private final long sequence = TEST_SEQUENCE.incrementAndGet(); @Override public long sequence() { return sequence; } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/notify/NotifyCenterTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.notify; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.ThreadUtils; 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.junit.jupiter.MockitoExtension; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NotifyCenterTest { private static AtomicInteger count; static { System.setProperty("nacos.core.notify.share-buffer-size", "8"); } SmartSubscriber smartSubscriber; Subscriber subscriber; @Mock ShardedEventPublisher shardedEventPublisher; @BeforeEach void setUp() throws Exception { count = new AtomicInteger(); NotifyCenter.registerToSharePublisher(TestSlowEvent.class); NotifyCenter.registerToPublisher(TestSlowEvent1.class, 10); NotifyCenter.registerToPublisher(TestEvent.class, 8); NotifyCenter.registerToPublisher(ExpireEvent.class, 16); NotifyCenter.registerToPublisher(SharedEvent.class, shardedEventPublisher); } @AfterEach void tearDown() throws Exception { if (null != smartSubscriber) { NotifyCenter.deregisterSubscriber(smartSubscriber); } if (null != subscriber) { NotifyCenter.deregisterSubscriber(subscriber); } NotifyCenter.deregisterPublisher(TestEvent.class); NotifyCenter.deregisterPublisher(ExpireEvent.class); } @Test void testRegisterNullPublisher() { int originalSize = NotifyCenter.getPublisherMap().size(); NotifyCenter.registerToPublisher(NoPublisherEvent.class, null); assertEquals(originalSize, NotifyCenter.getPublisherMap().size()); } @Test void testGetPublisher() { assertEquals(NotifyCenter.getSharePublisher(), NotifyCenter.getPublisher(TestSlowEvent.class)); assertTrue(NotifyCenter.getPublisher(TestEvent.class) instanceof DefaultPublisher); } @Test void testEventsCanBeSubscribed() { subscriber = new MockSubscriber<>(TestEvent.class, false); smartSubscriber = new MockSmartSubscriber(Collections.singletonList(TestSlowEvent.class)); NotifyCenter.registerSubscriber(subscriber); NotifyCenter.registerSubscriber(smartSubscriber); assertTrue(NotifyCenter.publishEvent(new TestEvent())); assertTrue(NotifyCenter.publishEvent(new TestSlowEvent())); ThreadUtils.sleep(2000L); assertEquals(2, count.get()); } @Test void testCanIgnoreExpireEvent() throws Exception { NotifyCenter.registerToPublisher(ExpireEvent.class, 16); CountDownLatch latch = new CountDownLatch(3); subscriber = new MockSubscriber<>(ExpireEvent.class, true, latch); NotifyCenter.registerSubscriber(subscriber); for (int i = 0; i < 3; i++) { assertTrue(NotifyCenter.publishEvent(new ExpireEvent(3 - i))); } latch.await(5000L, TimeUnit.MILLISECONDS); assertEquals(1, count.get()); } @Test void testSharePublishEvent() throws InterruptedException { CountDownLatch latch = new CountDownLatch(20); Subscriber subscriber = new MockSubscriber<>(TestSlowEvent.class, false, latch); Subscriber subscriber1 = new MockSubscriber<>(TestSlowEvent1.class, false, latch); try { NotifyCenter.registerSubscriber(subscriber); NotifyCenter.registerSubscriber(subscriber1); for (int i = 0; i < 10; i++) { assertTrue(NotifyCenter.publishEvent(new TestSlowEvent())); assertTrue(NotifyCenter.publishEvent(new TestSlowEvent1())); } latch.await(5000L, TimeUnit.MILLISECONDS); assertEquals(20, count.get()); } finally { NotifyCenter.deregisterSubscriber(subscriber); NotifyCenter.deregisterSubscriber(subscriber1); } } @Test void testMutipleSlowEventsListenedBySmartSubscriber() throws Exception { List> subscribedEvents = new LinkedList<>(); subscribedEvents.add(TestSlowEvent.class); subscribedEvents.add(TestSlowEvent1.class); CountDownLatch latch = new CountDownLatch(6); smartSubscriber = new MockSmartSubscriber(subscribedEvents, latch); NotifyCenter.registerSubscriber(smartSubscriber); for (int i = 0; i < 3; i++) { assertTrue(NotifyCenter.publishEvent(new TestSlowEvent())); assertTrue(NotifyCenter.publishEvent(new TestSlowEvent1())); } latch.await(5000L, TimeUnit.MILLISECONDS); assertEquals(6, count.get()); } @Test void testMutipleKindsEventsCanListenBySmartsubscriber() throws Exception { List> subscribedEvents = new LinkedList<>(); subscribedEvents.add(TestEvent.class); subscribedEvents.add(TestSlowEvent.class); CountDownLatch latch = new CountDownLatch(6); smartSubscriber = new MockSmartSubscriber(subscribedEvents, latch); NotifyCenter.registerSubscriber(smartSubscriber); for (int i = 0; i < 3; i++) { assertTrue(NotifyCenter.publishEvent(new TestEvent())); assertTrue(NotifyCenter.publishEvent(new TestSlowEvent())); } latch.await(5000L, TimeUnit.MILLISECONDS); assertEquals(6, count.get()); } @Test void testPublishEventByNoPublisher() { for (int i = 0; i < 3; i++) { assertFalse(NotifyCenter.publishEvent(new NoPublisherEvent())); } } @Test void testPublishEventByPluginEvent() { for (int i = 0; i < 3; i++) { assertTrue(NotifyCenter.publishEvent(new PluginEvent())); } } @Test void testDeregisterPublisherWithException() throws NacosException { final int originalSize = NotifyCenter.getPublisherMap().size(); doThrow(new RuntimeException("test")).when(shardedEventPublisher).shutdown(); NotifyCenter.getPublisherMap().put(SharedEvent.class.getCanonicalName(), shardedEventPublisher); NotifyCenter.deregisterPublisher(SharedEvent.class); assertEquals(originalSize - 1, NotifyCenter.getPublisherMap().size()); } @Test void testPublishEventWithException() { when(shardedEventPublisher.publish(any(Event.class))).thenThrow(new RuntimeException("test")); NotifyCenter.getPublisherMap().put(SharedEvent.class.getCanonicalName(), shardedEventPublisher); assertFalse(NotifyCenter.publishEvent(new SharedEvent())); } @Test void testOperateSubscriberForShardedPublisher() { subscriber = new MockSubscriber(SharedEvent.class, false); NotifyCenter.getPublisherMap().put(SharedEvent.class.getCanonicalName(), shardedEventPublisher); NotifyCenter.registerSubscriber(subscriber); verify(shardedEventPublisher).addSubscriber(subscriber, SharedEvent.class); NotifyCenter.deregisterSubscriber(subscriber); verify(shardedEventPublisher).removeSubscriber(subscriber, SharedEvent.class); } @Test void testDeregisterNonExistSubscriber() { assertThrows(NoSuchElementException.class, () -> { try { subscriber = new MockSubscriber(NoPublisherEvent.class, false); NotifyCenter.deregisterSubscriber(subscriber); } finally { subscriber = null; } }); } private static class MockSubscriber extends Subscriber { private final Class subscribedEvent; private final boolean ignoreExpiredEvent; private final CountDownLatch latch; private MockSubscriber(Class subscribedEvent, boolean ignoreExpiredEvent) { this(subscribedEvent, ignoreExpiredEvent, null); } public MockSubscriber(Class subscribedEvent, boolean ignoreExpiredEvent, CountDownLatch latch) { this.subscribedEvent = subscribedEvent; this.ignoreExpiredEvent = ignoreExpiredEvent; this.latch = latch; } @Override public void onEvent(Event event) { count.incrementAndGet(); if (null != latch) { latch.countDown(); } } @Override public Class subscribeType() { return subscribedEvent; } @Override public boolean ignoreExpireEvent() { return ignoreExpiredEvent; } } private static class MockSmartSubscriber extends SmartSubscriber { private final List> subscribedEvents; private final CountDownLatch latch; private MockSmartSubscriber(List> subscribedEvents) { this(subscribedEvents, null); } public MockSmartSubscriber(List> subscribedEvents, CountDownLatch latch) { this.subscribedEvents = subscribedEvents; this.latch = latch; } @Override public void onEvent(Event event) { count.incrementAndGet(); if (null != latch) { latch.countDown(); } } @Override public List> subscribeTypes() { return subscribedEvents; } } private static class TestSlowEvent extends SlowEvent { private static final long serialVersionUID = 6713279688910446154L; } private static class TestSlowEvent1 extends SlowEvent { private static final long serialVersionUID = 5946729801676058102L; } private static class TestEvent extends Event { private static final long serialVersionUID = 2522362576233446960L; @Override public long sequence() { return System.currentTimeMillis(); } } private static class NoPublisherEvent extends Event { private static final long serialVersionUID = 6532409163269714916L; } private static class SharedEvent extends Event { private static final long serialVersionUID = 7648766983252000074L; } private static class PluginEvent extends Event { private static final long serialVersionUID = -7787588724415976798L; @Override public boolean isPluginEvent() { return true; } } private static class ExpireEvent extends Event { private static final long serialVersionUID = 3024284255874382548L; private final long no; ExpireEvent(long no) { this.no = no; } @Override public long sequence() { return no; } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/packagescan/DefaultPackageScanTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan; import com.alibaba.nacos.common.packagescan.mock.AnnotationClass; import com.alibaba.nacos.common.packagescan.mock.MockClass; import com.alibaba.nacos.common.packagescan.mock.TestScan; import com.alibaba.nacos.common.packagescan.resource.PathMatchingResourcePatternResolver; import com.alibaba.nacos.common.packagescan.resource.Resource; 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.junit.jupiter.MockitoExtension; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.util.Set; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DefaultPackageScanTest { @Mock PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver; DefaultPackageScan packageScan; @BeforeEach void setUp() throws Exception { packageScan = new DefaultPackageScan(); } @Test void testGetSubTypesOf() { packageScan = new DefaultPackageScan(); Set> subTypesOf = packageScan.getSubTypesOf(AnnotationClass.class.getPackage().getName(), MockClass.class); assertEquals(3, subTypesOf.size()); } @Test void testGetTypesAnnotatedWith() { packageScan = new DefaultPackageScan(); Set> actual = packageScan.getTypesAnnotatedWith(AnnotationClass.class.getPackage().getName(), TestScan.class); assertEquals(1, actual.size()); assertEquals(AnnotationClass.class, actual.iterator().next()); } @Test void testGetSubTypesOfWithException() throws NoSuchFieldException, IllegalAccessException, IOException { setResolver(); String path = AnnotationClass.class.getPackage().getName(); when(pathMatchingResourcePatternResolver.getResources(anyString())).thenThrow(new IOException("test")); Set> subTypesOf = packageScan.getSubTypesOf(path, MockClass.class); assertTrue(subTypesOf.isEmpty()); } @Test void testGetTypesAnnotatedWithException() throws NoSuchFieldException, IllegalAccessException, IOException { setResolver(); String path = AnnotationClass.class.getPackage().getName(); when(pathMatchingResourcePatternResolver.getResources(anyString())).thenThrow(new IOException("test")); Set> actual = packageScan.getTypesAnnotatedWith(path, TestScan.class); assertTrue(actual.isEmpty()); } @Test void testClassVersionNotMatch() throws NoSuchFieldException, IllegalAccessException, IOException { setResolver(); Resource resource = mock(Resource.class); byte[] testCase = new byte[8]; testCase[7] = (byte) 64; InputStream inputStream = new ByteArrayInputStream(testCase); when(resource.getInputStream()).thenReturn(inputStream); String path = AnnotationClass.class.getPackage().getName(); when(pathMatchingResourcePatternResolver.getResources(anyString())).thenReturn(new Resource[] {resource}); Set> subTypesOf = packageScan.getSubTypesOf(path, MockClass.class); assertTrue(subTypesOf.isEmpty()); } private void setResolver() throws NoSuchFieldException, IllegalAccessException { Field field = DefaultPackageScan.class.getDeclaredField("resourcePatternResolver"); field.setAccessible(true); field.set(packageScan, pathMatchingResourcePatternResolver); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/packagescan/mock/AnnotationClass.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan.mock; @TestScan public class AnnotationClass extends MockClass { } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/packagescan/mock/MockClass.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan.mock; public class MockClass { } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/packagescan/mock/NoAnnotationClass.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan.mock; public class NoAnnotationClass extends MockClass { } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/packagescan/mock/TestScan.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.packagescan.mock; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.RUNTIME) public @interface TestScan { } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/paramcheck/DefaultParamCheckerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; import com.alibaba.nacos.common.utils.RandomUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Map; 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 DefaultParamCheckerTest { DefaultParamChecker paramChecker; int maxMetadataLength = RandomUtils.nextInt(1024, 10240); @BeforeEach void setUp() throws Exception { System.setProperty("nacos.naming.service.metadata.length", String.valueOf(maxMetadataLength)); paramChecker = new DefaultParamChecker(); } @Test void testCheckerType() { assertEquals("default", paramChecker.getCheckerType()); } @Test void testCheckEmptyParamInfoList() { ParamCheckResponse actual = paramChecker.checkParamInfoList(null); assertTrue(actual.isSuccess()); actual = paramChecker.checkParamInfoList(Collections.emptyList()); assertTrue(actual.isSuccess()); } @Test void testCheckEmptyParamInfo() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); paramInfos.add(null); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForNamespaceShowName() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String namespaceShowName = buildStringLength(257); paramInfo.setNamespaceShowName(namespaceShowName); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'namespaceShowName' is illegal, the param length should not exceed 256.", actual.getMessage()); // Pattern paramInfo.setNamespaceShowName("hsbfkj@$!#khdkad"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'namespaceShowName' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setNamespaceShowName("测试"); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForNamespaceId() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String namespaceId = buildStringLength(65); paramInfo.setNamespaceId(namespaceId); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'namespaceId/tenant' is illegal, the param length should not exceed 64.", actual.getMessage()); // Pattern paramInfo.setNamespaceId("hsbfkj@$!#khdkad"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'namespaceId/tenant' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setNamespaceId("123-ashdal"); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForDataId() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String dataId = buildStringLength(257); paramInfo.setDataId(dataId); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'dataId' is illegal, the param length should not exceed 256.", actual.getMessage()); // Pattern paramInfo.setDataId("hsbfkj@$!#khdkad"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'dataId' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setDataId("a-zA-Z0-9-_:."); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForServiceName() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String serviceName = buildStringLength(513); paramInfo.setServiceName(serviceName); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'serviceName' is illegal, the param length should not exceed 512.", actual.getMessage()); // Pattern paramInfo.setServiceName("@hsbfkj$@@!#khdkad啊"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'serviceName' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setServiceName("com.aaa@bbb#_{}-b:v1.2.2"); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForGroup() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String group = buildStringLength(129); paramInfo.setGroup(group); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'group' is illegal, the param length should not exceed 128.", actual.getMessage()); // Pattern paramInfo.setGroup("@hsbfkj$@@!#khdkad啊@@"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'group' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setGroup("a-zA-Z0-9-_:."); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForClusters() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String cluster = buildStringLength(65); paramInfo.setClusters(cluster + "," + cluster); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'cluster' is illegal, the param length should not exceed 64.", actual.getMessage()); // Pattern paramInfo.setClusters("@hsbfkj$@@!#khdkad啊@@"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'cluster' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setClusters("0-9a-zA-Z-_,DEFAULT_abc-100"); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForCluster() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String cluster = buildStringLength(65); paramInfo.setCluster(cluster); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'cluster' is illegal, the param length should not exceed 64.", actual.getMessage()); // Pattern paramInfo.setCluster("@hsbfkj$@@!#khdkad啊@@"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'cluster' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setCluster("0-9a-zA-Z-_"); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForIp() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Max Length String ip = buildStringLength(129); paramInfo.setIp(ip); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'ip' is illegal, the param length should not exceed 128.", actual.getMessage()); // Pattern paramInfo.setIp("禁止中文"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'ip' is illegal, illegal characters should not appear in the param.", actual.getMessage()); // Success paramInfo.setIp("host_or_domain_or_ipv4_or_ipv6"); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForPort() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); // Negative port paramInfo.setPort("-1"); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'port' is illegal, the value should be between 0 and 65535.", actual.getMessage()); // Over than range paramInfo.setPort("65536"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'port' is illegal, the value should be between 0 and 65535.", actual.getMessage()); // Not number paramInfo.setPort("port"); actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals("Param 'port' is illegal, the value should be between 0 and 65535.", actual.getMessage()); // Success paramInfo.setPort("8848"); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } @Test void testCheckParamInfoForMetadata() { ParamInfo paramInfo = new ParamInfo(); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); Map metadata = new HashMap<>(); paramInfo.setMetadata(metadata); // Max length metadata.put("key1", ""); metadata.put("key2", buildStringLength(maxMetadataLength)); ParamCheckResponse actual = paramChecker.checkParamInfoList(paramInfos); assertFalse(actual.isSuccess()); assertEquals(String.format("Param 'Metadata' is illegal, the param length should not exceed %d.", maxMetadataLength), actual.getMessage()); // Success metadata.put("key2", String.format("Any key and value, only require length sum not more than %d.", maxMetadataLength)); actual = paramChecker.checkParamInfoList(paramInfos); assertTrue(actual.isSuccess()); } private String buildStringLength(int length) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < length; i++) { builder.append("a"); } return builder.toString(); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/paramcheck/MockParamChecker.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; import java.util.List; public class MockParamChecker extends AbstractParamChecker { @Override public String getCheckerType() { return "mock"; } @Override public ParamCheckResponse checkParamInfoList(List paramInfos) { return new ParamCheckResponse(); } @Override public void initParamCheckRule() { } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/paramcheck/ParamCheckerManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.paramcheck; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertTrue; class ParamCheckerManagerTest { @Test void testGetParamCheckerNonExistType() { assertTrue(ParamCheckerManager.getInstance().getParamChecker("non") instanceof DefaultParamChecker); } @Test void testGetParamCheckerNull() { assertTrue(ParamCheckerManager.getInstance().getParamChecker("") instanceof DefaultParamChecker); assertTrue(ParamCheckerManager.getInstance().getParamChecker(null) instanceof DefaultParamChecker); } @Test void testGetParamCheckerDefault() { assertTrue(ParamCheckerManager.getInstance().getParamChecker("default") instanceof DefaultParamChecker); } @Test void testGetParamCheckerOther() { assertTrue(ParamCheckerManager.getInstance().getParamChecker("mock") instanceof MockParamChecker); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/pathencoder/PathEncoderManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.pathencoder; import com.alibaba.nacos.common.pathencoder.impl.WindowsEncoder; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.nio.charset.Charset; 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.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; class PathEncoderManagerTest { private String cachedOsName; private Field targetEncoder; private Object cachedEncoder; @BeforeEach void setUp() throws Exception { cachedOsName = System.getProperty("os.name"); targetEncoder = PathEncoderManager.class.getDeclaredField("targetEncoder"); targetEncoder.setAccessible(true); cachedEncoder = targetEncoder.get(PathEncoderManager.getInstance()); } @AfterEach void tearDown() throws Exception { System.setProperty("os.name", cachedOsName); targetEncoder.set(PathEncoderManager.getInstance(), cachedEncoder); } @Test void testInitWithWindows() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor constructor = PathEncoderManager.class.getDeclaredConstructor(); constructor.setAccessible(true); System.setProperty("os.name", "window"); PathEncoderManager instance = constructor.newInstance(); assertTrue(targetEncoder.get(instance) instanceof WindowsEncoder); } /** * test expose method. */ @Test void testWindowsEncode() throws Exception { // load static PathEncoderManager instance = PathEncoderManager.getInstance(); String case1 = "aa||a"; String case2 = "aa%A9%%A9%a"; // try to encode if in windows targetEncoder.set(instance, new WindowsEncoder()); assertEquals(PathEncoderManager.getInstance().encode(case1), case2); assertEquals(PathEncoderManager.getInstance().decode(case2), case1); } @Test void testEncodeWithNonExistOs() throws Exception { // load static PathEncoderManager instance = PathEncoderManager.getInstance(); // remove impl targetEncoder.set(instance, null); // try to encode, non windows String case1 = "aa||a"; assertEquals(PathEncoderManager.getInstance().encode(case1), case1); String case2 = "aa%A9%%A9%a"; assertEquals(PathEncoderManager.getInstance().decode(case2), case2); } @Test void testEncodeForNull() throws IllegalAccessException { PathEncoder mockPathEncoder = mock(PathEncoder.class); targetEncoder.set(PathEncoderManager.getInstance(), mockPathEncoder); assertNull(PathEncoderManager.getInstance().encode(null)); assertNull(PathEncoderManager.getInstance().decode(null)); verify(mockPathEncoder, never()).encode(null, Charset.defaultCharset().name()); verify(mockPathEncoder, never()).decode(null, Charset.defaultCharset().name()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/pathencoder/WindowsEncoderTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.pathencoder; import com.alibaba.nacos.common.pathencoder.impl.WindowsEncoder; import org.junit.jupiter.api.Test; import java.nio.charset.Charset; 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 WindowsEncoderTest { WindowsEncoder windowsEncoder = new WindowsEncoder(); /** * test encode. */ @Test void testEncode() { String charset = Charset.defaultCharset().name(); String case1 = "aaaadsaknkf"; assertEquals(windowsEncoder.encode(case1, charset), case1); // matches one String case2 = "aaaa\\dsaknkf"; assertEquals("aaaa%A1%dsaknkf", windowsEncoder.encode(case2, charset)); String case3 = "aaaa/dsaknkf"; assertEquals("aaaa%A2%dsaknkf", windowsEncoder.encode(case3, charset)); String case4 = "aaaa:dsaknkf"; assertEquals("aaaa%A3%dsaknkf", windowsEncoder.encode(case4, charset)); String case5 = "aaaa*dsaknkf"; assertEquals("aaaa%A4%dsaknkf", windowsEncoder.encode(case5, charset)); String case6 = "aaaa?dsaknkf"; assertEquals("aaaa%A5%dsaknkf", windowsEncoder.encode(case6, charset)); String case7 = "aaaa\"dsaknkf"; assertEquals("aaaa%A6%dsaknkf", windowsEncoder.encode(case7, charset)); String case8 = "aaaadsaknkf", windowsEncoder.decode(case9, charset)); String case10 = "aaaa%A9%dsaknkf"; assertEquals("aaaa|dsaknkf", windowsEncoder.decode(case10, charset)); // matches more String case11 = "aaaa%A7%dsa%A7%%A8%%A8%knkf"; assertEquals("aaaa>knkf", windowsEncoder.decode(case11, charset)); String case12 = "aaaa%A6%dsa%A6%%A1%%A1%knkf"; assertEquals("aaaa\"dsa\"\\\\knkf", windowsEncoder.decode(case12, charset)); } /** * test needEncode. */ @Test void testNeedEncode() { // / : ? " < > | \ assertFalse(windowsEncoder.needEncode(null)); String case1 = "aaaadsaknkf"; assertFalse(windowsEncoder.needEncode(case1)); String case2 = "?asda"; assertTrue(windowsEncoder.needEncode(case2)); String case3 = "/asdasd"; assertTrue(windowsEncoder.needEncode(case3)); String case4 = "as\\dasda"; assertTrue(windowsEncoder.needEncode(case4)); String case5 = "asd::as"; assertTrue(windowsEncoder.needEncode(case5)); String case6 = "sda\"sda"; assertTrue(windowsEncoder.needEncode(case6)); String case7 = "asdas { PayloadRegistry.register("ErrorResponse", ErrorResponse.class); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/TlsConfigTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote; 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.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class TlsConfigTest { @Test void testTlsConfig() { TlsConfig tlsConfig = new TlsConfig(); assertFalse(tlsConfig.getEnableTls()); assertFalse(tlsConfig.getMutualAuthEnable()); assertNull(tlsConfig.getProtocols()); assertNull(tlsConfig.getCiphers()); assertFalse(tlsConfig.getTrustAll()); assertNull(tlsConfig.getTrustCollectionCertFile()); assertNull(tlsConfig.getCertPrivateKeyPassword()); assertNull(tlsConfig.getCertPrivateKey()); assertNull(tlsConfig.getCertChainFile()); assertEquals("", tlsConfig.getSslProvider()); // Set values tlsConfig.setEnableTls(true); tlsConfig.setMutualAuthEnable(true); tlsConfig.setProtocols("TLSv1.1,TLSv1.2,TLSv1.3"); tlsConfig.setCiphers("cipher1,cipher2"); tlsConfig.setTrustAll(true); tlsConfig.setTrustCollectionCertFile("certFile"); tlsConfig.setCertPrivateKeyPassword("password"); tlsConfig.setCertPrivateKey("privateKey"); tlsConfig.setCertChainFile("chainFile"); tlsConfig.setSslProvider("OPENSSL"); // Test values assertTrue(tlsConfig.getEnableTls()); assertTrue(tlsConfig.getMutualAuthEnable()); assertEquals("TLSv1.1,TLSv1.2,TLSv1.3", tlsConfig.getProtocols()); assertEquals("cipher1,cipher2", tlsConfig.getCiphers()); assertTrue(tlsConfig.getTrustAll()); assertEquals("certFile", tlsConfig.getTrustCollectionCertFile()); assertEquals("password", tlsConfig.getCertPrivateKeyPassword()); assertEquals("privateKey", tlsConfig.getCertPrivateKey()); assertEquals("chainFile", tlsConfig.getCertChainFile()); assertEquals("OPENSSL", tlsConfig.getSslProvider()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/ConnectionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.RequestFuture; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.Response; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.Collections; 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 ConnectionTest { Connection connection; @BeforeEach void setUp() throws Exception { connection = new Connection(new RpcClient.ServerInfo("127.0.0.1", 8848)) { @Override public Response request(Request request, long timeoutMills) throws NacosException { return null; } @Override public RequestFuture requestFuture(Request request) throws NacosException { return null; } @Override public void asyncRequest(Request request, RequestCallBack requestCallBack) throws NacosException { } @Override public void close() { } }; } @AfterEach void tearDown() throws Exception { connection.close(); } @Test void testSetConnectionId() { assertNull(connection.getConnectionId()); connection.setConnectionId("testConnectionId"); assertEquals("testConnectionId", connection.getConnectionId()); } @Test void testGetConnectionAbility() { assertFalse(connection.isAbilitiesSet()); assertEquals(AbilityStatus.UNKNOWN, connection.getConnectionAbility(AbilityKey.SDK_CLIENT_FUZZY_WATCH)); connection.setAbilityTable(Collections.singletonMap(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName(), true)); assertTrue(connection.isAbilitiesSet()); assertEquals(AbilityStatus.UNKNOWN, connection.getConnectionAbility(AbilityKey.SDK_CLIENT_FUZZY_WATCH)); assertEquals(AbilityStatus.SUPPORTED, connection.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK)); connection.setAbilityTable(Collections.singletonMap(AbilityKey.SERVER_DISTRIBUTED_LOCK.getName(), false)); assertEquals(AbilityStatus.NOT_SUPPORTED, connection.getConnectionAbility(AbilityKey.SERVER_DISTRIBUTED_LOCK)); } @Test void testSetAbandon() { assertFalse(connection.isAbandon()); connection.setAbandon(true); assertTrue(connection.isAbandon()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientConfigFactoryTest.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcConstants; import com.alibaba.nacos.common.utils.ThreadUtils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; /** * RpcClientConfigFactory. * * @author Nacos */ public class RpcClientConfigFactoryTest { private RpcClientConfigFactory factory; @BeforeEach void setUp() { factory = RpcClientConfigFactory.getInstance(); } @Test void testGetInstanceSingletonPattern() { RpcClientConfigFactory instance1 = RpcClientConfigFactory.getInstance(); RpcClientConfigFactory instance2 = RpcClientConfigFactory.getInstance(); assertEquals(instance1, instance2); } @Test void testCreateGrpcClientConfig() { Properties properties = new Properties(); properties.setProperty(GrpcConstants.GRPC_NAME, "testClient"); properties.setProperty(GrpcConstants.GRPC_CHANNEL_CAPABILITY_NEGOTIATION_TIMEOUT, "1000"); properties.setProperty(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIME, "60"); properties.setProperty(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIMEOUT, "300"); properties.setProperty(GrpcConstants.GRPC_CONNECT_KEEP_ALIVE_TIME, "120"); properties.setProperty(GrpcConstants.GRPC_HEALTHCHECK_RETRY_TIMES, "3"); properties.setProperty(GrpcConstants.GRPC_HEALTHCHECK_TIMEOUT, "500"); properties.setProperty(GrpcConstants.GRPC_MAX_INBOUND_MESSAGE_SIZE, "1024"); properties.setProperty(GrpcConstants.GRPC_RETRY_TIMES, "5"); properties.setProperty(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT, "1000"); properties.setProperty(GrpcConstants.GRPC_THREADPOOL_CORE_SIZE, "10"); properties.setProperty(GrpcConstants.GRPC_THREADPOOL_KEEPALIVETIME, "60"); properties.setProperty(GrpcConstants.GRPC_THREADPOOL_MAX_SIZE, "20"); properties.setProperty(GrpcConstants.GRPC_QUEUESIZE, "100"); properties.setProperty(GrpcConstants.GRPC_TIMEOUT_MILLS, "2000"); Map labels = new HashMap<>(); labels.put("labelKey1", "labelValue1"); labels.put("labelKey2", "labelValue2"); labels.put("tls.enable", "false"); GrpcClientConfig config = factory.createGrpcClientConfig(properties, labels); assertEquals(labels, config.labels()); assertEquals(1000, config.capabilityNegotiationTimeout()); assertEquals(60, config.channelKeepAlive()); assertEquals(300, config.channelKeepAliveTimeout()); assertEquals(120, config.connectionKeepAlive()); assertEquals(3, config.healthCheckRetryTimes()); assertEquals(500, config.healthCheckTimeOut()); assertEquals(1024, config.maxInboundMessageSize()); assertEquals("testClient", config.name()); assertEquals(5, config.retryTimes()); assertEquals(1000, config.serverCheckTimeOut()); assertEquals(10, config.threadPoolCoreSize()); assertEquals(60, config.threadPoolKeepAlive()); assertEquals(20, config.threadPoolMaxSize()); assertEquals(100, config.threadPoolQueueSize()); assertEquals(2000, config.timeOutMills()); } @Test void testDefaultGrpcClientConfig() { Properties properties = new Properties(); Map labels = new HashMap<>(); labels.put("tls.enable", "false"); GrpcClientConfig config = factory.createGrpcClientConfig(properties, labels); assertEquals(labels, config.labels()); assertEquals(5000, config.capabilityNegotiationTimeout()); assertEquals(360000, config.channelKeepAlive()); // 6 * 60 * 1000 assertEquals(20000, config.channelKeepAliveTimeout()); // TimeUnit.SECONDS.toMillis(20L) assertEquals(5000, config.connectionKeepAlive()); assertEquals(3, config.healthCheckRetryTimes()); assertEquals(3000, config.healthCheckTimeOut()); assertEquals(10485760, config.maxInboundMessageSize()); // 10 * 1024 * 1024 assertEquals(null, config.name()); assertEquals(3, config.retryTimes()); assertEquals(3000, config.serverCheckTimeOut()); assertEquals(ThreadUtils.getSuitableThreadCount(2), config.threadPoolCoreSize()); assertEquals(10000, config.threadPoolKeepAlive()); assertEquals(ThreadUtils.getSuitableThreadCount(8), config.threadPoolMaxSize()); assertEquals(10000, config.threadPoolQueueSize()); assertEquals(3000, config.timeOutMills()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientFactoryTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.grpc.DefaultGrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcClusterClient; import com.alibaba.nacos.common.utils.CollectionUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; 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; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class RpcClientFactoryTest { static Field clientMapField; @Mock RpcClient rpcClient; @Mock(lenient = true) RpcClientTlsConfig clusterClientTlsConfig; @Mock(lenient = true) RpcClientTlsConfig rpcClientTlsConfig; @BeforeAll static void setUpBeforeClass() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { clientMapField = RpcClientFactory.class.getDeclaredField("CLIENT_MAP"); clientMapField.setAccessible(true); Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); getDeclaredFields0.setAccessible(true); Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); Field modifiersField1 = null; for (Field each : fields) { if ("modifiers".equals(each.getName())) { modifiersField1 = each; } } if (modifiersField1 != null) { modifiersField1.setAccessible(true); modifiersField1.setInt(clientMapField, clientMapField.getModifiers() & ~Modifier.FINAL); } } @AfterEach void tearDown() throws IllegalAccessException { clientMapField.set(null, new ConcurrentHashMap<>()); } @Test void testGetAllClientEntries() throws IllegalAccessException { assertTrue(RpcClientFactory.getAllClientEntries().isEmpty()); clientMapField.set(null, Collections.singletonMap("testClient", rpcClient)); assertEquals(1, RpcClientFactory.getAllClientEntries().size()); } @Test void testDestroyClientWhenClientExistThenRemoveAndShutDownRpcClient() throws IllegalAccessException, NacosException { clientMapField.set(null, new ConcurrentHashMap<>(Collections.singletonMap("testClient", rpcClient))); RpcClientFactory.destroyClient("testClient"); assertTrue(RpcClientFactory.getAllClientEntries().isEmpty()); verify(rpcClient).shutdown(); } @Test void testDestroyClientWhenClientNotExistThenDoNothing() throws IllegalAccessException, NacosException { clientMapField.set(null, new ConcurrentHashMap<>(Collections.singletonMap("testClient", rpcClient))); RpcClientFactory.destroyClient("notExistClientName"); Map.Entry element = CollectionUtils.getOnlyElement(RpcClientFactory.getAllClientEntries()); assertEquals("testClient", element.getKey()); assertEquals(rpcClient, element.getValue()); verify(rpcClient, times(0)).shutdown(); } @Test void testGetClient() throws IllegalAccessException { // may be null assertNull(RpcClientFactory.getClient("notExistClientName")); clientMapField.set(null, new ConcurrentHashMap<>(Collections.singletonMap("testClient", rpcClient))); assertEquals(rpcClient, RpcClientFactory.getClient("testClient")); } @Test void testCreateClientWhenNotCreatedThenCreate() { RpcClient client = RpcClientFactory.createClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue")); Map labesMap = new HashMap<>(); labesMap.put("labelKey", "labelValue"); labesMap.put("tls.enable", "false"); assertEquals(labesMap, client.rpcClientConfig.labels()); assertEquals(ConnectionType.GRPC, client.getConnectionType()); assertEquals("testClient", CollectionUtils.getOnlyElement(RpcClientFactory.getAllClientEntries()).getKey()); } @Test void testCreateClientWhenAlreadyCreatedThenNotCreateAgain() { RpcClient client1 = RpcClientFactory.createClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue")); RpcClient client2 = RpcClientFactory.createClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue")); assertEquals(client1, client2); assertEquals(1, RpcClientFactory.getAllClientEntries().size()); } @Test void testCreatedClientWhenConnectionTypeNotMappingThenThrowException() { assertThrows(Exception.class, () -> { RpcClientFactory.createClient("testClient", mock(ConnectionType.class), Collections.singletonMap("labelKey", "labelValue")); }); } @Test void testCreateClusterClientWhenNotCreatedThenCreate() { RpcClient client = RpcClientFactory.createClusterClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue")); Map labesMap = new HashMap<>(); labesMap.put("labelKey", "labelValue"); labesMap.put("tls.enable", "false"); assertEquals(labesMap, client.rpcClientConfig.labels()); assertEquals(ConnectionType.GRPC, client.getConnectionType()); assertEquals("testClient", CollectionUtils.getOnlyElement(RpcClientFactory.getAllClientEntries()).getKey()); } @Test void testCreateClusterClientWhenAlreadyCreatedThenNotCreateAgain() { RpcClient client1 = RpcClientFactory.createClusterClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue")); RpcClient client2 = RpcClientFactory.createClusterClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue")); assertEquals(client1, client2); assertEquals(1, RpcClientFactory.getAllClientEntries().size()); } @Test void testCreatedClusterClientWhenConnectionTypeNotMappingThenThrowException() { assertThrows(Exception.class, () -> { RpcClientFactory.createClusterClient("testClient", mock(ConnectionType.class), Collections.singletonMap("labelKey", "labelValue")); }); } @Test void testCreateClusterClientTsl() { Mockito.when(clusterClientTlsConfig.getEnableTls()).thenReturn(true); RpcClient client = RpcClientFactory.createClusterClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue"), clusterClientTlsConfig); Map labesMap = new HashMap<>(); labesMap.put("labelKey", "labelValue"); labesMap.put("tls.enable", "true"); assertEquals(labesMap, client.rpcClientConfig.labels()); assertEquals(ConnectionType.GRPC, client.getConnectionType()); assertEquals("testClient", CollectionUtils.getOnlyElement(RpcClientFactory.getAllClientEntries()).getKey()); } @Test void testCreateClientTsl() { Mockito.when(rpcClientTlsConfig.getEnableTls()).thenReturn(true); RpcClient client = RpcClientFactory.createClient("testClient", ConnectionType.GRPC, Collections.singletonMap("labelKey", "labelValue"), rpcClientTlsConfig); Map labesMap = new HashMap<>(); labesMap.put("labelKey", "labelValue"); labesMap.put("tls.enable", "true"); assertEquals(labesMap, client.rpcClientConfig.labels()); assertEquals(ConnectionType.GRPC, client.getConnectionType()); assertEquals("testClient", CollectionUtils.getOnlyElement(RpcClientFactory.getAllClientEntries()).getKey()); } @Test void testCreateClientWithProperties() { Mockito.when(rpcClientTlsConfig.getEnableTls()).thenReturn(true); Properties properties = new Properties(); Map labels = new HashMap<>(); labels.put("tls.enable", "false"); labels.put("labelKey", "labelValue"); GrpcClientConfig grpcClientConfig = RpcClientConfigFactory.getInstance() .createGrpcClientConfig(properties, labels); RpcClient testClient = RpcClientFactory.createClient("testClient", ConnectionType.GRPC, grpcClientConfig); assertEquals(testClient.getLabels(), labels); assertEquals(testClient.getConnectionType(), ConnectionType.GRPC); assertEquals(testClient.getName(), "testClient"); } @Test void testCreateClusterClientWithProperties() { Mockito.when(rpcClientTlsConfig.getEnableTls()).thenReturn(true); Properties properties = new Properties(); properties.setProperty("nacos.remote.client.grpc.maxinbound.message.size", "100000"); Map labels = new HashMap<>(); labels.put("tls.enable", "false"); labels.put("labelKey", "labelValue"); GrpcClientConfig clientConfig = DefaultGrpcClientConfig.newBuilder().buildClusterFromProperties(properties) .setLabels(labels).build(); GrpcClusterClient testClient = (GrpcClusterClient) RpcClientFactory.createClusterClient("testClient", ConnectionType.GRPC, clientConfig); assertEquals(testClient.getLabels(), labels); assertEquals(testClient.getConnectionType(), ConnectionType.GRPC); assertEquals(testClient.getName(), "testClient"); GrpcClientConfig testConfig = (GrpcClientConfig) testClient.rpcClientConfig; assertEquals(testConfig.maxInboundMessageSize(), 100000); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import com.alibaba.nacos.api.ability.constant.AbilityKey; import com.alibaba.nacos.api.ability.constant.AbilityStatus; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.request.ClientDetectionRequest; import com.alibaba.nacos.api.remote.request.ConnectResetRequest; import com.alibaba.nacos.api.remote.request.HealthCheckRequest; import com.alibaba.nacos.api.remote.request.Request; import com.alibaba.nacos.api.remote.response.ClientDetectionResponse; import com.alibaba.nacos.api.remote.response.ConnectResetResponse; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.HealthCheckResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.grpc.DefaultGrpcClientConfig; import com.alibaba.nacos.common.remote.client.grpc.GrpcConnection; 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.junit.jupiter.MockitoExtension; import org.mockito.stubbing.Answer; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; 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; @ExtendWith(MockitoExtension.class) class RpcClientTest { RpcClient rpcClient; Field serverListFactoryField; Field reconnectionSignalField; Method resolveServerInfoMethod; Method healthCheck; Answer runAsSync; Answer notInvoke; @Mock ServerListFactory serverListFactory; @Mock Connection connection; RpcClientConfig rpcClientConfig; @BeforeEach void setUp() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { rpcClientConfig = spy(new RpcClientConfig() { @Override public String name() { return "test"; } @Override public int retryTimes() { return 1; } @Override public long timeOutMills() { return 3000L; } @Override public long connectionKeepAlive() { return 5000L; } @Override public int healthCheckRetryTimes() { return 1; } @Override public long healthCheckTimeOut() { return 3000L; } @Override public Map labels() { return new HashMap<>(); } }); rpcClient = spy(new RpcClient(rpcClientConfig) { @Override public ConnectionType getConnectionType() { return null; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) { return null; } }); serverListFactoryField = RpcClient.class.getDeclaredField("serverListFactory"); serverListFactoryField.setAccessible(true); reconnectionSignalField = RpcClient.class.getDeclaredField("reconnectionSignal"); reconnectionSignalField.setAccessible(true); Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); getDeclaredFields0.setAccessible(true); Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); Field modifiersField1 = null; for (Field each : fields) { if ("modifiers".equals(each.getName())) { modifiersField1 = each; } } if (modifiersField1 != null) { modifiersField1.setAccessible(true); modifiersField1.setInt(reconnectionSignalField, reconnectionSignalField.getModifiers() & ~Modifier.FINAL); } resolveServerInfoMethod = RpcClient.class.getDeclaredMethod("resolveServerInfo", String.class); resolveServerInfoMethod.setAccessible(true); healthCheck = RpcClient.class.getDeclaredMethod("healthCheck"); healthCheck.setAccessible(true); runAsSync = invocationOnMock -> { Runnable runnable = (Runnable) invocationOnMock.getArguments()[0]; runnable.run(); return null; }; notInvoke = invocationOnMock -> null; } @AfterEach void tearDown() throws IllegalAccessException, NacosException { rpcClientConfig.labels().clear(); rpcClient.rpcClientStatus.set(RpcClientStatus.WAIT_INIT); serverListFactoryField.set(rpcClient, null); ((Queue) reconnectionSignalField.get(rpcClient)).clear(); rpcClient.currentConnection = null; System.clearProperty("nacos.server.port"); rpcClient.eventLinkedBlockingQueue.clear(); rpcClient.shutdown(); } @Test void testInitServerListFactory() { rpcClient.rpcClientStatus.set(RpcClientStatus.WAIT_INIT); rpcClient.serverListFactory(serverListFactory); assertEquals(RpcClientStatus.INITIALIZED, rpcClient.rpcClientStatus.get()); rpcClient.rpcClientStatus.set(RpcClientStatus.INITIALIZED); rpcClient.serverListFactory(serverListFactory); assertEquals(RpcClientStatus.INITIALIZED, rpcClient.rpcClientStatus.get()); RpcClient client1 = new RpcClient(new RpcClientConfig() { @Override public String name() { return "test"; } @Override public int retryTimes() { return 3; } @Override public long timeOutMills() { return 3000L; } @Override public long connectionKeepAlive() { return 5000L; } @Override public int healthCheckRetryTimes() { return 1; } @Override public long healthCheckTimeOut() { return 3000L; } @Override public Map labels() { return new HashMap<>(); } }, serverListFactory) { @Override public ConnectionType getConnectionType() { return null; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) { return null; } }; assertEquals(RpcClientStatus.INITIALIZED, client1.rpcClientStatus.get()); RpcClient client2 = new RpcClient(rpcClientConfig, serverListFactory) { @Override public ConnectionType getConnectionType() { return null; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) { return null; } }; assertEquals(RpcClientStatus.INITIALIZED, client2.rpcClientStatus.get()); } @Test void testLabels() { when(rpcClientConfig.labels()).thenReturn(Collections.singletonMap("labelKey1", "labelValue1")); Map.Entry element = rpcClient.getLabels().entrySet().iterator().next(); assertEquals("labelKey1", element.getKey()); assertEquals("labelValue1", element.getValue()); // accumulate labels Map map = new HashMap<>(); map.put("labelKey2", "labelValue2"); when(rpcClientConfig.labels()).thenReturn(map); assertEquals(1, rpcClient.getLabels().size()); assertEquals("test", rpcClient.getName()); } @Test void testOnServerListChangeWhenCurrentConnectionIsNullThenDoNothing() throws IllegalAccessException { int beforeSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); rpcClient.serverListFactory(serverListFactory); rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); assertEquals(beforeSize, afterSize); } @Test void testOnServerListChangeWhenServiceInfoIsNullThenDoNothing() throws IllegalAccessException { int beforeSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); rpcClient.currentConnection = mock(Connection.class); rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); assertEquals(beforeSize, afterSize); } @Test void testOnServerListChangeWhenCurrentConnectionIsNotInServerListThenSwitchServerAsync() throws IllegalAccessException { final int beforeSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); rpcClient.serverListFactory(serverListFactory); rpcClient.currentConnection = new GrpcConnection(new RpcClient.ServerInfo("10.10.10.10", 8848), null); doReturn(Collections.singletonList("")).when(serverListFactory).getServerList(); when(serverListFactory.getServerList()).thenReturn(Collections.singletonList("127.0.0.1")); rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); assertEquals(beforeSize + 1, afterSize); } @Test void testOnServerListChangeWhenCurrentConnectionIsInServerListThenDoNothing() throws IllegalAccessException { final int beforeSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); rpcClient.serverListFactory(serverListFactory); rpcClient.currentConnection = new GrpcConnection(new RpcClient.ServerInfo("10.10.10.10", 8848), null); doReturn(Collections.singletonList("http://10.10.10.10:8848")).when(serverListFactory).getServerList(); rpcClient.onServerListChange(); int afterSize = ((Queue) reconnectionSignalField.get(rpcClient)).size(); assertEquals(beforeSize, afterSize); } @Test void testResolveServerInfo1() throws InvocationTargetException, IllegalAccessException { assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "10.10.10.10::8848")).getAddress()); assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "10.10.10.10:8848")).getAddress()); assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10:8848")).getAddress()); assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10::8848")).getAddress()); assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10")).getAddress()); assertEquals("10.10.10.10:8848", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "https://10.10.10.10::8848")).getAddress()); } @Test void testResolveServerInfo2() throws InvocationTargetException, IllegalAccessException { System.setProperty("nacos.server.port", "4424"); assertEquals("10.10.10.10:4424", ((RpcClient.ServerInfo) resolveServerInfoMethod.invoke(rpcClient, "http://10.10.10.10")).getAddress()); } @Test void testRequestSuccess() throws NacosException, NoSuchFieldException, IllegalAccessException { rpcClient.currentConnection = connection; rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); when(connection.request(any(), anyLong())).thenReturn(new HealthCheckResponse()); Field lastActiveTimeStampField = RpcClient.class.getDeclaredField("lastActiveTimeStamp"); lastActiveTimeStampField.setAccessible(true); final long lastActiveTimeStamp = (long) lastActiveTimeStampField.get(rpcClient); Response response = rpcClient.request(new HealthCheckRequest()); assertTrue(response instanceof HealthCheckResponse); assertTrue(lastActiveTimeStamp <= (long) lastActiveTimeStampField.get(rpcClient)); } @Test void testRequestWithoutAnyTry() throws NacosException { assertThrows(NacosException.class, () -> { when(rpcClientConfig.retryTimes()).thenReturn(-1); rpcClient.request(null); }); } @Test void testRequestWhenClientAlreadyShutDownThenThrowException() throws NacosException { assertThrows(NacosException.class, () -> { rpcClient.rpcClientStatus.set(RpcClientStatus.SHUTDOWN); rpcClient.currentConnection = connection; rpcClient.request(null); }); } @Test void testRequestWhenTimeoutThenThrowException() throws NacosException { assertThrows(NacosException.class, () -> { rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; doReturn(null).when(connection).request(any(), anyLong()); rpcClient.request(null, 10000); }); } @Test void testRequestWhenResponseErrorThenThrowException() throws NacosException { assertThrows(NacosException.class, () -> { rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; doReturn(new ErrorResponse()).when(connection).request(any(), anyLong()); rpcClient.request(null, 10000); }); } @Test void testRequestWhenResponseUnregisterThenSwitchServer() throws NacosException { rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; ErrorResponse errorResponse = new ErrorResponse(); errorResponse.setErrorCode(NacosException.UN_REGISTER); doReturn(errorResponse).when(connection).request(any(), anyLong()); Exception exception = null; try { rpcClient.request(mock(Request.class), 10000); } catch (Exception e) { exception = e; } assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); verify(rpcClient).switchServerAsync(); assertNotNull(exception); } @Test void testAsyncRequestSuccess() throws NacosException { rpcClient.currentConnection = connection; rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); RequestCallBack requestCallBack = mock(RequestCallBack.class); when(requestCallBack.getTimeout()).thenReturn(1000L); rpcClient.asyncRequest(null, requestCallBack); verify(connection).asyncRequest(any(), any()); } @Test void testAsyncRequestWithoutAnyTry() throws NacosException { assertThrows(NacosException.class, () -> { when(rpcClientConfig.retryTimes()).thenReturn(-1); rpcClient.asyncRequest(null, null); }); } @Test void testAsyncRequestWhenClientAlreadyShutDownThenThrowException() throws NacosException { assertThrows(NacosException.class, () -> { rpcClient.rpcClientStatus.set(RpcClientStatus.SHUTDOWN); rpcClient.currentConnection = connection; RequestCallBack requestCallBack = mock(RequestCallBack.class); doReturn(10000L).when(requestCallBack).getTimeout(); rpcClient.asyncRequest(null, requestCallBack); }); } @Test void testAsyncRequestWhenSendRequestFailedMannyTimesThenSwitchServer() throws NacosException { rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; doThrow(new NacosException()).when(connection).asyncRequest(any(), any()); RequestCallBack requestCallBack = mock(RequestCallBack.class); doReturn(10000L).when(requestCallBack).getTimeout(); Exception exception = null; try { rpcClient.asyncRequest(null, requestCallBack); } catch (NacosException e) { exception = e; } verify(connection, atLeastOnce()).asyncRequest(any(), any()); verify(rpcClient).switchServerAsyncOnRequestFail(); assertNotNull(exception); assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); } @Test void testRequestFutureWithoutAnyTry() throws NacosException { assertThrows(NacosException.class, () -> { when(rpcClientConfig.retryTimes()).thenReturn(-1); rpcClient.requestFuture(null); }); } @Test void testRequestFutureWhenClientAlreadyShutDownThenThrowException() throws NacosException { assertThrows(NacosException.class, () -> { rpcClient.rpcClientStatus.set(RpcClientStatus.SHUTDOWN); rpcClient.currentConnection = connection; rpcClient.requestFuture(null); }); } @Test void testRequestFutureWhenRetryReachMaxRetryTimesThenSwitchServer() throws NacosException { when(rpcClientConfig.timeOutMills()).thenReturn(5000L); when(rpcClientConfig.retryTimes()).thenReturn(3); rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; doThrow(NacosException.class).when(connection).requestFuture(any()); Exception exception = null; try { rpcClient.requestFuture(null); } catch (NacosException e) { exception = e; } verify(connection, times(4)).requestFuture(any()); verify(rpcClient).switchServerAsyncOnRequestFail(); assertNotNull(exception); assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); } @Test void testRpcClientShutdownWhenClientDidntStart() throws NacosException { RpcClient rpcClient = new RpcClient(new RpcClientConfig() { @Override public String name() { return "test-client"; } @Override public int retryTimes() { return 3; } @Override public long timeOutMills() { return 3000L; } @Override public long connectionKeepAlive() { return 5000L; } @Override public int healthCheckRetryTimes() { return 1; } @Override public long healthCheckTimeOut() { return 3000L; } @Override public Map labels() { return new HashMap<>(); } }) { @Override public ConnectionType getConnectionType() { return null; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) { return null; } }; rpcClient.shutdown(); assertTrue(rpcClient.isShutdown()); } @Test void testHealthCheck() throws IllegalAccessException, NacosException { Random random = new Random(); int retry = random.nextInt(10); when(rpcClientConfig.healthCheckRetryTimes()).thenReturn(retry); rpcClient.rpcClientStatus.set(RpcClientStatus.RUNNING); rpcClient.currentConnection = connection; doThrow(new NacosException()).when(connection).request(any(), anyLong()); try { healthCheck.invoke(rpcClient); } catch (InvocationTargetException e) { e.printStackTrace(); } verify(connection, times(retry + 1)).request(any(), anyLong()); } @Test void testNextRpcServerForIpv4WithPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:7777"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("127.0.0.1:7777", actual.getAddress()); assertEquals("127.0.0.1", actual.getServerIp()); assertEquals(7777, actual.getServerPort()); } @Test void testNextRpcServerForIpv4WithoutPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("127.0.0.1"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("127.0.0.1:8848", actual.getAddress()); assertEquals("127.0.0.1", actual.getServerIp()); assertEquals(8848, actual.getServerPort()); } @Test void testNextRpcServerForIpv6WithPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("[fe80::35ba:6827:c5ff:d161%11]:7777"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("[fe80::35ba:6827:c5ff:d161%11]:7777", actual.getAddress()); assertEquals("[fe80::35ba:6827:c5ff:d161%11]", actual.getServerIp()); assertEquals(7777, actual.getServerPort()); } @Test void testNextRpcServerForIpv6WithoutPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("[fe80::35ba:6827:c5ff:d161%11]"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("[fe80::35ba:6827:c5ff:d161%11]:8848", actual.getAddress()); assertEquals("[fe80::35ba:6827:c5ff:d161%11]", actual.getServerIp()); assertEquals(8848, actual.getServerPort()); } @Test void testNextRpcServerForDomainWithPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("nacos.io:7777"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("nacos.io:7777", actual.getAddress()); assertEquals("nacos.io", actual.getServerIp()); assertEquals(7777, actual.getServerPort()); } @Test void testNextRpcServerForDomainWithoutPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("nacos.io"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("nacos.io:8848", actual.getAddress()); assertEquals("nacos.io", actual.getServerIp()); assertEquals(8848, actual.getServerPort()); } @Test void testNextRpcServerForLocalhostWithPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("localhost:7777"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("localhost:7777", actual.getAddress()); assertEquals("localhost", actual.getServerIp()); assertEquals(7777, actual.getServerPort()); } @Test void testNextRpcServerForLocalhostWithoutPort() { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn("localhost"); RpcClient.ServerInfo actual = rpcClient.nextRpcServer(); assertEquals("localhost:8848", actual.getAddress()); assertEquals("localhost", actual.getServerIp()); assertEquals(8848, actual.getServerPort()); } @Test void testNextRpcServerForEmpty() { assertThrows(IllegalArgumentException.class, () -> { RpcClient rpcClient = buildTestNextRpcServerClient(); rpcClient.serverListFactory(serverListFactory); when(serverListFactory.genNextServer()).thenReturn(""); rpcClient.nextRpcServer(); }); } private RpcClient buildTestNextRpcServerClient() { return new RpcClient(DefaultGrpcClientConfig.newBuilder().build()) { @Override public ConnectionType getConnectionType() { return null; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) { return null; } @Override public ServerInfo nextRpcServer() { return super.nextRpcServer(); } }; } @Test void testHandleServerRequestWhenExceptionThenThrowException() throws RuntimeException { assertThrows(RuntimeException.class, () -> { RpcClient rpcClient = buildTestNextRpcServerClient(); Request request = new Request() { @Override public String getModule() { return null; } }; rpcClient.serverRequestHandlers.add((req, conn) -> { throw new RuntimeException(); }); rpcClient.handleServerRequest(request); }); } @Test void testNotifyDisConnectedForEmpty() { rpcClient.notifyDisConnected(null); verify(rpcClientConfig, never()).name(); } @Test void testNotifyDisConnected() { ConnectionEventListener listener = mock(ConnectionEventListener.class); rpcClient.registerConnectionListener(listener); rpcClient.notifyDisConnected(null); verify(listener).onDisConnect(null); verify(rpcClientConfig, times(2)).name(); } @Test void testNotifyDisConnectedException() { ConnectionEventListener listener = mock(ConnectionEventListener.class); rpcClient.registerConnectionListener(listener); doThrow(new RuntimeException("test")).when(listener).onDisConnect(null); rpcClient.notifyDisConnected(null); verify(rpcClientConfig, times(3)).name(); } @Test void testNotifyConnectedForEmpty() { rpcClient.notifyConnected(null); verify(rpcClientConfig, never()).name(); } @Test void testNotifyConnected() { ConnectionEventListener listener = mock(ConnectionEventListener.class); rpcClient.registerConnectionListener(listener); rpcClient.notifyConnected(null); verify(listener).onConnected(null); verify(rpcClientConfig, times(2)).name(); } @Test void testNotifyConnectedException() { ConnectionEventListener listener = mock(ConnectionEventListener.class); rpcClient.registerConnectionListener(listener); doThrow(new RuntimeException("test")).when(listener).onConnected(null); rpcClient.notifyConnected(null); verify(rpcClientConfig, times(3)).name(); } @Test void testStartClient() throws NacosException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(new Function() { private int count; @Override public Connection apply(RpcClient.ServerInfo serverInfo) { if (count == 0) { count++; throw new RuntimeException("test"); } return connection; } }); try { rpcClient.start(); assertTrue(rpcClient.isRunning()); } finally { rpcClient.shutdown(); } } @Test void testStartClientWithFailed() throws NacosException, InterruptedException { RpcClient rpcClient = buildTestStartClient(serverInfo -> null); try { rpcClient.start(); TimeUnit.MILLISECONDS.sleep(1000); assertFalse(rpcClient.isRunning()); } finally { rpcClient.shutdown(); } } @Test void testStartClientAfterShutdown() throws NacosException { RpcClient rpcClient = buildTestStartClient(serverInfo -> null); rpcClient.shutdown(); rpcClient.start(); assertTrue(rpcClient.isShutdown()); } @Test void testDisConnectionEventAfterStart() throws NacosException, InterruptedException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> connection); ConnectionEventListener listener = mock(ConnectionEventListener.class); rpcClient.registerConnectionListener(listener); try { rpcClient.start(); TimeUnit.MILLISECONDS.sleep(100); rpcClient.eventLinkedBlockingQueue.put(new RpcClient.ConnectionEvent(0, connection)); TimeUnit.MILLISECONDS.sleep(100); verify(listener).onDisConnect(connection); } finally { rpcClient.shutdown(); } } @Test void testReconnectContextAfterStartWithNullConnection() throws NacosException, InterruptedException { RpcClient rpcClient = buildTestStartClient(serverInfo -> null); try { when(rpcClientConfig.connectionKeepAlive()).thenReturn(-1L); rpcClient.start(); TimeUnit.MILLISECONDS.sleep(100); verify(rpcClientConfig, never()).healthCheckRetryTimes(); } finally { rpcClient.shutdown(); } } @Test void testReconnectContextAfterStartWithConnectionHealthCheckFail() throws NacosException, InterruptedException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(new Function() { private int count; @Override public Connection apply(RpcClient.ServerInfo serverInfo) { if (count == 0) { count++; return connection; } return null; } }); try { when(rpcClientConfig.connectionKeepAlive()).thenReturn(10L); rpcClient.start(); TimeUnit.MILLISECONDS.sleep(500); assertEquals(RpcClientStatus.UNHEALTHY, rpcClient.rpcClientStatus.get()); } finally { rpcClient.shutdown(); } } @Test void testReconnectContextAfterStartWithConnectionHealthCheckSuccess() throws NacosException, InterruptedException, NoSuchFieldException, IllegalAccessException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> connection); when(connection.request(any(Request.class), anyLong())).thenReturn(new HealthCheckResponse()); try { Field lastActiveTimeStampField = RpcClient.class.getDeclaredField("lastActiveTimeStamp"); lastActiveTimeStampField.setAccessible(true); final long lastActiveTimeStamp = (long) lastActiveTimeStampField.get(rpcClient); when(rpcClientConfig.connectionKeepAlive()).thenReturn(10L); rpcClient.start(); TimeUnit.MILLISECONDS.sleep(100); assertEquals(RpcClientStatus.RUNNING, rpcClient.rpcClientStatus.get()); long newLastActiveTimeStamp = (long) lastActiveTimeStampField.get(rpcClient); assertTrue(newLastActiveTimeStamp > lastActiveTimeStamp); } finally { rpcClient.shutdown(); } } @Test void testReconnectContextAfterStartWithActiveTimeIsNew() throws NacosException, InterruptedException, NoSuchFieldException, IllegalAccessException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> connection); try { Field lastActiveTimeStampField = RpcClient.class.getDeclaredField("lastActiveTimeStamp"); lastActiveTimeStampField.setAccessible(true); long setTime = System.currentTimeMillis() + 10000; lastActiveTimeStampField.set(rpcClient, setTime); when(rpcClientConfig.connectionKeepAlive()).thenReturn(10L); rpcClient.start(); TimeUnit.MILLISECONDS.sleep(100); assertEquals(RpcClientStatus.RUNNING, rpcClient.rpcClientStatus.get()); long newLastActiveTimeStamp = (long) lastActiveTimeStampField.get(rpcClient); assertEquals(setTime, newLastActiveTimeStamp); } finally { rpcClient.shutdown(); } } @Test void testReconnectContextAfterStartWithOldServiceInfo() throws NacosException, InterruptedException, IllegalAccessException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); when(serverListFactory.getServerList()).thenReturn(Collections.singletonList("127.0.0.1:8848")); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> connection); try { rpcClient.start(); RpcClient.ReconnectContext reconnectContext = new RpcClient.ReconnectContext(new RpcClient.ServerInfo("127.0.0.1", 0), false); ((BlockingQueue) reconnectionSignalField.get(rpcClient)).put(reconnectContext); TimeUnit.MILLISECONDS.sleep(100); assertEquals(RpcClientStatus.RUNNING, rpcClient.rpcClientStatus.get()); assertEquals(8848, reconnectContext.serverInfo.serverPort); } finally { rpcClient.shutdown(); } } @Test void testReconnectContextAfterStartWithNewServiceInfo() throws NacosException, InterruptedException, IllegalAccessException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); when(serverListFactory.getServerList()).thenReturn(Collections.singletonList("1.1.1.1:8848")); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> connection); try { rpcClient.start(); RpcClient.ReconnectContext reconnectContext = new RpcClient.ReconnectContext(new RpcClient.ServerInfo("127.0.0.1", 0), false); ((BlockingQueue) reconnectionSignalField.get(rpcClient)).put(reconnectContext); TimeUnit.MILLISECONDS.sleep(100); assertEquals(RpcClientStatus.RUNNING, rpcClient.rpcClientStatus.get()); assertNull(reconnectContext.serverInfo); } finally { rpcClient.shutdown(); } } @Test void testHandleConnectionResetRequestWithoutServer() throws NacosException, InterruptedException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848", "1.1.1.1:8848"); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> { connection.serverInfo = serverInfo; return connection; }); try { rpcClient.start(); Response response = rpcClient.handleServerRequest(new ConnectResetRequest()); assertTrue(response instanceof ConnectResetResponse); TimeUnit.MILLISECONDS.sleep(500); assertEquals("1.1.1.1", connection.serverInfo.getServerIp()); } finally { rpcClient.shutdown(); } } @Test void testHandleConnectionResetRequestWithServer() throws NacosException, InterruptedException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848", "1.1.1.1:8848"); List serverList = new LinkedList<>(); serverList.add("127.0.0.1:8848"); serverList.add("1.1.1.1:8848"); serverList.add("2.2.2.2:8848"); when(serverListFactory.getServerList()).thenReturn(serverList); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> { connection.serverInfo = serverInfo; return connection; }); try { rpcClient.start(); ConnectResetRequest connectResetRequest = new ConnectResetRequest(); connectResetRequest.setServerIp("2.2.2.2"); connectResetRequest.setServerPort("8848"); Response response = rpcClient.handleServerRequest(connectResetRequest); assertTrue(response instanceof ConnectResetResponse); TimeUnit.MILLISECONDS.sleep(500); assertEquals("2.2.2.2", connection.serverInfo.getServerIp()); } finally { rpcClient.shutdown(); } } @Test void testHandleConnectionResetRequestWithException() throws NacosException, InterruptedException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848", "1.1.1.1:8848"); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); RpcClient rpcClient = buildTestStartClient(serverInfo -> { connection.serverInfo = serverInfo; return connection; }); try { rpcClient.start(); System.setProperty("nacos.server.port", "2.2.2.2"); ConnectResetRequest connectResetRequest = new ConnectResetRequest(); connectResetRequest.setServerIp("2.2.2.2"); Response response = rpcClient.handleServerRequest(connectResetRequest); assertTrue(response instanceof ConnectResetResponse); TimeUnit.MILLISECONDS.sleep(500); assertEquals("127.0.0.1", connection.serverInfo.getServerIp()); } finally { rpcClient.shutdown(); } } @Test void testHandleClientDetectionRequest() throws NacosException { RpcClient rpcClient = buildTestStartClient(serverInfo -> null); try { rpcClient.start(); Response response = rpcClient.handleServerRequest(new ClientDetectionRequest()); assertTrue(response instanceof ClientDetectionResponse); } finally { rpcClient.shutdown(); } } @Test void testHandleOtherRequest() throws NacosException { RpcClient rpcClient = buildTestStartClient(serverInfo -> null); try { rpcClient.start(); Response response = rpcClient.handleServerRequest(new HealthCheckRequest()); assertNull(response); } finally { rpcClient.shutdown(); } } @Test void testReconnectForRequestFailButHealthCheckOK() throws NacosException { RpcClient rpcClient = buildTestStartClient(serverInfo -> null); rpcClient.currentConnection = connection; connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); when(connection.request(any(Request.class), anyLong())).thenReturn(new HealthCheckResponse()); rpcClient.reconnect(null, true); assertTrue(rpcClient.isRunning()); } @Test void testReconnectFailTimes() throws NacosException { when(serverListFactory.genNextServer()).thenReturn("127.0.0.1:8848"); when(serverListFactory.getServerList()).thenReturn(Collections.singletonList("127.0.0.1:8848")); final AtomicInteger count = new AtomicInteger(0); RpcClient rpcClient = buildTestStartClient(serverInfo -> { int actual = count.incrementAndGet(); return actual > 3 ? connection : null; }); rpcClient.currentConnection = connection; connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); long start = System.currentTimeMillis(); rpcClient.reconnect(null, false); assertTrue(rpcClient.isRunning()); assertTrue(System.currentTimeMillis() - start > 400); } @Test void testGetCurrentServer() { assertNull(rpcClient.getCurrentServer()); rpcClient.currentConnection = connection; rpcClient.serverListFactory(serverListFactory); connection.serverInfo = new RpcClient.ServerInfo("127.0.0.1", 8848); assertNotNull(rpcClient.getCurrentServer()); } @Test void testCurrentRpcServer() throws IllegalAccessException { when(serverListFactory.getCurrentServer()).thenReturn("127.0.0.1:8848"); serverListFactoryField.set(rpcClient, serverListFactory); RpcClient.ServerInfo serverInfo = rpcClient.currentRpcServer(); assertEquals("127.0.0.1", serverInfo.getServerIp()); assertEquals(8848, serverInfo.getServerPort()); assertEquals("127.0.0.1:8848", serverInfo.getAddress()); } private RpcClient buildTestStartClient(Function function) { return new RpcClient(rpcClientConfig, serverListFactory) { @Override public ConnectionType getConnectionType() { return ConnectionType.GRPC; } @Override public int rpcPortOffset() { return 0; } @Override public Connection connectToServer(ServerInfo serverInfo) { return function.apply(serverInfo); } }; } @Test void testServerInfoSet() { RpcClient.ServerInfo serverInfo = new RpcClient.ServerInfo(); String ip = "127.0.0.1"; int port = 80; serverInfo.setServerIp(ip); serverInfo.setServerPort(port); assertEquals("127.0.0.1:80", serverInfo.getAddress()); assertEquals(port, serverInfo.getServerPort()); assertEquals(ip, serverInfo.getServerIp()); String expected = "{serverIp = '127.0.0.1', server main port = 80}"; assertEquals(expected, serverInfo.toString()); } @Test void testSetTenant() { String tenant = "testTenant"; assertNull(rpcClient.getTenant()); rpcClient.setTenant(tenant); assertEquals(tenant, rpcClient.getTenant()); } @Test void testGetConnectionAbilityWithNullConnection() { AbilityStatus abilityStatus = rpcClient.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH); assertNull(abilityStatus); } @Test void testGetConnectionAbilityWithReadyConnection() { when(connection.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH)).thenReturn(AbilityStatus.SUPPORTED); rpcClient.currentConnection = connection; AbilityStatus abilityStatus = rpcClient.getConnectionAbility(AbilityKey.SERVER_FUZZY_WATCH); assertNotNull(abilityStatus); assertEquals(AbilityStatus.SUPPORTED, abilityStatus); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClientTlsConfigTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import org.junit.jupiter.api.Test; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class RpcClientTlsConfigTest { @Test void testEnableTls() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_ENABLE, "true"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertTrue(tlsConfig.getEnableTls()); } @Test void testSslProvider() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_PROVIDER, "provider"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertEquals("provider", tlsConfig.getSslProvider()); } @Test void testMutualAuthEnable() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_MUTUAL_AUTH, "true"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertTrue(tlsConfig.getMutualAuthEnable()); } @Test void testProtocols() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_PROTOCOLS, "protocols"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertEquals("protocols", tlsConfig.getProtocols()); } @Test void testCiphers() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_CIPHERS, "ciphers"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertEquals("ciphers", tlsConfig.getCiphers()); } @Test void testTrustCollectionCertFile() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_TRUST_COLLECTION_CHAIN_PATH, "trustCollectionCertFile"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertEquals("trustCollectionCertFile", tlsConfig.getTrustCollectionCertFile()); } @Test void testCertChainFile() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_CERT_CHAIN_PATH, "certChainFile"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertEquals("certChainFile", tlsConfig.getCertChainFile()); } @Test void testCertPrivateKey() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_CERT_KEY, "certPrivateKey"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertEquals("certPrivateKey", tlsConfig.getCertPrivateKey()); } @Test void testTrustAll() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_TRUST_ALL, "true"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertTrue(tlsConfig.getTrustAll()); } @Test void testCertPrivateKeyPassword() { Properties properties = new Properties(); properties.setProperty(RpcConstants.RPC_CLIENT_TLS_TRUST_PWD, "trustPwd"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createSdkConfig(properties); assertEquals("trustPwd", tlsConfig.getCertPrivateKeyPassword()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/RpcClusterClientTlsConfigTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import org.junit.jupiter.api.Test; import java.util.Properties; import static com.alibaba.nacos.common.remote.client.RpcConstants.NACOS_PEER_RPC; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.MUTUAL_AUTH; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_CERT_CHAIN_PATH; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_CERT_KEY; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_CIPHERS; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_ENABLE; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_PROTOCOLS; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_PROVIDER; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_TRUST_ALL; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_TRUST_COLLECTION_CHAIN_PATH; import static com.alibaba.nacos.common.remote.client.RpcConstants.ServerSuffix.TLS_TRUST_PWD; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class RpcClusterClientTlsConfigTest { @Test void testEnableTls() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertTrue(tlsConfig.getEnableTls()); } @Test void testSslProvider() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_PROVIDER, "provider"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertEquals("provider", tlsConfig.getSslProvider()); } @Test void testMutualAuthEnable() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + MUTUAL_AUTH, "true"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertTrue(tlsConfig.getMutualAuthEnable()); } @Test void testProtocols() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_PROTOCOLS, "protocols"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertEquals("protocols", tlsConfig.getProtocols()); } @Test void testCiphers() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_CIPHERS, "ciphers"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertEquals("ciphers", tlsConfig.getCiphers()); } @Test void testTrustCollectionCertFile() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_TRUST_COLLECTION_CHAIN_PATH, "trustCollectionCertFile"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertEquals("trustCollectionCertFile", tlsConfig.getTrustCollectionCertFile()); } @Test void testCertChainFile() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_CERT_CHAIN_PATH, "certChainFile"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertEquals("certChainFile", tlsConfig.getCertChainFile()); } @Test void testCertPrivateKey() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_CERT_KEY, "certPrivateKey"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertEquals("certPrivateKey", tlsConfig.getCertPrivateKey()); } @Test void testTrustAll() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_TRUST_ALL, "true"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertTrue(tlsConfig.getTrustAll()); } @Test void testCertPrivateKeyPassword() { Properties properties = new Properties(); properties.setProperty(NACOS_PEER_RPC + TLS_ENABLE, "true"); properties.setProperty(NACOS_PEER_RPC + TLS_TRUST_PWD, "trustPwd"); RpcClientTlsConfig tlsConfig = RpcClientTlsConfigFactory.getInstance().createClusterConfig(properties); assertEquals("trustPwd", tlsConfig.getCertPrivateKeyPassword()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/RpcConstantsTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.assertEquals; class RpcConstantsTest { @Test void testGetRpcParams() { Field[] declaredFields = RpcConstants.class.getDeclaredFields(); int i = 0; for (Field declaredField : declaredFields) { declaredField.setAccessible(true); if (declaredField.getType().equals(String.class) && null != declaredField.getAnnotation( RpcConstants.RpcConfigLabel.class)) { i++; } } assertEquals(i, RpcConstants.getRpcParams().size()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/DefaultGrpcClientConfigTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.concurrent.TimeUnit; 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.assertFalse; class DefaultGrpcClientConfigTest { @BeforeEach void setUp() throws Exception { System.setProperty("nacos.common.processors", "2"); } @AfterEach void tearDown() throws Exception { System.clearProperty("nacos.common.processors"); } @Test void testDefault() { DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) DefaultGrpcClientConfig.newBuilder().build(); assertNull(config.name()); assertEquals(3, config.retryTimes()); assertEquals(3000L, config.timeOutMills()); assertEquals(5000L, config.connectionKeepAlive()); assertEquals(10000L, config.threadPoolKeepAlive()); assertEquals(4, config.threadPoolCoreSize()); assertEquals(16, config.threadPoolMaxSize()); assertEquals(3000L, config.serverCheckTimeOut()); assertEquals(10000, config.threadPoolQueueSize()); assertEquals(10 * 1024 * 1024, config.maxInboundMessageSize()); assertEquals(6 * 60 * 1000, config.channelKeepAlive()); assertEquals(TimeUnit.SECONDS.toMillis(20L), config.channelKeepAliveTimeout()); assertEquals(3, config.healthCheckRetryTimes()); assertEquals(3000L, config.healthCheckTimeOut()); assertEquals(5000L, config.capabilityNegotiationTimeout()); assertFalse(config.allowCoreThreadTimeOut()); assertEquals(1, config.labels().size()); assertNotNull(config.tlsConfig()); } @Test void testFromProperties() { Properties properties = new Properties(); properties.setProperty(GrpcConstants.GRPC_NAME, "test"); properties.setProperty(GrpcConstants.GRPC_RETRY_TIMES, "3"); properties.setProperty(GrpcConstants.GRPC_TIMEOUT_MILLS, "3000"); properties.setProperty(GrpcConstants.GRPC_CONNECT_KEEP_ALIVE_TIME, "5000"); properties.setProperty(GrpcConstants.GRPC_THREADPOOL_KEEPALIVETIME, "10000"); properties.setProperty(GrpcConstants.GRPC_THREADPOOL_CORE_SIZE, "2"); properties.setProperty(GrpcConstants.GRPC_THREADPOOL_MAX_SIZE, "8"); properties.setProperty(GrpcConstants.GRPC_SERVER_CHECK_TIMEOUT, "3000"); properties.setProperty(GrpcConstants.GRPC_QUEUESIZE, "10000"); properties.setProperty(GrpcConstants.GRPC_MAX_INBOUND_MESSAGE_SIZE, "10485760"); properties.setProperty(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIME, "60000"); properties.setProperty(GrpcConstants.GRPC_CHANNEL_KEEP_ALIVE_TIMEOUT, "20000"); properties.setProperty(GrpcConstants.GRPC_HEALTHCHECK_RETRY_TIMES, "3"); properties.setProperty(GrpcConstants.GRPC_HEALTHCHECK_TIMEOUT, "3000"); properties.setProperty(GrpcConstants.GRPC_CHANNEL_CAPABILITY_NEGOTIATION_TIMEOUT, "5000"); properties.setProperty(GrpcConstants.GRPC_THREADPOOL_ALLOW_CORE_THREAD_TIMEOUT, "false"); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) DefaultGrpcClientConfig.newBuilder() .fromProperties(properties, null).build(); assertEquals("test", config.name()); assertEquals(3, config.retryTimes()); assertEquals(3000, config.timeOutMills()); assertEquals(5000, config.connectionKeepAlive()); assertEquals(10000, config.threadPoolKeepAlive()); assertEquals(2, config.threadPoolCoreSize()); assertEquals(8, config.threadPoolMaxSize()); assertEquals(3000, config.serverCheckTimeOut()); assertEquals(10000, config.threadPoolQueueSize()); assertEquals(10485760, config.maxInboundMessageSize()); assertEquals(60000, config.channelKeepAlive()); assertEquals(20000, config.channelKeepAliveTimeout()); assertEquals(3, config.healthCheckRetryTimes()); assertEquals(3000, config.healthCheckTimeOut()); assertEquals(5000, config.capabilityNegotiationTimeout()); assertEquals(false, config.allowCoreThreadTimeOut()); assertEquals(1, config.labels().size()); assertNotNull(config.tlsConfig()); } @Test void testName() { String name = "test"; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setName(name); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(name, config.name()); } @Test void testSetRetryTimes() { int retryTimes = 3; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setRetryTimes(retryTimes); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(retryTimes, config.retryTimes()); } @Test void testSetTimeOutMills() { long timeOutMills = 3000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setTimeOutMills(timeOutMills); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(timeOutMills, config.timeOutMills()); } @Test void testSetConnectionKeepAlive() { long connectionKeepAlive = 5000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setConnectionKeepAlive(connectionKeepAlive); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(connectionKeepAlive, config.connectionKeepAlive()); } @Test void testSetThreadPoolKeepAlive() { long threadPoolKeepAlive = 10000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setThreadPoolKeepAlive(threadPoolKeepAlive); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(threadPoolKeepAlive, config.threadPoolKeepAlive()); } @Test void testSetThreadPoolCoreSize() { int threadPoolCoreSize = 2; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setThreadPoolCoreSize(threadPoolCoreSize); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(threadPoolCoreSize, config.threadPoolCoreSize()); } @Test void testSetThreadPoolMaxSize() { int threadPoolMaxSize = 8; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setThreadPoolMaxSize(threadPoolMaxSize); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(threadPoolMaxSize, config.threadPoolMaxSize()); } @Test void testSetServerCheckTimeOut() { long serverCheckTimeOut = 3000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setServerCheckTimeOut(serverCheckTimeOut); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(serverCheckTimeOut, config.serverCheckTimeOut()); } @Test void testSetThreadPoolQueueSize() { int threadPoolQueueSize = 10000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setThreadPoolQueueSize(threadPoolQueueSize); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(threadPoolQueueSize, config.threadPoolQueueSize()); } @Test void testSetMaxInboundMessageSize() { int maxInboundMessageSize = 10485760; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setMaxInboundMessageSize(maxInboundMessageSize); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(maxInboundMessageSize, config.maxInboundMessageSize()); } @Test void testSetChannelKeepAlive() { int channelKeepAlive = 60000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setChannelKeepAlive(channelKeepAlive); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(channelKeepAlive, config.channelKeepAlive()); } @Test void testSetChannelKeepAliveTimeout() { int channelKeepAliveTimeout = 20000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setChannelKeepAliveTimeout(channelKeepAliveTimeout); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(channelKeepAliveTimeout, config.channelKeepAliveTimeout()); } @Test void testSetCapabilityNegotiationTimeout() { long capabilityNegotiationTimeout = 5000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setCapabilityNegotiationTimeout(capabilityNegotiationTimeout); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(capabilityNegotiationTimeout, config.capabilityNegotiationTimeout()); } @Test void testSetHealthCheckRetryTimes() { int healthCheckRetryTimes = 3; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setHealthCheckRetryTimes(healthCheckRetryTimes); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(healthCheckRetryTimes, config.healthCheckRetryTimes()); } @Test void testSetHealthCheckTimeOut() { long healthCheckTimeOut = 3000; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setHealthCheckTimeOut(healthCheckTimeOut); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(healthCheckTimeOut, config.healthCheckTimeOut()); } @Test void testSetLabels() { Map labels = new HashMap<>(); labels.put("key1", "value1"); labels.put("key2", "value2"); DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setLabels(labels); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(3, config.labels().size()); assertEquals("value1", config.labels().get("key1")); assertEquals("value2", config.labels().get("key2")); } @Test void testSetTlsConfig() { RpcClientTlsConfig tlsConfig = new RpcClientTlsConfig(); DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setTlsConfig(tlsConfig); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(tlsConfig, config.tlsConfig()); } @Test void testSetTlsConfigDirectly() { RpcClientTlsConfig tlsConfig = new RpcClientTlsConfig(); DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); config.setTlsConfig(tlsConfig); assertEquals(tlsConfig, config.tlsConfig()); } @Test void testSetAllowCoreThreadTimeOut() { boolean allowCoreThreadTimeOut = false; DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder(); builder.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut); DefaultGrpcClientConfig config = (DefaultGrpcClientConfig) builder.build(); assertEquals(allowCoreThreadTimeOut, config.allowCoreThreadTimeOut()); // Test with true value allowCoreThreadTimeOut = true; builder = DefaultGrpcClientConfig.newBuilder(); builder.setAllowCoreThreadTimeOut(allowCoreThreadTimeOut); config = (DefaultGrpcClientConfig) builder.build(); assertEquals(allowCoreThreadTimeOut, config.allowCoreThreadTimeOut()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc; import com.alibaba.nacos.api.grpc.auto.Payload; import com.alibaba.nacos.api.grpc.auto.RequestGrpc; import com.alibaba.nacos.api.remote.request.ConnectResetRequest; import com.alibaba.nacos.api.remote.request.SetupAckRequest; import com.alibaba.nacos.api.remote.response.ConnectResetResponse; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.api.remote.response.ServerCheckResponse; import com.alibaba.nacos.common.remote.ConnectionType; import com.alibaba.nacos.common.remote.client.Connection; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.remote.client.RpcClientStatus; import com.alibaba.nacos.common.remote.client.RpcClientTlsConfig; import com.alibaba.nacos.common.remote.client.ServerListFactory; import com.google.common.util.concurrent.ListenableFuture; import io.grpc.Channel; import io.grpc.ClientCall; import io.grpc.ManagedChannel; import io.grpc.stub.StreamObserver; 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.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class GrpcClientTest { protected GrpcClient grpcClient; protected RpcClient.ServerInfo serverInfo; protected GrpcClientConfig clientConfig; @Mock RpcClientTlsConfig tlsConfig; @BeforeEach void setUp() throws Exception { clientConfig = DefaultGrpcClientConfig.newBuilder().setServerCheckTimeOut(100L).setCapabilityNegotiationTimeout(100L) .setChannelKeepAliveTimeout((int) TimeUnit.SECONDS.toMillis(3L)).setChannelKeepAlive(1000).setName("testClient") .build(); clientConfig.setTlsConfig(tlsConfig); grpcClient = spy(new GrpcClient(clientConfig) { @Override protected AbilityMode abilityMode() { return AbilityMode.SDK_CLIENT; } @Override public int rpcPortOffset() { return 0; } }); serverInfo = new RpcClient.ServerInfo("10.10.10.10", 8848); } @AfterEach void tearDown() throws NacosException { grpcClient.shutdown(); } @Test void testGetConnectionType() { assertEquals(ConnectionType.GRPC, grpcClient.getConnectionType()); } @Test void testConnectToServerFailed() { assertNull(grpcClient.connectToServer(serverInfo)); } @Test void testConnectToServerException() { doThrow(new RuntimeException("test")).when(grpcClient).createNewChannelStub(any(ManagedChannel.class)); assertNull(grpcClient.connectToServer(serverInfo)); } @Test void testConnectToServerMockSuccess() throws ExecutionException, InterruptedException, TimeoutException { RequestGrpc.RequestFutureStub stub = mockStub(new ServerCheckResponse(), null); doReturn(stub).when(grpcClient).createNewChannelStub(any(ManagedChannel.class)); Connection connection = grpcClient.connectToServer(serverInfo); assertNotNull(connection); assertTrue(connection instanceof GrpcConnection); assertEquals(stub, ((GrpcConnection) connection).getGrpcFutureServiceStub()); } @Test void testConnectToServerMockSuccessWithAbility() throws ExecutionException, InterruptedException, TimeoutException { ServerCheckResponse response = new ServerCheckResponse(); response.setSupportAbilityNegotiation(true); RequestGrpc.RequestFutureStub stub = mockStub(response, null); doReturn(stub).when(grpcClient).createNewChannelStub(any(ManagedChannel.class)); Connection connection = grpcClient.connectToServer(serverInfo); assertNull(connection); } @Test void testConnectToServerMockHealthCheckFailed() throws ExecutionException, InterruptedException, TimeoutException { RequestGrpc.RequestFutureStub stub = mockStub(null, new RuntimeException("test")); doReturn(stub).when(grpcClient).createNewChannelStub(any(ManagedChannel.class)); Connection connection = grpcClient.connectToServer(serverInfo); assertNull(connection); } private RequestGrpc.RequestFutureStub mockStub(ServerCheckResponse response, Throwable throwable) throws InterruptedException, ExecutionException, TimeoutException { RequestGrpc.RequestFutureStub stub = mock(RequestGrpc.RequestFutureStub.class); ListenableFuture listenableFuture = mock(ListenableFuture.class); when(stub.request(any(Payload.class))).thenReturn(listenableFuture); if (null == throwable) { when(listenableFuture.get(100L, TimeUnit.MILLISECONDS)).thenReturn(GrpcUtils.convert(response)); } else { when(listenableFuture.get(100L, TimeUnit.MILLISECONDS)).thenThrow(throwable); } Channel channel = mock(Channel.class); when(stub.getChannel()).thenReturn(channel); ClientCall mockCall = mock(ClientCall.class); when(channel.newCall(any(), any())).thenReturn(mockCall); return stub; } @Test void testBindRequestStreamOnNextSetupAckRequest() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onNext(GrpcUtils.convert(new SetupAckRequest())); return null; }); setCurrentConnection(grpcConnection, grpcClient); invokeBindRequestStream(grpcClient, stub, grpcConnection); verify(grpcConnection, never()).sendResponse(any(Response.class)); } @Test void testBindRequestStreamOnNextOtherRequest() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onNext(GrpcUtils.convert(new ConnectResetRequest())); return null; }); grpcClient.registerServerRequestHandler((request, connection) -> { if (request instanceof ConnectResetRequest) { return new ConnectResetResponse(); } return null; }); setCurrentConnection(grpcConnection, grpcClient); invokeBindRequestStream(grpcClient, stub, grpcConnection); verify(grpcConnection).sendResponse(any(ConnectResetResponse.class)); } @Test void testBindRequestStreamOnNextNoRequest() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onNext(GrpcUtils.convert(new ConnectResetRequest())); return null; }); grpcClient.registerServerRequestHandler((request, connection) -> null); setCurrentConnection(grpcConnection, grpcClient); invokeBindRequestStream(grpcClient, stub, grpcConnection); verify(grpcConnection, never()).sendResponse(any(Response.class)); } @Test void testBindRequestStreamOnNextHandleException() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onNext(GrpcUtils.convert(new ConnectResetRequest())); return null; }); grpcClient.registerServerRequestHandler((request, connection) -> { throw new RuntimeException("test"); }); setCurrentConnection(grpcConnection, grpcClient); invokeBindRequestStream(grpcClient, stub, grpcConnection); verify(grpcConnection).sendResponse(any(ErrorResponse.class)); } @Test void testBindRequestStreamOnNextParseException() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onNext(Payload.newBuilder().build()); return null; }); setCurrentConnection(grpcConnection, grpcClient); invokeBindRequestStream(grpcClient, stub, grpcConnection); verify(grpcConnection, never()).sendResponse(any(ErrorResponse.class)); } @Test void testBindRequestStreamOnErrorFromRunning() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onError(new RuntimeException("test")); return null; }); setStatus(grpcClient, RpcClientStatus.RUNNING); setCurrentConnection(grpcConnection, grpcClient); assertTrue(grpcClient.isRunning()); invokeBindRequestStream(grpcClient, stub, grpcConnection); assertFalse(grpcClient.isRunning()); } @Test void testBindRequestStreamOnErrorFromNotRunning() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onError(new RuntimeException("test")); return null; }); setStatus(grpcClient, RpcClientStatus.WAIT_INIT); setCurrentConnection(grpcConnection, grpcClient); assertFalse(grpcClient.isRunning()); assertTrue(grpcClient.isWaitInitiated()); invokeBindRequestStream(grpcClient, stub, grpcConnection); assertFalse(grpcClient.isRunning()); assertTrue(grpcClient.isWaitInitiated()); } @Test void testBindRequestStreamOnCompletedFromRunning() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onCompleted(); return null; }); setStatus(grpcClient, RpcClientStatus.RUNNING); setCurrentConnection(grpcConnection, grpcClient); assertTrue(grpcClient.isRunning()); invokeBindRequestStream(grpcClient, stub, grpcConnection); assertFalse(grpcClient.isRunning()); } @Test void testBindRequestStreamOnCompletedFromNotRunning() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { BiRequestStreamGrpc.BiRequestStreamStub stub = mock(BiRequestStreamGrpc.BiRequestStreamStub.class); GrpcConnection grpcConnection = mock(GrpcConnection.class); when(stub.requestBiStream(any())).thenAnswer((Answer>) invocationOnMock -> { ((StreamObserver) invocationOnMock.getArgument(0)).onCompleted(); return null; }); setStatus(grpcClient, RpcClientStatus.WAIT_INIT); setCurrentConnection(grpcConnection, grpcClient); assertFalse(grpcClient.isRunning()); assertTrue(grpcClient.isWaitInitiated()); invokeBindRequestStream(grpcClient, stub, grpcConnection); assertFalse(grpcClient.isRunning()); assertTrue(grpcClient.isWaitInitiated()); } private void invokeBindRequestStream(GrpcClient grpcClient, BiRequestStreamGrpc.BiRequestStreamStub stub, GrpcConnection grpcConnection) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method bindRequestStreamMethod = GrpcClient.class.getDeclaredMethod("bindRequestStream", BiRequestStreamGrpc.BiRequestStreamStub.class, GrpcConnection.class); bindRequestStreamMethod.setAccessible(true); bindRequestStreamMethod.invoke(grpcClient, stub, grpcConnection); } private void setCurrentConnection(GrpcConnection connection, GrpcClient client) throws NoSuchFieldException, IllegalAccessException { Field connectionField = RpcClient.class.getDeclaredField("currentConnection"); connectionField.setAccessible(true); connectionField.set(client, connection); } private void setStatus(GrpcClient grpcClient, RpcClientStatus status) throws IllegalAccessException, NoSuchFieldException { Field statusField = RpcClient.class.getDeclaredField("rpcClientStatus"); statusField.setAccessible(true); statusField.set(grpcClient, new AtomicReference<>(status)); } @Test void testAfterReset() throws NoSuchFieldException, IllegalAccessException { Field recAbilityContextField = GrpcClient.class.getDeclaredField("recAbilityContext"); recAbilityContextField.setAccessible(true); GrpcClient.RecAbilityContext context = mock(GrpcClient.RecAbilityContext.class); recAbilityContextField.set(grpcClient, context); grpcClient.afterReset(new ConnectResetRequest()); verify(context).release(null); } @Test void testAppendRecAbilityContext() { GrpcClient.RecAbilityContext context = new GrpcClient.RecAbilityContext(null); GrpcConnection connection = mock(GrpcConnection.class); context.reset(connection); assertTrue(context.isNeedToSync()); assertFalse(context.check(connection)); context.release(Collections.emptyMap()); assertFalse(context.isNeedToSync()); verify(connection).setAbilityTable(anyMap()); when(connection.isAbilitiesSet()).thenReturn(true); assertTrue(context.check(connection)); } @Test void testSendResponseWithException() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException { GrpcConnection connection = mock(GrpcConnection.class); setCurrentConnection(connection, grpcClient); doThrow(new RuntimeException("test")).when(connection).sendResponse(any(Response.class)); Method sendResponseMethod = GrpcClient.class.getDeclaredMethod("sendResponse", Response.class); sendResponseMethod.setAccessible(true); sendResponseMethod.invoke(grpcClient, new ConnectResetResponse()); // don't throw any exception. } @Test void testConstructorWithServerListFactory() { ServerListFactory serverListFactory = mock(ServerListFactory.class); GrpcClient grpcClient = new GrpcClient(clientConfig, serverListFactory) { @Override protected AbilityMode abilityMode() { return null; } @Override public int rpcPortOffset() { return 0; } }; assertFalse(grpcClient.isWaitInitiated()); } @Test void testConstructorWithoutServerListFactory() { GrpcClient grpcClient = new GrpcClient("testNoFactory", 2, 2, Collections.emptyMap()) { @Override protected AbilityMode abilityMode() { return null; } @Override public int rpcPortOffset() { return 0; } }; assertTrue(grpcClient.isWaitInitiated()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClientTlsTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; /** * Currently not good way to test tls relative codes, and it's a optional feature, single test first. */ class GrpcClientTlsTest extends GrpcClientTest { @Test void testGrpcEnableTlsAndTrustPart() throws Exception { when(tlsConfig.getEnableTls()).thenReturn(true); when(tlsConfig.getTrustCollectionCertFile()).thenReturn("ca-cert.pem"); when(tlsConfig.getCiphers()).thenReturn("ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384"); when(tlsConfig.getProtocols()).thenReturn("TLSv1.2,TLSv1.3"); assertNull(grpcClient.connectToServer(serverInfo)); } @Test void testGrpcEnableTlsAndTrustAll() throws Exception { when(tlsConfig.getEnableTls()).thenReturn(true); when(tlsConfig.getTrustCollectionCertFile()).thenReturn("ca-cert.pem"); when(tlsConfig.getCiphers()).thenReturn("ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384"); when(tlsConfig.getProtocols()).thenReturn("TLSv1.2,TLSv1.3"); when(tlsConfig.getTrustAll()).thenReturn(true); assertNull(grpcClient.connectToServer(serverInfo)); } @Test void testGrpcEnableTlsAndEnableMutualAuth() throws Exception { when(tlsConfig.getEnableTls()).thenReturn(true); when(tlsConfig.getTrustCollectionCertFile()).thenReturn("ca-cert.pem"); when(tlsConfig.getCiphers()).thenReturn("ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384"); when(tlsConfig.getProtocols()).thenReturn("TLSv1.2,TLSv1.3"); when(tlsConfig.getTrustAll()).thenReturn(true); when(tlsConfig.getMutualAuthEnable()).thenReturn(true); when(tlsConfig.getCertPrivateKey()).thenReturn("client-key.pem"); assertNull(grpcClient.connectToServer(serverInfo)); } @Test void testGrpcSslProvider() { when(tlsConfig.getEnableTls()).thenReturn(true); when(tlsConfig.getTrustCollectionCertFile()).thenReturn("ca-cert.pem"); when(tlsConfig.getCiphers()).thenReturn("ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384"); when(tlsConfig.getProtocols()).thenReturn("TLSv1.2,TLSv1.3"); when(tlsConfig.getTrustAll()).thenReturn(true); when(tlsConfig.getMutualAuthEnable()).thenReturn(true); when(tlsConfig.getCertPrivateKey()).thenReturn("client-key.pem"); when(tlsConfig.getSslProvider()).thenReturn("JDK"); assertNull(grpcClient.connectToServer(serverInfo)); } @Test void testGrpcEmptyTrustCollectionCertFile() { when(tlsConfig.getEnableTls()).thenReturn(true); when(tlsConfig.getTrustCollectionCertFile()).thenReturn(""); when(tlsConfig.getCiphers()).thenReturn("ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384"); when(tlsConfig.getProtocols()).thenReturn("TLSv1.2,TLSv1.3"); assertNull(grpcClient.connectToServer(serverInfo)); } @Test void testGrpcMutualAuth() { when(tlsConfig.getEnableTls()).thenReturn(true); when(tlsConfig.getCiphers()).thenReturn("ECDHE-RSA-AES128-GCM-SHA256", "ECDHE-RSA-AES256-GCM-SHA384"); when(tlsConfig.getProtocols()).thenReturn("TLSv1.2,TLSv1.3"); when(tlsConfig.getMutualAuthEnable()).thenReturn(true); when(tlsConfig.getTrustAll()).thenReturn(true); when(tlsConfig.getCertChainFile()).thenReturn("classpath:test-tls-cert.pem"); when(tlsConfig.getCertPrivateKey()).thenReturn("classpath:test-tls-cert.pem"); assertNull(grpcClient.connectToServer(serverInfo)); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcClusterClientTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.util.Collections; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; class GrpcClusterClientTest { GrpcClusterClient grpcClusterClient; @AfterEach void tearDown() throws NacosException { System.clearProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY); if (grpcClusterClient != null) { grpcClusterClient.shutdown(); } } @Test void testAbilityMode() { grpcClusterClient = new GrpcClusterClient("test"); assertEquals(AbilityMode.CLUSTER_CLIENT, grpcClusterClient.abilityMode()); } @Test void testRpcPortOffsetDefault() { DefaultGrpcClientConfig.Builder builder = DefaultGrpcClientConfig.newBuilder() .buildClusterFromProperties(new Properties()); grpcClusterClient = new GrpcClusterClient(builder.build()); assertEquals(1001, grpcClusterClient.rpcPortOffset()); } @Test void testRpcPortOffsetFromSystemProperty() { System.setProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY, "10001"); grpcClusterClient = new GrpcClusterClient("test", 8, 8, Collections.emptyMap()); assertEquals(10001, grpcClusterClient.rpcPortOffset()); } @Test void testGrpcClientByConfig() { GrpcClientConfig config = DefaultGrpcClientConfig.newBuilder().setName("test111").build(); grpcClusterClient = new GrpcClusterClient(config); assertEquals("test111", grpcClusterClient.getName()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConnectionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.grpc.auto.Metadata; import com.alibaba.nacos.api.grpc.auto.Payload; import com.alibaba.nacos.api.grpc.auto.RequestGrpc; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.api.remote.RequestFuture; import com.alibaba.nacos.api.remote.request.HealthCheckRequest; import com.alibaba.nacos.api.remote.response.ErrorResponse; import com.alibaba.nacos.api.remote.response.HealthCheckResponse; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.common.remote.PayloadRegistry; import com.alibaba.nacos.common.remote.client.RpcClient; import com.alibaba.nacos.common.utils.JacksonUtils; import com.google.common.util.concurrent.ListenableFuture; import com.google.protobuf.Any; import com.google.protobuf.UnsafeByteOperations; import io.grpc.ManagedChannel; import io.grpc.stub.StreamObserver; 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.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; 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.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class GrpcConnectionTest { @Mock ListenableFuture future; Payload responsePayload; Payload errorResponsePayload; GrpcConnection connection; @Mock private Executor executor; @Mock private ManagedChannel channel; @Mock private StreamObserver payloadStreamObserver; @Mock private RequestGrpc.RequestFutureStub requestFutureStub; @BeforeAll static void setUpBeforeClass() { PayloadRegistry.init(); } @BeforeEach void setUp() throws Exception { connection = new GrpcConnection(new RpcClient.ServerInfo(), executor); connection.setChannel(channel); connection.setPayloadStreamObserver(payloadStreamObserver); connection.setGrpcFutureServiceStub(requestFutureStub); when(requestFutureStub.request(any(Payload.class))).thenReturn(future); responsePayload = GrpcUtils.convert(new HealthCheckResponse()); errorResponsePayload = GrpcUtils.convert(ErrorResponse.build(500, "test")); when(future.get()).thenReturn(responsePayload); when(future.get(100L, TimeUnit.MILLISECONDS)).thenReturn(responsePayload); when(future.isDone()).thenReturn(true); } @AfterEach void tearDown() throws Exception { connection.close(); } @Test void testGetAll() { assertEquals(channel, connection.getChannel()); assertEquals(payloadStreamObserver, connection.getPayloadStreamObserver()); assertEquals(requestFutureStub, connection.getGrpcFutureServiceStub()); } @Test void testRequestSuccessSync() throws NacosException { Response response = connection.request(new HealthCheckRequest(), -1); assertTrue(response instanceof HealthCheckResponse); } @Test void testRequestSuccessAsync() throws NacosException { Response response = connection.request(new HealthCheckRequest(), 100); assertTrue(response instanceof HealthCheckResponse); } @Test void testRequestTimeout() throws InterruptedException, ExecutionException, TimeoutException, NacosException { assertThrows(NacosException.class, () -> { when(future.get(100L, TimeUnit.MILLISECONDS)).thenThrow(new TimeoutException("test")); connection.request(new HealthCheckRequest(), 100); }); } @Test void testRequestFuture() throws Exception { RequestFuture requestFuture = connection.requestFuture(new HealthCheckRequest()); assertTrue(requestFuture.isDone()); Response response = requestFuture.get(); assertTrue(response instanceof HealthCheckResponse); } @Test void testRequestFutureWithTimeout() throws Exception { RequestFuture requestFuture = connection.requestFuture(new HealthCheckRequest()); assertTrue(requestFuture.isDone()); Response response = requestFuture.get(100L); assertTrue(response instanceof HealthCheckResponse); } @Test void testRequestFutureFailure() throws Exception { assertThrows(NacosException.class, () -> { when(future.get()).thenReturn(errorResponsePayload); RequestFuture requestFuture = connection.requestFuture(new HealthCheckRequest()); assertTrue(requestFuture.isDone()); requestFuture.get(); }); } @Test void testRequestFutureWithTimeoutFailure() throws Exception { assertThrows(NacosException.class, () -> { when(future.get(100L, TimeUnit.MILLISECONDS)).thenReturn(errorResponsePayload); RequestFuture requestFuture = connection.requestFuture(new HealthCheckRequest()); assertTrue(requestFuture.isDone()); requestFuture.get(100L); }); } @Test void testSendResponse() { connection.sendResponse(new HealthCheckResponse()); verify(payloadStreamObserver).onNext(any(Payload.class)); } @Test void testSendRequest() { connection.sendRequest(new HealthCheckRequest()); verify(payloadStreamObserver).onNext(any(Payload.class)); } @Test void testAsyncRequestSuccess() throws NacosException { doAnswer(invocationOnMock -> { ((Runnable) invocationOnMock.getArgument(0)).run(); return null; }).when(future).addListener(any(Runnable.class), eq(executor)); RequestCallBack requestCallBack = mock(RequestCallBack.class); connection.asyncRequest(new HealthCheckRequest(), requestCallBack); verify(requestCallBack).onResponse(any(HealthCheckResponse.class)); } @Test void testAsyncRequestError() throws NacosException, ExecutionException, InterruptedException { when(future.get()).thenReturn(errorResponsePayload); doAnswer(invocationOnMock -> { ((Runnable) invocationOnMock.getArgument(0)).run(); return null; }).when(future).addListener(any(Runnable.class), eq(executor)); RequestCallBack requestCallBack = mock(RequestCallBack.class); connection.asyncRequest(new HealthCheckRequest(), requestCallBack); verify(requestCallBack).onException(any(NacosException.class)); } @Test void testAsyncRequestNullResponse() throws NacosException, ExecutionException, InterruptedException { byte[] jsonBytes = JacksonUtils.toJsonBytes(null); Metadata.Builder metaBuilder = Metadata.newBuilder().setType(HealthCheckResponse.class.getSimpleName()); Payload nullResponsePayload = Payload.newBuilder() .setBody(Any.newBuilder().setValue(UnsafeByteOperations.unsafeWrap(jsonBytes))).setMetadata(metaBuilder.build()) .build(); when(future.get()).thenReturn(nullResponsePayload); doAnswer(invocationOnMock -> { ((Runnable) invocationOnMock.getArgument(0)).run(); return null; }).when(future).addListener(any(Runnable.class), eq(executor)); RequestCallBack requestCallBack = mock(RequestCallBack.class); connection.asyncRequest(new HealthCheckRequest(), requestCallBack); verify(requestCallBack).onException(any(NacosException.class)); } @Test void testAsyncRequestWithCancelException() throws NacosException, ExecutionException, InterruptedException { when(future.get()).thenThrow(new CancellationException("test")); doAnswer(invocationOnMock -> { ((Runnable) invocationOnMock.getArgument(0)).run(); return null; }).when(future).addListener(any(Runnable.class), eq(executor)); RequestCallBack requestCallBack = mock(RequestCallBack.class); connection.asyncRequest(new HealthCheckRequest(), requestCallBack); verify(requestCallBack).onException(any(TimeoutException.class)); } @Test void testAsyncRequestWithOtherException() throws NacosException, ExecutionException, InterruptedException { when(future.get()).thenThrow(new RuntimeException("test")); doAnswer(invocationOnMock -> { ((Runnable) invocationOnMock.getArgument(0)).run(); return null; }).when(future).addListener(any(Runnable.class), eq(executor)); RequestCallBack requestCallBack = mock(RequestCallBack.class); connection.asyncRequest(new HealthCheckRequest(), requestCallBack); verify(requestCallBack).onException(any(RuntimeException.class)); } @Test void testCloseWithException() { doThrow(new RuntimeException("test")).when(payloadStreamObserver).onCompleted(); when(channel.shutdownNow()).thenThrow(new RuntimeException("test")); connection.close(); // don't throw any exception } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcConstantsTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.assertEquals; class GrpcConstantsTest { @Test void testGetRpcParams() { Class clazz = GrpcConstants.class; Field[] declaredFields = clazz.getDeclaredFields(); int i = 0; for (Field declaredField : declaredFields) { declaredField.setAccessible(true); if (declaredField.getType().equals(String.class) && null != declaredField.getAnnotation( GrpcConstants.GRpcConfigLabel.class)) { i++; } } assertEquals(i, GrpcConstants.getRpcParams().size()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcSdkClientTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.ability.constant.AbilityMode; import com.alibaba.nacos.api.exception.NacosException; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; class GrpcSdkClientTest { GrpcSdkClient grpcSdkClient; @AfterEach void tearDown() throws NacosException { System.clearProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY); if (grpcSdkClient != null) { grpcSdkClient.shutdown(); } } @Test void testAbilityMode() { grpcSdkClient = new GrpcSdkClient("test"); assertEquals(AbilityMode.SDK_CLIENT, grpcSdkClient.abilityMode()); } @Test void testRpcPortOffsetDefault() { grpcSdkClient = new GrpcSdkClient("test"); assertEquals(1000, grpcSdkClient.rpcPortOffset()); } @Test void testRpcPortOffsetFromSystemProperty() { System.setProperty(GrpcConstants.NACOS_SERVER_GRPC_PORT_OFFSET_KEY, "10000"); grpcSdkClient = new GrpcSdkClient("test", 8, 8, Collections.emptyMap()); assertEquals(10000, grpcSdkClient.rpcPortOffset()); } @Test void testGrpcClientByConfig() { GrpcClientConfig config = DefaultGrpcClientConfig.newBuilder().setName("test111").build(); grpcSdkClient = new GrpcSdkClient(config); assertEquals("test111", grpcSdkClient.getName()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/client/grpc/GrpcUtilsTest.java ================================================ /* * * * Copyright 1999-2022 Alibaba Group Holding Ltd. * * * * 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 com.alibaba.nacos.common.remote.client.grpc; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; import com.alibaba.nacos.api.grpc.auto.Metadata; import com.alibaba.nacos.api.grpc.auto.Payload; import com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.common.remote.PayloadRegistry; import com.alibaba.nacos.common.remote.exception.RemoteException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class GrpcUtilsTest { private ServiceQueryRequest request; private ClientConfigMetricResponse response; @BeforeEach void setup() { PayloadRegistry.init(); this.request = createRequest(); this.response = createResponse(); } private ClientConfigMetricResponse createResponse() { ClientConfigMetricResponse clientConfigMetricResponse = new ClientConfigMetricResponse(); clientConfigMetricResponse.setMetrics(new HashMap() { { put("m1", "v1"); put("m2", "v2"); put("m3", "v3"); } }); return clientConfigMetricResponse; } private ServiceQueryRequest createRequest() { ServiceQueryRequest request = new ServiceQueryRequest(); request.setCluster("cluster"); request.setHealthyOnly(true); request.setNamespace("namespace"); //headers request.putHeader("h1", "v1"); request.putHeader("h2", "v2"); request.putHeader("h3", "v3"); return request; } @Test void testConvertRequest() { Payload convert = GrpcUtils.convert(request); assertEquals(request.getClass().getSimpleName(), convert.getMetadata().getType()); assertEquals("v1", convert.getMetadata().getHeadersMap().get("h1")); assertEquals("v2", convert.getMetadata().getHeadersMap().get("h2")); assertEquals("v3", convert.getMetadata().getHeadersMap().get("h3")); } @Test void testConvertRequestWithMeta() { RequestMeta meta = new RequestMeta(); Payload convert = GrpcUtils.convert(request, meta); assertEquals(request.getClass().getSimpleName(), convert.getMetadata().getType()); assertEquals("v1", convert.getMetadata().getHeadersMap().get("h1")); assertEquals("v2", convert.getMetadata().getHeadersMap().get("h2")); assertEquals("v3", convert.getMetadata().getHeadersMap().get("h3")); } @Test void testConvertResponse() { Payload convert = GrpcUtils.convert(response); assertEquals(response.getClass().getSimpleName(), convert.getMetadata().getType()); } @Test void testParse() { Payload requestPayload = GrpcUtils.convert(request); ServiceQueryRequest request = (ServiceQueryRequest) GrpcUtils.parse(requestPayload); assertEquals(this.request.getHeaders(), request.getHeaders()); assertEquals(this.request.getCluster(), request.getCluster()); assertEquals(this.request.isHealthyOnly(), request.isHealthyOnly()); assertEquals(this.request.getNamespace(), request.getNamespace()); Payload responsePayload = GrpcUtils.convert(response); ClientConfigMetricResponse response = (ClientConfigMetricResponse) GrpcUtils.parse(responsePayload); assertEquals(this.response.getMetrics(), response.getMetrics()); } @Test void testParseNullType() { assertThrows(RemoteException.class, () -> { Payload mockPayload = mock(Payload.class); Metadata mockMetadata = mock(Metadata.class); when(mockPayload.getMetadata()).thenReturn(mockMetadata); GrpcUtils.parse(mockPayload); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/remote/exception/RemoteExceptionTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.remote.exception; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class RemoteExceptionTest { @Test void testConnectionAlreadyClosedException() { ConnectionAlreadyClosedException exception = new ConnectionAlreadyClosedException("test message"); assertEquals(600, exception.getErrCode()); assertEquals("errCode: 600, errMsg: test message ", exception.getMessage()); assertNull(exception.getCause()); exception = new ConnectionAlreadyClosedException(); assertEquals(600, exception.getErrCode()); assertNull(exception.getMessage()); assertNull(exception.getCause()); RuntimeException caused = new RuntimeException("test cause"); exception = new ConnectionAlreadyClosedException(caused); assertEquals(600, exception.getErrCode()); assertEquals(caused, exception.getCause()); assertEquals("java.lang.RuntimeException: test cause", exception.getMessage()); } @Test void testConnectionBusyException() { String msg = "Connection is busy"; ConnectionBusyException exception = new ConnectionBusyException(msg); assertEquals(601, exception.getErrCode()); assertEquals("errCode: 601, errMsg: " + msg + " ", exception.getMessage()); assertNull(exception.getCause()); RuntimeException caused = new RuntimeException("test cause"); exception = new ConnectionBusyException(caused); assertEquals(601, exception.getErrCode()); assertEquals(caused, exception.getCause()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/spi/NacosServiceLoaderTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.spi; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import java.util.Collection; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; class NacosServiceLoaderTest { @AfterEach void tearDown() { SpiTestImpl.newInstanceException = false; } @Test void testLoad() { Collection actual = NacosServiceLoader.load(SpiTestInterface.class); assertEquals(1, actual.size()); assertEquals(SpiTestImpl.class, actual.iterator().next().getClass()); } @Test void newServiceInstances() { SpiTestInterface loadInstance = NacosServiceLoader.load(SpiTestInterface.class).iterator().next(); Collection actual = NacosServiceLoader.newServiceInstances(SpiTestInterface.class); assertEquals(1, actual.size()); assertEquals(SpiTestImpl.class, actual.iterator().next().getClass()); assertNotEquals(loadInstance, actual.iterator().next()); } @Test void newServiceInstancesWithException() { NacosServiceLoader.load(SpiTestInterface.class); SpiTestImpl.newInstanceException = true; try { NacosServiceLoader.newServiceInstances(SpiTestInterface.class); } catch (ServiceLoaderException e) { assertEquals(SpiTestImpl.class, e.getClazz()); assertEquals("Can not load class `" + SpiTestImpl.class.getName() + "` by SPI ", e.getMessage()); } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/spi/SpiTestImpl.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.spi; public class SpiTestImpl implements SpiTestInterface { public static boolean newInstanceException; public SpiTestImpl() throws IllegalAccessException { if (newInstanceException) { throw new IllegalAccessException("test"); } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/spi/SpiTestInterface.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.spi; public interface SpiTestInterface { } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/task/engine/NacosDelayTaskExecuteEngineTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task.engine; import com.alibaba.nacos.common.task.AbstractDelayTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; 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.internal.verification.Times; import org.mockito.junit.jupiter.MockitoExtension; import java.util.concurrent.TimeUnit; 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.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NacosDelayTaskExecuteEngineTest { private NacosDelayTaskExecuteEngine nacosDelayTaskExecuteEngine; @Mock private NacosTaskProcessor taskProcessor; @Mock private NacosTaskProcessor testTaskProcessor; private AbstractDelayTask abstractTask; @BeforeEach void setUp() throws Exception { nacosDelayTaskExecuteEngine = new NacosDelayTaskExecuteEngine(NacosDelayTaskExecuteEngineTest.class.getName()); nacosDelayTaskExecuteEngine.setDefaultTaskProcessor(taskProcessor); abstractTask = new AbstractDelayTask() { @Override public void merge(AbstractDelayTask task) { } }; } @AfterEach void tearDown() throws Exception { nacosDelayTaskExecuteEngine.shutdown(); } @Test void testSize() { assertEquals(0, nacosDelayTaskExecuteEngine.size()); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); assertEquals(1, nacosDelayTaskExecuteEngine.size()); nacosDelayTaskExecuteEngine.removeTask("test"); assertEquals(0, nacosDelayTaskExecuteEngine.size()); } @Test void testIsEmpty() { assertTrue(nacosDelayTaskExecuteEngine.isEmpty()); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); assertFalse(nacosDelayTaskExecuteEngine.isEmpty()); nacosDelayTaskExecuteEngine.removeTask("test"); assertTrue(nacosDelayTaskExecuteEngine.isEmpty()); } @Test void testAddProcessor() throws InterruptedException { when(testTaskProcessor.process(abstractTask)).thenReturn(true); nacosDelayTaskExecuteEngine.addProcessor("test", testTaskProcessor); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); TimeUnit.MILLISECONDS.sleep(200); verify(testTaskProcessor).process(abstractTask); verify(taskProcessor, never()).process(abstractTask); assertEquals(1, nacosDelayTaskExecuteEngine.getAllProcessorKey().size()); } @Test void testRemoveProcessor() throws InterruptedException { when(taskProcessor.process(abstractTask)).thenReturn(true); nacosDelayTaskExecuteEngine.addProcessor("test", testTaskProcessor); nacosDelayTaskExecuteEngine.removeProcessor("test"); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); TimeUnit.MILLISECONDS.sleep(200); verify(testTaskProcessor, never()).process(abstractTask); verify(taskProcessor).process(abstractTask); } @Test void testRetryTaskAfterFail() throws InterruptedException { when(taskProcessor.process(abstractTask)).thenReturn(false, true); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); TimeUnit.MILLISECONDS.sleep(300); verify(taskProcessor, new Times(2)).process(abstractTask); } @Test void testProcessorWithException() throws InterruptedException { when(taskProcessor.process(abstractTask)).thenThrow(new RuntimeException("test")); nacosDelayTaskExecuteEngine.addProcessor("test", testTaskProcessor); nacosDelayTaskExecuteEngine.removeProcessor("test"); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); TimeUnit.MILLISECONDS.sleep(150); assertEquals(1, nacosDelayTaskExecuteEngine.size()); } @Test void testTaskShouldNotExecute() throws InterruptedException { nacosDelayTaskExecuteEngine.addProcessor("test", testTaskProcessor); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); abstractTask.setTaskInterval(10000L); abstractTask.setLastProcessTime(System.currentTimeMillis()); TimeUnit.MILLISECONDS.sleep(200); verify(testTaskProcessor, never()).process(abstractTask); assertEquals(1, nacosDelayTaskExecuteEngine.size()); } @Test void testTaskMerge() { nacosDelayTaskExecuteEngine.addProcessor("test", testTaskProcessor); nacosDelayTaskExecuteEngine.addTask("test", abstractTask); nacosDelayTaskExecuteEngine.addTask("test", new AbstractDelayTask() { @Override public void merge(AbstractDelayTask task) { setLastProcessTime(task.getLastProcessTime()); setTaskInterval(task.getTaskInterval()); } }); assertEquals(1, nacosDelayTaskExecuteEngine.size()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/task/engine/NacosExecuteTaskExecuteEngineTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.task.engine; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.task.AbstractExecuteTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; 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.junit.jupiter.MockitoExtension; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class NacosExecuteTaskExecuteEngineTest { @Mock NacosTaskProcessor taskProcessor; String cachedProcessor; private NacosExecuteTaskExecuteEngine executeTaskExecuteEngine; @Mock private AbstractExecuteTask task; @BeforeEach void setUp() { cachedProcessor = System.getProperty("nacos.common.processors"); System.setProperty("nacos.common.processors", "1"); executeTaskExecuteEngine = new NacosExecuteTaskExecuteEngine("TEST", null); } @AfterEach void tearDown() throws NacosException { System.setProperty("nacos.common.processors", null == cachedProcessor ? "" : cachedProcessor); executeTaskExecuteEngine.shutdown(); } @Test void testAddTask() throws InterruptedException { executeTaskExecuteEngine.addTask("test", task); TimeUnit.SECONDS.sleep(1); verify(task).run(); assertTrue(executeTaskExecuteEngine.isEmpty()); assertEquals(0, executeTaskExecuteEngine.size()); } @Test void testAddTaskByProcessor() throws InterruptedException { executeTaskExecuteEngine.addProcessor("test", taskProcessor); executeTaskExecuteEngine.addTask("test", task); verify(taskProcessor).process(task); assertTrue(executeTaskExecuteEngine.isEmpty()); assertEquals(0, executeTaskExecuteEngine.size()); } @Test void testRemoveTask() { assertThrows(UnsupportedOperationException.class, () -> { executeTaskExecuteEngine.removeTask(task); }); } @Test void testGetAllTaskKeys() { assertThrows(UnsupportedOperationException.class, () -> { executeTaskExecuteEngine.getAllTaskKeys(); }); } @Test void testWorkersStatus() { assertEquals("TEST_0%1, pending tasks: 0\n", executeTaskExecuteEngine.workersStatus()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/tls/SelfHostnameVerifierTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; 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.junit.jupiter.MockitoExtension; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLSession; 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.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class SelfHostnameVerifierTest { @Mock HostnameVerifier hostnameVerifier; @Mock SSLSession sslSession; SelfHostnameVerifier selfHostnameVerifier; @BeforeEach void setUp() { selfHostnameVerifier = new SelfHostnameVerifier(hostnameVerifier); doReturn(false).when(hostnameVerifier).verify(anyString(), eq(sslSession)); } @Test void testVerify() { assertTrue(selfHostnameVerifier.verify("localhost", sslSession)); assertTrue(selfHostnameVerifier.verify("127.0.0.1", sslSession)); assertTrue(selfHostnameVerifier.verify("10.10.10.10", sslSession)); // hit cache assertTrue(selfHostnameVerifier.verify("10.10.10.10", sslSession)); assertFalse(selfHostnameVerifier.verify("", sslSession)); assertFalse(selfHostnameVerifier.verify(null, sslSession)); verify(hostnameVerifier, times(2)).verify(any(), eq(sslSession)); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/tls/SelfTrustManagerTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.net.URL; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; 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; class SelfTrustManagerTest { @BeforeEach void setUp() throws Exception { } @AfterEach void tearDown() throws Exception { } @Test void testTrustManagerSuccess() throws CertificateException { URL url = SelfTrustManagerTest.class.getClassLoader().getResource("test-tls-cert.pem"); String path = url.getPath(); TrustManager[] actual = SelfTrustManager.trustManager(true, path); assertNotNull(actual); assertEquals(1, actual.length); assertTrue(actual[0] instanceof X509TrustManager); assertFalse(actual[0].getClass().getCanonicalName().startsWith("com.alibaba.nacos")); X509TrustManager x509TrustManager = (X509TrustManager) actual[0]; X509Certificate[] certificates = x509TrustManager.getAcceptedIssuers(); assertNotNull(certificates); x509TrustManager.checkClientTrusted(certificates, "a"); x509TrustManager.checkServerTrusted(certificates, "b"); } @Test void testTrustManagerNonExist() throws CertificateException { TrustManager[] actual = SelfTrustManager.trustManager(true, "non-exist-cert.pem"); assertNotNull(actual); assertEquals(1, actual.length); assertTrue(actual[0] instanceof X509TrustManager); assertTrue(actual[0].getClass().isAnonymousClass()); X509TrustManager x509TrustManager = (X509TrustManager) actual[0]; assertNull(x509TrustManager.getAcceptedIssuers()); x509TrustManager.checkClientTrusted(null, "a"); x509TrustManager.checkServerTrusted(null, "b"); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/tls/TlsFileWatcherTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; 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.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import org.mockito.stubbing.Answer; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.Map; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; 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.anyLong; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class TlsFileWatcherTest { static Field watchFilesMapField; static Field fileMd5MapField; static Field serviceField; static Field startedField; File tempFile; @Mock ScheduledExecutorService executorService; @BeforeAll static void setUpBeforeClass() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { watchFilesMapField = TlsFileWatcher.getInstance().getClass().getDeclaredField("watchFilesMap"); watchFilesMapField.setAccessible(true); fileMd5MapField = TlsFileWatcher.getInstance().getClass().getDeclaredField("fileMd5Map"); fileMd5MapField.setAccessible(true); serviceField = TlsFileWatcher.getInstance().getClass().getDeclaredField("service"); serviceField.setAccessible(true); startedField = TlsFileWatcher.getInstance().getClass().getDeclaredField("started"); startedField.setAccessible(true); } @BeforeEach void setUp() throws IOException, IllegalAccessException { tempFile = new File("test.txt"); tempFile.createNewFile(); serviceField.set(TlsFileWatcher.getInstance(), executorService); startedField.set(TlsFileWatcher.getInstance(), new AtomicBoolean(false)); Answer answer = invocationOnMock -> { Runnable runnable = (Runnable) invocationOnMock.getArguments()[0]; runnable.run(); return null; }; doAnswer(answer).when(executorService).scheduleAtFixedRate(any(), anyLong(), anyLong(), any()); } @AfterEach void tearDown() throws IllegalAccessException { ((Map) watchFilesMapField.get(TlsFileWatcher.getInstance())).clear(); ((Map) fileMd5MapField.get(TlsFileWatcher.getInstance())).clear(); tempFile.deleteOnExit(); } @Test void testAddFileChangeListener1() throws IOException, IllegalAccessException { TlsFileWatcher.getInstance().addFileChangeListener(filePath -> { }, "not/exist/path"); assertTrue(((Map) watchFilesMapField.get(TlsFileWatcher.getInstance())).isEmpty()); assertTrue(((Map) fileMd5MapField.get(TlsFileWatcher.getInstance())).isEmpty()); } @Test void testAddFileChangeListener2() throws IOException, IllegalAccessException { TlsFileWatcher.getInstance().addFileChangeListener(filePath -> { }, (String) null); assertTrue(((Map) watchFilesMapField.get(TlsFileWatcher.getInstance())).isEmpty()); assertTrue(((Map) fileMd5MapField.get(TlsFileWatcher.getInstance())).isEmpty()); } @Test void testAddFileChangeListener3() throws IOException, IllegalAccessException { TlsFileWatcher.getInstance().addFileChangeListener(filePath -> { }, tempFile.getPath()); assertEquals(1, ((Map) watchFilesMapField.get(TlsFileWatcher.getInstance())).size()); assertEquals(1, ((Map) fileMd5MapField.get(TlsFileWatcher.getInstance())).size()); } @Test void testStartGivenTlsFileNotChangeThenNoNotify() throws IllegalAccessException, InterruptedException, IOException { // given AtomicBoolean notified = new AtomicBoolean(false); TlsFileWatcher.getInstance().addFileChangeListener(filePath -> notified.set(true), tempFile.getPath()); // when TlsFileWatcher.getInstance().start(); // then assertFalse(notified.get()); } @Test void testStartGivenTlsFileChangeThenNotifyTheChangeFilePath() throws IllegalAccessException, IOException { // given AtomicBoolean notified = new AtomicBoolean(false); AtomicReference changedFilePath = new AtomicReference<>(); TlsFileWatcher.getInstance().addFileChangeListener(filePath -> { notified.set(true); changedFilePath.set(filePath); }, tempFile.getPath()); ((Map) fileMd5MapField.get(TlsFileWatcher.getInstance())).put("test.txt", ""); // when TlsFileWatcher.getInstance().start(); // then assertTrue(notified.get()); assertEquals("test.txt", changedFilePath.get()); } @Test void testStartGivenTaskIsAlreadyRunThenNotRunAgain() { TlsFileWatcher.getInstance().start(); TlsFileWatcher.getInstance().start(); verify(executorService, times(1)).scheduleAtFixedRate(any(), anyLong(), anyLong(), any()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/tls/TlsHelperTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.tls; import org.junit.jupiter.api.Test; import javax.net.ssl.SSLContext; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; import static org.junit.jupiter.api.Assertions.assertNotNull; class TlsHelperTest { @Test void testBuildSslContext() throws KeyManagementException, NoSuchAlgorithmException { SSLContext actual = TlsHelper.buildSslContext(true); assertNotNull(actual); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/event/naming/HealthStateChangeTraceEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import com.alibaba.nacos.common.trace.HealthCheckType; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; class HealthStateChangeTraceEventTest extends NamingTraceEventTest { @Test void testHealthStateChangeTraceEventForClientBeat() { HealthStateChangeTraceEvent healthStateChangeTraceEvent = new HealthStateChangeTraceEvent(TIME, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, IP, PORT, false, "client_beat"); assertBasicInfo(healthStateChangeTraceEvent); assertHealthChangeInfo(healthStateChangeTraceEvent); assertEquals(HealthCheckType.CLIENT_BEAT, healthStateChangeTraceEvent.getHealthCheckType()); assertEquals("client_beat", healthStateChangeTraceEvent.getHealthStateChangeReason()); } @Test void testHealthStateChangeTraceEventForTcp() { HealthStateChangeTraceEvent healthStateChangeTraceEvent = new HealthStateChangeTraceEvent(TIME, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, IP, PORT, false, "tcp:unable2connect:"); assertBasicInfo(healthStateChangeTraceEvent); assertHealthChangeInfo(healthStateChangeTraceEvent); assertEquals(HealthCheckType.TCP_SUPER_SENSE, healthStateChangeTraceEvent.getHealthCheckType()); assertEquals("tcp:unable2connect:", healthStateChangeTraceEvent.getHealthStateChangeReason()); } @Test void testHealthStateChangeTraceEventForHttp() { HealthStateChangeTraceEvent healthStateChangeTraceEvent = new HealthStateChangeTraceEvent(TIME, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, IP, PORT, false, "http:error:"); assertBasicInfo(healthStateChangeTraceEvent); assertHealthChangeInfo(healthStateChangeTraceEvent); assertEquals(HealthCheckType.HTTP_HEALTH_CHECK, healthStateChangeTraceEvent.getHealthCheckType()); assertEquals("http:error:", healthStateChangeTraceEvent.getHealthStateChangeReason()); } @Test void testHealthStateChangeTraceEventForMysql() { HealthStateChangeTraceEvent healthStateChangeTraceEvent = new HealthStateChangeTraceEvent(TIME, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, IP, PORT, false, "mysql:timeout:"); assertBasicInfo(healthStateChangeTraceEvent); assertHealthChangeInfo(healthStateChangeTraceEvent); assertEquals(HealthCheckType.MYSQL_HEALTH_CHECK, healthStateChangeTraceEvent.getHealthCheckType()); assertEquals("mysql:timeout:", healthStateChangeTraceEvent.getHealthStateChangeReason()); } private void assertHealthChangeInfo(HealthStateChangeTraceEvent event) { assertEquals("HEALTH_STATE_CHANGE_TRACE_EVENT", event.getType()); assertEquals(IP, event.getInstanceIp()); assertEquals(PORT, event.getInstancePort()); assertEquals(IP + ":" + PORT, event.toInetAddr()); assertFalse(event.isHealthy()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/event/naming/InstanceTraceEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import com.alibaba.nacos.common.trace.DeregisterInstanceReason; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class InstanceTraceEventTest extends NamingTraceEventTest { @Test void testRegisterInstanceTraceEvent() { RegisterInstanceTraceEvent registerInstanceTraceEvent = new RegisterInstanceTraceEvent(TIME, CLIENT_IP, true, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, IP, PORT); assertBasicInfo(registerInstanceTraceEvent); assertEquals("REGISTER_INSTANCE_TRACE_EVENT", registerInstanceTraceEvent.getType()); assertEquals(CLIENT_IP, registerInstanceTraceEvent.getClientIp()); assertTrue(registerInstanceTraceEvent.isRpc()); assertEquals(IP, registerInstanceTraceEvent.getInstanceIp()); assertEquals(PORT, registerInstanceTraceEvent.getInstancePort()); assertEquals(IP + ":" + PORT, registerInstanceTraceEvent.toInetAddr()); } @Test void testDeregisterInstanceTraceEvent() { DeregisterInstanceTraceEvent deregisterInstanceTraceEvent = new DeregisterInstanceTraceEvent(TIME, CLIENT_IP, true, DeregisterInstanceReason.NATIVE_DISCONNECTED, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, IP, PORT); assertBasicInfo(deregisterInstanceTraceEvent); assertEquals("DEREGISTER_INSTANCE_TRACE_EVENT", deregisterInstanceTraceEvent.getType()); assertEquals(CLIENT_IP, deregisterInstanceTraceEvent.getClientIp()); assertTrue(deregisterInstanceTraceEvent.isRpc()); assertEquals(IP, deregisterInstanceTraceEvent.getInstanceIp()); assertEquals(PORT, deregisterInstanceTraceEvent.getInstancePort()); assertEquals(IP + ":" + PORT, deregisterInstanceTraceEvent.toInetAddr()); assertEquals(DeregisterInstanceReason.NATIVE_DISCONNECTED, deregisterInstanceTraceEvent.getReason()); } @Test void testUpdateInstanceTraceEvent() { Map metadata = new HashMap<>(); metadata.put("test1", "testValue"); UpdateInstanceTraceEvent updateInstanceTraceEvent = new UpdateInstanceTraceEvent(TIME, CLIENT_IP, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, IP, PORT, metadata); assertBasicInfo(updateInstanceTraceEvent); assertEquals("UPDATE_INSTANCE_TRACE_EVENT", updateInstanceTraceEvent.getType()); assertEquals(CLIENT_IP, updateInstanceTraceEvent.getClientIp()); assertEquals(IP, updateInstanceTraceEvent.getInstanceIp()); assertEquals(PORT, updateInstanceTraceEvent.getInstancePort()); assertEquals(IP + ":" + PORT, updateInstanceTraceEvent.toInetAddr()); assertEquals(metadata, updateInstanceTraceEvent.getMetadata()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/event/naming/NamingTraceEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class NamingTraceEventTest { protected static final long TIME = System.currentTimeMillis(); protected static final String NAMESPACE_ID = "ns"; protected static final String GROUP_NAME = "testG"; protected static final String SERVICE_NAME = "testS"; protected static final String CLUSTER_NAME = "test_cluster"; protected static final String IP = "127.0.0.1"; protected static final int PORT = 8848; protected static final String CLIENT_IP = "1.1.1.1"; protected void assertBasicInfo(NamingTraceEvent event) { assertEquals(TIME, event.getEventTime()); assertEquals(NAMESPACE_ID, event.getNamespace()); assertEquals(GROUP_NAME, event.getGroup()); assertEquals(SERVICE_NAME, event.getName()); assertTrue(event.isPluginEvent()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/event/naming/ServiceTraceEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; class ServiceTraceEventTest extends NamingTraceEventTest { @Test void testRegisterInstanceTraceEvent() { RegisterServiceTraceEvent registerServiceTraceEvent = new RegisterServiceTraceEvent(TIME, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME); assertBasicInfo(registerServiceTraceEvent); assertEquals("REGISTER_SERVICE_TRACE_EVENT", registerServiceTraceEvent.getType()); } @Test void testDeregisterInstanceTraceEvent() { DeregisterServiceTraceEvent deregisterServiceTraceEvent = new DeregisterServiceTraceEvent(TIME, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME); assertBasicInfo(deregisterServiceTraceEvent); assertEquals("DEREGISTER_SERVICE_TRACE_EVENT", deregisterServiceTraceEvent.getType()); } @Test void testUpdateInstanceTraceEvent() { Map metadata = new HashMap<>(); metadata.put("test1", "testValue"); UpdateServiceTraceEvent updateServiceTraceEvent = new UpdateServiceTraceEvent(TIME, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, metadata); assertBasicInfo(updateServiceTraceEvent); assertEquals("UPDATE_SERVICE_TRACE_EVENT", updateServiceTraceEvent.getType()); assertEquals(metadata, updateServiceTraceEvent.getMetadata()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/event/naming/SubscribeTraceEventTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.event.naming; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class SubscribeTraceEventTest extends NamingTraceEventTest { @Test void testRegisterInstanceTraceEvent() { SubscribeServiceTraceEvent subscribeServiceTraceEvent = new SubscribeServiceTraceEvent(TIME, CLIENT_IP, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME); assertBasicInfo(subscribeServiceTraceEvent); assertEquals("SUBSCRIBE_SERVICE_TRACE_EVENT", subscribeServiceTraceEvent.getType()); assertEquals(CLIENT_IP, subscribeServiceTraceEvent.getClientIp()); } @Test void testDeregisterInstanceTraceEvent() { UnsubscribeServiceTraceEvent unsubscribeServiceTraceEvent = new UnsubscribeServiceTraceEvent(TIME, CLIENT_IP, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME); assertBasicInfo(unsubscribeServiceTraceEvent); assertEquals("UNSUBSCRIBE_SERVICE_TRACE_EVENT", unsubscribeServiceTraceEvent.getType()); assertEquals(CLIENT_IP, unsubscribeServiceTraceEvent.getClientIp()); } @Test void testPushServiceTraceEvent() { PushServiceTraceEvent pushServiceTraceEvent = new PushServiceTraceEvent(TIME, 10, 510, 510, CLIENT_IP, NAMESPACE_ID, GROUP_NAME, SERVICE_NAME, 100); assertBasicInfo(pushServiceTraceEvent); assertEquals("PUSH_SERVICE_TRACE_EVENT", pushServiceTraceEvent.getType()); assertEquals(CLIENT_IP, pushServiceTraceEvent.getClientIp()); assertEquals(10L, pushServiceTraceEvent.getPushCostTimeForNetWork()); assertEquals(510L, pushServiceTraceEvent.getPushCostTimeForAll()); assertEquals(510L, pushServiceTraceEvent.getServiceLevelAgreementTime()); assertEquals(100, pushServiceTraceEvent.getInstanceSize()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisherFactoryTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.publisher; import com.alibaba.nacos.common.notify.EventPublisher; import com.alibaba.nacos.common.notify.NotifyCenter; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; class TraceEventPublisherFactoryTest { private Map originalEventPublisherMap; @BeforeEach void setUp() throws Exception { originalEventPublisherMap = new HashMap<>(NotifyCenter.getPublisherMap()); NotifyCenter.getPublisherMap().clear(); // Protect other unit test publisher affect this case. Field field = TraceEventPublisherFactory.class.getDeclaredField("publisher"); field.setAccessible(true); Map map = (Map) field.get(TraceEventPublisherFactory.getInstance()); map.clear(); } @AfterEach void tearDown() throws Exception { NotifyCenter.getPublisherMap().clear(); NotifyCenter.getPublisherMap().putAll(originalEventPublisherMap); originalEventPublisherMap = null; } @Test void testApply() { TraceEventPublisherFactory.getInstance().apply(TraceTestEvent.TraceTestEvent1.class, Byte.SIZE); TraceEventPublisherFactory.getInstance().apply(TraceTestEvent.TraceTestEvent2.class, Byte.SIZE); TraceEventPublisherFactory.getInstance().apply(TraceTestEvent.class, Byte.SIZE); String expectedStatus = "Trace event publisher statues:\n" + "\tPublisher TraceEvent : shutdown=false, queue= 0/8 \n"; assertEquals(expectedStatus, TraceEventPublisherFactory.getInstance().getAllPublisherStatues()); } @Test void testApplyAfterAddEventType() { TraceEventPublisherFactory.getInstance().addPublisherEvent(TraceTestEvent.class); TraceEventPublisherFactory.getInstance().apply(TraceTestEvent.TraceTestEvent1.class, Byte.SIZE); TraceEventPublisherFactory.getInstance().apply(TraceTestEvent.TraceTestEvent2.class, Byte.SIZE); TraceEventPublisherFactory.getInstance().apply(TraceTestEvent.class, Byte.SIZE); String expectedStatus = "Trace event publisher statues:\n" + "\tPublisher TraceTestEvent : shutdown=false, queue= 0/8 \n"; assertEquals(expectedStatus, TraceEventPublisherFactory.getInstance().getAllPublisherStatues()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/publisher/TraceEventPublisherTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.publisher; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.ThreadUtils; 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.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class TraceEventPublisherTest { @Mock private Subscriber subscriber; @Mock private SmartSubscriber smartSubscriber; private TraceEventPublisher traceEventPublisher; @BeforeEach void setUp() throws Exception { traceEventPublisher = new TraceEventPublisher(); traceEventPublisher.init(TraceTestEvent.class, Byte.SIZE); } @AfterEach void tearDown() throws Exception { traceEventPublisher.shutdown(); } @Test void testAddSubscriber() { when(subscriber.subscribeType()).thenReturn(TraceTestEvent.TraceTestEvent1.class); traceEventPublisher.addSubscriber(subscriber); traceEventPublisher.addSubscriber(smartSubscriber, TraceTestEvent.TraceTestEvent2.class); TraceTestEvent.TraceTestEvent1 traceTestEvent1 = new TraceTestEvent.TraceTestEvent1(); TraceTestEvent.TraceTestEvent2 traceTestEvent2 = new TraceTestEvent.TraceTestEvent2(); traceEventPublisher.publish(traceTestEvent1); traceEventPublisher.publish(traceTestEvent2); ThreadUtils.sleep(2000L); verify(subscriber).onEvent(traceTestEvent1); verify(smartSubscriber).onEvent(traceTestEvent2); } @Test void testRemoveSubscriber() { traceEventPublisher.addSubscriber(subscriber, TraceTestEvent.TraceTestEvent1.class); traceEventPublisher.addSubscriber(smartSubscriber, TraceTestEvent.TraceTestEvent1.class); TraceTestEvent.TraceTestEvent1 traceTestEvent1 = new TraceTestEvent.TraceTestEvent1(); traceEventPublisher.publish(traceTestEvent1); ThreadUtils.sleep(2000L); verify(subscriber).onEvent(traceTestEvent1); verify(smartSubscriber).onEvent(traceTestEvent1); traceEventPublisher.removeSubscriber(smartSubscriber, TraceTestEvent.TraceTestEvent1.class); traceTestEvent1 = new TraceTestEvent.TraceTestEvent1(); traceEventPublisher.publish(traceTestEvent1); ThreadUtils.sleep(500L); verify(subscriber).onEvent(traceTestEvent1); verify(smartSubscriber, never()).onEvent(traceTestEvent1); reset(subscriber); when(subscriber.subscribeType()).thenReturn(TraceTestEvent.TraceTestEvent1.class); traceEventPublisher.removeSubscriber(subscriber); traceEventPublisher.publish(traceTestEvent1); ThreadUtils.sleep(500L); verify(subscriber, never()).onEvent(traceTestEvent1); verify(smartSubscriber, never()).onEvent(traceTestEvent1); } @Test void getStatus() throws NacosException { traceEventPublisher.publish(new TraceTestEvent()); traceEventPublisher.publish(new TraceTestEvent.TraceTestEvent1()); traceEventPublisher.publish(new TraceTestEvent.TraceTestEvent2()); String expectedStatus = "Publisher TraceTestEvent : shutdown=false, queue= 3/8 "; assertEquals(traceEventPublisher.getStatus(), expectedStatus); traceEventPublisher.addSubscriber(subscriber, TraceTestEvent.TraceTestEvent1.class); ThreadUtils.sleep(2000L); expectedStatus = "Publisher TraceTestEvent : shutdown=false, queue= 0/8 "; assertEquals(traceEventPublisher.getStatus(), expectedStatus); traceEventPublisher.shutdown(); expectedStatus = "Publisher TraceTestEvent : shutdown= true, queue= 0/8 "; assertEquals(traceEventPublisher.getStatus(), expectedStatus); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/trace/publisher/TraceTestEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.trace.publisher; import com.alibaba.nacos.common.notify.Event; public class TraceTestEvent extends Event { private static final long serialVersionUID = 8568231862586636388L; static class TraceTestEvent1 extends TraceTestEvent { private static final long serialVersionUID = 4188906203345433816L; } static class TraceTestEvent2 extends TraceTestEvent { private static final long serialVersionUID = -7358195336881398548L; } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ArrayUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Test ArrayUtils. * * @author zzq */ class ArrayUtilsTest { Integer[] nullArr = null; Integer[] nothingArr = new Integer[] {}; @Test void testisEmpty() { Integer[] arr = new Integer[] {1, 2}; assertTrue(ArrayUtils.isEmpty(nullArr)); assertTrue(ArrayUtils.isEmpty(nothingArr)); assertFalse(ArrayUtils.isEmpty(arr)); } @Test void contains() { assertFalse(ArrayUtils.contains(nullArr, "a")); assertFalse(ArrayUtils.contains(nullArr, null)); assertFalse(ArrayUtils.contains(nothingArr, "b")); Integer[] arr = new Integer[] {1, 2, 3}; assertFalse(ArrayUtils.contains(arr, null)); Integer[] arr1 = new Integer[] {1, 2, 3, null}; assertTrue(ArrayUtils.contains(arr1, null)); assertTrue(ArrayUtils.contains(arr, 1)); assertFalse(ArrayUtils.contains(arr, "1")); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ByteUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.common.utils.to.User; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * ByteUtils Test. * * @ClassName: ByteUtilsTest * @Author: ChenHao26 * @Date: 2022/8/22 10:58 */ class ByteUtilsTest { @Test void objectToByte() { User user = new User(1, "google"); byte[] bytes = ByteUtils.toBytes(user); assertNotNull(bytes); } @Test void stringToByte() { byte[] bytes = ByteUtils.toBytes("google"); assertNotNull(bytes); } @Test void toStringTest() { byte[] bytes = ByteUtils.toBytes("google"); String str = ByteUtils.toString(bytes); assertEquals("google", str); } @Test void testForInputNull() { assertEquals(0, ByteUtils.toBytes(null).length); assertEquals(0, ByteUtils.toBytes((Object) null).length); assertEquals("", ByteUtils.toString(null)); } @Test void isEmpty() { byte[] bytes = ByteUtils.toBytes(""); assertTrue(ByteUtils.isEmpty(bytes)); byte[] byte2 = new byte[1024]; assertFalse(ByteUtils.isEmpty(byte2)); byte[] byte3 = null; assertTrue(ByteUtils.isEmpty(byte3)); } @Test void isNotEmpty() { byte[] bytes = ByteUtils.toBytes("google"); assertTrue(ByteUtils.isNotEmpty(bytes)); byte[] bytes2 = ByteUtils.toBytes(""); assertFalse(ByteUtils.isNotEmpty(bytes2)); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ClassUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.util.Map; 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.junit.jupiter.api.Assertions.assertTrue; class ClassUtilsTest { @Test void testFindClassByName1() { Class clazz = ClassUtils.findClassByName("java.lang.Integer"); assertEquals("java.lang.Integer", clazz.getName()); } @Test void testFindClassByName2() { assertThrows(NacosRuntimeException.class, () -> { ClassUtils.findClassByName("not.exist.Class"); }); } @Test void testGetName() { final String name = "java.lang.Integer"; Integer val = 1; assertEquals(name, ClassUtils.getName(val)); assertEquals(name, ClassUtils.getName(Integer.class)); assertEquals(name, ClassUtils.getCanonicalName(val)); assertEquals(name, ClassUtils.getCanonicalName(Integer.class)); assertEquals("Integer", ClassUtils.getSimpleName(val)); assertEquals("Integer", ClassUtils.getSimpleName(Integer.class)); } @Test void testIsAssignableFrom() { assertTrue(ClassUtils.isAssignableFrom(Object.class, Integer.class)); } @Test void testForNameArray() throws ClassNotFoundException { Class clazz = ClassUtils.forName("[Lcom.alibaba.nacos.common.utils.ClassUtilsTest;", null); assertEquals("[Lcom.alibaba.nacos.common.utils.ClassUtilsTest;", clazz.getName()); clazz = ClassUtils.forName("java.lang.String[]", null); assertEquals("[Ljava.lang.String;", clazz.getName()); clazz = ClassUtils.forName("[[Ljava.lang.String;", null); assertEquals("[[Ljava.lang.String;", clazz.getName()); } @Test void testForNameNonExist() throws ClassNotFoundException { assertThrows(ClassNotFoundException.class, () -> { ClassUtils.forName("com.alibaba.nacos.common.NonExistClass", null); }); } @Test void testForNameFromPrimitive() throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException { Field field = ClassUtils.class.getDeclaredField("PRIMITIVE_TYPE_NAME_MAP"); field.setAccessible(true); Map> map = (Map>) field.get(null); map.put("Test", ClassUtilsTest.class); assertEquals(ClassUtilsTest.class, ClassUtils.forName("Test", null)); } @Test void testGetDefaultClassLoader() { ClassLoader cachedClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(null); assertNotNull(ClassUtils.getDefaultClassLoader()); } finally { Thread.currentThread().setContextClassLoader(cachedClassLoader); } } @Test void testClassPackageAsResourcePath() throws ClassNotFoundException { Class noPackageClass = ClassUtils.forName("ClassUtilsTestMockClass", null); assertEquals("", ClassUtils.classPackageAsResourcePath(null)); assertEquals("", ClassUtils.classPackageAsResourcePath(noPackageClass)); assertEquals("com/alibaba/nacos/common/utils", ClassUtils.classPackageAsResourcePath(ClassUtilsTest.class)); } @Test void testConvertClassNameAndClassPath() { String name = ClassUtilsTest.class.getName(); assertEquals("com/alibaba/nacos/common/utils/ClassUtilsTest", ClassUtils.convertClassNameToResourcePath(name)); assertEquals(name, ClassUtils.resourcePathToConvertClassName("com/alibaba/nacos/common/utils/ClassUtilsTest")); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/CollectionUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; 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.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; import java.util.Vector; import java.util.stream.Collectors; import java.util.stream.IntStream; 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; /** * Unit test of CollectionUtil. * * @author sunjifeng */ class CollectionUtilsTest { @Test void testGetList1() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(Collections.emptyList(), -1); }); } @Test void testGetList2() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(Collections.emptyList(), 1); }); } @Test void testGetList3() { assertEquals("element", CollectionUtils.get(Collections.singletonList("element"), 0)); assertEquals("element2", CollectionUtils.get(Arrays.asList("element1", "element2"), 1)); } @Test void testGetMap1() { assertThrows(IndexOutOfBoundsException.class, () -> { Map map = new HashMap<>(); map.put("key1", "value1"); CollectionUtils.get(map, -1); }); } @Test void testGetMap2() { assertThrows(IndexOutOfBoundsException.class, () -> { Map map = new HashMap<>(); map.put("key1", "value1"); CollectionUtils.get(map, -1); CollectionUtils.get(map, 1); }); } @Test void testGetMap3() { Map map1 = new LinkedHashMap(1); Map map2 = new LinkedHashMap(2); map1.put("key", "value"); map2.put("key1", "value1"); map2.put("key2", "value2"); Iterator> iter = map1.entrySet().iterator(); assertEquals(iter.next(), CollectionUtils.get(map1, 0)); Iterator> iter2 = map2.entrySet().iterator(); iter2.next(); Map.Entry second = iter2.next(); assertEquals(second, CollectionUtils.get(map2, 1)); } @Test void testGetArray1() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(new Object[] {}, -1); }); } @Test void testGetArray2() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(new Object[] {}, 0); }); } @Test void testGetArray3() { assertEquals("1", CollectionUtils.get(new Object[] {"1"}, 0)); assertEquals("2", CollectionUtils.get(new Object[] {"1", "2"}, 1)); } @Test void testGetArray4() { assertThrows(ArrayIndexOutOfBoundsException.class, () -> { CollectionUtils.get(new int[] {}, 0); }); } @Test void testGetArray5() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(new int[] {}, -1); }); } @Test void testGetArray6() { assertEquals(1, CollectionUtils.get(new int[] {1, 2}, 0)); assertEquals(2, CollectionUtils.get(new int[] {1, 2}, 1)); } @Test void testGetIterator1() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(Collections.emptyIterator(), 0); }); } @Test void testGetIterator2() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(Collections.emptyIterator(), -1); }); } @Test void testGetIterator3() { assertEquals("1", CollectionUtils.get(Collections.singleton("1").iterator(), 0)); assertEquals("2", CollectionUtils.get(Arrays.asList("1", "2").iterator(), 1)); } @Test void testGetCollection1() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(Collections.emptySet(), 0); }); } @Test void testGetCollection2() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(Collections.emptySet(), -1); }); } @Test void testGetCollection3() { assertEquals("1", CollectionUtils.get(Collections.singleton("1"), 0)); assertEquals("2", CollectionUtils.get(CollectionUtils.set("1", "2"), 1)); } @Test void testGetEnumeration1() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(asEnumeration(Collections.emptyIterator()), 0); }); } @Test void testGetEnumeration2() { assertThrows(IndexOutOfBoundsException.class, () -> { CollectionUtils.get(asEnumeration(Collections.emptyIterator()), -1); }); } @Test void testGetEnumeration3() { Vector vector = new Vector<>(); vector.add("1"); vector.add("2"); assertEquals("1", CollectionUtils.get(vector.elements(), 0)); assertEquals("2", CollectionUtils.get(vector.elements(), 1)); } @Test void testGet1() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.get(null, 0); }); } @Test void testGet2() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.get("string", 0); }); } @Test void testSize() { // collection assertEquals(0, CollectionUtils.size(Collections.emptyList())); assertEquals(1, CollectionUtils.size(Collections.singletonList(""))); assertEquals(10, CollectionUtils.size(IntStream.range(0, 10).boxed().collect(Collectors.toList()))); // map Map map = new HashMap<>(); map.put("key1", "value1"); assertEquals(1, CollectionUtils.size(map)); map.put("key2", "value2"); assertEquals(2, CollectionUtils.size(map)); map.put("key3", "value3"); assertEquals(3, CollectionUtils.size(map)); // array assertEquals(1, CollectionUtils.size(new Object[] {"1"})); assertEquals(2, CollectionUtils.size(new Object[] {"1", "2"})); assertEquals(6, CollectionUtils.size(new Object[] {"1", "2", "3", "4", "5", "6"})); assertEquals(1000, CollectionUtils.size(IntStream.range(0, 1000).boxed().toArray())); // primitive array assertEquals(1, CollectionUtils.size(new int[] {1})); assertEquals(2, CollectionUtils.size(new int[] {1, 2})); assertEquals(6, CollectionUtils.size(new int[] {1, 2, 3, 4, 5, 6})); assertEquals(1000, CollectionUtils.size(IntStream.range(0, 1000).toArray())); // iterator assertEquals(1, CollectionUtils.size(Collections.singleton("1").iterator())); assertEquals(2, CollectionUtils.size(Arrays.asList("1", "2").iterator())); // enumeration assertEquals(0, CollectionUtils.size(asEnumeration(Collections.emptyIterator()))); assertEquals(1, CollectionUtils.size(asEnumeration(Collections.singleton("").iterator()))); } @Test void testSize1() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.size(null); }); } @Test void testSize2() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.size("string"); }); } @Test void testSizeIsEmpty() { // collection assertTrue(CollectionUtils.sizeIsEmpty(Collections.emptyList())); assertFalse(CollectionUtils.sizeIsEmpty(Collections.singletonList(""))); // map Map map = new HashMap<>(); map.put("key1", "value1"); map.put("key2", "value2"); assertTrue(CollectionUtils.sizeIsEmpty(Collections.emptyMap())); assertFalse(CollectionUtils.sizeIsEmpty(map)); // array assertTrue(CollectionUtils.sizeIsEmpty(new Object[] {})); assertFalse(CollectionUtils.sizeIsEmpty(new Object[] {"1", "2"})); // primitive array assertTrue(CollectionUtils.sizeIsEmpty(new int[] {})); assertFalse(CollectionUtils.sizeIsEmpty(new int[] {1, 2})); // iterator assertTrue(CollectionUtils.sizeIsEmpty(Collections.emptyIterator())); assertFalse(CollectionUtils.sizeIsEmpty(Arrays.asList("1", "2").iterator())); // enumeration assertTrue(CollectionUtils.sizeIsEmpty(asEnumeration(Collections.emptyIterator()))); assertFalse(CollectionUtils.sizeIsEmpty(asEnumeration(Collections.singleton("").iterator()))); } @Test void testSizeIsEmpty1() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.sizeIsEmpty(null); }); } @Test void testSizeIsEmpty2() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.sizeIsEmpty("string"); }); } @Test void testContains() { assertTrue(CollectionUtils.contains(Collections.singletonList("target"), "target")); assertFalse(CollectionUtils.contains(Collections.emptyList(), "target")); } @Test void testIsEmpty() { assertFalse(CollectionUtils.isEmpty(Collections.singletonList("target"))); assertTrue(CollectionUtils.isEmpty(Collections.emptyList())); assertTrue(CollectionUtils.isEmpty(null)); } @Test void testIsNotEmpty() { assertTrue(CollectionUtils.isNotEmpty(Collections.singletonList("target"))); assertFalse(CollectionUtils.isNotEmpty(Collections.emptyList())); assertFalse(CollectionUtils.isNotEmpty(null)); } @Test void testGetOrDefault() { assertEquals("default", CollectionUtils.getOrDefault(Collections.emptyList(), 1, "default")); assertEquals("element", CollectionUtils.getOrDefault(Collections.singletonList("element"), 0, "default")); } @Test void testList() { assertEquals(Arrays.asList(null, null, null), CollectionUtils.list(null, null, null)); assertEquals(Arrays.asList("", "a", "b"), CollectionUtils.list("", "a", "b")); assertEquals(new ArrayList(), CollectionUtils.list()); } @Test void testListNullPointerException() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.list(null); }); } private Enumeration asEnumeration(final Iterator iterator) { if (iterator == null) { throw new IllegalArgumentException("iterator cannot be null "); } return new Enumeration() { public boolean hasMoreElements() { return iterator.hasNext(); } public T nextElement() { return iterator.next(); } }; } @Test void testSet() { Set set = new HashSet<>(); set.add(null); assertEquals(set, CollectionUtils.set(null, null, null)); assertEquals(new LinkedHashSet(Arrays.asList("", "a", "b")), CollectionUtils.set("", "a", "b")); assertEquals(new HashSet(), CollectionUtils.set()); } @Test void testSetNullPointerException() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.set(null); }); } @Test void testGetOnlyElementIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> { List list = Arrays.asList(1, 2, 3, 4, 5, 6); CollectionUtils.getOnlyElement(list); }); } @Test void testGetOnlyElementIllegalArgumentException2() { assertThrows(IllegalArgumentException.class, () -> { CollectionUtils.getOnlyElement(null); }); } @Test void testGetOnlyElementNoSuchElementException() { assertThrows(NoSuchElementException.class, () -> { List list = new ArrayList<>(); CollectionUtils.getOnlyElement(list); }); } @Test void testGetOnly() { List list = Arrays.asList(1); int element = CollectionUtils.getOnlyElement(list); assertEquals(1, element); } @Test void testIsMapEmpty() { assertTrue(CollectionUtils.isMapEmpty(null)); assertTrue(CollectionUtils.isMapEmpty(Collections.emptyMap())); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ConcurrentHashSetTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.util.ConcurrentModificationException; import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * ConcurrentHashSet Test. * * @ClassName: ConcurrentHashSetTest * @Author: ChenHao26 * @Date: 2022/8/22 11:21 */ class ConcurrentHashSetTest { @Test void testBasicOps() { Set set = new ConcurrentHashSet<>(); // addition assertTrue(set.add(0)); assertTrue(set.add(1)); assertTrue(set.contains(0)); assertTrue(set.contains(1)); assertFalse(set.contains(-1)); assertEquals(2, set.size()); // iter for (int i : set) { assertTrue(i == 0 || i == 1); } // removal assertTrue(set.remove(0)); assertFalse(set.remove(0)); assertFalse(set.contains(0)); assertTrue(set.contains(1)); assertEquals(1, set.size()); // clear assertFalse(set.isEmpty()); set.clear(); assertEquals(0, set.size()); assertTrue(set.isEmpty()); } @Test void testMultiThread() throws Exception { int count = 5; SetMultiThreadChecker hashSetChecker = new SetMultiThreadChecker(new HashSet<>()); hashSetChecker.start(); while (!hashSetChecker.hasConcurrentError() && hashSetChecker.isRunning()) { TimeUnit.SECONDS.sleep(1); if (count <= 0) { hashSetChecker.stop(); } count--; } assertTrue(hashSetChecker.hasConcurrentError()); count = 5; SetMultiThreadChecker concurrentSetChecker = new SetMultiThreadChecker(new ConcurrentHashSet<>()); concurrentSetChecker.start(); while (!concurrentSetChecker.hasConcurrentError() && concurrentSetChecker.isRunning()) { TimeUnit.SECONDS.sleep(1); if (count == 0) { concurrentSetChecker.stop(); } count--; } assertFalse(concurrentSetChecker.hasConcurrentError()); } static class SetMultiThreadChecker { private final AddDataThread addThread; private final DeleteDataThread deleteThread; private final IteratorThread iteratorThread; public SetMultiThreadChecker(Set setToCheck) { for (int i = 0; i < 1000; i++) { setToCheck.add(i); } this.addThread = new AddDataThread(setToCheck); this.deleteThread = new DeleteDataThread(setToCheck); this.iteratorThread = new IteratorThread(setToCheck); } public void start() { new Thread(addThread).start(); new Thread(deleteThread).start(); new Thread(iteratorThread).start(); } public boolean hasConcurrentError() { return addThread.hasConcurrentError() || deleteThread.hasConcurrentError() || iteratorThread.hasConcurrentError(); } public boolean isRunning() { return addThread.isRunning() || deleteThread.isRunning() || iteratorThread.isRunning(); } public void stop() { addThread.stop(); deleteThread.stop(); iteratorThread.stop(); } } abstract static class ConcurrentCheckThread implements Runnable { protected final Set hashSet; protected boolean concurrentError = false; protected boolean finish = false; public ConcurrentCheckThread(Set hashSet) { this.hashSet = hashSet; } public boolean hasConcurrentError() { return concurrentError; } public void stop() { finish = true; } public boolean isRunning() { return !finish; } @Override public void run() { try { while (isRunning()) { process(); } } catch (ConcurrentModificationException e) { concurrentError = true; } finally { finish = true; } } protected abstract void process(); } //add data thread static class AddDataThread extends ConcurrentCheckThread implements Runnable { public AddDataThread(Set hashSet) { super(hashSet); } @Override protected void process() { int random = new Random().nextInt(1000); hashSet.add(random); } } // delete data thread static class DeleteDataThread extends ConcurrentCheckThread implements Runnable { public DeleteDataThread(Set hashSet) { super(hashSet); } @Override protected void process() { int random = new Random().nextInt(1000); hashSet.remove(random); } } static class IteratorThread extends ConcurrentCheckThread implements Runnable { public IteratorThread(Set hashSet) { super(hashSet); } @Override public void run() { System.out.println("start -- hashSet.size() : " + hashSet.size()); Integer f = null; try { while (isRunning()) { for (Integer i : hashSet) { f = i; } } } catch (ConcurrentModificationException e) { concurrentError = true; } finally { finish = true; } System.out.println("finished at " + f); System.out.println("end -- hashSet.size() : " + hashSet.size()); } @Override protected void process() { } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ConnLabelsUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.util.Map; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * description. * * @author rong * @date 2024-03-01 15:10 */ class ConnLabelsUtilsTest { @Test void testParsePropertyValue2Map() { Properties properties = new Properties(); String property = "property"; String rawValue = "k1 = v1, k2 = v2"; properties.put(property, rawValue); String property1 = "property2"; String rawValue1 = "k11=v11, kk2"; properties.put(property1, rawValue1); Map m = ConnLabelsUtils.parsePropertyValue2Map(properties, property); assertEquals(2, m.size()); assertEquals("v1", m.get("k1")); assertEquals("v2", m.get("k2")); Map m1 = ConnLabelsUtils.parsePropertyValue2Map(properties, property1); assertEquals(1, m1.size()); assertEquals("v11", m1.get("k11")); assertNull(m1.get("kk2")); m = ConnLabelsUtils.mergeMapByOrder(m, m1); assertEquals(3, m.size()); assertEquals("v1", m.get("k1")); assertEquals("v2", m.get("k2")); assertEquals("v11", m.get("k11")); m = ConnLabelsUtils.addPrefixForEachKey(m, "test_prefix"); assertEquals(3, m.size()); m.forEach((k, v) -> { assertTrue(k.startsWith("test_prefix")); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ConvertUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.util.ArrayList; 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; /** * Unit test of ConvertUtils. * * @author sunjifeng */ class ConvertUtilsTest { @Test void testToInt() { // ConvertUtils.toInt(String) assertEquals(0, ConvertUtils.toInt("0")); assertEquals(-1, ConvertUtils.toInt("-1")); assertEquals(10, ConvertUtils.toInt("10")); assertEquals(Integer.MAX_VALUE, ConvertUtils.toInt(String.valueOf(Integer.MAX_VALUE))); assertEquals(Integer.MIN_VALUE, ConvertUtils.toInt(String.valueOf(Integer.MIN_VALUE))); assertEquals(0, ConvertUtils.toInt("notIntValue")); // ConvertUtils.toInt(String, Integer) assertEquals(0, ConvertUtils.toInt("0", 100)); assertEquals(100, ConvertUtils.toInt(null, 100)); assertEquals(100, ConvertUtils.toInt("null", 100)); assertEquals(100, ConvertUtils.toInt("notIntValue", 100)); } @Test void testToLong() { // ConvertUtils.toLong(Object) assertEquals(0L, ConvertUtils.toLong(new ArrayList<>())); assertEquals(10L, ConvertUtils.toLong((Object) 10L)); // ConvertUtils.toLong(String) assertEquals(0L, ConvertUtils.toLong("0")); assertEquals(-1L, ConvertUtils.toLong("-1")); assertEquals(10L, ConvertUtils.toLong("10")); assertEquals(Long.MAX_VALUE, ConvertUtils.toLong(String.valueOf(Long.MAX_VALUE))); assertEquals(Long.MIN_VALUE, ConvertUtils.toLong(String.valueOf(Long.MIN_VALUE))); assertEquals(0L, ConvertUtils.toLong("notIntValue")); // ConvertUtils.toLong(String, Integer) assertEquals(0L, ConvertUtils.toLong("0", 100L)); assertEquals(100L, ConvertUtils.toLong(null, 100L)); assertEquals(100L, ConvertUtils.toLong("null", 100L)); assertEquals(100L, ConvertUtils.toLong("notIntValue", 100L)); } @Test void testToBoolean() { // ConvertUtils.toBoolean(String) assertTrue(ConvertUtils.toBoolean("true")); assertTrue(ConvertUtils.toBoolean("True")); assertTrue(ConvertUtils.toBoolean("TRUE")); assertFalse(ConvertUtils.toBoolean("false")); assertFalse(ConvertUtils.toBoolean("False")); assertFalse(ConvertUtils.toBoolean("FALSE")); assertFalse(ConvertUtils.toBoolean(null)); assertFalse(ConvertUtils.toBoolean("notBoolean")); // ConvertUtils.toBoolean(String, boolean) assertFalse(ConvertUtils.toBoolean("", false)); assertFalse(ConvertUtils.toBoolean(null, false)); assertFalse(ConvertUtils.toBoolean("notBoolean", false)); assertTrue(ConvertUtils.toBoolean("true", false)); } @Test void testToBooleanObject() { assertTrue(ConvertUtils.toBooleanObject("T")); assertTrue(ConvertUtils.toBooleanObject("t")); assertTrue(ConvertUtils.toBooleanObject("Y")); assertTrue(ConvertUtils.toBooleanObject("y")); assertFalse(ConvertUtils.toBooleanObject("f")); assertFalse(ConvertUtils.toBooleanObject("F")); assertFalse(ConvertUtils.toBooleanObject("n")); assertFalse(ConvertUtils.toBooleanObject("N")); assertNull(ConvertUtils.toBooleanObject("a")); assertTrue(ConvertUtils.toBooleanObject("on")); assertTrue(ConvertUtils.toBooleanObject("oN")); assertTrue(ConvertUtils.toBooleanObject("On")); assertTrue(ConvertUtils.toBooleanObject("ON")); assertFalse(ConvertUtils.toBooleanObject("No")); assertFalse(ConvertUtils.toBooleanObject("NO")); assertNull(ConvertUtils.toBooleanObject("an")); assertNull(ConvertUtils.toBooleanObject("aN")); assertNull(ConvertUtils.toBooleanObject("oa")); assertNull(ConvertUtils.toBooleanObject("Oa")); assertNull(ConvertUtils.toBooleanObject("Na")); assertNull(ConvertUtils.toBooleanObject("na")); assertNull(ConvertUtils.toBooleanObject("aO")); assertNull(ConvertUtils.toBooleanObject("ao")); assertFalse(ConvertUtils.toBooleanObject("off")); assertFalse(ConvertUtils.toBooleanObject("ofF")); assertFalse(ConvertUtils.toBooleanObject("oFf")); assertFalse(ConvertUtils.toBooleanObject("oFF")); assertFalse(ConvertUtils.toBooleanObject("Off")); assertFalse(ConvertUtils.toBooleanObject("OfF")); assertFalse(ConvertUtils.toBooleanObject("OFf")); assertFalse(ConvertUtils.toBooleanObject("OFF")); assertTrue(ConvertUtils.toBooleanObject("yes")); assertTrue(ConvertUtils.toBooleanObject("yeS")); assertTrue(ConvertUtils.toBooleanObject("yEs")); assertTrue(ConvertUtils.toBooleanObject("yES")); assertTrue(ConvertUtils.toBooleanObject("Yes")); assertTrue(ConvertUtils.toBooleanObject("YeS")); assertTrue(ConvertUtils.toBooleanObject("YEs")); assertTrue(ConvertUtils.toBooleanObject("YES")); assertNull(ConvertUtils.toBooleanObject("ono")); assertNull(ConvertUtils.toBooleanObject("aes")); assertNull(ConvertUtils.toBooleanObject("aeS")); assertNull(ConvertUtils.toBooleanObject("aEs")); assertNull(ConvertUtils.toBooleanObject("aES")); assertNull(ConvertUtils.toBooleanObject("yas")); assertNull(ConvertUtils.toBooleanObject("yaS")); assertNull(ConvertUtils.toBooleanObject("Yas")); assertNull(ConvertUtils.toBooleanObject("YaS")); assertNull(ConvertUtils.toBooleanObject("yea")); assertNull(ConvertUtils.toBooleanObject("yEa")); assertNull(ConvertUtils.toBooleanObject("Yea")); assertNull(ConvertUtils.toBooleanObject("YEa")); assertNull(ConvertUtils.toBooleanObject("aff")); assertNull(ConvertUtils.toBooleanObject("afF")); assertNull(ConvertUtils.toBooleanObject("aFf")); assertNull(ConvertUtils.toBooleanObject("aFF")); assertNull(ConvertUtils.toBooleanObject("oaf")); assertNull(ConvertUtils.toBooleanObject("oaF")); assertNull(ConvertUtils.toBooleanObject("Oaf")); assertNull(ConvertUtils.toBooleanObject("OaF")); assertNull(ConvertUtils.toBooleanObject("Ofa")); assertNull(ConvertUtils.toBooleanObject("ofa")); assertNull(ConvertUtils.toBooleanObject("OFa")); assertNull(ConvertUtils.toBooleanObject("oFa")); assertTrue(ConvertUtils.toBooleanObject("true")); assertTrue(ConvertUtils.toBooleanObject("truE")); assertTrue(ConvertUtils.toBooleanObject("trUe")); assertTrue(ConvertUtils.toBooleanObject("trUE")); assertTrue(ConvertUtils.toBooleanObject("tRue")); assertTrue(ConvertUtils.toBooleanObject("tRuE")); assertTrue(ConvertUtils.toBooleanObject("tRUe")); assertTrue(ConvertUtils.toBooleanObject("tRUE")); assertTrue(ConvertUtils.toBooleanObject("True")); assertTrue(ConvertUtils.toBooleanObject("TruE")); assertTrue(ConvertUtils.toBooleanObject("TrUe")); assertTrue(ConvertUtils.toBooleanObject("TrUE")); assertTrue(ConvertUtils.toBooleanObject("TRue")); assertTrue(ConvertUtils.toBooleanObject("TRuE")); assertTrue(ConvertUtils.toBooleanObject("TRUe")); assertTrue(ConvertUtils.toBooleanObject("TRUE")); assertNull(ConvertUtils.toBooleanObject("Xrue")); assertNull(ConvertUtils.toBooleanObject("XruE")); assertNull(ConvertUtils.toBooleanObject("XrUe")); assertNull(ConvertUtils.toBooleanObject("XrUE")); assertNull(ConvertUtils.toBooleanObject("XRue")); assertNull(ConvertUtils.toBooleanObject("XRuE")); assertNull(ConvertUtils.toBooleanObject("XRUe")); assertNull(ConvertUtils.toBooleanObject("XRUE")); assertNull(ConvertUtils.toBooleanObject("tXue")); assertNull(ConvertUtils.toBooleanObject("tXuE")); assertNull(ConvertUtils.toBooleanObject("tXUe")); assertNull(ConvertUtils.toBooleanObject("tXUE")); assertNull(ConvertUtils.toBooleanObject("TXue")); assertNull(ConvertUtils.toBooleanObject("TXuE")); assertNull(ConvertUtils.toBooleanObject("TXUe")); assertNull(ConvertUtils.toBooleanObject("TXUE")); assertNull(ConvertUtils.toBooleanObject("trXe")); assertNull(ConvertUtils.toBooleanObject("trXE")); assertNull(ConvertUtils.toBooleanObject("tRXe")); assertNull(ConvertUtils.toBooleanObject("tRXE")); assertNull(ConvertUtils.toBooleanObject("TrXe")); assertNull(ConvertUtils.toBooleanObject("TrXE")); assertNull(ConvertUtils.toBooleanObject("TRXe")); assertNull(ConvertUtils.toBooleanObject("TRXE")); assertNull(ConvertUtils.toBooleanObject("truX")); assertNull(ConvertUtils.toBooleanObject("trUX")); assertNull(ConvertUtils.toBooleanObject("tRuX")); assertNull(ConvertUtils.toBooleanObject("tRUX")); assertNull(ConvertUtils.toBooleanObject("TruX")); assertNull(ConvertUtils.toBooleanObject("TrUX")); assertNull(ConvertUtils.toBooleanObject("TRuX")); assertNull(ConvertUtils.toBooleanObject("TRUX")); assertFalse(ConvertUtils.toBooleanObject("false")); assertFalse(ConvertUtils.toBooleanObject("falsE")); assertFalse(ConvertUtils.toBooleanObject("falSe")); assertFalse(ConvertUtils.toBooleanObject("falSE")); assertFalse(ConvertUtils.toBooleanObject("faLse")); assertFalse(ConvertUtils.toBooleanObject("faLsE")); assertFalse(ConvertUtils.toBooleanObject("faLSe")); assertFalse(ConvertUtils.toBooleanObject("faLSE")); assertFalse(ConvertUtils.toBooleanObject("fAlse")); assertFalse(ConvertUtils.toBooleanObject("fAlsE")); assertFalse(ConvertUtils.toBooleanObject("fAlSe")); assertFalse(ConvertUtils.toBooleanObject("fAlSE")); assertFalse(ConvertUtils.toBooleanObject("fALse")); assertFalse(ConvertUtils.toBooleanObject("fALsE")); assertFalse(ConvertUtils.toBooleanObject("fALSe")); assertFalse(ConvertUtils.toBooleanObject("fALSE")); assertFalse(ConvertUtils.toBooleanObject("False")); assertFalse(ConvertUtils.toBooleanObject("FalsE")); assertFalse(ConvertUtils.toBooleanObject("FalSe")); assertFalse(ConvertUtils.toBooleanObject("FalSE")); assertFalse(ConvertUtils.toBooleanObject("FaLse")); assertFalse(ConvertUtils.toBooleanObject("FaLsE")); assertFalse(ConvertUtils.toBooleanObject("FaLSe")); assertFalse(ConvertUtils.toBooleanObject("FaLSE")); assertFalse(ConvertUtils.toBooleanObject("FAlse")); assertFalse(ConvertUtils.toBooleanObject("FAlsE")); assertFalse(ConvertUtils.toBooleanObject("FAlSe")); assertFalse(ConvertUtils.toBooleanObject("FAlSE")); assertFalse(ConvertUtils.toBooleanObject("FALse")); assertFalse(ConvertUtils.toBooleanObject("FALsE")); assertFalse(ConvertUtils.toBooleanObject("FALSe")); assertFalse(ConvertUtils.toBooleanObject("FALSE")); assertNull(ConvertUtils.toBooleanObject("Xalse")); assertNull(ConvertUtils.toBooleanObject("XalsE")); assertNull(ConvertUtils.toBooleanObject("XalSe")); assertNull(ConvertUtils.toBooleanObject("XalSE")); assertNull(ConvertUtils.toBooleanObject("XaLse")); assertNull(ConvertUtils.toBooleanObject("XaLsE")); assertNull(ConvertUtils.toBooleanObject("XaLSe")); assertNull(ConvertUtils.toBooleanObject("XaLSE")); assertNull(ConvertUtils.toBooleanObject("XAlse")); assertNull(ConvertUtils.toBooleanObject("XAlsE")); assertNull(ConvertUtils.toBooleanObject("XAlSe")); assertNull(ConvertUtils.toBooleanObject("XAlSE")); assertNull(ConvertUtils.toBooleanObject("XALse")); assertNull(ConvertUtils.toBooleanObject("XALsE")); assertNull(ConvertUtils.toBooleanObject("XALSe")); assertNull(ConvertUtils.toBooleanObject("XALSE")); assertNull(ConvertUtils.toBooleanObject("fXlse")); assertNull(ConvertUtils.toBooleanObject("fXlsE")); assertNull(ConvertUtils.toBooleanObject("fXlSe")); assertNull(ConvertUtils.toBooleanObject("fXlSE")); assertNull(ConvertUtils.toBooleanObject("fXLse")); assertNull(ConvertUtils.toBooleanObject("fXLsE")); assertNull(ConvertUtils.toBooleanObject("fXLSe")); assertNull(ConvertUtils.toBooleanObject("fXLSE")); assertNull(ConvertUtils.toBooleanObject("FXlse")); assertNull(ConvertUtils.toBooleanObject("FXlsE")); assertNull(ConvertUtils.toBooleanObject("FXlSe")); assertNull(ConvertUtils.toBooleanObject("FXlSE")); assertNull(ConvertUtils.toBooleanObject("FXLse")); assertNull(ConvertUtils.toBooleanObject("FXLsE")); assertNull(ConvertUtils.toBooleanObject("FXLSe")); assertNull(ConvertUtils.toBooleanObject("FXLSE")); assertNull(ConvertUtils.toBooleanObject("faXse")); assertNull(ConvertUtils.toBooleanObject("faXsE")); assertNull(ConvertUtils.toBooleanObject("faXSe")); assertNull(ConvertUtils.toBooleanObject("faXSE")); assertNull(ConvertUtils.toBooleanObject("fAXse")); assertNull(ConvertUtils.toBooleanObject("fAXsE")); assertNull(ConvertUtils.toBooleanObject("fAXSe")); assertNull(ConvertUtils.toBooleanObject("fAXSE")); assertNull(ConvertUtils.toBooleanObject("FaXse")); assertNull(ConvertUtils.toBooleanObject("FaXsE")); assertNull(ConvertUtils.toBooleanObject("FaXSe")); assertNull(ConvertUtils.toBooleanObject("FaXSE")); assertNull(ConvertUtils.toBooleanObject("FAXse")); assertNull(ConvertUtils.toBooleanObject("FAXsE")); assertNull(ConvertUtils.toBooleanObject("FAXSe")); assertNull(ConvertUtils.toBooleanObject("FAXSE")); assertNull(ConvertUtils.toBooleanObject("falXe")); assertNull(ConvertUtils.toBooleanObject("falXE")); assertNull(ConvertUtils.toBooleanObject("faLXe")); assertNull(ConvertUtils.toBooleanObject("faLXE")); assertNull(ConvertUtils.toBooleanObject("fAlXe")); assertNull(ConvertUtils.toBooleanObject("fAlXE")); assertNull(ConvertUtils.toBooleanObject("fALXe")); assertNull(ConvertUtils.toBooleanObject("fALXE")); assertNull(ConvertUtils.toBooleanObject("FalXe")); assertNull(ConvertUtils.toBooleanObject("FalXE")); assertNull(ConvertUtils.toBooleanObject("FaLXe")); assertNull(ConvertUtils.toBooleanObject("FaLXE")); assertNull(ConvertUtils.toBooleanObject("FAlXe")); assertNull(ConvertUtils.toBooleanObject("FAlXE")); assertNull(ConvertUtils.toBooleanObject("FALXe")); assertNull(ConvertUtils.toBooleanObject("FALXE")); assertNull(ConvertUtils.toBooleanObject("falsX")); assertNull(ConvertUtils.toBooleanObject("falSX")); assertNull(ConvertUtils.toBooleanObject("faLsX")); assertNull(ConvertUtils.toBooleanObject("faLSX")); assertNull(ConvertUtils.toBooleanObject("fAlsX")); assertNull(ConvertUtils.toBooleanObject("fAlSX")); assertNull(ConvertUtils.toBooleanObject("fALsX")); assertNull(ConvertUtils.toBooleanObject("fALSX")); assertNull(ConvertUtils.toBooleanObject("FalsX")); assertNull(ConvertUtils.toBooleanObject("FalSX")); assertNull(ConvertUtils.toBooleanObject("FaLsX")); assertNull(ConvertUtils.toBooleanObject("FaLSX")); assertNull(ConvertUtils.toBooleanObject("FAlsX")); assertNull(ConvertUtils.toBooleanObject("FAlSX")); assertNull(ConvertUtils.toBooleanObject("FALsX")); assertNull(ConvertUtils.toBooleanObject("FALSX")); assertNull(ConvertUtils.toBooleanObject(null)); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/DateFormatUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; /** * DateFormatUtils test. * * @author zzq */ class DateFormatUtilsTest { @Test void testformat() { final Calendar c = Calendar.getInstance(TimeZone.getDefault()); c.set(2021, Calendar.JANUARY, 1, 12, 0, 0); c.setTimeZone(TimeZone.getDefault()); final StringBuilder buffer = new StringBuilder(); final int year = c.get(Calendar.YEAR); final int month = c.get(Calendar.MONTH) + 1; final int day = c.get(Calendar.DAY_OF_MONTH); final int hour = c.get(Calendar.HOUR_OF_DAY); buffer.append(year); buffer.append(month); buffer.append(day); buffer.append(hour); assertEquals(buffer.toString(), DateFormatUtils.format(c.getTime(), "yyyyMdH")); } @Test void testForNullPointerExceptionWithDate() { assertThrows(NullPointerException.class, () -> { DateFormatUtils.format(new Date(), null); }); } @Test void testForNullPointerExceptionWithPattern() { assertThrows(NullPointerException.class, () -> { DateFormatUtils.format(null, "yyyyMdH"); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ExceptionUtilTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; 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; class ExceptionUtilTest { NacosRuntimeException nacosRuntimeException; @BeforeEach void setUp() { RuntimeException caused = new RuntimeException("I'm caused exception."); nacosRuntimeException = new NacosRuntimeException(500, "Test", caused); } @Test void testGetAllExceptionMsg() { String msg = ExceptionUtil.getAllExceptionMsg(nacosRuntimeException); assertEquals("caused: errCode: 500, errMsg: Test ;caused: I'm caused exception.;", msg); } @Test void testGetCause() { assertEquals("I'm caused exception.", ExceptionUtil.getCause(nacosRuntimeException).getMessage()); NacosRuntimeException nreWithoutCaused = new NacosRuntimeException(500); assertEquals(nreWithoutCaused, ExceptionUtil.getCause(nreWithoutCaused)); } @Test void testGetStackTrace() { assertEquals("", ExceptionUtil.getStackTrace(null)); String stackTrace = ExceptionUtil.getStackTrace(nacosRuntimeException); assertTrue( stackTrace.contains("com.alibaba.nacos.api.exception.runtime.NacosRuntimeException: errCode: 500, errMsg: Test")); assertTrue(stackTrace.contains("at")); assertTrue(stackTrace.contains("Caused by: java.lang.RuntimeException: I'm caused exception.")); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/FuzzyGroupKeyPatternTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; /** * GroupKeyPatternUtilsTest. * * @author stone-98 * @date 2024/3/19 */ public class FuzzyGroupKeyPatternTest { @Test public void testGetGroupKeyPattern() { String dataIdPattern = "examplePattern*"; String group = "exampleGroup"; String namespace = "exampleNamespace"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPattern, group, namespace); assertEquals("exampleNamespace>>exampleGroup>>examplePattern*", groupKeyPattern); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/InetAddressValidatorTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * InetAddressValidator Test. * * @ClassName: InetAddressValidatorTest * @Author: ChenHao26 * @Date: 2022/8/22 13:07 */ class InetAddressValidatorTest { @Test void isIpv6Address() { assertTrue(InetAddressValidator.isIpv6Address("2000:0000:0000:0000:0001:2345:6789:abcd")); assertTrue(InetAddressValidator.isIpv6Address("2001:DB8:0:0:8:800:200C:417A")); assertTrue(InetAddressValidator.isIpv6Address("2001:DB8::8:800:200C:417A")); assertFalse(InetAddressValidator.isIpv6Address("2001:DB8::8:800:200C141aA")); } @Test void isIpv6MixedAddress() { assertTrue(InetAddressValidator.isIpv6MixedAddress("1:0:0:0:0:0:172.12.55.18")); assertTrue(InetAddressValidator.isIpv6MixedAddress("::172.12.55.18")); assertFalse(InetAddressValidator.isIpv6MixedAddress("2001:DB8::8:800:200C141aA")); } @Test void isIpv6Ipv4MappedAddress() { assertFalse(InetAddressValidator.isIpv6Ipv4MappedAddress(":ffff:1.1.1.1")); assertTrue(InetAddressValidator.isIpv6Ipv4MappedAddress("::FFFF:192.168.1.2")); } @Test void isIpv4Address() { assertTrue(InetAddressValidator.isIpv4Address("192.168.1.2")); } @Test void isLinkLocalIpv6WithZoneIndex() { assertTrue(InetAddressValidator.isLinkLocalIpv6WithZoneIndex("fe80::1%lo0")); assertFalse(InetAddressValidator.isLinkLocalIpv6WithZoneIndex("2000:0000:0000:0000:0001:2345:6789:abcd")); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/InternetAddressUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; 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; import static org.junit.jupiter.api.Assertions.fail; /** * test IpUtil. * * @ClassName: IpUtilTest * @date 2020/9/3 10:31 */ @SuppressWarnings("checkstyle:AbbreviationAsWordInName") public class InternetAddressUtilTest { /** * checkSplitIpPortStr. 2020/9/4 14:12 * * @param addr addr * @param isEx isEx * @param equalsStrs equalsStrs */ public static void checkSplitIpPortStr(String addr, boolean isEx, String... equalsStrs) { try { String[] array = InternetAddressUtil.splitIpPortStr(addr); assertEquals(array.length, equalsStrs.length); for (int i = 0; i < array.length; i++) { assertEquals(array[i], equalsStrs[i]); } } catch (Exception ex) { if (!isEx) { // No exception is expected here, but an exception has occurred fail("Unexpected exception"); } } } @Test void testIsIpv4() { assertTrue(InternetAddressUtil.isIpv4("127.0.0.1")); assertFalse(InternetAddressUtil.isIpv4("[::1]")); assertFalse(InternetAddressUtil.isIpv4("asdfasf")); assertFalse(InternetAddressUtil.isIpv4("ffgertert")); assertFalse(InternetAddressUtil.isIpv4("127.100.19")); } @Test void testIsIpv6() { assertTrue(InternetAddressUtil.isIpv6("[::1]")); assertFalse(InternetAddressUtil.isIpv6("127.0.0.1")); assertFalse(InternetAddressUtil.isIpv6("er34234")); } @Test void testIsIp() { assertTrue(InternetAddressUtil.isIp("[::1]")); assertTrue(InternetAddressUtil.isIp("127.0.0.1")); assertFalse(InternetAddressUtil.isIp("er34234")); assertFalse(InternetAddressUtil.isIp("127.100.19")); } @Test void testGetIpFromString() { assertEquals("[::1]", InternetAddressUtil.getIpFromString("http://[::1]:666/xzdsfasdf/awerwef" + "?eewer=2&xxx=3")); assertEquals("[::1]", InternetAddressUtil.getIpFromString( "jdbc:mysql://[::1]:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("127.0.0.1", InternetAddressUtil.getIpFromString("http://127.0.0.1:666/xzdsfasdf/awerwef" + "?eewer=2&xxx=3")); assertEquals("127.0.0.1", InternetAddressUtil.getIpFromString( "jdbc:mysql://127.0.0.1:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("", InternetAddressUtil.getIpFromString("http://[::1:666")); assertEquals("", InternetAddressUtil.getIpFromString("http://[dddd]:666/xzdsfasdf/awerwef" + "?eewer=2&xxx=3")); assertEquals("", InternetAddressUtil.getIpFromString( "jdbc:mysql://[127.0.0.1]:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("", InternetAddressUtil.getIpFromString( "jdbc:mysql://666.288.333.444:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("", InternetAddressUtil.getIpFromString( "jdbc:mysql://292.168.1.1:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("", InternetAddressUtil.getIpFromString( "jdbc:mysql://29.168.1.288:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("", InternetAddressUtil.getIpFromString( "jdbc:mysql://29.168.288.28:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("", InternetAddressUtil.getIpFromString( "jdbc:mysql://29.288.28.28:3306/nacos_config_test?characterEncoding=utf8&connectTimeout=1000" + "&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC")); assertEquals("", InternetAddressUtil.getIpFromString("")); assertEquals("", InternetAddressUtil.getIpFromString(null)); } @Test void testSplitIpPort() { checkSplitIpPortStr("[::1]:88", false, "[::1]", "88"); checkSplitIpPortStr("[::1]", false, "[::1]"); checkSplitIpPortStr("127.0.0.1:88", false, "127.0.0.1", "88"); checkSplitIpPortStr("127.0.0.1", false, "127.0.0.1"); checkSplitIpPortStr("[2001:db8:0:0:1:0:0:1]:88", false, "[2001:db8:0:0:1:0:0:1]", "88"); checkSplitIpPortStr("[2001:0db8:0:0:1:0:0:1]:88", false, "[2001:0db8:0:0:1:0:0:1]", "88"); checkSplitIpPortStr("[2001:db8::1:0:0:1]:88", false, "[2001:db8::1:0:0:1]", "88"); checkSplitIpPortStr("[2001:db8::0:1:0:0:1]:88", false, "[2001:db8::0:1:0:0:1]", "88"); checkSplitIpPortStr("[2001:0db8::1:0:0:1]:88", false, "[2001:0db8::1:0:0:1]", "88"); checkSplitIpPortStr("[2001:db8:0:0:1::1]:88", false, "[2001:db8:0:0:1::1]", "88"); checkSplitIpPortStr("[2001:db8:0000:0:1::1]:88", false, "[2001:db8:0000:0:1::1]", "88"); checkSplitIpPortStr("[2001:DB8:0:0:1::1]:88", false, "[2001:DB8:0:0:1::1]", "88"); checkSplitIpPortStr("localhost:8848", false, "localhost", "8848"); checkSplitIpPortStr("[dead::beef]:88", false, "[dead::beef]", "88"); // illegal ip will get abnormal results checkSplitIpPortStr("::1:88", false, "", "", "1", "88"); checkSplitIpPortStr("[::1:88", false, "[", "", "1", "88"); checkSplitIpPortStr("[127.0.0.1]:88", false, "[127.0.0.1]", "88"); checkSplitIpPortStr("[dead:beef]:88", false, "[dead:beef]", "88"); checkSplitIpPortStr("[fe80::3ce6:7132:808e:707a%19]:88", false, "[fe80::3ce6:7132:808e:707a%19]", "88"); checkSplitIpPortStr("[fe80::3]e6]:88", false, "[fe80::3]", "6]:88"); checkSplitIpPortStr("", true); } @Test void testCheckIPs() { assertEquals("ok", InternetAddressUtil.checkIps("127.0.0.1")); assertEquals("ok", InternetAddressUtil.checkIps()); assertEquals("ok", InternetAddressUtil.checkIps()); assertEquals("ok", InternetAddressUtil.checkIps(null)); assertEquals("illegal ip: 127.100.19", InternetAddressUtil.checkIps("127.100.19", "127.0.0.1")); } @Test void testIsDomain() { assertTrue(InternetAddressUtil.isDomain("localhost")); assertTrue(InternetAddressUtil.isDomain("github.com")); assertTrue(InternetAddressUtil.isDomain("prefix.infix.suffix")); assertTrue(InternetAddressUtil.isDomain("p-hub.com")); assertFalse(InternetAddressUtil.isDomain("")); assertFalse(InternetAddressUtil.isDomain(null)); } @Test void testRemoveBrackets() { assertEquals("2001:DB8:0:0:1::1", InternetAddressUtil.removeBrackets("[2001:DB8:0:0:1::1]")); assertEquals("2077", InternetAddressUtil.removeBrackets("[2077[]]]")); assertEquals("", InternetAddressUtil.removeBrackets("")); assertEquals("", InternetAddressUtil.removeBrackets(null)); } @Test void testCheckOk() { assertTrue(InternetAddressUtil.checkOk("ok")); assertFalse(InternetAddressUtil.checkOk("ojbk")); } @Test void testContainsPort() { assertTrue(InternetAddressUtil.containsPort("127.0.0.1:80")); assertFalse(InternetAddressUtil.containsPort("127.0.0.1:80:80")); } @Test void testLocalHostIp() throws NoSuchFieldException, IllegalAccessException, InvocationTargetException, NoSuchMethodException { Field field = InternetAddressUtil.class.getField("PREFER_IPV6_ADDRESSES"); field.setAccessible(true); Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); getDeclaredFields0.setAccessible(true); Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); Field modifiersField = null; for (Field each : fields) { if ("modifiers".equals(each.getName())) { modifiersField = each; } } if (modifiersField != null) { modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); } field.set(null, false); assertEquals("127.0.0.1", InternetAddressUtil.localHostIp()); field.set(null, true); assertEquals("[::1]", InternetAddressUtil.localHostIp()); } @Test void testIpToInt() { assertEquals(2130706433, InternetAddressUtil.ipToInt("127.0.0.1")); assertEquals(-1062731775, InternetAddressUtil.ipToInt("192.168.0.1")); } @Test void testIllegalIpToInt() { assertThrows(IllegalArgumentException.class, () -> { InternetAddressUtil.ipToInt("127.0.0.256"); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/IoUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.apache.commons.io.Charsets; import org.junit.jupiter.api.Test; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.nio.charset.StandardCharsets; import java.security.AccessController; import java.security.PrivilegedAction; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * Unit test of IoUtils. * * @author karsonto */ class IoUtilsTest { @Test void testTryDecompressForNotGzip() throws Exception { byte[] testCase = "123".getBytes(Charsets.toCharset("UTF-8")); assertEquals(testCase, IoUtils.tryDecompress(testCase)); } @Test void testTryDecompressForGzip() throws Exception { byte[] testCase = IoUtils.tryCompress("123", "UTF-8"); assertEquals("123", new String(IoUtils.tryDecompress(testCase), StandardCharsets.UTF_8)); } @Test void testTryCompressWithEmptyString() { assertEquals(0, IoUtils.tryCompress("", "UTF-8").length); assertEquals(0, IoUtils.tryCompress(null, "UTF-8").length); } @Test void testWriteStringToFile() throws IOException { File file = null; try { file = File.createTempFile("test_writeStringToFile", ".txt"); IoUtils.writeStringToFile(file, "123", "UTF-8"); List actual = IoUtils.readLines(new FileReader(file)); assertEquals(1, actual.size()); assertEquals("123", actual.get(0)); } finally { if (null != file) { file.deleteOnExit(); } } } @Test void testToStringWithNull() throws IOException { assertEquals("", IoUtils.toString(null, "UTF-8")); } @Test void testToStringWithReader() throws IOException { String testCase = "123"; assertEquals(testCase, IoUtils.toString(new ByteArrayInputStream(testCase.getBytes(Charsets.toCharset("UTF-8"))), "UTF-8")); } @Test void testDeleteForNullFile() throws IOException { IoUtils.delete(null); } @Test void testDeleteSuccess() throws IOException { File file = null; try { file = File.createTempFile("test_deleteForFile", ".txt"); assertTrue(file.exists()); IoUtils.delete(file); assertFalse(file.exists()); } finally { if (null != file) { file.deleteOnExit(); } } } @Test void testDeleteFileFailure() throws IOException { assertThrows(IOException.class, () -> { File file = mock(File.class); when(file.exists()).thenReturn(true); when(file.delete()).thenReturn(false); IoUtils.delete(file); }); } @Test void testDeleteForDirectory() throws IOException { File file = null; try { String tmpDir = AccessController.doPrivileged((PrivilegedAction) () -> System.getProperty("java.io.tmpdir")); File tmpDirFile = new File(tmpDir, "IoUtilsTest"); tmpDirFile.mkdirs(); file = File.createTempFile("test_deleteForDirectory", ".txt", tmpDirFile); assertTrue(file.exists()); IoUtils.delete(file.getParentFile()); assertTrue(tmpDirFile.exists()); assertFalse(file.exists()); } finally { if (null != file) { file.getParentFile().deleteOnExit(); file.deleteOnExit(); } } } @Test void testCleanDirectoryForNonExistingDirectory() throws IOException { assertThrows(IllegalArgumentException.class, () -> { File nonexistentDir = new File("non_exist"); IoUtils.cleanDirectory(nonexistentDir); }); } @Test void testCleanDirectoryForFile() throws IOException { assertThrows(IllegalArgumentException.class, () -> { File mockFile = mock(File.class); when(mockFile.exists()).thenReturn(true); IoUtils.cleanDirectory(mockFile); }); } @Test void testCleanDirectoryWithEmptyDirectory() throws IOException { assertThrows(IOException.class, () -> { File mockFile = mock(File.class); when(mockFile.exists()).thenReturn(true); when(mockFile.isDirectory()).thenReturn(true); IoUtils.cleanDirectory(mockFile); }); } @Test void testCleanDirectory() throws IOException { assertThrows(IOException.class, () -> { File mockFile = mock(File.class); when(mockFile.exists()).thenReturn(true); when(mockFile.isDirectory()).thenReturn(true); File mockSubFile = mock(File.class); when(mockSubFile.exists()).thenReturn(true); when(mockFile.listFiles()).thenReturn(new File[] {mockSubFile}); IoUtils.cleanDirectory(mockFile); }); } @Test void testIsGzipStreamWithNull() { assertFalse(IoUtils.isGzipStream(null)); } @Test void testIsGzipStreamWithEmpty() { assertFalse(IoUtils.isGzipStream(new byte[0])); } @Test void testCloseQuietly() throws IOException { BufferedReader br = new BufferedReader( new InputStreamReader(new ByteArrayInputStream("111".getBytes(Charsets.toCharset("UTF-8"))))); assertEquals("111", br.readLine()); IoUtils.closeQuietly(br); try { br.readLine(); } catch (IOException e) { assertNotNull(e); return; } fail(); } @Test void testCloseQuietly2() throws IOException { BufferedReader br = new BufferedReader( new InputStreamReader(new ByteArrayInputStream("123".getBytes(Charsets.toCharset("UTF-8"))))); assertEquals("123", br.readLine()); BufferedReader br2 = new BufferedReader( new InputStreamReader(new ByteArrayInputStream("456".getBytes(Charsets.toCharset("UTF-8"))))); assertEquals("456", br2.readLine()); IoUtils.closeQuietly(br, br2); try { br.readLine(); } catch (IOException e) { assertNotNull(e); } try { br2.readLine(); } catch (IOException e) { assertNotNull(e); return; } fail(); } @Test void testCloseQuietlyForHttpConnection() throws IOException { HttpURLConnection conn = mock(HttpURLConnection.class); InputStream inputStream = mock(InputStream.class); when(conn.getInputStream()).thenReturn(inputStream); IoUtils.closeQuietly(conn); verify(inputStream).close(); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/JacksonUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.api.exception.runtime.NacosSerializationException; import com.alibaba.nacos.common.model.RestResult; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyOrder; import com.fasterxml.jackson.annotation.JsonSubTypes; import com.fasterxml.jackson.annotation.JsonTypeInfo; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.Test; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.math.BigDecimal; import java.nio.charset.Charset; import java.util.Collections; import java.util.Date; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; 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.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class JacksonUtilsTest { @Test void testToJson1() { assertEquals("null", JacksonUtils.toJson(null)); assertEquals("\"string\"", JacksonUtils.toJson("string")); assertEquals("30", JacksonUtils.toJson(new BigDecimal(30))); assertEquals("{\"key\":\"value\"}", JacksonUtils.toJson(Collections.singletonMap("key", "value"))); assertEquals("[{\"key\":\"value\"}]", JacksonUtils.toJson(Collections.singletonList(Collections.singletonMap("key", "value")))); assertEquals("{\"aLong\":0,\"aInteger\":1,\"aBoolean\":false}", JacksonUtils.toJson(new TestOfAtomicObject())); assertEquals("{\"date\":1626192000000}", JacksonUtils.toJson(new TestOfDate())); // only public assertEquals("{\"publicAccessModifier\":\"public\"}", JacksonUtils.toJson(new TestOfAccessModifier())); // getter is also recognized assertEquals("{\"value\":\"value\",\"key\":\"key\"}", JacksonUtils.toJson(new TestOfGetter())); // annotation available assertEquals( "{\"@type\":\"JacksonUtilsTest$TestOfAnnotationSub\",\"date\":\"2021-07-14\",\"subField\":\"subField\"," + "\"camelCase\":\"value\"}", JacksonUtils.toJson(new TestOfAnnotationSub())); } @Test void testToJson2() { assertThrows(NacosSerializationException.class, () -> { // object without field will throw exceptions JacksonUtils.toJson(new Object()); }); } @Test void testToJsonBytes1() { assertArrayEquals("null".getBytes(), JacksonUtils.toJsonBytes(null)); assertArrayEquals("\"string\"".getBytes(), JacksonUtils.toJsonBytes("string")); assertArrayEquals("30".getBytes(), JacksonUtils.toJsonBytes(new BigDecimal(30))); assertArrayEquals("{\"key\":\"value\"}".getBytes(), JacksonUtils.toJsonBytes(Collections.singletonMap("key", "value"))); assertArrayEquals("[{\"key\":\"value\"}]".getBytes(), JacksonUtils.toJsonBytes(Collections.singletonList(Collections.singletonMap("key", "value")))); assertArrayEquals("{\"aLong\":0,\"aInteger\":1,\"aBoolean\":false}".getBytes(), JacksonUtils.toJsonBytes(new TestOfAtomicObject())); assertArrayEquals("{\"date\":1626192000000}".getBytes(), JacksonUtils.toJsonBytes(new TestOfDate())); // only public assertArrayEquals("{\"publicAccessModifier\":\"public\"}".getBytes(), JacksonUtils.toJsonBytes(new TestOfAccessModifier())); // getter is also recognized assertArrayEquals("{\"value\":\"value\",\"key\":\"key\"}".getBytes(), JacksonUtils.toJsonBytes(new TestOfGetter())); // annotation available assertArrayEquals( ("{\"@type\":\"JacksonUtilsTest$TestOfAnnotationSub\",\"date\":\"2021-07-14\",\"subField\":\"subField\"," + "\"camelCase\":\"value\"}").getBytes(), JacksonUtils.toJsonBytes(new TestOfAnnotationSub())); } @Test void testToJsonBytes2() { assertThrows(NacosSerializationException.class, () -> { // object without field will throw exceptions JacksonUtils.toJsonBytes(new Object()); }); } /** * JacksonUtils.toObj(byte[], Class) */ @Test void testToObject1() { assertNull(JacksonUtils.toObj("null".getBytes(), Object.class)); assertEquals("string", JacksonUtils.toObj("\"string\"".getBytes(), String.class)); assertEquals(new BigDecimal(30), JacksonUtils.toObj("30".getBytes(), BigDecimal.class)); assertEquals(Collections.singletonMap("key", "value"), JacksonUtils.toObj("{\"key\":\"value\"}".getBytes(), Map.class)); assertEquals(Collections.singletonList(Collections.singletonMap("key", "value")), JacksonUtils.toObj("[{\"key\":\"value\"}]".getBytes(), List.class)); assertEquals(new TestOfAtomicObject(), JacksonUtils.toObj("{\"aLong\":0,\"aInteger\":1,\"aBoolean\":false}".getBytes(), TestOfAtomicObject.class)); assertEquals(new TestOfDate(), JacksonUtils.toObj("{\"date\":1626192000000}".getBytes(), TestOfDate.class)); assertEquals(new TestOfAccessModifier(), JacksonUtils.toObj("{\"publicAccessModifier\":\"public\"}".getBytes(), TestOfAccessModifier.class)); assertEquals(new TestOfGetter(), JacksonUtils.toObj("{\"value\":\"value\",\"key\":\"key\"}".getBytes(), TestOfGetter.class)); assertEquals(new TestOfAnnotationSub(), JacksonUtils.toObj( ("{\"@type\":\"JacksonUtilsTest$TestOfAnnotationSub\",\"date\":\"2021-07-14\"," + "\"subField\":\"subField\",\"camelCase\":\"value\"}").getBytes(), TestOfAnnotation.class)); } /** * JacksonUtils.toObj(byte[], Class) */ @Test void testToObject2() { assertThrows(Exception.class, () -> { JacksonUtils.toObj(("{not_A}Json:String}").getBytes(), TestOfAnnotationSub.class); }); } /** * JacksonUtils.toObj(byte[], Type) */ @Test void testToObject3() { assertEquals(Collections.singletonMap("key", "value"), JacksonUtils.toObj("{\"key\":\"value\"}".getBytes(), TypeUtils.parameterize(Map.class, String.class, String.class))); assertEquals(Collections.singletonList(Collections.singletonMap("key", "value")), JacksonUtils.toObj("[{\"key\":\"value\"}]".getBytes(), TypeUtils.parameterize(List.class, TypeUtils.parameterize(Map.class, String.class, String.class)))); } /** * JacksonUtils.toObj(byte[], Type) */ @Test void testToObject4() { assertThrows(Exception.class, () -> { JacksonUtils.toObj("{not_A}Json:String}".getBytes(), TypeUtils.parameterize(Map.class, String.class, String.class)); }); } /** * JacksonUtils.toObj(byte[], Type) */ @Test void testToObject5() { assertThrows(Exception.class, () -> { JacksonUtils.toObj("{\"key\":\"value\"}".getBytes(), Object.class.getGenericSuperclass()); }); } /** * JacksonUtils.toObj(InputStream, Class) */ @Test void testToObject6() { assertNull(JacksonUtils.toObj(new ByteArrayInputStream("null".getBytes()), Object.class)); assertEquals("string", JacksonUtils.toObj(new ByteArrayInputStream("\"string\"".getBytes()), String.class)); assertEquals(new BigDecimal(30), JacksonUtils.toObj(new ByteArrayInputStream("30".getBytes()), BigDecimal.class)); assertEquals(Collections.singletonMap("key", "value"), JacksonUtils.toObj(new ByteArrayInputStream("{\"key\":\"value\"}".getBytes()), Map.class)); assertEquals(Collections.singletonList(Collections.singletonMap("key", "value")), JacksonUtils.toObj(new ByteArrayInputStream("[{\"key\":\"value\"}]".getBytes()), List.class)); assertEquals(new TestOfAtomicObject(), JacksonUtils.toObj(new ByteArrayInputStream("{\"aLong\":0,\"aInteger\":1,\"aBoolean\":false}".getBytes()), TestOfAtomicObject.class)); assertEquals(new TestOfDate(), JacksonUtils.toObj(new ByteArrayInputStream("{\"date\":1626192000000}".getBytes()), TestOfDate.class)); assertEquals(new TestOfAccessModifier(), JacksonUtils.toObj(new ByteArrayInputStream("{\"publicAccessModifier\":\"public\"}".getBytes()), TestOfAccessModifier.class)); assertEquals(new TestOfGetter(), JacksonUtils.toObj(new ByteArrayInputStream("{\"value\":\"value\",\"key\":\"key\"}".getBytes()), TestOfGetter.class)); assertEquals(new TestOfAnnotationSub(), JacksonUtils.toObj((new ByteArrayInputStream( ("{\"@type\":\"JacksonUtilsTest$TestOfAnnotationSub\"," + "\"date\":\"2021-07-14\",\"subField\":\"subField\",\"camelCase\":\"value\"}").getBytes())), TestOfAnnotation.class)); } /** * JacksonUtils.toObj(InputStream, Class) */ @Test void testToObject7() { assertThrows(Exception.class, () -> { JacksonUtils.toObj((ByteArrayInputStream) null, BigDecimal.class); }); } /** * JacksonUtils.toObj(InputStream, Class) */ @Test void testToObject8() { assertThrows(Exception.class, () -> { JacksonUtils.toObj(new ByteArrayInputStream("{not_A}Json:String}".getBytes()), Object.class); }); } /** * JacksonUtils.toObj(byte[], TypeReference) */ @Test void testToObject9() { assertNull(JacksonUtils.toObj("null".getBytes(), new TypeReference() { })); assertEquals("string", JacksonUtils.toObj("\"string\"".getBytes(), new TypeReference() { })); assertEquals(new BigDecimal(30), JacksonUtils.toObj("30".getBytes(), new TypeReference() { })); assertEquals(Collections.singletonMap("key", "value"), JacksonUtils.toObj("{\"key\":\"value\"}".getBytes(), new TypeReference>() { })); assertEquals(Collections.singletonList(Collections.singletonMap("key", "value")), JacksonUtils.toObj("[{\"key\":\"value\"}]".getBytes(), new TypeReference>>() { })); assertEquals(new TestOfAtomicObject(), JacksonUtils.toObj("{\"aLong\":0,\"aInteger\":1,\"aBoolean\":false}".getBytes(), new TypeReference() { })); assertEquals(new TestOfDate(), JacksonUtils.toObj("{\"date\":1626192000000}".getBytes(), new TypeReference() { })); assertEquals(new TestOfAccessModifier(), JacksonUtils.toObj("{\"publicAccessModifier\":\"public\"}".getBytes(), new TypeReference() { })); assertEquals(new TestOfGetter(), JacksonUtils.toObj("{\"value\":\"value\",\"key\":\"key\"}".getBytes(), new TypeReference() { })); assertEquals(new TestOfAnnotationSub(), JacksonUtils.toObj( ("{\"@type\":\"JacksonUtilsTest$TestOfAnnotationSub\",\"date\":\"2021-07-14\"," + "\"subField\":\"subField\",\"camelCase\":\"value\"}").getBytes(), new TypeReference() { })); } /** * JacksonUtils.toObj(byte[], TypeReference) */ @Test void testToObject10() { assertThrows(Exception.class, () -> { JacksonUtils.toObj("{not_A}Json:String}".getBytes(), new TypeReference() { }); }); } /** * JacksonUtils.toObj(InputStream, Type) */ @Test void testToObject11() { assertEquals(Collections.singletonMap("key", "value"), JacksonUtils.toObj(new ByteArrayInputStream("{\"key\":\"value\"}".getBytes()), TypeUtils.parameterize(Map.class, String.class, String.class))); assertEquals(Collections.singletonList(Collections.singletonMap("key", "value")), JacksonUtils.toObj(new ByteArrayInputStream("[{\"key\":\"value\"}]".getBytes()), TypeUtils.parameterize(List.class, TypeUtils.parameterize(Map.class, String.class, String.class)))); } /** * JacksonUtils.toObj(InputStream, Type) */ @Test void testToObject12() { assertThrows(Exception.class, () -> { JacksonUtils.toObj(new ByteArrayInputStream("{not_A}Json:String}".getBytes()), TypeUtils.parameterize(Map.class, String.class, String.class)); }); } /** * JacksonUtils.toObj(InputStream, Type) */ @Test void testToObject13() { assertThrows(Exception.class, () -> { JacksonUtils.toObj(new ByteArrayInputStream("{\"key\":\"value\"}".getBytes()), Object.class.getGenericSuperclass()); }); } /** * JacksonUtils.toObj(InputStream, Type) */ @Test void testToObject14() { assertThrows(Exception.class, () -> { JacksonUtils.toObj((InputStream) null, Object.class.getGenericSuperclass()); }); } /** * JacksonUtils.toObj(String) */ @Test void testToObject15() { assertEquals("null", JacksonUtils.toObj("null").asText()); assertEquals("string", JacksonUtils.toObj("\"string\"").asText()); assertEquals(30, JacksonUtils.toObj("30").asInt()); assertEquals("value", JacksonUtils.toObj("{\"key\":\"value\"}").get("key").asText()); assertEquals("value", JacksonUtils.toObj("[{\"key\":\"value\"}]").get(0).get("key").asText()); JsonNode jsonNode = JacksonUtils.toObj("{\"aLong\":0,\"aInteger\":1,\"aBoolean\":false}"); assertEquals(0L, jsonNode.get("aLong").asLong()); assertEquals(1, jsonNode.get("aInteger").asInt()); } /** * JacksonUtils.toObj(String) */ @Test void testToObject16() { assertThrows(Exception.class, () -> { JacksonUtils.toObj("{not_A}Json:String}"); }); } @Test void testRegisterSubtype() { JacksonUtils.registerSubtype(TestOfChild.class, "JacksonUtilsTest$TestOfChild"); assertEquals(new TestOfChild(), JacksonUtils.toObj( "{\"@type\":\"JacksonUtilsTest$TestOfChild\",\"parentField\":\"parentValue\"," + "\"childField\":\"childValue\"}", TestOfParent.class)); } @Test void testCreateEmptyJsonNode() { assertEquals("", JacksonUtils.createEmptyJsonNode().asText()); assertTrue(JacksonUtils.createEmptyJsonNode().isEmpty()); } @Test void testCreateEmptyArrayNode() { assertEquals("", JacksonUtils.createEmptyJsonNode().asText()); assertEquals(0, JacksonUtils.createEmptyArrayNode().size()); assertTrue(JacksonUtils.createEmptyArrayNode().isEmpty()); } @Test void testTransferToJsonNode() { JsonNode jsonNode1 = JacksonUtils.transferToJsonNode(Collections.singletonMap("key", "value")); assertEquals("value", jsonNode1.get("key").asText()); JsonNode jsonNode2 = JacksonUtils.transferToJsonNode(new TestOfAtomicObject()); assertEquals("0", jsonNode2.get("aLong").asText()); assertEquals("1", jsonNode2.get("aInteger").asText()); assertEquals("false", jsonNode2.get("aBoolean").asText()); } @Test void testConstructJavaType() { assertEquals("java.lang.String", JacksonUtils.constructJavaType(String.class).getRawClass().getName()); assertTrue(JacksonUtils.constructJavaType(String.class).isFinal()); } @Test void testToJsonBytes() { Map map = new LinkedHashMap(); map.put("string", "你好,中国!"); map.put("integer", 999); RestResult> restResult = new RestResult(); restResult.setData(map); byte[] bytes = JacksonUtils.toJsonBytes(restResult); String jsonFromBytes = ByteUtils.toString(bytes); assertTrue(jsonFromBytes.contains("\"code\":0")); assertTrue(jsonFromBytes.contains("\"data\":{\"string\":\"你好,中国!\",\"integer\":999}")); // old `toJsonBytes` method implementation: // public static byte[] toJsonBytes(Object obj) { // try { // return ByteUtils.toBytes(mapper.writeValueAsString(obj)); // } catch (JsonProcessingException e) { // throw new NacosSerializationException(obj.getClass(), e); // } // } // here is a verification to compare with the old implementation byte[] bytesFromOldImplementation = ByteUtils.toBytes(JacksonUtils.toJson(restResult)); String jsonFromBytesOldImplementation = new String(bytesFromOldImplementation, Charset.forName(Constants.ENCODE)); assertTrue(jsonFromBytesOldImplementation.contains("\"code\":0")); assertTrue(jsonFromBytesOldImplementation.contains("\"data\":{\"string\":\"你好,中国!\",\"integer\":999}")); } @Test void testToObjFromBytes() { String json = "{\"code\":0,\"data\":{\"string\":\"你好,中国!\",\"integer\":999}}"; RestResult> restResult = JacksonUtils.toObj(json, RestResult.class); assertEquals(0, restResult.getCode()); assertEquals("你好,中国!", restResult.getData().get("string")); assertEquals(999, restResult.getData().get("integer")); restResult = JacksonUtils.toObj(json, new TypeReference>>() { }); assertEquals(0, restResult.getCode()); assertEquals("你好,中国!", restResult.getData().get("string")); assertEquals(999, restResult.getData().get("integer")); } @Test void tesToObjForClassWithException() { assertThrows(NacosDeserializationException.class, () -> { JacksonUtils.toObj("aaa", JsonNode.class); }); } @Test void tesToObjForTypeWithException() { assertThrows(NacosDeserializationException.class, () -> { JacksonUtils.toObj("aaa", TypeUtils.parameterize(JsonNode.class)); }); } @Test void tesToObjForTypeTypeReferenceWithException() { assertThrows(NacosDeserializationException.class, () -> { JacksonUtils.toObj("aaa", new TypeReference() { }); }); } @JsonPropertyOrder({"aLong", "aInteger", "aBoolean"}) static class TestOfAtomicObject { public AtomicLong aLong = new AtomicLong(0); public AtomicInteger aInteger = new AtomicInteger(1); public AtomicBoolean aBoolean = new AtomicBoolean(false); @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TestOfAtomicObject that = (TestOfAtomicObject) o; if (aLong != null ? !(aLong.longValue() == that.aLong.longValue()) : that.aLong != null) { return false; } if (aInteger != null ? !(aInteger.intValue() == that.aInteger.intValue()) : that.aInteger != null) { return false; } return aBoolean != null ? aBoolean.get() == that.aBoolean.get() : that.aBoolean == null; } @Override public int hashCode() { int result = aLong != null ? aLong.hashCode() : 0; result = 31 * result + (aInteger != null ? aInteger.hashCode() : 0); result = 31 * result + (aBoolean != null ? aBoolean.hashCode() : 0); return result; } } static class TestOfAccessModifier { public String publicAccessModifier = "public"; protected String protectedAccessModifier = "protected"; String defaultAccessModifier = "default"; private String privateAccessModifier = "private"; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TestOfAccessModifier that = (TestOfAccessModifier) o; if (publicAccessModifier != null ? !publicAccessModifier.equals(that.publicAccessModifier) : that.publicAccessModifier != null) { return false; } if (protectedAccessModifier != null ? !protectedAccessModifier.equals(that.protectedAccessModifier) : that.protectedAccessModifier != null) { return false; } if (defaultAccessModifier != null ? !defaultAccessModifier.equals(that.defaultAccessModifier) : that.defaultAccessModifier != null) { return false; } return privateAccessModifier != null ? privateAccessModifier.equals(that.privateAccessModifier) : that.privateAccessModifier == null; } @Override public int hashCode() { int result = publicAccessModifier != null ? publicAccessModifier.hashCode() : 0; result = 31 * result + (protectedAccessModifier != null ? protectedAccessModifier.hashCode() : 0); result = 31 * result + (defaultAccessModifier != null ? defaultAccessModifier.hashCode() : 0); result = 31 * result + (privateAccessModifier != null ? privateAccessModifier.hashCode() : 0); return result; } } @JsonPropertyOrder({"value", "key"}) static class TestOfGetter { public String getKey() { return "key"; } public String getValue() { return "value"; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TestOfGetter that = (TestOfGetter) o; if (getKey() != null ? !getKey().equals(that.getKey()) : that.getKey() != null) { return false; } return getValue() != null ? getValue().equals(that.getValue()) : that.getValue() == null; } @Override public int hashCode() { int result = getKey() != null ? getKey().hashCode() : 0; result = 31 * result + (getValue() != null ? getValue().hashCode() : 0); return result; } } static class TestOfDate { public Date date = new Date(1626192000000L); @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TestOfDate that = (TestOfDate) o; return date != null ? date.equals(that.date) : that.date == null; } @Override public int hashCode() { return date != null ? date.hashCode() : 0; } } @JsonInclude(JsonInclude.Include.NON_NULL) @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) @JsonSubTypes({@JsonSubTypes.Type(TestOfAnnotationSub.class)}) static class TestOfAnnotation { @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8") public Date date = new Date(1626192000000L); @JsonProperty(value = "camelCase") public String underScore = "value"; @JsonIgnore public String ignore = "ignore"; public String nullString = null; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TestOfAnnotation that = (TestOfAnnotation) o; if (date != null ? !date.equals(that.date) : that.date != null) { return false; } if (underScore != null ? !underScore.equals(that.underScore) : that.underScore != null) { return false; } if (ignore != null ? !ignore.equals(that.ignore) : that.ignore != null) { return false; } return nullString != null ? nullString.equals(that.nullString) : that.nullString == null; } @Override public int hashCode() { int result = date != null ? date.hashCode() : 0; result = 31 * result + (underScore != null ? underScore.hashCode() : 0); result = 31 * result + (ignore != null ? ignore.hashCode() : 0); result = 31 * result + (nullString != null ? nullString.hashCode() : 0); return result; } } static class TestOfAnnotationSub extends TestOfAnnotation { public String subField = "subField"; @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; } TestOfAnnotationSub that = (TestOfAnnotationSub) o; return subField != null ? subField.equals(that.subField) : that.subField == null; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (subField != null ? subField.hashCode() : 0); return result; } } @JsonTypeInfo(use = JsonTypeInfo.Id.NAME) static class TestOfParent { public String parentField = "parentValue"; @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TestOfParent that = (TestOfParent) o; return parentField != null ? parentField.equals(that.parentField) : that.parentField == null; } @Override public int hashCode() { return parentField != null ? parentField.hashCode() : 0; } } static class TestOfChild extends TestOfParent { public String childField = "childValue"; @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; } TestOfChild that = (TestOfChild) o; return childField != null ? childField.equals(that.childField) : that.childField == null; } @Override public int hashCode() { int result = super.hashCode(); result = 31 * result + (childField != null ? childField.hashCode() : 0); return result; } } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/LoggerUtilsTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.slf4j.Logger; class LoggerUtilsTest { @Test void testPrintIfDebugEnabled() { Logger logger = Mockito.mock(Logger.class); Mockito.when(logger.isDebugEnabled()).thenReturn(true); LoggerUtils.printIfDebugEnabled(logger, "test", "arg1", "arg2", "arg3"); Mockito.verify(logger, Mockito.times(1)).debug("test", "arg1", "arg2", "arg3"); } @Test void testPrintIfInfoEnabled() { Logger logger = Mockito.mock(Logger.class); Mockito.when(logger.isInfoEnabled()).thenReturn(true); LoggerUtils.printIfInfoEnabled(logger, "test", "arg1", "arg2", "arg3"); Mockito.verify(logger, Mockito.times(1)).info("test", "arg1", "arg2", "arg3"); } @Test void testPrintIfTraceEnabled() { Logger logger = Mockito.mock(Logger.class); Mockito.when(logger.isTraceEnabled()).thenReturn(true); LoggerUtils.printIfTraceEnabled(logger, "test", "arg1", "arg2", "arg3"); Mockito.verify(logger, Mockito.times(1)).trace("test", "arg1", "arg2", "arg3"); } @Test void testPrintIfWarnEnabled() { Logger logger = Mockito.mock(Logger.class); Mockito.when(logger.isWarnEnabled()).thenReturn(true); LoggerUtils.printIfWarnEnabled(logger, "test", "arg1", "arg2", "arg3"); Mockito.verify(logger, Mockito.times(1)).warn("test", "arg1", "arg2", "arg3"); } @Test void testPrintIfErrorEnabled() { Logger logger = Mockito.mock(Logger.class); Mockito.when(logger.isErrorEnabled()).thenReturn(true); LoggerUtils.printIfErrorEnabled(logger, "test", "arg1", "arg2", "arg3"); Mockito.verify(logger, Mockito.times(1)).error("test", "arg1", "arg2", "arg3"); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/MD5UtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.common.Constants; import org.junit.jupiter.api.Test; import java.security.NoSuchAlgorithmException; import static org.junit.jupiter.api.Assertions.assertEquals; class MD5UtilsTest { @Test void testMd5Hex() throws NoSuchAlgorithmException { assertEquals("d41d8cd98f00b204e9800998ecf8427e", MD5Utils.md5Hex("", Constants.ENCODE)); assertEquals("acbd18db4cc2f85cedef654fccc4a4d8", MD5Utils.md5Hex("foo", Constants.ENCODE)); assertEquals("02f463eb799797e2a978fb1a2ae2991e", MD5Utils.md5Hex( "38c5ee9532f037a20b93d0f804cf111fca4003e451d09a692d9dea8032308d9c64eda9047fcd5e850284a49b1a0cfb2ecd45", Constants.ENCODE)); assertEquals("d41d8cd98f00b204e9800998ecf8427e", MD5Utils.md5Hex(new byte[0])); assertEquals("5289df737df57326fcdd22597afb1fac", MD5Utils.md5Hex(new byte[] {1, 2, 3})); } @Test void testEncodeHexString() { assertEquals("", MD5Utils.encodeHexString(new byte[0])); assertEquals("010203", MD5Utils.encodeHexString(new byte[] {1, 2, 3})); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/MapUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; 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.Mockito.when; class MapUtilTest { @Test void testIsEmptyAndNotEmptyMap() { Map map = null; assertTrue(MapUtil.isEmpty(map)); assertFalse(MapUtil.isNotEmpty(map)); map = new HashMap<>(); assertTrue(MapUtil.isEmpty(map)); assertFalse(MapUtil.isNotEmpty(map)); map.put("a", "b"); assertFalse(MapUtil.isEmpty(map)); assertTrue(MapUtil.isNotEmpty(map)); } @Test void testIsEmptyOrEmptyDictionary() { Dictionary dictionary = null; assertTrue(MapUtil.isEmpty(dictionary)); assertFalse(MapUtil.isNotEmpty(dictionary)); dictionary = new Hashtable<>(); assertTrue(MapUtil.isEmpty(dictionary)); assertFalse(MapUtil.isNotEmpty(dictionary)); dictionary.put("a", "b"); assertFalse(MapUtil.isEmpty(dictionary)); assertTrue(MapUtil.isNotEmpty(dictionary)); } @Test void testPutIfValNoNull() { Map map = new HashMap<>(); MapUtil.putIfValNoNull(map, "key-1", null); assertTrue(map.isEmpty()); MapUtil.putIfValNoNull(map, "key-1", "null"); assertTrue(map.containsKey("key-1")); } @Test void testPutIfValNoEmptyMap() { Map map = new HashMap<>(); MapUtil.putIfValNoEmpty(map, "key-str", null); assertFalse(map.containsKey("key-str")); MapUtil.putIfValNoEmpty(map, "key-str", ""); assertFalse(map.containsKey("key-str")); MapUtil.putIfValNoEmpty(map, "key-str", "1"); assertTrue(map.containsKey("key-str")); MapUtil.putIfValNoEmpty(map, "key-list", null); assertFalse(map.containsKey("key-list")); MapUtil.putIfValNoEmpty(map, "key-list", Collections.emptyList()); assertFalse(map.containsKey("key-list")); MapUtil.putIfValNoEmpty(map, "key-list", Collections.singletonList(1)); assertTrue(map.containsKey("key-list")); MapUtil.putIfValNoEmpty(map, "key-map", null); assertFalse(map.containsKey("key-map")); MapUtil.putIfValNoEmpty(map, "key-map", Collections.emptyMap()); assertFalse(map.containsKey("key-map")); Map map1 = new HashMap<>(); map1.put("1123", "123"); MapUtil.putIfValNoEmpty(map, "key-map", map1); assertTrue(map.containsKey("key-map")); Dictionary dictionary = Mockito.mock(Dictionary.class); when(dictionary.isEmpty()).thenReturn(true); MapUtil.putIfValNoEmpty(map, "key-dict", dictionary); assertFalse(map.containsKey("key-dict")); when(dictionary.isEmpty()).thenReturn(false); MapUtil.putIfValNoEmpty(map, "key-dict", dictionary); assertTrue(map.containsKey("key-dict")); } @Test void testComputeIfAbsent() { Map target = new HashMap<>(); String key = "key"; String param1 = "param1"; String param2 = "param2"; BiFunction mappingFunction = (p1, p2) -> p1 + p2; String result = MapUtil.computeIfAbsent(target, key, mappingFunction, param1, param2); assertEquals("param1param2", result); // Test that the mappingFunction is only called once AtomicInteger counter = new AtomicInteger(); mappingFunction = (p1, p2) -> { counter.incrementAndGet(); return p1 + p2; }; result = MapUtil.computeIfAbsent(target, key, mappingFunction, param1, param2); assertEquals("param1param2", result); assertEquals(0, counter.get()); } @Test void testRemoveKey() { Map map = new HashMap<>(); map.put("A", 1); map.put("B", 2); map.put("C", 3); MapUtil.removeKey(map, "B", integer -> integer == 1); assertEquals(3, map.size()); MapUtil.removeKey(map, "B", integer -> integer == 2); assertEquals(2, map.size()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/NamespaceUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import com.alibaba.nacos.api.common.Constants; import org.junit.jupiter.api.AfterEach; 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; /** * test NamespaceUtil. * * @author klw(213539 @ qq.com) * @date 2020/10/13 9:46 */ class NamespaceUtilTest { @AfterEach void tearDown() { NamespaceUtil.setNamespaceDefaultId(Constants.DEFAULT_NAMESPACE_ID); } @Test void testProcessTenantParameter() { String strPublic = "public"; assertEquals(strPublic, NamespaceUtil.processNamespaceParameter(strPublic)); String strEmpty = ""; String strNull = "null"; assertEquals(strNull, NamespaceUtil.processNamespaceParameter(strNull)); assertEquals(strPublic, NamespaceUtil.processNamespaceParameter(strEmpty)); assertEquals(strPublic, NamespaceUtil.processNamespaceParameter(null)); String strAbc = "abc"; assertEquals(strAbc, NamespaceUtil.processNamespaceParameter(strAbc)); String strdef123 = "def123"; assertEquals(strdef123, NamespaceUtil.processNamespaceParameter(strdef123)); String strAbcHasSpace = " abc "; assertEquals(strAbc, NamespaceUtil.processNamespaceParameter(strAbcHasSpace)); } @Test void testSetNamespaceDefaultId() { assertTrue(NamespaceUtil.isDefaultNamespaceId(Constants.DEFAULT_NAMESPACE_ID)); NamespaceUtil.setNamespaceDefaultId("Deprecated"); assertEquals("Deprecated", NamespaceUtil.getNamespaceDefaultId()); assertFalse(NamespaceUtil.isDefaultNamespaceId(Constants.DEFAULT_NAMESPACE_ID)); } @Test void testIsNeedTransferNamespace() { assertTrue(NamespaceUtil.isNeedTransferNamespace(null)); assertTrue(NamespaceUtil.isNeedTransferNamespace("")); assertFalse(NamespaceUtil.isNeedTransferNamespace("public")); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/NumberUtilsTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; 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; /** * Number utils. * * @author zzq */ class NumberUtilsTest { @Test void testToInt() { assertEquals(0, NumberUtils.toInt(null)); assertEquals(0, NumberUtils.toInt(StringUtils.EMPTY)); assertEquals(1, NumberUtils.toInt("1")); } @Test void testTestToInt() { assertEquals(1, NumberUtils.toInt(null, 1)); assertEquals(1, NumberUtils.toInt("", 1)); assertEquals(1, NumberUtils.toInt("1", 0)); } @Test void testToLong() { assertEquals(1L, NumberUtils.toLong(null, 1L)); assertEquals(1L, NumberUtils.toLong("", 1L)); assertEquals(1L, NumberUtils.toLong("1", 0L)); } @Test void testToDouble() { assertEquals(1.1d, NumberUtils.toDouble(null, 1.1d), 0); assertEquals(1.1d, NumberUtils.toDouble("", 1.1d), 0); assertEquals(1.5d, NumberUtils.toDouble("1.5", 0.0d), 0); } @Test void testIsDigits() { assertFalse(NumberUtils.isDigits(null)); assertFalse(NumberUtils.isDigits("")); assertTrue(NumberUtils.isDigits("12345")); assertFalse(NumberUtils.isDigits("1234.5")); assertFalse(NumberUtils.isDigits("1ab")); assertFalse(NumberUtils.isDigits("abc")); } @Test void testToFloatString() { assertEquals(NumberUtils.toFloat("-1.2345"), -1.2345f, 0); assertEquals(1.2345f, NumberUtils.toFloat("1.2345"), 0); assertEquals(0.0f, NumberUtils.toFloat("abc"), 0); assertEquals(NumberUtils.toFloat("-001.2345"), -1.2345f, 0); assertEquals(1.2345f, NumberUtils.toFloat("+001.2345"), 0); assertEquals(1.2345f, NumberUtils.toFloat("001.2345"), 0); assertEquals(0f, NumberUtils.toFloat("000.00"), 0); assertEquals(Float.MAX_VALUE, NumberUtils.toFloat(Float.MAX_VALUE + ""), 0); assertEquals(Float.MIN_VALUE, NumberUtils.toFloat(Float.MIN_VALUE + ""), 0); assertEquals(0.0f, NumberUtils.toFloat(""), 0); assertEquals(0.0f, NumberUtils.toFloat(null), 0); } @Test void testToFloatStringString() { assertEquals(1.2345f, NumberUtils.toFloat("1.2345", 5.1f), 0); assertEquals(5.0f, NumberUtils.toFloat("a", 5.0f), 0); // LANG-1060 assertEquals(5.0f, NumberUtils.toFloat("-001Z.2345", 5.0f), 0); assertEquals(5.0f, NumberUtils.toFloat("+001AB.2345", 5.0f), 0); assertEquals(5.0f, NumberUtils.toFloat("001Z.2345", 5.0f), 0); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ObservableTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; 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.junit.jupiter.MockitoExtension; 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.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class ObservableTest { @Mock private Observer observer; private Observable observable; @BeforeEach void setUp() throws Exception { observable = new Observable(); } @AfterEach void tearDown() throws Exception { } @Test void testAddObserver() { observable.addObserver(observer); assertEquals(1, observable.countObservers()); verify(observer).update(observable); } @Test void testDeleteObserver() { observable.addObserver(observer); assertEquals(1, observable.countObservers()); observable.deleteObserver(observer); assertEquals(0, observable.countObservers()); } @Test void testNotifyObservers() { observable.addObserver(observer); reset(observer); observable.notifyObservers(); assertFalse(observable.hasChanged()); verify(observer, never()).update(observable); observable.setChanged(); assertTrue(observable.hasChanged()); observable.notifyObservers(); verify(observer).update(observable); assertFalse(observable.hasChanged()); } @Test void testDeleteObservers() { observable.addObserver(observer); observable.deleteObservers(); assertEquals(1, observable.countObservers()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/PairTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class PairTest { @Test void testPair() { Pair pair = Pair.with("a", "b"); assertEquals("a", pair.getFirst()); assertEquals("b", pair.getSecond()); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/PreconditionsTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertThrows; /** * {@code Preconditions} unit test. * * @author zzq * @date 2021/7/29 */ final class PreconditionsTest { private static final String FORMAT = "I ate %s pies."; private static final String ARG = "one"; private static final String ERRORMSG = "A message"; @Test void testCheckArgument2Args1true() { Preconditions.checkArgument(true, ERRORMSG); } @Test void testCheckArgument2Args1false() { assertThrows(IllegalArgumentException.class, () -> { Preconditions.checkArgument(false, ERRORMSG); }); } @Test void testCheckArgument2Args1true2null() { assertThrows(IllegalArgumentException.class, () -> { Preconditions.checkArgument(true, null); }); } @Test void testCheckArgument2Args1false2null() { assertThrows(IllegalArgumentException.class, () -> { Preconditions.checkArgument(false, null); }); } @Test void testCheckArgument3Args1true() { Preconditions.checkArgument(true, ERRORMSG, ARG); } @Test void testCheckArgument3Args1false() { assertThrows(IllegalArgumentException.class, () -> { Preconditions.checkArgument(false, ERRORMSG, ARG); }); } @Test void testCheckArgument3Args1true2null() { assertThrows(IllegalArgumentException.class, () -> { Preconditions.checkArgument(true, null, ARG); }); } @Test void testCheckArgument3Args1false2null() { assertThrows(IllegalArgumentException.class, () -> { Preconditions.checkArgument(false, null, ARG); }); } @Test void testCheckArgument3Args1false3null() { assertThrows(IllegalArgumentException.class, () -> { Preconditions.checkArgument(false, ERRORMSG, null); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/PropertyUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; /** * PropertyUtils Test. * * @ClassName: PropertyUtilsTest * @Author: ChenHao26 * @Date: 2022/8/22 13:28 */ class PropertyUtilsTest { @Test void getProperty() { System.setProperty("nacos.test", "google"); String property = PropertyUtils.getProperty("nacos.test", "xx"); assertEquals("google", property); } @Test void getPropertyWithDefaultValue() { String property = PropertyUtils.getProperty("nacos.test", "xx", "test001"); assertEquals("test001", property); } @Test void getProcessorsCount() { int processorsCount = PropertyUtils.getProcessorsCount(); assertNotNull(processorsCount); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/RandomUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * test RandomUtils. * * @author zzq */ class RandomUtilsTest { @Test void testNextLong() { final long result = RandomUtils.nextLong(1L, 199L); assertTrue(result >= 1L && result < 199L); } @Test void testNextLongWithSame() { final long result = RandomUtils.nextLong(1L, 1L); assertEquals(1L, result); } @Test void testNextLongWithIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> { RandomUtils.nextLong(999L, 199L); }); } @Test void testNextLongWithIllegalArgumentException2() { assertThrows(IllegalArgumentException.class, () -> { RandomUtils.nextLong(-10L, 199L); }); } @Test void testNextInt() { final int result = RandomUtils.nextInt(1, 199); assertTrue(result >= 1 && result < 199); } @Test void testNextIntWithSame() { final int result = RandomUtils.nextInt(1, 1); assertEquals(1, result); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ReflectUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.UndeclaredThrowableException; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * ReflectUtils unit test. * * @author karsonto * @date 2022/08/19 */ class ReflectUtilsTest { List listStr; @BeforeEach void before() { listStr = new ArrayList<>(2); } /* * Pay attention to JDK 17 changes. Need to add vm options below: * --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED * --add-opens java.base/java.nio=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED */ @Test void testGetFieldValue() { Object elementData = ReflectUtils.getFieldValue(listStr, "elementData"); assertTrue(elementData instanceof Object[]); assertEquals(2, ((Object[]) elementData).length); } @Test void testGetFieldValueWithoutField() { assertThrows(RuntimeException.class, () -> { ReflectUtils.getFieldValue(listStr, "elementDataxx"); }); } @Test void testGetFieldValueWithDefault() { Object elementData = ReflectUtils.getFieldValue(listStr, "elementDataxx", 3); assertEquals(3, elementData); elementData = ReflectUtils.getFieldValue(listStr, "elementData", 3); assertTrue(elementData instanceof Object[]); assertEquals(2, ((Object[]) elementData).length); } @Test void testGetField() throws NoSuchFieldException { Field field = listStr.getClass().getDeclaredField("elementData"); field.setAccessible(true); Object elementData = ReflectUtils.getField(field, listStr); assertTrue(elementData instanceof Object[]); } @Test void testGetFieldWithoutAccess() throws NoSuchFieldException { assertThrows(IllegalStateException.class, () -> { Field field = listStr.getClass().getDeclaredField("elementData"); ReflectUtils.getField(field, listStr); }); } @Test void testInvokeMethod() throws Exception { Method method = listStr.getClass().getDeclaredMethod("grow", int.class); method.setAccessible(true); ReflectUtils.invokeMethod(method, listStr, 4); Object elementData = ReflectUtils.getFieldValue(listStr, "elementData"); assertEquals(4, ((Object[]) elementData).length); } @Test void testInvokeMethodWithoutAccess() throws Exception { assertThrows(IllegalStateException.class, () -> { Method method = listStr.getClass().getDeclaredMethod("grow", int.class); ReflectUtils.invokeMethod(method, listStr, 4); }); } @Test void testHandleReflectionException() { assertThrows(UndeclaredThrowableException.class, () -> { try { NoSuchMethodException exception = new NoSuchMethodException("test"); ReflectUtils.handleReflectionException(exception); } catch (Exception e) { assertEquals("Method not found: test", e.getMessage()); } try { IllegalAccessException exception = new IllegalAccessException("test"); ReflectUtils.handleReflectionException(exception); } catch (Exception e) { assertEquals("Could not access method or field: test", e.getMessage()); } RuntimeException exception = new RuntimeException("test"); try { ReflectUtils.handleReflectionException(exception); } catch (Exception e) { assertEquals(exception, e); } try { InvocationTargetException invocationTargetException = new InvocationTargetException(exception); ReflectUtils.handleReflectionException(invocationTargetException); } catch (Exception e) { assertEquals(exception, e); } ReflectUtils.handleReflectionException(new IOException()); }); } @Test void testRethrowRuntimeException() { assertThrows(UndeclaredThrowableException.class, () -> { ClassFormatError error = new ClassFormatError("test"); try { ReflectUtils.rethrowRuntimeException(error); } catch (Error e) { assertEquals(error, e); } ReflectUtils.rethrowRuntimeException(new IOException()); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ResourceUtilsTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.net.URL; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; class ResourceUtilsTest { @Test void testGetResourceUrlForClasspath() throws IOException { URL url = ResourceUtils.getResourceUrl("classpath:test-tls-cert.pem"); assertNotNull(url); } @Test void testGetResourceUrlForClasspathNotExists() throws IOException { assertThrows(FileNotFoundException.class, () -> { ResourceUtils.getResourceUrl("classpath:non-exist.pem"); }); } @Test void testGetResourceUrlForFile() throws IOException { File file = File.createTempFile("test", ".txt"); try { URL url = ResourceUtils.getResourceUrl("file://" + file.getPath()); assertNotNull(url); } finally { file.deleteOnExit(); } } @Test void testGetResourceUrlForFileWithoutProtocol() throws IOException { File file = File.createTempFile("test", ".txt"); try { URL url = ResourceUtils.getResourceUrl(file.getPath()); assertNotNull(url); } finally { file.deleteOnExit(); } } @Test void testGetResourceUrlFromLoader() throws IOException { URL url = ResourceUtils.getResourceUrl(this.getClass().getClassLoader(), "test-tls-cert.pem"); assertNotNull(url); } @Test void testGetResourceUrlFromSystemLoader() throws IOException { URL url = ResourceUtils.getResourceUrl(null, "test-tls-cert.pem"); assertNotNull(url); } @Test void testGetResourceUrlFromLoaderWithoutExist() throws IOException { assertThrows(IOException.class, () -> { URL url = ResourceUtils.getResourceUrl(null, "non-exist"); assertNotNull(url); }); } @Test void testGetResourceAsStreamForClasspath() throws IOException { try (InputStream inputStream = ResourceUtils.getResourceAsStream("test-tls-cert.pem")) { assertNotNull(inputStream); } } @Test void testGetResourceAsStreamForClasspathFromSystem() throws IOException { try (InputStream inputStream = ResourceUtils.getResourceAsStream(null, "test-tls-cert.pem")) { assertNotNull(inputStream); } } @Test void testGetResourceAsStreamForClasspathWithoutExist() throws IOException { assertThrows(IOException.class, () -> { URL url = ResourceUtils.getResourceUrl("non-exist"); ResourceUtils.getResourceAsStream(null, url.toString()); }); } @Test void testGetResourceAsPropertiesForClasspath() throws IOException { Properties properties = ResourceUtils.getResourceAsProperties("resource_utils_test.properties"); assertNotNull(properties); assertTrue(properties.containsKey("a")); } @Test void testGetResourceAsReader() throws IOException { try (Reader reader = ResourceUtils.getResourceAsReader("resource_utils_test.properties", "UTF-8")) { assertNotNull(reader); } } @Test void testGetResourceAsReaderWithLoader() throws IOException { try (Reader reader = ResourceUtils.getResourceAsReader(ResourceUtilsTest.class.getClassLoader(), "resource_utils_test.properties", "UTF-8")) { assertNotNull(reader); } } @Test void testGetResourceAsFile() throws IOException { File file = ResourceUtils.getResourceAsFile("classpath:resource_utils_test.properties"); assertNotNull(file); } @Test void testGetResourceAsFileByUrl() throws IOException { File file = ResourceUtils.getResourceAsFile(ResourceUtils.getResourceUrl("classpath:resource_utils_test.properties")); assertNotNull(file); } @Test void testGetResourceAsFileByLoader() throws IOException { File file = ResourceUtils.getResourceAsFile(ResourceUtils.class.getClassLoader(), "resource_utils_test.properties"); assertNotNull(file); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/StringUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.Arrays; 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; /** * String utils. * * @author zzq */ class StringUtilsTest { @Test void testNewStringForUtf8() { String abc = "abc"; byte[] abcByte = abc.getBytes(); assertEquals(abc, StringUtils.newStringForUtf8(abcByte)); } @Test void isBlank() { assertTrue(StringUtils.isBlank(null)); assertTrue(StringUtils.isBlank("")); assertTrue(StringUtils.isBlank(" ")); assertFalse(StringUtils.isBlank("bob")); assertFalse(StringUtils.isBlank(" bob ")); } @Test void testIsNotBlank() { assertFalse(StringUtils.isNotBlank(null)); assertFalse(StringUtils.isNotBlank("")); assertFalse(StringUtils.isNotBlank(" ")); assertTrue(StringUtils.isNotBlank("bob")); assertTrue(StringUtils.isNotBlank(" bob ")); } @Test void testIsNotEmpty() { assertFalse(StringUtils.isNotEmpty(null)); assertFalse(StringUtils.isNotEmpty("")); assertTrue(StringUtils.isNotEmpty(" ")); assertTrue(StringUtils.isNotEmpty("bob")); assertTrue(StringUtils.isNotEmpty(" bob ")); } @Test void testIsEmpty() { assertTrue(StringUtils.isEmpty(null)); assertTrue(StringUtils.isEmpty("")); assertFalse(StringUtils.isEmpty(" ")); assertFalse(StringUtils.isEmpty("bob")); assertFalse(StringUtils.isEmpty(" bob ")); } @Test void testDefaultIfEmpty() { assertEquals("NULL", StringUtils.defaultIfEmpty(null, "NULL")); assertEquals("NULL", StringUtils.defaultIfEmpty("", "NULL")); assertEquals(" ", StringUtils.defaultIfEmpty(" ", "NULL")); assertEquals("bat", StringUtils.defaultIfEmpty("bat", "NULL")); assertNull(StringUtils.defaultIfEmpty("", null)); } @Test void testDefaultIfBlank() { assertEquals("NULL", StringUtils.defaultIfBlank(null, "NULL")); assertEquals("NULL", StringUtils.defaultIfBlank("", "NULL")); assertEquals("NULL", StringUtils.defaultIfBlank(" ", "NULL")); assertEquals("bat", StringUtils.defaultIfBlank("bat", "NULL")); assertNull(StringUtils.defaultIfBlank("", null)); } @Test void testDefaultEmptyIfBlank() { assertEquals("", StringUtils.defaultEmptyIfBlank(null)); assertEquals("", StringUtils.defaultEmptyIfBlank("")); assertEquals("", StringUtils.defaultEmptyIfBlank(" ")); assertEquals("bat", StringUtils.defaultEmptyIfBlank("bat")); } @Test void testEquals() { assertTrue(StringUtils.equals(null, null)); assertFalse(StringUtils.equals(null, "abc")); assertFalse(StringUtils.equals("abc", null)); assertTrue(StringUtils.equals("abc", "abc")); assertFalse(StringUtils.equals("abc", "ABC")); } @Test void trim() { assertNull(StringUtils.trim(null)); assertEquals(StringUtils.EMPTY, StringUtils.trim("")); assertEquals(StringUtils.EMPTY, StringUtils.trim(" ")); assertEquals("abc", StringUtils.trim("abc")); assertEquals("abc", StringUtils.trim(" abc ")); } @Test void testSubstringBetween() { assertNull(StringUtils.substringBetween(null, "a", "b")); assertNull(StringUtils.substringBetween("a", null, "b")); assertNull(StringUtils.substringBetween("a", "b", null)); assertNull(StringUtils.substringBetween(StringUtils.EMPTY, StringUtils.EMPTY, "]")); assertNull(StringUtils.substringBetween(StringUtils.EMPTY, "[", "]")); assertEquals(StringUtils.EMPTY, StringUtils.substringBetween("yabcz", StringUtils.EMPTY, StringUtils.EMPTY)); assertEquals(StringUtils.EMPTY, StringUtils.substringBetween(StringUtils.EMPTY, StringUtils.EMPTY, StringUtils.EMPTY)); assertEquals("b", StringUtils.substringBetween("wx[b]yz", "[", "]")); assertEquals("abc", StringUtils.substringBetween("yabcz", "y", "z")); assertEquals("abc", StringUtils.substringBetween("yabczyabcz", "y", "z")); } @Test void testJoin() { ArrayList objects = new ArrayList<>(); objects.add(null); assertNull(StringUtils.join(null, "a")); assertEquals(StringUtils.EMPTY, StringUtils.join(Arrays.asList(), "a")); assertEquals(StringUtils.EMPTY, StringUtils.join(objects, "a")); assertEquals("a;b;c", StringUtils.join(Arrays.asList("a", "b", "c"), ";")); assertEquals("abc", StringUtils.join(Arrays.asList("a", "b", "c"), null)); } @Test void testContainsIgnoreCase() { assertFalse(StringUtils.containsIgnoreCase(null, "1")); assertFalse(StringUtils.containsIgnoreCase("abc", null)); assertTrue(StringUtils.containsIgnoreCase(StringUtils.EMPTY, StringUtils.EMPTY)); assertTrue(StringUtils.containsIgnoreCase("abc", StringUtils.EMPTY)); assertTrue(StringUtils.containsIgnoreCase("abc", "a")); assertFalse(StringUtils.containsIgnoreCase("abc", "z")); assertTrue(StringUtils.containsIgnoreCase("abc", "A")); assertFalse(StringUtils.containsIgnoreCase("abc", "Z")); } @Test void testContains() { assertFalse(StringUtils.contains(null, "1")); assertFalse(StringUtils.contains("abc", null)); assertTrue(StringUtils.contains(StringUtils.EMPTY, StringUtils.EMPTY)); assertTrue(StringUtils.contains("abc", StringUtils.EMPTY)); assertTrue(StringUtils.contains("abc", "a")); assertFalse(StringUtils.contains("abc", "z")); assertFalse(StringUtils.contains("abc", "A")); assertFalse(StringUtils.contains("abc", "Z")); } @Test void testIsNoneBlank() { assertFalse(StringUtils.isNoneBlank(null)); assertFalse(StringUtils.isNoneBlank(null, "foo")); assertFalse(StringUtils.isNoneBlank(null, null)); assertFalse(StringUtils.isNoneBlank("", "bar")); assertFalse(StringUtils.isNoneBlank("bob", "")); assertFalse(StringUtils.isNoneBlank(" bob ", null)); assertFalse(StringUtils.isNoneBlank(" ", "bar")); assertTrue(StringUtils.isNoneBlank("foo", "bar")); } @Test void isAnyBlank() { assertTrue(StringUtils.isAnyBlank(null)); assertTrue(StringUtils.isAnyBlank(null, "foo")); assertTrue(StringUtils.isAnyBlank(null, null)); assertTrue(StringUtils.isAnyBlank("", "bar")); assertTrue(StringUtils.isAnyBlank("bob", "")); assertTrue(StringUtils.isAnyBlank(" bob ", null)); assertTrue(StringUtils.isAnyBlank(" ", "bar")); assertFalse(StringUtils.isAnyBlank("foo", "bar")); } @Test void testStartsWith() { assertTrue(StringUtils.startsWith(null, null)); assertFalse(StringUtils.startsWith(null, "abc")); assertFalse(StringUtils.startsWith("abcdef", null)); assertTrue(StringUtils.startsWith("abcdef", "abc")); assertFalse(StringUtils.startsWith("ABCDEF", "abc")); assertFalse(StringUtils.startsWith("ABC", "ABCDEF")); } @Test void testStartsWithIgnoreCase() { assertTrue(StringUtils.startsWithIgnoreCase(null, null)); assertFalse(StringUtils.startsWithIgnoreCase(null, "abc")); assertFalse(StringUtils.startsWithIgnoreCase("abcdef", null)); assertTrue(StringUtils.startsWithIgnoreCase("abcdef", "abc")); assertTrue(StringUtils.startsWithIgnoreCase("ABCDEF", "abc")); } @Test void testDeleteWhitespace() { assertNull(StringUtils.deleteWhitespace(null)); assertEquals(StringUtils.EMPTY, StringUtils.deleteWhitespace("")); assertEquals("abc", StringUtils.deleteWhitespace("abc")); assertEquals("abc", StringUtils.deleteWhitespace(" ab c ")); } @Test void testEqualsIgnoreCase() { assertTrue(StringUtils.equalsIgnoreCase(null, null)); assertFalse(StringUtils.equalsIgnoreCase(null, "abc")); assertFalse(StringUtils.equalsIgnoreCase("abc", null)); assertTrue(StringUtils.equalsIgnoreCase("abc", "abc")); assertTrue(StringUtils.equalsIgnoreCase("abc", "ABC")); } @Test void testSplit() { assertNull(StringUtils.split(null, ",")); assertArrayEquals(new String[0], StringUtils.split("", ",")); assertArrayEquals(new String[] {"ab", "cd", "ef"}, StringUtils.split("ab cd ef", null)); assertArrayEquals(new String[] {"ab", "cd", "ef"}, StringUtils.split("ab cd ef", null)); assertArrayEquals(new String[] {"ab", "cd", "ef"}, StringUtils.split("ab:cd:ef", ":")); } @Test void testTokenizeToStringArray() { // Test case 1: Empty string String str1 = ""; String delimiters1 = ","; boolean trimTokens1 = true; boolean ignoreEmptyTokens1 = false; String[] expected1 = new String[0]; String[] result1 = StringUtils.tokenizeToStringArray(str1, delimiters1, trimTokens1, ignoreEmptyTokens1); assertArrayEquals(expected1, result1); // Test case 2: Null string String str2 = null; String delimiters2 = " "; boolean trimTokens2 = false; boolean ignoreEmptyTokens2 = true; String[] expected2 = new String[0]; String[] result2 = StringUtils.tokenizeToStringArray(str2, delimiters2, trimTokens2, ignoreEmptyTokens2); assertArrayEquals(expected2, result2); // Test case 3: Single token String str3 = "Hello"; String delimiters3 = ","; boolean trimTokens3 = true; boolean ignoreEmptyTokens3 = false; String[] expected3 = {"Hello"}; String[] result3 = StringUtils.tokenizeToStringArray(str3, delimiters3, trimTokens3, ignoreEmptyTokens3); assertArrayEquals(expected3, result3); // Test case 4: Multiple tokens with trimming String str4 = " Hello, World, "; String delimiters4 = ","; boolean trimTokens4 = true; boolean ignoreEmptyTokens4 = false; String[] expected4 = {"Hello", "World", ""}; String[] result4 = StringUtils.tokenizeToStringArray(str4, delimiters4, trimTokens4, ignoreEmptyTokens4); assertArrayEquals(expected4, result4); // Test case 5: Multiple tokens with empty tokens ignored String str5 = " ,Hello, ,World, "; String delimiters5 = ","; boolean trimTokens5 = true; boolean ignoreEmptyTokens5 = true; String[] expected5 = {"Hello", "World"}; String[] result5 = StringUtils.tokenizeToStringArray(str5, delimiters5, trimTokens5, ignoreEmptyTokens5); assertArrayEquals(expected5, result5); } @Test void testHasText() { // Test case 1: Empty string assertFalse(StringUtils.hasText("")); // Test case 2: String with whitespace only assertFalse(StringUtils.hasText(" ")); // Test case 3: Null string assertFalse(StringUtils.hasText(null)); // Test case 4: String with non-whitespace characters assertTrue(StringUtils.hasText("hello")); // Test case 5: String with both text and whitespace assertTrue(StringUtils.hasText(" hello ")); } @Test void testCleanPath() { // Test case 1: path with no length String path1 = ""; String expected1 = ""; assertEquals(expected1, StringUtils.cleanPath(path1)); // Test case 2: normal path String path2 = "path/to/file"; String expected2 = "path/to/file"; assertEquals(expected2, StringUtils.cleanPath(path2)); // Test case 3: path with Windows folder separator String path3 = "path\\to\\文件"; String expected3 = "path/to/文件"; assertEquals(expected3, StringUtils.cleanPath(path3)); // Test case 4: path with dot String path4 = "path/.."; String expected4 = ""; assertEquals(expected4, StringUtils.cleanPath(path4)); // Test case 5: path with top path String path5 = "path/../top"; String expected5 = "top"; assertEquals(expected5, StringUtils.cleanPath(path5)); // Test case 6: path with multiple top path String path6 = "path/../../top"; String expected6 = "../top"; assertEquals(expected6, StringUtils.cleanPath(path6)); // Test case 7: path with leading colon String path7 = "file:../top"; String expected7 = "file:../top"; assertEquals(expected7, StringUtils.cleanPath(path7)); // Test case 8: path with leading slash String path8 = "file:/path/../file"; String expected8 = "file:/file"; assertEquals(expected8, StringUtils.cleanPath(path8)); // Test case 9: path with empty prefix String path9 = "file:path/../file"; String expected9 = "file:file"; assertEquals(expected9, StringUtils.cleanPath(path9)); // Test case 10: prefix contain separator String path10 = "file/:path/../file"; String expected10 = "file/file"; assertEquals(expected10, StringUtils.cleanPath(path10)); // Test case 11: dot in file name String path11 = "file:/path/to/file.txt"; String expected11 = "file:/path/to/file.txt"; assertEquals(expected11, StringUtils.cleanPath(path11)); // Test case 12: dot in path String path12 = "file:/path/./file.txt"; String expected12 = "file:/path/file.txt"; assertEquals(expected12, StringUtils.cleanPath(path12)); // Test case 13: path with dot and slash String path13 = "file:aaa/../"; String expected13 = "file:./"; assertEquals(expected13, StringUtils.cleanPath(path13)); } @Test void testDelimitedListToStringArrayWithNull() { assertEquals(0, StringUtils.delimitedListToStringArray(null, ",", "").length); assertEquals(1, StringUtils.delimitedListToStringArray("a,b", null, "").length); } @Test void testDelimitedListToStringArrayWithEmptyDelimiter() { String testCase = "a,b"; String[] actual = StringUtils.delimitedListToStringArray(testCase, "", ""); assertEquals(3, actual.length); assertEquals("a", actual[0]); assertEquals(",", actual[1]); assertEquals("b", actual[2]); } @Test void testDeleteAny() { // Test case 1: inString is empty, charsToDelete is empty String inString1 = ""; String charsToDelete1 = ""; assertEquals("", StringUtils.deleteAny(inString1, charsToDelete1)); // Test case 2: inString is empty, charsToDelete is not empty String inString2 = ""; String charsToDelete2 = "abc"; assertEquals("", StringUtils.deleteAny(inString2, charsToDelete2)); // Test case 3: inString is not empty, charsToDelete is empty String inString3 = "abc"; String charsToDelete3 = ""; assertEquals("abc", StringUtils.deleteAny(inString3, charsToDelete3)); // Test case 4: inString is not empty, charsToDelete is not empty String inString4 = "abc"; String charsToDelete4 = "a"; assertEquals("bc", StringUtils.deleteAny(inString4, charsToDelete4)); // Test case 5: inString contains special characters String inString5 = "abc\n"; String charsToDelete5 = "\n"; assertEquals("abc", StringUtils.deleteAny(inString5, charsToDelete5)); // Test case 6: inString not contains special characters String inString6 = "abc\n"; String charsToDelete6 = "d"; assertEquals("abc\n", StringUtils.deleteAny(inString6, charsToDelete6)); } @Test void testReplace() { // Test case 1: pattern is empty assertEquals("abc", StringUtils.replace("abc", "", "a")); // Test case 2: oldPattern less than newPattern assertEquals("aabc", StringUtils.replace("abc", "a", "aa")); // Test case 3: oldPattern more than newPattern assertEquals("dc", StringUtils.replace("abc", "ab", "d")); } @Test void testApplyRelativePath() { // Test case 1 String path1 = "/path/to/file"; String relativePath1 = "subfolder/subfile"; String expected1 = "/path/to/subfolder/subfile"; String result1 = StringUtils.applyRelativePath(path1, relativePath1); assertEquals(expected1, result1); // Test case 2 String path2 = "path/to/file"; String relativePath2 = "subfolder/subfile"; String expected2 = "path/to/subfolder/subfile"; String result2 = StringUtils.applyRelativePath(path2, relativePath2); assertEquals(expected2, result2); // Test case 3 String path3 = "/path/to/file"; String relativePath3 = "/subfolder/subfile"; String expected3 = "/path/to/subfolder/subfile"; String result3 = StringUtils.applyRelativePath(path3, relativePath3); assertEquals(expected3, result3); //Test case 4 String path4 = "file"; String relativePath4 = "/subfolder/subfile"; String expected4 = "/subfolder/subfile"; String result4 = StringUtils.applyRelativePath(path4, relativePath4); assertEquals(expected4, result4); } @Test void testGetFilename() { // Test case 1: null path String path1 = null; String result1 = StringUtils.getFilename(path1); assertNull(result1); // Test case 2: path without separator String path2 = "myFile.txt"; String expectedResult2 = "myFile.txt"; String result2 = StringUtils.getFilename(path2); assertEquals(expectedResult2, result2); // Test case 3: path with separator String path3 = "myPath/myFile.txt"; String expectedResult3 = "myFile.txt"; String result3 = StringUtils.getFilename(path3); assertEquals(expectedResult3, result3); // Test case 4: path with multiple separators String path4 = "myPath/subPath/myFile.txt"; String expectedResult4 = "myFile.txt"; String result4 = StringUtils.getFilename(path4); assertEquals(expectedResult4, result4); } @Test void testCapitalize() { // Test for an empty string String str1 = ""; assertEquals("", StringUtils.capitalize(str1)); // Test for a single word string String str2 = "hello"; assertEquals("Hello", StringUtils.capitalize(str2)); // Test for a multiple word string String str3 = "hello world"; assertEquals("Hello world", StringUtils.capitalize(str3)); // Test for a string with special characters String str4 = "!@#$%^&*()"; assertEquals("!@#$%^&*()", StringUtils.capitalize(str4)); // Test for a string with numbers String str5 = "abc123"; assertEquals("Abc123", StringUtils.capitalize(str5)); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ThreadFactoryBuilderTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; /** * thread factory builder unit test. * * @author zzq * @date 2021/8/3 */ class ThreadFactoryBuilderTest { int priority = 2; @Test void simpleTest() { ThreadFactory threadFactory = new ThreadFactoryBuilder().daemon(true).priority(priority) .nameFormat("nacos-grpc-executor-%d").build(); Thread thread1 = threadFactory.newThread(() -> { }); assertEquals("nacos-grpc-executor-0", thread1.getName()); assertEquals(priority, thread1.getPriority()); assertTrue(thread1.isDaemon()); Thread thread2 = threadFactory.newThread(() -> { }); assertEquals("nacos-grpc-executor-1", thread2.getName()); assertEquals(priority, thread2.getPriority()); assertTrue(thread2.isDaemon()); } @Test void customizeFactoryTest() { String threadName = "hello is me!"; ThreadFactory myFactory = r -> { Thread thread = new Thread(); thread.setName(threadName); return thread; }; ThreadFactory factory = new ThreadFactoryBuilder().daemon(true).priority(priority).customizeFactory(myFactory).build(); Thread thread = factory.newThread(() -> { }); assertEquals(threadName, thread.getName()); } @Test void uncaughtExceptionHandlerTest() throws Exception { AtomicBoolean state = new AtomicBoolean(false); ThreadFactory threadFactory = new ThreadFactoryBuilder().daemon(true).priority(priority) .nameFormat("nacos-grpc-executor-%d").uncaughtExceptionHandler((t, e) -> state.set(true)).build(); threadFactory.newThread(() -> { throw new NullPointerException("null pointer"); }).start(); TimeUnit.SECONDS.sleep(1); assertTrue(state.get()); } @Test void propertyPriorityTest1() { assertThrows(IllegalArgumentException.class, () -> { new ThreadFactoryBuilder().priority(11).nameFormat("nacos-grpc-executor-%d").build(); }); } @Test void propertyPriorityTest2() { assertThrows(IllegalArgumentException.class, () -> { new ThreadFactoryBuilder().priority(-1).nameFormat("nacos-grpc-executor-%d").build(); }); } @Test void propertyNameFormatTest() { assertThrows(IllegalArgumentException.class, () -> { new ThreadFactoryBuilder().priority(priority).nameFormat(null).build(); }); } @Test void propertyUncaughtExceptionHandlerTest() { assertThrows(IllegalArgumentException.class, () -> { new ThreadFactoryBuilder().priority(priority).nameFormat("nacos-grpc-executor-%d").uncaughtExceptionHandler(null) .build(); }); } @Test void propertyCustomizeFactoryHandlerTest() { assertThrows(IllegalArgumentException.class, () -> { new ThreadFactoryBuilder().priority(priority).nameFormat("nacos-grpc-executor-%d").customizeFactory(null).build(); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/ThreadUtilsTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; 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.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ThreadUtilsTest { private ExecutorService executorService; @BeforeEach void setUp() throws Exception { System.setProperty("nacos.common.processors", "2"); executorService = Executors.newFixedThreadPool(1); } @AfterEach void tearDown() throws Exception { System.setProperty("nacos.common.processors", ""); ThreadUtils.shutdownThreadPool(executorService); } @Test void testLatchAwait() { final CountDownLatch countDownLatch = new CountDownLatch(1); long currentTime = System.currentTimeMillis(); executorService.execute(() -> { ThreadUtils.sleep(100); ThreadUtils.countDown(countDownLatch); }); ThreadUtils.latchAwait(countDownLatch); assertTrue(System.currentTimeMillis() - currentTime >= 100); } @Test void testLatchAwaitForTimeout() { final CountDownLatch countDownLatch = new CountDownLatch(1); long currentTime = System.currentTimeMillis(); ThreadUtils.latchAwait(countDownLatch, 50, TimeUnit.MILLISECONDS); assertTrue(System.currentTimeMillis() - currentTime >= 50); } @Test void testGetSuitableThreadCount() { assertEquals(4, ThreadUtils.getSuitableThreadCount()); assertEquals(8, ThreadUtils.getSuitableThreadCount(3)); } @Test void testShutdownThreadPoolWithInterruptedException() throws InterruptedException { ExecutorService executor = mock(ExecutorService.class); when(executor.awaitTermination(100, TimeUnit.MILLISECONDS)).thenThrow(new InterruptedException()); ThreadUtils.shutdownThreadPool(executor); verify(executor, times(4)).shutdownNow(); } @Test void testShutdownThreadPoolWithOtherException() throws InterruptedException { ExecutorService executor = mock(ExecutorService.class); Logger logger = mock(Logger.class); Throwable cause = new RuntimeException(); when(executor.awaitTermination(100, TimeUnit.MILLISECONDS)).thenThrow(cause); ThreadUtils.shutdownThreadPool(executor, logger); verify(executor).shutdownNow(); verify(logger, times(3)).error("ThreadPoolManager shutdown executor has error : ", cause); } @Test void testAddShutdownHook() { Runnable shutdownHook = () -> { }; ThreadUtils.addShutdownHook(shutdownHook); // It seems no way to check it. } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/TlsTypeResolveTest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import io.grpc.netty.shaded.io.netty.handler.ssl.SslProvider; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class TlsTypeResolveTest { @Test void test() { SslProvider openssl = TlsTypeResolve.getSslProvider("openssl"); assertEquals(SslProvider.OPENSSL, openssl); SslProvider openSsL = TlsTypeResolve.getSslProvider("openSSL"); assertEquals(SslProvider.OPENSSL, openSsL); SslProvider jdk = TlsTypeResolve.getSslProvider("JDK"); assertEquals(SslProvider.JDK, jdk); SslProvider anySsl = TlsTypeResolve.getSslProvider("anySSL"); assertEquals(SslProvider.OPENSSL, anySsl); SslProvider refcnt = TlsTypeResolve.getSslProvider("openSSL_refcnt"); assertEquals(SslProvider.OPENSSL_REFCNT, refcnt); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/TypeUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; /** * type utils test. * * @author zzq */ class TypeUtilsTest { @Test void parameterize() { ParameterizedType stringComparableType = TypeUtils.parameterize(List.class, String.class); assertEquals("java.util.List", stringComparableType.toString()); assertEquals(List.class, stringComparableType.getRawType()); assertNull(stringComparableType.getOwnerType()); assertEquals(1, stringComparableType.getActualTypeArguments().length); assertEquals(String.class, stringComparableType.getActualTypeArguments()[0]); ParameterizedType stringIntegerComparableType = TypeUtils.parameterize(Map.class, String.class, Integer.class); assertEquals("java.util.Map", stringIntegerComparableType.toString()); assertEquals(Map.class, stringIntegerComparableType.getRawType()); assertNull(stringComparableType.getOwnerType()); assertEquals(2, stringIntegerComparableType.getActualTypeArguments().length); assertEquals(String.class, stringIntegerComparableType.getActualTypeArguments()[0]); assertEquals(Integer.class, stringIntegerComparableType.getActualTypeArguments()[1]); } @Test void testParameterizeForNull() { assertThrows(NullPointerException.class, () -> { TypeUtils.parameterize(null, String.class); }); } @Test void testParameterizeForNullType() { assertThrows(NullPointerException.class, () -> { TypeUtils.parameterize(List.class, (Type[]) null); }); } @Test void testParameterizeForNullTypeArray() { assertThrows(IllegalArgumentException.class, () -> { TypeUtils.parameterize(List.class, (Type) null); }); } @Test void testParameterizeForDiffLength() { assertThrows(IllegalArgumentException.class, () -> { TypeUtils.parameterize(List.class, String.class, Integer.class); }); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/UuidUtilsTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.util.UUID; class UuidUtilsTest { @Test void testGenerateUuid() { String uuid = UuidUtils.generateUuid(); // try parse to UUID. UUID.fromString(uuid); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/VersionUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils; import org.junit.jupiter.api.Test; import java.io.File; import java.net.URL; 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.junit.jupiter.api.Assertions.assertTrue; class VersionUtilsTest { @Test void testVersionCompareLt() { assertTrue(VersionUtils.compareVersion("1.2.0", "1.2.1") < 0); assertTrue(VersionUtils.compareVersion("0.2.0", "1.2.0") < 0); assertTrue(VersionUtils.compareVersion("1.2.0", "1.3.0") < 0); } @Test void testVersionCompareGt() { assertTrue(VersionUtils.compareVersion("1.2.2", "1.2.1") > 0); assertTrue(VersionUtils.compareVersion("2.2.0", "1.2.0") > 0); assertTrue(VersionUtils.compareVersion("1.3.0", "1.2.0") > 0); } @Test void testVersionCompareEt() { assertEquals(0, VersionUtils.compareVersion("1.2.1", "1.2.1")); } @Test void testVersionCompareLtWithChar() { assertTrue(VersionUtils.compareVersion("1.2.0-beta", "1.2.1") < 0); } @Test void testVersionCompareGtWithChar() { assertTrue(VersionUtils.compareVersion("1.2.2-beta", "1.2.1-beta") > 0); } @Test void testVersionCompareEtWithChar() { assertEquals(0, VersionUtils.compareVersion("1.2.1", "1.2.1-beta")); } @Test void testVersionCompareResourceNotExist() { URL resource = VersionUtils.class.getClassLoader().getResource("nacos-version.txt"); assertNotNull(resource); File originFile = new File(resource.getFile()); File tempFile = new File(originFile.getAbsolutePath() + ".rename"); assertTrue(originFile.renameTo(tempFile)); // not throw any exception VersionUtils.compareVersion("1.2.1", "1.2.1"); assertTrue(tempFile.renameTo(originFile)); } @Test void testVersionCompareVersionNotValid1() { assertThrows(IllegalArgumentException.class, () -> { VersionUtils.compareVersion("1.2.1.1", "1.2.1.1"); }); } @Test void testVersionCompareVersionNotValid2() { assertThrows(IllegalArgumentException.class, () -> { VersionUtils.compareVersion("1.2.1", "1.2.1.1"); }); } @Test void testVersionCompareVersionNotValid3() { assertThrows(IllegalArgumentException.class, () -> { VersionUtils.compareVersion("1.2.1.1", "1.2.1"); }); } @Test void testFullClientVersion() { assertNotNull(VersionUtils.getFullClientVersion()); assertTrue(VersionUtils.getFullClientVersion().startsWith("Nacos-Java-Client:v")); } } ================================================ FILE: common/src/test/java/com/alibaba/nacos/common/utils/to/User.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.common.utils.to; public class User { private Integer id; private String name; public User(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } } ================================================ FILE: common/src/test/resources/META-INF/services/com.alibaba.nacos.api.ability.initializer.AbilityPostProcessor ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.common.ability.MockAbilityPostProcessor ================================================ FILE: common/src/test/resources/META-INF/services/com.alibaba.nacos.common.ability.AbstractAbilityControlManager ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.common.ability.discover.HigherMockAbilityManager com.alibaba.nacos.common.ability.discover.LowerMockAbilityManager ================================================ FILE: common/src/test/resources/META-INF/services/com.alibaba.nacos.common.labels.LabelsCollector ================================================ # # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.common.labels.impl.DefaultLabelsCollector com.alibaba.nacos.common.labels.impl.Test1LabelsCollector com.alibaba.nacos.common.labels.impl.Test2LabelsCollector ================================================ FILE: common/src/test/resources/META-INF/services/com.alibaba.nacos.common.paramcheck.AbstractParamChecker ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.common.paramcheck.MockParamChecker ================================================ FILE: common/src/test/resources/META-INF/services/com.alibaba.nacos.common.spi.SpiTestInterface ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.common.spi.SpiTestImpl ================================================ FILE: common/src/test/resources/resource_utils_test.properties ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # a=b ================================================ FILE: common/src/test/resources/test-tls-cert.pem ================================================ -----BEGIN CERTIFICATE----- MIIC+zCCAmSgAwIBAgIJAK68bP5/APz/MA0GCSqGSIb3DQEBBQUAMHAxCzAJBgNV BAYTAkNOMRIwEAYDVQQIDAlaaGUgSmlhbmcxEjAQBgNVBAcMCUhhbmcgWmhvdTEW MBQGA1UECgwNQWxpYmFiYSBDbG91ZDEOMAwGA1UECwwFTmFjb3MxETAPBgNVBAMM CG5hY29zIGNhMCAXDTIzMDQyMTA4MzI0MVoYDzIxMjMwMzI4MDgzMjQxWjB0MQsw CQYDVQQGEwJDTjESMBAGA1UECAwJWmhlIEppYW5nMRIwEAYDVQQHDAlIYW5nIFpo b3UxFjAUBgNVBAoMDUFsaWJhYmEgQ2xvdWQxDjAMBgNVBAsMBU5hY29zMRUwEwYD VQQDDAxOYWNvcyBDbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB AQDCQ93itb/8s1WW9TjBgoH6OZ1lO6dn08hKFy8vq/IhiSv8k8ks78PzCAWeeDYD xzjA0gsq+2MREt3CE+Vd2Rza/MYCVaHYVdyDzJp2v9kwWrtMrTvUDvtnAf4zq7Oj tObEbFkUn2hPXN9i8pLfeqYdO//HjKcckniRMfretS+zoRVjMBa9upSapl3zUD6C eqarvghg/h7RFpaZJ16suW9zfRIImb6skB81bZU39pf38RYgxQzOkQmjhmGQPEom GFDkM8Y01haPLXI6Un5r6Bohbsh5Or+FOWgW+VW7Ql4smv8Pt+XKDs/3D6wCTXs7 gMAVG+fMoRySj+B90TfdkwNrAgMBAAGjEzARMA8GA1UdEQQIMAaHBH8AAAEwDQYJ KoZIhvcNAQEFBQADgYEAb2hcGyID/IEAecjWlc8Q5AMDFE6DRJlh5lI+a08aHT30 2/o35CxOscoWECKURYD6h24mrGX9XQ1ruOrv4Tga81NX6XE12YwILeKlYK8DDmig MkS9kjHjwdVOjnfpv8Ixfgwpst1TYAAPfD8jwXYg27bixqyse6dLPZR1adBxCwM= -----END CERTIFICATE----- ================================================ FILE: config/pom.xml ================================================ com.alibaba.nacos nacos-all ${revision} 4.0.0 nacos-config jar nacos-config ${project.version} https://nacos.io org.springframework.boot spring-boot-starter-web ${project.groupId} nacos-api ${project.groupId} nacos-core com.alibaba.nacos nacos-persistence commons-io commons-io ch.qos.logback logback-classic com.alibaba.nacos nacos-encryption-plugin com.alibaba.nacos nacos-config-plugin org.apache.httpcomponents.client5 httpclient5 org.springframework.boot spring-boot-starter-tomcat com.fasterxml.jackson.core jackson-core com.fasterxml.jackson.core jackson-databind io.micrometer micrometer-registry-prometheus io.micrometer micrometer-registry-influx io.micrometer micrometer-registry-elastic org.springframework.boot spring-boot-starter-aop org.yaml snakeyaml com.alibaba.nacos nacos-control-plugin ${revision} com.alibaba.nacos nacos-datasource-plugin org.springframework.boot spring-boot-test-autoconfigure test org.springframework spring-test test org.hamcrest hamcrest test com.mysql mysql-connector-j test com.alibaba.nacos nacos-datasource-plugin-mysql ${project.version} test com.alibaba.nacos nacos-datasource-plugin-derby ${project.version} test com.alibaba.nacos nacos-datasource-plugin-postgresql ${project.version} test com.alibaba.nacos nacos-datasource-plugin-oracle ${project.version} test org.apache.maven.plugins maven-assembly-plugin com.alibaba.nacos.config.server.Config jar-with-dependencies springboot ${project.groupId} nacos-core org.springframework.boot spring-boot-maven-plugin com.alibaba.nacos.config.server.Config nacos-config ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/Config.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableScheduling; /** * Config main. * * @author Nacos */ @EnableScheduling @SpringBootApplication(scanBasePackages = { "com.alibaba.nacos.config.server", "com.alibaba.nacos.core"}) public class Config { public static void main(String[] args) { SpringApplication.run(Config.class, args); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/aspect/CapacityManagementAspect.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.CounterMode; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.capacity.CapacityService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.PropertyUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.nio.charset.StandardCharsets; import static com.alibaba.nacos.config.server.constant.Constants.LIMIT_ERROR_CODE; /** * Capacity management aspect for config service. * * @author Nacos */ @Aspect @Component public class CapacityManagementAspect { private static final Logger LOGGER = LoggerFactory.getLogger(CapacityManagementAspect.class); private static final String PUBLISH_CONFIG = "execution(* com.alibaba.nacos.config.server.service.ConfigOperationService.publishConfig(..))"; private static final String DELETE_CONFIG = "execution(* com.alibaba.nacos.config.server.service.ConfigOperationService.deleteConfig(..))"; private final CapacityService capacityService; private final ConfigInfoPersistService configInfoPersistService; public CapacityManagementAspect(ConfigInfoPersistService configInfoPersistService, CapacityService capacityService) { this.configInfoPersistService = configInfoPersistService; this.capacityService = capacityService; } /** * Intercept publish config operations to perform capacity management checks. */ @Around(PUBLISH_CONFIG) public Object aroundPublishConfig(ProceedingJoinPoint pjp) throws Throwable { if (!PropertyUtil.isManageCapacity()) { return pjp.proceed(); } Object[] args = pjp.getArgs(); ConfigForm configForm = (ConfigForm) args[0]; ConfigRequestInfo configRequestInfo = (ConfigRequestInfo) args[1]; String dataId = configForm.getDataId(); String group = configForm.getGroup(); String namespaceId = configForm.getNamespaceId(); String content = configForm.getContent(); String betaIps = configRequestInfo.getBetaIps(); String tag = configForm.getTag(); LOGGER.info("[CapacityManagement] Intercepting publishConfig operation for dataId: {}, group: {}, namespaceId: {}", dataId, group, namespaceId); if (StringUtils.isBlank(betaIps) && StringUtils.isBlank(tag) && StringUtils.isBlank(configForm.getGrayName())) { // do capacity management limitation check for writing or updating config_info table. if (configInfoPersistService.findConfigInfo(dataId, group, namespaceId) == null) { // Write operation. return do4Insert(pjp, group, namespaceId, content); } else { // Update operation. return do4Update(pjp, dataId, group, namespaceId, content); } } return pjp.proceed(); } /** * Update operation: open the limitation of capacity management, and it will check the size of content. * * @throws Throwable Throws Exception when actually operate. */ private Object do4Update(ProceedingJoinPoint pjp, String dataId, String group, String namespaceId, String content) throws Throwable { if (!PropertyUtil.isCapacityLimitCheck()) { return pjp.proceed(); } try { boolean hasTenant = StringUtils.isNotBlank(namespaceId); Capacity capacity = getCapacity(group, namespaceId, hasTenant); if (isSizeLimited(group, namespaceId, getCurrentSize(content), hasTenant, false, capacity)) { throw new NacosException(ErrorCode.OVER_MAX_SIZE.getCode(), String.format("Configuration content size limit exceeded [group=%s, namespaceId=%s].", group, namespaceId)); } } catch (Exception e) { LOGGER.error("[CapacityManagement] Error during update operation for dataId: {}, group: {}, namespaceId: {}", dataId, group, namespaceId, e); throw e; } return pjp.proceed(); } /** * Write operation. Step 1: count whether to open the limitation checking function for capacity management; Step 2: * open limitation checking capacity management and check size of content and quota; * * @throws Throwable Exception. */ private Object do4Insert(ProceedingJoinPoint pjp, String group, String namespaceId, String content) throws Throwable { LOGGER.info("[CapacityManagement] Handling insert operation for group: {}, namespaceId: {}", group, namespaceId); CounterMode counterMode = CounterMode.INCREMENT; boolean hasTenant = StringUtils.isNotBlank(namespaceId); if (PropertyUtil.isCapacityLimitCheck()) { // Write or update: usage + 1 LimitType limitType = getLimitType(counterMode, group, namespaceId, content, hasTenant); if (limitType != null) { ErrorCode errorCode = ErrorCode.getErrorCode(limitType.name()); if (errorCode != null) { throw new NacosException(errorCode.getCode(), String.format("Configuration limit exceeded [group=%s, namespaceId=%s].", group, namespaceId)); } } } else { // Write or update: usage + 1 insertOrUpdateUsage(group, namespaceId, counterMode, hasTenant); } return getResult(pjp, group, namespaceId, counterMode, hasTenant); } /** * Intercept delete config operations to perform capacity management checks. */ @Around(DELETE_CONFIG) public Object aroundDeleteConfig(ProceedingJoinPoint pjp) throws Throwable { if (!PropertyUtil.isManageCapacity()) { return pjp.proceed(); } Object[] args = pjp.getArgs(); String dataId = (String) args[0]; String group = (String) args[1]; String namespaceId = (String) args[2]; String grayName = (String) args[3]; LOGGER.info("[CapacityManagement] Intercepting deleteConfig operation for dataId: {}, group: {}, namespaceId: {}", dataId, group, namespaceId); if (StringUtils.isNotBlank(grayName)) { return pjp.proceed(); } ConfigInfo configInfo = configInfoPersistService.findConfigInfo(dataId, group, namespaceId); if (configInfo == null) { return pjp.proceed(); } return do4Delete(pjp, group, namespaceId, configInfo); } /** * Delete Operation. * * @throws Throwable Exception. */ private Object do4Delete(ProceedingJoinPoint pjp, String group, String namespaceId, ConfigInfo configInfo) throws Throwable { boolean hasTenant = StringUtils.isNotBlank(namespaceId); if (configInfo == null) { // "configInfo == null", has two possible points. // 1. Concurrently deletion. // 2. First, new sub configurations are added, and then all sub configurations are deleted. // At this time, the task (asynchronous) written to configinfo has not been executed. // // About 2 point, then it will execute to merge to write config_info's task orderly, and delete config_info's task. // Active modification of usage, when it happens to be in the above "merging to write config_info's task". // Modify usage when the task of info is finished, and usage = 1. // The following "delete config_info" task will not be executed with usage-1, because the request has already returned. // Therefore, it is necessary to modify the usage job regularly. correctUsage(group, namespaceId, hasTenant); return pjp.proceed(); } // The same record can be deleted concurrently. This interface can be deleted asynchronously(submit MergeDataTask // to MergeTaskProcessor for processing), It may lead to more than one decrease in usage. // Therefore, it is necessary to modify the usage job regularly. CounterMode counterMode = CounterMode.DECREMENT; insertOrUpdateUsage(group, namespaceId, counterMode, hasTenant); return getResult(pjp, group, namespaceId, counterMode, hasTenant); } private void correctUsage(String group, String namespaceId, boolean hasTenant) { try { if (hasTenant) { LOGGER.info("[capacityManagement] correct usage, namespaceId: {}", namespaceId); capacityService.correctTenantUsage(namespaceId); } else { LOGGER.info("[capacityManagement] correct usage, group: {}", group); capacityService.correctGroupUsage(group); } } catch (Exception e) { LOGGER.error("[capacityManagement] correctUsage ", e); } } private Object getResult(ProceedingJoinPoint pjp, String group, String namespaceId, CounterMode counterMode, boolean hasTenant) throws Throwable { try { // Execute operation actually. Boolean result = (Boolean) pjp.proceed(); if (!result) { rollbackUsage(counterMode, group, namespaceId, hasTenant); } return result; } catch (Throwable throwable) { LOGGER.warn("[capacityManagement] inner operation throw exception, rollback, group: {}, namespaceId: {}", group, namespaceId, throwable); rollbackUsage(counterMode, group, namespaceId, hasTenant); throw throwable; } } /** * Usage counting service: it will count whether the limitation check function will be open. */ private void insertOrUpdateUsage(String group, String namespaceId, CounterMode counterMode, boolean hasTenant) { try { capacityService.insertAndUpdateClusterUsage(counterMode, true); if (hasTenant) { capacityService.insertAndUpdateTenantUsage(counterMode, namespaceId, true); } else { capacityService.insertAndUpdateGroupUsage(counterMode, group, true); } } catch (Exception e) { LOGGER.error("[capacityManagement] insertOrUpdateUsage ", e); } } private LimitType getLimitType(CounterMode counterMode, String group, String namespaceId, String content, boolean hasTenant) { try { boolean clusterLimited = !capacityService.insertAndUpdateClusterUsage(counterMode, false); if (clusterLimited) { LOGGER.warn("[capacityManagement] cluster capacity reaches quota."); return LimitType.OVER_CLUSTER_QUOTA; } if (content == null) { return null; } int currentSize = getCurrentSize(content); LimitType limitType = getGroupOrTenantLimitType(counterMode, group, namespaceId, currentSize, hasTenant); if (limitType != null) { rollbackClusterUsage(counterMode); return limitType; } } catch (Exception e) { LOGGER.error("[capacityManagement] isLimited ", e); } return null; } /** * Get and return the byte size of encoding. */ private int getCurrentSize(String content) { try { return content.getBytes(StandardCharsets.UTF_8).length; } catch (Exception e) { LOGGER.error("[capacityManagement] getCurrentSize ", e); } return 0; } private LimitType getGroupOrTenantLimitType(CounterMode counterMode, String group, String namespaceId, int currentSize, boolean hasTenant) { if (group == null) { return null; } Capacity capacity = getCapacity(group, namespaceId, hasTenant); if (isSizeLimited(group, namespaceId, currentSize, hasTenant, false, capacity)) { return LimitType.OVER_MAX_SIZE; } if (capacity == null) { insertCapacity(group, namespaceId, hasTenant); } boolean updateSuccess = isUpdateSuccess(counterMode, group, namespaceId, hasTenant); if (updateSuccess) { return null; } if (hasTenant) { return LimitType.OVER_TENANT_QUOTA; } return LimitType.OVER_GROUP_QUOTA; } private boolean isUpdateSuccess(CounterMode counterMode, String group, String namespaceId, boolean hasTenant) { boolean updateSuccess; if (hasTenant) { updateSuccess = capacityService.updateTenantUsage(counterMode, namespaceId); if (!updateSuccess) { LOGGER.warn("[capacityManagement] namespaceId capacity reaches quota, namespaceId: {}", namespaceId); } } else { updateSuccess = capacityService.updateGroupUsage(counterMode, group); if (!updateSuccess) { LOGGER.warn("[capacityManagement] group capacity reaches quota, group: {}", group); } } return updateSuccess; } private void insertCapacity(String group, String namespaceId, boolean hasTenant) { if (hasTenant) { capacityService.initTenantCapacity(namespaceId); } else { capacityService.initGroupCapacity(group); } } private Capacity getCapacity(String group, String namespaceId, boolean hasTenant) { Capacity capacity; if (hasTenant) { capacity = capacityService.getTenantCapacity(namespaceId); } else { capacity = capacityService.getGroupCapacity(group); } return capacity; } private boolean isSizeLimited(String group, String namespaceId, int currentSize, boolean hasTenant, boolean isAggr, Capacity capacity) { int defaultMaxSize = getDefaultMaxSize(isAggr); if (capacity != null) { Integer maxSize = getMaxSize(isAggr, capacity); if (maxSize == 0) { // If there exists capacity info and maxSize = 0, then it uses maxSize limitation default value to compare. return isOverSize(group, namespaceId, currentSize, defaultMaxSize, hasTenant); } // If there exists capacity info, then maxSize!=0. return isOverSize(group, namespaceId, currentSize, maxSize, hasTenant); } // If there no exists capacity info, then it uses maxSize limitation default value to compare. return isOverSize(group, namespaceId, currentSize, defaultMaxSize, hasTenant); } private Integer getMaxSize(boolean isAggr, Capacity capacity) { if (isAggr) { return capacity.getMaxAggrSize(); } return capacity.getMaxSize(); } private int getDefaultMaxSize(boolean isAggr) { if (isAggr) { return PropertyUtil.getDefaultMaxAggrSize(); } return PropertyUtil.getDefaultMaxSize(); } private boolean isOverSize(String group, String namespaceId, int currentSize, int maxSize, boolean hasTenant) { if (currentSize > maxSize) { if (hasTenant) { LOGGER.warn( "[capacityManagement] namespaceId content is over maxSize, namespaceId: {}, maxSize: {}, currentSize: {}", namespaceId, maxSize, currentSize); } else { LOGGER.warn( "[capacityManagement] group content is over maxSize, group: {}, maxSize: {}, currentSize: {}", group, maxSize, currentSize); } return true; } return false; } private void rollbackUsage(CounterMode counterMode, String group, String namespaceId, boolean hasTenant) { try { rollbackClusterUsage(counterMode); if (hasTenant) { capacityService.updateTenantUsage(counterMode.reverse(), namespaceId); } else { capacityService.updateGroupUsage(counterMode.reverse(), group); } } catch (Exception e) { LOGGER.error("[capacityManagement] rollback ", e); } } private void rollbackClusterUsage(CounterMode counterMode) { try { if (!capacityService.updateClusterUsage(counterMode.reverse())) { LOGGER.error("[capacityManagement] cluster usage rollback fail counterMode: {}", counterMode); } } catch (Exception e) { LOGGER.error("[capacityManagement] rollback ", e); } } /** * limit type. * * @author Nacos. */ public enum LimitType { /** * over limit. */ OVER_CLUSTER_QUOTA("Exceeded the maximum number of configurations in the cluster", LIMIT_ERROR_CODE), OVER_GROUP_QUOTA("Exceeded the maximum number of configurations in this group", LIMIT_ERROR_CODE), OVER_TENANT_QUOTA("Exceeded the maximum number of configurations for this namespaceId", LIMIT_ERROR_CODE), OVER_MAX_SIZE("Exceeded the maximum size limit of the configuration content", LIMIT_ERROR_CODE); public final String description; public final int status; LimitType(String description, int status) { this.description = description; this.status = status; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/aspect/ConfigChangeAspect.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.configuration.ConfigChangeConfigs; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.plugin.config.ConfigChangePluginManager; import com.alibaba.nacos.plugin.config.constants.ConfigChangeConstants; import com.alibaba.nacos.plugin.config.constants.ConfigChangeExecuteTypes; import com.alibaba.nacos.plugin.config.constants.ConfigChangePointCutTypes; import com.alibaba.nacos.plugin.config.model.ConfigChangeRequest; import com.alibaba.nacos.plugin.config.model.ConfigChangeResponse; import com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Properties; import static com.alibaba.nacos.config.server.constant.Constants.HTTP; /** * Config change pointcut aspect,which config change plugin services will pointcut. * * @author Nacos */ @Aspect @Component public class ConfigChangeAspect { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigChangeAspect.class); private static final Integer DEFAULT_BEFORE_LIST_CAPACITY = 2; private static final Integer DEFAULT_AFTER_LIST_CAPACITY = 1; private static final String ENABLED = "enabled"; /** * Publish config. */ private static final String PUBLISH_CONFIG = "execution(* com.alibaba.nacos.config.server.service.ConfigOperationService.publishConfig(..))"; /** * Delete config. */ private static final String DELETE_CONFIG = "execution(* com.alibaba.nacos.config.server.service.ConfigOperationService.deleteConfig(..))"; private final ConfigChangeConfigs configChangeConfigs; public ConfigChangeAspect(ConfigChangeConfigs configChangeConfigs) { this.configChangeConfigs = configChangeConfigs; } /** * Publish or update config. */ @Around(PUBLISH_CONFIG) Object publishOrUpdateConfigAround(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); ConfigForm configForm = (ConfigForm) args[0]; ConfigRequestInfo configRequestInfo = (ConfigRequestInfo) args[1]; final String dataId = configForm.getDataId(); final String group = configForm.getGroup(); final String namespaceId = configForm.getNamespaceId(); final String content = configForm.getContent(); final String desc = configForm.getDesc(); final String use = configForm.getUse(); final String effect = configForm.getEffect(); final String type = configForm.getType(); final String tag = configForm.getTag(); final String configTags = configForm.getConfigTags(); final String requestIpApp = configRequestInfo.getRequestIpApp(); final String scrIp = configRequestInfo.getSrcIp(); final String scrType = configRequestInfo.getSrcType(); final String betaIps = configRequestInfo.getBetaIps(); String grayName = null; String grayRuleExp = null; if (StringUtils.isNotBlank(betaIps)) { grayName = BetaGrayRule.TYPE_BETA; grayRuleExp = betaIps; } else if (StringUtils.isNotBlank(tag)) { grayName = TagGrayRule.TYPE_TAG + "_" + configForm.getTag(); grayRuleExp = tag; } ConfigChangePointCutTypes configChangePointCutType = null; if (HTTP.equals(scrType)) { // via console or api calls configChangePointCutType = ConfigChangePointCutTypes.PUBLISH_BY_HTTP; } else { // via sdk rpc calls configChangePointCutType = ConfigChangePointCutTypes.PUBLISH_BY_RPC; } final List pluginServices = getPluginServices( configChangePointCutType); // didn't enabled or add relative plugin if (pluginServices.isEmpty()) { return pjp.proceed(); } ConfigChangeRequest configChangeRequest = new ConfigChangeRequest(configChangePointCutType); configChangeRequest.setArg("dataId", dataId); configChangeRequest.setArg("group", group); configChangeRequest.setArg("namespaceId", namespaceId); configChangeRequest.setArg("content", content); configChangeRequest.setArg("tag", tag); configChangeRequest.setArg("requestIpApp", requestIpApp); configChangeRequest.setArg("srcIp", scrIp); configChangeRequest.setArg("configTags", configTags); configChangeRequest.setArg("desc", desc); configChangeRequest.setArg("use", use); configChangeRequest.setArg("effect", effect); configChangeRequest.setArg("type", type); configChangeRequest.setArg("grayName", grayName); configChangeRequest.setArg("grayRuleExp", grayRuleExp); return configChangeServiceHandle(pjp, pluginServices, configChangeRequest); } /** * Remove config. */ @Around(DELETE_CONFIG) Object removeConfigByIdAround(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); final String dataId = (String) args[0]; final String group = (String) args[1]; final String namespaceId = (String) args[2]; final String grayName = (String) args[3]; final String srcIp = (String) args[4]; final String srcUser = (String) args[5]; final String scrType = (String) args[6]; ConfigChangePointCutTypes configChangePointCutType = null; if (HTTP.equals(scrType)) { // via console or api calls configChangePointCutType = ConfigChangePointCutTypes.PUBLISH_BY_HTTP; } else { // via sdk rpc calls configChangePointCutType = ConfigChangePointCutTypes.PUBLISH_BY_RPC; } final List pluginServices = getPluginServices(configChangePointCutType); // didn't enabled or add relative plugin if (pluginServices.isEmpty()) { return pjp.proceed(); } ConfigChangeRequest configChangeRequest = new ConfigChangeRequest(configChangePointCutType); configChangeRequest.setArg("dataId", dataId); configChangeRequest.setArg("group", group); configChangeRequest.setArg("namespaceId", namespaceId); configChangeRequest.setArg("srcIp", srcIp); configChangeRequest.setArg("srcUser", srcUser); configChangeRequest.setArg("grayName", grayName); configChangeRequest.setArg("modifyTime", TimeUtils.getCurrentTimeStr()); return configChangeServiceHandle(pjp, pluginServices, configChangeRequest); } /** * Execute relevant config change plugin services. */ private Object configChangeServiceHandle(ProceedingJoinPoint pjp, List configChangePluginServiceList, ConfigChangeRequest configChangeRequest) { ConfigChangePointCutTypes handleType = configChangeRequest.getRequestType(); ConfigChangeResponse configChangeResponse = new ConfigChangeResponse(handleType); // default success,when before plugin service verify failed , set false configChangeResponse.setSuccess(true); List beforeExecutePluginServices = new ArrayList<>(DEFAULT_BEFORE_LIST_CAPACITY); List afterExecutePluginServices = new ArrayList<>(DEFAULT_AFTER_LIST_CAPACITY); Object retVal = null; Object[] args = pjp.getArgs(); configChangeRequest.setArg(ConfigChangeConstants.ORIGINAL_ARGS, args); for (ConfigChangePluginService ccs : configChangePluginServiceList) { if (!isEnabled(ccs)) { continue; } if (ConfigChangeExecuteTypes.EXECUTE_BEFORE_TYPE.equals(ccs.executeType())) { beforeExecutePluginServices.add(ccs); } else { afterExecutePluginServices.add(ccs); } } // before plugin service execute for (ConfigChangePluginService ccs : beforeExecutePluginServices) { final String serviceType = ccs.getServiceType().toLowerCase(Locale.ROOT); final Properties properties = configChangeConfigs.getPluginProperties(serviceType); configChangeRequest.setArg(ConfigChangeConstants.PLUGIN_PROPERTIES, properties); ccs.execute(configChangeRequest, configChangeResponse); if (null != configChangeResponse.getArgs()) { // update args by filter with whitelist args = configChangeResponse.getArgs(); } // prevent execute next before plugins service if (!configChangeResponse.isSuccess()) { retVal = false; break; } } try { if (configChangeResponse.isSuccess()) { retVal = pjp.proceed(args); } } catch (Throwable e) { LOGGER.warn("Config change join point execution failed. Error details: {}", e.getMessage()); configChangeResponse.setMsg("Config change join point failed: " + e.getMessage()); retVal = false; } // after plugin service execute ConfigExecutor.executeAsyncConfigChangePluginTask(() -> { for (ConfigChangePluginService ccs : afterExecutePluginServices) { try { final String serviceType = ccs.getServiceType().toLowerCase(Locale.ROOT); final Properties properties = configChangeConfigs.getPluginProperties(serviceType); configChangeRequest.setArg(ConfigChangeConstants.PLUGIN_PROPERTIES, properties); ccs.execute(configChangeRequest, configChangeResponse); } catch (Throwable throwable) { LOGGER.warn("execute async plugin services failed {}", throwable.getMessage()); } } }); return retVal; } private List getPluginServices( ConfigChangePointCutTypes configChangePointCutType) { List pluginServicePriorityList = ConfigChangePluginManager .findPluginServicesByPointcut(configChangePointCutType); if (pluginServicePriorityList == null) { return new ArrayList<>(); } for (ConfigChangePluginService each : pluginServicePriorityList) { if (isEnabled(each)) { return pluginServicePriorityList; } } return new ArrayList<>(); } private boolean isEnabled(ConfigChangePluginService configChangePluginService) { Properties serviceConfigProperties = configChangeConfigs .getPluginProperties(configChangePluginService.getServiceType()); return Boolean.parseBoolean(serviceConfigProperties.getProperty(ENABLED)); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/aspect/ConfigOpFailureAspect.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.config.server.utils.LogUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.springframework.stereotype.Component; /** * The pointcut for configuration change operations, it will log when the configuration change fails. * * @author blake.qiu */ @Aspect @Component public class ConfigOpFailureAspect { private static final Logger LOGGER = LogUtil.DEFAULT_LOG; /** * Pointcut for all methods from 'configRepositoryInterface'. */ @Pointcut("within(com.alibaba.nacos.config.server.service.repository..*)") public void configRepositoryInterfaceMethods() { } /** * Log message when a method from 'configRepositoryInterface' throws an exception. */ @AfterThrowing(pointcut = "configRepositoryInterfaceMethods()", throwing = "exception") public void logException(JoinPoint joinPoint, Throwable exception) { try { Object[] args = joinPoint.getArgs(); StringBuilder params = new StringBuilder(); if (args != null) { for (int i = 0; i < args.length; i++) { if (i < args.length - 1) { params.append(args[i]).append(", "); } else { params.append(args[i]); } } } String methodName = joinPoint.getSignature().getName(); LOGGER.error("An error occurred while executing method [{}].\n Parameters: [{}].", methodName, params, exception); } catch (Exception e) { LOGGER.error("An error occurred while logging the original exception. method [{}]", joinPoint.getSignature().getName(), e); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/aspect/RequestLogAspect.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.RequestUtil; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; /** * Aspect for logging HTTP API and SDK API requests in Nacos. * * @author Nacos */ @Aspect @Component public class RequestLogAspect { private static final String PUBLISH_CONFIG = "execution(* com.alibaba.nacos.config.server.service.ConfigOperationService.publishConfig(..))"; private static final String GET_CONFIG = "execution(* com.alibaba.nacos.config.server.service.query.ConfigQueryChainService.handle(..))"; private static final String DELETE_CONFIG = "execution(* com.alibaba.nacos.config.server.service.ConfigOperationService.deleteConfig(..))"; private static final String CONFIG_CHANGE_LISTEN_RPC = "execution(* com.alibaba.nacos.core.remote.RequestHandler.handleRequest(..)) " + " && target(com.alibaba.nacos.config.server.remote.ConfigChangeBatchListenRequestHandler) && args(request,meta)"; /** * Intercepts configuration publishing operations, records metrics, and logs client requests. */ @Around(PUBLISH_CONFIG) public Object interfacePublishConfig(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); ConfigForm configForm = (ConfigForm) args[0]; ConfigRequestInfo configRequestInfo = (ConfigRequestInfo) args[1]; String dataId = configForm.getDataId(); String group = configForm.getGroup(); String namespaceId = configForm.getNamespaceId(); String content = configForm.getContent(); String requestIp = configRequestInfo.getSrcIp(); String md5 = content == null ? null : MD5Utils.md5Hex(content, Constants.ENCODE); MetricsMonitor.getPublishMonitor().incrementAndGet(); AtomicLong rtHolder = new AtomicLong(); Object retVal = logClientRequest("publish", pjp, dataId, group, namespaceId, requestIp, md5, rtHolder); MetricsMonitor.getWriteConfigRtTimer().record(rtHolder.get(), TimeUnit.MILLISECONDS); return retVal; } /** * Intercepts configuration get operations, records metrics, and logs client requests. */ @Around(GET_CONFIG) public Object interfaceGetConfig(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); ConfigQueryChainRequest chainRequest = (ConfigQueryChainRequest) args[0]; String dataId = chainRequest.getDataId(); String group = chainRequest.getGroup(); String tenant = chainRequest.getTenant(); String requestIp = null; if (chainRequest.getAppLabels() != null) { requestIp = chainRequest.getAppLabels().getOrDefault(BetaGrayRule.CLIENT_IP_LABEL, null); } String groupKey = GroupKey2.getKey(dataId, group, tenant); String md5 = ConfigCacheService.getContentMd5(groupKey); MetricsMonitor.getConfigMonitor().incrementAndGet(); AtomicLong rtHolder = new AtomicLong(); Object retVal = logClientRequest("get", pjp, dataId, group, tenant, requestIp, md5, rtHolder); MetricsMonitor.getReadConfigRtTimer().record(rtHolder.get(), TimeUnit.MILLISECONDS); return retVal; } /** * Deletes a configuration entry and logs the operation. */ @Around(DELETE_CONFIG) public Object interfaceRemoveConfig(ProceedingJoinPoint pjp) throws Throwable { Object[] args = pjp.getArgs(); String dataId = (String) args[0]; String group = (String) args[1]; String tenant = (String) args[2]; String clientIp = (String) args[4]; String groupKey = GroupKey2.getKey(dataId, group, tenant); String md5 = ConfigCacheService.getContentMd5(groupKey); MetricsMonitor.getConfigMonitor().incrementAndGet(); AtomicLong rtHolder = new AtomicLong(); Object retVal = logClientRequest("delete", pjp, dataId, group, tenant, clientIp, md5, rtHolder); MetricsMonitor.getReadConfigRtTimer().record(rtHolder.get(), TimeUnit.MILLISECONDS); return retVal; } /** * Client api request log rt | status | requestIp | opType | dataId | group | datumId | md5. */ private Object logClientRequest(String requestType, ProceedingJoinPoint pjp, String dataId, String group, String tenant, String requestIp, String md5, AtomicLong rtHolder) throws Throwable { long startTime = System.currentTimeMillis(); try { Object retVal = pjp.proceed(); long rt = System.currentTimeMillis() - startTime; if (rtHolder != null) { rtHolder.set(rt); } LogUtil.CLIENT_LOG.info( "opType: {} | rt: {}ms | status: success | requestIp: {} | dataId: {} | group: {} | tenant: {} | md5: {}", requestType, rt, requestIp, dataId, group, tenant, md5); return retVal; } catch (Throwable e) { long rt = System.currentTimeMillis() - startTime; if (rtHolder != null) { rtHolder.set(rt); } LogUtil.CLIENT_LOG.error( "opType: {} | rt: {}ms | status: failure | requestIp: {} | dataId: {} | group: {} | tenant: {} | md5: {}", requestType, rt, requestIp, dataId, group, tenant, md5); throw e; } } /** * Handles configuration change listening requests. */ @Around(CONFIG_CHANGE_LISTEN_RPC) public Object interfaceListenConfigRpc(ProceedingJoinPoint pjp, ConfigBatchListenRequest request, RequestMeta meta) throws Throwable { MetricsMonitor.getConfigMonitor().incrementAndGet(); final String requestIp = meta.getClientIp(); String appName = request.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); final long st = System.currentTimeMillis(); Response retVal = (Response) pjp.proceed(); final long rt = System.currentTimeMillis() - st; LogUtil.CLIENT_LOG.info( "opType: {} | rt: {}ms | status: {} | requestIp: {} | listenSize: {} | listenOrCancel: {} | appName: {}", "listen", rt, retVal.isSuccess() ? retVal.getResultCode() : retVal.getErrorCode(), requestIp, request.getConfigListenContexts().size(), request.isListen(), appName); return retVal; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigChangeConfigs.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.configuration; import com.alibaba.nacos.common.event.ServerConfigChangeEvent; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.plugin.config.constants.ConfigChangeConstants; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.PropertiesUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Configuration; import java.util.HashMap; import java.util.Map; import java.util.Properties; /** * config change plugin configs. * * @author liyunfei **/ @Configuration public class ConfigChangeConfigs extends Subscriber { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigChangeConfigs.class); private static final String PREFIX = ConfigChangeConstants.NACOS_CORE_CONFIG_PLUGIN_PREFIX; private Map configPluginProperties = new HashMap<>(); public ConfigChangeConfigs() { NotifyCenter.registerSubscriber(this); refreshPluginProperties(); } private void refreshPluginProperties() { try { Map newProperties = new HashMap<>(3); Properties properties = PropertiesUtil.getPropertiesWithPrefix(EnvUtil.getEnvironment(), PREFIX); if (properties != null) { for (String each : properties.stringPropertyNames()) { int typeIndex = each.indexOf('.'); String type = each.substring(0, typeIndex); String subKey = each.substring(typeIndex + 1); newProperties.computeIfAbsent(type, key -> new Properties()) .setProperty(subKey, properties.getProperty(each)); } } configPluginProperties = newProperties; } catch (Exception e) { LOGGER.warn("[ConfigChangeConfigs]Refresh config plugin properties failed ", e); } } public Properties getPluginProperties(String configPluginType) { if (!configPluginProperties.containsKey(configPluginType)) { LOGGER.warn( "[ConfigChangeConfigs]Can't find config plugin properties for type {}, will use empty properties", configPluginType); return new Properties(); } return configPluginProperties.get(configPluginType); } @Override public void onEvent(ServerConfigChangeEvent event) { refreshPluginProperties(); } @Override public Class subscribeType() { return ServerConfigChangeEvent.class; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfig.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.configuration; import com.alibaba.nacos.core.config.AbstractDynamicConfig; import com.alibaba.nacos.sys.env.EnvUtil; /** * Nacos config common configs. * * @author blake.qiu */ public class ConfigCommonConfig extends AbstractDynamicConfig { private static final String CONFIG_COMMON = "ConfigCommon"; private static final ConfigCommonConfig INSTANCE = new ConfigCommonConfig(); private int maxPushRetryTimes = 50; private long pushTimeout = 3000L; private int batchSize = 20; private boolean derbyOpsEnabled = false; private int maxPatternCount = 20; private int maxMatchedConfigCount = 500; private ConfigCommonConfig() { super(CONFIG_COMMON); resetConfig(); } public static ConfigCommonConfig getInstance() { return INSTANCE; } public int getMaxPushRetryTimes() { return maxPushRetryTimes; } public void setMaxPushRetryTimes(int maxPushRetryTimes) { this.maxPushRetryTimes = maxPushRetryTimes; } public long getPushTimeout() { return pushTimeout; } public int getBatchSize() { return batchSize; } public boolean isDerbyOpsEnabled() { return derbyOpsEnabled; } public void setDerbyOpsEnabled(boolean derbyOpsEnabled) { this.derbyOpsEnabled = derbyOpsEnabled; } public int getMaxPatternCount() { return maxPatternCount; } public int getMaxMatchedConfigCount() { return maxMatchedConfigCount; } @Override protected void getConfigFromEnv() { maxPushRetryTimes = EnvUtil.getProperty("nacos.config.push.maxRetryTime", Integer.class, 50); pushTimeout = EnvUtil.getProperty("nacos.config.push.timeout", Long.class, 3000L); batchSize = EnvUtil.getProperty("nacos.config.push.batchSize", Integer.class, 20); derbyOpsEnabled = EnvUtil.getProperty("nacos.config.derby.ops.enabled", Boolean.class, false); maxPatternCount = EnvUtil.getProperty("nacos.config.fuzzy.watch.max.pattern.count", Integer.class, 20); maxMatchedConfigCount = EnvUtil.getProperty("nacos.config.fuzzy.watch.max.pattern.match.config.count", Integer.class, 500); } @Override protected String printConfig() { return toString(); } @Override public String toString() { return "ConfigCommonConfig{" + "maxPushRetryTimes=" + maxPushRetryTimes + ", derbyOpsEnabled=" + derbyOpsEnabled + '}'; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/configuration/ConfigCompatibleConfig.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.configuration; import com.alibaba.nacos.config.server.constant.PropertiesConstant; import com.alibaba.nacos.core.config.AbstractDynamicConfig; import com.alibaba.nacos.sys.env.EnvUtil; /** * The type Config compatible config. * * @author Sunrisea */ public class ConfigCompatibleConfig extends AbstractDynamicConfig { private static final String CONFIG_NAME = "configCompatible"; private boolean namespaceCompatibleMode = true; private static final ConfigCompatibleConfig INSTANCE = new ConfigCompatibleConfig(); protected ConfigCompatibleConfig() { super(CONFIG_NAME); resetConfig(); } public boolean isNamespaceCompatibleMode() { return namespaceCompatibleMode; } @Override protected void getConfigFromEnv() { namespaceCompatibleMode = EnvUtil.getProperty(PropertiesConstant.NAMESPACE_COMPATIBLE_MODE, Boolean.class, true); } @Override protected String printConfig() { return "ConfigCompatibleConfig{" + "namespaceCompatibleMode=" + namespaceCompatibleMode + '}'; } public static ConfigCompatibleConfig getInstance() { return INSTANCE; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/configuration/NacosConfigConfiguration.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.configuration; import com.alibaba.nacos.config.server.filter.CircuitFilter; import com.alibaba.nacos.config.server.filter.NacosWebFilter; import com.alibaba.nacos.core.code.ControllerMethodsCache; import com.alibaba.nacos.core.web.NacosWebBean; import com.alibaba.nacos.persistence.configuration.condition.ConditionDistributedEmbedStorage; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; /** * Nacos Config {@link Configuration} includes required Spring components. * * @author Mercy * @since 0.2.2 */ @Configuration @NacosWebBean public class NacosConfigConfiguration { private final ControllerMethodsCache methodsCache; public NacosConfigConfiguration(ControllerMethodsCache methodsCache) { this.methodsCache = methodsCache; } @PostConstruct public void init() { methodsCache.initClassMethod("com.alibaba.nacos.config.server.controller"); } @Bean @ConditionalOnProperty(name = "nacos.web.charset.filter", havingValue = "nacos", matchIfMissing = true) public FilterRegistrationBean nacosWebFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(nacosWebFilter()); registration.addUrlPatterns("/v1/cs/*"); registration.setName("nacosWebFilter"); registration.setOrder(1); return registration; } @Bean public NacosWebFilter nacosWebFilter() { return new NacosWebFilter(); } @Conditional(ConditionDistributedEmbedStorage.class) @Bean public FilterRegistrationBean transferToLeaderRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(transferToLeader()); registration.addUrlPatterns("/v1/cs/*"); registration.setName("curcuitFilter"); registration.setOrder(6); return registration; } @Conditional(ConditionDistributedEmbedStorage.class) @Bean public CircuitFilter transferToLeader() { return new CircuitFilter(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/constant/ConfigModuleStateBuilder.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.persistence.utils.DatasourcePlatformUtil; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.module.AbstractServerModuleStateBuilder; import com.alibaba.nacos.sys.module.ModuleState; /** * config module state builder. * @author 985492783@qq.com * @date 2023/4/6 10:25 */ public class ConfigModuleStateBuilder extends AbstractServerModuleStateBuilder { @Override public ModuleState build() { ModuleState moduleState = new ModuleState(com.alibaba.nacos.api.common.Constants.Config.CONFIG_MODULE); moduleState.newState(Constants.DATASOURCE_PLATFORM_PROPERTY_STATE, DatasourcePlatformUtil.getDatasourcePlatform("")); moduleState.newState(Constants.NACOS_PLUGIN_DATASOURCE_LOG_STATE, EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false)); moduleState.newState(PropertiesConstant.NOTIFY_CONNECT_TIMEOUT, PropertyUtil.getNotifyConnectTimeout()); moduleState.newState(PropertiesConstant.NOTIFY_SOCKET_TIMEOUT, PropertyUtil.getNotifySocketTimeout()); moduleState.newState(PropertiesConstant.IS_HEALTH_CHECK, PropertyUtil.isHealthCheck()); moduleState.newState(PropertiesConstant.MAX_HEALTH_CHECK_FAIL_COUNT, PropertyUtil.getMaxHealthCheckFailCount()); moduleState.newState(PropertiesConstant.MAX_CONTENT, PropertyUtil.getMaxContent()); moduleState.newState(PropertiesConstant.IS_MANAGE_CAPACITY, PropertyUtil.isManageCapacity()); moduleState.newState(PropertiesConstant.IS_CAPACITY_LIMIT_CHECK, PropertyUtil.isCapacityLimitCheck()); moduleState.newState(PropertiesConstant.DEFAULT_CLUSTER_QUOTA, PropertyUtil.getDefaultClusterQuota()); moduleState.newState(PropertiesConstant.DEFAULT_GROUP_QUOTA, PropertyUtil.getDefaultGroupQuota()); moduleState.newState(PropertiesConstant.DEFAULT_MAX_SIZE, PropertyUtil.getDefaultMaxSize()); moduleState.newState(PropertiesConstant.DEFAULT_MAX_AGGR_COUNT, PropertyUtil.getDefaultMaxAggrCount()); moduleState.newState(PropertiesConstant.DEFAULT_MAX_AGGR_SIZE, PropertyUtil.getDefaultMaxAggrSize()); moduleState.newState(Constants.CONFIG_RENTENTION_DAYS_PROPERTY_STATE, PropertyUtil.getConfigRententionDays()); return moduleState; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/constant/Constants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.event.ConfigDumpEvent; /** * Server Constants. * * @author Nacos */ public class Constants { public static final String CLIENT_VERSION_HEADER = "Client-Version"; public static final String CLIENT_VERSION = "3.0.0"; public static final String DEFAULT_GROUP = "DEFAULT_GROUP"; public static final String DATASOURCE_PLATFORM_PROPERTY_STATE = "datasource_platform"; public static final String CONFIG_RENTENTION_DAYS_PROPERTY_STATE = "config_retention_days"; /** * Config file directory in server side. */ public static final String BASE_DIR = "config-data"; /** * Back up file directory in server side. */ public static final String CONFIG_BAK_DIR = System.getProperty("user.home", "/home/admin") + "/nacos/bak_data"; public static final String APPNAME = "AppName"; public static final String UNKNOWN_APP = "UnknownApp"; public static final String DEFAULT_DOMAINNAME = "commonconfig.config-host.taobao.com"; public static final String DAILY_DOMAINNAME = "commonconfig.taobao.net"; public static final String NULL = ""; public static final String DATAID = "dataId"; public static final String GROUP = "group"; public static final String LAST_MODIFIED = "Last-Modified"; public static final String ACCEPT_ENCODING = "Accept-Encoding"; public static final String CONTENT_ENCODING = "Content-Encoding"; public static final String PROBE_MODIFY_REQUEST = "Listening-Configs"; public static final String PROBE_MODIFY_RESPONSE = "Probe-Modify-Response"; public static final String PROBE_MODIFY_RESPONSE_NEW = "Probe-Modify-Response-New"; public static final String USE_ZIP = "true"; public static final String CONTENT_MD5 = "Content-MD5"; public static final String CONFIG_VERSION = "Config-Version"; public static final String IF_MODIFIED_SINCE = "If-Modified-Since"; public static final String SPACING_INTERVAL = "client-spacing-interval"; /** * Interval for async update address(unit: second). */ public static final int ASYNC_UPDATE_ADDRESS_INTERVAL = 300; /** * Interval for polling(unit: second). */ public static final int POLLING_INTERVAL_TIME = 15; /** * Unit: millisecond. */ public static final int ONCE_TIMEOUT = 2000; /** * Unit: millisecond. */ public static final int CONN_TIMEOUT = 2000; /** * Unit: millisecond. */ public static final int SO_TIMEOUT = 60000; /** * Unit: millisecond. */ public static final int RECV_WAIT_TIMEOUT = ONCE_TIMEOUT * 5; public static final String BASE_PATH = "/v1/cs"; public static final String BASE_V2_PATH = "/v2/cs"; public static final String BASE_ADMIN_V3_PATH = "/v3/admin/cs"; public static final String OPS_CONTROLLER_PATH = BASE_PATH + "/ops"; public static final String OPS_CONTROLLER_V3_ADMIN_PATH = BASE_ADMIN_V3_PATH + "/ops"; public static final String CAPACITY_CONTROLLER_PATH = BASE_PATH + "/capacity"; public static final String CAPACITY_CONTROLLER_V3_ADMIN_PATH = BASE_ADMIN_V3_PATH + "/capacity"; public static final String COMMUNICATION_CONTROLLER_PATH = BASE_PATH + "/communication"; public static final String CONFIG_CONTROLLER_PATH = BASE_PATH + "/configs"; public static final String CONFIG_CONTROLLER_V2_PATH = BASE_V2_PATH + "/config"; public static final String CONFIG_ADMIN_V3_PATH = BASE_ADMIN_V3_PATH + "/config"; public static final String HEALTH_CONTROLLER_PATH = BASE_PATH + "/health"; public static final String HISTORY_CONTROLLER_PATH = BASE_PATH + "/history"; public static final String HISTORY_CONTROLLER_V2_PATH = BASE_V2_PATH + "/history"; public static final String HISTORY_ADMIN_V3_PATH = BASE_ADMIN_V3_PATH + "/history"; public static final String LISTENER_CONTROLLER_PATH = BASE_PATH + "/listener"; public static final String LISTENER_CONTROLLER_V3_ADMIN_PATH = BASE_ADMIN_V3_PATH + "/listener"; public static final String NAMESPACE_CONTROLLER_PATH = BASE_PATH + "/namespaces"; public static final String METRICS_CONTROLLER_PATH = BASE_PATH + "/metrics"; public static final String METRICS_CONTROLLER_V3_ADMIN_PATH = BASE_ADMIN_V3_PATH + "/metrics"; public static final String CONFIG_V3_CLIENT_API_PATH = "/v3/client/cs/config"; public static final String ENCODE = "UTF-8"; public static final String PERSIST_ENCODE = getPersistEncode(); public static final String ENCODE_GBK = "GBK"; public static final String ENCODE_UTF8 = "UTF-8"; public static final String MAP_FILE = "map-file.js"; public static final int FLOW_CONTROL_THRESHOLD = 20; public static final int FLOW_CONTROL_SLOT = 10; public static final int FLOW_CONTROL_INTERVAL = 1000; public static final String LINE_SEPARATOR = Character.toString((char) 1); public static final String WORD_SEPARATOR = Character.toString((char) 2); public static final String NACOS_LINE_SEPARATOR = "\r\n"; /** * Total time of threshold value when getting data from network(unit: millisecond). */ public static final long TOTALTIME_FROM_SERVER = 10000; /** * Invalid total time of threshold value when getting data from network(unit: millisecond). */ public static final long TOTALTIME_INVALID_THRESHOLD = 60000; /** * When exception or error occurs. */ public static final int BATCH_OP_ERROR = -1; /** * State code of single data when batch operation. */ public static final String BATCH_OP_ERROR_IO_MSG = "get config dump error"; public static final String BATCH_OP_ERROR_CONFLICT_MSG = "config get conflicts"; /** * Batch query when data existent. */ public static final int BATCH_QUERY_EXISTS = 1; public static final String BATCH_QUERY_EXISTS_MSG = "config exits"; /** * Batch query when data non-existent. */ public static final int BATCH_QUERY_NONEXISTS = 2; public static final String BATCH_QUERY_NONEEXISTS_MSG = "config not exits"; /** * Batch adding successfully. */ public static final int BATCH_ADD_SUCCESS = 3; /** * Batch updating successfully. */ public static final int BATCH_UPDATE_SUCCESS = 4; public static final int MAX_UPDATE_FAIL_COUNT = 5; public static final int MAX_UPDATEALL_FAIL_COUNT = 5; public static final int MAX_REMOVE_FAIL_COUNT = 5; public static final int MAX_REMOVEALL_FAIL_COUNT = 5; public static final int MAX_NOTIFY_COUNT = 5; public static final int MAX_ADDACK_COUNT = 5; /** * First version of data. */ public static final int FIRST_VERSION = 1; /** * Poison version when data is deleted. */ public static final int POISON_VERSION = -1; /** * Temporary version when disk file is full. */ public static final int TEMP_VERSION = 0; /** * Plain sequence of getting data: backup file -> server -> local file. */ public static final int GETCONFIG_LOCAL_SERVER_SNAPSHOT = 1; /** * Plain sequence of getting data: backup file -> local file -> server. */ public static final int GETCONFIG_LOCAL_SNAPSHOT_SERVER = 2; public static final String CLIENT_APPNAME_HEADER = "Client-AppName"; public static final String CLIENT_REQUEST_TS_HEADER = "Client-RequestTS"; public static final String CLIENT_REQUEST_TOKEN_HEADER = "Client-RequestToken"; /** * Client, identity for sdk request to server. */ public static final String REQUEST_IDENTITY = "Request-Identity"; /** * Forward to leader node. */ public static final String FORWARD_LEADER = "Forward-Leader"; /** * Acl result information. */ public static final String ACL_RESPONSE = "ACL-Response"; public static final int ATOMIC_MAX_SIZE = 1000; public static final int DATA_IN_BODY_VERSION = 204; /** * Configure the dump event name. */ public static final String EXTEND_INFO_CONFIG_DUMP_EVENT = ConfigDumpEvent.class.getName(); /** * Configure the dump event-list name. */ public static final String EXTEND_INFOS_CONFIG_DUMP_EVENT = ConfigDumpEvent.class.getName() + "@@many"; public static final String CONFIG_EXPORT_ITEM_FILE_SEPARATOR = "/"; public static final String CONFIG_EXPORT_METADATA = ".meta.yml"; public static final String CONFIG_EXPORT_METADATA_NEW = ".metadata.yml"; public static final int LIMIT_ERROR_CODE = 429; public static final String NACOS_PLUGIN_DATASOURCE_LOG_STATE = "plugin_datasource_log_enabled"; public static final String CONFIG_SEARCH_BLUR = "blur"; public static final String CONFIG_SEARCH_ACCURATE = "accurate"; /** * Gray rule. */ public static final String GRAY_RULE_TYPE = "type"; public static final String GRAY_RULE_EXPR = "expr"; public static final String GRAY_RULE_VERSION = "version"; public static final String GRAY_RULE_PRIORITY = "priority"; /** * default nacos encode. */ public static final String DEFAULT_NACOS_ENCODE = "UTF-8"; public static final String NACOS_PERSIST_ENCODE_KEY = "nacosPersistEncodingKey"; /** * config publish type. */ public static final String FORMAL = "formal"; public static final String GRAY = "gray"; /** * request source type. */ public static final String HTTP = "http"; public static final String RPC = "rpc"; /** * Separator. */ public static final String COLON = ":"; static String getPersistEncode() { String persistEncode = System.getenv(NACOS_PERSIST_ENCODE_KEY); if (StringUtils.isBlank(persistEncode)) { persistEncode = System.getProperty(NACOS_PERSIST_ENCODE_KEY); if (StringUtils.isBlank(persistEncode)) { persistEncode = DEFAULT_NACOS_ENCODE; } } return persistEncode; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/constant/CounterMode.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; /** * counter mode. * * @author hexu.hxy * @date 2018/3/13 */ public enum CounterMode { /** * Increment. */ INCREMENT, /** * Decrement. */ DECREMENT; /** * Reverse the two mode value. * * @return CounterMode */ public CounterMode reverse() { if (INCREMENT == this) { return DECREMENT; } return INCREMENT; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/constant/ParametersField.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; /** * Parameters Field. * * @author haiqi.wang * @date 2024/08/13 */ public final class ParametersField { /** * Types. */ public static final String TYPES = "types"; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/constant/PropertiesConstant.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; /** * PropertiesConstant. * * @author lixiaoshuang */ public class PropertiesConstant { public static final String NOTIFY_CONNECT_TIMEOUT = "notifyConnectTimeout"; public static final String NOTIFY_SOCKET_TIMEOUT = "notifySocketTimeout"; public static final String IS_HEALTH_CHECK = "isHealthCheck"; public static final String MAX_HEALTH_CHECK_FAIL_COUNT = "maxHealthCheckFailCount"; public static final String MAX_CONTENT = "maxContent"; public static final String IS_MANAGE_CAPACITY = "isManageCapacity"; public static final String IS_CAPACITY_LIMIT_CHECK = "isCapacityLimitCheck"; public static final String DEFAULT_CLUSTER_QUOTA = "defaultClusterQuota"; public static final String DEFAULT_GROUP_QUOTA = "defaultGroupQuota"; public static final String DEFAULT_TENANT_QUOTA = "defaultTenantQuota"; public static final String DEFAULT_MAX_SIZE = "defaultMaxSize"; public static final String DEFAULT_MAX_AGGR_COUNT = "defaultMaxAggrCount"; public static final String DEFAULT_MAX_AGGR_SIZE = "defaultMaxAggrSize"; public static final String CORRECT_USAGE_DELAY = "correctUsageDelay"; public static final String INITIAL_EXPANSION_PERCENT = "initialExpansionPercent"; public static final String SEARCH_MAX_CAPACITY = "nacos.config.search.max_capacity"; public static final String SEARCH_MAX_THREAD = "nacos.config.search.max_thread"; public static final String SEARCH_WAIT_TIMEOUT = "nacos.config.search.wait_timeout"; public static final String DUMP_CHANGE_ON = "dumpChangeOn"; public static final String DUMP_CHANGE_WORKER_INTERVAL = "dumpChangeWorkerInterval"; public static final String CONFIG_RENTENTION_DAYS = "nacos.config.retention.days"; public static final String GRAY_CAPATIBEL_MODEL = "nacos.config.gray.compatible.model"; public static final String NAMESPACE_COMPATIBLE_MODE = "nacos.config.namespace.compatible.mode"; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigServletInner.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.enums.ApiVersionEnum; import com.alibaba.nacos.config.server.enums.FileTypeEnum; import com.alibaba.nacos.config.server.exception.NacosConfigException; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.LongPollingService; import com.alibaba.nacos.config.server.service.query.ConfigChainRequestExtractorService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.enums.ResponseCode; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.MD5Util; import com.alibaba.nacos.config.server.utils.Protocol; import com.alibaba.nacos.config.server.utils.RequestUtil; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.io.IOException; import java.io.PrintWriter; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Map; import static com.alibaba.nacos.api.common.Constants.CONFIG_TYPE; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; import static com.alibaba.nacos.config.server.constant.Constants.CONTENT_MD5; /** * ConfigServlet inner for aop. * * @author Nacos */ @Service public class ConfigServletInner { private static final int TRY_GET_LOCK_TIMES = 9; private static final int START_LONG_POLLING_VERSION_NUM = 204; private static final Logger LOGGER = LoggerFactory.getLogger(ConfigServletInner.class); private final LongPollingService longPollingService; private final ConfigQueryChainService configQueryChainService; public ConfigServletInner(LongPollingService longPollingService, ConfigQueryChainService configQueryChainService) { this.longPollingService = longPollingService; this.configQueryChainService = configQueryChainService; } private static String getDecryptContent(ConfigQueryChainResponse chainResponse, String dataId) { Pair pair = EncryptionHandler.decryptHandler(dataId, chainResponse.getEncryptedDataKey(), chainResponse.getContent()); return pair.getSecond(); } /** * long polling the config. */ public String doPollingConfig(HttpServletRequest request, HttpServletResponse response, Map clientMd5Map, int probeRequestSize) throws IOException { // Long polling. if (LongPollingService.isSupportLongPolling(request)) { longPollingService.addLongPollingClient(request, response, clientMd5Map, probeRequestSize); return HttpServletResponse.SC_OK + ""; } // Compatible with short polling logic. Map changedGroups = MD5Util.compareMd5(request, response, clientMd5Map); // Compatible with short polling result. String oldResult = MD5Util.compareMd5OldResult(changedGroups); String newResult = MD5Util.compareMd5ResultString(changedGroups); String version = request.getHeader(Constants.CLIENT_VERSION_HEADER); if (version == null) { version = "2.0.0"; } int versionNum = Protocol.getVersionNumber(version); // Before 2.0.4 version, return value is put into header. if (versionNum < START_LONG_POLLING_VERSION_NUM) { response.addHeader(Constants.PROBE_MODIFY_RESPONSE, oldResult); response.addHeader(Constants.PROBE_MODIFY_RESPONSE_NEW, newResult); } else { request.setAttribute("content", newResult); } // Disable cache. response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-cache,no-store"); response.setStatus(HttpServletResponse.SC_OK); return HttpServletResponse.SC_OK + ""; } /** * Execute to get config [API V1] or [API V2]. */ public String doGetConfig(HttpServletRequest request, HttpServletResponse response, String dataId, String group, String tenant, String tag, String isNotify, String clientIp, ApiVersionEnum apiVersion) throws IOException { boolean notify = StringUtils.isNotBlank(isNotify) && Boolean.parseBoolean(isNotify); String requestIpApp = RequestUtil.getAppName(request); ConfigQueryChainRequest chainRequest = ConfigChainRequestExtractorService.getExtractor().extract(request); chainRequest.setTenant(NamespaceUtil.processNamespaceParameter(chainRequest.getTenant())); ConfigQueryChainResponse chainResponse = configQueryChainService.handle(chainRequest); if (ResponseCode.FAIL.getCode() == chainResponse.getResultCode()) { throw new NacosConfigException(chainResponse.getMessage()); } logPullEvent(dataId, group, tenant, requestIpApp, chainResponse, clientIp, notify, tag); switch (chainResponse.getStatus()) { case CONFIG_NOT_FOUND: case SPECIAL_TAG_CONFIG_NOT_FOUND: return handlerConfigNotFound(response, apiVersion); case CONFIG_QUERY_CONFLICT: return handlerConfigConflict(response, apiVersion); default: return handleResponse(response, chainResponse, dataId, group, apiVersion); } } private String handlerConfigNotFound(HttpServletResponse response, ApiVersionEnum apiVersion) throws IOException { response.setStatus(HttpServletResponse.SC_NOT_FOUND); if (apiVersion == ApiVersionEnum.V1) { return writeResponseForV1(response, Result.failure(ErrorCode.RESOURCE_NOT_FOUND, "config data not exist")); } else { return writeResponseForV2(response, Result.failure(ErrorCode.RESOURCE_NOT_FOUND, "config data not exist")); } } private String handlerConfigConflict(HttpServletResponse response, ApiVersionEnum apiVersion) throws IOException { response.setStatus(HttpServletResponse.SC_CONFLICT); if (apiVersion == ApiVersionEnum.V1) { return writeResponseForV1(response, Result.failure(ErrorCode.RESOURCE_CONFLICT, "requested file is being modified, please try later.")); } else { return writeResponseForV2(response, Result.failure(ErrorCode.RESOURCE_CONFLICT, "requested file is being modified, please try later.")); } } private String handleResponse(HttpServletResponse response, ConfigQueryChainResponse chainResponse, String dataId, String group, ApiVersionEnum apiVersion) throws IOException { if (apiVersion == ApiVersionEnum.V1) { return handleResponseForV1(response, chainResponse, dataId, group); } else { return handleResponseForV2(response, chainResponse, dataId, group); } } private String handleResponseForV1(HttpServletResponse response, ConfigQueryChainResponse chainResponse, String dataId, String tag) throws IOException { if (chainResponse.getContent() == null) { return handlerConfigNotFound(response, ApiVersionEnum.V1); } setCommonResponseHead(response, chainResponse, tag); setResponseHeadForV1(response, chainResponse); writeContentForV1(response, chainResponse, dataId); return HttpServletResponse.SC_OK + ""; } private String handleResponseForV2(HttpServletResponse response, ConfigQueryChainResponse chainResponse, String dataId, String tag) throws IOException { if (chainResponse.getContent() == null) { return handlerConfigNotFound(response, ApiVersionEnum.V2); } setCommonResponseHead(response, chainResponse, tag); setResponseHeadForV2(response); writeContentForV2(response, chainResponse, dataId); return HttpServletResponse.SC_OK + ""; } private void setResponseHeadForV1(HttpServletResponse response, ConfigQueryChainResponse chainResponse) { String contentType = chainResponse.getContentType(); if (StringUtils.isBlank(contentType)) { contentType = FileTypeEnum.TEXT.getContentType(); } response.setHeader(HttpHeaderConsts.CONTENT_TYPE, contentType); } private void setResponseHeadForV2(HttpServletResponse response) { response.setHeader(HttpHeaderConsts.CONTENT_TYPE, MediaType.APPLICATION_JSON); } private void writeContentForV1(HttpServletResponse response, ConfigQueryChainResponse chainResponse, String dataId) throws IOException { PrintWriter out = response.getWriter(); try { String decryptContent = getDecryptContent(chainResponse, dataId); out.print(decryptContent); } finally { out.flush(); out.close(); } } private void writeContentForV2(HttpServletResponse response, ConfigQueryChainResponse chainResponse, String dataId) throws IOException { PrintWriter out = response.getWriter(); try { String decryptContent = getDecryptContent(chainResponse, dataId); out.print(JacksonUtils.toJson(Result.success(decryptContent))); } finally { out.flush(); out.close(); } } private String writeResponseForV1(HttpServletResponse response, Result result) throws IOException { PrintWriter writer = response.getWriter(); writer.println(result.getData()); return response.getStatus() + ""; } private String writeResponseForV2(HttpServletResponse response, Result result) throws IOException { PrintWriter writer = response.getWriter(); writer.println(JacksonUtils.toJson(result)); return response.getStatus() + ""; } private String resolvePullEvent(ConfigQueryChainResponse chainResponse, String tag) { switch (chainResponse.getStatus()) { case CONFIG_FOUND_GRAY: ConfigCacheGray matchedGray = chainResponse.getMatchedGray(); if (matchedGray != null) { return ConfigTraceService.PULL_EVENT + "-" + matchedGray.getGrayName(); } else { return ConfigTraceService.PULL_EVENT; } case SPECIAL_TAG_CONFIG_NOT_FOUND: return ConfigTraceService.PULL_EVENT + "-" + TagGrayRule.TYPE_TAG + "-" + tag; default: return ConfigTraceService.PULL_EVENT; } } private void logPullEvent(String dataId, String group, String tenant, String requestIpApp, ConfigQueryChainResponse chainResponse, String clientIp, boolean notify, String tag) { String pullEvent = resolvePullEvent(chainResponse, tag); ConfigQueryChainResponse.ConfigQueryStatus status = chainResponse.getStatus(); if (status == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_QUERY_CONFLICT) { ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, pullEvent, ConfigTraceService.PULL_TYPE_CONFLICT, -1, clientIp, notify, "http"); } else if (status == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND || chainResponse.getContent() == null) { ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, pullEvent, ConfigTraceService.PULL_TYPE_NOTFOUND, -1, clientIp, notify, "http"); } else { long delayed = System.currentTimeMillis() - chainResponse.getLastModified(); ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, chainResponse.getLastModified(), pullEvent, ConfigTraceService.PULL_TYPE_OK, delayed, clientIp, notify, "http"); } } private void setCommonResponseHead(HttpServletResponse response, ConfigQueryChainResponse chainResponse, String tag) { String configType = chainResponse.getConfigType() != null ? chainResponse.getConfigType() : FileTypeEnum.TEXT.getFileType(); response.setHeader(CONFIG_TYPE, configType); response.setHeader(CONTENT_MD5, chainResponse.getMd5()); response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-cache,no-store"); response.setDateHeader("Last-Modified", chainResponse.getLastModified()); if (chainResponse.getEncryptedDataKey() != null) { response.setHeader("Encrypted-Data-Key", chainResponse.getEncryptedDataKey()); } // Check if there is a matched gray rule if (ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY == chainResponse.getStatus()) { if (BetaGrayRule.TYPE_BETA.equals(chainResponse.getMatchedGray().getGrayRule().getType())) { response.setHeader("isBeta", "true"); } else if (TagGrayRule.TYPE_TAG.equals(chainResponse.getMatchedGray().getGrayRule().getType())) { try { response.setHeader(TagGrayRule.TYPE_TAG, URLEncoder.encode(chainResponse.getMatchedGray().getGrayRule().getRawGrayRuleExp(), StandardCharsets.UTF_8.displayName())); } catch (Exception e) { LOGGER.error("Error encoding tag", e); } } } // Check if there is a special tag if (ConfigQueryChainResponse.ConfigQueryStatus.SPECIAL_TAG_CONFIG_NOT_FOUND == chainResponse.getStatus()) { try { response.setHeader(VIPSERVER_TAG, URLEncoder.encode(tag, StandardCharsets.UTF_8.displayName())); } catch (Exception e) { LOGGER.error("Error encoding tag", e); } } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/parameters/SameNamespaceCloneConfigBean.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.parameters; /** * SameNamespaceCloneConfigBean. * * @author klw(213539 @ qq.com) * @date 2019/12/13 16:10 */ public class SameNamespaceCloneConfigBean { private Long cfgId; private String dataId; private String group; public Long getCfgId() { return cfgId; } public void setCfgId(Long cfgId) { this.cfgId = cfgId; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/v3/CapacityControllerV3.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.form.UpdateCapacityForm; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.service.capacity.CapacityService; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import static com.alibaba.nacos.config.server.constant.Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH; /** * Capacity Management. * * @author Nacos */ @NacosApi @RestController @RequestMapping(CAPACITY_CONTROLLER_V3_ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class CapacityControllerV3 { private static final Logger LOGGER = LoggerFactory.getLogger(CapacityControllerV3.class); private final CapacityService capacityService; public CapacityControllerV3(CapacityService capacityService) { this.capacityService = capacityService; } /** * Get capacity information. */ @GetMapping @Secured(resource = Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result getCapacity(@RequestParam(required = false) String groupName, @RequestParam(required = false) String namespaceId) throws NacosApiException { if (StringUtils.isBlank(groupName) && StringUtils.isBlank(namespaceId)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "At least one of the parameters (groupName or namespaceId) must be provided"); } try { Capacity capacity = capacityService.getCapacityWithDefault(groupName, namespaceId); if (capacity == null) { LOGGER.warn("[getCapacity] capacity not exist,need init groupName: {}, namespaceId: {}", groupName, namespaceId); capacityService.initCapacity(groupName, namespaceId); capacity = capacityService.getCapacityWithDefault(groupName, namespaceId); } return Result.success(capacity); } catch (Exception e) { LOGGER.error("[getCapacity] Failed to fetch capacity for groupName: {}, namespaceId: {}", groupName, namespaceId, e); return Result.failure(ErrorCode.SERVER_ERROR.getCode(), e.getMessage(), null); } } /** * Modify group or capacity of namespaceId, and init record when capacity information are still initial. */ @PostMapping @Secured(resource = Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result updateCapacity(UpdateCapacityForm updateCapacityForm) throws NacosApiException { updateCapacityForm.checkNamespaceIdAndGroupName(capacityService); updateCapacityForm.validate(); String groupName = updateCapacityForm.getGroupName(); String namespaceId = updateCapacityForm.getNamespaceId(); Integer quota = updateCapacityForm.getQuota(); Integer maxSize = updateCapacityForm.getMaxSize(); Integer maxAggrCount = updateCapacityForm.getMaxAggrCount(); Integer maxAggrSize = updateCapacityForm.getMaxAggrSize(); try { boolean isSuccess = capacityService.insertOrUpdateCapacity(groupName, namespaceId, quota, maxSize, maxAggrCount, maxAggrSize); if (isSuccess) { return Result.success(true); } else { return Result.failure(ErrorCode.SERVER_ERROR.getCode(), String.format("Failed to update the capacity for groupName: %s, namespaceId: %s", groupName, namespaceId), null); } } catch (Exception e) { LOGGER.error("[updateCapacity] Failed to update the capacity for groupName: {}, namespaceId: {}", groupName, namespaceId, e); return Result.failure(ErrorCode.SERVER_ERROR.getCode(), e.getMessage(), null); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/v3/ConfigControllerV3.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigCloneInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.DateFormatUtils; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.constant.ParametersField; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigMetadata; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.paramcheck.ConfigBlurSearchHttpParamExtractor; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.service.ConfigChangePublisher; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.listener.ConfigListenerStateDelegate; import com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.RequestUtil; import com.alibaba.nacos.config.server.utils.ResponseUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.config.server.utils.YamlParserUtil; import com.alibaba.nacos.config.server.utils.ZipUtils; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.model.form.AggregationForm; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import com.alibaba.nacos.sys.utils.InetUtils; import jakarta.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.CollectionUtils; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import static com.alibaba.nacos.config.server.utils.RequestUtil.getRemoteIp; /** * Configuration management. * * @author Nacos */ @NacosApi @RestController @RequestMapping(Constants.CONFIG_ADMIN_V3_PATH) @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class ConfigControllerV3 { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigControllerV3.class); private static final String EXPORT_CONFIG_FILE_NAME = "nacos_config_export_"; private static final String EXPORT_CONFIG_FILE_NAME_EXT = ".zip"; private static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyyMMddHHmmss"; private final ConfigOperationService configOperationService; private final ConfigInfoPersistService configInfoPersistService; private final ConfigDetailService configDetailService; private final ConfigInfoGrayPersistService configInfoGrayPersistService; private final ConfigInfoBetaPersistService configInfoBetaPersistService; private final NamespacePersistService namespacePersistService; private final ConfigListenerStateDelegate configListenerStateDelegate; private final ConfigMigrateService configMigrateService; /** * Flag to indicate if the table `config_info_beta` exists, which means the old version of table schema is used. */ private boolean oldTableVersion; public ConfigControllerV3(ConfigOperationService configOperationService, ConfigInfoPersistService configInfoPersistService, ConfigDetailService configDetailService, ConfigInfoGrayPersistService configInfoGrayPersistService, ConfigInfoBetaPersistService configInfoBetaPersistService, NamespacePersistService namespacePersistService, ConfigListenerStateDelegate configListenerStateDelegate, ConfigMigrateService configMigrateService) { this.configOperationService = configOperationService; this.configInfoPersistService = configInfoPersistService; this.configDetailService = configDetailService; this.configInfoGrayPersistService = configInfoGrayPersistService; this.configInfoBetaPersistService = configInfoBetaPersistService; this.namespacePersistService = namespacePersistService; this.configListenerStateDelegate = configListenerStateDelegate; this.configMigrateService = configMigrateService; this.oldTableVersion = namespacePersistService.isExistTable("config_info_beta"); } /** * Query configuration. */ @GetMapping @TpsControl(pointName = "ConfigQuery") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result getConfig(ConfigFormV3 configForm) throws NacosException { configForm.validate(); // check namespaceId String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); // check params String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); ConfigAllInfo configAllInfo = configInfoPersistService.findConfigAllInfo(dataId, groupName, namespaceId); if (Objects.isNull(configAllInfo)) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "Config not exist, please publish Config first."); } // decrypted String encryptedDataKey = configAllInfo.getEncryptedDataKey(); Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, configAllInfo.getContent()); configAllInfo.setContent(pair.getSecond()); ConfigDetailInfo result = ResponseUtil.transferToConfigDetailInfo(configAllInfo); return Result.success(result); } /** * Publish configuration. */ @PostMapping @TpsControl(pointName = "ConfigPublish") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result publishConfig(HttpServletRequest request, ConfigFormV3 configForm) throws NacosException { // check required field configForm.validateWithContent(); final boolean namespaceTransferred = NamespaceUtil.isNeedTransferNamespace(configForm.getNamespaceId()); configForm.setNamespaceId(NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId())); // check param ParamUtils.checkParam(configForm.getDataId(), configForm.getGroup(), "datumId", configForm.getContent()); ParamUtils.checkParamV2(configForm.getTag()); if (StringUtils.isBlank(configForm.getSrcUser())) { configForm.setSrcUser(RequestUtil.getSrcUserName(request)); } if (!ConfigType.isValidType(configForm.getType())) { configForm.setType(ConfigType.getDefaultType().getType()); } String encryptedDataKeyFinal = configForm.getEncryptedDataKey(); if (StringUtils.isBlank(encryptedDataKeyFinal)) { // encrypted Pair pair = EncryptionHandler.encryptHandler(configForm.getDataId(), configForm.getContent()); configForm.setContent(pair.getSecond()); encryptedDataKeyFinal = pair.getFirst(); } ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setSrcIp(RequestUtil.getRemoteIp(request)); configRequestInfo.setRequestIpApp(RequestUtil.getAppName(request)); configRequestInfo.setBetaIps(request.getHeader("betaIps")); configRequestInfo.setCasMd5(request.getHeader("casMd5")); configRequestInfo.setNamespaceTransferred(namespaceTransferred); return Result.success( configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKeyFinal)); } /** * Publish config metadata result. * * @param request the request * @param configForm the config form * @return the result * @throws NacosException the nacos exception */ @PutMapping("/metadata") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result publishConfigMetadata(HttpServletRequest request, ConfigFormV3 configForm) throws NacosException { configForm.validate(); String remoteIp = getRemoteIp(request); String configTags = configForm.getConfigTags(); String description = configForm.getDesc(); String dataId = configForm.getDataId(); String group = configForm.getGroup(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); configInfoPersistService.updateConfigInfoMetadata(dataId, group, namespaceId, configTags, description); configMigrateService.updateConfigMetadataMigrate(dataId, group, namespaceId, configTags, description); final Timestamp time = TimeUtils.getCurrentTime(); ConfigTraceService.logPersistenceEvent(dataId, group, namespaceId, null, time.getTime(), remoteIp, ConfigTraceService.PERSISTENCE_EVENT_METADATA, ConfigTraceService.PERSISTENCE_TYPE_PUB, null); ConfigChangePublisher.notifyConfigChange(new ConfigDataChangeEvent(dataId, group, namespaceId, time.getTime())); return Result.success(true); } /** * Delete configuration. */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result deleteConfig(HttpServletRequest request, ConfigFormV3 configForm) throws NacosException { configForm.validate(); // check namespaceId String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String tag = configForm.getTag(); ParamUtils.checkParamV2(tag); String clientIp = getRemoteIp(request); String srcUser = RequestUtil.getSrcUserName(request); return Result.success( configOperationService.deleteConfig(configForm.getDataId(), configForm.getGroupName(), namespaceId, tag, clientIp, srcUser, Constants.HTTP)); } /** * Batch delete configuration by ids. */ @DeleteMapping("/batch") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result deleteConfigs(HttpServletRequest request, @RequestParam(value = "ids") List ids) { String clientIp = getRemoteIp(request); String srcUser = RequestUtil.getSrcUserName(request); try { for (Long id : ids) { ConfigInfo configInfo = configInfoPersistService.findConfigInfo(id); if (configInfo == null) { LOGGER.warn("[deleteConfigs] configInfo is null, id: {}", id); continue; } configOperationService.deleteConfig(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), null, clientIp, srcUser, Constants.HTTP); } return Result.success(true); } catch (Exception e) { LOGGER.error("delete configs based on the IDs list error, IDs: {}", ids, e); return Result.failure(ErrorCode.SERVER_ERROR); } } /** * Subscribe to configured client information. */ @GetMapping("/listener") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result getListeners(ConfigFormV3 configForm, AggregationForm aggregationForm) throws Exception { configForm.validate(); aggregationForm.validate(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); return Result.success( configListenerStateDelegate.getListenerState(configForm.getDataId(), configForm.getGroupName(), namespaceId, aggregationForm.isAggregation())); } /** * List or Search config by config condition. * *

    * This API will entry the request into an queue to cache and do query limit. If API called with frequently or * datasource is high performance and slow RT, The API will return {@code 503}. Can use * `nacos.config.search.max_capacity` and `nacos.config.search.max_thread` to upper the limit of query. And use * `nacos.config.search.wait_timeout` to control the waiting time of query. *

    */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) @ExtractorManager.Extractor(httpExtractor = ConfigBlurSearchHttpParamExtractor.class) public Result> list(ConfigFormV3 configForm, PageForm pageForm, String configDetail, @RequestParam(defaultValue = "blur") String search) throws NacosApiException { configForm.blurSearchValidate(); pageForm.validate(); Map configAdvanceInfo = new HashMap<>(100); if (StringUtils.isNotBlank(configForm.getAppName())) { configAdvanceInfo.put("appName", configForm.getAppName()); } if (StringUtils.isNotBlank(configForm.getConfigTags())) { configAdvanceInfo.put("config_tags", configForm.getConfigTags()); } if (StringUtils.isNotBlank(configForm.getType())) { configAdvanceInfo.put(ParametersField.TYPES, configForm.getType()); } if (StringUtils.isNotBlank(configDetail)) { configAdvanceInfo.put("content", configDetail); } int pageNo = pageForm.getPageNo(); int pageSize = pageForm.getPageSize(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); Page configInfoPage = configDetailService.findConfigInfoPage(search, pageNo, pageSize, dataId, groupName, namespaceId, configAdvanceInfo); Page result = new Page<>(); result.setTotalCount(configInfoPage.getTotalCount()); result.setPagesAvailable(configInfoPage.getPagesAvailable()); result.setPageNumber(configInfoPage.getPageNumber()); result.setPageItems(configInfoPage.getPageItems().stream().map(ResponseUtil::transferToConfigBasicInfo) .collect(Collectors.toList())); return Result.success(result); } /** * Execute to remove beta operation. */ @DeleteMapping("/beta") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result stopBeta(HttpServletRequest httpServletRequest, ConfigFormV3 configForm) throws NacosApiException { configForm.validate(); String remoteIp = getRemoteIp(httpServletRequest); String requestIpApp = RequestUtil.getAppName(httpServletRequest); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); try { configInfoGrayPersistService.removeConfigInfoGray(dataId, groupName, namespaceId, BetaGrayRule.TYPE_BETA, remoteIp, RequestUtil.getSrcUserName(httpServletRequest)); configMigrateService.removeConfigInfoGrayMigrate(dataId, groupName, namespaceId, BetaGrayRule.TYPE_BETA, remoteIp, RequestUtil.getSrcUserName(httpServletRequest)); } catch (Throwable e) { LOGGER.error("remove beta data error", e); return Result.failure(ErrorCode.SERVER_ERROR.getCode(), "remove beta data error", false); } ConfigTraceService.logPersistenceEvent(dataId, groupName, namespaceId, requestIpApp, System.currentTimeMillis(), remoteIp, ConfigTraceService.PERSISTENCE_EVENT_BETA, ConfigTraceService.PERSISTENCE_TYPE_REMOVE, null); if (PropertyUtil.isGrayCompatibleModel() && oldTableVersion) { configInfoBetaPersistService.removeConfigInfo4Beta(dataId, groupName, namespaceId); } ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(dataId, groupName, namespaceId, BetaGrayRule.TYPE_BETA, System.currentTimeMillis())); return Result.success(true); } /** * Execute to query beta operation. */ @GetMapping("/beta") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result queryBeta(ConfigFormV3 configForm) throws NacosApiException { configForm.validate(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); ConfigInfoGrayWrapper beta4Gray = configInfoGrayPersistService.findConfigInfo4Gray(dataId, groupName, namespaceId, "beta"); if (Objects.nonNull(beta4Gray)) { String encryptedDataKey = beta4Gray.getEncryptedDataKey(); Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, beta4Gray.getContent()); beta4Gray.setContent(pair.getSecond()); ConfigGrayInfo result = ResponseUtil.transferToConfigGrayInfo(beta4Gray); return Result.success(result); } else { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.RESOURCE_NOT_FOUND, "Config is not in beta."); } } /** * Execute import and publish config operation. */ @PostMapping("/import") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result> importAndPublishConfig(HttpServletRequest request, @RequestParam(value = "src_user", required = false) String srcUser, @RequestParam(value = "namespaceId", required = false) String namespaceId, @RequestParam(value = "policy", defaultValue = "ABORT") SameConfigPolicy policy, MultipartFile file) throws NacosException { Map failedData = new HashMap<>(4); if (Objects.isNull(file)) { return Result.failure(ErrorCode.DATA_EMPTY, failedData); } namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); if (StringUtils.isNotBlank(namespaceId) && !NamespaceUtil.isDefaultNamespaceId(namespaceId) && namespacePersistService.tenantInfoCountByTenantId(namespaceId) <= 0) { failedData.put("succCount", 0); return Result.failure(ErrorCode.NAMESPACE_NOT_EXIST, failedData); } if (StringUtils.isBlank(srcUser)) { srcUser = RequestUtil.getSrcUserName(request); } List configInfoList = new ArrayList<>(); List> unrecognizedList = new ArrayList<>(); try { ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes()); Result> errorResult = parseImportDataV2(srcUser, unziped, configInfoList, unrecognizedList, namespaceId); if (errorResult != null) { return errorResult; } } catch (IOException e) { failedData.put("succCount", 0); LOGGER.error("parsing data failed", e); return Result.failure(ErrorCode.PARSING_DATA_FAILED, failedData); } if (CollectionUtils.isEmpty(configInfoList)) { failedData.put("succCount", 0); return Result.failure(ErrorCode.DATA_EMPTY, failedData); } final String srcIp = RequestUtil.getRemoteIp(request); String requestIpApp = RequestUtil.getAppName(request); final Timestamp time = TimeUtils.getCurrentTime(); Map saveResult = configInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, null, policy); for (ConfigInfo configInfo : configInfoList) { ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), time.getTime())); ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), requestIpApp, time.getTime(), InetUtils.getSelfIP(), ConfigTraceService.PERSISTENCE_EVENT, ConfigTraceService.PERSISTENCE_TYPE_PUB, configInfo.getContent()); } // unrecognizedCount if (!unrecognizedList.isEmpty()) { saveResult.put("unrecognizedCount", unrecognizedList.size()); saveResult.put("unrecognizedData", unrecognizedList); } return Result.success(saveResult); } /** * Import config add .metadata.yml file. */ private Result> parseImportDataV2(String srcUser, ZipUtils.UnZipResult unziped, List configInfoList, List> unrecognizedList, String namespaceId) { ZipUtils.ZipItem metaDataItem = unziped.getMetaDataItem(); String metaData = metaDataItem.getItemData(); Map failedData = new HashMap<>(4); ConfigMetadata configMetadata = YamlParserUtil.loadObject(metaData, ConfigMetadata.class); if (configMetadata == null || CollectionUtils.isEmpty(configMetadata.getMetadata())) { failedData.put("succCount", 0); return Result.failure(ErrorCode.METADATA_ILLEGAL, failedData); } List configExportItems = configMetadata.getMetadata(); // check config metadata for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) { if (StringUtils.isBlank(configExportItem.getDataId()) || StringUtils.isBlank(configExportItem.getGroup()) || StringUtils.isBlank(configExportItem.getType())) { failedData.put("succCount", 0); return Result.failure(ErrorCode.METADATA_ILLEGAL, failedData); } } List zipItemList = unziped.getZipItemList(); Set metaDataKeys = configExportItems.stream() .map(metaItem -> GroupKey.getKey(metaItem.getDataId(), metaItem.getGroup())) .collect(Collectors.toSet()); Map configContentMap = new HashMap<>(zipItemList.size()); int itemNameLength = 2; zipItemList.forEach(item -> { String itemName = item.getItemName(); String[] groupAdnDataId = itemName.split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR); if (groupAdnDataId.length != itemNameLength) { Map unrecognizedItem = new HashMap<>(2); unrecognizedItem.put("itemName", item.getItemName()); unrecognizedList.add(unrecognizedItem); return; } String group = groupAdnDataId[0]; String dataId = groupAdnDataId[1]; String key = GroupKey.getKey(dataId, group); // metadata does not contain config file if (!metaDataKeys.contains(key)) { Map unrecognizedItem = new HashMap<>(2); unrecognizedItem.put("itemName", "Item not found in metadata: " + item.getItemName()); unrecognizedList.add(unrecognizedItem); return; } String itemData = item.getItemData(); configContentMap.put(key, itemData); }); for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) { String dataId = configExportItem.getDataId(); String group = configExportItem.getGroup(); String content = configContentMap.get(GroupKey.getKey(dataId, group)); // config file not in metadata if (content == null) { Map unrecognizedItem = new HashMap<>(2); unrecognizedItem.put("itemName", "Item not found in file: " + group + "/" + dataId); unrecognizedList.add(unrecognizedItem); continue; } // encrypted Pair pair = EncryptionHandler.encryptHandler(dataId, content); content = pair.getSecond(); ConfigAllInfo ci = new ConfigAllInfo(); ci.setGroup(group); ci.setDataId(dataId); ci.setContent(content); ci.setType(configExportItem.getType()); ci.setDesc(configExportItem.getDesc()); ci.setAppName(configExportItem.getAppName()); ci.setTenant(namespaceId); ci.setEncryptedDataKey(pair.getFirst()); ci.setCreateUser(srcUser); configInfoList.add(ci); } return null; } /** * Export config add metadata.yml file record config metadata. */ @GetMapping("/export") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public ResponseEntity exportConfig(ConfigFormV3 configForm, @RequestParam(value = "ids", required = false) List ids) throws NacosApiException { configForm.blurSearchValidate(); ids.removeAll(Collections.singleton(null)); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); List dataList = configInfoPersistService.findAllConfigInfo4Export(configForm.getDataId(), configForm.getGroupName(), namespaceId, configForm.getAppName(), ids); List zipItemList = new ArrayList<>(); List configMetadataItems = new ArrayList<>(); for (ConfigAllInfo ci : dataList) { ConfigMetadata.ConfigExportItem configMetadataItem = new ConfigMetadata.ConfigExportItem(); configMetadataItem.setAppName(ci.getAppName()); configMetadataItem.setDataId(ci.getDataId()); configMetadataItem.setDesc(ci.getDesc()); configMetadataItem.setGroup(ci.getGroup()); configMetadataItem.setType(ci.getType()); configMetadataItems.add(configMetadataItem); Pair pair = EncryptionHandler.decryptHandler(ci.getDataId(), ci.getEncryptedDataKey(), ci.getContent()); String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId(); zipItemList.add(new ZipUtils.ZipItem(itemName, pair.getSecond())); } ConfigMetadata configMetadata = new ConfigMetadata(); configMetadata.setMetadata(configMetadataItems); zipItemList.add( new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, YamlParserUtil.dumpObject(configMetadata))); HttpHeaders headers = new HttpHeaders(); String fileName = EXPORT_CONFIG_FILE_NAME + DateFormatUtils.format(new Date(), EXPORT_CONFIG_FILE_NAME_DATE_FORMAT) + EXPORT_CONFIG_FILE_NAME_EXT; headers.add("Content-Disposition", "attachment;filename=" + fileName); return new ResponseEntity<>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK); } /** * Execute clone config operation. */ @PostMapping("/clone") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result> cloneConfig(HttpServletRequest request, @RequestParam(value = "src_user", required = false) String srcUser, @RequestParam(value = "namespaceId") String namespaceId, @RequestBody List cloneInfos, @RequestParam(value = "policy", defaultValue = "ABORT") SameConfigPolicy policy) throws NacosException { Map failedData = new HashMap<>(4); if (CollectionUtils.isEmpty(cloneInfos)) { failedData.put("succCount", 0); return Result.failure(ErrorCode.NO_SELECTED_CONFIG, failedData); } cloneInfos.removeAll(Collections.singleton(null)); namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); if (StringUtils.isNotBlank(namespaceId) && !NamespaceUtil.isDefaultNamespaceId(namespaceId) && namespacePersistService.tenantInfoCountByTenantId(namespaceId) <= 0) { failedData.put("succCount", 0); return Result.failure(ErrorCode.NAMESPACE_NOT_EXIST, failedData); } List idList = new ArrayList<>(cloneInfos.size()); Map configBeansMap = cloneInfos.stream() .collect(Collectors.toMap(ConfigCloneInfo::getConfigId, cfg -> { idList.add(cfg.getConfigId()); return cfg; }, (k1, k2) -> k1)); List queryedDataList = configInfoPersistService.findAllConfigInfo4Export(null, null, null, null, idList); if (queryedDataList == null || queryedDataList.isEmpty()) { failedData.put("succCount", 0); return Result.failure(ErrorCode.DATA_EMPTY, failedData); } List configInfoList4Clone = new ArrayList<>(queryedDataList.size()); for (ConfigAllInfo ci : queryedDataList) { ConfigCloneInfo paramBean = configBeansMap.get(ci.getId()); ConfigAllInfo ci4save = new ConfigAllInfo(); ci4save.setTenant(namespaceId); ci4save.setType(ci.getType()); ci4save.setGroup((paramBean != null && StringUtils.isNotBlank(paramBean.getTargetGroupName())) ? paramBean.getTargetGroupName() : ci.getGroup()); ci4save.setDataId((paramBean != null && StringUtils.isNotBlank(paramBean.getTargetDataId())) ? paramBean.getTargetDataId() : ci.getDataId()); ci4save.setContent(ci.getContent()); if (StringUtils.isNotBlank(ci.getAppName())) { ci4save.setAppName(ci.getAppName()); } ci4save.setDesc(ci.getDesc()); ci4save.setEncryptedDataKey( ci.getEncryptedDataKey() == null ? StringUtils.EMPTY : ci.getEncryptedDataKey()); configInfoList4Clone.add(ci4save); } if (StringUtils.isBlank(srcUser)) { srcUser = RequestUtil.getSrcUserName(request); } final String srcIp = getRemoteIp(request); String requestIpApp = RequestUtil.getAppName(request); final Timestamp time = TimeUtils.getCurrentTime(); Map saveResult = configInfoPersistService.batchInsertOrUpdate(configInfoList4Clone, srcUser, srcIp, null, policy); for (ConfigInfo configInfo : configInfoList4Clone) { ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), time.getTime())); ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), requestIpApp, time.getTime(), InetUtils.getSelfIP(), ConfigTraceService.PERSISTENCE_EVENT, ConfigTraceService.PERSISTENCE_TYPE_PUB, configInfo.getContent()); } return Result.success(saveResult); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/v3/ConfigOpenApiController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.core.context.RequestContext; import com.alibaba.nacos.core.context.RequestContextHolder; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import java.util.Objects; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE_UTF8; /** * Nacos config module client used HTTP Open API controller. * *

    * This open API is used for some program language which not support gRPC request and want to develop a application used * client to get remote configuration from Nacos. So this client used open API only provide specified feature API to get * specified configuration. Not support listen configuration with HTTP, please use gRPC request to listen * configuration. *

    * * @author xiweng.yy */ @RestController @RequestMapping(Constants.CONFIG_V3_CLIENT_API_PATH) @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class ConfigOpenApiController { private final ConfigQueryChainService configQueryChainService; public ConfigOpenApiController(ConfigQueryChainService configQueryChainService) { this.configQueryChainService = configQueryChainService; } @GetMapping @TpsControl(pointName = "ConfigQuery") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.OPEN_API) public Result getConfig(ConfigFormV3 configForm) throws NacosApiException, UnsupportedEncodingException { configForm.validate(); configForm.setNamespaceId(NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId())); RequestContext requestContext = RequestContextHolder.getContext(); String sourceIp = requestContext.getBasicContext().getAddressContext().getSourceIp(); ConfigQueryChainRequest chainRequest = buildQueryChainRequest(configForm, sourceIp); ConfigQueryChainResponse chainResponse = configQueryChainService.handle(chainRequest); if (Objects.isNull(chainResponse.getContent())) { traceQuery(configForm, chainResponse, requestContext, sourceIp, ConfigTraceService.PULL_TYPE_NOTFOUND); return Result.failure(ErrorCode.RESOURCE_NOT_FOUND); } traceQuery(configForm, chainResponse, requestContext, sourceIp, ConfigTraceService.PULL_TYPE_OK); return Result.success(transferToResult(chainResponse)); } private ConfigQueryChainRequest buildQueryChainRequest(ConfigFormV3 configForm, String sourceIp) { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setTenant(configForm.getNamespaceId()); request.setGroup(configForm.getGroup()); request.setDataId(configForm.getDataId()); Map appLabels = new HashMap<>(4); appLabels.put(BetaGrayRule.CLIENT_IP_LABEL, sourceIp); request.setAppLabels(appLabels); return request; } private ConfigQueryResponse transferToResult(ConfigQueryChainResponse chainResponse) throws UnsupportedEncodingException { ConfigQueryResponse result = new ConfigQueryResponse(); result.setMd5(chainResponse.getMd5()); result.setEncryptedDataKey(chainResponse.getEncryptedDataKey()); result.setContent(chainResponse.getContent()); result.setContentType(chainResponse.getConfigType()); result.setLastModified(chainResponse.getLastModified()); // Check if there is a matched gray rule if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY) { if (BetaGrayRule.TYPE_BETA.equals(chainResponse.getMatchedGray().getGrayRule().getType())) { result.setBeta(true); } else if (TagGrayRule.TYPE_TAG.equals(chainResponse.getMatchedGray().getGrayRule().getType())) { result.setTag(URLEncoder.encode(chainResponse.getMatchedGray().getRawGrayRule(), ENCODE_UTF8)); } } return result; } private void traceQuery(ConfigFormV3 configForm, ConfigQueryChainResponse chainResponse, RequestContext requestContext, String sourceIp, String pullType) { final long delayed = System.currentTimeMillis() - chainResponse.getLastModified(); ConfigTraceService.logPullEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), requestContext.getBasicContext().getApp(), chainResponse.getLastModified(), resolvePullEventType(chainResponse), pullType, delayed, sourceIp, false, "http"); } private String resolvePullEventType(ConfigQueryChainResponse chainResponse) { if (Objects.requireNonNull(chainResponse.getStatus()) == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY) { ConfigCacheGray matchedGray = chainResponse.getMatchedGray(); if (matchedGray != null) { return ConfigTraceService.PULL_EVENT + "-" + matchedGray.getGrayName(); } else { return ConfigTraceService.PULL_EVENT; } } return ConfigTraceService.PULL_EVENT; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/v3/ConfigOpsControllerV3.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.service.dump.DumpService; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.persistence.datasource.LocalDataSourceServiceImpl; import com.alibaba.nacos.persistence.model.event.DerbyImportEvent; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.sys.utils.ApplicationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.context.request.async.DeferredResult; import org.springframework.web.multipart.MultipartFile; import java.util.List; import java.util.Map; import java.util.Objects; /** * Configuration ops management. * * @author Nacos */ @NacosApi @RestController @RequestMapping(Constants.OPS_CONTROLLER_V3_ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class ConfigOpsControllerV3 { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigOpsControllerV3.class); private final DumpService dumpService; public ConfigOpsControllerV3(DumpService dumpService) { this.dumpService = dumpService; } /** * Manually trigger dump of a local configuration file. */ @PostMapping(value = "/localCache") @Secured(resource = Constants.OPS_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result updateLocalCacheFromStore() { LOGGER.info("start to dump all data from store."); try { dumpService.dumpAll(); return Result.success("Local cache updated from store successfully!"); } catch (Exception e) { LOGGER.error("[updateLocalCacheFromStore] ", e); return Result.failure(ErrorCode.SERVER_ERROR.getCode(), "Local cache updated from store failed!", e.getMessage()); } } @PutMapping(value = "/log") @Secured(resource = Constants.OPS_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result setLogLevel(@RequestParam String logName, @RequestParam String logLevel) { try { LogUtil.setLogLevel(logName, logLevel); return Result.success(String.format("Log level updated successfully! Module: %s, Log Level: %s", logName, logLevel)); } catch (Exception e) { LOGGER.error("Failed to set log level for module {} to {}", logName, logLevel, e); return Result.failure(ErrorCode.SERVER_ERROR.getCode(), String.format("Failed to set log level for module %s to %s: %s", logName, logLevel, e.getMessage()), null); } } /** * Can only run select statements and is a direct query to the native Derby database without any additional logic. * *

    * This API is used for maintainer of Nacos to do datasource management when using derby datasource. * So This API required ADMIN permission and need open switch `nacos.config.derby.ops.enabled=true`. *

    * * @param sql The query * @return {@link RestResult} */ @GetMapping(value = "/derby") @Secured(resource = Constants.OPS_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result derbyOps(@RequestParam(value = "sql") String sql) { String selectSign = "SELECT"; String limitSign = "ROWS FETCH NEXT"; String limit = " OFFSET 0 ROWS FETCH NEXT 1000 ROWS ONLY"; try { if (!DatasourceConfiguration.isEmbeddedStorage()) { return Result.failure(ErrorCode.SERVER_ERROR.getCode(), "The current storage mode is not Derby", null); } if (!ConfigCommonConfig.getInstance().isDerbyOpsEnabled()) { return Result.failure(ErrorCode.SERVER_ERROR.getCode(), "Derby ops is disabled, please set `nacos.config.derby.ops.enabled=true` to enabled this feature.", null); } LocalDataSourceServiceImpl dataSourceService = (LocalDataSourceServiceImpl) DynamicDataSource.getInstance() .getDataSource(); if (StringUtils.startsWithIgnoreCase(sql, selectSign)) { if (!StringUtils.containsIgnoreCase(sql, limitSign)) { sql += limit; } JdbcTemplate template = dataSourceService.getJdbcTemplate(); List> result = template.queryForList(sql); return Result.success(result); } return Result.failure(ErrorCode.SERVER_ERROR.getCode(), "Only query statements are allowed to be executed", null); } catch (Exception e) { LOGGER.error("Derby failed to execute sql: " + sql); return Result.failure(ErrorCode.SERVER_ERROR.getCode(), "Failed to execute sql: " + sql, null); } } /** * Import Derby data from other Derby database. * *

    mysqldump --defaults-file="XXX" --host=0.0.0.0 --protocol=tcp --user=XXX --extended-insert=FALSE \ * --complete-insert=TRUE \ --skip-triggers --no-create-info --skip-column-statistics "{SCHEMA}" "{TABLE_NAME}" * *

    * This API is used for maintainer of Nacos to do datasource management when using derby datasource. * So This API required ADMIN permission and need open switch `nacos.config.derby.ops.enabled=true`. *

    * * @param multipartFile {@link MultipartFile} * @return {@link DeferredResult} */ @PostMapping(value = "/derby/import") @Secured(resource = Constants.OPS_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public DeferredResult> importDerby(@RequestParam(value = "file") MultipartFile multipartFile) { DeferredResult> response = new DeferredResult<>(); if (!DatasourceConfiguration.isEmbeddedStorage()) { response.setResult(RestResultUtils.failed("Limited to embedded storage mode")); return convertToResult(response); } if (!ConfigCommonConfig.getInstance().isDerbyOpsEnabled()) { response.setResult(RestResultUtils.failed( "Derby ops is disabled, please set `nacos.config.derby.ops.enabled=true` to enabled this feature.")); return convertToResult(response); } DatabaseOperate databaseOperate = ApplicationUtils.getBean(DatabaseOperate.class); WebUtils.onFileUpload(multipartFile, file -> { NotifyCenter.publishEvent(new DerbyImportEvent(false)); databaseOperate.dataImport(file).whenComplete((result, ex) -> { NotifyCenter.publishEvent(new DerbyImportEvent(true)); if (Objects.nonNull(ex)) { response.setResult(RestResultUtils.failed(ex.getMessage())); return; } response.setResult(result); }); }, response); return convertToResult(response); } /** * Ensure backward compatibility. */ private DeferredResult> convertToResult(DeferredResult> restResult) { DeferredResult> wrappedResponse = new DeferredResult<>(); restResult.onCompletion(() -> { if (restResult.getResult() != null) { RestResult originalResult = (RestResult) restResult.getResult(); Result newResult = new Result<>(originalResult.getCode(), originalResult.getMessage(), originalResult.getData()); wrappedResponse.setResult(newResult); } }); return wrappedResponse; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/v3/HistoryControllerV3.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.service.HistoryService; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.config.server.utils.ResponseUtil; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.auth.exception.AccessException; import org.springframework.dao.DataAccessException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; /** * History configuration management. * * @author Nacos */ @NacosApi @RestController @RequestMapping(Constants.HISTORY_ADMIN_V3_PATH) @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class HistoryControllerV3 { private final HistoryService historyService; public HistoryControllerV3(HistoryService historyService) { this.historyService = historyService; } /** * Query the list history config. */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result> listConfigHistory(ConfigFormV3 configForm, PageForm pageForm) throws NacosApiException { configForm.validate(); pageForm.validate(); int pageSize = Math.min(500, pageForm.getPageSize()); int pageNo = pageForm.getPageNo(); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); Page configHistoryInfoPage = historyService.listConfigHistory(dataId, groupName, namespaceId, pageNo, pageSize); Page result = new Page<>(); result.setPagesAvailable(configHistoryInfoPage.getPagesAvailable()); result.setPageNumber(configHistoryInfoPage.getPageNumber()); result.setTotalCount(configHistoryInfoPage.getTotalCount()); result.setPageItems( configHistoryInfoPage.getPageItems().stream().map(ResponseUtil::transferToConfigHistoryBasicInfo) .collect(Collectors.toList())); return Result.success(result); } /** * Query the detailed configuration history information. */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result getConfigHistoryInfo(ConfigFormV3 configForm, @RequestParam("nid") Long nid) throws AccessException, NacosApiException { ConfigHistoryInfo configHistoryInfo; configForm.validate(); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); try { //fix issue #9783 namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); configHistoryInfo = historyService.getConfigHistoryInfo(dataId, groupName, namespaceId, nid); } catch (DataAccessException e) { throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, "certain config history for nid = " + nid + " not exist"); } return Result.success(ResponseUtil.transferToConfigHistoryDetailInfo(configHistoryInfo)); } /** * Query previous config history information. */ @GetMapping(value = "/previous") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result getPreviousConfigHistoryInfo(ConfigFormV3 configForm, @RequestParam("id") Long id) throws AccessException, NacosApiException { ConfigHistoryInfo configHistoryInfo; configForm.validate(); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); try { //fix issue #9783. namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); configHistoryInfo = historyService.getPreviousConfigHistoryInfo(dataId, groupName, namespaceId, id); } catch (DataAccessException e) { throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, "previous config history for id = " + id + " not exist"); } return Result.success(ResponseUtil.transferToConfigHistoryDetailInfo(configHistoryInfo)); } /** * Query configs list by namespace. */ @GetMapping(value = "/configs") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result> getConfigsByNamespace(@RequestParam("namespaceId") String namespaceId) throws NacosApiException { // check namespaceId ParamUtils.checkTenantV2(namespaceId); namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); List configListByNamespace = historyService.getConfigListByNamespace(namespaceId); List result = configListByNamespace.stream().map(ResponseUtil::transferToConfigBasicInfo) .toList(); return Result.success(result); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/v3/ListenerControllerV3.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.service.listener.ConfigListenerStateDelegate; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.core.model.form.AggregationForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; /** * Listener management. * * @author Nacos */ @NacosApi @RestController @RequestMapping(Constants.LISTENER_CONTROLLER_V3_ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class ListenerControllerV3 { private final ConfigListenerStateDelegate configListenerStateDelegate; public ListenerControllerV3(ConfigListenerStateDelegate configListenerStateDelegate) { this.configListenerStateDelegate = configListenerStateDelegate; } /** * Get subscribe information from client side. */ @GetMapping @Secured(signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result getAllSubClientConfigByIp(@RequestParam("ip") String ip, @RequestParam(value = "all", required = false) boolean all, @RequestParam(value = "namespaceId", required = false) String namespaceId, AggregationForm aggregationForm) { ConfigListenerInfo result = configListenerStateDelegate.getListenerStateByIp(ip, aggregationForm.isAggregation()); result.setQueryType(ConfigListenerInfo.QUERY_TYPE_IP); Map configMd5Status = new HashMap<>(100); if (result.getListenersStatus() == null || result.getListenersStatus().isEmpty()) { return Result.success(result); } Map status = result.getListenersStatus(); for (Map.Entry config : status.entrySet()) { if (!StringUtils.isBlank(namespaceId) && config.getKey().contains(namespaceId)) { configMd5Status.put(config.getKey(), config.getValue()); continue; } if (all) { configMd5Status.put(config.getKey(), config.getValue()); } else { String[] configKeys = GroupKey2.parseKey(config.getKey()); if (StringUtils.isBlank(configKeys[2])) { configMd5Status.put(config.getKey(), config.getValue()); } } } result.setListenersStatus(configMd5Status); return Result.success(result); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/controller/v3/MetricsControllerV3.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfigHolder; import com.alibaba.nacos.auth.util.AuthHeaderUtil; import com.alibaba.nacos.common.http.Callback; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.core.auth.NacosServerAuthConfig; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.utils.GenericType; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest.MetricsKey.CACHE_DATA; import static com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest.MetricsKey.SNAPSHOT_DATA; /** * Metric management. * * @author Nacos */ @NacosApi @RestController @RequestMapping(Constants.METRICS_CONTROLLER_V3_ADMIN_PATH) @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class MetricsControllerV3 { private final ServerMemberManager serverMemberManager; private final ConnectionManager connectionManager; public MetricsControllerV3(ServerMemberManager serverMemberManager, ConnectionManager connectionManager) { this.serverMemberManager = serverMemberManager; this.connectionManager = connectionManager; } /** * get client metric. */ @GetMapping("/cluster") @Secured(resource = Constants.METRICS_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result> metric(@RequestParam("ip") String ip, @RequestParam(value = "dataId", required = false) String dataId, @RequestParam(value = "groupName", required = false) String groupName, @RequestParam(value = "namespaceId", required = false) String namespaceId) throws NacosException { ParamUtils.checkTenant(namespaceId); namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); ParamUtils.checkParam(dataId, groupName, "default", "default"); Loggers.CORE.info("Get cluster config metrics received, ip={},dataId={},groupName={},namespaceId={}", ip, dataId, groupName, namespaceId); Map responseMap = new HashMap<>(3); Collection members = serverMemberManager.allMembers(); final NacosAsyncRestTemplate nacosAsyncRestTemplate = HttpClientBeanHolder.getNacosAsyncRestTemplate( Loggers.CLUSTER); CountDownLatch latch = new CountDownLatch(members.size()); for (Member member : members) { String url = HttpUtils.buildUrl(false, member.getAddress(), EnvUtil.getContextPath(), Constants.METRICS_CONTROLLER_V3_ADMIN_PATH, "ip"); Query query = Query.newInstance().addParam("ip", ip).addParam("dataId", dataId) .addParam("groupName", groupName).addParam("namespaceId", namespaceId); Header header = Header.newInstance(); AuthHeaderUtil.addIdentityToHeader(header, NacosAuthConfigHolder.getInstance() .getNacosAuthConfigByScope(NacosServerAuthConfig.NACOS_SERVER_AUTH_SCOPE)); nacosAsyncRestTemplate.get(url, header, query, new GenericType() { }.getType(), new ClusterMetricsCallBack(responseMap, latch, dataId, groupName, namespaceId, ip, member)); } try { latch.await(3L, TimeUnit.SECONDS); } catch (InterruptedException e) { e.printStackTrace(); } return Result.success(responseMap); } static class ClusterMetricsCallBack implements Callback { Map responseMap; CountDownLatch latch; String dataId; String group; String namespaceId; String ip; Member member; public ClusterMetricsCallBack(Map responseMap, CountDownLatch latch, String dataId, String group, String namespaceId, String ip, Member member) { this.responseMap = responseMap; this.latch = latch; this.dataId = dataId; this.group = group; this.namespaceId = namespaceId; this.member = member; this.ip = ip; } @Override public void onReceive(RestResult result) { if (result.ok()) { responseMap.putAll(result.getData()); } latch.countDown(); } @Override public void onError(Throwable throwable) { Loggers.CORE.error( "Get config metrics error from member address={}, ip={},dataId={},group={},namespaceId={},error={}", member.getAddress(), ip, dataId, group, namespaceId, throwable); latch.countDown(); } @Override public void onCancel() { latch.countDown(); } } /** * Get client config listener lists of subscriber in local machine. */ @GetMapping("/ip") @Secured(resource = Constants.METRICS_CONTROLLER_V3_ADMIN_PATH, action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.ADMIN_API) public Result> getClientMetrics(@RequestParam("ip") String ip, @RequestParam(value = "dataId", required = false) String dataId, @RequestParam(value = "groupName", required = false) String groupName, @RequestParam(value = "namespaceId", required = false) String namespaceId) throws NacosException { ParamUtils.checkTenant(namespaceId); namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); ParamUtils.checkParam(dataId, groupName, "default", "default"); Map metrics = new HashMap<>(16); List connectionsByIp = connectionManager.getConnectionByIp(ip); for (Connection connectionByIp : connectionsByIp) { try { ClientConfigMetricRequest clientMetrics = new ClientConfigMetricRequest(); if (StringUtils.isNotBlank(dataId)) { clientMetrics.getMetricsKeys().add(ClientConfigMetricRequest.MetricsKey.build(CACHE_DATA, GroupKey2.getKey(dataId, groupName, namespaceId))); clientMetrics.getMetricsKeys().add(ClientConfigMetricRequest.MetricsKey.build(SNAPSHOT_DATA, GroupKey2.getKey(dataId, groupName, namespaceId))); } ClientConfigMetricResponse request1 = (ClientConfigMetricResponse) connectionByIp.request(clientMetrics, 1000L); metrics.putAll(request1.getMetrics()); } catch (Exception e) { Loggers.CORE.error( "Get config metrics error from client ip={},dataId={},groupName={},namespaceId={},error={}", ip, dataId, groupName, namespaceId, e); throw new NacosException(NacosException.SERVER_ERROR, e); } } return Result.success(metrics); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/enums/ApiVersionEnum.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.enums; /** * Config Api Version enum. * @author Nacos */ public enum ApiVersionEnum { /** * API version v1. */ V1("v1"), /** * API version v2. */ V2("v2"); private final String version; ApiVersionEnum(String version) { this.version = version; } public String getVersion() { return version; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/enums/FileTypeEnum.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.enums; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.utils.StringUtils; /** * Config file type enum. * * @author klw * @date 2019/7/1 10:21 */ public enum FileTypeEnum { /** * Yaml file. */ YML("yaml", MediaType.TEXT_PLAIN), /** * Yaml file. */ YAML("yaml", MediaType.TEXT_PLAIN), /** * Text file. */ TXT("text", MediaType.TEXT_PLAIN), /** * Text file. */ TEXT("text", MediaType.TEXT_PLAIN), /** * Json file. */ JSON("json", MediaType.APPLICATION_JSON), /** * Xml file. */ XML("xml", MediaType.APPLICATION_XML), /** * Html file. */ HTM("html", MediaType.TEXT_HTML), /** * Html file. */ HTML("html", MediaType.TEXT_HTML), /** * Properties file. */ PROPERTIES("properties", MediaType.TEXT_PLAIN); /** * File type corresponding to file extension. */ private String fileType; /** * Http Content type corresponding to file extension. */ private String contentType; FileTypeEnum(String fileType) { this.fileType = fileType; this.contentType = MediaType.TEXT_PLAIN; } FileTypeEnum(String fileType, String contentType) { this.fileType = fileType; this.contentType = contentType; } public String getFileType() { return this.fileType; } public String getContentType() { return contentType; } /** * Get the corresponding FileTypeEnum by file extension or fileType. If not found FileTypeEnum.TEXT is returned * * @param extOrFileType file extension or fileType * @return */ public static FileTypeEnum getFileTypeEnumByFileExtensionOrFileType(String extOrFileType) { if (StringUtils.isNotBlank(extOrFileType)) { String upperExtName = extOrFileType.trim().toUpperCase(); for (FileTypeEnum value : VALUES) { if (value.name().equals(upperExtName)) { return value; } } } return FileTypeEnum.TEXT; } private static final FileTypeEnum[] VALUES = FileTypeEnum.values(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/enums/OperationType.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.enums; /** * Operation type enum. * * @author dirtybit */ public enum OperationType { /** * Insert. */ INSERT("I"), /** * Update. */ UPDATE("U"), /** * Delete. */ DELETE("D"); /** * operation type value. */ private String value; OperationType(String value) { this.value = value; } public void setValue(String value) { this.value = value; } public String getValue() { return this.value; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/exception/ConfigAlreadyExistsException.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.exception; import com.alibaba.nacos.api.exception.NacosException; /** * ConfigAlreadyExistsException. * * @author Nacos */ public class ConfigAlreadyExistsException extends NacosException { private static final long serialVersionUID = -8247262927932720692L; ConfigAlreadyExistsException() { super(); } public ConfigAlreadyExistsException(int errCode, String errMsg) { super(errCode, errMsg); } public ConfigAlreadyExistsException(String errMsg) { super(NacosException.CONFIG_ALREADY_EXISTS, errMsg); } public ConfigAlreadyExistsException(int errCode, Throwable throwable) { super(errCode, throwable); } public ConfigAlreadyExistsException(int errCode, String errMsg, Throwable throwable) { super(errCode, errMsg, throwable); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.exception; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.persistence.monitor.DatasourceMetrics; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.dao.DataAccessException; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import java.io.IOException; /** * Global exception handler. * * @author Nacos */ @ControllerAdvice(basePackages = "com.alibaba.nacos.config.server") @Order(Ordered.LOWEST_PRECEDENCE - 1) public class GlobalExceptionHandler { /** * For IllegalArgumentException, we are returning void with status code as 400, so our error-page will be used in * this case. * * @throws IllegalArgumentException IllegalArgumentException. */ @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity handleIllegalArgumentException(Exception ex) throws IOException { MetricsMonitor.getIllegalArgumentException().increment(); return ResponseEntity.status(400).body(ExceptionUtil.getAllExceptionMsg(ex)); } /** * For NacosRuntimeException. * * @throws com.alibaba.nacos.api.exception.runtime.NacosRuntimeException NacosRuntimeException. */ @ExceptionHandler(NacosRuntimeException.class) public ResponseEntity handleNacosRunTimeException(NacosRuntimeException ex) throws IOException { MetricsMonitor.getNacosException().increment(); return ResponseEntity.status(ex.getErrCode()).body(ExceptionUtil.getAllExceptionMsg(ex)); } /** * For NacosException. * * @throws NacosException NacosException. */ @ExceptionHandler(NacosException.class) public ResponseEntity handleNacosException(NacosException ex) throws IOException { MetricsMonitor.getNacosException().increment(); return ResponseEntity.status(ex.getErrCode()).body(ExceptionUtil.getAllExceptionMsg(ex)); } /** * For DataAccessException. * * @throws DataAccessException DataAccessException. */ @ExceptionHandler(DataAccessException.class) public ResponseEntity handleDataAccessException(DataAccessException ex) throws DataAccessException { DatasourceMetrics.getDbException().increment(); return ResponseEntity.status(500).body(ExceptionUtil.getAllExceptionMsg(ex)); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/exception/NacosConfigException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.exception; /** * NacosConfigException. * * @author liaochuntao */ public class NacosConfigException extends RuntimeException { public NacosConfigException() { } public NacosConfigException(String message) { super(message); } public NacosConfigException(String message, Throwable cause) { super(message, cause); } public NacosConfigException(Throwable cause) { super(cause); } public NacosConfigException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/filter/CircuitFilter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.filter; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.persistence.model.event.RaftDbErrorEvent; import com.alibaba.nacos.config.server.model.event.RaftDbErrorRecoverEvent; import com.alibaba.nacos.consistency.ProtocolMetaData; import com.alibaba.nacos.consistency.cp.CPProtocol; import com.alibaba.nacos.consistency.cp.MetadataKey; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.MemberMetaDataConstants; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.persistence.constants.PersistenceConstant; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.security.AccessControlException; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; /** * If the embedded distributed storage is enabled, all requests are routed to the Leader node for processing, and the * maximum number of forwards for a single request cannot exceed three. * * @author liaochuntao */ public class CircuitFilter implements Filter { private volatile boolean isDowngrading = false; private volatile boolean isOpenService = false; @Autowired private ServerMemberManager memberManager; @Autowired private CPProtocol protocol; @PostConstruct protected void init() { listenerSelfInCluster(); registerSubscribe(); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; if (!isOpenService) { resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "In the node initialization, unable to process any requests at this time"); return; } try { // If an unrecoverable exception occurs on this node, the write request operation shall not be processed // This is a very important warning message !!! if (isDowngrading) { resp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Unable to process the request at this time: System triggered degradation"); return; } chain.doFilter(req, response); } catch (AccessControlException e) { resp.sendError(HttpServletResponse.SC_FORBIDDEN, "access denied: " + ExceptionUtil.getAllExceptionMsg(e)); } catch (Throwable e) { DEFAULT_LOG.warn("[CURCUIT-FILTER] Server failed: ", e); resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Server failed, " + e); } } @Override public void destroy() { } private void listenerSelfInCluster() { protocol.protocolMetaData().subscribe(PersistenceConstant.CONFIG_MODEL_RAFT_GROUP, MetadataKey.RAFT_GROUP_MEMBER, o -> { if (!(o instanceof ProtocolMetaData.ValueItem)) { return; } final List peers = (List) ((ProtocolMetaData.ValueItem) o).getData(); if (CollectionUtils.isEmpty(peers)) { isOpenService = false; return; } final Member self = memberManager.getSelf(); final String raftAddress = self.getIp() + ":" + self.getExtendVal(MemberMetaDataConstants.RAFT_PORT); // Only when you are in the cluster and the current Leader is // elected can you provide external services isOpenService = peers.contains(raftAddress); }); } private void registerSubscribe() { NotifyCenter.registerSubscriber(new SmartSubscriber() { @Override public void onEvent(Event event) { // @JustForTest // This event only happens in the case of unit tests if (event instanceof RaftDbErrorRecoverEvent) { isDowngrading = false; return; } if (event instanceof RaftDbErrorEvent) { isDowngrading = true; } } @Override public List> subscribeTypes() { return Arrays.asList(RaftDbErrorRecoverEvent.class, RaftDbErrorEvent.class); } }); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/filter/ConfigEnabledFilter.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.filter; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.Config; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter; import java.util.Set; import static com.alibaba.nacos.sys.env.EnvUtil.FUNCTION_MODE_CONFIG; /** * Config module enabled filter by spring packages scan. * * @author xiweng.yy */ public class ConfigEnabledFilter implements NacosPackageExcludeFilter { @Override public String getResponsiblePackagePrefix() { return Config.class.getPackage().getName(); } @Override public boolean isExcluded(String className, Set annotationNames) { String functionMode = EnvUtil.getFunctionMode(); // When not specified config mode or specified all mode, the config module not start and load. if (StringUtils.isEmpty(functionMode)) { return false; } return !FUNCTION_MODE_CONFIG.equals(functionMode); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/filter/NacosWebFilter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.filter; import com.alibaba.nacos.config.server.constant.Constants; import jakarta.servlet.Filter; import jakarta.servlet.FilterChain; import jakarta.servlet.FilterConfig; import jakarta.servlet.ServletContext; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import java.io.IOException; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; /** * Web encode filter. * * @author Nacos */ public class NacosWebFilter implements Filter { private static String webRootPath; public static String rootPath() { return webRootPath; } /** * Easy for testing. * * @param path web path. */ public static void setWebRootPath(String path) { webRootPath = path; } @Override public void init(FilterConfig filterConfig) throws ServletException { ServletContext ctx = filterConfig.getServletContext(); setWebRootPath(ctx.getRealPath("/")); } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { request.setCharacterEncoding(Constants.ENCODE); response.setContentType("application/json;charset=" + Constants.ENCODE); try { chain.doFilter(request, response); } catch (IOException | ServletException ioe) { DEFAULT_LOG.debug("Filter catch exception, " + ioe.toString(), ioe); throw ioe; } } @Override public void destroy() { } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.manager; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.task.AbstractDelayTask; import com.alibaba.nacos.common.task.engine.NacosDelayTaskExecuteEngine; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.utils.LogUtil; import org.slf4j.Logger; import javax.management.ObjectName; import java.lang.management.ManagementFactory; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; /** * TaskManager, is aim to process the task which is need to be done. And this class process the task by single thread to * ensure task should be process successfully. * * @author huali */ public final class TaskManager extends NacosDelayTaskExecuteEngine implements TaskManagerMBean { private static final Logger LOGGER = LogUtil.DEFAULT_LOG; private String name; Condition notEmpty = this.lock.newCondition(); public TaskManager(String name) { super(name, 32, LOGGER, 100L); this.name = name; } /** * Close task manager. */ public void close() { try { super.shutdown(); } catch (NacosException ignored) { } } /** * Await for lock. * * @throws InterruptedException InterruptedException. */ public void await() throws InterruptedException { this.lock.lock(); try { while (!this.isEmpty()) { this.notEmpty.await(); } } finally { this.lock.unlock(); } } /** * Await for lock by timeout. * * @param timeout timeout value. * @param unit time unit. * @return success or not. * @throws InterruptedException InterruptedException. */ public boolean await(long timeout, TimeUnit unit) throws InterruptedException { this.lock.lock(); boolean isawait = false; try { while (!this.isEmpty()) { isawait = this.notEmpty.await(timeout, unit); } return isawait; } finally { this.lock.unlock(); } } @Override public void addTask(Object key, AbstractDelayTask newTask) { super.addTask(key, newTask); MetricsMonitor.getDumpTaskMonitor().set(tasks.size()); } @Override public AbstractDelayTask removeTask(Object key) { AbstractDelayTask result = super.removeTask(key); MetricsMonitor.getDumpTaskMonitor().set(tasks.size()); return result; } @Override protected void processTasks() { super.processTasks(); MetricsMonitor.getDumpTaskMonitor().set(tasks.size()); if (tasks.isEmpty()) { this.lock.lock(); try { this.notEmpty.signalAll(); } finally { this.lock.unlock(); } } } @Override public String getTaskInfos() { StringBuilder sb = new StringBuilder(); for (Object taskType : getAllProcessorKey()) { sb.append(taskType).append(':'); AbstractDelayTask task = this.tasks.get(taskType); if (task != null) { sb.append(new Date(task.getLastProcessTime())); } else { sb.append("finished"); } sb.append(Constants.NACOS_LINE_SEPARATOR); } return sb.toString(); } /** * Init and register the mbean object. */ public void init() { try { ObjectName oName = new ObjectName(this.name + ":type=" + TaskManager.class.getSimpleName()); ManagementFactory.getPlatformMBeanServer().registerMBean(this, oName); } catch (Exception e) { LOGGER.error("registerMBean_fail", e); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/manager/TaskManagerMBean.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.manager; /** * TaskManagerMBean. * * @author Nacos */ public interface TaskManagerMBean { /** * Get task info. * * @return info */ String getTaskInfos(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/AclInfo.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.List; /** * Acl info. * * @author Nacos */ public class AclInfo implements Serializable { private static final long serialVersionUID = 1383026926036269457L; private Boolean isOpen; private List ips; public List getIps() { return ips; } public void setIps(List ips) { this.ips = ips; } public Boolean getIsOpen() { return isOpen; } public void setIsOpen(Boolean isOpen) { this.isOpen = isOpen; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/CacheItem.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.config.server.utils.SimpleReadWriteLock; import com.alibaba.nacos.core.utils.StringPool; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * Cache item. * * @author Nacos */ public class CacheItem { final String groupKey; public String type; ConfigCache configCache = ConfigCacheFactoryDelegate.getInstance().createConfigCache(); /** * Use for gray. */ private volatile Map configCacheGray = null; List sortedConfigCacheGrayList = null; private final SimpleReadWriteLock rwLock = new SimpleReadWriteLock(); public CacheItem(String groupKey, String encryptedDataKey) { this.groupKey = StringPool.get(groupKey); this.getConfigCache().setEncryptedDataKey(encryptedDataKey); } public CacheItem(String groupKey) { this.groupKey = StringPool.get(groupKey); } public ConfigCache getConfigCache() { return configCache; } public SimpleReadWriteLock getRwLock() { return rwLock; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getGroupKey() { return groupKey; } /** * init config gray if empty. */ public void initConfigGrayIfEmpty() { if (this.configCacheGray == null) { this.configCacheGray = new HashMap<>(4); } } /** * init config gray if empty. * * @param grayName gray name. */ public void initConfigGrayIfEmpty(String grayName) { initConfigGrayIfEmpty(); if (!this.configCacheGray.containsKey(grayName)) { this.configCacheGray.put(grayName, ConfigCacheFactoryDelegate.getInstance().createConfigCacheGray(grayName)); } } public List getSortConfigGrays() { return sortedConfigCacheGrayList; } /** * sort config gray. */ public void sortConfigGray() { if (configCacheGray == null || configCacheGray.isEmpty()) { sortedConfigCacheGrayList = null; return; } sortedConfigCacheGrayList = configCacheGray.values().stream().sorted((o1, o2) -> { if (o1.getPriority() != o2.getPriority()) { return Integer.compare(o1.getPriority(), o2.getPriority()) * -1; } else { return o1.getGrayName().compareTo(o2.getGrayName()); } }).collect(Collectors.toList()); } public Map getConfigCacheGray() { return configCacheGray; } public void clearConfigGrays() { this.configCacheGray = null; this.sortedConfigCacheGrayList = null; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAdvanceInfo.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Objects; /** * Config advance info. * * @author Nacos */ public class ConfigAdvanceInfo implements Serializable { static final long serialVersionUID = 3148031484920416869L; private long createTime; private long modifyTime; private String createUser; private String createIp; private String desc; private String use; private String effect; private String type; private String schema; private String configTags; public long getCreateTime() { return createTime; } public void setCreateTime(long createTime) { this.createTime = createTime; } public long getModifyTime() { return modifyTime; } public void setModifyTime(long modifyTime) { this.modifyTime = modifyTime; } public String getCreateUser() { return createUser; } public void setCreateUser(String createUser) { this.createUser = createUser; } public String getCreateIp() { return createIp; } public void setCreateIp(String createIp) { this.createIp = createIp; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getUse() { return use; } public void setUse(String use) { this.use = use; } public String getEffect() { return effect; } public void setEffect(String effect) { this.effect = effect; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } public String getConfigTags() { return configTags; } public void setConfigTags(String configTags) { this.configTags = configTags; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigAdvanceInfo that = (ConfigAdvanceInfo) o; return createTime == that.createTime && modifyTime == that.modifyTime && Objects.equals(createUser, that.createUser) && Objects.equals(createIp, that.createIp) && Objects.equals(desc, that.desc) && Objects.equals(use, that.use) && Objects.equals(effect, that.effect) && Objects.equals(type, that.type) && Objects.equals(schema, that.schema) && Objects.equals(configTags, that.configTags); } @Override public int hashCode() { return Objects.hash(createTime, modifyTime, createUser, createIp, desc, use, effect, type, schema, configTags); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigAllInfo.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigAllInfo. * * @author Nacos */ public class ConfigAllInfo extends ConfigInfo { private static final long serialVersionUID = 296578467953931353L; private long createTime; private long modifyTime; private String createUser; private String createIp; private String use; private String effect; private String schema; public ConfigAllInfo() { } public long getCreateTime() { return createTime; } public void setCreateTime(long createTime) { this.createTime = createTime; } public long getModifyTime() { return modifyTime; } public void setModifyTime(long modifyTime) { this.modifyTime = modifyTime; } public String getCreateUser() { return createUser; } public void setCreateUser(String createUser) { this.createUser = createUser; } public String getCreateIp() { return createIp; } public void setCreateIp(String createIp) { this.createIp = createIp; } public String getUse() { return use; } public void setUse(String use) { this.use = use; } public String getEffect() { return effect; } public void setEffect(String effect) { this.effect = effect; } public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCache.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.core.utils.StringPool; import java.io.Serializable; /** * config cache . * * @author shiyiyue1102 */ public class ConfigCache implements Serializable { volatile String md5 = Constants.NULL; volatile String encryptedDataKey; volatile long lastModifiedTs; /** * clear cache. */ public void clear() { this.md5 = Constants.NULL; this.encryptedDataKey = null; this.lastModifiedTs = -1L; } public ConfigCache() { } public ConfigCache(String md5, long lastModifiedTs) { this.md5 = StringPool.get(md5); this.lastModifiedTs = lastModifiedTs; } public String getMd5() { return md5; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } public void setMd5(String md5) { this.md5 = StringPool.get(md5); } public long getLastModifiedTs() { return lastModifiedTs; } public void setLastModifiedTs(long lastModifiedTs) { this.lastModifiedTs = lastModifiedTs; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCacheFactory.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * The interface Config cache factory. * * @author Sunrisea */ public interface ConfigCacheFactory { /** * Create config cache config cache. * * @return the config cache */ public ConfigCache createConfigCache(); /** * Create config cache gray config cache gray. * * @return the config cache gray */ public ConfigCacheGray createConfigCacheGray(); /** * Gets config cache factroy name. * * @return the config cache factory name */ public String getName(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCacheFactoryDelegate.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; /** * The type Config cache factory delegate. * * @author Sunrisea */ public class ConfigCacheFactoryDelegate { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCacheFactoryDelegate.class); private static final ConfigCacheFactoryDelegate INSTANCE = new ConfigCacheFactoryDelegate(); private String configCacheFactoryType = EnvUtil.getProperty("nacos.config.cache.type", "nacos"); private ConfigCacheFactory configCacheFactory = null; private ConfigCacheFactoryDelegate() { Collection configCacheFactories = NacosServiceLoader.load(ConfigCacheFactory.class); for (ConfigCacheFactory each : configCacheFactories) { if (StringUtils.isEmpty(each.getName())) { LOGGER.warn( "[ConfigCacheFactoryDelegate] Load ConfigCacheFactory({}) ConfigFactroyName (null/empty) fail. " + "Please add ConfigFactoryName to resolve", each.getClass().getName()); continue; } LOGGER.info( "[ConfigCacheFactoryDelegate] Load ConfigCacheFactory({}) ConfigCacheFactoryName({}) successfully. ", each.getClass().getName(), each.getName()); if (StringUtils.equals(configCacheFactoryType, each.getName())) { LOGGER.info("[ConfigCacheFactoryDelegate] Matched ConfigCacheFactory found,set configCacheFactory={}", each.getClass().getName()); this.configCacheFactory = each; } } if (this.configCacheFactory == null) { LOGGER.info( "[ConfigCacheFactoryDelegate] Matched ConfigCacheFactory not found, Load Default NacosConfigCacheFactory successfully."); this.configCacheFactory = new NacosConfigCacheFactory(); } } /** * Gets instance. * * @return the instance */ public static ConfigCacheFactoryDelegate getInstance() { return INSTANCE; } /** * Create config cache config cache. * * @return the config cache */ public ConfigCache createConfigCache() { return configCacheFactory.createConfigCache(); } /** * Create config cache config cache. * * @param md5 the md 5 * @param lastModifiedTs the last modified ts * @return the config cache */ public ConfigCache createConfigCache(String md5, long lastModifiedTs) { ConfigCache configCache = this.createConfigCache(); configCache.setMd5(md5); configCache.setLastModifiedTs(lastModifiedTs); return configCache; } /** * Create config cache gray config cache gray. * * @return the config cache gray */ public ConfigCacheGray createConfigCacheGray() { return configCacheFactory.createConfigCacheGray(); } /** * Create config cache gray config cache gray. * * @param grayName the gray name * @return the config cache gray */ public ConfigCacheGray createConfigCacheGray(String grayName) { ConfigCacheGray configCacheGray = configCacheFactory.createConfigCacheGray(); configCacheGray.setGrayName(grayName); return configCacheGray; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCacheGray.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.config.server.model.gray.GrayRule; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import java.io.Serializable; import java.util.Map; /** * extensible config cache. * * @author rong */ public class ConfigCacheGray extends ConfigCache implements Serializable { private String grayName; private GrayRule grayRule; /** * clear cache. */ @Override public void clear() { super.clear(); } public ConfigCacheGray() {} public ConfigCacheGray(String grayName) { this.grayName = grayName; } public GrayRule getGrayRule() { return grayRule; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } /** * get raw gray rule from db. * * @return raw gray rule from db. * @date 2024/3/14 */ public String getRawGrayRule() { return grayRule.getRawGrayRuleExp(); } /** * reset gray rule. * * @param grayRule raw gray rule from db. * @throws RuntimeException if gray rule is invalid. * @date 2024/3/14 */ public void resetGrayRule(String grayRule) throws RuntimeException { this.grayRule = GrayRuleManager.constructGrayRule(GrayRuleManager.deserializeConfigGrayPersistInfo(grayRule)); if (this.grayRule == null || !this.grayRule.isValid()) { throw new RuntimeException("raw gray rule is invalid"); } } /** * judge whether match gray rule. * * @param tags conn tags. * @return true if match, false otherwise. * @date 2024/3/14 */ public boolean match(Map tags) { return grayRule.match(tags); } public int getPriority() { return grayRule.getPriority(); } /** * if gray rule is valid. * * @return true if valid, false otherwise. * @date 2024/3/14 */ public boolean isValid() { return grayRule != null && grayRule.isValid(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessor.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * The interface Config cache md5 post processor. * * @author Sunrisea */ public interface ConfigCachePostProcessor { /** * Gets post processor name. * * @return the post processor name */ public String getName(); /** * Post process. * * @param configCache the config cache * @param content the content */ public void postProcess(ConfigCache configCache, String content); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegate.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; /** * The type Config cache md5 post processor delegate. * * @author Sunrisea */ public class ConfigCachePostProcessorDelegate { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigCacheFactoryDelegate.class); private static ConfigCachePostProcessorDelegate instance = new ConfigCachePostProcessorDelegate(); private String configCacheMd5PostProcessorType = EnvUtil.getProperty("nacos.config.cache.type", "nacos"); private ConfigCachePostProcessor configCachePostProcessor; private ConfigCachePostProcessorDelegate() { Collection processors = NacosServiceLoader.load(ConfigCachePostProcessor.class); for (ConfigCachePostProcessor processor : processors) { if (StringUtils.isEmpty(processor.getName())) { LOGGER.warn( "[ConfigCachePostProcessorDelegate] Load ConfigCachePostProcessor({}) PostProcessorName(null/empty) fail. " + "Please add PostProcessorName to resolve", processor.getClass().getName()); continue; } LOGGER.info( "[ConfigCachePostProcessorDelegate] Load ConfigCachePostProcessor({}) PostProcessorName({}) successfully. ", processor.getClass().getName(), processor.getName()); if (StringUtils.equals(configCacheMd5PostProcessorType, processor.getName())) { LOGGER.info( "[ConfigCachePostProcessorDelegate] Matched ConfigCachePostProcessor found,set configCacheFactory={}", processor.getClass().getName()); this.configCachePostProcessor = processor; } } if (configCachePostProcessor == null) { LOGGER.info( "[ConfigCachePostProcessorDelegate] Matched ConfigCachePostProcessor not found, " + "load Default NacosConfigCachePostProcessor successfully"); configCachePostProcessor = new NacosConfigCachePostProcessor(); } } public static ConfigCachePostProcessorDelegate getInstance() { return instance; } public void postProcess(ConfigCache configCache, String content) { configCachePostProcessor.postProcess(configCache, content); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigHistoryInfo.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import java.io.Serializable; import java.sql.Timestamp; import java.util.Objects; /** * ConfigHistoryInfo. * * @author Nacos */ public class ConfigHistoryInfo implements Serializable { private static final long serialVersionUID = -7827521105376245603L; @JsonSerialize(using = ToStringSerializer.class) private long id; private long lastId = -1; private String dataId; private String group; private String tenant; private String appName; private String md5; private String content; private String srcIp; private String srcUser; /** * Operation type, include inserting, updating and deleting. */ private String opType; private String publishType; private String grayName; private String extInfo; private Timestamp createdTime; private Timestamp lastModifiedTime; private String encryptedDataKey; public long getId() { return id; } public void setId(long id) { this.id = id; } public long getLastId() { return lastId; } public void setLastId(long lastId) { this.lastId = lastId; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getSrcIp() { return srcIp; } public void setSrcIp(String srcIp) { this.srcIp = srcIp; } public String getSrcUser() { return srcUser; } public void setSrcUser(String srcUser) { this.srcUser = srcUser; } public String getOpType() { return opType; } public void setOpType(String opType) { this.opType = opType; } public String getPublishType() { return publishType; } public void setPublishType(String publishType) { this.publishType = publishType; } public String getExtInfo() { return extInfo; } public void setExtInfo(String extInfo) { this.extInfo = extInfo; } public Timestamp getCreatedTime() { return new Timestamp(createdTime.getTime()); } public void setCreatedTime(Timestamp createdTime) { this.createdTime = new Timestamp(createdTime.getTime()); } public Timestamp getLastModifiedTime() { return new Timestamp(lastModifiedTime.getTime()); } public void setLastModifiedTime(Timestamp lastModifiedTime) { this.lastModifiedTime = new Timestamp(lastModifiedTime.getTime()); } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigHistoryInfo that = (ConfigHistoryInfo) o; return id == that.id && lastId == that.lastId && Objects.equals(dataId, that.dataId) && Objects.equals(group, that.group) && Objects.equals(tenant, that.tenant) && Objects.equals(appName, that.appName) && Objects.equals(md5, that.md5) && Objects.equals(content, that.content) && Objects.equals(srcIp, that.srcIp) && Objects.equals(srcUser, that.srcUser) && Objects.equals(opType, that.opType) && Objects.equals(createdTime, that.createdTime) && Objects.equals(lastModifiedTime, that.lastModifiedTime) && Objects.equals(encryptedDataKey, that.encryptedDataKey); } @Override public int hashCode() { return Objects.hash(id, lastId, dataId, group, tenant, appName, md5, content, srcIp, srcUser, opType, createdTime, lastModifiedTime, encryptedDataKey); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigHistoryInfoDetail.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.sql.Timestamp; /** * ConfigHistoryInfoPair. * * @author dirtybit */ public class ConfigHistoryInfoDetail implements Serializable { private static final long serialVersionUID = -7827521105376245603L; private long id; private long lastId = -1; private String dataId; private String group; private String tenant; /** * Operation type, include inserting, updating and deleting. */ private String opType; private String publishType; private String grayName; private String appName; private String srcIp; private String srcUser; private String originalMd5; private String originalContent; private String originalEncryptedDataKey; private String originalExtInfo; private String updatedMd5; private String updatedContent; private String updatedEncryptedDataKey; private String updateExtInfo; private Timestamp createdTime; private Timestamp lastModifiedTime; public long getId() { return id; } public void setId(long id) { this.id = id; } public long getLastId() { return lastId; } public void setLastId(long lastId) { this.lastId = lastId; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public String getOpType() { return opType; } public void setOpType(String opType) { this.opType = opType; } public String getPublishType() { return publishType; } public void setPublishType(String publishType) { this.publishType = publishType; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getSrcIp() { return srcIp; } public void setSrcIp(String srcIp) { this.srcIp = srcIp; } public String getSrcUser() { return srcUser; } public void setSrcUser(String srcUser) { this.srcUser = srcUser; } public String getOriginalMd5() { return originalMd5; } public void setOriginalMd5(String originalMd5) { this.originalMd5 = originalMd5; } public String getOriginalContent() { return originalContent; } public void setOriginalContent(String originalContent) { this.originalContent = originalContent; } public String getOriginalEncryptedDataKey() { return originalEncryptedDataKey; } public void setOriginalEncryptedDataKey(String originalEncryptedDataKey) { this.originalEncryptedDataKey = originalEncryptedDataKey; } public String getOriginalExtInfo() { return originalExtInfo; } public void setOriginalExtInfo(String originalExtInfo) { this.originalExtInfo = originalExtInfo; } public String getUpdatedMd5() { return updatedMd5; } public void setUpdatedMd5(String updatedMd5) { this.updatedMd5 = updatedMd5; } public String getUpdatedContent() { return updatedContent; } public void setUpdatedContent(String updatedContent) { this.updatedContent = updatedContent; } public String getUpdatedEncryptedDataKey() { return updatedEncryptedDataKey; } public void setUpdatedEncryptedDataKey(String updatedEncryptedDataKey) { this.updatedEncryptedDataKey = updatedEncryptedDataKey; } public String getUpdateExtInfo() { return updateExtInfo; } public void setUpdateExtInfo(String updateExtInfo) { this.updateExtInfo = updateExtInfo; } public Timestamp getCreatedTime() { return createdTime; } public void setCreatedTime(Timestamp createdTime) { this.createdTime = createdTime; } public Timestamp getLastModifiedTime() { return lastModifiedTime; } public void setLastModifiedTime(Timestamp lastModifiedTime) { this.lastModifiedTime = lastModifiedTime; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfo. * * @author boyan * @date 2010-5-4 */ public class ConfigInfo extends ConfigInfoBase { static final long serialVersionUID = 3115358782431229202L; private String tenant; private String appName; private String type; private String desc; private String configTags; private Long gmtModified; public ConfigInfo() { } public ConfigInfo(String dataId, String group, String content) { super(dataId, group, content); } public ConfigInfo(String dataId, String group, String appName, String content) { super(dataId, group, content); this.appName = appName; } public ConfigInfo(String dataId, String group, String tenant, String appName, String content) { super(dataId, group, content); this.tenant = tenant; this.appName = appName; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getConfigTags() { return configTags; } public void setConfigTags(String configTags) { this.configTags = configTags; } public Long getGmtModified() { return gmtModified; } public void setGmtModified(Long gmtModified) { this.gmtModified = gmtModified; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } @Override public String toString() { return "ConfigInfo{" + "id=" + getId() + ", dataId='" + getDataId() + '\'' + ", group='" + getGroup() + '\'' + ", tenant='" + tenant + '\'' + ", appName='" + appName + '\'' + ", content='" + getContent() + '\'' + ", md5='" + getMd5() + '\'' + ", type='" + type + '\'' + ", desc='" + desc + '\'' + ", configTags='" + configTags + '\'' + '}'; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Beta.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfo4Beta. * * @author Nacos */ public class ConfigInfo4Beta extends ConfigInfo { private static final long serialVersionUID = 296578467953931353L; private String betaIps; public ConfigInfo4Beta() { } public ConfigInfo4Beta(String dataId, String group, String appName, String content, String betaIps) { super(dataId, group, appName, content); this.betaIps = betaIps; } public String getBetaIps() { return betaIps; } public void setBetaIps(String betaIps) { this.betaIps = betaIps; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfo4Tag.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfo4Tag. * * @author Nacos */ public class ConfigInfo4Tag extends ConfigInfo { private static final long serialVersionUID = 296578467953931353L; private String tag; public ConfigInfo4Tag() { } public ConfigInfo4Tag(String dataId, String group, String tag, String appName, String content) { super(dataId, group, appName, content); this.tag = tag; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBase.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import java.io.PrintWriter; import java.io.Serializable; import java.util.Objects; /** * ConfigInfoBase. * And can't add field, to compatible with old interface(If adding a field, then it will occur compatibility problems). * * @author Nacos */ public class ConfigInfoBase implements Serializable, Comparable { static final long serialVersionUID = 265316491795790798L; @JsonSerialize(using = ToStringSerializer.class) private long id; private String dataId; private String group; private String content; private String md5; private String encryptedDataKey; public ConfigInfoBase() { } public ConfigInfoBase(String dataId, String group, String content) { this.dataId = dataId; this.group = group; this.content = content; if (this.content != null) { this.md5 = MD5Utils.md5Hex(this.content, Constants.PERSIST_ENCODE); } } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public void dump(PrintWriter writer) { writer.write(this.content); } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } @Override public int compareTo(ConfigInfoBase o) { if (o == null) { return 1; } if (this.dataId == null) { if (o.getDataId() == null) { return 0; } else { return -1; } } else { if (o.getDataId() == null) { return 1; } else { int cmpDataId = this.dataId.compareTo(o.getDataId()); if (cmpDataId != 0) { return cmpDataId; } } } if (this.group == null) { if (o.getGroup() == null) { return 0; } else { return -1; } } else { if (o.getGroup() == null) { return 1; } else { int cmpGroup = this.group.compareTo(o.getGroup()); if (cmpGroup != 0) { return cmpGroup; } } } if (this.content == null) { if (o.getContent() == null) { return 0; } else { return -1; } } else { if (o.getContent() == null) { return 1; } else { int cmpContent = this.content.compareTo(o.getContent()); if (cmpContent != 0) { return cmpContent; } } } return 0; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ConfigInfoBase other = (ConfigInfoBase) obj; if (content == null) { if (other.content != null) { return false; } } else if (!content.equals(other.content)) { return false; } if (dataId == null) { if (other.dataId != null) { return false; } } else if (!dataId.equals(other.dataId)) { return false; } if (group == null) { if (other.group != null) { return false; } } else if (!group.equals(other.group)) { return false; } if (md5 == null) { if (other.md5 != null) { return false; } } else if (!md5.equals(other.md5)) { return false; } return true; } @Override public int hashCode() { return Objects.hash(dataId, group, content, md5); } @Override public String toString() { return "ConfigInfoBase{" + "id=" + id + ", dataId='" + dataId + '\'' + ", group='" + group + '\'' + ", content='" + content + '\'' + ", md5='" + md5 + '\'' + '}'; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBaseEx.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfoBaseEx. * And can't add field, to compatible with old interface(If adding a field, then it will occur compatibility problems). * * @author Nacos */ public class ConfigInfoBaseEx extends ConfigInfoBase { private static final long serialVersionUID = 5802322506486922169L; /** * Single message status code, when querying for batch. * And details of message status code, you can see Constants.java. */ private int status; /** * Single message information, when querying for batch. */ private String message; public ConfigInfoBaseEx() { super(); } public ConfigInfoBaseEx(String dataId, String group, String content) { super(dataId, group, content); } public ConfigInfoBaseEx(String dataId, String group, String content, int status, String message) { super(dataId, group, content); this.status = status; this.message = message; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public int hashCode() { return super.hashCode(); } @Override public String toString() { return "ConfigInfoBaseEx [status=" + status + ", message=" + message + ", dataId=" + getDataId() + ", group()=" + getGroup() + ", content()=" + getContent() + "]"; } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoBetaWrapper.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfoBetaWrapper. * @author liaochuntao */ public class ConfigInfoBetaWrapper extends ConfigInfo4Beta { private static final long serialVersionUID = 4511997359365712505L; private long lastModified; public ConfigInfoBetaWrapper() { } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoChanged.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Objects; /** * ConfigInfoChanged. * * @author leiwen.zh */ public class ConfigInfoChanged implements Serializable { private static final long serialVersionUID = -1819539062100125171L; private String dataId; private String group; private String tenant; public ConfigInfoChanged(String dataId, String group, String tenant) { this.dataId = dataId; this.group = group; this.setTenant(tenant); } public ConfigInfoChanged() { } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } ConfigInfoChanged other = (ConfigInfoChanged) obj; if (dataId == null) { if (other.dataId != null) { return false; } } else if (!dataId.equals(other.dataId)) { return false; } if (group == null) { if (other.group != null) { return false; } } else if (!group.equals(other.group)) { return false; } return true; } @Override public int hashCode() { return Objects.hash(dataId, group); } @Override public String toString() { return "ConfigInfoChanged [dataId=" + dataId + ", group=" + group + "]"; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoEx.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfoEx. * * @author leiwen.zh */ public class ConfigInfoEx extends ConfigInfo { private static final long serialVersionUID = 8905036592920606608L; /** * Single message status code, when querying for batch. * And details of message status code, you can see Constants.java. */ private int status; /** * Single message information, when querying for batch. */ private String message; public ConfigInfoEx() { super(); } public ConfigInfoEx(String dataId, String group, String content) { super(dataId, group, content); } public ConfigInfoEx(String dataId, String group, String content, int status, String message) { super(dataId, group, content); this.status = status; this.message = message; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } @Override public String toString() { return "ConfigInfoEx [status=" + status + ", message=" + message + ", dataId=" + getDataId() + ", group=" + getGroup() + ", appName=" + getAppName() + ", content=" + getContent() + "]"; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoGrayWrapper.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfoGrayWrapper. * * @author rong */ public class ConfigInfoGrayWrapper extends ConfigInfo { private static final long serialVersionUID = 4511997591465712505L; private long lastModified; private String grayName; private String grayRule; private String srcUser; public ConfigInfoGrayWrapper() { } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getGrayRule() { return grayRule; } public void setGrayRule(String grayRule) { this.grayRule = grayRule; } public String getSrcUser() { return srcUser; } public void setSrcUser(String srcUser) { this.srcUser = srcUser; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoStateWrapper.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Objects; /** * ConfigInfoStateWrapper. include id,dataId,group,tenant,lastModified. * * @author zunfei.lzf */ public class ConfigInfoStateWrapper implements Serializable { private long id; private String dataId; private String group; private String tenant; private long lastModified; private String md5; private String grayName; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigInfoStateWrapper that = (ConfigInfoStateWrapper) o; return id == that.id && lastModified == that.lastModified && Objects.equals(dataId, that.dataId) && Objects.equals(group, that.group) && Objects.equals(tenant, that.tenant) && Objects.equals(md5, that.md5); } @Override public int hashCode() { return Objects.hash(dataId, group, tenant); } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoTagWrapper.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfoTagWrapper. * @author liaochuntao */ public class ConfigInfoTagWrapper extends ConfigInfo4Tag { private static final long serialVersionUID = 4511997359365712505L; private long lastModified; public ConfigInfoTagWrapper() { } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigInfoWrapper.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * ConfigInfo Wrapper. * * @author Nacos */ public class ConfigInfoWrapper extends ConfigInfo { private static final long serialVersionUID = 4511997359365712505L; private long lastModified; public ConfigInfoWrapper() { } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigKey.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Objects; /** * ConfigKey. * * @author Nacos */ public class ConfigKey implements Serializable { private static final long serialVersionUID = -1748953484511867580L; private String appName; private String dataId; private String group; public ConfigKey() { } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigKey configKey = (ConfigKey) o; return Objects.equals(appName, configKey.appName) && Objects.equals(dataId, configKey.dataId) && Objects.equals( group, configKey.group); } @Override public int hashCode() { return Objects.hash(appName, dataId, group); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigListenState.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * The type Config listen state. * * @author Sunrisea */ public class ConfigListenState { private String md5; private boolean namespaceTransfer; public ConfigListenState(String md5) { this.md5 = md5; } /** * Is namespace transfer boolean. * * @return the boolean */ public boolean isNamespaceTransfer() { return namespaceTransfer; } /** * Sets namespace transfer. * * @param namespaceTransfer the namespace transfer */ public void setNamespaceTransfer(boolean namespaceTransfer) { this.namespaceTransfer = namespaceTransfer; } /** * Gets md 5. * * @return the md 5 */ public String getMd5() { return md5; } /** * Sets md 5. * * @param md5 the md 5 */ public void setMd5(String md5) { this.md5 = md5; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigMetadata.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.util.List; import java.util.Objects; /** * config export Metadata. * * @author Nacos */ public class ConfigMetadata { private List metadata; public static class ConfigExportItem { private String group; private String dataId; private String desc; private String type; private String appName; private String configTags; public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getConfigTags() { return configTags; } public void setConfigTags(String configTags) { this.configTags = configTags; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigExportItem that = (ConfigExportItem) o; return Objects.equals(group, that.group) && Objects.equals(dataId, that.dataId) && Objects .equals(desc, that.desc) && Objects.equals(type, that.type) && Objects .equals(appName, that.appName) && Objects.equals(configTags, that.configTags); } @Override public int hashCode() { return Objects.hash(group, dataId, desc, type, appName, configTags); } } public List getMetadata() { return metadata; } public void setMetadata(List metadata) { this.metadata = metadata; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigOperateResult.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; /** * config operation result. * @author shiyiyue */ public class ConfigOperateResult implements Serializable { boolean success = true; private long id; private long lastModified; public ConfigOperateResult(long id, long lastModified) { this.id = id; this.lastModified = lastModified; } public ConfigOperateResult(boolean success) { this.success = success; } public ConfigOperateResult() { } public boolean isSuccess() { return success; } public void setSuccess(boolean success) { this.success = success; } public long getId() { return id; } public void setId(long id) { this.id = id; } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ConfigRequestInfo.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Objects; /** * ConfigRequestInfo. * @author dongyafei * @date 2022/8/11 */ public class ConfigRequestInfo implements Serializable { private static final long serialVersionUID = 326726654448860273L; private String srcIp; private String srcType; private String requestIpApp; private String betaIps; private String casMd5; private boolean namespaceTransferred; private Boolean updateForExist = Boolean.TRUE; public ConfigRequestInfo(String srcIp, String srcType, String requestIpApp, String betaIps, String casMd5) { this.srcIp = srcIp; this.srcType = srcType; this.requestIpApp = requestIpApp; this.betaIps = betaIps; this.casMd5 = casMd5; } public ConfigRequestInfo() { } public String getSrcIp() { return srcIp; } public void setSrcIp(String srcIp) { this.srcIp = srcIp; } public String getSrcType() { return srcType; } public void setSrcType(String srcType) { this.srcType = srcType; } public String getRequestIpApp() { return requestIpApp; } public void setRequestIpApp(String requestIpApp) { this.requestIpApp = requestIpApp; } public String getBetaIps() { return betaIps; } public void setBetaIps(String betaIps) { this.betaIps = betaIps; } public String getCasMd5() { return casMd5; } public void setCasMd5(String casMd5) { this.casMd5 = casMd5; } public Boolean getUpdateForExist() { return updateForExist; } public void setUpdateForExist(Boolean updateForExist) { this.updateForExist = updateForExist; } public boolean isNamespaceTransferred() { return namespaceTransferred; } public void setNamespaceTransferred(boolean namespaceTransferred) { this.namespaceTransferred = namespaceTransferred; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigRequestInfo that = (ConfigRequestInfo) o; return Objects.equals(srcIp, that.srcIp) && Objects.equals(requestIpApp, that.requestIpApp) && Objects.equals( betaIps, that.betaIps) && Objects.equals(casMd5, that.casMd5) && Objects.equals(updateForExist, that.updateForExist); } @Override public int hashCode() { return Objects.hash(srcIp, requestIpApp, betaIps, casMd5); } @Override public String toString() { return "ConfigRequestInfoVo{" + "srcIp='" + srcIp + '\'' + ", requestIpApp='" + requestIpApp + '\'' + ", betaIps='" + betaIps + '\'' + ", casMd5='" + casMd5 + '\'' + ", updateForExist='" + updateForExist + '}'; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/GroupkeyListenserStatus.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Map; /** * GroupkeyListenserStatus. * * @author Nacos */ public class GroupkeyListenserStatus implements Serializable { private static final long serialVersionUID = -2094829323598842474L; private int collectStatus; private Map lisentersGroupkeyStatus; public int getCollectStatus() { return collectStatus; } public void setCollectStatus(int collectStatus) { this.collectStatus = collectStatus; } public Map getLisentersGroupkeyStatus() { return lisentersGroupkeyStatus; } public void setLisentersGroupkeyStatus(Map lisentersGroupkeyStatus) { this.lisentersGroupkeyStatus = lisentersGroupkeyStatus; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/ListenerCheckResult.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; /** * check config has listener. * * @author shiyiyue */ public class ListenerCheckResult implements Serializable { private boolean hasListener; private int code; private String message; public boolean isHasListener() { return hasListener; } public void setHasListener(boolean hasListener) { this.hasListener = hasListener; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/NacosConfigCacheFactory.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * The type Nacos config cache factory. * * @author Sunrisea */ public class NacosConfigCacheFactory implements ConfigCacheFactory { @Override public ConfigCache createConfigCache() { return new ConfigCache(); } @Override public ConfigCacheGray createConfigCacheGray() { return new ConfigCacheGray(); } @Override public String getName() { return "nacos"; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/NacosConfigCachePostProcessor.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; /** * The type Nacos config cache md 5 post processor. * * @author Sunrisea */ public class NacosConfigCachePostProcessor implements ConfigCachePostProcessor { @Override public String getName() { return "nacos"; } @Override public void postProcess(ConfigCache configCache, String content) { } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/SampleResult.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; import java.util.Map; /** * SampleResult. * * @author Nacos */ public class SampleResult implements Serializable { private static final long serialVersionUID = 2587823382317389453L; private Map lisentersGroupkeyStatus; public Map getLisentersGroupkeyStatus() { return lisentersGroupkeyStatus; } public void setLisentersGroupkeyStatus(Map lisentersGroupkeyStatus) { this.lisentersGroupkeyStatus = lisentersGroupkeyStatus; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/SubscriberStatus.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import java.io.Serializable; /** * SubscriberStatus. * * @author Nacos */ public class SubscriberStatus implements Serializable { private static final long serialVersionUID = 1065466896062351086L; private String groupKey; private String md5; private Long lastTime; private Boolean status; private String serverIp; public SubscriberStatus() { } public SubscriberStatus(String groupKey, Boolean status, String md5, Long lastTime) { this.groupKey = groupKey; this.md5 = md5; this.lastTime = lastTime; this.status = status; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public Long getLastTime() { return lastTime; } public void setLastTime(Long lastTime) { this.lastTime = lastTime; } public Boolean getStatus() { return status; } public void setStatus(Boolean status) { this.status = status; } public String getGroupKey() { return groupKey; } public void setGroupKey(String groupKey) { this.groupKey = groupKey; } public String getServerIp() { return serverIp; } public void setServerIp(String serverIp) { this.serverIp = serverIp; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/capacity/Capacity.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.capacity; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; import java.io.Serializable; import java.sql.Timestamp; /** * Capacity. * * @author hexu.hxy * @date 2018/3/13 */ public class Capacity implements Serializable { private static final long serialVersionUID = 77343194329627468L; @JsonSerialize(using = ToStringSerializer.class) private Long id; private Integer quota; private Integer usage; private Integer maxSize; private Integer maxAggrCount; private Integer maxAggrSize; private Timestamp gmtCreate; private Timestamp gmtModified; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public Integer getQuota() { return quota; } public void setQuota(Integer quota) { this.quota = quota; } public Integer getUsage() { return usage; } public void setUsage(Integer usage) { this.usage = usage; } public Integer getMaxSize() { return maxSize; } public void setMaxSize(Integer maxSize) { this.maxSize = maxSize; } public Integer getMaxAggrCount() { return maxAggrCount; } public void setMaxAggrCount(Integer maxAggrCount) { this.maxAggrCount = maxAggrCount; } public Integer getMaxAggrSize() { return maxAggrSize; } public void setMaxAggrSize(Integer maxAggrSize) { this.maxAggrSize = maxAggrSize; } public Timestamp getGmtCreate() { if (gmtCreate == null) { return null; } return new Timestamp(gmtCreate.getTime()); } public void setGmtCreate(Timestamp gmtCreate) { if (gmtCreate == null) { this.gmtCreate = null; } else { this.gmtCreate = new Timestamp(gmtCreate.getTime()); } } public Timestamp getGmtModified() { if (gmtModified == null) { return null; } return new Timestamp(gmtModified.getTime()); } public void setGmtModified(Timestamp gmtModified) { if (gmtModified == null) { this.gmtModified = null; } else { this.gmtModified = new Timestamp(gmtModified.getTime()); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/capacity/GroupCapacity.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.capacity; /** * Group Capacity. * * @author hexu.hxy * @date 2018/3/13 */ public class GroupCapacity extends Capacity { private static final long serialVersionUID = 5888791286289014878L; private String groupName; public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/capacity/NamespaceCapacity.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.capacity; /** * Namespace Capacity. * * @author hexu.hxy * @date 2018/3/13 */ public class NamespaceCapacity extends Capacity { private static final long serialVersionUID = -1238179608935781384L; private String namespaceId; public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigDataChangeEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.event; import com.alibaba.nacos.common.notify.Event; /** * ConfigDataChangeEvent. * * @author Nacos */ public class ConfigDataChangeEvent extends Event { public String dataId; public String group; public String tenant; public String grayName; public final long lastModifiedTs; public ConfigDataChangeEvent(String dataId, String group, String tenant, long gmtModified) { if (null == dataId || null == group) { throw new IllegalArgumentException("dataId is null or group is null"); } this.dataId = dataId; this.group = group; this.tenant = tenant; this.lastModifiedTs = gmtModified; } public ConfigDataChangeEvent(String dataId, String group, String tenant, String grayName, long gmtModified) { this(dataId, group, tenant, gmtModified); this.grayName = grayName; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigDumpEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.event; import com.alibaba.nacos.common.notify.Event; /** * ConfigDumpEvent. * * @author liaochuntao */ public class ConfigDumpEvent extends Event { private static final long serialVersionUID = -8776888606458370294L; private boolean remove; private String namespaceId; private String dataId; private String group; private String encryptedDataKey; private boolean isBeta; private boolean isBatch; private int delimiter; private String tag; private String grayName; private String grayRule; private String content; private String betaIps; private String handleIp; private String type; private long lastModifiedTs; public int getDelimiter() { return delimiter; } public void setDelimiter(int delimiter) { this.delimiter = delimiter; } public boolean isRemove() { return remove; } public void setRemove(boolean remove) { this.remove = remove; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public boolean isBeta() { return isBeta; } public void setBeta(boolean beta) { isBeta = beta; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public boolean isBatch() { return isBatch; } public void setBatch(boolean batch) { isBatch = batch; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getBetaIps() { return betaIps; } public void setBetaIps(String betaIps) { this.betaIps = betaIps; } public String getHandleIp() { return handleIp; } public void setHandleIp(String handleIp) { this.handleIp = handleIp; } public String getType() { return type; } public void setType(String type) { this.type = type; } public long getLastModifiedTs() { return lastModifiedTs; } public void setLastModifiedTs(long lastModifiedTs) { this.lastModifiedTs = lastModifiedTs; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getGrayRule() { return grayRule; } public void setGrayRule(String grayRule) { this.grayRule = grayRule; } public static ConfigDumpEventBuilder builder() { return new ConfigDumpEventBuilder(); } public static final class ConfigDumpEventBuilder { private boolean remove; private String namespaceId; private String dataId; private String group; private boolean isBeta; private boolean isBatch; private int delimiter; private String tag; private String grayName; private String grayRule; private String encryptedDataKey; private String content; private String betaIps; private String handleIp; private String type; private long lastModifiedTs; private ConfigDumpEventBuilder() { } public ConfigDumpEventBuilder delimiter(int delimiter) { this.delimiter = delimiter; return this; } public ConfigDumpEventBuilder remove(boolean remove) { this.remove = remove; return this; } public ConfigDumpEventBuilder namespaceId(String namespaceId) { this.namespaceId = namespaceId; return this; } public ConfigDumpEventBuilder dataId(String dataId) { this.dataId = dataId; return this; } public ConfigDumpEventBuilder group(String group) { this.group = group; return this; } public ConfigDumpEventBuilder isBeta(boolean isBeta) { this.isBeta = isBeta; return this; } public ConfigDumpEventBuilder tag(String tag) { this.tag = tag; return this; } public ConfigDumpEventBuilder grayName(String grayName) { this.grayName = grayName; return this; } public ConfigDumpEventBuilder grayRule(String grayRule) { this.grayRule = grayRule; return this; } public ConfigDumpEventBuilder content(String content) { this.content = content; return this; } public ConfigDumpEventBuilder betaIps(String betaIps) { this.betaIps = betaIps; return this; } public ConfigDumpEventBuilder handleIp(String handleIp) { this.handleIp = handleIp; return this; } public ConfigDumpEventBuilder encryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; return this; } public ConfigDumpEventBuilder type(String type) { this.type = type; return this; } public ConfigDumpEventBuilder lastModifiedTs(long lastModifiedTs) { this.lastModifiedTs = lastModifiedTs; return this; } public ConfigDumpEventBuilder isBatch(boolean isBatch) { this.isBatch = isBatch; return this; } /** * Build a configDumpEvent. * * @return ConfigDumpEvent object instance. */ public ConfigDumpEvent build() { ConfigDumpEvent configDumpEvent = new ConfigDumpEvent(); configDumpEvent.setRemove(remove); configDumpEvent.setNamespaceId(namespaceId); configDumpEvent.setDataId(dataId); configDumpEvent.setGroup(group); configDumpEvent.setTag(tag); configDumpEvent.setContent(content); configDumpEvent.setBetaIps(betaIps); configDumpEvent.setHandleIp(handleIp); configDumpEvent.setEncryptedDataKey(encryptedDataKey); configDumpEvent.setType(type); configDumpEvent.setBatch(isBatch); configDumpEvent.setDelimiter(delimiter); configDumpEvent.setLastModifiedTs(lastModifiedTs); configDumpEvent.setGrayName(grayName); configDumpEvent.setGrayRule(grayRule); configDumpEvent.isBeta = this.isBeta; return configDumpEvent; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/event/ConfigFuzzyWatchEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.event; import com.alibaba.nacos.common.notify.Event; import java.util.Set; /** * This event represents a batch fuzzy listening event for configurations. It is used to notify the server about a batch * of fuzzy listening requests from clients. Each request contains a client ID, a set of existing group keys associated * with the client, a key group pattern, and a flag indicating whether the client is initializing. * * @author stone-98 * @date 2024/3/5 */ public class ConfigFuzzyWatchEvent extends Event { private static final long serialVersionUID = 1953965691384930209L; /** * ID of the client making the request. */ private String connectionId; /** * Pattern for matching group keys. */ private String groupKeyPattern; /** * Set of existing group keys associated with the client. */ private Set clientExistingGroupKeys; /** * Flag indicating whether the client is initializing. */ private boolean isInitializing; /** * Constructs a new ConfigBatchFuzzyListenEvent with the specified parameters. * * @param connectionId ID of the client making the request * @param clientExistingGroupKeys Set of existing group keys associated with the client * @param groupKeyPattern Pattern for matching group keys * @param isInitializing Flag indicating whether the client is initializing */ public ConfigFuzzyWatchEvent(String connectionId, Set clientExistingGroupKeys, String groupKeyPattern, boolean isInitializing) { this.connectionId = connectionId; this.clientExistingGroupKeys = clientExistingGroupKeys; this.groupKeyPattern = groupKeyPattern; this.isInitializing = isInitializing; } /** * Get the ID of the client making the request. * * @return The client ID */ public String getConnectionId() { return connectionId; } /** * Set the ID of the client making the request. * * @param connectionId The client ID to be set */ public void setConnectionId(String connectionId) { this.connectionId = connectionId; } /** * Get the pattern for matching group keys. * * @return The key group pattern */ public String getGroupKeyPattern() { return groupKeyPattern; } /** * Set the pattern for matching group keys. * * @param groupKeyPattern The key group pattern to be set */ public void setGroupKeyPattern(String groupKeyPattern) { this.groupKeyPattern = groupKeyPattern; } /** * Get the set of existing group keys associated with the client. * * @return The set of existing group keys */ public Set getClientExistingGroupKeys() { return clientExistingGroupKeys; } /** * Set the set of existing group keys associated with the client. * * @param clientExistingGroupKeys The set of existing group keys to be set */ public void setClientExistingGroupKeys(Set clientExistingGroupKeys) { this.clientExistingGroupKeys = clientExistingGroupKeys; } /** * Check whether the client is initializing. * * @return True if the client is initializing, otherwise false */ public boolean isInitializing() { return isInitializing; } /** * Set the flag indicating whether the client is initializing. * * @param initializing True if the client is initializing, otherwise false */ public void setInitializing(boolean initializing) { isInitializing = initializing; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/event/IstioConfigChangeEvent.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.event; /** * XDS config change event. * * @author PoisonGravity */ public class IstioConfigChangeEvent extends ConfigDataChangeEvent { private static final long serialVersionUID = -2618455009648617192L; public final String content; public final String type; public IstioConfigChangeEvent(String dataId, String group, String tenant, long gmtModified, String content, String type) { super(dataId, group, tenant, gmtModified); this.content = content; this.type = type; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/event/LocalDataChangeEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.event; import com.alibaba.nacos.common.notify.Event; /** * LocalDataChangeEvent. * * @author Nacos */ public class LocalDataChangeEvent extends Event { public final String groupKey; public LocalDataChangeEvent(String groupKey) { this.groupKey = groupKey; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/event/RaftDbErrorRecoverEvent.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.event; import com.alibaba.nacos.common.JustForTest; import com.alibaba.nacos.common.notify.Event; /** * RaftDBErrorRecoverEvent. * * @author liaochuntao */ @JustForTest public class RaftDbErrorRecoverEvent extends Event { } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/form/ConfigForm.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.form; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.api.model.NacosForm; import org.springframework.http.HttpStatus; /** * ConfigForm. * * @author dongyafei * @author xiweng.yy */ public class ConfigForm implements NacosForm, Cloneable { private static final long serialVersionUID = 4124932564086863921L; private String dataId; /** * Deprecated, please use {@link ConfigFormV3#groupName} replaced. */ @Deprecated private String group; private String namespaceId = StringUtils.EMPTY; private String content; private String tag; private String appName; private String srcUser; private String configTags; private String encryptedDataKey; private String grayName; private String grayRuleExp; private String grayVersion; private int grayPriority; private String desc; private String use; private String effect; private String type; private String schema; public ConfigForm() { } public ConfigForm(String dataId, String group, String namespaceId, String content, String tag, String appName, String srcUser, String configTags, String desc, String use, String effect, String type, String schema) { this.dataId = dataId; this.group = group; this.namespaceId = namespaceId; this.content = content; this.tag = tag; this.appName = appName; this.srcUser = srcUser; this.configTags = configTags; this.desc = desc; this.use = use; this.effect = effect; this.type = type; this.schema = schema; } @Override public ConfigForm clone() { try { // Object.clone() 是浅拷贝,但对于 String 和基本类型已经足够 return (ConfigForm) super.clone(); } catch (CloneNotSupportedException e) { // 理论上不会发生,因为实现了接口Cloneable throw new AssertionError(e); } } public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getAppName() { return appName; } public void setAppName(String appName) { this.appName = appName; } public String getSrcUser() { return srcUser; } public void setSrcUser(String srcUser) { this.srcUser = srcUser; } public String getConfigTags() { return configTags; } public void setConfigTags(String configTags) { this.configTags = configTags; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public String getUse() { return use; } public void setUse(String use) { this.use = use; } public String getEffect() { return effect; } public void setEffect(String effect) { this.effect = effect; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getSchema() { return schema; } public void setSchema(String schema) { this.schema = schema; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getGrayRuleExp() { return grayRuleExp; } public void setGrayRuleExp(String grayRuleExp) { this.grayRuleExp = grayRuleExp; } public String getGrayVersion() { return grayVersion; } public void setGrayVersion(String grayVersion) { this.grayVersion = grayVersion; } public int getGrayPriority() { return grayPriority; } public void setGrayPriority(int grayPriority) { this.grayPriority = grayPriority; } @Override public void validate() throws NacosApiException { if (StringUtils.isBlank(dataId)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "Required parameter 'dataId' type String is not present"); } else if (StringUtils.isBlank(group)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "Required parameter 'group' type String is not present"); } } /** * Validate form parameter and include validate `content` parameters. * * @throws NacosApiException NacosApiException */ public void validateWithContent() throws NacosApiException { validate(); if (StringUtils.isBlank(content)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "Required parameter 'content' type String is not present"); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/form/ConfigFormV3.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.form; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.http.HttpStatus; /** * Nacos HTTP config form v3, use `groupName` replace `group`. * * @author xiweng.yy */ public class ConfigFormV3 extends ConfigForm { private static final long serialVersionUID = 1105715502736280287L; private String groupName; public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } @Override public void validate() throws NacosApiException { if (StringUtils.isBlank(groupName)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "Required parameter 'groupName' type String is not present"); } super.setGroup(groupName); super.validate(); } /** * Validate for blur search API, which allow user input empty groupName and dataId to search all configs. * * @throws NacosApiException when form parameters is invalid. */ public void blurSearchValidate() throws NacosApiException { if (null == groupName) { groupName = StringUtils.EMPTY; super.setGroup(groupName); } if (null == getDataId()) { setDataId(StringUtils.EMPTY); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/form/UpdateCapacityForm.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.form; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.service.capacity.CapacityService; import com.alibaba.nacos.api.model.NacosForm; import org.springframework.http.HttpStatus; /** * This form is used to update capacity-related configurations. * * @author Nacos */ public class UpdateCapacityForm implements NacosForm { private static final long serialVersionUID = -1912905276914026856L; private String groupName; private String namespaceId; private Integer quota; private Integer maxSize; private Integer maxAggrCount; private Integer maxAggrSize; public String getGroupName() { return groupName; } public void setGroupName(String groupName) { this.groupName = groupName; } public String getNamespaceId() { return namespaceId; } public void setNamespaceId(String namespaceId) { this.namespaceId = namespaceId; } public Integer getQuota() { return quota; } public void setQuota(Integer quota) { this.quota = quota; } public Integer getMaxSize() { return maxSize; } public void setMaxSize(Integer maxSize) { this.maxSize = maxSize; } public Integer getMaxAggrCount() { return maxAggrCount; } public void setMaxAggrCount(Integer maxAggrCount) { this.maxAggrCount = maxAggrCount; } public Integer getMaxAggrSize() { return maxAggrSize; } public void setMaxAggrSize(Integer maxAggrSize) { this.maxAggrSize = maxAggrSize; } @Override public void validate() throws NacosApiException { if (quota == null && maxSize == null && maxAggrCount == null && maxAggrSize == null) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "The parameters quota, maxSize, maxAggrCount, maxAggrSize cannot be empty at the same time"); } } /** * Check namespaceId and groupName. * * @param capacityService capacity service * @throws NacosApiException NacosApiException */ public void checkNamespaceIdAndGroupName(CapacityService capacityService) throws NacosApiException { if (StringUtils.isBlank(groupName) && StringUtils.isBlank(namespaceId)) { capacityService.initAllCapacity(); throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING, "At least one of the parameters (groupName or namespaceId) must be provided"); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/gray/AbstractGrayRule.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.gray; import com.alibaba.nacos.api.exception.NacosException; import java.util.Map; /** * Gray rule. type with version determined parse logic. * * @author shiyiyue */ public abstract class AbstractGrayRule implements GrayRule { protected String rawGrayRuleExp; protected int priority; protected volatile boolean valid = true; public AbstractGrayRule() { } public AbstractGrayRule(String rawGrayRuleExp, int priority) { try { parse(rawGrayRuleExp); this.priority = priority; } catch (NacosException e) { valid = false; } this.rawGrayRuleExp = rawGrayRuleExp; } /** * parse gray rule. * * @param rawGrayRule raw gray rule. * @throws NacosException if parse failed. * @date 2024/3/14 */ protected abstract void parse(String rawGrayRule) throws NacosException; /** * match gray rule. * * @param labels conn labels. * @return true if match. * @date 2024/3/14 */ public abstract boolean match(Map labels); public boolean isValid() { return valid; } /** * get type. * * @return gray rule type. * @date 2024/3/14 */ public abstract String getType(); /** * get version. * * @return gray rule version. * @date 2024/3/14 */ public abstract String getVersion(); public String getRawGrayRuleExp() { return rawGrayRuleExp; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/gray/BetaGrayRule.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.gray; import com.alibaba.nacos.api.exception.NacosException; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; /** * beta gray rule for beta ips. * @author shiyiyue1102 */ public class BetaGrayRule extends AbstractGrayRule { Set betaIps; public static final String CLIENT_IP_LABEL = "ClientIp"; public static final String TYPE_BETA = "beta"; public static final String VERSION = "1.0.0"; public static final int PRIORITY = Integer.MAX_VALUE; public BetaGrayRule() { super(); } public BetaGrayRule(String betaIps, int priority) { super(betaIps, priority); } /** * parse beta gray rule. * @param rawGrayRule raw gray rule. * @throws NacosException exception. */ @Override protected void parse(String rawGrayRule) throws NacosException { Set betaIps = new HashSet<>(); String[] ips = rawGrayRule.split(","); for (String ip : ips) { betaIps.add(ip); } this.betaIps = betaIps; } @Override public boolean match(Map labels) { return labels.containsKey(CLIENT_IP_LABEL) && betaIps.contains(labels.get(CLIENT_IP_LABEL)); } @Override public String getType() { return TYPE_BETA; } @Override public String getVersion() { return VERSION; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } BetaGrayRule that = (BetaGrayRule) o; return Objects.equals(betaIps, that.betaIps); } @Override public int hashCode() { return Objects.hash(betaIps); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/gray/ConfigGrayPersistInfo.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.gray; /** * description. * * @author rong * @date 2024-03-14 10:57 */ public class ConfigGrayPersistInfo { private String type; private String version; private String expr; private int priority; public ConfigGrayPersistInfo(String type, String version, String expr, int priority) { this.type = type; this.version = version; this.expr = expr; this.priority = priority; } public String getType() { return type; } public void setType(String type) { this.type = type; } public String getVersion() { return version; } public void setVersion(String version) { this.version = version; } public String getExpr() { return expr; } public void setExpr(String expr) { this.expr = expr; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/gray/GrayRule.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.gray; import java.util.Map; /** * gray rule. * * @author rong */ public interface GrayRule { /** * gray rule match labels or not. * * @date 2024/3/14 * @param labels conn labels. * @return true if match, false otherwise. */ boolean match(Map labels); /** * if the gray rule is valid. * * @date 2024/3/14 * @return true if valid, false otherwise. */ boolean isValid(); /** * get gray rule type. * * @date 2024/3/14 * @return the gray rule type. */ String getType(); /** * get gray rule version. * * @date 2024/3/14 * @return the gray rule version. */ String getVersion(); /** * get gray rule priority. * * @date 2024/3/14 * @return the gray rule priority. */ int getPriority(); /** * get raw String of gray rule. * * @date 2024/3/14 * @return the raw String of gray rule. */ String getRawGrayRuleExp(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/gray/GrayRuleManager.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.gray; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.google.gson.Gson; import java.lang.reflect.Constructor; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * GrayRuleManager. * * @author zunfei.lzf */ public class GrayRuleManager { private static final Map> GRAY_RULE_MAP = new ConcurrentHashMap<>(8); public static final String SPLIT = "_"; static { Collection grayRuleCollection = NacosServiceLoader.load(GrayRule.class); for (GrayRule grayRule : grayRuleCollection) { GRAY_RULE_MAP.put(grayRule.getType() + SPLIT + grayRule.getVersion(), grayRule.getClass()); } } /** * get class by type and version. * * @param type type. * @param version version. * @return class. * @date 2024/3/14 */ public static Class getClassByTypeAndVersion(String type, String version) { return GRAY_RULE_MAP.get(type + SPLIT + version); } /** * construct gray rule. * * @param configGrayPersistInfo config gray persist info. * @return gray rule. * @date 2024/3/14 */ public static GrayRule constructGrayRule(ConfigGrayPersistInfo configGrayPersistInfo) { Class classByTypeAndVersion = getClassByTypeAndVersion(configGrayPersistInfo.getType(), configGrayPersistInfo.getVersion()); if (classByTypeAndVersion == null) { return null; } try { Constructor declaredConstructor = classByTypeAndVersion.getDeclaredConstructor(String.class, int.class); declaredConstructor.setAccessible(true); return (GrayRule) declaredConstructor.newInstance(configGrayPersistInfo.getExpr(), configGrayPersistInfo.getPriority()); } catch (Exception e) { throw new RuntimeException(String.format("construct gray rule failed with type[%s], version[%s].", configGrayPersistInfo.getType(), configGrayPersistInfo.getVersion()), e); } } /** * construct config gray persist info. * * @param grayRule gray rule. * @return config gray persist info. * @date 2024/3/14 */ public static ConfigGrayPersistInfo constructConfigGrayPersistInfo(GrayRule grayRule) { return new ConfigGrayPersistInfo(grayRule.getType(), grayRule.getVersion(), grayRule.getRawGrayRuleExp(), grayRule.getPriority()); } /** * deserialize config gray persist info. * * @param grayRuleRawStringFromDb gray rule raw string from db. * @return config gray persist info. * @date 2024/3/14 */ public static ConfigGrayPersistInfo deserializeConfigGrayPersistInfo(String grayRuleRawStringFromDb) { return (new Gson()).fromJson(grayRuleRawStringFromDb, ConfigGrayPersistInfo.class); } /** * serialize config gray persist info. * * @param configGrayPersistInfo config gray persist info. * @return serialized string. * @date 2024/3/14 */ public static String serializeConfigGrayPersistInfo(ConfigGrayPersistInfo configGrayPersistInfo) { return (new Gson()).toJson(configGrayPersistInfo); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/model/gray/TagGrayRule.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.gray; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.utils.StringUtils; import java.util.Map; import java.util.Objects; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; /** * Tag gray rule. * * @author shiyiyue */ public class TagGrayRule extends AbstractGrayRule { String tagValue; public static final String VIP_SERVER_TAG_LABEL = VIPSERVER_TAG; public static final String TYPE_TAG = "tag"; public static final String VERSION = "1.0.0"; public static final int PRIORITY = Integer.MAX_VALUE - 1; public TagGrayRule() { super(); } public TagGrayRule(String rawGrayRuleExp, int priority) { super(rawGrayRuleExp, priority); } @Override protected void parse(String rawGrayRule) throws NacosException { if (StringUtils.isBlank(rawGrayRule)) { return; } this.tagValue = rawGrayRule; } @Override public boolean match(Map labels) { return labels.containsKey(VIP_SERVER_TAG_LABEL) && tagValue.equals(labels.get(VIP_SERVER_TAG_LABEL)); } @Override public String getType() { return TYPE_TAG; } @Override public String getVersion() { return VERSION; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } TagGrayRule that = (TagGrayRule) o; return tagValue.equals(that.tagValue); } @Override public int hashCode() { return Objects.hash(tagValue); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/ConfigDynamicMeterRefreshService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.core.monitor.NacosMeterRegistryCenter; import io.micrometer.core.instrument.ImmutableTag; import io.micrometer.core.instrument.Tag; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; /** * dynamic meter refresh service. * * @author liuyixiao */ @Service public class ConfigDynamicMeterRefreshService { private static final String TOPN_CONFIG_CHANGE_REGISTRY = NacosMeterRegistryCenter.TOPN_CONFIG_CHANGE_REGISTRY; private static final int CONFIG_CHANGE_N = 10; /** * refresh config change count top n per 30s. */ @Scheduled(cron = "0/30 * * * * *") public void refreshTopnConfigChangeCount() { NacosMeterRegistryCenter.clear(TOPN_CONFIG_CHANGE_REGISTRY); List> topnConfigChangeCount = MetricsMonitor.getConfigChangeCount() .getCounterOfTopN(CONFIG_CHANGE_N); for (Pair configChangeCount : topnConfigChangeCount) { List tags = new ArrayList<>(); tags.add(new ImmutableTag("config", configChangeCount.getFirst())); NacosMeterRegistryCenter.gauge(TOPN_CONFIG_CHANGE_REGISTRY, "config_change_count", tags, configChangeCount.getSecond()); } } /** * reset config change count to 0 every week. */ @Scheduled(cron = "0 0 0 ? * 1") public void resetTopnConfigChangeCount() { MetricsMonitor.getConfigChangeCount().reset(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/MemoryMonitor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor; import com.alibaba.nacos.config.server.service.notify.AsyncNotifyService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * Memory monitor. * * @author Nacos */ @Service public class MemoryMonitor { @Autowired public MemoryMonitor(AsyncNotifyService notifySingleService) { ConfigExecutor.scheduleConfigTask(new PrintMemoryTask(), DELAY_SECONDS, DELAY_SECONDS, TimeUnit.SECONDS); ConfigExecutor .scheduleConfigTask(new PrintGetConfigResponeTask(), DELAY_SECONDS, DELAY_SECONDS, TimeUnit.SECONDS); ConfigExecutor .scheduleConfigTask(new ThreadTaskQueueMonitorTask(notifySingleService), DELAY_SECONDS, DELAY_SECONDS, TimeUnit.SECONDS); } private static final long DELAY_SECONDS = 10; /** * reset some metrics to 0 every day. */ @Scheduled(cron = "0 0 0 * * ?") public void clear() { MetricsMonitor.getConfigMonitor().set(0); MetricsMonitor.getPublishMonitor().set(0); MetricsMonitor.getFuzzySearchMonitor().set(0); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/MetricsMonitor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor; import com.alibaba.nacos.core.monitor.NacosMeterRegistryCenter; import com.alibaba.nacos.core.monitor.topn.StringTopNCounter; import io.micrometer.core.instrument.Counter; import io.micrometer.core.instrument.ImmutableTag; import io.micrometer.core.instrument.Tag; import io.micrometer.core.instrument.Timer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; /** * Metrics Monitor. * * @author Nacos */ public class MetricsMonitor { private static final String METER_REGISTRY = NacosMeterRegistryCenter.CONFIG_STABLE_REGISTRY; private static AtomicInteger getConfig = new AtomicInteger(); private static AtomicInteger publish = new AtomicInteger(); /** * task for notify config change to sub client of http long polling. */ private static AtomicInteger longPolling = new AtomicInteger(); private static AtomicInteger configCount = new AtomicInteger(); /** * task for notify config change to cluster server. */ private static AtomicInteger notifyTask = new AtomicInteger(); /** * task for notify config change to sub client of long connection. */ private static AtomicInteger notifyClientTask = new AtomicInteger(); private static AtomicInteger dumpTask = new AtomicInteger(); /** * config fuzzy search count. */ private static AtomicInteger fuzzySearch = new AtomicInteger(); /** * version -> client config subscriber count. */ private static ConcurrentHashMap configSubscriber = new ConcurrentHashMap<>(); /** * config change count. */ private static StringTopNCounter configChangeCount = new StringTopNCounter(); static { ImmutableTag immutableTag = new ImmutableTag("module", "config"); List tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "getConfig")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, getConfig); tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "publish")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, publish); tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "longPolling")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, longPolling); tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "configCount")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, configCount); tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "notifyTask")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, notifyTask); tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "notifyClientTask")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, notifyClientTask); tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "dumpTask")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, dumpTask); tags = new ArrayList<>(); tags.add(immutableTag); tags.add(new ImmutableTag("name", "fuzzySearch")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_monitor", tags, fuzzySearch); configSubscriber.put("v1", new AtomicInteger(0)); configSubscriber.put("v2", new AtomicInteger(0)); tags = new ArrayList<>(); tags.add(new ImmutableTag("version", "v1")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_config_subscriber", tags, configSubscriber.get("v1")); tags = new ArrayList<>(); tags.add(new ImmutableTag("version", "v2")); NacosMeterRegistryCenter.gauge(METER_REGISTRY, "nacos_config_subscriber", tags, configSubscriber.get("v2")); } public static AtomicInteger getConfigMonitor() { return getConfig; } public static AtomicInteger getPublishMonitor() { return publish; } public static AtomicInteger getLongPollingMonitor() { return longPolling; } public static AtomicInteger getConfigCountMonitor() { return configCount; } public static AtomicInteger getNotifyTaskMonitor() { return notifyTask; } public static AtomicInteger getNotifyClientTaskMonitor() { return notifyClientTask; } public static AtomicInteger getDumpTaskMonitor() { return dumpTask; } public static AtomicInteger getFuzzySearchMonitor() { return fuzzySearch; } public static AtomicInteger getConfigSubscriberMonitor(String version) { return configSubscriber.get(version); } public static StringTopNCounter getConfigChangeCount() { return configChangeCount; } public static Timer getReadConfigRtTimer() { return NacosMeterRegistryCenter .timer(METER_REGISTRY, "nacos_timer", "module", "config", "name", "readConfigRt"); } public static Timer getWriteConfigRtTimer() { return NacosMeterRegistryCenter .timer(METER_REGISTRY, "nacos_timer", "module", "config", "name", "writeConfigRt"); } public static Timer getNotifyRtTimer() { return NacosMeterRegistryCenter.timer(METER_REGISTRY, "nacos_timer", "module", "config", "name", "notifyRt"); } public static Timer getDumpRtTimer() { return NacosMeterRegistryCenter.timer(METER_REGISTRY, "nacos_timer", "module", "config", "name", "dumpRt"); } public static Counter getIllegalArgumentException() { return NacosMeterRegistryCenter .counter(METER_REGISTRY, "nacos_exception", "module", "config", "name", "illegalArgument"); } public static Counter getNacosException() { return NacosMeterRegistryCenter.counter(METER_REGISTRY, "nacos_exception", "module", "config", "name", "nacos"); } public static Counter getConfigNotifyException() { return NacosMeterRegistryCenter .counter(METER_REGISTRY, "nacos_exception", "module", "config", "name", "configNotify"); } public static Counter getUnhealthException() { return NacosMeterRegistryCenter .counter(METER_REGISTRY, "nacos_exception", "module", "config", "name", "unhealth"); } public static void incrementConfigChangeCount(String tenant, String group, String dataId) { configChangeCount.increment(tenant + "@" + group + "@" + dataId); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/PrintGetConfigResponeTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor; import static com.alibaba.nacos.config.server.utils.LogUtil.MEMORY_LOG; /** * PrintGetConfigResponeTask. * * @author zongtanghu */ public class PrintGetConfigResponeTask implements Runnable { @Override public void run() { MEMORY_LOG.info(ResponseMonitor.getStringForPrint()); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/PrintMemoryTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor; import com.alibaba.nacos.config.server.service.ClientTrackService; import com.alibaba.nacos.config.server.service.ConfigCacheService; import static com.alibaba.nacos.config.server.utils.LogUtil.MEMORY_LOG; /** * Print memory task. * * @author zongtanghu */ public class PrintMemoryTask implements Runnable { @Override public void run() { int groupCount = ConfigCacheService.groupCount(); int subClientCount = ClientTrackService.subscribeClientCount(); long subCount = ClientTrackService.subscriberCount(); MEMORY_LOG.info("groupCount = {}, subscriberClientCount = {}, subscriberCount = {}", groupCount, subClientCount, subCount); MetricsMonitor.getConfigCountMonitor().set(groupCount); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/ResponseMonitor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor; import java.text.DecimalFormat; import java.util.concurrent.atomic.AtomicLong; /** * Response Monitory. * * @author Nacos */ public class ResponseMonitor { private static AtomicLong[] getConfigCountDetail = new AtomicLong[8]; private static AtomicLong getConfigCount = new AtomicLong(); private static final int MS_50 = 50; private static final int MS_100 = 100; private static final int MS_200 = 200; private static final int MS_500 = 500; private static final int MS_1000 = 1000; private static final int MS_2000 = 2000; private static final int MS_3000 = 3000; static { refresh(); } /** * Refresh for getting configCountDetail. */ public static void refresh() { for (int i = 0; i < getConfigCountDetail.length; i++) { getConfigCountDetail[i] = new AtomicLong(); } } /** * AddConfigTime. * * @param time config time which is added. */ public static void addConfigTime(long time) { getConfigCount.incrementAndGet(); if (time < MS_50) { getConfigCountDetail[0].incrementAndGet(); } else if (time < MS_100) { getConfigCountDetail[1].incrementAndGet(); } else if (time < MS_200) { getConfigCountDetail[2].incrementAndGet(); } else if (time < MS_500) { getConfigCountDetail[3].incrementAndGet(); } else if (time < MS_1000) { getConfigCountDetail[4].incrementAndGet(); } else if (time < MS_2000) { getConfigCountDetail[5].incrementAndGet(); } else if (time < MS_3000) { getConfigCountDetail[6].incrementAndGet(); } else { getConfigCountDetail[7].incrementAndGet(); } } public static String getStringForPrint() { DecimalFormat df = new DecimalFormat("##.0"); StringBuilder s = new StringBuilder("getConfig monitor:\r\n"); s.append("0-50ms:" + df.format(getConfigCountDetail[0].getAndSet(0) * 100 / getConfigCount.get())) .append("%\r\n"); s.append("100-200ms:" + df.format(getConfigCountDetail[2].getAndSet(0) * 100 / getConfigCount.get())) .append("%\r\n"); s.append("200-500ms:" + df.format(getConfigCountDetail[3].getAndSet(0) * 100 / getConfigCount.get())) .append("%\r\n"); s.append("500-1000ms:" + df.format(getConfigCountDetail[4].getAndSet(0) * 100 / getConfigCount.get())) .append("%\r\n"); s.append("1000-2000ms:" + df.format(getConfigCountDetail[5].getAndSet(0) * 100 / getConfigCount.get())) .append("%\r\n"); s.append("2000-3000ms:" + df.format(getConfigCountDetail[6].getAndSet(0) * 100 / getConfigCount.get())) .append("%\r\n"); s.append("3000以上ms:" + df.format(getConfigCountDetail[7].getAndSet(0) * 100 / getConfigCount.getAndSet(0))) .append("%\r\n"); return s.toString(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/ThreadTaskQueueMonitorTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor; import com.alibaba.nacos.config.server.service.notify.AsyncNotifyService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import static com.alibaba.nacos.config.server.utils.LogUtil.MEMORY_LOG; /** * NotifyTaskQueueMonitorTask. * * @author zongtanghu */ public class ThreadTaskQueueMonitorTask implements Runnable { private final AsyncNotifyService notifySingleService; ThreadTaskQueueMonitorTask(AsyncNotifyService notifySingleService) { this.notifySingleService = notifySingleService; } @Override public void run() { int size = ConfigExecutor.asyncNotifyQueueSize(); int notifierClientSize = ConfigExecutor.asyncConfigChangeClientNotifyQueueSize(); MEMORY_LOG.info("toNotifyTaskSize = {}", size); MEMORY_LOG.info("toClientNotifyTaskSize = {}", notifierClientSize); MetricsMonitor.getNotifyTaskMonitor().set(size); MetricsMonitor.getNotifyClientTaskMonitor().set(notifierClientSize); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/monitor/collector/ConfigSubscriberMetricsCollector.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.monitor.collector; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.remote.ConfigChangeListenContext; import com.alibaba.nacos.config.server.service.LongPollingService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * v1 and v2 config subscriber metrics collector. * * @author liuyixiao */ @Service public class ConfigSubscriberMetricsCollector { private static final long DELAY_SECONDS = 5; @Autowired public ConfigSubscriberMetricsCollector(LongPollingService longPollingService, ConfigChangeListenContext configChangeListenContext) { ConfigExecutor.scheduleConfigTask(() -> { MetricsMonitor.getConfigSubscriberMonitor("v1").set(longPollingService.getSubscriberCount()); MetricsMonitor.getConfigSubscriberMonitor("v2").set(configChangeListenContext.getConnectionCount()); }, DELAY_SECONDS, DELAY_SECONDS, TimeUnit.SECONDS); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/paramcheck/ConfigBlurSearchHttpParamExtractor.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.paramcheck; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; /** * The type Config blur search http param extractor. * * @author zhuoguang */ public class ConfigBlurSearchHttpParamExtractor extends AbstractHttpParamExtractor { private static final String BLUR_SEARCH_MODE = "blur"; @Override public List extractParam(HttpServletRequest request) { String searchMode = request.getParameter("search"); ArrayList paramInfos = new ArrayList<>(); // TODO might replace '*' to empty char '' and still do check. if (StringUtils.equals(searchMode, BLUR_SEARCH_MODE)) { return paramInfos; } ParamInfo paramInfo = new ParamInfo(); paramInfo.setNamespaceId(request.getParameter("tenant")); paramInfo.setDataId(request.getParameter("dataId")); paramInfo.setGroup(request.getParameter("group")); paramInfos.add(paramInfo); return paramInfos; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/paramcheck/ConfigDefaultHttpParamExtractor.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.paramcheck; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; /** * Config default http param extractor. * * @author zhuoguang */ public class ConfigDefaultHttpParamExtractor extends AbstractHttpParamExtractor { @Override public List extractParam(HttpServletRequest request) { ParamInfo paramInfo = new ParamInfo(); paramInfo.setNamespaceId(getAliasNamespaceId(request)); paramInfo.setDataId(getAliasDataId(request)); paramInfo.setGroup(getAliasGroup(request)); paramInfo.setIp(getAliasIp(request)); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); return paramInfos; } private String getAliasNamespaceId(HttpServletRequest request) { String namespaceid = request.getParameter("namespaceId"); if (StringUtils.isBlank(namespaceid)) { namespaceid = request.getParameter("tenant"); } if (StringUtils.isBlank(namespaceid)) { namespaceid = request.getParameter("namespace"); } return namespaceid; } private String getAliasDataId(HttpServletRequest request) { String dataid = request.getParameter("dataId"); return dataid; } private String getAliasGroup(HttpServletRequest request) { String group = request.getParameter("groupName"); if (StringUtils.isBlank(group)) { group = request.getParameter("group"); } return group; } private String getAliasIp(HttpServletRequest request) { String ip = request.getParameter("ip"); return ip; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/paramcheck/ConfigListenerHttpParamExtractor.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.paramcheck; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.core.exception.ErrorCode; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; /** * ConfigListener http param extractor. * * @author zhuoguang */ public class ConfigListenerHttpParamExtractor extends AbstractHttpParamExtractor { static final char WORD_SEPARATOR_CHAR = (char) 2; static final char LINE_SEPARATOR_CHAR = (char) 1; @Override public List extractParam(HttpServletRequest request) throws NacosRuntimeException { ArrayList paramInfos = new ArrayList<>(); String listenConfigs = request.getParameter("Listening-Configs"); if (StringUtils.isBlank(listenConfigs)) { return paramInfos; } try { listenConfigs = URLDecoder.decode(listenConfigs, Constants.ENCODE); } catch (UnsupportedEncodingException e) { throw new NacosRuntimeException(ErrorCode.UnKnowError.getCode(), e); } if (StringUtils.isBlank(listenConfigs)) { return paramInfos; } String[] lines = listenConfigs.split(Character.toString(LINE_SEPARATOR_CHAR)); for (String line : lines) { ParamInfo paramInfo = new ParamInfo(); String[] words = line.split(Character.toString(WORD_SEPARATOR_CHAR)); if (words.length < 2 || words.length > 4) { throw new IllegalArgumentException("invalid probeModify"); } paramInfo.setDataId(words[0]); paramInfo.setGroup(words[1]); if (words.length == 4) { paramInfo.setNamespaceId(words[3]); } paramInfos.add(paramInfo); } return paramInfos; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeBatchListenRequestHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigBatchListenRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.core.utils.StringPool; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * config change listen request handler. * * @author liuzunfei * @version $Id: ConfigChangeListenRequestHandler.java, v 0.1 2020年07月14日 10:11 AM liuzunfei Exp $ */ @Component public class ConfigChangeBatchListenRequestHandler extends RequestHandler { @Autowired private ConfigChangeListenContext configChangeListenContext; @Override @NamespaceValidation @TpsControl(pointName = "ConfigListen") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigBatchListenRequestParamExtractor.class) public ConfigChangeBatchListenResponse handle(ConfigBatchListenRequest configChangeListenRequest, RequestMeta meta) throws NacosException { String connectionId = StringPool.get(meta.getConnectionId()); String tag = configChangeListenRequest.getHeader(Constants.VIPSERVER_TAG); ParamUtils.checkParam(tag); ConfigChangeBatchListenResponse configChangeBatchListenResponse = new ConfigChangeBatchListenResponse(); for (ConfigBatchListenRequest.ConfigListenContext listenContext : configChangeListenRequest.getConfigListenContexts()) { boolean isNeedTransferNamespace = NamespaceUtil.isNeedTransferNamespace(listenContext.getTenant()); String namespaceId = NamespaceUtil.processNamespaceParameter(listenContext.getTenant()); String groupKey = GroupKey2.getKey(listenContext.getDataId(), listenContext.getGroup(), namespaceId); groupKey = StringPool.get(groupKey); String md5 = StringPool.get(listenContext.getMd5()); if (configChangeListenRequest.isListen()) { configChangeListenContext.addListen(groupKey, md5, connectionId, isNeedTransferNamespace); boolean isUptoDate = ConfigCacheService.isUptodate(groupKey, md5, meta.getClientIp(), tag, meta.getAppLabels()); if (!isUptoDate) { configChangeBatchListenResponse.addChangeConfig(listenContext.getDataId(), listenContext.getGroup(), listenContext.getTenant()); } } else { configChangeListenContext.removeListen(groupKey, connectionId); } } return configChangeBatchListenResponse; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeClusterSyncRequestHandler.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest; import com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RemoteConstants; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.VersionUtils; import com.alibaba.nacos.config.server.configuration.ConfigCompatibleConfig; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.dump.DumpRequest; import com.alibaba.nacos.config.server.service.dump.DumpService; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.core.remote.grpc.InvokeSource; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.stereotype.Component; /** * handler to handler config change from other servers. * * @author liuzunfei * @version $Id: ConfigChangeClusterSyncRequestHandler.java, v 0.1 2020年08月11日 4:35 PM liuzunfei Exp $ */ @Component @InvokeSource(source = {RemoteConstants.LABEL_SOURCE_CLUSTER}) public class ConfigChangeClusterSyncRequestHandler extends RequestHandler { private final DumpService dumpService; private ConfigMigrateService configMigrateService; public ConfigChangeClusterSyncRequestHandler(DumpService dumpService, ConfigMigrateService configMigrateService) { this.dumpService = dumpService; this.configMigrateService = configMigrateService; } @Override @NamespaceValidation @TpsControl(pointName = "ClusterConfigChangeNotify") @ExtractorManager.Extractor(rpcExtractor = ConfigRequestParamExtractor.class) @Secured(signType = SignType.CONFIG, apiType = ApiType.INNER_API) public ConfigChangeClusterSyncResponse handle(ConfigChangeClusterSyncRequest configChangeSyncRequest, RequestMeta meta) throws NacosException { checkCompatity(configChangeSyncRequest, meta); ParamUtils.checkParam(configChangeSyncRequest.getTag()); DumpRequest dumpRequest = DumpRequest.create(configChangeSyncRequest.getDataId(), configChangeSyncRequest.getGroup(), configChangeSyncRequest.getTenant(), configChangeSyncRequest.getLastModified(), meta.getClientIp()); dumpRequest.setGrayName(configChangeSyncRequest.getGrayName()); dumpService.dump(dumpRequest); return new ConfigChangeClusterSyncResponse(); } /** * if notified from old server,try to migrate and transfer gray model. * * @param configChangeSyncRequest request. */ private void checkCompatity(ConfigChangeClusterSyncRequest configChangeSyncRequest, RequestMeta meta) { if (PropertyUtil.isGrayCompatibleModel() && StringUtils.isBlank(configChangeSyncRequest.getGrayName())) { if (configChangeSyncRequest.isBeta() || StringUtils.isNotBlank(configChangeSyncRequest.getTag())) { String grayName = null; //from old server ,beta or tag persist into old model,try migrate and transfer gray model. if (configChangeSyncRequest.isBeta()) { configMigrateService.checkMigrateBeta(configChangeSyncRequest.getDataId(), configChangeSyncRequest.getGroup(), configChangeSyncRequest.getTenant()); grayName = BetaGrayRule.TYPE_BETA; } else { configMigrateService.checkMigrateTag(configChangeSyncRequest.getDataId(), configChangeSyncRequest.getGroup(), configChangeSyncRequest.getTenant(), configChangeSyncRequest.getTag()); grayName = TagGrayRule.TYPE_TAG + "_" + configChangeSyncRequest.getTag(); } configChangeSyncRequest.setGrayName(grayName); } } if (!checkNamespaceCompatible(configChangeSyncRequest, meta)) { return; } if (StringUtils.isNotBlank(configChangeSyncRequest.getGrayName())) { configMigrateService.namespaceMigrateGray(configChangeSyncRequest.getDataId(), configChangeSyncRequest.getGroup(), configChangeSyncRequest.getTenant(), configChangeSyncRequest.getGrayName()); } else { configMigrateService.namespaceMigrate(configChangeSyncRequest.getDataId(), configChangeSyncRequest.getGroup(), configChangeSyncRequest.getTenant()); } configChangeSyncRequest.setTenant("public"); } /** * Check namespace compatible boolean. * * @param configSyncRequest the config sync request * @param meta the meta * @return the boolean */ public boolean checkNamespaceCompatible(ConfigChangeClusterSyncRequest configSyncRequest, RequestMeta meta) { if (!ConfigCompatibleConfig.getInstance().isNamespaceCompatibleMode()) { return false; } final String ignoreCheckVersion = "3.0.0"; final String clusterVersionPrefixNew = "Nacos-Server:v"; final String clusterVersionPrefixOld = "Nacos-Java-Client:v"; try { String version = null; if (meta.getClientVersion().split(clusterVersionPrefixNew).length > 1) { version = meta.getClientVersion().split(clusterVersionPrefixNew)[1]; } else { version = meta.getClientVersion().split(clusterVersionPrefixOld)[1]; } if (VersionUtils.compareVersion(version, ignoreCheckVersion) >= 0) { return false; } } catch (Exception e) { Loggers.REMOTE_DIGEST.error("checkCompatity error", e); } return StringUtils.equals(configSyncRequest.getTenant(), "public") || StringUtils.isBlank( configSyncRequest.getTenant()); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContext.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.ConfigListenState; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; /** * config change listen context. * * @author liuzunfei * @version $Id: ConfigChangeListenContext.java, v 0.1 2020年07月20日 1:37 PM liuzunfei Exp $ */ @Component public class ConfigChangeListenContext { /** * groupKey-> connection set. */ private ConcurrentHashMap> groupKeyContext = new ConcurrentHashMap<>(); /** * connectionId-> group key set. */ private ConcurrentHashMap> connectionIdContext = new ConcurrentHashMap<>(); /** * add listen. * * @param groupKey groupKey. * @param connectionId connectionId. */ public synchronized void addListen(String groupKey, String md5, String connectionId, boolean isNamespaceTransfer) { // 1.add groupKeyContext groupKeyContext.computeIfAbsent(groupKey, k -> new HashSet<>()).add(connectionId); // 2.add connectionIdContext ConfigListenState listenState = new ConfigListenState(md5); listenState.setNamespaceTransfer(isNamespaceTransfer); connectionIdContext.computeIfAbsent(connectionId, k -> new HashMap<>(16)).put(groupKey, listenState); } /** * remove listen context for connection id . * * @param groupKey groupKey. * @param connectionId connection id. */ public synchronized void removeListen(String groupKey, String connectionId) { //1. remove groupKeyContext Set connectionIds = groupKeyContext.get(groupKey); if (connectionIds != null) { connectionIds.remove(connectionId); if (connectionIds.isEmpty()) { groupKeyContext.remove(groupKey); } } //2.remove connectionIdContext HashMap groupKeys = connectionIdContext.get(connectionId); if (groupKeys != null) { groupKeys.remove(groupKey); } } /** * get listeners of the group key. * * @param groupKey groupKey. * @return the copy of listeners, may be return null. */ public synchronized Set getListeners(String groupKey) { HashSet strings = groupKeyContext.get(groupKey); if (CollectionUtils.isNotEmpty(strings)) { Set listenConnections = new HashSet<>(); safeCopy(strings, listenConnections); return listenConnections; } return null; } /** * copy collections. * * @param src may be modified concurrently * @param dest dest collection */ private void safeCopy(Collection src, Collection dest) { Iterator iterator = src.iterator(); while (iterator.hasNext()) { dest.add(iterator.next()); } } /** * remove the context related to the connection id. * * @param connectionId connectionId. */ public synchronized void clearContextForConnectionId(final String connectionId) { Map listenKeys = getListenKeys(connectionId); if (listenKeys == null) { connectionIdContext.remove(connectionId); return; } for (Map.Entry groupKey : listenKeys.entrySet()) { Set connectionIds = groupKeyContext.get(groupKey.getKey()); if (CollectionUtils.isNotEmpty(connectionIds)) { connectionIds.remove(connectionId); if (connectionIds.isEmpty()) { groupKeyContext.remove(groupKey.getKey()); } } else { groupKeyContext.remove(groupKey.getKey()); } } connectionIdContext.remove(connectionId); } /** * get listen keys. * * @param connectionId connection id. * @return listen group keys of the connection id, key:group key,value:md5 */ public synchronized Map getListenKeys(String connectionId) { HashMap stringStringHashMap = connectionIdContext.get(connectionId); if (stringStringHashMap != null) { HashMap md5Map = new HashMap<>(stringStringHashMap.size()); for (Map.Entry entry : stringStringHashMap.entrySet()) { md5Map.put(entry.getKey(), entry.getValue().getMd5()); } return md5Map; } else { return null; } } /** * get md5. * * @param connectionId connection id. * @return md5 of the listen group key. */ public String getListenKeyMd5(String connectionId, String groupKey) { Map groupKeyContexts = connectionIdContext.get(connectionId); return groupKeyContexts == null ? null : groupKeyContexts.get(groupKey).getMd5(); } public ConfigListenState getConfigListenState(String connectionId, String groupKey) { Map groupKeyContexts = connectionIdContext.get(connectionId); return groupKeyContexts == null ? null : groupKeyContexts.get(groupKey); } public synchronized HashMap getConfigListenStates(String connectionId) { HashMap configListenStateHashMap = connectionIdContext.get(connectionId); return configListenStateHashMap == null ? null : new HashMap<>(configListenStateHashMap); } /** * get connection count. * * @return count of long connections. */ public int getConnectionCount() { return connectionIdContext.size(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigClusterRpcClientProxy.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy; import org.springframework.stereotype.Service; /** * ConfigClusterRpcClientProxy. * * @author liuzunfei * @version $Id: ConfigClusterRpcClientProxy.java, v 0.1 2020年08月11日 4:28 PM liuzunfei Exp $ */ @Service public class ConfigClusterRpcClientProxy { final ClusterRpcClientProxy clusterRpcClientProxy; public ConfigClusterRpcClientProxy(ClusterRpcClientProxy clusterRpcClientProxy) { this.clusterRpcClientProxy = clusterRpcClientProxy; } /** * sync config change request. * * @param member member of server. * @param request request of config change sync. * @param callBack callBack of config change sync. * @throws NacosException exception. */ public void syncConfigChange(Member member, ConfigChangeClusterSyncRequest request, RequestCallBack callBack) throws NacosException { clusterRpcClientProxy.asyncRequest(member, request, callBack); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.remote.ClientConnectionEventListener; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.utils.Loggers; import org.springframework.stereotype.Component; /** * ConfigConnectionEventListener. * * @author liuzunfei * @version $Id: ConfigConnectionEventListener.java, v 0.1 2020年07月20日 2:27 PM liuzunfei Exp $ */ @Component public class ConfigConnectionEventListener extends ClientConnectionEventListener { final ConfigChangeListenContext configChangeListenContext; final ConfigFuzzyWatchContextService configFuzzyWatchContextService; public ConfigConnectionEventListener(ConfigChangeListenContext configChangeListenContext, ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.configChangeListenContext = configChangeListenContext; this.configFuzzyWatchContextService = configFuzzyWatchContextService; } @Override public void clientConnected(Connection connect) { //Do nothing. } @Override public void clientDisConnected(Connection connect) { String connectionId = connect.getMetaInfo().getConnectionId(); Loggers.REMOTE_DIGEST.info("[{}]client disconnected,clear config listen context", connectionId); configChangeListenContext.clearContextForConnectionId(connectionId); configFuzzyWatchContextService.clearFuzzyWatchContext(connectionId); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifier.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.core.utils.Loggers; import org.springframework.stereotype.Component; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.CONFIG_CHANGED; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; /** * Notify remote clients about fuzzy listen configuration changes. Use subscriber mode to monitor local data changes, * and push notifications to remote clients accordingly. * * @author stone-98 * @date 2024/3/18 */ @Component public class ConfigFuzzyWatchChangeNotifier extends Subscriber { private final ConnectionManager connectionManager; private RpcPushService rpcPushService; private final ConfigFuzzyWatchContextService configFuzzyWatchContextService; /** * Constructs RpcFuzzyListenConfigChangeNotifier with the specified dependencies. * * @param connectionManager The manager for connections. * @param rpcPushService The service for RPC push. */ public ConfigFuzzyWatchChangeNotifier(ConnectionManager connectionManager, RpcPushService rpcPushService, ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.rpcPushService = rpcPushService; this.connectionManager = connectionManager; this.configFuzzyWatchContextService = configFuzzyWatchContextService; NotifyCenter.registerSubscriber(this); } @Override public void onEvent(LocalDataChangeEvent event) { boolean exists = ConfigCacheService.getContentCache(event.groupKey) != null; //can not recognize add or update, set config_changed here String changedType = exists ? CONFIG_CHANGED : DELETE_CONFIG; boolean needNotify = configFuzzyWatchContextService.syncGroupKeyContext(event.groupKey, changedType); if (needNotify) { for (String clientId : configFuzzyWatchContextService.getMatchedClients(event.groupKey)) { Connection connection = connectionManager.getConnection(clientId); if (null == connection) { Loggers.REMOTE_PUSH.warn( "clientId not found, Config change notification not sent. clientId={},keyGroupPattern={}", clientId, event.groupKey); continue; } ConfigFuzzyWatchChangeNotifyRequest request = new ConfigFuzzyWatchChangeNotifyRequest(event.groupKey, changedType); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); FuzzyWatchChangeNotifyTask fuzzyWatchChangeNotifyTask = new FuzzyWatchChangeNotifyTask( connectionManager, rpcPushService, request, maxPushRetryTimes, clientId); fuzzyWatchChangeNotifyTask.scheduleSelf(); } } } @Override public Class subscribeType() { return LocalDataChangeEvent.class; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchRequestHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchRequest; import com.alibaba.nacos.api.config.remote.response.ConfigFuzzyWatchResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigFuzzyWatchRequestParamsExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.core.utils.StringPool; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.stereotype.Component; import java.util.Set; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_CANCEL_WATCH; import static com.alibaba.nacos.api.common.Constants.WATCH_TYPE_WATCH; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT; /** * Handler for processing batch fuzzy listen requests. *

    * This handler is responsible for processing batch fuzzy listen requests sent by clients. It adds or removes clients * from the fuzzy listening context based on the request, and publishes corresponding events to notify interested * parties. *

    * * @author stone-98 * @date 2024/3/4 */ @Component public class ConfigFuzzyWatchRequestHandler extends RequestHandler { private ConfigFuzzyWatchContextService configFuzzyWatchContextService; public ConfigFuzzyWatchRequestHandler(ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.configFuzzyWatchContextService = configFuzzyWatchContextService; } /** * Handles the batch fuzzy listen request. *

    * This method processes the batch fuzzy listen request by adding or removing clients from the fuzzy listening * context based on the request, and publishes corresponding events to notify interested parties. *

    * * @param request The batch fuzzy listen request * @param meta Request meta information * @return The response to the batch fuzzy listen request * @throws NacosException If an error occurs while processing the request */ @Override @NamespaceValidation @TpsControl(pointName = "ConfigFuzzyWatch") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigFuzzyWatchRequestParamsExtractor.class) public ConfigFuzzyWatchResponse handle(ConfigFuzzyWatchRequest request, RequestMeta meta) throws NacosException { String connectionId = StringPool.get(meta.getConnectionId()); String groupKeyPattern = request.getGroupKeyPattern(); if (WATCH_TYPE_WATCH.equals(request.getWatchType())) { // Add client to the fuzzy listening context try { configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, connectionId); // Get existing group keys for the client and publish initialization event Set clientExistingGroupKeys = request.getReceivedGroupKeys(); NotifyCenter.publishEvent( new ConfigFuzzyWatchEvent(connectionId, clientExistingGroupKeys, groupKeyPattern, request.isInitializing())); } catch (NacosException nacosException) { ConfigFuzzyWatchResponse configFuzzyWatchResponse = new ConfigFuzzyWatchResponse(); configFuzzyWatchResponse.setErrorInfo(nacosException.getErrCode(), nacosException.getErrMsg()); return configFuzzyWatchResponse; } boolean reachToUpLimit = configFuzzyWatchContextService.reachToUpLimit(groupKeyPattern); if (reachToUpLimit) { ConfigFuzzyWatchResponse configFuzzyWatchResponse = new ConfigFuzzyWatchResponse(); configFuzzyWatchResponse.setErrorInfo(FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_MATCH_COUNT_OVER_LIMIT.getMsg()); return configFuzzyWatchResponse; } } else if (WATCH_TYPE_CANCEL_WATCH.equals(request.getWatchType())) { configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, connectionId); } // Return response return new ConfigFuzzyWatchResponse(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifier.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.SmartSubscriber; import com.alibaba.nacos.common.task.BatchTaskCounter; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_DIFF_SYNC_NOTIFY; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; /** * Handles batch fuzzy listen events and pushes corresponding notifications to clients. * * @author stone-98 * @date 2024/3/18 */ @Component(value = "configFuzzyWatchSyncNotifier") public class ConfigFuzzyWatchSyncNotifier extends SmartSubscriber { private final ConnectionManager connectionManager; private final RpcPushService rpcPushService; private final ConfigFuzzyWatchContextService configFuzzyWatchContextService; public ConfigFuzzyWatchSyncNotifier(ConnectionManager connectionManager, RpcPushService rpcPushService, ConfigFuzzyWatchContextService configFuzzyWatchContextService) { this.connectionManager = connectionManager; this.rpcPushService = rpcPushService; this.configFuzzyWatchContextService = configFuzzyWatchContextService; NotifyCenter.registerSubscriber(this); } /** * Handles the ConfigBatchFuzzyListenEvent. This method is responsible for processing batch fuzzy listen events and * pushing corresponding notifications to clients. * * @param event The ConfigBatchFuzzyListenEvent to handle */ public void handleFuzzyWatchEvent(ConfigFuzzyWatchEvent event) { // Match client effective group keys based on the event pattern, client IP, and tag Set matchGroupKeys = configFuzzyWatchContextService.matchGroupKeys(event.getGroupKeyPattern()); // Retrieve existing group keys for the client from the event Set clientExistingGroupKeys = event.getClientExistingGroupKeys(); // Calculate and merge configuration states based on matched and existing group keys List configStates = FuzzyGroupKeyPattern.diffGroupKeys(matchGroupKeys, clientExistingGroupKeys); if (CollectionUtils.isEmpty(configStates)) { int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); if (event.isInitializing()) { ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( event.getGroupKeyPattern()); // Create RPC push task and push the request to the client FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(connectionManager, rpcPushService, request, null, maxPushRetryTimes, event.getConnectionId()); fuzzyWatchSyncNotifyTask.scheduleSelf(); } } else { // delete notify protection when pattern match count over limit because matchGroupKeys may not a full set. if (configFuzzyWatchContextService.reachToUpLimit(event.getGroupKeyPattern())) { configStates.removeIf(item -> !item.isExist()); } String syncType = event.isInitializing() ? FUZZY_WATCH_INIT_NOTIFY : FUZZY_WATCH_DIFF_SYNC_NOTIFY; int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); // Divide config states into batches List> divideConfigStatesIntoBatches = divideConfigStatesIntoBatches( configStates, batchSize); // Calculate the number of batches and initialize push batch finish count int totalBatch = divideConfigStatesIntoBatches.size(); BatchTaskCounter batchTaskCounter = new BatchTaskCounter(divideConfigStatesIntoBatches.size()); int currentBatch = 1; for (List configStateList : divideConfigStatesIntoBatches) { // Map config states to FuzzyListenNotifyDiffRequest.Context objects Set contexts = configStateList.stream().map(state -> { String changeType = state.isExist() ? Constants.ConfigChangedType.ADD_CONFIG : Constants.ConfigChangedType.DELETE_CONFIG; return ConfigFuzzyWatchSyncRequest.Context.build(state.getGroupKey(), changeType); }).collect(Collectors.toSet()); ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildSyncRequest(syncType, contexts, event.getGroupKeyPattern(), totalBatch, currentBatch); int maxPushRetryTimes = ConfigCommonConfig.getInstance().getMaxPushRetryTimes(); // Create RPC push task and push the request to the client FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(connectionManager, rpcPushService, request, batchTaskCounter, maxPushRetryTimes, event.getConnectionId()); fuzzyWatchSyncNotifyTask.scheduleSelf(); currentBatch++; } } } @Override public List> subscribeTypes() { List> result = new LinkedList<>(); result.add(ConfigFuzzyWatchEvent.class); return result; } @Override public void onEvent(Event event) { if (event instanceof ConfigFuzzyWatchEvent) { handleFuzzyWatchEvent((ConfigFuzzyWatchEvent) event); } } /** * Divides a collection of items into batches. * * @param configStates The collection of items to be divided into batches * @param batchSize The size of each batch * @param The type of items in the collection * @return A list of batches, each containing a sublist of items */ private List> divideConfigStatesIntoBatches(Collection configStates, int batchSize) { // Initialize an index to track the current batch number AtomicInteger index = new AtomicInteger(); // Group the elements into batches based on their index divided by the batch size return new ArrayList<>( configStates.stream().collect(Collectors.groupingBy(e -> index.getAndIncrement() / batchSize)) .values()); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigPublishRequestHandler.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.exception.ConfigAlreadyExistsException; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import org.springframework.stereotype.Component; import static com.alibaba.nacos.config.server.constant.Constants.RPC; /** * request handler to publish config. * * @author liuzunfei * @version $Id: ConfigPublishRequestHandler.java, v 0.1 2020年07月16日 4:41 PM liuzunfei Exp $ */ @Component public class ConfigPublishRequestHandler extends RequestHandler { private ConfigOperationService configOperationService; public ConfigPublishRequestHandler(ConfigOperationService configOperationService) { this.configOperationService = configOperationService; } @Override @NamespaceValidation @TpsControl(pointName = "ConfigPublish") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigRequestParamExtractor.class) public ConfigPublishResponse handle(ConfigPublishRequest request, RequestMeta meta) throws NacosException { try { String dataId = request.getDataId(); String group = request.getGroup(); String content = request.getContent(); final boolean namespaceTransferred = NamespaceUtil.isNeedTransferNamespace(request.getTenant()); final String tenant = NamespaceUtil.processNamespaceParameter(request.getTenant()); final String srcIp = meta.getClientIp(); final String tag = request.getAdditionParam("tag"); final String appName = request.getAdditionParam("appName"); final String type = request.getAdditionParam("type"); final String srcUser = request.getAdditionParam("src_user"); final String encryptedDataKey = request.getAdditionParam("encryptedDataKey"); // check tenant ParamUtils.checkParam(dataId, group, "datumId", content); ParamUtils.checkParam(tag); ConfigForm configForm = new ConfigForm(); configForm.setDataId(dataId); configForm.setGroup(group); configForm.setNamespaceId(tenant); configForm.setContent(content); configForm.setTag(tag); configForm.setAppName(appName); configForm.setSrcUser(srcUser); configForm.setConfigTags(request.getAdditionParam("config_tags")); configForm.setDesc(request.getAdditionParam("desc")); configForm.setUse(request.getAdditionParam("use")); configForm.setEffect(request.getAdditionParam("effect")); configForm.setType(type); configForm.setSchema(request.getAdditionParam("schema")); if (!ConfigType.isValidType(type)) { configForm.setType(ConfigType.getDefaultType().getType()); } ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setSrcIp(srcIp); configRequestInfo.setSrcType(RPC); configRequestInfo.setRequestIpApp(meta.getLabels().get(Constants.APPNAME)); configRequestInfo.setBetaIps(request.getAdditionParam("betaIps")); configRequestInfo.setCasMd5(request.getCasMd5()); configRequestInfo.setNamespaceTransferred(namespaceTransferred); String encryptedDataKeyFinal = null; if (StringUtils.isNotBlank(encryptedDataKey)) { encryptedDataKeyFinal = encryptedDataKey; } else { Pair pair = EncryptionHandler.encryptHandler(dataId, content); content = pair.getSecond(); encryptedDataKeyFinal = pair.getFirst(); configForm.setContent(content); } try { configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKeyFinal); return ConfigPublishResponse.buildSuccessResponse(); } catch (NacosApiException | ConfigAlreadyExistsException ex) { return ConfigPublishResponse.buildFailResponse(ResponseCode.FAIL.getCode(), ex.getErrMsg()); } } catch (Exception e) { Loggers.REMOTE_DIGEST.error("[ConfigPublishRequestHandler] publish config error ,request ={}", request, e); return ConfigPublishResponse.buildFailResponse( (e instanceof NacosException) ? ((NacosException) e).getErrCode() : ResponseCode.FAIL.getCode(), e.getMessage()); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.query.ConfigChainRequestExtractorService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.net.URLEncoder; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE_UTF8; import static com.alibaba.nacos.config.server.utils.LogUtil.PULL_LOG; import static com.alibaba.nacos.config.server.utils.RequestUtil.CLIENT_APPNAME_HEADER; /** * ConfigQueryRequestHandler. * * @author liuzunfei * @version $Id: ConfigQueryRequestHandler.java, v 0.1 2020年07月14日 9:54 AM liuzunfei Exp $ */ @Component public class ConfigQueryRequestHandler extends RequestHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigQueryRequestHandler.class); private final ConfigQueryChainService configQueryChainService; public ConfigQueryRequestHandler(ConfigQueryChainService configQueryChainService) { this.configQueryChainService = configQueryChainService; } @Override @NamespaceValidation @TpsControl(pointName = "ConfigQuery") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigRequestParamExtractor.class) public ConfigQueryResponse handle(ConfigQueryRequest request, RequestMeta meta) throws NacosException { try { request.setTenant(NamespaceUtil.processNamespaceParameter(request.getTenant())); String dataId = request.getDataId(); String group = request.getGroup(); String tenant = request.getTenant(); String groupKey = GroupKey2.getKey(dataId, group, tenant); boolean notify = request.isNotify(); String requestIpApp = meta.getLabels().get(CLIENT_APPNAME_HEADER); String clientIp = meta.getClientIp(); ConfigQueryChainRequest chainRequest = ConfigChainRequestExtractorService.getExtractor() .extract(request, meta); ConfigQueryChainResponse chainResponse = configQueryChainService.handle(chainRequest); if (ResponseCode.FAIL.getCode() == chainResponse.getResultCode()) { return ConfigQueryResponse.buildFailResponse(ResponseCode.FAIL.getCode(), chainResponse.getMessage()); } if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND) { return handlerConfigNotFound(request.getDataId(), request.getGroup(), request.getTenant(), requestIpApp, clientIp, notify); } if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_QUERY_CONFLICT) { return handlerConfigConflict(clientIp, groupKey); } ConfigQueryResponse response = new ConfigQueryResponse(); // Check if there is a matched gray rule if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY) { if (BetaGrayRule.TYPE_BETA.equals(chainResponse.getMatchedGray().getGrayRule().getType())) { response.setBeta(true); } else if (TagGrayRule.TYPE_TAG.equals(chainResponse.getMatchedGray().getGrayRule().getType())) { response.setTag(URLEncoder.encode(chainResponse.getMatchedGray().getRawGrayRule(), ENCODE_UTF8)); } } // Check if there is a special tag if (chainResponse.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.SPECIAL_TAG_CONFIG_NOT_FOUND) { response.setTag(request.getTag()); } response.setMd5(chainResponse.getMd5()); response.setEncryptedDataKey(chainResponse.getEncryptedDataKey()); response.setContent(chainResponse.getContent()); response.setContentType(chainResponse.getConfigType()); response.setLastModified(chainResponse.getLastModified()); String pullType = ConfigTraceService.PULL_TYPE_OK; if (chainResponse.getContent() == null) { pullType = ConfigTraceService.PULL_TYPE_NOTFOUND; response.setErrorInfo(ConfigQueryResponse.CONFIG_NOT_FOUND, "config data not exist"); } else { response.setResultCode(ResponseCode.SUCCESS.getCode()); } String pullEvent = resolvePullEventType(chainResponse, request.getTag()); LogUtil.PULL_CHECK_LOG.warn("{}|{}|{}|{}", groupKey, clientIp, response.getMd5(), TimeUtils.getCurrentTimeStr()); final long delayed = System.currentTimeMillis() - response.getLastModified(); ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, response.getLastModified(), pullEvent, pullType, delayed, clientIp, notify, "grpc"); return response; } catch (Exception e) { LOGGER.error("Failed to handle grpc configuration query", e); return ConfigQueryResponse.buildFailResponse(ResponseCode.FAIL.getCode(), e.getMessage()); } } private ConfigQueryResponse handlerConfigConflict(String clientIp, String groupKey) { ConfigQueryResponse response = new ConfigQueryResponse(); PULL_LOG.info("[client-get] clientIp={}, {}, get data during dump", clientIp, groupKey); response.setErrorInfo(ConfigQueryResponse.CONFIG_QUERY_CONFLICT, "requested file is being modified, please try later."); return response; } private ConfigQueryResponse handlerConfigNotFound(String dataId, String group, String tenant, String requestIpApp, String clientIp, boolean notify) { //CacheItem No longer exists. It is impossible to simply calculate the push delayed. Here, simply record it as - 1. ConfigQueryResponse response = new ConfigQueryResponse(); ConfigTraceService.logPullEvent(dataId, group, tenant, requestIpApp, -1, ConfigTraceService.PULL_EVENT, ConfigTraceService.PULL_TYPE_NOTFOUND, -1, clientIp, notify, "grpc"); response.setErrorInfo(ConfigQueryResponse.CONFIG_NOT_FOUND, "config data not exist"); return response; } private String resolvePullEventType(ConfigQueryChainResponse chainResponse, String tag) { switch (chainResponse.getStatus()) { case CONFIG_FOUND_GRAY: ConfigCacheGray matchedGray = chainResponse.getMatchedGray(); if (matchedGray != null) { return ConfigTraceService.PULL_EVENT + "-" + matchedGray.getGrayName(); } else { return ConfigTraceService.PULL_EVENT; } case SPECIAL_TAG_CONFIG_NOT_FOUND: return ConfigTraceService.PULL_EVENT + "-" + TagGrayRule.TYPE_TAG + "-" + tag; default: return ConfigTraceService.PULL_EVENT; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/ConfigRemoveRequestHandler.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.namespace.filter.NamespaceValidation; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.paramcheck.impl.ConfigRequestParamExtractor; import com.alibaba.nacos.core.remote.RequestHandler; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.stereotype.Component; /** * handler to remove config. * * @author liuzunfei * @version $Id: ConfiRemoveRequestHandler.java, v 0.1 2020年07月16日 5:49 PM liuzunfei Exp $ */ @Component public class ConfigRemoveRequestHandler extends RequestHandler { private final ConfigInfoPersistService configInfoPersistService; private final ConfigInfoGrayPersistService configInfoGrayPersistService; private final ConfigOperationService configOperationService; public ConfigRemoveRequestHandler(ConfigInfoPersistService configInfoPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService, ConfigOperationService configOperationService) { this.configInfoPersistService = configInfoPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; this.configOperationService = configOperationService; } @Override @NamespaceValidation @TpsControl(pointName = "ConfigRemove") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG) @ExtractorManager.Extractor(rpcExtractor = ConfigRequestParamExtractor.class) public ConfigRemoveResponse handle(ConfigRemoveRequest configRemoveRequest, RequestMeta meta) throws NacosException { // check tenant String tenant = configRemoveRequest.getTenant(); tenant = NamespaceUtil.processNamespaceParameter(tenant); String dataId = configRemoveRequest.getDataId(); String group = configRemoveRequest.getGroup(); String tag = configRemoveRequest.getTag(); try { ParamUtils.checkTenant(tenant); ParamUtils.checkParam(dataId, group, "datumId", "rm"); ParamUtils.checkParam(tag); String clientIp = meta.getClientIp(); configOperationService.deleteConfig(dataId, group, tenant, tag, clientIp, null, Constants.RPC); return ConfigRemoveResponse.buildSuccessResponse(); } catch (Exception e) { Loggers.REMOTE_DIGEST.error("remove config error,error msg is {}", e.getMessage(), e); return ConfigRemoveResponse.buildFailResponse(e.getMessage()); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchChangeNotifyTask.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchChangeNotifyRequest; import com.alibaba.nacos.api.remote.AbstractPushCallBack; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; import java.util.concurrent.TimeUnit; /** * Represents a task for pushing notification to remote clients. */ class FuzzyWatchChangeNotifyTask implements Runnable { private static final String POINT_FUZZY_WATCH_CONFIG_PUSH = "POINT_FUZZY_WATCH_CONFIG_PUSH"; private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS = "POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS"; private static final String POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL = "POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL"; ConfigFuzzyWatchChangeNotifyRequest notifyRequest; final ConnectionManager connectionManager; final RpcPushService rpcPushService; int maxRetryTimes; int tryTimes = 0; String connectionId; /** * Constructs a RpcPushTask with the specified parameters. * * @param notifyRequest The notification request to be sent. * @param maxRetryTimes The maximum number of retry times. * @param connectionId The ID of the connection. */ public FuzzyWatchChangeNotifyTask(ConnectionManager connectionManager, RpcPushService rpcPushService, ConfigFuzzyWatchChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId) { this.connectionManager = connectionManager; this.rpcPushService = rpcPushService; this.notifyRequest = notifyRequest; this.maxRetryTimes = maxRetryTimes; this.connectionId = connectionId; } /** * Checks if the number of retry times exceeds the maximum limit. * * @return {@code true} if the number of retry times exceeds the maximum limit; otherwise, {@code false}. */ public boolean isOverTimes() { return maxRetryTimes > 0 && this.tryTimes >= maxRetryTimes; } @Override public void run() { if (isOverTimes()) { Loggers.REMOTE_PUSH.warn( "push callback retry fail over times.groupKey={},,clientId={}, will unregister client.", notifyRequest.getGroupKey(), connectionId); connectionManager.unregister(connectionId); } else if (connectionManager.getConnection(connectionId) == null) { // Client is already offline, ignore the task. Loggers.REMOTE_PUSH.warn( "Client is already offline, ignore the task. dataId={},groupKey={},tenant={},clientId={}", notifyRequest.getGroupKey(), connectionId); return; } TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH); if (!ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest).isSuccess()) { scheduleSelf(); } else { long timeout = ConfigCommonConfig.getInstance().getPushTimeout(); rpcPushService.pushWithCallback(connectionId, notifyRequest, new AbstractPushCallBack(timeout) { @Override public void onSuccess() { TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_SUCCESS); ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); } @Override public void onFail(Throwable e) { TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(POINT_FUZZY_WATCH_CONFIG_PUSH_FAIL); ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); Loggers.REMOTE_PUSH.warn("Push fail, groupKey={}, clientId={}", notifyRequest.getGroupKey(), connectionId, e); FuzzyWatchChangeNotifyTask.this.scheduleSelf(); } }, ConfigExecutor.getClientConfigNotifierServiceExecutor()); } } void scheduleSelf() { ConfigExecutor.scheduleClientConfigNotifier(this, tryTimes * 2L, TimeUnit.SECONDS); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallback.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; import com.alibaba.nacos.api.remote.AbstractPushCallBack; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; /** * Represents a callback for handling the result of an RPC push operation. * * @author stone-98 */ class FuzzyWatchSyncNotifyCallback extends AbstractPushCallBack { /** * The RpcPushTask associated with the callback. */ FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask; /** * Constructs a new RpcPushCallback with the specified parameters. * * @param fuzzyWatchSyncNotifyTask The RpcPushTask associated with the callback */ public FuzzyWatchSyncNotifyCallback(FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask) { super(3000L); this.fuzzyWatchSyncNotifyTask = fuzzyWatchSyncNotifyTask; } /** * Handles the successful completion of the RPC push operation. */ @Override public void onSuccess() { // Check TPS limits TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(FuzzyWatchSyncNotifyTask.CONFIG_FUZZY_WATCH_CONFIG_SYNC_SUCCESS); ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); if (fuzzyWatchSyncNotifyTask.batchTaskCounter != null) { fuzzyWatchSyncNotifyTask.batchTaskCounter.batchSuccess( fuzzyWatchSyncNotifyTask.notifyRequest.getCurrentBatch()); if (fuzzyWatchSyncNotifyTask.batchTaskCounter.batchCompleted() && fuzzyWatchSyncNotifyTask.notifyRequest.getSyncType().equals(FUZZY_WATCH_INIT_NOTIFY)) { ConfigFuzzyWatchSyncRequest request = ConfigFuzzyWatchSyncRequest.buildInitFinishRequest( fuzzyWatchSyncNotifyTask.notifyRequest.getGroupKeyPattern()); // Create RPC push task and push the request to the client FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTaskFinish = new FuzzyWatchSyncNotifyTask( fuzzyWatchSyncNotifyTask.connectionManager, fuzzyWatchSyncNotifyTask.rpcPushService, request, null, fuzzyWatchSyncNotifyTask.maxRetryTimes, fuzzyWatchSyncNotifyTask.connectionId); fuzzyWatchSyncNotifyTaskFinish.scheduleSelf(); } } } /** * Handles the failure of the RPC push operation. * * @param e The exception thrown during the operation */ @Override public void onFail(Throwable e) { // Check TPS limits TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(FuzzyWatchSyncNotifyTask.CONFIG_FUZZY_WATCH_CONFIG_SYNC_FAIL); ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest); // Log the failure and retry the task Loggers.REMOTE_PUSH.warn("Push fail, groupKeyPattern={}, clientId={}", fuzzyWatchSyncNotifyTask.notifyRequest.getGroupKeyPattern(), fuzzyWatchSyncNotifyTask.connectionId, e); fuzzyWatchSyncNotifyTask.scheduleSelf(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyTask.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; import com.alibaba.nacos.common.task.BatchTaskCounter; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; import java.util.concurrent.TimeUnit; /** * Represents a task for pushing FuzzyListenNotifyDiffRequest to clients. * * @author stone-98 */ class FuzzyWatchSyncNotifyTask implements Runnable { static final String CONFIG_FUZZY_WATCH_CONFIG_SYNC = "FUZZY_WATCH_CONFIG_SYNC_PUSH"; static final String CONFIG_FUZZY_WATCH_CONFIG_SYNC_SUCCESS = "CONFIG_FUZZY_WATCH_CONFIG_SYNC_SUCCESS"; static final String CONFIG_FUZZY_WATCH_CONFIG_SYNC_FAIL = "CONFIG_FUZZY_WATCH_CONFIG_SYNC_FAIL"; final ConnectionManager connectionManager; final RpcPushService rpcPushService; /** * The FuzzyListenNotifyDiffRequest to be pushed. */ ConfigFuzzyWatchSyncRequest notifyRequest; /** * The maximum number of times to retry pushing the request. */ int maxRetryTimes; /** * The current number of attempts made to push the request. */ int tryTimes = 0; /** * The ID of the connection associated with the client. */ String connectionId; BatchTaskCounter batchTaskCounter; /** * Constructs a new RpcPushTask with the specified parameters. * * @param notifyRequest The FuzzyListenNotifyDiffRequest to be pushed * @param batchTaskCounter The batchTaskCounter counter for tracking the number of finished push batches * @param maxRetryTimes The maximum number of times to retry pushing the request * @param connectionId The ID of the connection associated with the client */ public FuzzyWatchSyncNotifyTask(ConnectionManager connectionManager, RpcPushService rpcPushService, ConfigFuzzyWatchSyncRequest notifyRequest, BatchTaskCounter batchTaskCounter, int maxRetryTimes, String connectionId) { this.connectionManager = connectionManager; this.rpcPushService = rpcPushService; this.notifyRequest = notifyRequest; this.batchTaskCounter = batchTaskCounter; this.maxRetryTimes = maxRetryTimes; this.connectionId = connectionId; } /** * Checks if the maximum number of retry times has been reached. * * @return true if the maximum number of retry times has been reached, otherwise false */ public boolean isOverTimes() { return maxRetryTimes > 0 && this.tryTimes >= maxRetryTimes; } /** * Executes the task, attempting to push the request to the client. */ @Override public void run() { if (isOverTimes()) { // If over the maximum retry times, log a warning and unregister the client connection Loggers.REMOTE_PUSH.warn( "Push callback retry failed over times. groupKeyPattern={}, clientId={}, will unregister client.", notifyRequest.getGroupKeyPattern(), connectionId); connectionManager.unregister(connectionId); } else if (connectionManager.getConnection(connectionId) != null) { tryTimes++; TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(CONFIG_FUZZY_WATCH_CONFIG_SYNC); if (!ControlManagerCenter.getInstance().getTpsControlManager().check(tpsCheckRequest).isSuccess()) { scheduleSelf(); } else { rpcPushService.pushWithCallback(connectionId, notifyRequest, new FuzzyWatchSyncNotifyCallback(this), ConfigExecutor.getClientConfigNotifierServiceExecutor()); } } } void scheduleSelf() { ConfigExecutor.scheduleClientConfigNotifier(this, tryTimes * 2L, TimeUnit.SECONDS); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; import com.alibaba.nacos.api.remote.AbstractPushCallBack; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.ConnectionMeta; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.core.utils.Loggers; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.tps.TpsControlManager; import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.Set; import java.util.concurrent.TimeUnit; /** * ConfigChangeNotifier. * * @author liuzunfei * @version $Id: ConfigChangeNotifier.java, v 0.1 2020年07月20日 3:00 PM liuzunfei Exp $ */ @Component(value = "rpcConfigChangeNotifier") public class RpcConfigChangeNotifier extends Subscriber { private static final String POINT_CONFIG_PUSH = "CONFIG_PUSH_COUNT"; private static final String POINT_CONFIG_PUSH_SUCCESS = "CONFIG_PUSH_SUCCESS"; private static final String POINT_CONFIG_PUSH_FAIL = "CONFIG_PUSH_FAIL"; TpsControlManager tpsControlManager = ControlManagerCenter.getInstance().getTpsControlManager(); public RpcConfigChangeNotifier() { NotifyCenter.registerSubscriber(this); } @PostConstruct void registerTpsPoint() { tpsControlManager.registerTpsPoint(POINT_CONFIG_PUSH); tpsControlManager.registerTpsPoint(POINT_CONFIG_PUSH_SUCCESS); tpsControlManager.registerTpsPoint(POINT_CONFIG_PUSH_FAIL); } @Autowired ConfigChangeListenContext configChangeListenContext; @Autowired private RpcPushService rpcPushService; @Autowired private ConnectionManager connectionManager; /** * adaptor to config module ,when server side config change ,invoke this method. * * @param groupKey groupKey */ public void configDataChanged(String groupKey, String dataId, String group, String tenant) { Set listeners = configChangeListenContext.getListeners(groupKey); if (CollectionUtils.isEmpty(listeners)) { return; } int notifyClientCount = 0; for (final String client : listeners) { Connection connection = connectionManager.getConnection(client); if (connection == null) { continue; } boolean ifNamespaceTransfer = configChangeListenContext.getConfigListenState(client, groupKey).isNamespaceTransfer(); if (ifNamespaceTransfer) { tenant = null; } ConnectionMeta metaInfo = connection.getMetaInfo(); String clientIp = metaInfo.getClientIp(); ConfigChangeNotifyRequest notifyRequest = ConfigChangeNotifyRequest.build(dataId, group, tenant); RpcPushTask rpcPushRetryTask = new RpcPushTask(notifyRequest, ConfigCommonConfig.getInstance().getMaxPushRetryTimes(), client, clientIp, metaInfo.getAppName()); push(rpcPushRetryTask, connectionManager); notifyClientCount++; } Loggers.REMOTE_PUSH.info("push [{}] clients, groupKey=[{}]", notifyClientCount, groupKey); } @Override public void onEvent(LocalDataChangeEvent event) { String groupKey = event.groupKey; String[] strings = GroupKey.parseKey(groupKey); String dataId = strings[0]; String group = strings[1]; String tenant = strings.length > 2 ? strings[2] : ""; configDataChanged(groupKey, dataId, group, tenant); } @Override public Class subscribeType() { return LocalDataChangeEvent.class; } class RpcPushTask implements Runnable { ConfigChangeNotifyRequest notifyRequest; int maxRetryTimes = -1; int tryTimes = 0; String connectionId; String clientIp; String appName; public RpcPushTask(ConfigChangeNotifyRequest notifyRequest, int maxRetryTimes, String connectionId, String clientIp, String appName) { this.notifyRequest = notifyRequest; this.maxRetryTimes = maxRetryTimes; this.connectionId = connectionId; this.clientIp = clientIp; this.appName = appName; } public boolean isOverTimes() { return maxRetryTimes > 0 && this.tryTimes >= maxRetryTimes; } public int getTryTimes() { return tryTimes; } public ConfigChangeNotifyRequest getNotifyRequest() { return notifyRequest; } public int getMaxRetryTimes() { return maxRetryTimes; } public String getClientIp() { return clientIp; } public String getAppName() { return appName; } public String getConnectionId() { return connectionId; } @Override public void run() { tryTimes++; TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(POINT_CONFIG_PUSH); if (!tpsControlManager.check(tpsCheckRequest).isSuccess()) { push(this, connectionManager); } else { rpcPushService.pushWithCallback(connectionId, notifyRequest, new RpcPushCallback(this, tpsControlManager, connectionManager), ConfigExecutor.getClientConfigNotifierServiceExecutor()); } } } static class RpcPushCallback extends AbstractPushCallBack { RpcPushTask rpcPushTask; TpsControlManager tpsControlManager; ConnectionManager connectionManager; public RpcPushCallback(RpcPushTask rpcPushTask, TpsControlManager tpsControlManager, ConnectionManager connectionManager) { super(3000L); this.rpcPushTask = rpcPushTask; this.tpsControlManager = tpsControlManager; this.connectionManager = connectionManager; } @Override public void onSuccess() { TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(POINT_CONFIG_PUSH_SUCCESS); tpsControlManager.check(tpsCheckRequest); } @Override public void onFail(Throwable e) { TpsCheckRequest tpsCheckRequest = new TpsCheckRequest(); tpsCheckRequest.setPointName(POINT_CONFIG_PUSH_FAIL); tpsControlManager.check(tpsCheckRequest); Loggers.REMOTE_PUSH.warn("Push fail, dataId={}, group={}, tenant={}, clientId={}", rpcPushTask.getNotifyRequest().getDataId(), rpcPushTask.getNotifyRequest().getGroup(), rpcPushTask.getNotifyRequest().getTenant(), rpcPushTask.getConnectionId(), e); push(rpcPushTask, connectionManager); } } private static void push(RpcPushTask retryTask, ConnectionManager connectionManager) { ConfigChangeNotifyRequest notifyRequest = retryTask.getNotifyRequest(); if (retryTask.isOverTimes()) { Loggers.REMOTE_PUSH.warn( "push callback retry fail over times. dataId={},group={},tenant={},clientId={}, will unregister client.", notifyRequest.getDataId(), notifyRequest.getGroup(), notifyRequest.getTenant(), retryTask.getConnectionId()); connectionManager.unregister(retryTask.getConnectionId()); } else if (connectionManager.getConnection(retryTask.getConnectionId()) != null) { // first time:delay 0s; second time:delay 2s; third time:delay 4s ConfigExecutor.scheduleClientConfigNotifier(retryTask, retryTask.getTryTimes() * 2, TimeUnit.SECONDS); } else { // client is already offline, ignore task. } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/result/code/ResultCodeEnum.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.result.code; import com.alibaba.nacos.common.model.core.IResultCode; /** * ResultCodeEnum. * * @author klw * @ClassName: ResultCodeEnum * @Description: result code enum * @date 2019/6/28 14:43 */ @Deprecated public enum ResultCodeEnum implements IResultCode { /** * Common code. **/ SUCCESS(200, "处理成功"), ERROR(500, "服务器内部错误"), /** * Config use 100001 ~ 100999. **/ NAMESPACE_NOT_EXIST(100001, "目标 namespace 不存在"), METADATA_ILLEGAL(100002, "导入的元数据非法"), DATA_VALIDATION_FAILED(100003, "未读取到合法数据"), PARSING_DATA_FAILED(100004, "解析数据失败"), DATA_EMPTY(100005, "导入的文件数据为空"), NO_SELECTED_CONFIG(100006, "没有选择任何配置"); private int code; private String msg; ResultCodeEnum(int code, String codeMsg) { this.code = code; this.msg = codeMsg; } @Override public int getCode() { return code; } @Override public String getCodeMsg() { return msg; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ClientIpWhiteList.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.AclInfo; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.concurrent.atomic.AtomicReference; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; /** * Client ip whitelist. * * @author Nacos */ @Service public class ClientIpWhiteList { public static final String CLIENT_IP_WHITELIST_METADATA = "com.alibaba.nacos.metadata.clientIpWhitelist"; private static final AtomicReference> CLIENT_IP_WHITELIST = new AtomicReference<>( new ArrayList<>()); private static Boolean isOpen = false; /** * Judge whether specified client ip includes in the whitelist. * * @param clientIp clientIp string value. * @return Judge result. */ public static boolean isLegalClient(String clientIp) { if (StringUtils.isBlank(clientIp)) { throw new IllegalArgumentException("clientIp is empty"); } clientIp = clientIp.trim(); if (CLIENT_IP_WHITELIST.get().contains(clientIp)) { return true; } return false; } /** * Whether start client ip whitelist. * * @return true: enable ; false disable */ public static boolean isEnableWhitelist() { return isOpen; } /** * Load white lists based content parameter value. * * @param content content string value. */ public static void load(String content) { if (StringUtils.isBlank(content)) { DEFAULT_LOG.warn("clientIpWhiteList is blank.close whitelist."); isOpen = false; CLIENT_IP_WHITELIST.get().clear(); return; } DEFAULT_LOG.warn("[clientIpWhiteList] {}", content); try { AclInfo acl = JacksonUtils.toObj(content, AclInfo.class); isOpen = acl.getIsOpen(); CLIENT_IP_WHITELIST.set(acl.getIps()); } catch (Exception ioe) { DEFAULT_LOG.error("failed to load clientIpWhiteList, " + ioe.toString(), ioe); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ClientRecord.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * ClientRecord saves records which fetch from client-side. * * @author zongtanghu */ public class ClientRecord { private final String ip; private volatile long lastTime; private final ConcurrentMap groupKey2md5Map; private final ConcurrentMap groupKey2pollingTsMap; public ClientRecord(final String clientIp) { this.ip = clientIp; this.groupKey2md5Map = new ConcurrentHashMap<>(20, 0.75f, 1); this.groupKey2pollingTsMap = new ConcurrentHashMap<>(20, 0.75f, 1); } public String getIp() { return ip; } public long getLastTime() { return lastTime; } public void setLastTime(long lastTime) { this.lastTime = lastTime; } public ConcurrentMap getGroupKey2md5Map() { return groupKey2md5Map; } public ConcurrentMap getGroupKey2pollingTsMap() { return groupKey2pollingTsMap; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ClientTrackService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.config.server.model.SubscriberStatus; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * ClientTrackService which tracks client's md5 service and delete expired ip's records. * * @author Nacos */ public class ClientTrackService { /** * Put the specified value(ip/groupKey/clientMd5) into clientRecords Map. * * @param ip ip string value. * @param groupKey groupKey string value. * @param clientMd5 clientMd5 string value. */ public static void trackClientMd5(String ip, String groupKey, String clientMd5) { ClientRecord record = getClientRecord(ip); record.setLastTime(System.currentTimeMillis()); record.getGroupKey2md5Map().put(groupKey, clientMd5); record.getGroupKey2pollingTsMap().put(groupKey, record.getLastTime()); } /** * Get subscribe client count. * * @return subscribe client count. */ public static int subscribeClientCount() { return clientRecords.size(); } /** * Get all of subscriber count. * * @return all of subscriber count. */ public static long subscriberCount() { long count = 0; for (ClientRecord record : clientRecords.values()) { count += record.getGroupKey2md5Map().size(); } return count; } /** * Groupkey -> SubscriberStatus. */ public static Map listSubStatus(String ip) { Map status = new HashMap<>(100); // record here is non-null ClientRecord record = getClientRecord(ip); for (Map.Entry entry : record.getGroupKey2md5Map().entrySet()) { String groupKey = entry.getKey(); String clientMd5 = entry.getValue(); long lastPollingTs = record.getGroupKey2pollingTsMap().get(groupKey); boolean isUpdate = ConfigCacheService.isUptodate(groupKey, clientMd5); status.put(groupKey, new SubscriberStatus(groupKey, isUpdate, clientMd5, lastPollingTs)); } return status; } /** * Specify subscriber's ip and look up whether data is latest. * groupKey -> isUptodate. */ public static Map isClientUptodate(String ip) { Map result = new HashMap<>(100); for (Map.Entry entry : getClientRecord(ip).getGroupKey2md5Map().entrySet()) { String groupKey = entry.getKey(); String clientMd5 = entry.getValue(); Boolean isuptodate = ConfigCacheService.isUptodate(groupKey, clientMd5); result.put(groupKey, isuptodate); } return result; } /** * Get and return the record of specified client ip. * * @param clientIp clientIp string value. * @return the record of specified client ip. */ private static ClientRecord getClientRecord(String clientIp) { ClientRecord record = clientRecords.get(clientIp); if (null != record) { return record; } ClientRecord clientRecord = new ClientRecord(clientIp); record = clientRecords.putIfAbsent(clientIp, clientRecord); return null == record ? clientRecord : record; } public static void refreshClientRecord() { clientRecords = new ConcurrentHashMap<>(50); } /** * All of client records, adding or deleting. */ static volatile ConcurrentMap clientRecords = new ConcurrentHashMap<>(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigCacheService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigCache; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.ConfigCachePostProcessorDelegate; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.model.gray.GrayRule; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.sys.env.EnvUtil; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import static com.alibaba.nacos.api.common.Constants.CLIENT_IP; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE_UTF8; import static com.alibaba.nacos.config.server.constant.Constants.NULL; import static com.alibaba.nacos.config.server.constant.Constants.PERSIST_ENCODE; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; import static com.alibaba.nacos.config.server.utils.LogUtil.DUMP_LOG; import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG; /** * Config service. * * @author Nacos */ public class ConfigCacheService { private static final String NO_SPACE_CN = "设备上没有空间"; private static final String NO_SPACE_EN = "No space left on device"; private static final String DISK_QUOTA_CN = "超出磁盘限额"; private static final String DISK_QUOTA_EN = "Disk quota exceeded"; /** * groupKey -> cacheItem. */ static final ConcurrentHashMap CACHE = new ConcurrentHashMap<>(); public static int groupCount() { return CACHE.size(); } /** * Save config file and update md5 value in cache. * * @param dataId dataId string value. * @param group group string value. * @param tenant tenant string value. * @param content content string value. * @param md5 md5 of persist. * @param lastModifiedTs lastModifiedTs. * @param type file type. * @return dumpChange success or not. */ public static boolean dumpWithMd5(String dataId, String group, String tenant, String content, String md5, long lastModifiedTs, String type, String encryptedDataKey) { String groupKey = GroupKey2.getKey(dataId, group, tenant); CacheItem ci = makeSure(groupKey, encryptedDataKey); ci.setType(type); final int lockResult = tryWriteLock(groupKey); if (lockResult < 0) { DUMP_LOG.warn("[dump-error] write lock failed. {}", groupKey); return false; } try { //check timestamp boolean lastModifiedOutDated = lastModifiedTs < ConfigCacheService.getLastModifiedTs(groupKey); if (lastModifiedOutDated) { DUMP_LOG.warn("[dump-ignore] timestamp is outdated,groupKey={}", groupKey); return true; } boolean newLastModified = lastModifiedTs > ConfigCacheService.getLastModifiedTs(groupKey); if (md5 == null) { md5 = MD5Utils.md5Hex(content, PERSIST_ENCODE); } //check md5 & update local disk cache. String localContentMd5 = ConfigCacheService.getContentMd5(groupKey); boolean md5Changed = !md5.equals(localContentMd5); if (md5Changed) { DUMP_LOG.info("[dump] md5 changed, save to disk cache ,groupKey={}, newMd5={},oldMd5={}", groupKey, md5, localContentMd5); ConfigDiskServiceFactory.getInstance().saveToDisk(dataId, group, tenant, content); } else { DUMP_LOG.warn("[dump-ignore] ignore to save to disk cache. md5 consistent,groupKey={}, md5={}", groupKey, md5); } //check md5 and timestamp & update local jvm cache. if (md5Changed) { DUMP_LOG.info( "[dump] md5 changed, update md5 and timestamp in jvm cache ,groupKey={}, newMd5={},oldMd5={},lastModifiedTs={}", groupKey, md5, localContentMd5, lastModifiedTs); updateMd5(groupKey, md5, content, lastModifiedTs, encryptedDataKey); } else if (newLastModified) { DUMP_LOG.info( "[dump] md5 consistent ,timestamp changed, update timestamp only in jvm cache ,groupKey={},lastModifiedTs={}", groupKey, lastModifiedTs); updateTimeStamp(groupKey, lastModifiedTs, encryptedDataKey); } else { DUMP_LOG.warn( "[dump-ignore] ignore to save to jvm cache. md5 consistent and no new timestamp changed.groupKey={}", groupKey); } return true; } catch (IOException ioe) { DUMP_LOG.error("[dump-exception] save disk error. " + groupKey + ", " + ioe); if (ioe.getMessage() != null) { String errMsg = ioe.getMessage(); if (errMsg.contains(NO_SPACE_CN) || errMsg.contains(NO_SPACE_EN) || errMsg.contains(DISK_QUOTA_CN) || errMsg.contains(DISK_QUOTA_EN)) { // Protect from disk full. FATAL_LOG.error("Local Disk Full,Exit", ioe); EnvUtil.systemExit(); } } return false; } finally { releaseWriteLock(groupKey); } } /** * Save config file and update md5 value in cache. * * @param dataId dataId string value. * @param group group string value. * @param tenant tenant string value. * @param content content string value. * @param lastModifiedTs lastModifiedTs. * @param type file type. * @param encryptedDataKey encryptedDataKey. * @return dumpChange success or not. */ public static boolean dump(String dataId, String group, String tenant, String content, long lastModifiedTs, String type, String encryptedDataKey) { return dumpWithMd5(dataId, group, tenant, content, null, lastModifiedTs, type, encryptedDataKey); } /** * Save gray config file and update md5 value in cache. * * @param dataId dataId string value. * @param group group string value. * @param tenant tenant string value. * @param grayName grayName string value. * @param grayRule grayRule string value. * @param content content string value. * @param lastModifiedTs lastModifiedTs. * @return dumpChange success or not. */ public static boolean dumpGray(String dataId, String group, String tenant, String grayName, String grayRule, String content, long lastModifiedTs, String encryptedDataKey) { final String groupKey = GroupKey2.getKey(dataId, group, tenant); makeSure(groupKey, null); final int lockResult = tryWriteLock(groupKey); if (lockResult < 0) { DUMP_LOG.warn("[dump-gray-error] write lock failed. {}", groupKey); return false; } try { //check timestamp long localGrayLastModifiedTs = ConfigCacheService.getGrayLastModifiedTs(groupKey, grayName); boolean timestampOutdated = lastModifiedTs < localGrayLastModifiedTs; if (timestampOutdated) { DUMP_LOG.warn("[dump-gray-ignore] timestamp is outdated,groupKey={}", groupKey); return true; } boolean timestampChanged = lastModifiedTs > localGrayLastModifiedTs; final String md5 = MD5Utils.md5Hex(content, ENCODE_UTF8); String localContentGrayMd5 = ConfigCacheService.getContentGrayMd5(groupKey, grayName); boolean md5Changed = !md5.equals(localContentGrayMd5); GrayRule localGrayRule = ConfigCacheService.getGrayRule(groupKey, grayName); GrayRule grayRuleNew = GrayRuleManager.constructGrayRule( GrayRuleManager.deserializeConfigGrayPersistInfo(grayRule)); if (grayRuleNew == null) { DUMP_LOG.warn("[dump-gray-exception] . " + groupKey + ", unknown gray rule for gray name" + grayName); return false; } boolean grayRuleChanged = !grayRuleNew.equals(localGrayRule); if (md5Changed) { DUMP_LOG.info( "[dump-gray] md5 changed, update local jvm cache& local disk cache, groupKey={},grayName={}, " + "newMd5={},oldMd5={}, newGrayRule={}, oldGrayRule={},lastModifiedTs={}", groupKey, grayName, md5, localContentGrayMd5, grayRule, localGrayRule, lastModifiedTs); updateGrayMd5(groupKey, grayName, grayRule, md5, content, lastModifiedTs, encryptedDataKey); ConfigDiskServiceFactory.getInstance().saveGrayToDisk(dataId, group, tenant, grayName, content); } else if (grayRuleChanged) { DUMP_LOG.info("[dump-gray] gray rule changed, update local jvm cache, groupKey={},grayName={}, " + "newMd5={},oldMd5={}, newGrayRule={}, oldGrayRule={},lastModifiedTs={}", groupKey, grayName, md5, localContentGrayMd5, grayRule, localGrayRule, lastModifiedTs); updateGrayRule(groupKey, grayName, grayRule, lastModifiedTs, encryptedDataKey); } else if (timestampChanged) { DUMP_LOG.info( "[dump-gray] timestamp changed, update last modified in local jvm cache, groupKey={},grayName={}," + "grayLastModifiedTs={},oldgrayLastModifiedTs={}", groupKey, grayName, lastModifiedTs, localGrayLastModifiedTs); updateGrayTimeStamp(groupKey, grayName, lastModifiedTs); } else { DUMP_LOG.warn("[dump-gray-ignore] md5 & timestamp not changed. groupKey={},grayName={}", groupKey, grayName); } return true; } catch (IOException ioe) { DUMP_LOG.error("[dump-gray-exception] save disk error. " + groupKey + ", " + ioe.toString(), ioe); return false; } finally { releaseWriteLock(groupKey); } } /** * Delete gray config file, and delete cache. * * @param dataId dataId string value. * @param group group string value. * @param tenant tenant string value. * @param grayName grayName string value. * @return remove success or not. */ public static boolean removeGray(String dataId, String group, String tenant, String grayName) { final String groupKey = GroupKey2.getKey(dataId, group, tenant); final int lockResult = tryWriteLock(groupKey); // If data is non-existent. if (0 == lockResult) { DUMP_LOG.info("[remove-ok] {} not exist.", groupKey); return true; } // try to lock failed if (lockResult < 0) { DUMP_LOG.warn("[remove-error] write lock failed. {}", groupKey); return false; } try { DUMP_LOG.info("[remove-gray-ok] remove gray in local disk cache,grayName={},groupKey={} ", grayName, groupKey); ConfigDiskServiceFactory.getInstance().removeConfigInfo4Gray(dataId, group, tenant, grayName); CacheItem ci = CACHE.get(groupKey); if (ci.getConfigCacheGray() != null) { ci.getConfigCacheGray().remove(grayName); if (ci.getConfigCacheGray().isEmpty()) { ci.clearConfigGrays(); } else { ci.sortConfigGray(); } } DUMP_LOG.info("[remove-gray-ok] remove gray in local jvm cache,grayName={},groupKey={} ", grayName, groupKey); NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey)); return true; } finally { releaseWriteLock(groupKey); } } /** * Delete config file, and delete cache. * * @param dataId dataId string value. * @param group group string value. * @param tenant tenant string value. * @return remove success or not. */ public static boolean remove(String dataId, String group, String tenant) { final String groupKey = GroupKey2.getKey(dataId, group, tenant); final int lockResult = tryWriteLock(groupKey); // If data is non-existent. if (0 == lockResult) { DUMP_LOG.info("[remove-ok] {} not exist.", groupKey); return true; } // try to lock failed if (lockResult < 0) { DUMP_LOG.warn("[remove-error] write lock failed. {}", groupKey); return false; } try { DUMP_LOG.info("[dump] remove local disk cache,groupKey={} ", groupKey); ConfigDiskServiceFactory.getInstance().removeConfigInfo(dataId, group, tenant); CACHE.remove(groupKey); DUMP_LOG.info("[dump] remove local jvm cache,groupKey={} ", groupKey); NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey)); return true; } finally { releaseWriteLock(groupKey); } } /** * Update md5 value. * * @param groupKey the group key * @param md5 the md 5 * @param content the content * @param lastModifiedTs the last modified ts * @param encryptedDataKey the encrypted data key */ public static void updateMd5(String groupKey, String md5, String content, long lastModifiedTs, String encryptedDataKey) { CacheItem cache = makeSure(groupKey, encryptedDataKey); ConfigCache configCache = cache.getConfigCache(); if (configCache.getMd5() == null || !configCache.getMd5().equals(md5)) { configCache.setMd5(md5); configCache.setLastModifiedTs(lastModifiedTs); configCache.setEncryptedDataKey(encryptedDataKey); ConfigCachePostProcessorDelegate.getInstance().postProcess(configCache, content); NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey)); } } /** * Update gray md5 value. * * @param groupKey the group key * @param grayName the gray name * @param grayRule the gray rule * @param md5 the md 5 * @param content the content * @param lastModifiedTs the last modified ts * @param encryptedDataKey the encrypted data key */ public static void updateGrayMd5(String groupKey, String grayName, String grayRule, String md5, String content, long lastModifiedTs, String encryptedDataKey) { CacheItem cache = makeSure(groupKey, null); cache.initConfigGrayIfEmpty(grayName); ConfigCacheGray configCache = cache.getConfigCacheGray().get(grayName); configCache.setMd5(md5); configCache.setLastModifiedTs(lastModifiedTs); configCache.setEncryptedDataKey(encryptedDataKey); configCache.resetGrayRule(grayRule); cache.sortConfigGray(); ConfigCachePostProcessorDelegate.getInstance().postProcess(configCache, content); NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey)); } /** * Get and return content md5 value from cache. Empty string represents no data. */ public static String getContentMd5(String groupKey) { return getContentMd5(groupKey, null, null); } public static String getContentMd5(String groupKey, String ip, String tag) { return getContentMd5(groupKey, ip, tag, null); } public static String getContentMd5(String groupKey, String ip, String tag, Map connLabels) { CacheItem item = CACHE.get(groupKey); if (item == null) { return NULL; } if (connLabels == null && StringUtils.isNotBlank(ip)) { connLabels = new HashMap<>(4); } if (connLabels == null && StringUtils.isNotBlank(tag)) { connLabels = new HashMap<>(4); } if (StringUtils.isNotBlank(ip)) { connLabels.put(CLIENT_IP, ip); } if (StringUtils.isNotBlank(tag)) { connLabels.put(VIPSERVER_TAG, tag); } if (item.getSortConfigGrays() != null && connLabels != null && !connLabels.isEmpty()) { for (ConfigCacheGray entry : item.getSortConfigGrays()) { if (entry.match(connLabels)) { return entry.getMd5(); } } } String md5 = item.getConfigCache().getMd5(); return md5 == null ? NULL : md5; } private static void updateGrayRule(String groupKey, String grayName, String grayRule, long lastModifiedTs, String encryptedDataKey) { CacheItem cache = makeSure(groupKey, null); cache.initConfigGrayIfEmpty(grayName); ConfigCacheGray configCache = cache.getConfigCacheGray().get(grayName); configCache.setLastModifiedTs(lastModifiedTs); configCache.setEncryptedDataKey(encryptedDataKey); configCache.resetGrayRule(grayRule); cache.sortConfigGray(); NotifyCenter.publishEvent(new LocalDataChangeEvent(groupKey)); } /** * Get and return gray md5 value from cache. Empty string represents no data. * * @param groupKey groupKey string value. * @param grayName grayName string value. * @return Content Tag Md5 value. */ public static String getContentGrayMd5(String groupKey, String grayName) { CacheItem item = CACHE.get(groupKey); if (item == null || item.getConfigCacheGray() == null || !item.getConfigCacheGray().containsKey(grayName)) { return NULL; } return item.getConfigCacheGray().get(grayName).getMd5(); } public static long getGrayLastModifiedTs(String groupKey, String grayName) { CacheItem item = CACHE.get(groupKey); if (item.getConfigCacheGray() == null || !item.getConfigCacheGray().containsKey(grayName)) { return 0; } ConfigCache configCacheGray = item.getConfigCacheGray().get(grayName); return (null != configCacheGray) ? configCacheGray.getLastModifiedTs() : 0; } public static GrayRule getGrayRule(String groupKey, String grayName) { CacheItem item = CACHE.get(groupKey); if (item == null || item.getConfigCacheGray() == null || !item.getConfigCacheGray().containsKey(grayName)) { return null; } return item.getConfigCacheGray().get(grayName).getGrayRule(); } /** * Get and return content cache. * * @param groupKey groupKey string value. * @return CacheItem. */ public static CacheItem getContentCache(String groupKey) { return CACHE.get(groupKey); } public static long getLastModifiedTs(String groupKey) { CacheItem item = CACHE.get(groupKey); return (null != item) ? item.getConfigCache().getLastModifiedTs() : 0L; } /** * update gray timestamp. * * @param groupKey groupKey. * @param grayName grayName. * @param lastModifiedTs lastModifiedTs. */ private static void updateGrayTimeStamp(String groupKey, String grayName, long lastModifiedTs) { CacheItem cache = makeSure(groupKey, null); cache.initConfigGrayIfEmpty(grayName); cache.getConfigCacheGray().get(grayName).setLastModifiedTs(lastModifiedTs); } public static boolean isUptodate(String groupKey, String md5) { return isUptodate(groupKey, md5, null, null); } public static boolean isUptodate(String groupKey, String md5, String ip, String tag) { return isUptodate(groupKey, md5, ip, tag, null); } public static boolean isUptodate(String groupKey, String md5, String ip, String tag, Map appLabels) { String serverMd5 = ConfigCacheService.getContentMd5(groupKey, ip, tag, appLabels); return StringUtils.equals(md5, serverMd5); } /** * Try to add read lock. If it succeeded, then it can call {@link #releaseWriteLock(String)}.And it won't call if * failed. * * @param groupKey groupKey string value. * @return 0 - No data and failed. Positive number - lock succeeded. Negative number - lock failed。 */ public static int tryReadLock(String groupKey) { CacheItem groupItem = CACHE.get(groupKey); int result = (null == groupItem) ? 0 : (groupItem.getRwLock().tryReadLock() ? 1 : -1); if (result < 0) { DEFAULT_LOG.warn("[read-lock] failed, {}, {}", result, groupKey); } return result; } /** * Release readLock. * * @param groupKey groupKey string value. */ public static void releaseReadLock(String groupKey) { CacheItem item = CACHE.get(groupKey); if (null != item) { item.getRwLock().releaseReadLock(); } } /** * Try to add write lock. If it succeeded, then it can call {@link #releaseWriteLock(String)}.And it won't call if * failed. * * @param groupKey groupKey string value. * @return 0 - No data and failed. Positive number 0 - Success. Negative number - lock failed。 */ static int tryWriteLock(String groupKey) { CacheItem groupItem = CACHE.get(groupKey); int result = (null == groupItem) ? 0 : (groupItem.getRwLock().tryWriteLock() ? 1 : -1); if (result < 0) { DEFAULT_LOG.warn("[write-lock] failed, {}, {}", result, groupKey); } return result; } static void releaseWriteLock(String groupKey) { CacheItem groupItem = CACHE.get(groupKey); if (null != groupItem) { groupItem.getRwLock().releaseWriteLock(); } } static CacheItem makeSure(final String groupKey, final String encryptedDataKey) { CacheItem item = CACHE.get(groupKey); if (null != item) { return item; } CacheItem tmp = new CacheItem(groupKey, encryptedDataKey); item = CACHE.putIfAbsent(groupKey, tmp); return (null == item) ? tmp : item; } /** * update time stamp. * * @param groupKey groupKey. * @param lastModifiedTs lastModifiedTs. * @param encryptedDataKey encryptedDataKey. */ private static void updateTimeStamp(String groupKey, long lastModifiedTs, String encryptedDataKey) { CacheItem cache = makeSure(groupKey, encryptedDataKey); cache.getConfigCache().setLastModifiedTs(lastModifiedTs); } private static final int TRY_GET_LOCK_TIMES = 9; /** * try config read lock with spin of try get lock times. * * @param groupKey group key of config. * @return 0 - No data and failed. Positive number - lock succeeded. Negative number - lock failed. */ public static int tryConfigReadLock(String groupKey) { // Lock failed by default. int lockResult = -1; // Try to get lock times, max value: 10; for (int i = TRY_GET_LOCK_TIMES; i >= 0; --i) { lockResult = ConfigCacheService.tryReadLock(groupKey); // The data is non-existent. if (0 == lockResult) { break; } // Success if (lockResult > 0) { break; } // Retry. if (i > 0) { try { Thread.sleep(1); } catch (Exception e) { LogUtil.PULL_CHECK_LOG.error("An Exception occurred while thread sleep", e); } } } return lockResult; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigChangePublisher.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration; import com.alibaba.nacos.sys.env.EnvUtil; /** * ConfigChangePublisher. * * @author liaochuntao */ public class ConfigChangePublisher { /** * Notify ConfigChange. * * @param event ConfigDataChangeEvent instance. */ public static void notifyConfigChange(ConfigDataChangeEvent event) { if (DatasourceConfiguration.isEmbeddedStorage() && !EnvUtil.getStandaloneMode()) { return; } NotifyCenter.publishEvent(event); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigDetailService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.constant.PropertiesConstant; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; /** * config detail service. * * @author 985492783@qq.com * @date 2023/2/9 5:25 */ @Service public class ConfigDetailService { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigDetailService.class); private final ConfigInfoPersistService configInfoPersistService; private BlockingQueue eventLinkedBlockingQueue; private ScheduledExecutorService clientEventExecutor; /** * the max_capacity of eventLinkedBlockingQueue may be controlled by the properties {@link PropertiesConstant#SEARCH_MAX_CAPACITY}. */ private static int maxCapacity = 4; private static final int MAX_CAPACITY = 32; /** * the wait_timeout of search config business may be controlled by the properties {@link PropertiesConstant#SEARCH_WAIT_TIMEOUT}. */ private static long waitTimeout = 8000L; /** * the max_thread of clientEventExecutor may be controlled by the properties {@link PropertiesConstant#SEARCH_MAX_THREAD}. */ private static int maxThread = 2; private static final int MAX_THREAD = 16; public ConfigDetailService(ConfigInfoPersistService configInfoPersistService) { this.configInfoPersistService = configInfoPersistService; loadSetting(); initWorker(); } private void loadSetting() { setMaxCapacity(Math.min(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.SEARCH_MAX_CAPACITY, String.valueOf(getMaxCapacity()))), MAX_CAPACITY)); setMaxThread(Math.min(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.SEARCH_MAX_THREAD, String.valueOf(getMaxThread()))), MAX_THREAD)); setWaitTimeout(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.SEARCH_WAIT_TIMEOUT, String.valueOf(getWaitTimeout())))); } /** * init worker thread. */ private void initWorker() { this.eventLinkedBlockingQueue = new LinkedBlockingQueue<>(maxCapacity); clientEventExecutor = new ScheduledThreadPoolExecutor(maxThread, new NameThreadFactory("com.alibaba.nacos.config.search.worker")); for (int i = 0; i < maxThread; i++) { clientEventExecutor.submit(() -> { while (true) { try { SearchEvent event = eventLinkedBlockingQueue.take(); Page result = null; if (Constants.CONFIG_SEARCH_BLUR.equals(event.getType())) { result = configInfoPersistService.findConfigInfoLike4Page(event.pageNo, event.pageSize, event.dataId, event.group, event.tenant, event.configAdvanceInfo); } else { result = configInfoPersistService.findConfigInfo4Page(event.pageNo, event.pageSize, event.dataId, event.group, event.tenant, event.configAdvanceInfo); } synchronized (event) { event.setResponse(result); event.notifyAll(); } } catch (Exception e) { LOGGER.error("catch search worker error: {}", e.getMessage()); } } }); } } /** * block thread and use workerThread to search config. */ public Page findConfigInfoPage(String search, int pageNo, int pageSize, String dataId, String group, String tenant, Map configAdvanceInfo) throws NacosRuntimeException { SearchEvent searchEvent = new SearchEvent(search, pageNo, pageSize, dataId, group, tenant, configAdvanceInfo); Page result = null; try { synchronized (searchEvent) { boolean offer = eventLinkedBlockingQueue.offer(searchEvent); if (!offer) { throw new NacosRuntimeException(503, "server limit match."); } searchEvent.wait(waitTimeout); result = searchEvent.getResponse(); } } catch (InterruptedException e) { LOGGER.error("get config detail timeout: {}.", e.getMessage()); throw new NacosRuntimeException(503, "server limit match."); } if (result == null) { throw new NacosRuntimeException(503, "server limit match."); } return result; } public static int getMaxCapacity() { return maxCapacity; } public static void setMaxCapacity(int maxCapacity) { ConfigDetailService.maxCapacity = maxCapacity; } public static long getWaitTimeout() { return waitTimeout; } public static void setWaitTimeout(long waitTimeout) { ConfigDetailService.waitTimeout = waitTimeout; } public static int getMaxThread() { return maxThread; } public static void setMaxThread(int maxThread) { ConfigDetailService.maxThread = maxThread; } public static class SearchEvent { private String type; private int pageNo; private int pageSize; private String dataId; private String group; private String tenant; private Map configAdvanceInfo; private Page response; public SearchEvent() { } public SearchEvent(String type, int pageNo, int pageSize, String dataId, String group, String tenant, Map configAdvanceInfo) { this.type = type; this.pageNo = pageNo; this.pageSize = pageSize; this.dataId = dataId; this.group = group; this.tenant = tenant; this.configAdvanceInfo = configAdvanceInfo; } public String getType() { return type; } public int getPageNo() { return pageNo; } public int getPageSize() { return pageSize; } public String getDataId() { return dataId; } public String getGroup() { return group; } public String getTenant() { return tenant; } public Map getConfigAdvanceInfo() { return configAdvanceInfo; } public Page getResponse() { return response; } public void setResponse(Page response) { this.response = response; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.core.utils.GlobalExecutor; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.CONFIG_CHANGED; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; /** * fuzzy watch context for config. * * @author shiyiyue */ @Component public class ConfigFuzzyWatchContextService { /** * groupKeyPattern -> watched client id set. */ private final Map> watchedClientsMap = new ConcurrentHashMap<>(); /** * groupKeyPattern -> matched groupKeys set. */ private final Map> matchedGroupKeysMap = new ConcurrentHashMap<>(); public ConfigFuzzyWatchContextService() { } @PostConstruct public void init() { GlobalExecutor.scheduleWithFixDelayByCommon(() -> trimFuzzyWatchContext(), 30000); } /** * trim fuzzy watch context.
    1.remove watchedClients if watched client is empty. 2.remove matchedServiceKeys * if watchedClients is null. pattern matchedServiceKeys will be removed in second period to avoid frequently * matchedServiceKeys init. */ void trimFuzzyWatchContext() { try { Iterator>> iterator = matchedGroupKeysMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> matchedGroupKeys = iterator.next(); Set watchedClients = this.watchedClientsMap.get(matchedGroupKeys.getKey()); if (watchedClients == null) { iterator.remove(); LogUtil.DEFAULT_LOG.info( "[fuzzy-watch] no watchedClients context for pattern {},remove matchedGroupKeys context", matchedGroupKeys.getKey()); } else if (watchedClients.isEmpty()) { LogUtil.DEFAULT_LOG.info("[fuzzy-watch] no client watched pattern {},remove watchedClients context", matchedGroupKeys.getKey()); this.watchedClientsMap.remove(matchedGroupKeys.getKey()); } else if (reachToUpLimit(matchedGroupKeys.getValue().size())) { LogUtil.DEFAULT_LOG.warn( "[fuzzy-watch] pattern {} matched config count has reached to upper limit {}, fuzzy watch has been suppressed ", matchedGroupKeys.getKey(), matchedGroupKeys.getValue().size()); } else if (reachToUpLimit((int) (matchedGroupKeys.getValue().size() * 1.25))) { LogUtil.DEFAULT_LOG.warn( "[fuzzy-watch] pattern {} matched config count has reached to 80% of the upper limit {} " + ",it may has a risk of notify suppressed in the near further", matchedGroupKeys.getKey(), matchedGroupKeys.getValue().size()); } } } catch (Throwable throwable) { LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] trim fuzzy watch context fail", throwable); } } /** * get matched exist group keys with the groupKeyPattern. return null if not matched. * * @param groupKeyPattern groupKeyPattern. * @return */ public Set matchGroupKeys(String groupKeyPattern) { Set stringSet = matchedGroupKeysMap.get(groupKeyPattern); return stringSet == null ? new HashSet<>() : new HashSet<>(matchedGroupKeysMap.get(groupKeyPattern)); } /** * sync group key change to fuzzy context. * * @param groupKey groupKey. * @param changedType changedType. * @return need notify ot not. */ public boolean syncGroupKeyContext(String groupKey, String changedType) { boolean needNotify = false; String[] groupKeyItems = GroupKey.parseKey(groupKey); String dataId = groupKeyItems[0]; String group = groupKeyItems[1]; String namespace = groupKeyItems[2]; boolean tryAdd = changedType.equals(ADD_CONFIG) || changedType.equals(CONFIG_CHANGED); boolean tryRemove = changedType.equals(DELETE_CONFIG); Iterator>> iterator = matchedGroupKeysMap.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry> entry = iterator.next(); if (FuzzyGroupKeyPattern.matchPattern(entry.getKey(), dataId, group, namespace)) { boolean containsAlready = entry.getValue().contains(groupKey); boolean reachToUpLimit = reachToUpLimit(entry.getValue().size()); if (tryAdd && !containsAlready && reachToUpLimit) { LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] pattern matched config count is over limit , " + "current config will be ignored for pattern {} ,current count is {}", entry.getKey(), entry.getValue().size()); continue; } if (tryAdd && !containsAlready && entry.getValue().add(groupKey)) { needNotify = true; } if (tryRemove && containsAlready && entry.getValue().remove(groupKey)) { needNotify = true; if (reachToUpLimit) { makeupMatchedGroupKeys(entry.getKey()); } } } } return needNotify; } /** * make matched group key when deleted configs on loa protection model. * * @param groupKeyPattern group key pattern. */ public void makeupMatchedGroupKeys(String groupKeyPattern) { Set matchedGroupKeys = matchedGroupKeysMap.get(groupKeyPattern); if (matchedGroupKeys == null || reachToUpLimit(matchedGroupKeys.size())) { return; } for (String groupKey : ConfigCacheService.CACHE.keySet()) { String[] groupKeyItems = GroupKey.parseKey(groupKey); if (FuzzyGroupKeyPattern.matchPattern(groupKeyPattern, groupKeyItems[0], groupKeyItems[1], groupKeyItems[2]) && !matchedGroupKeys.contains(groupKey)) { matchedGroupKeys.add(groupKey); LogUtil.DEFAULT_LOG.info("[fuzzy-watch] pattern {} makeup group key {}", groupKeyPattern, groupKey); if (reachToUpLimit(matchedGroupKeys.size())) { LogUtil.DEFAULT_LOG.warn( "[fuzzy-watch] pattern {] matched config count is over limit ,makeup group keys skip.", groupKeyPattern); return; } } } } private boolean reachToUpLimit(int size) { return size >= ConfigCommonConfig.getInstance().getMaxMatchedConfigCount(); } public boolean reachToUpLimit(String groupKeyPattern) { Set strings = matchedGroupKeysMap.get(groupKeyPattern); return strings != null && (reachToUpLimit(strings.size())); } /** * Matches the client effective group keys based on the specified group key pattern, client IP, and tag. * * @param groupKeyPattern The pattern to match group keys. */ private void initMatchGroupKeys(String groupKeyPattern) throws NacosException { if (matchedGroupKeysMap.containsKey(groupKeyPattern)) { return; } if (matchedGroupKeysMap.size() >= ConfigCommonConfig.getInstance().getMaxPatternCount()) { LogUtil.DEFAULT_LOG.warn( "[fuzzy-watch] pattern count is over limit ,pattern {} init fail,current count is {}", groupKeyPattern, matchedGroupKeysMap.size()); throw new NacosException(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg()); } matchedGroupKeysMap.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); Set matchedGroupKeys = this.matchedGroupKeysMap.get(groupKeyPattern); long matchBeginTime = System.currentTimeMillis(); boolean overMatchCount = false; for (String groupKey : ConfigCacheService.CACHE.keySet()) { String[] groupKeyItems = GroupKey.parseKey(groupKey); if (FuzzyGroupKeyPattern.matchPattern(groupKeyPattern, groupKeyItems[0], groupKeyItems[1], groupKeyItems[2])) { if (reachToUpLimit(matchedGroupKeys.size())) { LogUtil.DEFAULT_LOG.warn("[fuzzy-watch] pattern matched service count is over limit , " + "other services will stop notify for pattern {} ,current count is {}", groupKeyPattern, matchedGroupKeys.size()); overMatchCount = true; break; } matchedGroupKeys.add(groupKey); } } LogUtil.DEFAULT_LOG.info("[fuzzy-watch] pattern {} match {} group keys,overMatchCount={}, cost {}ms", groupKeyPattern, matchedGroupKeys.size(), overMatchCount, System.currentTimeMillis() - matchBeginTime); } /** * Adds a fuzzy listen connection ID associated with the specified group key pattern. If the key pattern does not * exist in the context, a new entry will be created. If the key pattern already exists, the connection ID will be * added to the existing set. * * @param groupKeyPattern The group key pattern to associate with the listen connection. * @param connectId The connection ID to be added. * @throws NacosException over max pattern count. */ public synchronized void addFuzzyWatch(String groupKeyPattern, String connectId) throws NacosException { watchedClientsMap.computeIfAbsent(groupKeyPattern, k -> new HashSet<>()); initMatchGroupKeys(groupKeyPattern); // Add the connection ID to the set associated with the key pattern in keyPatternContext watchedClientsMap.get(groupKeyPattern).add(connectId); } /** * Removes a fuzzy listen connection ID associated with the specified group key pattern. If the group key pattern * exists in the context and the connection ID is found in the associated set, the connection ID will be removed * from the set. If the set becomes empty after removal, the entry for the group key pattern will be removed from * the context. * * @param groupKeyPattern The group key pattern associated with the listen connection to be removed. * @param connectionId The connection ID to be removed. */ public synchronized void removeFuzzyListen(String groupKeyPattern, String connectionId) { // Retrieve the set of connection IDs associated with the group key pattern Set connectIds = watchedClientsMap.get(groupKeyPattern); if (CollectionUtils.isNotEmpty(connectIds)) { // Remove the connection ID from the set if it exists connectIds.remove(connectionId); } } /** * remove watch context for connection id. * * @param connectionId connection id. */ public void clearFuzzyWatchContext(String connectionId) { for (Map.Entry> keyPatternContextEntry : watchedClientsMap.entrySet()) { Set connectionIds = keyPatternContextEntry.getValue(); if (CollectionUtils.isNotEmpty(connectionIds)) { connectionIds.remove(connectionId); } } } /** * Retrieves the set of connection IDs matched with the specified group key. * * @param groupKey The group key to match with the key patterns. * @return The set of connection IDs matched with the group key. */ public Set getMatchedClients(String groupKey) { // Initialize a set to store the matched connection IDs Set connectIds = new HashSet<>(); // Iterate over each key pattern in the context Iterator>> watchClientIterator = watchedClientsMap.entrySet().iterator(); String[] groupItems = GroupKey2.parseKey(groupKey); while (watchClientIterator.hasNext()) { Map.Entry> watchClientEntry = watchClientIterator.next(); String keyPattern = watchClientEntry.getKey(); if (FuzzyGroupKeyPattern.matchPattern(keyPattern, groupItems[0], groupItems[1], groupItems[2])) { if (CollectionUtils.isNotEmpty(watchClientEntry.getValue())) { connectIds.addAll(watchClientEntry.getValue()); } } } return connectIds; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigMigrateService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.utils.NetUtils; import com.alibaba.nacos.common.utils.MapUtil; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.config.server.configuration.ConfigCompatibleConfig; import com.alibaba.nacos.config.server.exception.ConfigAlreadyExistsException; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.ConfigGrayPersistInfo; import com.alibaba.nacos.config.server.model.gray.GrayRule; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigMigratePersistService; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.config.server.model.gray.GrayRuleManager.SPLIT; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; import static com.alibaba.nacos.config.server.utils.PropertyUtil.CONFIG_MIGRATE_FLAG; import static com.alibaba.nacos.config.server.utils.PropertyUtil.GRAY_MIGRATE_FLAG; /** * migrate beta and tag to gray model. should only invoked from config sync notify. * * @author shiyiyue */ @Service public class ConfigMigrateService { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigMigrateService.class); private static final String NAMESPACE_MIGRATE_SRC_USER = "nacos_namespace_migrate"; private final String namespacePublic = "public"; /** * The Config info beta persist service. */ ConfigInfoBetaPersistService configInfoBetaPersistService; /** * The Config info tag persist service. */ ConfigInfoTagPersistService configInfoTagPersistService; /** * The Config info gray persist service. */ ConfigInfoGrayPersistService configInfoGrayPersistService; /** * The Config info persist service. */ ConfigInfoPersistService configInfoPersistService; /** * The Config migrate persist service. */ ConfigMigratePersistService configMigratePersistService; /** * The Namespace persist service. */ NamespacePersistService namespacePersistService; /** * The Old table version. */ boolean oldTableVersion = false; /** * Instantiates a new Config migrate service. * * @param configInfoBetaPersistService the config info beta persist service * @param configInfoTagPersistService the config info tag persist service * @param configInfoGrayPersistService the config info gray persist service * @param configMigratePersistService the config migrate persist service * @param namespacePersistService the namespace persist service * @param configInfoPersistService the config info persist service */ public ConfigMigrateService(ConfigInfoBetaPersistService configInfoBetaPersistService, ConfigInfoTagPersistService configInfoTagPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService, ConfigMigratePersistService configMigratePersistService, NamespacePersistService namespacePersistService, ConfigInfoPersistService configInfoPersistService) { this.configInfoBetaPersistService = configInfoBetaPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; this.configInfoTagPersistService = configInfoTagPersistService; this.configMigratePersistService = configMigratePersistService; this.namespacePersistService = namespacePersistService; this.configInfoPersistService = configInfoPersistService; } /** * migrate beta&tag to gray . * * @throws Exception the exception */ @PostConstruct public void migrate() throws Exception { oldTableVersion = namespacePersistService.isExistTable("config_info_beta"); if (PropertyUtil.isGrayCompatibleModel() && oldTableVersion) { doCheckMigrate(); } if (ConfigCompatibleConfig.getInstance().isNamespaceCompatibleMode()) { doCheckNamespaceMigrate(); } } /** * handler tag v1 config. * * @param configForm configForm. * @param configInfo configInfo. * @param configRequestInfo configRequestInfo. * @throws NacosApiException NacosApiException. */ public void persistTagv1(ConfigForm configForm, ConfigInfo configInfo, ConfigRequestInfo configRequestInfo) throws NacosApiException { if (!PropertyUtil.isGrayCompatibleModel() || !oldTableVersion) { return; } if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { ConfigOperateResult configOperateResult = configInfoTagPersistService.insertOrUpdateTagCas(configInfo, configForm.getTag(), configRequestInfo.getSrcIp(), configForm.getSrcUser()); if (!configOperateResult.isSuccess()) { LOGGER.warn( "[cas-publish-tag-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.", configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5()); throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT, "Cas publish tag config fail, server md5 may have changed."); } } else { configInfoTagPersistService.insertOrUpdateTag(configInfo, configForm.getTag(), configRequestInfo.getSrcIp(), configForm.getSrcUser()); } } /** * handle old beta. * * @param configForm configForm. * @param configInfo configInfo. * @param configRequestInfo configRequestInfo. * @throws NacosApiException NacosApiException. */ public void persistBeta(ConfigForm configForm, ConfigInfo configInfo, ConfigRequestInfo configRequestInfo) throws NacosApiException { if (!PropertyUtil.isGrayCompatibleModel() || !oldTableVersion) { return; } ConfigOperateResult configOperateResult = null; // beta publish if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configOperateResult = configInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, configRequestInfo.getBetaIps(), configRequestInfo.getSrcIp(), configForm.getSrcUser()); if (!configOperateResult.isSuccess()) { LOGGER.warn( "[cas-publish-beta-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.", configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5()); throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT, "Cas publish beta config fail, server md5 may have changed."); } } else { configInfoBetaPersistService.insertOrUpdateBeta(configInfo, configRequestInfo.getBetaIps(), configRequestInfo.getSrcIp(), configForm.getSrcUser()); } } /** * delete beta and tag. * * @param dataId dataId. * @param group group. * @param namespaceId namespaceId. * @param grayName grayName. * @param clientIp clientIp. * @param srcUser srcUser. */ public void deleteConfigGrayV1(String dataId, String group, String namespaceId, String grayName, String clientIp, String srcUser) { if (!PropertyUtil.isGrayCompatibleModel() || !oldTableVersion) { return; } if (BetaGrayRule.TYPE_BETA.equals(grayName)) { configInfoBetaPersistService.removeConfigInfo4Beta(dataId, group, namespaceId); } else if (grayName.startsWith(TagGrayRule.TYPE_TAG + SPLIT)) { configInfoTagPersistService.removeConfigInfoTag(dataId, group, namespaceId, grayName.substring(4), clientIp, srcUser); } } /** * migrate single config beta. * * @param dataId dataId. * @param group group. * @param tenant tenant. */ public void checkMigrateBeta(String dataId, String group, String tenant) { ConfigInfoBetaWrapper configInfo4Beta = configInfoBetaPersistService.findConfigInfo4Beta(dataId, group, tenant); if (configInfo4Beta == null) { ConfigInfoGrayWrapper configInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, BetaGrayRule.TYPE_BETA); if (configInfoGrayWrapper == null) { return; } configInfoGrayPersistService.removeConfigInfoGray(dataId, group, tenant, BetaGrayRule.TYPE_BETA, NetUtils.localIp(), "nacos_auto_migrate"); return; } ConfigInfoGrayWrapper configInfo4Gray = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, BetaGrayRule.TYPE_BETA); if (configInfo4Gray == null || configInfo4Gray.getLastModified() < configInfo4Beta.getLastModified()) { DEFAULT_LOG.info("[migrate beta to gray] dataId={}, group={}, tenant={}, md5={}", configInfo4Beta.getDataId(), configInfo4Beta.getGroup(), configInfo4Beta.getTenant(), configInfo4Beta.getMd5()); ConfigGrayPersistInfo localConfigGrayPersistInfo = new ConfigGrayPersistInfo(BetaGrayRule.TYPE_BETA, BetaGrayRule.VERSION, configInfo4Beta.getBetaIps(), BetaGrayRule.PRIORITY); configInfoGrayPersistService.insertOrUpdateGray(configInfo4Beta, BetaGrayRule.TYPE_BETA, GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), NetUtils.localIp(), "nacos_auto_migrate"); } } /** * migrate single config tag. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param tag tag. */ public void checkMigrateTag(String dataId, String group, String tenant, String tag) { ConfigInfoTagWrapper configInfo4Tag = configInfoTagPersistService.findConfigInfo4Tag(dataId, group, tenant, tag); if (configInfo4Tag == null) { ConfigInfoGrayWrapper configInfo4Gray = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, TagGrayRule.TYPE_TAG + "_" + tag); if (configInfo4Gray == null) { return; } configInfoGrayPersistService.removeConfigInfoGray(dataId, group, tenant, TagGrayRule.TYPE_TAG + "_" + tag, NetUtils.localIp(), "nacos_auto_migrate"); return; } ConfigInfoGrayWrapper configInfo4Gray = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, TagGrayRule.TYPE_TAG + "_" + tag); if (configInfo4Gray == null || configInfo4Gray.getLastModified() < configInfo4Tag.getLastModified()) { DEFAULT_LOG.info("[migrate tag to gray] dataId={}, group={}, tenant={}, md5={}", configInfo4Tag.getDataId(), configInfo4Tag.getGroup(), configInfo4Tag.getTenant(), configInfo4Tag.getMd5()); ConfigGrayPersistInfo localConfigGrayPersistInfo = new ConfigGrayPersistInfo(TagGrayRule.TYPE_TAG, TagGrayRule.VERSION, configInfo4Tag.getTag(), TagGrayRule.PRIORITY); configInfoGrayPersistService.insertOrUpdateGray(configInfo4Tag, TagGrayRule.TYPE_TAG, GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), NetUtils.localIp(), "nacos_auto_migrate"); } } /** * Check changed config gray migrate state. * * @param changedConfigInfoGrayWrapper the changed config info gray wrapper */ public void checkChangedConfigGrayMigrateState(ConfigInfoGrayWrapper changedConfigInfoGrayWrapper) { String tenant = changedConfigInfoGrayWrapper.getTenant(); if (!ConfigCompatibleConfig.getInstance().isNamespaceCompatibleMode()) { return; } if (!StringUtils.equals(tenant, namespacePublic) && StringUtils.isNotBlank(tenant)) { return; } String targetTenant = StringUtils.EMPTY; if (StringUtils.isBlank(tenant)) { targetTenant = namespacePublic; } ConfigInfoGrayWrapper targetConfigInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray( changedConfigInfoGrayWrapper.getDataId(), changedConfigInfoGrayWrapper.getGroup(), targetTenant, changedConfigInfoGrayWrapper.getGrayName()); try { GRAY_MIGRATE_FLAG.set(true); if (StringUtils.equals(changedConfigInfoGrayWrapper.getSrcUser(), NAMESPACE_MIGRATE_SRC_USER)) { if (targetConfigInfoGrayWrapper == null) { configInfoGrayPersistService.removeConfigInfoGray(changedConfigInfoGrayWrapper.getDataId(), changedConfigInfoGrayWrapper.getGroup(), tenant, changedConfigInfoGrayWrapper.getGrayName(), null, NAMESPACE_MIGRATE_SRC_USER); } else if (!targetConfigInfoGrayWrapper.getMd5().equals(changedConfigInfoGrayWrapper.getMd5()) || !targetConfigInfoGrayWrapper.getGrayRule() .equals(changedConfigInfoGrayWrapper.getGrayRule())) { if (targetConfigInfoGrayWrapper.getLastModified() >= changedConfigInfoGrayWrapper.getLastModified() || !StringUtils.equals(targetConfigInfoGrayWrapper.getSrcUser(), NAMESPACE_MIGRATE_SRC_USER)) { targetConfigInfoGrayWrapper.setTenant(tenant); configInfoGrayPersistService.updateConfigInfo4Gray(targetConfigInfoGrayWrapper, targetConfigInfoGrayWrapper.getGrayName(), targetConfigInfoGrayWrapper.getGrayRule(), null, NAMESPACE_MIGRATE_SRC_USER); } } } else { if (targetConfigInfoGrayWrapper == null) { changedConfigInfoGrayWrapper.setTenant(targetTenant); configInfoGrayPersistService.addConfigInfo4Gray(changedConfigInfoGrayWrapper, changedConfigInfoGrayWrapper.getGrayName(), changedConfigInfoGrayWrapper.getGrayRule(), null, NAMESPACE_MIGRATE_SRC_USER); } else if (!targetConfigInfoGrayWrapper.getMd5().equals(changedConfigInfoGrayWrapper.getMd5()) || !targetConfigInfoGrayWrapper.getGrayRule() .equals(changedConfigInfoGrayWrapper.getGrayRule())) { if (targetConfigInfoGrayWrapper.getLastModified() >= changedConfigInfoGrayWrapper.getLastModified() && !StringUtils.equals(targetConfigInfoGrayWrapper.getSrcUser(), NAMESPACE_MIGRATE_SRC_USER)) { targetConfigInfoGrayWrapper.setTenant(tenant); configInfoGrayPersistService.updateConfigInfo4Gray(targetConfigInfoGrayWrapper, targetConfigInfoGrayWrapper.getGrayName(), targetConfigInfoGrayWrapper.getGrayRule(), null, NAMESPACE_MIGRATE_SRC_USER); } else if (targetConfigInfoGrayWrapper.getLastModified() < changedConfigInfoGrayWrapper.getLastModified()) { changedConfigInfoGrayWrapper.setTenant(targetTenant); configInfoGrayPersistService.updateConfigInfo4Gray(changedConfigInfoGrayWrapper, changedConfigInfoGrayWrapper.getGrayName(), changedConfigInfoGrayWrapper.getGrayRule(), null, NAMESPACE_MIGRATE_SRC_USER); } } } } finally { GRAY_MIGRATE_FLAG.set(false); } } /** * Check changed config migrate state. * * @param changedConfigInfoStateWrapper the config info state wrapper */ public void checkChangedConfigMigrateState(ConfigInfoStateWrapper changedConfigInfoStateWrapper) { String tenant = changedConfigInfoStateWrapper.getTenant(); if (!ConfigCompatibleConfig.getInstance().isNamespaceCompatibleMode()) { return; } if (!StringUtils.equals(tenant, namespacePublic) && StringUtils.isNotBlank(tenant)) { return; } String targetTenant = StringUtils.EMPTY; if (StringUtils.isBlank(tenant)) { targetTenant = namespacePublic; } ConfigAllInfo changedConfigAllInfo = configInfoPersistService.findConfigAllInfo( changedConfigInfoStateWrapper.getDataId(), changedConfigInfoStateWrapper.getGroup(), tenant); ConfigAllInfo targetConfigAllInfo = configInfoPersistService.findConfigAllInfo( changedConfigInfoStateWrapper.getDataId(), changedConfigInfoStateWrapper.getGroup(), targetTenant); try { CONFIG_MIGRATE_FLAG.set(true); if (NAMESPACE_MIGRATE_SRC_USER.equals(changedConfigAllInfo.getCreateUser())) { if (targetConfigAllInfo == null) { configInfoPersistService.removeConfigInfo(changedConfigAllInfo.getDataId(), changedConfigAllInfo.getGroup(), tenant, null, NAMESPACE_MIGRATE_SRC_USER); } else if (!targetConfigAllInfo.getMd5().equals(changedConfigAllInfo.getMd5())) { if (targetConfigAllInfo.getModifyTime() >= changedConfigAllInfo.getModifyTime() || !StringUtils.equals(targetConfigAllInfo.getCreateUser(), NAMESPACE_MIGRATE_SRC_USER)) { targetConfigAllInfo.setTenant(tenant); configInfoPersistService.updateConfigInfo(targetConfigAllInfo, null, NAMESPACE_MIGRATE_SRC_USER, null); } } } else { if (targetConfigAllInfo == null) { changedConfigAllInfo.setTenant(targetTenant); configInfoPersistService.addConfigInfo(null, NAMESPACE_MIGRATE_SRC_USER, changedConfigAllInfo, null); } else if (!targetConfigAllInfo.getMd5().equals(changedConfigAllInfo.getMd5())) { if (targetConfigAllInfo.getModifyTime() >= changedConfigAllInfo.getModifyTime() && !StringUtils.equals(targetConfigAllInfo.getCreateUser(), NAMESPACE_MIGRATE_SRC_USER)) { targetConfigAllInfo.setTenant(tenant); configInfoPersistService.updateConfigInfo(targetConfigAllInfo, null, NAMESPACE_MIGRATE_SRC_USER, null); } else if (targetConfigAllInfo.getModifyTime() < changedConfigAllInfo.getModifyTime()) { changedConfigAllInfo.setTenant(targetTenant); configInfoPersistService.updateConfigInfo(changedConfigAllInfo, null, NAMESPACE_MIGRATE_SRC_USER, null); } } } } finally { CONFIG_MIGRATE_FLAG.set(false); } } /** * Check deleted config gray migrate state. * * @param deletedConfigInfoGrayStateWrapper the deleted config info gray state wrapper */ public void checkDeletedConfigGrayMigrateState(ConfigInfoStateWrapper deletedConfigInfoGrayStateWrapper) { if (deletedConfigInfoGrayStateWrapper == null) { return; } String tenant = deletedConfigInfoGrayStateWrapper.getTenant(); if (!ConfigCompatibleConfig.getInstance().isNamespaceCompatibleMode()) { return; } if (!StringUtils.equals(tenant, namespacePublic) && StringUtils.isNotBlank(tenant)) { return; } String targetTenant = StringUtils.EMPTY; if (StringUtils.isBlank(tenant)) { targetTenant = namespacePublic; } ConfigInfoStateWrapper targetConfigInfoGrayStateWrapper = configInfoGrayPersistService.findConfigInfo4GrayState( deletedConfigInfoGrayStateWrapper.getDataId(), deletedConfigInfoGrayStateWrapper.getGroup(), targetTenant, deletedConfigInfoGrayStateWrapper.getGrayName()); if (targetConfigInfoGrayStateWrapper == null) { return; } try { GRAY_MIGRATE_FLAG.set(true); if (targetConfigInfoGrayStateWrapper.getLastModified() <= deletedConfigInfoGrayStateWrapper.getLastModified()) { configInfoGrayPersistService.removeConfigInfoGray(deletedConfigInfoGrayStateWrapper.getDataId(), deletedConfigInfoGrayStateWrapper.getGroup(), targetTenant, deletedConfigInfoGrayStateWrapper.getGrayName(), null, NAMESPACE_MIGRATE_SRC_USER); } } finally { GRAY_MIGRATE_FLAG.set(false); } } /** * Check deleted config migrate state. * * @param deletedConfigInfoStateWrapper the deleted config info state wrapper */ public void checkDeletedConfigMigrateState(ConfigInfoStateWrapper deletedConfigInfoStateWrapper) { String tenant = deletedConfigInfoStateWrapper.getTenant(); if (!ConfigCompatibleConfig.getInstance().isNamespaceCompatibleMode()) { return; } if (!StringUtils.equals(tenant, namespacePublic) && StringUtils.isNotBlank(tenant)) { return; } String targetTenant = StringUtils.EMPTY; if (StringUtils.isBlank(tenant)) { targetTenant = namespacePublic; } ConfigInfoStateWrapper targetConfigInfoStateWrapper = configInfoPersistService.findConfigInfoState( deletedConfigInfoStateWrapper.getDataId(), deletedConfigInfoStateWrapper.getGroup(), targetTenant); if (targetConfigInfoStateWrapper == null) { return; } try { CONFIG_MIGRATE_FLAG.set(true); if (targetConfigInfoStateWrapper.getLastModified() <= deletedConfigInfoStateWrapper.getLastModified()) { configInfoPersistService.removeConfigInfo(deletedConfigInfoStateWrapper.getDataId(), deletedConfigInfoStateWrapper.getGroup(), targetTenant, null, NAMESPACE_MIGRATE_SRC_USER); } } finally { CONFIG_MIGRATE_FLAG.set(false); } } private void doCheckNamespaceMigrate() throws Exception { final long startTime = System.currentTimeMillis(); int maxNamespaceMigrateRetryTimes = EnvUtil.getProperty("nacos.namespace.migrate.retry.times", Integer.class, 3); namespaceMigratePreCheck(maxNamespaceMigrateRetryTimes); int batchSize = EnvUtil.getProperty("nacos.namespace.migrate.batch.size", Integer.class, 100); long startId = -1; List batchIds = new ArrayList<>(); int totalInsertNums = 0; LOGGER.info("[migrate] start migrate config namespace"); do { int retryTimes = 0; boolean migrateSuccess = false; while (retryTimes <= maxNamespaceMigrateRetryTimes) { try { batchIds = configMigratePersistService.getMigrateConfigInsertIdList(startId, batchSize); if (!batchIds.isEmpty()) { configMigratePersistService.migrateConfigInsertByIds(batchIds, NAMESPACE_MIGRATE_SRC_USER); startId = batchIds.get(batchIds.size() - 1); } migrateSuccess = true; break; } catch (Exception e) { LOGGER.error("[migrate] config_info namespace migrate insert failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!migrateSuccess) { LOGGER.error("[migrate] config_info namespace migrate insert failed"); throw new Exception("[migrate] config_info namespace migrate insert failed"); } else { totalInsertNums += batchIds.size(); } LOGGER.info("[migrate] migrating config namespace from empty to public, finished:" + totalInsertNums); } while (batchIds.size() == batchSize); long startEmptyId = -1; int totalUpdateFromEmptyNums = 0; List batchConfigInfosFromEmpty = new ArrayList<>(); do { int retryTimes = 0; boolean migrateSuccess = false; while (retryTimes <= maxNamespaceMigrateRetryTimes) { try { batchConfigInfosFromEmpty = configMigratePersistService .getMigrateConfigUpdateList(startEmptyId, batchSize, StringUtils.EMPTY, namespacePublic, NAMESPACE_MIGRATE_SRC_USER); if (!batchConfigInfosFromEmpty.isEmpty()) { for (ConfigInfo configInfo : batchConfigInfosFromEmpty) { configMigratePersistService.syncConfig(configInfo.getDataId(), configInfo.getGroup(), StringUtils.EMPTY, namespacePublic, NAMESPACE_MIGRATE_SRC_USER); } startEmptyId = batchConfigInfosFromEmpty.get(batchConfigInfosFromEmpty.size() - 1) .getId(); } migrateSuccess = true; break; } catch (Exception e) { LOGGER.error("[migrate] config_info namespace migrate update from empty failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!migrateSuccess) { LOGGER.error("[migrate] config_info namespace migrate update from empty failed, skipped"); if (!batchConfigInfosFromEmpty.isEmpty()) { startEmptyId = batchConfigInfosFromEmpty.get(batchConfigInfosFromEmpty.size() - 1).getId(); } } else { totalUpdateFromEmptyNums += batchConfigInfosFromEmpty.size(); } LOGGER.info("[migrate] syncing config namespace from empty to public, finished:" + totalUpdateFromEmptyNums); } while (batchConfigInfosFromEmpty.size() == batchSize); long startPublicId = -1; int totalUpdateFromPublicNums = 0; List batchConfigInfosFromPublic = new ArrayList<>(); do { int retryTimes = 0; boolean migrateSuccess = false; while (retryTimes <= maxNamespaceMigrateRetryTimes) { try { batchConfigInfosFromPublic = configMigratePersistService .getMigrateConfigUpdateList(startPublicId, batchSize, namespacePublic, StringUtils.EMPTY, NAMESPACE_MIGRATE_SRC_USER); if (!batchConfigInfosFromPublic.isEmpty()) { for (ConfigInfo configInfo : batchConfigInfosFromPublic) { configMigratePersistService.syncConfig(configInfo.getDataId(), configInfo.getGroup(), namespacePublic, StringUtils.EMPTY, NAMESPACE_MIGRATE_SRC_USER); } startPublicId = batchConfigInfosFromPublic.get(batchConfigInfosFromPublic.size() - 1) .getId(); } migrateSuccess = true; break; } catch (Exception e) { LOGGER.error("[migrate] config_info namespace migrate update from public failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!migrateSuccess) { LOGGER.error("[migrate] config_info namespace migrate update from public failed, skipped"); if (!batchConfigInfosFromPublic.isEmpty()) { startPublicId = batchConfigInfosFromPublic.get(batchConfigInfosFromPublic.size() - 1).getId(); } } else { totalUpdateFromPublicNums += batchConfigInfosFromPublic.size(); } LOGGER.info("[migrate] syncing config namespace from public to empty, finished:" + totalUpdateFromPublicNums); } while (batchConfigInfosFromPublic.size() == batchSize); long startGrayId = -1; int totalInsertGrayNum = 0; do { int retryTimes = 0; boolean migrateSuccess = false; while (retryTimes <= maxNamespaceMigrateRetryTimes) { try { batchIds = configMigratePersistService.getMigrateConfigGrayInsertIdList(startGrayId, batchSize); if (!batchIds.isEmpty()) { configMigratePersistService.migrateConfigGrayInsertByIds(batchIds, NAMESPACE_MIGRATE_SRC_USER); startGrayId = batchIds.get(batchIds.size() - 1); } migrateSuccess = true; break; } catch (Exception e) { LOGGER.error("[migrate] config_info gray namespace migrate insert failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!migrateSuccess) { LOGGER.error("[migrate] config_info_gray namespace migrate insert failed"); throw new Exception("[migrate] config_info_gray namespace migrate insert failed"); } else { totalInsertGrayNum += batchIds.size(); } LOGGER.info("[migrate] migrating config gray namespace from empty to public, finished:" + totalInsertGrayNum); } while (batchIds.size() == batchSize); long startGrayEmptyId = -1; int totalUpdateGrayFromEmptyNum = 0; List batchConfigInfoGraysFromEmpty = new ArrayList<>(); do { int retryTimes = 0; boolean migrateSuccess = false; while (retryTimes <= maxNamespaceMigrateRetryTimes) { try { batchConfigInfoGraysFromEmpty = configMigratePersistService .getMigrateConfigGrayUpdateList(startGrayEmptyId, batchSize, StringUtils.EMPTY, namespacePublic, NAMESPACE_MIGRATE_SRC_USER); if (!batchConfigInfoGraysFromEmpty.isEmpty()) { for (ConfigInfoGrayWrapper configInfoGrayWrapper : batchConfigInfoGraysFromEmpty) { configMigratePersistService.syncConfigGray(configInfoGrayWrapper.getDataId(), configInfoGrayWrapper.getGroup(), StringUtils.EMPTY, configInfoGrayWrapper.getGrayName(), namespacePublic, NAMESPACE_MIGRATE_SRC_USER); } startGrayEmptyId = batchConfigInfoGraysFromEmpty.get(batchConfigInfoGraysFromEmpty.size() - 1) .getId(); } migrateSuccess = true; break; } catch (Exception e) { LOGGER.error("[migrate] config_info_gray namespace migrate update from empty failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!migrateSuccess) { LOGGER.error("[migrate] config_info_gray namespace migrate update from empty failed, skipped"); if (!batchConfigInfoGraysFromEmpty.isEmpty()) { startGrayEmptyId = batchConfigInfoGraysFromEmpty.get(batchConfigInfoGraysFromEmpty.size() - 1) .getId(); } } else { totalUpdateGrayFromEmptyNum += batchConfigInfoGraysFromEmpty.size(); } LOGGER.info("[migrate] syncing config gray namespace from empty to public, finished:" + totalUpdateGrayFromEmptyNum); } while (batchConfigInfoGraysFromEmpty.size() == batchSize); long startGrayPublicId = -1; int totalUpdateGrayFromPublicNum = 0; List batchConfigInfoGraysFromPublic = new ArrayList<>(); do { int retryTimes = 0; boolean migrateSuccess = false; while (retryTimes <= maxNamespaceMigrateRetryTimes) { try { batchConfigInfoGraysFromPublic = configMigratePersistService .getMigrateConfigGrayUpdateList(startGrayPublicId, batchSize, namespacePublic, StringUtils.EMPTY, NAMESPACE_MIGRATE_SRC_USER); if (!batchConfigInfoGraysFromPublic.isEmpty()) { for (ConfigInfoGrayWrapper configInfoGrayWrapper : batchConfigInfoGraysFromPublic) { configMigratePersistService.syncConfigGray(configInfoGrayWrapper.getDataId(), configInfoGrayWrapper.getGroup(), namespacePublic, configInfoGrayWrapper.getGrayName(), StringUtils.EMPTY, NAMESPACE_MIGRATE_SRC_USER); } startGrayPublicId = batchConfigInfoGraysFromPublic.get(batchConfigInfoGraysFromPublic.size() - 1) .getId(); } migrateSuccess = true; break; } catch (Exception e) { LOGGER.error("[migrate] config_info_gray namespace migrate update from public failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!migrateSuccess) { LOGGER.error("[migrate] config_info_gray namespace migrate update from public failed, skipped"); if (!batchConfigInfoGraysFromPublic.isEmpty()) { startGrayPublicId = batchConfigInfoGraysFromPublic.get(batchConfigInfoGraysFromPublic.size() - 1) .getId(); } } else { totalUpdateGrayFromPublicNum += batchConfigInfoGraysFromPublic.size(); } LOGGER.info("[migrate] syncing config gray namespace from public to empty, finished:" + totalUpdateGrayFromPublicNum); } while (batchConfigInfoGraysFromPublic.size() == batchSize); LOGGER.info("[migrate] finish migrate config namespace" + "total time taken: " + (System.currentTimeMillis() - startTime) + " ms"); } private void namespaceMigratePreCheck(int maxRetryTimes) throws Exception { int retryTimes = 0; boolean checkSuccess = false; while (retryTimes <= maxRetryTimes) { try { int conflictCount = configMigratePersistService.configInfoConflictCount(NAMESPACE_MIGRATE_SRC_USER); if (conflictCount > 0) { LOGGER.error("[migrate] config_info conflict count=" + conflictCount); } else { checkSuccess = true; break; } } catch (Exception e) { LOGGER.error("[migrate] namespace migrate pre check failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!checkSuccess) { throw new Exception("[migrate] config_info namespace migrate pre check failed"); } retryTimes = 0; checkSuccess = false; while (retryTimes <= maxRetryTimes) { try { int conflictCount = configMigratePersistService.configInfoGrayConflictCount(NAMESPACE_MIGRATE_SRC_USER); if (conflictCount > 0) { LOGGER.error("[migrate] config_info_gray conflict count=" + conflictCount); } else { checkSuccess = true; break; } } catch (Exception e) { LOGGER.error("[migrate] namespace migrate pre check failed, retry times={}, error={}", retryTimes, e.getMessage()); } retryTimes++; Thread.sleep(1000L); } if (!checkSuccess) { throw new Exception("[migrate] config_gray namespace migrate pre check failed"); } } /** * Namespace migrate gray. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param grayName the gray name */ public void namespaceMigrateGray(String dataId, String group, String tenant, String grayName) { try { GRAY_MIGRATE_FLAG.set(true); if (StringUtils.isBlank(tenant)) { configMigratePersistService.syncConfigGray(dataId, group, tenant, grayName, namespacePublic, NAMESPACE_MIGRATE_SRC_USER); } else if (StringUtils.equals(tenant, namespacePublic)) { configMigratePersistService.syncConfigGray(dataId, group, tenant, grayName, "", NAMESPACE_MIGRATE_SRC_USER); } } catch (Exception e) { LOGGER.error("[migrate] namespace migrate gray failed", e); } finally { GRAY_MIGRATE_FLAG.set(false); } } /** * Namespace migrate. * * @param dataId the data id * @param group the group * @param tenant the tenant */ public void namespaceMigrate(String dataId, String group, String tenant) { try { CONFIG_MIGRATE_FLAG.set(true); if (StringUtils.isBlank(tenant)) { configMigratePersistService.syncConfig(dataId, group, tenant, namespacePublic, NAMESPACE_MIGRATE_SRC_USER); } else if (StringUtils.equals(tenant, namespacePublic)) { configMigratePersistService.syncConfig(dataId, group, tenant, "", NAMESPACE_MIGRATE_SRC_USER); } } catch (Exception e) { LOGGER.error("[migrate] namespace migrate failed", e); } finally { CONFIG_MIGRATE_FLAG.set(false); } } /** * Publish config migrate. * * @param configFormOrigin the config form origin * @param configRequestInfo the config request info * @param encryptedDataKey the encrypted data key * @throws NacosException the nacos exception */ public void publishConfigMigrate(ConfigForm configFormOrigin, ConfigRequestInfo configRequestInfo, String encryptedDataKey) throws NacosException { ConfigForm configForm = configFormOrigin.clone(); if (!StringUtils.equals(configForm.getNamespaceId(), namespacePublic) || !ConfigCompatibleConfig.getInstance() .isNamespaceCompatibleMode()) { return; } ConfigInfoWrapper targetConfigInfoWrapper = configInfoPersistService.findConfigInfo(configForm.getDataId(), configForm.getGroup(), ""); configForm.setNamespaceId(StringUtils.EMPTY); configForm.setSrcUser(NAMESPACE_MIGRATE_SRC_USER); Map configAdvanceInfo = getConfigAdvanceInfo(configForm); ParamUtils.checkParam(configAdvanceInfo); configForm.setEncryptedDataKey(encryptedDataKey); ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getAppName(), configForm.getContent()); //set old md5 if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configInfo.setMd5(configRequestInfo.getCasMd5()); } configInfo.setType(configForm.getType()); configInfo.setEncryptedDataKey(encryptedDataKey); ConfigOperateResult configOperateResult; try { CONFIG_MIGRATE_FLAG.set(true); if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configOperateResult = configInfoPersistService.insertOrUpdateCas(configRequestInfo.getSrcIp(), configForm.getSrcUser(), configInfo, configAdvanceInfo); if (!configOperateResult.isSuccess()) { LOGGER.warn( "[cas-publish-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.", configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5()); throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT, "Cas publish fail, server md5 may have changed."); } } else { if (configRequestInfo.getUpdateForExist()) { configInfoPersistService.insertOrUpdate(configRequestInfo.getSrcIp(), configForm.getSrcUser(), configInfo, configAdvanceInfo); } else { try { configInfoPersistService.addConfigInfo(configRequestInfo.getSrcIp(), configForm.getSrcUser(), configInfo, configAdvanceInfo); } catch (DataIntegrityViolationException ive) { LOGGER.warn( "[publish-config-failed] config already exists. dataId: {}, group: {}, namespaceId: {}", configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId()); throw new ConfigAlreadyExistsException( String.format("config already exist, dataId: %s, group: %s, namespaceId: %s", configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId())); } } } } finally { CONFIG_MIGRATE_FLAG.set(false); } } /** * Update config metadata migrate. * * @param dataId the data id * @param group the group * @param namespaceId the namespace id * @param configTags the config tags * @param description the description * @throws NacosException the nacos exception */ public void updateConfigMetadataMigrate(final String dataId, final String group, final String namespaceId, final String configTags, final String description) throws NacosException { if (!StringUtils.equals(namespaceId, namespacePublic) || !ConfigCompatibleConfig.getInstance() .isNamespaceCompatibleMode()) { return; } ConfigOperateResult configOperateResult; configOperateResult = configInfoPersistService.updateConfigInfoMetadata(dataId, group, StringUtils.EMPTY, configTags, description); if (!configOperateResult.isSuccess()) { LOGGER.warn("[update-config-metadata-fail] dataId: {}, group: {}, namespaceId: {}", dataId, group, namespaceId); throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT, "update metadata fail."); } } /** * Publish config gray migrate. * * @param grayType the gray type * @param configFormOrigin the config form origin * @param configRequestInfo the config request info * @throws NacosException the nacos exception */ public void publishConfigGrayMigrate(String grayType, ConfigForm configFormOrigin, ConfigRequestInfo configRequestInfo) throws NacosException { ConfigForm configForm = configFormOrigin.clone(); if (!StringUtils.equals(configForm.getNamespaceId(), namespacePublic) || !ConfigCompatibleConfig.getInstance() .isNamespaceCompatibleMode()) { return; } ConfigInfoGrayWrapper targetConfigInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray( configForm.getDataId(), configForm.getGroup(), "", configForm.getGrayName()); configForm.setNamespaceId(StringUtils.EMPTY); configForm.setSrcUser(NAMESPACE_MIGRATE_SRC_USER); Map configAdvanceInfo = getConfigAdvanceInfo(configForm); ParamUtils.checkParam(configAdvanceInfo); ConfigGrayPersistInfo localConfigGrayPersistInfo = new ConfigGrayPersistInfo(grayType, configForm.getGrayVersion(), configForm.getGrayRuleExp(), configForm.getGrayPriority()); GrayRule grayRuleStruct = GrayRuleManager.constructGrayRule(localConfigGrayPersistInfo); if (grayRuleStruct == null) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.CONFIG_GRAY_VERSION_INVALID, ErrorCode.CONFIG_GRAY_VERSION_INVALID.getMsg()); } if (!grayRuleStruct.isValid()) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.CONFIG_GRAY_RULE_FORMAT_INVALID, ErrorCode.CONFIG_GRAY_RULE_FORMAT_INVALID.getMsg()); } ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getAppName(), configForm.getContent()); // set old md5 if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configInfo.setMd5(configRequestInfo.getCasMd5()); } configInfo.setType(configForm.getType()); configInfo.setEncryptedDataKey(configForm.getEncryptedDataKey()); if (StringUtils.equals(grayType, TagGrayRule.TYPE_TAG)) { persistTagv1(configForm, configInfo, configRequestInfo); } else if (StringUtils.equals(grayType, BetaGrayRule.TYPE_BETA)) { persistBeta(configForm, configInfo, configRequestInfo); } ConfigOperateResult configOperateResult; try { GRAY_MIGRATE_FLAG.set(true); if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configOperateResult = configInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, configForm.getGrayName(), GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), configRequestInfo.getSrcIp(), configForm.getSrcUser()); if (!configOperateResult.isSuccess()) { LOGGER.warn( "[cas-publish-gray-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, grayName = {}, msg = server md5 may have changed.", configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5(), configForm.getGrayName()); throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT, "Cas publish gray config fail, server md5 may have changed."); } } else { configInfoGrayPersistService.insertOrUpdateGray(configInfo, configForm.getGrayName(), GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), configRequestInfo.getSrcIp(), configForm.getSrcUser()); } } finally { GRAY_MIGRATE_FLAG.set(false); } } /** * Remove config info migrate. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param srcIp the src ip * @param srcUser the src user */ public void removeConfigInfoMigrate(String dataId, String group, String tenant, String srcIp, String srcUser) { if (!StringUtils.equals(tenant, namespacePublic) || !ConfigCompatibleConfig.getInstance() .isNamespaceCompatibleMode()) { return; } try { CONFIG_MIGRATE_FLAG.set(true); configInfoPersistService.removeConfigInfo(dataId, group, "", srcIp, NAMESPACE_MIGRATE_SRC_USER); } finally { CONFIG_MIGRATE_FLAG.set(false); } } /** * Remove config info gray migrate. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param grayName the gray name * @param srcIp the src ip * @param srcUser the src user */ public void removeConfigInfoGrayMigrate(String dataId, String group, String tenant, String grayName, String srcIp, String srcUser) { if (!StringUtils.equals(tenant, namespacePublic) || !ConfigCompatibleConfig.getInstance() .isNamespaceCompatibleMode()) { return; } try { GRAY_MIGRATE_FLAG.set(true); configInfoGrayPersistService.removeConfigInfoGray(dataId, group, "", grayName, srcIp, NAMESPACE_MIGRATE_SRC_USER); deleteConfigGrayV1(dataId, group, "", grayName, srcIp, NAMESPACE_MIGRATE_SRC_USER); } finally { GRAY_MIGRATE_FLAG.set(false); } } public Map getConfigAdvanceInfo(ConfigForm configForm) { Map configAdvanceInfo = new HashMap<>(10); MapUtil.putIfValNoNull(configAdvanceInfo, "config_tags", configForm.getConfigTags()); MapUtil.putIfValNoNull(configAdvanceInfo, "desc", configForm.getDesc()); MapUtil.putIfValNoNull(configAdvanceInfo, "use", configForm.getUse()); MapUtil.putIfValNoNull(configAdvanceInfo, "effect", configForm.getEffect()); MapUtil.putIfValNoNull(configAdvanceInfo, "type", configForm.getType()); MapUtil.putIfValNoNull(configAdvanceInfo, "schema", configForm.getSchema()); return configAdvanceInfo; } private void doCheckMigrate() throws Exception { int migrateMulti = EnvUtil.getProperty("nacos.gray.migrate.executor.multi", Integer.class, Integer.valueOf(4)); ThreadPoolExecutor executorService = new ThreadPoolExecutor(ThreadUtils.getSuitableThreadCount(migrateMulti), ThreadUtils.getSuitableThreadCount(migrateMulti), 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(PropertyUtil.getAllDumpPageSize() * migrateMulti), r -> new Thread(r, "gray-migrate-worker"), new ThreadPoolExecutor.CallerRunsPolicy()); int pageSize = 100; int rowCount = configInfoBetaPersistService.configInfoBetaCount(); int pageCount = (int) Math.ceil(rowCount * 1.0 / pageSize); int actualRowCount = 0; for (int pageNo = 1; pageNo <= pageCount; pageNo++) { Page page = configInfoBetaPersistService.findAllConfigInfoBetaForDumpAll(pageNo, pageSize); if (page != null) { for (ConfigInfoBetaWrapper cf : page.getPageItems()) { executorService.execute(() -> { GRAY_MIGRATE_FLAG.set(true); ConfigInfoGrayWrapper configInfo4Gray = configInfoGrayPersistService.findConfigInfo4Gray( cf.getDataId(), cf.getGroup(), cf.getTenant(), BetaGrayRule.TYPE_BETA); if (configInfo4Gray == null || configInfo4Gray.getLastModified() < cf.getLastModified()) { DEFAULT_LOG.info("[migrate beta to gray] dataId={}, group={}, tenant={}, md5={}", cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getMd5()); ConfigGrayPersistInfo localConfigGrayPersistInfo = new ConfigGrayPersistInfo( BetaGrayRule.TYPE_BETA, BetaGrayRule.VERSION, cf.getBetaIps(), BetaGrayRule.PRIORITY); configInfoGrayPersistService.insertOrUpdateGray(cf, BetaGrayRule.TYPE_BETA, GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), NetUtils.localIp(), "nacos_auto_migrate"); GRAY_MIGRATE_FLAG.set(false); } }); } actualRowCount += page.getPageItems().size(); DEFAULT_LOG.info("[gray-migrate-beta] submit gray task {} / {}", actualRowCount, rowCount); } } try { int unfinishedTaskCount = 0; while ((unfinishedTaskCount = executorService.getQueue().size() + executorService.getActiveCount()) > 0) { DEFAULT_LOG.info("[gray-migrate-beta] wait {} migrate tasks to be finished", unfinishedTaskCount); Thread.sleep(1000L); } } catch (Exception e) { DEFAULT_LOG.error("[gray-migrate-beta] wait dump tasks to be finished error", e); throw e; } rowCount = configInfoTagPersistService.configInfoTagCount(); pageCount = (int) Math.ceil(rowCount * 1.0 / pageSize); actualRowCount = 0; for (int pageNo = 1; pageNo <= pageCount; pageNo++) { Page page = configInfoTagPersistService.findAllConfigInfoTagForDumpAll(pageNo, pageSize); if (page != null) { for (ConfigInfoTagWrapper cf : page.getPageItems()) { executorService.execute(() -> { GRAY_MIGRATE_FLAG.set(true); ConfigInfoGrayWrapper configInfo4Gray = configInfoGrayPersistService.findConfigInfo4Gray( cf.getDataId(), cf.getGroup(), cf.getTenant(), TagGrayRule.TYPE_TAG + "_" + cf.getTag()); if (configInfo4Gray == null || configInfo4Gray.getLastModified() < cf.getLastModified()) { DEFAULT_LOG.info("[migrate tag to gray] dataId={}, group={}, tenant={}, md5={}", cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getMd5()); ConfigGrayPersistInfo localConfigGrayPersistInfo = new ConfigGrayPersistInfo( TagGrayRule.TYPE_TAG, TagGrayRule.VERSION, cf.getTag(), TagGrayRule.PRIORITY); configInfoGrayPersistService.insertOrUpdateGray(cf, TagGrayRule.TYPE_TAG + "_" + cf.getTag(), GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), NetUtils.localIp(), "nacos_auto_migrate"); GRAY_MIGRATE_FLAG.set(false); } }); } actualRowCount += page.getPageItems().size(); DEFAULT_LOG.info("[gray-migrate-tag] submit gray task {} / {}", actualRowCount, rowCount); } } try { int unfinishedTaskCount = 0; while ((unfinishedTaskCount = executorService.getQueue().size() + executorService.getActiveCount()) > 0) { DEFAULT_LOG.info("[gray-migrate-tag] wait {} migrate tasks to be finished", unfinishedTaskCount); Thread.sleep(1000L); } } catch (Exception e) { DEFAULT_LOG.error("[gray-migrate-tag] wait migrate tasks to be finished error", e); throw e; } //shut down migrate executor executorService.shutdown(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigOperationService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.MapUtil; import com.alibaba.nacos.common.utils.NumberUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.exception.ConfigAlreadyExistsException; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.model.event.IstioConfigChangeEvent; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.ConfigGrayPersistInfo; import com.alibaba.nacos.config.server.model.gray.GrayRule; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.ConfigTagUtil; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.InetUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.sql.Timestamp; import java.util.HashMap; import java.util.List; import java.util.Map; /** * ConfigService. * * @author dongyafei * @date 2022/8/11 */ @Service public class ConfigOperationService { private ConfigInfoPersistService configInfoPersistService; private ConfigInfoGrayPersistService configInfoGrayPersistService; private ConfigMigrateService configMigrateService; private static final Logger LOGGER = LoggerFactory.getLogger(ConfigOperationService.class); public ConfigOperationService(ConfigInfoPersistService configInfoPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService, ConfigMigrateService configMigrateService) { this.configInfoPersistService = configInfoPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; this.configMigrateService = configMigrateService; } /** * Adds or updates non-aggregated data. * * @throws NacosException NacosException. */ public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo, String encryptedDataKey) throws NacosException { Map configAdvanceInfo = getConfigAdvanceInfo(configForm); ParamUtils.checkParam(configAdvanceInfo); configForm.setEncryptedDataKey(encryptedDataKey); ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getAppName(), configForm.getContent()); //set old md5 if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configInfo.setMd5(configRequestInfo.getCasMd5()); } configInfo.setType(configForm.getType()); configInfo.setEncryptedDataKey(encryptedDataKey); //beta publish if (StringUtils.isNotBlank(configRequestInfo.getBetaIps())) { configForm.setGrayName(BetaGrayRule.TYPE_BETA); configForm.setGrayRuleExp(configRequestInfo.getBetaIps()); configForm.setGrayVersion(BetaGrayRule.VERSION); configMigrateService.persistBeta(configForm, configInfo, configRequestInfo); configForm.setGrayPriority(Integer.MAX_VALUE); configMigrateService.publishConfigGrayMigrate(BetaGrayRule.TYPE_BETA, configForm, configRequestInfo); publishConfigGray(BetaGrayRule.TYPE_BETA, configForm, configRequestInfo); return Boolean.TRUE; } // tag publish if (StringUtils.isNotBlank(configForm.getTag())) { configForm.setGrayName(TagGrayRule.TYPE_TAG + "_" + configForm.getTag()); configForm.setGrayRuleExp(configForm.getTag()); configForm.setGrayVersion(TagGrayRule.VERSION); configForm.setGrayPriority(Integer.MAX_VALUE - 1); configMigrateService.persistTagv1(configForm, configInfo, configRequestInfo); configMigrateService.publishConfigGrayMigrate(TagGrayRule.TYPE_TAG, configForm, configRequestInfo); publishConfigGray(TagGrayRule.TYPE_TAG, configForm, configRequestInfo); return Boolean.TRUE; } ConfigOperateResult configOperateResult; configMigrateService.publishConfigMigrate(configForm, configRequestInfo, configForm.getEncryptedDataKey()); //formal publish if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configOperateResult = configInfoPersistService.insertOrUpdateCas(configRequestInfo.getSrcIp(), configForm.getSrcUser(), configInfo, configAdvanceInfo); if (!configOperateResult.isSuccess()) { LOGGER.warn( "[cas-publish-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, msg = server md5 may have changed.", configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5()); throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT, "Cas publish fail, server md5 may have changed."); } } else { if (configRequestInfo.getUpdateForExist()) { configOperateResult = configInfoPersistService.insertOrUpdate(configRequestInfo.getSrcIp(), configForm.getSrcUser(), configInfo, configAdvanceInfo); } else { try { configOperateResult = configInfoPersistService.addConfigInfo(configRequestInfo.getSrcIp(), configForm.getSrcUser(), configInfo, configAdvanceInfo); } catch (DataIntegrityViolationException ive) { configOperateResult = new ConfigOperateResult(false); } } } if (!configOperateResult.isSuccess()) { LOGGER.warn("[publish-config-failed] config already exists. dataId: {}, group: {}, namespaceId: {}", configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId()); throw new ConfigAlreadyExistsException( String.format("config already exist, dataId: %s, group: %s, namespaceId: %s", configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId())); } ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configOperateResult.getLastModified())); if (ConfigTagUtil.isIstio(configForm.getConfigTags())) { ConfigChangePublisher.notifyConfigChange( new IstioConfigChangeEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configOperateResult.getLastModified(), configForm.getContent(), ConfigTagUtil.getIstioType(configForm.getConfigTags()))); } ConfigTraceService.logPersistenceEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configRequestInfo.getRequestIpApp(), configOperateResult.getLastModified(), InetUtils.getSelfIP(), ConfigTraceService.PERSISTENCE_EVENT, ConfigTraceService.PERSISTENCE_TYPE_PUB, configForm.getContent()); return true; } /** * publish gray config tag v2. * * @param configForm ConfigForm * @param configRequestInfo ConfigRequestInfo * @return boolean * @throws NacosException NacosException. * @date 2024/2/5 */ private Boolean publishConfigGray(String grayType, ConfigForm configForm, ConfigRequestInfo configRequestInfo) throws NacosException { Map configAdvanceInfo = getConfigAdvanceInfo(configForm); ParamUtils.checkParam(configAdvanceInfo); ConfigGrayPersistInfo localConfigGrayPersistInfo = new ConfigGrayPersistInfo(grayType, configForm.getGrayVersion(), configForm.getGrayRuleExp(), configForm.getGrayPriority()); GrayRule grayRuleStruct = GrayRuleManager.constructGrayRule(localConfigGrayPersistInfo); if (grayRuleStruct == null) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.CONFIG_GRAY_VERSION_INVALID, ErrorCode.CONFIG_GRAY_VERSION_INVALID.getMsg()); } if (!grayRuleStruct.isValid()) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.CONFIG_GRAY_RULE_FORMAT_INVALID, ErrorCode.CONFIG_GRAY_RULE_FORMAT_INVALID.getMsg()); } //version count check. if (checkGrayVersionOverMaxCount(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getGrayName())) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.CONFIG_GRAY_OVER_MAX_VERSION_COUNT, "gray config version is over max count :" + getMaxGrayVersionCount()); } ConfigInfo configInfo = new ConfigInfo(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getAppName(), configForm.getContent()); // set old md5 if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configInfo.setMd5(configRequestInfo.getCasMd5()); } configInfo.setType(configForm.getType()); configInfo.setEncryptedDataKey(configForm.getEncryptedDataKey()); ConfigOperateResult configOperateResult; if (StringUtils.isNotBlank(configRequestInfo.getCasMd5())) { configOperateResult = configInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, configForm.getGrayName(), GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), configRequestInfo.getSrcIp(), configForm.getSrcUser()); if (!configOperateResult.isSuccess()) { LOGGER.warn( "[cas-publish-gray-config-fail] srcIp = {}, dataId= {}, casMd5 = {}, grayName = {}, msg = server md5 may have changed.", configRequestInfo.getSrcIp(), configForm.getDataId(), configRequestInfo.getCasMd5(), configForm.getGrayName()); throw new NacosApiException(HttpStatus.INTERNAL_SERVER_ERROR.value(), ErrorCode.RESOURCE_CONFLICT, "Cas publish gray config fail, server md5 may have changed."); } } else { configOperateResult = configInfoGrayPersistService.insertOrUpdateGray(configInfo, configForm.getGrayName(), GrayRuleManager.serializeConfigGrayPersistInfo(localConfigGrayPersistInfo), configRequestInfo.getSrcIp(), configForm.getSrcUser()); } ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getGrayName(), configOperateResult.getLastModified())); String eventType = ConfigTraceService.PERSISTENCE_EVENT + "-" + configForm.getGrayName(); ConfigTraceService.logPersistenceEvent(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configRequestInfo.getRequestIpApp(), configOperateResult.getLastModified(), InetUtils.getSelfIP(), eventType, ConfigTraceService.PERSISTENCE_TYPE_PUB, configForm.getContent()); return true; } private boolean checkGrayVersionOverMaxCount(String dataId, String group, String tenant, String grayName) { List configInfoGrays = configInfoGrayPersistService.findConfigInfoGrays(dataId, group, tenant); if (configInfoGrays == null || configInfoGrays.isEmpty()) { return false; } else { if (configInfoGrays.contains(grayName)) { return false; } return configInfoGrays.size() >= getMaxGrayVersionCount(); } } private static final int DEFAULT_MAX_GRAY_VERSION_COUNT = 10; private int getMaxGrayVersionCount() { String value = EnvUtil.getProperty("nacos.config.gray.version.max.count", ""); return NumberUtils.isDigits(value) ? NumberUtils.toInt(value) : DEFAULT_MAX_GRAY_VERSION_COUNT; } /** * Synchronously delete all pre-aggregation data under a dataId. */ public Boolean deleteConfig(String dataId, String group, String namespaceId, String grayName, String clientIp, String srcUser, String srcType) { String persistEvent = ConfigTraceService.PERSISTENCE_EVENT; if (StringUtils.isBlank(grayName)) { configInfoPersistService.removeConfigInfo(dataId, group, namespaceId, clientIp, srcUser); configMigrateService.removeConfigInfoMigrate(dataId, group, namespaceId, clientIp, srcUser); } else { persistEvent = ConfigTraceService.PERSISTENCE_EVENT + "-" + grayName; configInfoGrayPersistService.removeConfigInfoGray(dataId, group, namespaceId, grayName, clientIp, srcUser); configMigrateService.deleteConfigGrayV1(dataId, group, namespaceId, grayName, clientIp, srcUser); configMigrateService.removeConfigInfoGrayMigrate(dataId, group, namespaceId, grayName, clientIp, srcUser); } final Timestamp time = TimeUtils.getCurrentTime(); ConfigTraceService.logPersistenceEvent(dataId, group, namespaceId, null, time.getTime(), clientIp, persistEvent, ConfigTraceService.PERSISTENCE_TYPE_REMOVE, null); ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(dataId, group, namespaceId, grayName, time.getTime())); return true; } public Map getConfigAdvanceInfo(ConfigForm configForm) { Map configAdvanceInfo = new HashMap<>(10); MapUtil.putIfValNoNull(configAdvanceInfo, "config_tags", configForm.getConfigTags()); MapUtil.putIfValNoNull(configAdvanceInfo, "desc", configForm.getDesc()); MapUtil.putIfValNoNull(configAdvanceInfo, "use", configForm.getUse()); MapUtil.putIfValNoNull(configAdvanceInfo, "effect", configForm.getEffect()); MapUtil.putIfValNoNull(configAdvanceInfo, "type", configForm.getType()); MapUtil.putIfValNoNull(configAdvanceInfo, "schema", configForm.getSchema()); return configAdvanceInfo; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigReadinessCheckService.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.core.cluster.health.AbstractModuleHealthChecker; import com.alibaba.nacos.core.utils.Loggers; import org.springframework.stereotype.Service; /** * Readiness check service for config module. * * @author xiweng.yy */ @Service public class ConfigReadinessCheckService extends AbstractModuleHealthChecker { private final ConfigInfoPersistService configInfoPersistService; public ConfigReadinessCheckService(ConfigInfoPersistService configInfoPersistService) { this.configInfoPersistService = configInfoPersistService; } @Override public boolean readiness() { // check db try { configInfoPersistService.configInfoCount(""); return true; } catch (Exception e) { Loggers.CLUSTER.error("Config health check fail.", e); } return false; } @Override public String getModuleName() { return Constants.Config.CONFIG_MODULE; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/ConfigSubService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.config.NacosAuthConfigHolder; import com.alibaba.nacos.auth.util.AuthHeaderUtil; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ListenerCheckResult; import com.alibaba.nacos.config.server.model.SampleResult; import com.alibaba.nacos.config.server.service.notify.HttpClientManager; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.core.auth.NacosServerAuthConfig; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.stereotype.Service; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.CompletionService; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * Config sub service. * * @author Nacos */ @Service @Deprecated public class ConfigSubService { private ServerMemberManager memberManager; public ConfigSubService(ServerMemberManager memberManager) { this.memberManager = memberManager; } /** * Get and return called url string value. * * @param ip ip. * @param relativePath path. * @return all path. */ private static String getUrl(String ip, String relativePath) { return HTTP_PREFIX + ip + EnvUtil.getContextPath() + relativePath; } private List runConfigListenerCollectionJob(Map params, CompletionService completionService) { return new ClusterListenerJob(params, completionService, memberManager).runJobs(); } private List runConfigListenerByIpCollectionJob(Map params, CompletionService completionService) { return new ClusterListenerByIpJob(params, completionService, memberManager).runJobs(); } static class ClusterListenerJob extends ClusterJob { static final String URL = Constants.COMMUNICATION_CONTROLLER_PATH + "/configWatchers"; ClusterListenerJob(Map params, CompletionService completionService, ServerMemberManager serverMemberManager) { super(URL, params, completionService, serverMemberManager); } } static class ClusterListenerByIpJob extends ClusterJob { static final String URL = Constants.COMMUNICATION_CONTROLLER_PATH + "/watcherConfigs"; ClusterListenerByIpJob(Map params, CompletionService completionService, ServerMemberManager serverMemberManager) { super(URL, params, completionService, serverMemberManager); } } abstract static class ClusterJob { private String url; private Map params; private CompletionService completionService; private ServerMemberManager serverMemberManager; ClusterJob(String url, Map params, CompletionService completionService, ServerMemberManager serverMemberManager) { this.url = url; this.params = params; this.completionService = completionService; this.serverMemberManager = serverMemberManager; } class Job implements Callable { private String ip; public Job(String ip) { this.ip = ip; } @Override public T call() throws Exception { return (T) runSingleJob(ip, params, url, ((ParameterizedType) ClusterJob.this.getClass() .getGenericSuperclass()).getActualTypeArguments()[0]); } } List runJobs() { Collection ipList = serverMemberManager.allMembers(); List collectionResult = new ArrayList<>(ipList.size()); // Submit query task. for (Member ip : ipList) { try { completionService.submit(new Job(ip.getAddress()) { }); } catch (Throwable e) { // Send request failed. LogUtil.DEFAULT_LOG.warn("invoke to {} with exception: {} during submit job", ip, e.getMessage()); } } // Get and merge result. T sampleResults; for (Member member : ipList) { try { Future f = completionService.poll(1000, TimeUnit.MILLISECONDS); try { if (f != null) { sampleResults = f.get(500, TimeUnit.MILLISECONDS); if (sampleResults != null) { collectionResult.add(sampleResults); } } else { LogUtil.DEFAULT_LOG.warn("The task in ip: {} did not completed in 1000ms ", member); } } catch (TimeoutException e) { if (f != null) { f.cancel(true); } LogUtil.DEFAULT_LOG.warn("get task result with TimeoutException: {} ", e.getMessage()); } } catch (Exception e) { LogUtil.DEFAULT_LOG.warn("get task result with Exception: {} ", e.getMessage()); } } return collectionResult; } } /** * run job to a single member. * * @param ip ip. * @param params params. * @param url url. * @param type type. * @return */ public static Object runSingleJob(String ip, Map params, String url, Type type) { try { StringBuilder paramUrl = new StringBuilder(); for (Map.Entry param : params.entrySet()) { paramUrl.append("&").append(param.getKey()).append("=") .append(URLEncoder.encode(param.getValue(), Constants.ENCODE_UTF8)); } String urlAll = getUrl(ip, url) + "?" + paramUrl; RestResult result = invokeUrl(urlAll, Constants.ENCODE_UTF8); // Http code 200 if (result.ok()) { Object t = JacksonUtils.toObj(result.getData(), type); return t; } else { LogUtil.DEFAULT_LOG.info("Can not get remote from {} with {}", ip, result.getData()); return null; } } catch (Exception e) { LogUtil.DEFAULT_LOG.warn("Get remote info from {} with exception: {}", ip, e.getMessage()); return null; } } /** * if has all server has not listener,return false. * * @param listenerCheckResult listenerCheckResult. * @param sampleResults sampleResults. * @return */ public ListenerCheckResult mergeListenerCheckResult(ListenerCheckResult listenerCheckResult, List sampleResults, int expectSize) { for (ListenerCheckResult sampleResult : sampleResults) { if (sampleResult.getCode() == 200 && sampleResult.isHasListener()) { listenerCheckResult.setHasListener(true); listenerCheckResult.setCode(200); break; } } if (!listenerCheckResult.isHasListener() && sampleResults.size() != expectSize) { listenerCheckResult.setCode(201); } return listenerCheckResult; } /** * Merge SampleResult. * * @param sampleCollectResult sampleCollectResult. * @param sampleResults sampleResults. * @return SampleResult. */ public SampleResult mergeSampleResult(SampleResult sampleCollectResult, List sampleResults) { SampleResult mergeResult = new SampleResult(); Map listenersGroupkeyStatus; if (sampleCollectResult.getLisentersGroupkeyStatus() == null || sampleCollectResult.getLisentersGroupkeyStatus() .isEmpty()) { listenersGroupkeyStatus = new HashMap<>(10); } else { listenersGroupkeyStatus = sampleCollectResult.getLisentersGroupkeyStatus(); } for (SampleResult sampleResult : sampleResults) { Map listenersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus(); listenersGroupkeyStatus.putAll(listenersGroupkeyStatusTmp); } mergeResult.setLisentersGroupkeyStatus(listenersGroupkeyStatus); return mergeResult; } public SampleResult getCollectSampleResult(String dataId, String group, String tenant, int sampleTime) throws Exception { Map params = new HashMap<>(5); params.put("dataId", dataId); params.put("group", group); if (!StringUtils.isBlank(tenant)) { params.put("tenant", tenant); } BlockingQueue> queue = new LinkedBlockingDeque<>(memberManager.getServerList().size()); CompletionService completionService = new ExecutorCompletionService<>( ConfigExecutor.getConfigSubServiceExecutor(), queue); SampleResult sampleCollectResult = new SampleResult(); for (int i = 0; i < sampleTime; i++) { List sampleResults = runConfigListenerCollectionJob(params, completionService); if (sampleResults != null) { sampleCollectResult = mergeSampleResult(sampleCollectResult, sampleResults); } } return sampleCollectResult; } public SampleResult getCollectSampleResultByIp(String ip, int sampleTime) { Map params = new HashMap<>(50); params.put("ip", ip); BlockingQueue> queue = new LinkedBlockingDeque<>(memberManager.getServerList().size()); CompletionService completionService = new ExecutorCompletionService<>( ConfigExecutor.getConfigSubServiceExecutor(), queue); SampleResult sampleCollectResult = new SampleResult(); for (int i = 0; i < sampleTime; i++) { List sampleResults = runConfigListenerByIpCollectionJob(params, completionService); if (sampleResults != null) { sampleCollectResult = mergeSampleResult(sampleCollectResult, sampleResults); } } return sampleCollectResult; } /** * invoke url with http. * * @param url url. * @param encoding encoding. * @return result. * @throws Exception exception. */ public static RestResult invokeUrl(String url, String encoding) throws Exception { Header header = Header.newInstance(); header.addParam(HttpHeaderConsts.ACCEPT_CHARSET, encoding); NacosAuthConfig authConfig = NacosAuthConfigHolder.getInstance() .getNacosAuthConfigByScope(NacosServerAuthConfig.NACOS_SERVER_AUTH_SCOPE); AuthHeaderUtil.addIdentityToHeader(header, authConfig); return HttpClientManager.getNacosRestTemplate().get(url, header, Query.EMPTY, String.class); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/HistoryService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.enums.OperationType; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigHistoryInfoDetail; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.plugin.auth.exception.AccessException; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import java.util.List; import java.util.Objects; /** * HistoryService. * * @author dongyafei * @date 2022/8/11 */ @Service public class HistoryService { private final HistoryConfigInfoPersistService historyConfigInfoPersistService; private final ConfigInfoPersistService configInfoPersistService; private final ConfigInfoGrayPersistService configInfoGrayPersistService; public HistoryService(HistoryConfigInfoPersistService historyConfigInfoPersistService, ConfigInfoPersistService configInfoPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService) { this.historyConfigInfoPersistService = historyConfigInfoPersistService; this.configInfoPersistService = configInfoPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; } /** * Query the list history config. */ public Page listConfigHistory(String dataId, String group, String namespaceId, Integer pageNo, Integer pageSize) { return historyConfigInfoPersistService.findConfigHistory(dataId, group, namespaceId, pageNo, pageSize); } /** * Query the detailed configuration history information. */ public ConfigHistoryInfo getConfigHistoryInfo(String dataId, String group, String namespaceId, Long nid) throws AccessException { ConfigHistoryInfo configHistoryInfo = historyConfigInfoPersistService.detailConfigHistory(nid); if (Objects.isNull(configHistoryInfo)) { return null; } // check if history config match the input checkHistoryInfoPermission(configHistoryInfo, dataId, group, namespaceId); String encryptedDataKey = configHistoryInfo.getEncryptedDataKey(); Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, configHistoryInfo.getContent()); configHistoryInfo.setContent(pair.getSecond()); return configHistoryInfo; } /** * Query previous config history information. */ public ConfigHistoryInfo getPreviousConfigHistoryInfo(String dataId, String group, String namespaceId, Long id) throws AccessException { ConfigHistoryInfo configHistoryInfo = historyConfigInfoPersistService.detailPreviousConfigHistory(id); if (Objects.isNull(configHistoryInfo)) { return null; } // check if history config match the input checkHistoryInfoPermission(configHistoryInfo, dataId, group, namespaceId); String encryptedDataKey = configHistoryInfo.getEncryptedDataKey(); Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, configHistoryInfo.getContent()); configHistoryInfo.setContent(pair.getSecond()); return configHistoryInfo; } /** * Query configs list by namespace. */ public List getConfigListByNamespace(String namespaceId) { return configInfoPersistService.queryConfigInfoByNamespace(namespaceId); } /** * Check if the input dataId,group and namespaceId match the history config. */ private void checkHistoryInfoPermission(ConfigHistoryInfo configHistoryInfo, String dataId, String group, String namespaceId) throws AccessException { if (!Objects.equals(configHistoryInfo.getDataId(), dataId) || !Objects.equals(configHistoryInfo.getGroup(), group) || !Objects.equals(configHistoryInfo.getTenant(), namespaceId)) { throw new AccessException("Please check dataId, group or namespaceId."); } } /** * Query the detailed config history info pair, including the original version and the updated version. */ public ConfigHistoryInfoDetail getConfigHistoryInfoDetail(String dataId, String group, String namespaceId, Long nid) throws AccessException { ConfigHistoryInfo configHistoryInfo = historyConfigInfoPersistService.detailConfigHistory(nid); if (Objects.isNull(configHistoryInfo)) { return null; } // check if history config match the input checkHistoryInfoPermission(configHistoryInfo, dataId, group, namespaceId); // transform ConfigHistoryInfoDetail configHistoryInfoDetail = new ConfigHistoryInfoDetail(); BeanUtils.copyProperties(configHistoryInfo, configHistoryInfoDetail); configHistoryInfoDetail.setOpType(configHistoryInfoDetail.getOpType().trim()); //insert if (OperationType.INSERT.getValue().equals(configHistoryInfoDetail.getOpType())) { configHistoryInfoDetail.setUpdatedContent(configHistoryInfo.getContent()); configHistoryInfoDetail.setUpdatedMd5(configHistoryInfo.getMd5()); configHistoryInfoDetail.setUpdatedEncryptedDataKey(configHistoryInfo.getEncryptedDataKey()); configHistoryInfoDetail.setUpdateExtInfo(configHistoryInfo.getExtInfo()); configHistoryInfoDetail.setOriginalExtInfo(StringUtils.EMPTY); configHistoryInfoDetail.setOriginalContent(StringUtils.EMPTY); configHistoryInfoDetail.setOriginalMd5(StringUtils.EMPTY); configHistoryInfoDetail.setOriginalEncryptedDataKey(StringUtils.EMPTY); } //update if (OperationType.UPDATE.getValue().equals(configHistoryInfoDetail.getOpType())) { configHistoryInfoDetail.setOriginalExtInfo(configHistoryInfo.getExtInfo()); configHistoryInfoDetail.setOriginalContent(configHistoryInfo.getContent()); configHistoryInfoDetail.setOriginalMd5(configHistoryInfo.getMd5()); configHistoryInfoDetail.setOriginalEncryptedDataKey(configHistoryInfo.getEncryptedDataKey()); ConfigHistoryInfo nextHistoryInfo = historyConfigInfoPersistService.getNextHistoryInfo(dataId, group, namespaceId, configHistoryInfoDetail.getPublishType(), configHistoryInfoDetail.getGrayName(), nid); ConfigInfo currentConfigInfo = null; if (Objects.isNull(nextHistoryInfo)) { //double check for concurrent currentConfigInfo = StringUtils.isEmpty(configHistoryInfoDetail.getGrayName()) ? configInfoPersistService.findConfigInfo(dataId, group, namespaceId) : configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, namespaceId, configHistoryInfoDetail.getGrayName()); nextHistoryInfo = historyConfigInfoPersistService.getNextHistoryInfo(dataId, group, namespaceId, configHistoryInfoDetail.getPublishType(), configHistoryInfoDetail.getGrayName(), nid); } if (nextHistoryInfo != null) { configHistoryInfoDetail.setUpdateExtInfo(nextHistoryInfo.getExtInfo()); configHistoryInfoDetail.setUpdatedContent(nextHistoryInfo.getContent()); configHistoryInfoDetail.setUpdatedMd5(nextHistoryInfo.getMd5()); configHistoryInfoDetail.setUpdatedEncryptedDataKey(nextHistoryInfo.getEncryptedDataKey()); } else { configHistoryInfoDetail.setUpdatedContent(currentConfigInfo.getContent()); configHistoryInfoDetail.setUpdatedMd5(currentConfigInfo.getMd5()); configHistoryInfoDetail.setUpdatedEncryptedDataKey(currentConfigInfo.getEncryptedDataKey()); } } //delete if (OperationType.DELETE.getValue().equals(configHistoryInfoDetail.getOpType())) { configHistoryInfoDetail.setOriginalMd5(configHistoryInfo.getMd5()); configHistoryInfoDetail.setOriginalContent(configHistoryInfo.getContent()); configHistoryInfoDetail.setOriginalEncryptedDataKey(configHistoryInfo.getEncryptedDataKey()); configHistoryInfoDetail.setOriginalExtInfo(configHistoryInfo.getExtInfo()); } // decrypt content if (StringUtils.isNotBlank(configHistoryInfoDetail.getOriginalContent())) { String originalContent = EncryptionHandler.decryptHandler(dataId, configHistoryInfoDetail.getOriginalEncryptedDataKey(), configHistoryInfoDetail.getOriginalContent()) .getSecond(); configHistoryInfoDetail.setOriginalContent(originalContent); } if (StringUtils.isNotBlank(configHistoryInfoDetail.getUpdatedContent())) { String updatedContent = EncryptionHandler.decryptHandler(dataId, configHistoryInfoDetail.getUpdatedEncryptedDataKey(), configHistoryInfoDetail.getUpdatedContent()) .getSecond(); configHistoryInfoDetail.setUpdatedContent(updatedContent); } return configHistoryInfoDetail; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingConnectionMetricsCollector.java ================================================ /* * * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector; import com.alibaba.nacos.sys.utils.ApplicationUtils; import java.util.stream.Collectors; /** * long polling connection metrics. * * @author shiyiyue */ public class LongPollingConnectionMetricsCollector implements ConnectionMetricsCollector { @Override public String getName() { return "long_polling"; } @Override public int getTotalCount() { return ApplicationUtils.getBean(LongPollingService.class).allSubs.size(); } @Override public int getCountForIp(String ip) { return ApplicationUtils.getBean(LongPollingService.class).allSubs.stream() .filter(a -> a.ip.equalsIgnoreCase(ip)).collect(Collectors.toList()).size(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/LongPollingService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.remote.RpcScheduledExecutor; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.model.SampleResult; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.MD5Util; import com.alibaba.nacos.config.server.utils.RequestUtil; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.connection.request.ConnectionCheckRequest; import com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckResponse; import org.springframework.stereotype.Service; import jakarta.servlet.AsyncContext; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.config.server.utils.LogUtil.MEMORY_LOG; import static com.alibaba.nacos.config.server.utils.LogUtil.PULL_LOG; /** * LongPollingService. * * @author Nacos */ @Service public class LongPollingService { private static final int SAMPLE_PERIOD = 100; private static final int SAMPLE_TIMES = 3; private static final String TRUE_STR = "true"; private Map retainIps = new ConcurrentHashMap<>(); public SampleResult getSubscribleInfo(String dataId, String group, String tenant) { String groupKey = GroupKey.getKeyTenant(dataId, group, tenant); SampleResult sampleResult = new SampleResult(); Map lisentersGroupkeyStatus = new HashMap<>(50); for (ClientLongPolling clientLongPolling : allSubs) { if (clientLongPolling.clientMd5Map.containsKey(groupKey)) { lisentersGroupkeyStatus.put(clientLongPolling.ip, clientLongPolling.clientMd5Map.get(groupKey).getMd5()); } } sampleResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); return sampleResult; } public SampleResult getSubscribleInfoByIp(String clientIp) { SampleResult sampleResult = new SampleResult(); Map lisenersGroupkeyStatus = new HashMap<>(50); for (ClientLongPolling clientLongPolling : allSubs) { if (clientLongPolling.ip.equals(clientIp)) { // One ip can have multiple listener. for (Map.Entry entry : clientLongPolling.clientMd5Map.entrySet()) { lisenersGroupkeyStatus.put(entry.getKey(), entry.getValue().getMd5()); } } } sampleResult.setLisentersGroupkeyStatus(lisenersGroupkeyStatus); return sampleResult; } /** * Aggregate the sampling IP and monitoring configuration information in the sampling results. There is no problem * for the merging strategy to cover the previous one with the latter. * * @param sampleResults sample Results. * @return Results. */ public SampleResult mergeSampleResult(List sampleResults) { SampleResult mergeResult = new SampleResult(); Map lisentersGroupkeyStatus = new HashMap<>(50); for (SampleResult sampleResult : sampleResults) { Map lisentersGroupkeyStatusTmp = sampleResult.getLisentersGroupkeyStatus(); for (Map.Entry entry : lisentersGroupkeyStatusTmp.entrySet()) { lisentersGroupkeyStatus.put(entry.getKey(), entry.getValue()); } } mergeResult.setLisentersGroupkeyStatus(lisentersGroupkeyStatus); return mergeResult; } public SampleResult getCollectSubscribleInfo(String dataId, String group, String tenant) { List sampleResultLst = new ArrayList<>(50); for (int i = 0; i < SAMPLE_TIMES; i++) { SampleResult sampleTmp = getSubscribleInfo(dataId, group, tenant); if (sampleTmp != null) { sampleResultLst.add(sampleTmp); } if (i < SAMPLE_TIMES - 1) { try { Thread.sleep(SAMPLE_PERIOD); } catch (InterruptedException e) { LogUtil.CLIENT_LOG.error("sleep wrong", e); } } } return mergeSampleResult(sampleResultLst); } public SampleResult getCollectSubscribleInfoByIp(String ip) { SampleResult sampleResult = new SampleResult(); sampleResult.setLisentersGroupkeyStatus(new HashMap<>(50)); for (int i = 0; i < SAMPLE_TIMES; i++) { SampleResult sampleTmp = getSubscribleInfoByIp(ip); if (sampleTmp != null) { if (sampleTmp.getLisentersGroupkeyStatus() != null && !sampleResult.getLisentersGroupkeyStatus() .equals(sampleTmp.getLisentersGroupkeyStatus())) { sampleResult.getLisentersGroupkeyStatus().putAll(sampleTmp.getLisentersGroupkeyStatus()); } } if (i < SAMPLE_TIMES - 1) { try { Thread.sleep(SAMPLE_PERIOD); } catch (InterruptedException e) { LogUtil.CLIENT_LOG.error("sleep wrong", e); } } } return sampleResult; } /** * Add LongPollingClient. * * @param req HttpServletRequest. * @param rsp HttpServletResponse. * @param clientMd5Map clientMd5Map. * @param probeRequestSize probeRequestSize. */ public void addLongPollingClient(HttpServletRequest req, HttpServletResponse rsp, Map clientMd5Map, int probeRequestSize) { String noHangUpFlag = req.getHeader(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER); long start = System.currentTimeMillis(); Map changedGroups = MD5Util.compareMd5(req, rsp, clientMd5Map); if (changedGroups.size() > 0) { generateResponse(req, rsp, changedGroups); LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "instant", RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize, changedGroups.size()); return; } else if (noHangUpFlag != null && noHangUpFlag.equalsIgnoreCase(TRUE_STR)) { LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}", System.currentTimeMillis() - start, "nohangup", RequestUtil.getRemoteIp(req), "polling", clientMd5Map.size(), probeRequestSize, changedGroups.size()); return; } // Must be called by http thread, or send response. final AsyncContext asyncContext = req.startAsync(); // AsyncContext.setTimeout() is incorrect, Control by oneself asyncContext.setTimeout(0L); String ip = RequestUtil.getRemoteIp(req); ConnectionCheckResponse connectionCheckResponse = checkLimit(req); if (!connectionCheckResponse.isSuccess()) { RpcScheduledExecutor.CONTROL_SCHEDULER.schedule( () -> generate503Response(asyncContext, rsp, connectionCheckResponse.getMessage()), 1000L + ThreadLocalRandom.current().nextInt(2000), TimeUnit.MILLISECONDS); return; } String appName = req.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); String tag = req.getHeader("Vipserver-Tag"); int delayTime = SwitchService.getSwitchInteger(SwitchService.FIXED_DELAY_TIME, 500); int minLongPoolingTimeout = SwitchService.getSwitchInteger("MIN_LONG_POOLING_TIMEOUT", 10000); // Add delay time for LoadBalance, and one response is returned 500 ms in advance to avoid client timeout. String requestLongPollingTimeOut = req.getHeader(LongPollingService.LONG_POLLING_HEADER); long timeout = Math.max(minLongPoolingTimeout, Long.parseLong(requestLongPollingTimeOut) - delayTime); ConfigExecutor.executeLongPolling( new ClientLongPolling(asyncContext, clientMd5Map, ip, probeRequestSize, timeout, appName, tag)); } private ConnectionCheckResponse checkLimit(HttpServletRequest httpServletRequest) { String ip = RequestUtil.getRemoteIp(httpServletRequest); String appName = httpServletRequest.getHeader(RequestUtil.CLIENT_APPNAME_HEADER); ConnectionCheckRequest connectionCheckRequest = new ConnectionCheckRequest(ip, appName, "LongPolling"); ConnectionCheckResponse checkResponse = ControlManagerCenter.getInstance().getConnectionControlManager() .check(connectionCheckRequest); return checkResponse; } public static boolean isSupportLongPolling(HttpServletRequest req) { return null != req.getHeader(LONG_POLLING_HEADER); } public LongPollingService() { allSubs = new ConcurrentLinkedQueue<>(); ConfigExecutor.scheduleLongPolling(new StatTask(), 0L, 10L, TimeUnit.SECONDS); // Register LocalDataChangeEvent to NotifyCenter. NotifyCenter.registerToPublisher(LocalDataChangeEvent.class, NotifyCenter.ringBufferSize); // Register A Subscriber to subscribe LocalDataChangeEvent. NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { if (event instanceof LocalDataChangeEvent) { LocalDataChangeEvent evt = (LocalDataChangeEvent) event; ConfigExecutor.executeLongPolling(new DataChangeTask(evt.groupKey)); } } @Override public Class subscribeType() { return LocalDataChangeEvent.class; } }); } public static final String LONG_POLLING_HEADER = "Long-Pulling-Timeout"; public static final String LONG_POLLING_NO_HANG_UP_HEADER = "Long-Pulling-Timeout-No-Hangup"; /** * ClientLongPolling subscribers. */ final Queue allSubs; class DataChangeTask implements Runnable { @Override public void run() { try { for (Iterator iter = allSubs.iterator(); iter.hasNext(); ) { ClientLongPolling clientSub = iter.next(); if (clientSub.clientMd5Map.containsKey(groupKey)) { getRetainIps().put(clientSub.ip, System.currentTimeMillis()); iter.remove(); // Delete subscribers' relationships. LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}|{}", (System.currentTimeMillis() - changeTime), "in-advance", RequestUtil.getRemoteIp((HttpServletRequest) clientSub.asyncContext.getRequest()), "polling", clientSub.clientMd5Map.size(), clientSub.probeRequestSize, groupKey); clientSub.sendResponse(Collections.singletonMap(groupKey, clientSub.clientMd5Map.get(groupKey))); } } } catch (Throwable t) { LogUtil.DEFAULT_LOG.error("data change error: {}", ExceptionUtil.getStackTrace(t)); } } DataChangeTask(String groupKey) { this.groupKey = groupKey; } final String groupKey; final long changeTime = System.currentTimeMillis(); } class StatTask implements Runnable { @Override public void run() { MEMORY_LOG.info("[long-pulling] client count " + allSubs.size()); MetricsMonitor.getLongPollingMonitor().set(allSubs.size()); } } public class ClientLongPolling implements Runnable { @Override public void run() { asyncTimeoutFuture = ConfigExecutor.scheduleLongPolling(() -> { try { getRetainIps().put(ClientLongPolling.this.ip, System.currentTimeMillis()); // Delete subscriber's relations. boolean removeFlag = allSubs.remove(ClientLongPolling.this); if (removeFlag) { LogUtil.CLIENT_LOG.info("{}|{}|{}|{}|{}|{}", (System.currentTimeMillis() - createTime), "timeout", RequestUtil.getRemoteIp((HttpServletRequest) asyncContext.getRequest()), "polling", clientMd5Map.size(), probeRequestSize); sendResponse(null); } else { LogUtil.DEFAULT_LOG.warn("client subsciber's relations delete fail."); } } catch (Throwable t) { LogUtil.DEFAULT_LOG.error("long polling error:" + t.getMessage(), t.getCause()); } }, timeoutTime, TimeUnit.MILLISECONDS); allSubs.add(this); } void sendResponse(Map changedGroups) { // Cancel time out task. if (null != asyncTimeoutFuture) { asyncTimeoutFuture.cancel(false); } generateResponse(changedGroups); } void generateResponse(Map changedGroups) { if (null == changedGroups) { // Tell web container to send http response. asyncContext.complete(); return; } HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse(); try { final String respString = MD5Util.compareMd5ResultString(changedGroups); // Disable cache. response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-cache,no-store"); response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println(respString); asyncContext.complete(); } catch (Exception ex) { PULL_LOG.error(ex.toString(), ex); asyncContext.complete(); } } ClientLongPolling(AsyncContext ac, Map clientMd5Map, String ip, int probeRequestSize, long timeoutTime, String appName, String tag) { this.asyncContext = ac; this.clientMd5Map = clientMd5Map; this.probeRequestSize = probeRequestSize; this.createTime = System.currentTimeMillis(); this.ip = ip; this.timeoutTime = timeoutTime; this.appName = appName; this.tag = tag; } final AsyncContext asyncContext; final Map clientMd5Map; final long createTime; final String ip; final String appName; final String tag; final int probeRequestSize; final long timeoutTime; Future asyncTimeoutFuture; @Override public String toString() { return "ClientLongPolling{" + "clientMd5Map=" + clientMd5Map + ", createTime=" + createTime + ", ip='" + ip + '\'' + ", appName='" + appName + '\'' + ", tag='" + tag + '\'' + ", probeRequestSize=" + probeRequestSize + ", timeoutTime=" + timeoutTime + '}'; } } void generateResponse(HttpServletRequest request, HttpServletResponse response, Map changedGroups) { if (null == changedGroups) { return; } try { final String respString = MD5Util.compareMd5ResultString(changedGroups); // Disable cache. response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-cache,no-store"); response.setStatus(HttpServletResponse.SC_OK); response.getWriter().println(respString); } catch (Exception ex) { PULL_LOG.error(ex.toString(), ex); } } void generate503Response(AsyncContext asyncContext, HttpServletResponse response, String message) { try { // Disable cache. response.setHeader("Pragma", "no-cache"); response.setDateHeader("Expires", 0); response.setHeader("Cache-Control", "no-cache,no-store"); response.setStatus(HttpServletResponse.SC_SERVICE_UNAVAILABLE); response.getWriter().println(message); asyncContext.complete(); } catch (Exception ex) { PULL_LOG.error(ex.toString(), ex); } } public Map getRetainIps() { return retainIps; } public int getSubscriberCount() { return allSubs.size(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/NamespaceConfigInfoService.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.config.server.constant.PropertiesConstant; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.core.namespace.injector.AbstractNamespaceDetailInjector; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.stereotype.Service; /** * Namespace detail for config info. * * @author xiweng.yy */ @Service public class NamespaceConfigInfoService extends AbstractNamespaceDetailInjector { private final ConfigInfoPersistService configInfoPersistService; public NamespaceConfigInfoService(ConfigInfoPersistService configInfoPersistService) { this.configInfoPersistService = configInfoPersistService; } @Override public void injectDetail(Namespace namespace) { if (EnvUtil.getProperty(PropertiesConstant.DEFAULT_TENANT_QUOTA, Integer.class) != null) { namespace.setQuota(EnvUtil.getProperty(PropertiesConstant.DEFAULT_TENANT_QUOTA, Integer.class)); } // set config count. int configCount = configInfoPersistService.configInfoCount(namespace.getNamespace()); namespace.setConfigCount(configCount); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/SwitchService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.stereotype.Service; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG; /** * SwitchService. * * @author Nacos */ @Service public class SwitchService { public static final String SWITCH_META_DATA_ID = "com.alibaba.nacos.meta.switch"; public static final String FIXED_DELAY_TIME = "fixedDelayTime"; private static volatile Map switches = new HashMap<>(); public static int getSwitchInteger(String key, int defaultValue) { int rtn; try { String status = switches.get(key); rtn = status != null ? Integer.parseInt(status) : defaultValue; } catch (Exception e) { rtn = defaultValue; LogUtil.FATAL_LOG.error("corrupt switch value {}={}", key, switches.get(key)); } return rtn; } /** * Load config. * * @param config config content string value. */ public static void load(String config) { if (StringUtils.isBlank(config)) { FATAL_LOG.warn("switch config is blank."); return; } FATAL_LOG.warn("[switch-config] {}", config); Map map = new HashMap<>(30); try (StringReader reader = new StringReader(config)) { for (String line : IoUtils.readLines(reader)) { if (!StringUtils.isBlank(line) && !line.startsWith("#")) { String[] array = line.split("="); if (array.length != 2) { LogUtil.FATAL_LOG.error("corrupt switch record {}", line); continue; } String key = array[0].trim(); String value = array[1].trim(); map.put(key, value); } switches = map; FATAL_LOG.warn("[reload-switches] {}", getSwitches()); } } catch (IOException e) { LogUtil.FATAL_LOG.warn("[reload-switches] error! {}", config); } } public static String getSwitches() { StringBuilder sb = new StringBuilder(); String split = ""; for (Map.Entry entry : switches.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(split); sb.append(key); sb.append('='); sb.append(value); split = "; "; } return sb.toString(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/capacity/CapacityService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.config.server.constant.CounterMode; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.capacity.GroupCapacity; import com.alibaba.nacos.config.server.model.capacity.NamespaceCapacity; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.dao.DuplicateKeyException; import org.springframework.stereotype.Service; import org.springframework.util.StopWatch; import javax.annotation.PostConstruct; import java.sql.Timestamp; import java.util.List; import java.util.concurrent.TimeUnit; /** * Capacity service. * * @author hexu.hxy * @date 2018/03/05 */ @Service public class CapacityService { private static final Logger LOGGER = LoggerFactory.getLogger(CapacityService.class); private static final Integer ZERO = 0; private static final int INIT_PAGE_SIZE = 500; @Autowired private GroupCapacityPersistService groupCapacityPersistService; @Autowired private TenantCapacityPersistService tenantCapacityPersistService; @Autowired private ConfigInfoPersistService configInfoPersistService; /** * Init. */ @PostConstruct public void init() { // All servers have jobs that modify usage, idempotent. ConfigExecutor.scheduleCorrectUsageTask(() -> { LOGGER.info("[capacityManagement] start correct usage"); StopWatch watch = new StopWatch(); watch.start(); correctUsage(); watch.stop(); LOGGER.info("[capacityManagement] end correct usage, cost: {}s", watch.getTotalTimeSeconds()); }, PropertyUtil.getCorrectUsageDelay(), PropertyUtil.getCorrectUsageDelay(), TimeUnit.SECONDS); } public void correctUsage() { correctGroupUsage(); correctTenantUsage(); } /** * Correct the usage of group capacity. */ private void correctGroupUsage() { long lastId = 0; int pageSize = 100; while (true) { List groupCapacityList = groupCapacityPersistService .getCapacityList4CorrectUsage(lastId, pageSize); if (groupCapacityList.isEmpty()) { break; } lastId = groupCapacityList.get(groupCapacityList.size() - 1).getId(); for (GroupCapacity groupCapacity : groupCapacityList) { String group = groupCapacity.getGroupName(); groupCapacityPersistService.correctUsage(group, TimeUtils.getCurrentTime()); } try { Thread.sleep(100); } catch (InterruptedException e) { // ignore // set the interrupted flag Thread.currentThread().interrupt(); } } } public void correctGroupUsage(String group) { groupCapacityPersistService.correctUsage(group, TimeUtils.getCurrentTime()); } public void correctTenantUsage(String tenant) { tenantCapacityPersistService.correctUsage(tenant, TimeUtils.getCurrentTime()); } /** * Correct the usage of group capacity. */ private void correctTenantUsage() { long lastId = 0; int pageSize = 100; while (true) { List tenantCapacityList = tenantCapacityPersistService .getCapacityList4CorrectUsage(lastId, pageSize); if (tenantCapacityList.isEmpty()) { break; } lastId = tenantCapacityList.get(tenantCapacityList.size() - 1).getId(); try { Thread.sleep(100); } catch (InterruptedException ignored) { } for (NamespaceCapacity tenantCapacity : tenantCapacityList) { String tenant = tenantCapacity.getNamespaceId(); tenantCapacityPersistService.correctUsage(tenant, TimeUtils.getCurrentTime()); } } } public void initAllCapacity() { initAllCapacity(false); initAllCapacity(true); } private void initAllCapacity(boolean isTenant) { int page = 1; while (true) { List list; if (isTenant) { list = configInfoPersistService.getTenantIdList(page, INIT_PAGE_SIZE); } else { list = configInfoPersistService.getGroupIdList(page, INIT_PAGE_SIZE); } for (String targetId : list) { if (isTenant) { insertTenantCapacity(targetId); autoExpansion(null, targetId); } else { insertGroupCapacity(targetId); autoExpansion(targetId, null); } } if (list.size() < INIT_PAGE_SIZE) { break; } try { Thread.sleep(100); } catch (InterruptedException ignored) { } ++page; } } /** * To Cluster. 1.If the capacity information does not exist, initialize the capacity information. 2.Update capacity * usage, plus or minus one. * * @param counterMode increase or decrease mode. * @param ignoreQuotaLimit ignoreQuotaLimit flag. * @return the result of update cluster usage. */ public boolean insertAndUpdateClusterUsage(CounterMode counterMode, boolean ignoreQuotaLimit) { Capacity capacity = groupCapacityPersistService.getClusterCapacity(); if (capacity == null) { insertGroupCapacity(GroupCapacityPersistService.CLUSTER); } return updateGroupUsage(counterMode, GroupCapacityPersistService.CLUSTER, PropertyUtil.getDefaultClusterQuota(), ignoreQuotaLimit); } public boolean updateClusterUsage(CounterMode counterMode) { return updateGroupUsage(counterMode, GroupCapacityPersistService.CLUSTER, PropertyUtil.getDefaultClusterQuota(), false); } /** * It is used for counting when the limit check function of capacity management is turned off. 1.If the capacity * information does not exist, initialize the capacity information. 2.Update capacity usage, plus or minus one. * * @param counterMode increase or decrease mode. * @param group tenant string value. * @param ignoreQuotaLimit ignoreQuotaLimit flag. * @return operate successfully or not. */ public boolean insertAndUpdateGroupUsage(CounterMode counterMode, String group, boolean ignoreQuotaLimit) { GroupCapacity groupCapacity = getGroupCapacity(group); if (groupCapacity == null) { initGroupCapacity(group, null, null, null, null); } return updateGroupUsage(counterMode, group, PropertyUtil.getDefaultGroupQuota(), ignoreQuotaLimit); } public boolean updateGroupUsage(CounterMode counterMode, String group) { return updateGroupUsage(counterMode, group, PropertyUtil.getDefaultGroupQuota(), false); } private boolean updateGroupUsage(CounterMode counterMode, String group, int defaultQuota, boolean ignoreQuotaLimit) { final Timestamp now = TimeUtils.getCurrentTime(); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setGroupName(group); groupCapacity.setQuota(defaultQuota); groupCapacity.setGmtModified(now); if (CounterMode.INCREMENT == counterMode) { if (ignoreQuotaLimit) { return groupCapacityPersistService.incrementUsage(groupCapacity); } // First update the quota according to the default value. In most cases, it is the default value. // The quota field in the default value table is 0 return groupCapacityPersistService.incrementUsageWithDefaultQuotaLimit(groupCapacity) || groupCapacityPersistService.incrementUsageWithQuotaLimit(groupCapacity); } return groupCapacityPersistService.decrementUsage(groupCapacity); } public GroupCapacity getGroupCapacity(String group) { return groupCapacityPersistService.getGroupCapacity(group); } /** * Initialize the capacity information of the group. If the quota is reached, the capacity will be automatically * expanded to reduce the operation and maintenance cost. * * @param group group string value. * @return init result. */ public boolean initGroupCapacity(String group) { return initGroupCapacity(group, null, null, null, null); } /** * Initialize the capacity information of the group. If the quota is reached, the capacity will be automatically * expanded to reduce the operation and maintenance cost. * * @param group group string value. * @param quota quota int value. * @param maxSize maxSize int value. * @param maxAggrCount maxAggrCount int value. * @param maxAggrSize maxAggrSize int value. * @return init result. */ private boolean initGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { boolean insertSuccess = insertGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); if (quota == null) { autoExpansion(group, null); } return insertSuccess; } /** * Expand capacity automatically. * * @param group group string value. * @param tenant tenant string value. */ private void autoExpansion(String group, String tenant) { Capacity capacity = getCapacity(group, tenant); int defaultQuota = getDefaultQuota(tenant != null); Integer usage = capacity.getUsage(); if (usage < defaultQuota) { return; } // Initialize the capacity information of the group. If the quota is reached, // the capacity will be automatically expanded to reduce the operation and maintenance cost. int initialExpansionPercent = PropertyUtil.getInitialExpansionPercent(); if (initialExpansionPercent > 0) { int finalQuota = (int) (usage + defaultQuota * (1.0 * initialExpansionPercent / 100)); if (tenant != null) { tenantCapacityPersistService.updateQuota(tenant, finalQuota); LogUtil.DEFAULT_LOG.warn("[capacityManagement] The usage({}) already reach the upper limit({}) when init the tenant({}), " + "automatic upgrade to ({})", usage, defaultQuota, tenant, finalQuota); } else { groupCapacityPersistService.updateQuota(group, finalQuota); LogUtil.DEFAULT_LOG.warn("[capacityManagement] The usage({}) already reach the upper limit({}) when init the group({}), " + "automatic upgrade to ({})", usage, defaultQuota, group, finalQuota); } } } private int getDefaultQuota(boolean isTenant) { if (isTenant) { return PropertyUtil.getDefaultTenantQuota(); } return PropertyUtil.getDefaultGroupQuota(); } public Capacity getCapacity(String group, String tenant) { if (tenant != null) { return getTenantCapacity(tenant); } return getGroupCapacity(group); } public Capacity getCapacityWithDefault(String group, String tenant) { Capacity capacity; boolean isTenant = StringUtils.isNotBlank(tenant); if (isTenant) { capacity = getTenantCapacity(tenant); } else { capacity = getGroupCapacity(group); } if (capacity == null) { return null; } Integer quota = capacity.getQuota(); if (quota == 0) { if (isTenant) { capacity.setQuota(PropertyUtil.getDefaultTenantQuota()); } else { if (GroupCapacityPersistService.CLUSTER.equals(group)) { capacity.setQuota(PropertyUtil.getDefaultClusterQuota()); } else { capacity.setQuota(PropertyUtil.getDefaultGroupQuota()); } } } Integer maxSize = capacity.getMaxSize(); if (maxSize == 0) { capacity.setMaxSize(PropertyUtil.getDefaultMaxSize()); } Integer maxAggrCount = capacity.getMaxAggrCount(); if (maxAggrCount == 0) { capacity.setMaxAggrCount(PropertyUtil.getDefaultMaxAggrCount()); } Integer maxAggrSize = capacity.getMaxAggrSize(); if (maxAggrSize == 0) { capacity.setMaxAggrSize(PropertyUtil.getDefaultMaxAggrSize()); } return capacity; } /** * Init capacity. * * @param group group string value. * @param tenant tenant string value. * @return init result. */ public boolean initCapacity(String group, String tenant) { if (StringUtils.isNotBlank(tenant)) { return initTenantCapacity(tenant); } if (GroupCapacityPersistService.CLUSTER.equals(group)) { return insertGroupCapacity(GroupCapacityPersistService.CLUSTER); } // Group can expand capacity automatically. return initGroupCapacity(group); } private boolean insertGroupCapacity(String group) { return insertGroupCapacity(group, null, null, null, null); } private boolean insertGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { try { final Timestamp now = TimeUtils.getCurrentTime(); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setGroupName(group); // When adding a new quota, quota = 0 means that the quota is the default value. // In order to update the default quota, only the Nacos configuration needs to be modified, // and most of the data in the table need not be updated. groupCapacity.setQuota(quota == null ? ZERO : quota); // When adding new data, maxsize = 0 means that the size is the default value. // In order to update the default size, you only need to modify the Nacos configuration without updating most of the data in the table. groupCapacity.setMaxSize(maxSize == null ? ZERO : maxSize); groupCapacity.setMaxAggrCount(maxAggrCount == null ? ZERO : maxAggrCount); groupCapacity.setMaxAggrSize(maxAggrSize == null ? ZERO : maxAggrSize); groupCapacity.setGmtCreate(now); groupCapacity.setGmtModified(now); return groupCapacityPersistService.insertGroupCapacity(groupCapacity); } catch (DuplicateKeyException e) { // this exception will meet when concurrent insert,ignore it LogUtil.DEFAULT_LOG.warn("group: {}, message: {}", group, e.getMessage()); } return false; } /** * It is used for counting when the limit check function of capacity management is turned off. 1.If the capacity * information does not exist, initialize the capacity information. 2.Update capacity usage, plus or minus one. * * @param counterMode increase or decrease mode. * @param tenant tenant string value. * @param ignoreQuotaLimit ignoreQuotaLimit flag. * @return operate successfully or not. */ public boolean insertAndUpdateTenantUsage(CounterMode counterMode, String tenant, boolean ignoreQuotaLimit) { NamespaceCapacity tenantCapacity = getTenantCapacity(tenant); if (tenantCapacity == null) { // Init capacity information. initTenantCapacity(tenant); } return updateTenantUsage(counterMode, tenant, ignoreQuotaLimit); } private boolean updateTenantUsage(CounterMode counterMode, String tenant, boolean ignoreQuotaLimit) { final Timestamp now = TimeUtils.getCurrentTime(); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId(tenant); tenantCapacity.setQuota(PropertyUtil.getDefaultTenantQuota()); tenantCapacity.setGmtModified(now); if (CounterMode.INCREMENT == counterMode) { if (ignoreQuotaLimit) { return tenantCapacityPersistService.incrementUsage(tenantCapacity); } // First update the quota according to the default value. In most cases, it is the default value. // The quota field in the default value table is 0. return tenantCapacityPersistService.incrementUsageWithDefaultQuotaLimit(tenantCapacity) || tenantCapacityPersistService.incrementUsageWithQuotaLimit(tenantCapacity); } return tenantCapacityPersistService.decrementUsage(tenantCapacity); } public boolean updateTenantUsage(CounterMode counterMode, String tenant) { return updateTenantUsage(counterMode, tenant, false); } /** * Initialize the capacity information of the tenant. If the quota is reached, the capacity will be automatically * expanded to reduce the operation and maintenance cos. * * @param tenant tenant string value. * @return init result. */ public boolean initTenantCapacity(String tenant) { return initTenantCapacity(tenant, null, null, null, null); } /** * Initialize the capacity information of the tenant. If the quota is reached, the capacity will be automatically * expanded to reduce the operation and maintenance cost * * @param tenant tenant string value. * @param quota quota int value. * @param maxSize maxSize int value. * @param maxAggrCount maxAggrCount int value. * @param maxAggrSize maxAggrSize int value. * @return */ public boolean initTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { boolean insertSuccess = insertTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize); if (quota != null) { return insertSuccess; } autoExpansion(null, tenant); return insertSuccess; } private boolean insertTenantCapacity(String tenant) { return insertTenantCapacity(tenant, null, null, null, null); } private boolean insertTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { try { final Timestamp now = TimeUtils.getCurrentTime(); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId(tenant); // When adding a new quota, quota = 0 means that the quota is the default value. // In order to update the default quota, only the Nacos configuration needs to be modified, // and most of the data in the table need not be updated. tenantCapacity.setQuota(quota == null ? ZERO : quota); // When adding new data, maxsize = 0 means that the size is the default value. // In order to update the default size, you only need to modify the Nacos configuration without updating most of the data in the table. tenantCapacity.setMaxSize(maxSize == null ? ZERO : maxSize); tenantCapacity.setMaxAggrCount(maxAggrCount == null ? ZERO : maxAggrCount); tenantCapacity.setMaxAggrSize(maxAggrSize == null ? ZERO : maxAggrSize); tenantCapacity.setGmtCreate(now); tenantCapacity.setGmtModified(now); return tenantCapacityPersistService.insertTenantCapacity(tenantCapacity); } catch (DuplicateKeyException e) { // this exception will meet when concurrent insert,ignore it LogUtil.DEFAULT_LOG.warn("tenant: {}, message: {}", tenant, e.getMessage()); } return false; } public NamespaceCapacity getTenantCapacity(String tenant) { return tenantCapacityPersistService.getTenantCapacity(tenant); } /** * Support for API interface, Tenant: initialize if the record does not exist, and update the capacity quota or * content size directly if it exists. * * @param group group string value. * @param tenant tenant string value. * @param quota quota int value. * @param maxSize maxSize int value. * @param maxAggrCount maxAggrCount int value. * @param maxAggrSize maxAggrSize int value. * @return operate successfully or not. */ public boolean insertOrUpdateCapacity(String group, String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { if (StringUtils.isNotBlank(tenant)) { Capacity capacity = tenantCapacityPersistService.getTenantCapacity(tenant); if (capacity == null) { return initTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize); } return tenantCapacityPersistService.updateTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize); } Capacity capacity = groupCapacityPersistService.getGroupCapacity(group); if (capacity == null) { return initGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); } return groupCapacityPersistService.updateGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.capacity.GroupCapacity; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; import com.alibaba.nacos.plugin.datasource.mapper.GroupCapacityMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG; /** * Group Capacity Service. * * @author hexu.hxy * @date 2018/03/05 */ @Service public class GroupCapacityPersistService { static final String CLUSTER = ""; private static final GroupCapacityRowMapper GROUP_CAPACITY_ROW_MAPPER = new GroupCapacityRowMapper(); private JdbcTemplate jdbcTemplate; private DataSourceService dataSourceService; private MapperManager mapperManager; /** * init. */ @PostConstruct public void init() { this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jdbcTemplate = dataSourceService.getJdbcTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); } static final class GroupCapacityRowMapper implements RowMapper { @Override public GroupCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setId(rs.getLong("id")); groupCapacity.setQuota(rs.getInt("quota")); groupCapacity.setUsage(rs.getInt("usage")); groupCapacity.setMaxSize(rs.getInt("max_size")); groupCapacity.setMaxAggrCount(rs.getInt("max_aggr_count")); groupCapacity.setMaxAggrSize(rs.getInt("max_aggr_size")); groupCapacity.setGroupName(rs.getString("group_id")); return groupCapacity; } } public GroupCapacity getGroupCapacity(String groupId) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.GROUP_ID, groupId); MapperResult mapperResult = groupCapacityMapper.select(context); List list = jdbcTemplate.query(mapperResult.getSql(), GROUP_CAPACITY_ROW_MAPPER, mapperResult.getParamList().toArray()); if (list.isEmpty()) { return null; } return list.get(0); } public Capacity getClusterCapacity() { return getGroupCapacity(CLUSTER); } /** * Insert GroupCapacity into db. * * @param capacity capacity object instance. * @return operate result. */ public boolean insertGroupCapacity(final GroupCapacity capacity) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperResult mapperResult; MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GROUP_ID, capacity.getGroupName()); context.putUpdateParameter(FieldConstant.QUOTA, capacity.getQuota()); context.putUpdateParameter(FieldConstant.MAX_SIZE, capacity.getMaxSize()); context.putUpdateParameter(FieldConstant.MAX_AGGR_SIZE, capacity.getMaxAggrSize()); context.putUpdateParameter(FieldConstant.MAX_AGGR_COUNT, capacity.getMaxAggrCount()); context.putUpdateParameter(FieldConstant.GMT_CREATE, capacity.getGmtCreate()); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, capacity.getGmtModified()); context.putWhereParameter(FieldConstant.GROUP_ID, capacity.getGroupName()); if (CLUSTER.equals(capacity.getGroupName())) { mapperResult = groupCapacityMapper.insertIntoSelect(context); } else { // Note: add "tenant_id = ''" condition. mapperResult = groupCapacityMapper.insertIntoSelectByWhere(context); } return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) > 0; } public int getClusterUsage() { Capacity clusterCapacity = getClusterCapacity(); if (clusterCapacity != null) { return clusterCapacity.getUsage(); } ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); String sql = configInfoMapper.count(null); Integer result = jdbcTemplate.queryForObject(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); } return result.intValue(); } /** * Increment UsageWithDefaultQuotaLimit. * * @param groupCapacity groupCapacity object instance. * @return operate result. */ public boolean incrementUsageWithDefaultQuotaLimit(GroupCapacity groupCapacity) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, groupCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.GROUP_ID, groupCapacity.getGroupName()); context.putWhereParameter(FieldConstant.USAGE, groupCapacity.getQuota()); MapperResult mapperResult = groupCapacityMapper.incrementUsageByWhereQuotaEqualZero(context); try { int affectRow = jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()); return affectRow == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Increment UsageWithQuotaLimit. * * @param groupCapacity groupCapacity object instance. * @return operate result. */ public boolean incrementUsageWithQuotaLimit(GroupCapacity groupCapacity) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, groupCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.GROUP_ID, groupCapacity.getGroupName()); MapperResult mapperResult = groupCapacityMapper.incrementUsageByWhereQuotaNotEqualZero(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Increment Usage. * * @param groupCapacity groupCapacity object instance. * @return operate result. */ public boolean incrementUsage(GroupCapacity groupCapacity) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, groupCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.GROUP_ID, groupCapacity.getGroupName()); MapperResult mapperResult = groupCapacityMapper.incrementUsageByWhere(context); try { int affectRow = jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()); return affectRow == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Decrement Usage. * * @param groupCapacity groupCapacity object instance. * @return operate result. */ public boolean decrementUsage(GroupCapacity groupCapacity) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, groupCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.GROUP_ID, groupCapacity.getGroupName()); MapperResult mapperResult = groupCapacityMapper.decrementUsageByWhere(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Update GroupCapacity. * * @param group group string value. * @param quota quota int value. * @param maxSize maxSize int value. * @param maxAggrCount maxAggrCount int value. * @param maxAggrSize maxAggrSize int value. * @return operate result. */ public boolean updateGroupCapacity(String group, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { List argList = CollectionUtils.list(); List columnList = CollectionUtils.list(); if (quota != null) { columnList.add("quota"); argList.add(quota); } if (maxSize != null) { columnList.add("max_size"); argList.add(maxSize); } if (maxAggrCount != null) { columnList.add("max_aggr_count"); argList.add(maxAggrCount); } if (maxAggrSize != null) { columnList.add("max_aggr_size"); argList.add(maxAggrSize); } columnList.add("gmt_modified"); argList.add(TimeUtils.getCurrentTime()); List whereList = CollectionUtils.list(); whereList.add("group_id"); argList.add(group); GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); String sql = groupCapacityMapper.update(columnList, whereList); try { return jdbcTemplate.update(sql, argList.toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } public boolean updateQuota(String group, Integer quota) { return updateGroupCapacity(group, quota, null, null, null); } public boolean updateMaxSize(String group, Integer maxSize) { return updateGroupCapacity(group, null, maxSize, null, null); } /** * Correct Usage. * * @param group group string value. * @param gmtModified gmtModified. * @return operate result. */ public boolean correctUsage(String group, Timestamp gmtModified) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperResult mapperResult; MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, gmtModified); context.putWhereParameter(FieldConstant.GROUP_ID, group); if (CLUSTER.equals(group)) { mapperResult = groupCapacityMapper.updateUsage(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } else { // Note: add "tenant_id = ''" condition. mapperResult = groupCapacityMapper.updateUsageByWhere(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } } /** * Get group capacity list, noly has id and groupId value. * * @param lastId lastId long value. * @param pageSize pageSize long value. * @return GroupCapacity list. */ public List getCapacityList4CorrectUsage(long lastId, int pageSize) { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, lastId); context.setPageSize(pageSize); MapperResult mapperResult = groupCapacityMapper.selectGroupInfoBySize(context); try { return jdbcTemplate.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), (rs, rowNum) -> { GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setId(rs.getLong("id")); groupCapacity.setGroupName(rs.getString("group_id")); return groupCapacity; }); } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Delete GroupCapacity. * * @param group group string value. * @return operate result. */ public boolean deleteGroupCapacity(final String group) { try { GroupCapacityMapper groupCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.GROUP_CAPACITY); PreparedStatementCreator preparedStatementCreator = connection -> { PreparedStatement ps = connection.prepareStatement( groupCapacityMapper.delete(Collections.singletonList("group_id"))); ps.setString(1, group); return ps; }; return jdbcTemplate.update(preparedStatementCreator) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.capacity.NamespaceCapacity; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.TenantCapacityMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG; /** * Tenant Capacity Service. * * @author hexu.hxy * @date 2018/03/05 */ @Service public class TenantCapacityPersistService { private static final TenantCapacityRowMapper TENANT_CAPACITY_ROW_MAPPER = new TenantCapacityRowMapper(); private JdbcTemplate jdbcTemplate; private DataSourceService dataSourceService; private MapperManager mapperManager; /** * init method. */ @PostConstruct public void init() { this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jdbcTemplate = dataSourceService.getJdbcTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); } static final class TenantCapacityRowMapper implements RowMapper { @Override public NamespaceCapacity mapRow(ResultSet rs, int rowNum) throws SQLException { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setId(rs.getLong("id")); tenantCapacity.setQuota(rs.getInt("quota")); tenantCapacity.setUsage(rs.getInt("usage")); tenantCapacity.setMaxSize(rs.getInt("max_size")); tenantCapacity.setMaxAggrCount(rs.getInt("max_aggr_count")); tenantCapacity.setMaxAggrSize(rs.getInt("max_aggr_size")); tenantCapacity.setNamespaceId(rs.getString("tenant_id")); return tenantCapacity; } } public NamespaceCapacity getTenantCapacity(String tenantId) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.TENANT_ID, tenantId); MapperResult mapperResult = tenantCapacityMapper.select(context); List capacityList = jdbcTemplate.query(mapperResult.getSql(), TENANT_CAPACITY_ROW_MAPPER, mapperResult.getParamList().toArray()); if (capacityList.isEmpty()) { return null; } return capacityList.get(0); } /** * Insert TenantCapacity. * * @param tenantCapacity tenantCapacity object instance. * @return operate result. */ public boolean insertTenantCapacity(final NamespaceCapacity tenantCapacity) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.TENANT_ID, tenantCapacity.getNamespaceId()); context.putUpdateParameter(FieldConstant.QUOTA, tenantCapacity.getQuota()); context.putUpdateParameter(FieldConstant.MAX_SIZE, tenantCapacity.getMaxSize()); context.putUpdateParameter(FieldConstant.MAX_AGGR_SIZE, tenantCapacity.getMaxAggrSize()); context.putUpdateParameter(FieldConstant.MAX_AGGR_COUNT, tenantCapacity.getMaxAggrCount()); context.putUpdateParameter(FieldConstant.GMT_CREATE, tenantCapacity.getGmtCreate()); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, tenantCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantCapacity.getNamespaceId()); final MapperResult mapperResult = tenantCapacityMapper.insertTenantCapacity(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) > 0; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Increment UsageWithDefaultQuotaLimit. * * @param tenantCapacity tenantCapacity object instance. * @return operate result. */ public boolean incrementUsageWithDefaultQuotaLimit(NamespaceCapacity tenantCapacity) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, tenantCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantCapacity.getNamespaceId()); context.putWhereParameter(FieldConstant.USAGE, tenantCapacity.getQuota()); MapperResult mapperResult = tenantCapacityMapper.incrementUsageWithDefaultQuotaLimit(context); try { int affectRow = jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()); return affectRow == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Increment UsageWithQuotaLimit. * * @param tenantCapacity tenantCapacity object instance. * @return operate result. */ public boolean incrementUsageWithQuotaLimit(NamespaceCapacity tenantCapacity) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, tenantCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantCapacity.getNamespaceId()); MapperResult mapperResult = tenantCapacityMapper.incrementUsageWithQuotaLimit(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Increment Usage. * * @param tenantCapacity tenantCapacity object instance. * @return operate result. */ public boolean incrementUsage(NamespaceCapacity tenantCapacity) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, tenantCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantCapacity.getNamespaceId()); MapperResult mapperResult = tenantCapacityMapper.incrementUsage(context); try { int affectRow = jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()); return affectRow == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * DecrementUsage. * * @param tenantCapacity tenantCapacity object instance. * @return operate result. */ public boolean decrementUsage(NamespaceCapacity tenantCapacity) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, tenantCapacity.getGmtModified()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantCapacity.getNamespaceId()); MapperResult mapperResult = tenantCapacityMapper.decrementUsage(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Update TenantCapacity. * * @param tenant tenant string value. * @param quota quota int value. * @param maxSize maxSize int value. * @param maxAggrCount maxAggrCount int value. * @param maxAggrSize maxAggrSize int value. * @return operate result. */ public boolean updateTenantCapacity(String tenant, Integer quota, Integer maxSize, Integer maxAggrCount, Integer maxAggrSize) { List argList = CollectionUtils.list(); List columns = new ArrayList<>(); if (quota != null) { columns.add("quota"); argList.add(quota); } if (maxSize != null) { columns.add("max_size"); argList.add(maxSize); } if (maxAggrCount != null) { columns.add("max_aggr_count"); argList.add(maxAggrCount); } if (maxAggrSize != null) { columns.add("max_aggr_size"); argList.add(maxAggrSize); } columns.add("gmt_modified"); argList.add(TimeUtils.getCurrentTime()); List where = new ArrayList<>(); where.add("tenant_id"); argList.add(tenant); TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); String sql = tenantCapacityMapper.update(columns, where); try { return jdbcTemplate.update(sql, argList.toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } public boolean updateQuota(String tenant, Integer quota) { return updateTenantCapacity(tenant, quota, null, null, null); } /** * Correct Usage. * * @param tenant tenant. * @param gmtModified gmtModified. * @return operate result. */ public boolean correctUsage(String tenant, Timestamp gmtModified) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, gmtModified); context.putWhereParameter(FieldConstant.TENANT_ID, tenant); MapperResult mapperResult = tenantCapacityMapper.correctUsage(context); try { return jdbcTemplate.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Get TenantCapacity List, only including id and tenantId value. * * @param lastId lastId long value. * @param pageSize pageSize int value. * @return TenantCapacity List. */ public List getCapacityList4CorrectUsage(long lastId, int pageSize) { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, lastId); context.putWhereParameter(FieldConstant.LIMIT_SIZE, pageSize); MapperResult mapperResult = tenantCapacityMapper.getCapacityList4CorrectUsage(context); try { return jdbcTemplate.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), (rs, rowNum) -> { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setId(rs.getLong("id")); tenantCapacity.setNamespaceId(rs.getString("tenant_id")); return tenantCapacity; }); } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } /** * Delete TenantCapacity. * * @param tenant tenant string value. * @return operate result. */ public boolean deleteTenantCapacity(final String tenant) { try { TenantCapacityMapper tenantCapacityMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.TENANT_CAPACITY); PreparedStatementCreator preparedStatementCreator = connection -> { PreparedStatement ps = connection.prepareStatement( tenantCapacityMapper.delete(Collections.singletonList("tenant_id"))); ps.setString(1, tenant); return ps; }; return jdbcTemplate.update(preparedStatementCreator) == 1; } catch (CannotGetJdbcConnectionException e) { FATAL_LOG.error("[db-error]", e); throw e; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/DefaultHistoryConfigCleaner.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.sys.utils.ApplicationUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.Calendar; /** * The type Default history config cleaner. * * @author Sunrisea */ public class DefaultHistoryConfigCleaner implements HistoryConfigCleaner { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultHistoryConfigCleaner.class); private HistoryConfigInfoPersistService historyConfigInfoPersistService; @Override public void cleanHistoryConfig() { Timestamp startTime = getBeforeStamp(TimeUtils.getCurrentTime(), 24 * getRetentionDays()); int pageSize = 1000; LOGGER.warn("clearConfigHistory, getBeforeStamp:{}, pageSize:{}", startTime, pageSize); getHistoryConfigInfoPersistService().removeConfigHistory(startTime, pageSize); } private HistoryConfigInfoPersistService getHistoryConfigInfoPersistService() { if (historyConfigInfoPersistService == null) { historyConfigInfoPersistService = ApplicationUtils.getBean(HistoryConfigInfoPersistService.class); } return historyConfigInfoPersistService; } private Timestamp getBeforeStamp(Timestamp date, int step) { Calendar cal = Calendar.getInstance(); cal.setTime(date); cal.add(Calendar.HOUR_OF_DAY, -step); SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return Timestamp.valueOf(format.format(cal.getTime())); } private int getRetentionDays() { return PropertyUtil.getConfigRententionDays(); } @Override public String getName() { return "nacos"; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpChangeConfigWorker.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.PropertyUtil; import java.sql.Timestamp; import java.util.List; import java.util.concurrent.TimeUnit; /** * Dump change processor. * * @author Nacos * @date 2020/7/5 12:19 PM */ public class DumpChangeConfigWorker implements Runnable { private ConfigInfoPersistService configInfoPersistService; private HistoryConfigInfoPersistService historyConfigInfoPersistService; private ConfigMigrateService configMigrateService; Timestamp startTime; public DumpChangeConfigWorker(ConfigInfoPersistService configInfoPersistService, HistoryConfigInfoPersistService historyConfigInfoPersistService, ConfigMigrateService configMigrateService, Timestamp startTime) { this.configInfoPersistService = configInfoPersistService; this.historyConfigInfoPersistService = historyConfigInfoPersistService; this.configMigrateService = configMigrateService; this.startTime = startTime; } int pageSize = 100; public void setPageSize(int pageSize) { this.pageSize = pageSize; } /** * do check change. */ @Override public void run() { try { if (!PropertyUtil.isDumpChangeOn()) { LogUtil.DEFAULT_LOG.info("DumpChange task is not open"); return; } Timestamp currentTime = new Timestamp(System.currentTimeMillis()); LogUtil.DEFAULT_LOG.info("DumpChange start ,from time {},current time {}", startTime, currentTime); LogUtil.DEFAULT_LOG.info("Start to check delete configs from time {}", startTime); long startDeletedConfigTime = System.currentTimeMillis(); LogUtil.DEFAULT_LOG.info("Check delete configs from time {}", startTime); long deleteCursorId = 0L; while (true) { List configDeleted = historyConfigInfoPersistService.findDeletedConfig(startTime, deleteCursorId, pageSize, Constants.FORMAL); for (ConfigInfoStateWrapper configInfo : configDeleted) { if (configInfoPersistService.findConfigInfoState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()) == null) { ConfigCacheService.remove(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); LogUtil.DEFAULT_LOG.info("[dump-delete-ok], groupKey: {}, tenant: {}", new Object[] {GroupKey2.getKey(configInfo.getDataId(), configInfo.getGroup())}, configInfo.getTenant()); configMigrateService.checkDeletedConfigMigrateState(configInfo); } } if (configDeleted.size() < pageSize) { break; } deleteCursorId = configDeleted.get(configDeleted.size() - 1).getId(); } LogUtil.DEFAULT_LOG.info("Check delete configs finished,cost:{}", System.currentTimeMillis() - startDeletedConfigTime); LogUtil.DEFAULT_LOG.info("Check changeConfig start"); long startChangeConfigTime = System.currentTimeMillis(); long changeCursorId = 0L; while (true) { LogUtil.DEFAULT_LOG.info("Check changed configs from time {},lastMaxId={}", startTime, changeCursorId); List changeConfigs = configInfoPersistService.findChangeConfig(startTime, changeCursorId, pageSize); for (ConfigInfoStateWrapper cf : changeConfigs) { configMigrateService.checkChangedConfigMigrateState(cf); if (StringUtils.isBlank(cf.getTenant())) { continue; } final String groupKey = GroupKey2.getKey(cf.getDataId(), cf.getGroup(), cf.getTenant()); //check md5 & localtimestamp update local disk cache. boolean newLastModified = cf.getLastModified() > ConfigCacheService.getLastModifiedTs(groupKey); String localContentMd5 = ConfigCacheService.getContentMd5(groupKey); boolean md5Update = !localContentMd5.equals(cf.getMd5()); if (newLastModified || md5Update) { LogUtil.DEFAULT_LOG.info("[dump-change] find change config {}, {}, md5={}", groupKey, cf.getLastModified(), cf.getMd5()); ConfigInfoWrapper configInfoWrapper = configInfoPersistService.findConfigInfo(cf.getDataId(), cf.getGroup(), cf.getTenant()); LogUtil.DUMP_LOG.info("[dump-change] find change config {}, {}, md5={}", groupKey, cf.getLastModified(), cf.getMd5()); ConfigCacheService.dump(configInfoWrapper.getDataId(), configInfoWrapper.getGroup(), configInfoWrapper.getTenant(), configInfoWrapper.getContent(), configInfoWrapper.getLastModified(), configInfoWrapper.getType(), configInfoWrapper.getEncryptedDataKey()); final String content = configInfoWrapper.getContent(); final String md5 = MD5Utils.md5Hex(content, Constants.ENCODE_GBK); final String md5Utf8 = MD5Utils.md5Hex(content, Constants.ENCODE_UTF8); LogUtil.DEFAULT_LOG.info("[dump-change-ok] {}, {}, length={}, md5={},md5UTF8={}", groupKey, configInfoWrapper.getLastModified(), content.length(), md5, md5Utf8); } } if (changeConfigs.size() < pageSize) { break; } changeCursorId = changeConfigs.get(changeConfigs.size() - 1).getId(); } long endChangeConfigTime = System.currentTimeMillis(); LogUtil.DEFAULT_LOG.info( "Check changed configs finished,cost:{}, next task ready will from start time {}", endChangeConfigTime - startChangeConfigTime, currentTime); startTime = currentTime; } catch (Throwable e) { LogUtil.DEFAULT_LOG.error("Check changed configs error", e); } finally { ConfigExecutor.scheduleConfigChangeTask(this, PropertyUtil.getDumpChangeWorkerInterval(), TimeUnit.MILLISECONDS); LogUtil.DEFAULT_LOG.info("Next dump change will scheduled after {} milliseconds", PropertyUtil.getDumpChangeWorkerInterval()); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpChangeGrayConfigWorker.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.PropertyUtil; import java.sql.Timestamp; import java.util.List; import java.util.concurrent.TimeUnit; /** * Dump gray config worker. * * @author shiyiyue */ public class DumpChangeGrayConfigWorker implements Runnable { Timestamp startTime; ConfigInfoGrayPersistService configInfoGrayPersistService; ConfigMigrateService configMigrateService; private final HistoryConfigInfoPersistService historyConfigInfoPersistService; int pageSize = 100; public DumpChangeGrayConfigWorker(ConfigInfoGrayPersistService configInfoGrayPersistService, Timestamp startTime, HistoryConfigInfoPersistService historyConfigInfoPersistService, ConfigMigrateService configMigrateService) { this.configInfoGrayPersistService = configInfoGrayPersistService; this.startTime = startTime; this.historyConfigInfoPersistService = historyConfigInfoPersistService; this.configMigrateService = configMigrateService; } @Override public void run() { try { if (!PropertyUtil.isDumpChangeOn()) { LogUtil.DEFAULT_LOG.info("DumpGrayChange task is not open"); return; } Timestamp currentTime = new Timestamp(System.currentTimeMillis()); LogUtil.DEFAULT_LOG.info("DumpGrayChange start ,from time {},current time {}", startTime, currentTime); LogUtil.DEFAULT_LOG.info("Start to check delete configs from time {}", startTime); long startDeletedConfigTime = System.currentTimeMillis(); long deleteCursorId = 0L; while (true) { List configDeleted = historyConfigInfoPersistService.findDeletedConfig(startTime, deleteCursorId, pageSize, Constants.GRAY); for (ConfigInfoStateWrapper configInfo : configDeleted) { String grayName = configInfo.getGrayName(); if (StringUtils.isBlank(grayName)) { continue; } ConfigInfoStateWrapper configInfoStateWrapper = configInfoGrayPersistService.findConfigInfo4GrayState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), grayName); if (configInfoStateWrapper == null) { ConfigCacheService.removeGray(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), grayName); LogUtil.DEFAULT_LOG.info("[dump-gray-delete-ok], groupKey: {}, tenant: {}, grayName: {}", GroupKey2.getKey(configInfo.getDataId(), configInfo.getGroup()), configInfo.getTenant(), grayName); configMigrateService.checkDeletedConfigGrayMigrateState(configInfo); } } if (configDeleted.size() < pageSize) { break; } deleteCursorId = configDeleted.get(configDeleted.size() - 1).getId(); } LogUtil.DEFAULT_LOG.info("Check delete configs finished,cost:{}", System.currentTimeMillis() - startDeletedConfigTime); LogUtil.DEFAULT_LOG.info("Check changeGrayConfig start"); long startChangeConfigTime = System.currentTimeMillis(); long changeCursorId = 0L; while (true) { LogUtil.DEFAULT_LOG.info("Check changed gray configs from time {},lastMaxId={}", startTime, changeCursorId); List changeConfigs = configInfoGrayPersistService.findChangeConfig(startTime, changeCursorId, pageSize); for (ConfigInfoGrayWrapper cf : changeConfigs) { configMigrateService.checkChangedConfigGrayMigrateState(cf); if (StringUtils.isBlank(cf.getTenant())) { continue; } final String groupKey = GroupKey2.getKey(cf.getDataId(), cf.getGroup(), cf.getTenant()); //check md5 & localtimestamp update local disk cache. boolean newLastModified = cf.getLastModified() > ConfigCacheService.getLastModifiedTs(groupKey); String localContentMd5 = ConfigCacheService.getContentMd5(groupKey); boolean md5Update = !localContentMd5.equals(cf.getMd5()); if (newLastModified || md5Update) { LogUtil.DEFAULT_LOG.info("[dump-change-gray] find change config {}, {}, md5={}", new Object[] {groupKey, cf.getLastModified(), cf.getMd5()}); LogUtil.DUMP_LOG.info("[dump-change-gray] find change config {}, {}, md5={}", new Object[] {groupKey, cf.getLastModified(), cf.getMd5()}); ConfigCacheService.dumpGray(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getGrayName(), cf.getGrayRule(), cf.getContent(), cf.getLastModified(), cf.getEncryptedDataKey()); final String content = cf.getContent(); final String md5 = MD5Utils.md5Hex(content, Constants.ENCODE_GBK); final String md5Utf8 = MD5Utils.md5Hex(content, Constants.ENCODE_UTF8); LogUtil.DEFAULT_LOG.info("[dump-change-gray-ok] {}, {}, length={}, md5={},md5UTF8={}", new Object[] {groupKey, cf.getLastModified(), content.length(), md5, md5Utf8}); } } if (changeConfigs.size() < pageSize) { break; } changeCursorId = changeConfigs.get(changeConfigs.size() - 1).getId(); } long endChangeConfigTime = System.currentTimeMillis(); LogUtil.DEFAULT_LOG.info( "Check changed gray configs finished,cost:{}, next task running will from start time {}", endChangeConfigTime - startChangeConfigTime, currentTime); startTime = currentTime; } catch (Throwable e) { LogUtil.DEFAULT_LOG.error("Check changed gray configs error", e); } finally { ConfigExecutor.scheduleConfigChangeTask(this, PropertyUtil.getDumpChangeWorkerInterval(), TimeUnit.MILLISECONDS); LogUtil.DEFAULT_LOG.info("Next dump gray change will scheduled after {} milliseconds", PropertyUtil.getDumpChangeWorkerInterval()); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpConfigHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.config.server.model.event.ConfigDumpEvent; import com.alibaba.nacos.config.server.service.ClientIpWhiteList; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.SwitchService; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; /** * Dump config subscriber. * * @author liaochuntao */ public class DumpConfigHandler extends Subscriber { /** * trigger config dump event. * * @param event {@link ConfigDumpEvent} * @return {@code true} if the config dump task success , else {@code false} */ public static boolean configDump(ConfigDumpEvent event) { final String dataId = event.getDataId(); final String group = event.getGroup(); final String namespaceId = event.getNamespaceId(); final String content = event.getContent(); final long lastModified = event.getLastModifiedTs(); //gray if (StringUtils.isNotBlank(event.getGrayName())) { boolean result = false; if (!event.isRemove()) { result = ConfigCacheService.dumpGray(dataId, group, namespaceId, event.getGrayName(), event.getGrayRule(), content, lastModified, event.getEncryptedDataKey()); if (result) { ConfigTraceService.logDumpGrayNameEvent(dataId, group, namespaceId, event.getGrayName(), null, lastModified, event.getHandleIp(), ConfigTraceService.DUMP_TYPE_OK, System.currentTimeMillis() - lastModified, content.length()); } } else { result = ConfigCacheService.removeGray(dataId, group, namespaceId, event.getGrayName()); if (result) { ConfigTraceService.logDumpGrayNameEvent(dataId, group, namespaceId, event.getGrayName(), null, lastModified, event.getHandleIp(), ConfigTraceService.DUMP_TYPE_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); } } return result; } if (dataId.equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) { ClientIpWhiteList.load(content); } if (dataId.equals(SwitchService.SWITCH_META_DATA_ID)) { SwitchService.load(content); } boolean result; if (!event.isRemove()) { result = ConfigCacheService.dump(dataId, group, namespaceId, content, lastModified, event.getType(), event.getEncryptedDataKey()); if (result) { ConfigTraceService.logDumpEvent(dataId, group, namespaceId, null, lastModified, event.getHandleIp(), ConfigTraceService.DUMP_TYPE_OK, System.currentTimeMillis() - lastModified, content.length()); } } else { result = ConfigCacheService.remove(dataId, group, namespaceId); if (result) { ConfigTraceService.logDumpEvent(dataId, group, namespaceId, null, lastModified, event.getHandleIp(), ConfigTraceService.DUMP_TYPE_REMOVE_OK, System.currentTimeMillis() - lastModified, 0); } } return result; } @Override public void onEvent(ConfigDumpEvent event) { configDump(event); } @Override public Class subscribeType() { return ConfigDumpEvent.class; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpRequest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; /** * dump request. * * @author shiyiyue */ public class DumpRequest { String dataId; String group; String tenant; String grayName; private long lastModifiedTs; private String sourceIp; public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public long getLastModifiedTs() { return lastModifiedTs; } public void setLastModifiedTs(long lastModifiedTs) { this.lastModifiedTs = lastModifiedTs; } public String getSourceIp() { return sourceIp; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public void setSourceIp(String sourceIp) { this.sourceIp = sourceIp; } /** * create dump request. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param lastModifiedTs lastModifiedTs. * @param sourceIp sourceIp. * @return */ public static DumpRequest create(String dataId, String group, String tenant, long lastModifiedTs, String sourceIp) { DumpRequest dumpRequest = new DumpRequest(); dumpRequest.dataId = dataId; dumpRequest.group = group; dumpRequest.tenant = tenant; dumpRequest.lastModifiedTs = lastModifiedTs; dumpRequest.sourceIp = sourceIp; return dumpRequest; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/DumpService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.utils.NetUtils; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.manager.TaskManager; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.processor.DumpAllGrayProcessor; import com.alibaba.nacos.config.server.service.dump.processor.DumpAllProcessor; import com.alibaba.nacos.config.server.service.dump.processor.DumpProcessor; import com.alibaba.nacos.config.server.service.dump.task.DumpAllGrayTask; import com.alibaba.nacos.config.server.service.dump.task.DumpAllTask; import com.alibaba.nacos.config.server.service.dump.task.DumpTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.TimerContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.Timestamp; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.config.server.utils.LogUtil.DUMP_LOG; /** * Dump data service. * * @author Nacos */ public abstract class DumpService { private static final Logger LOGGER = LoggerFactory.getLogger(DumpService.class); protected DumpProcessor processor; protected DumpAllProcessor dumpAllProcessor; protected DumpAllGrayProcessor dumpAllGrayProcessor; protected ConfigInfoPersistService configInfoPersistService; protected NamespacePersistService namespacePersistService; protected HistoryConfigInfoPersistService historyConfigInfoPersistService; protected ConfigInfoGrayPersistService configInfoGrayPersistService; protected ConfigMigrateService configMigrateService; protected final ServerMemberManager memberManager; /** * full dump interval. */ static final int DUMP_ALL_INTERVAL_IN_MINUTE = 6 * 60; /** * full dump delay. */ static final int INITIAL_DELAY_IN_MINUTE = 6 * 60; private TaskManager dumpTaskMgr; private TaskManager dumpAllTaskMgr; static final int INIT_THREAD_COUNT = 10; int total = 0; /** * Here you inject the dependent objects constructively, ensuring that some of the dependent functionality is * initialized ahead of time. * * @param memberManager {@link ServerMemberManager} */ public DumpService(ConfigInfoPersistService configInfoPersistService, NamespacePersistService namespacePersistService, HistoryConfigInfoPersistService historyConfigInfoPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService, ServerMemberManager memberManager, ConfigMigrateService configMigrateService) { this.configInfoPersistService = configInfoPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; this.namespacePersistService = namespacePersistService; this.historyConfigInfoPersistService = historyConfigInfoPersistService; this.memberManager = memberManager; this.configMigrateService = configMigrateService; this.processor = new DumpProcessor(this.configInfoPersistService, this.configInfoGrayPersistService); this.dumpAllProcessor = new DumpAllProcessor(this.configInfoPersistService); this.dumpAllGrayProcessor = new DumpAllGrayProcessor(this.configInfoGrayPersistService); this.dumpTaskMgr = new TaskManager("com.alibaba.nacos.server.DumpTaskManager"); this.dumpTaskMgr.setDefaultTaskProcessor(processor); this.dumpAllTaskMgr = new TaskManager("com.alibaba.nacos.server.DumpAllTaskManager"); this.dumpAllTaskMgr.setDefaultTaskProcessor(dumpAllProcessor); this.dumpAllTaskMgr.addProcessor(DumpAllTask.TASK_ID, dumpAllProcessor); this.dumpAllTaskMgr.addProcessor(DumpAllGrayTask.TASK_ID, dumpAllGrayProcessor); DynamicDataSource.getInstance().getDataSource(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { handleConfigDataChange(event); } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); } void handleConfigDataChange(Event event) { // Generate ConfigDataChangeEvent concurrently if (event instanceof ConfigDataChangeEvent) { ConfigDataChangeEvent evt = (ConfigDataChangeEvent) event; DumpRequest dumpRequest = DumpRequest.create(evt.dataId, evt.group, evt.tenant, evt.lastModifiedTs, NetUtils.localIp()); dumpRequest.setGrayName(evt.grayName); DumpService.this.dump(dumpRequest); } } /** * initialize. * * @throws Throwable throws Exception when actually operate. */ protected abstract void init() throws Throwable; /** * config history clear. */ class ConfigHistoryClear implements Runnable { private HistoryConfigCleaner historyConfigCleaner; public ConfigHistoryClear(HistoryConfigCleaner historyConfigCleaner) { this.historyConfigCleaner = historyConfigCleaner; } @Override public void run() { LOGGER.warn("clearHistoryConfig get scheduled"); if (canExecute()) { try { LOGGER.warn("clearHistoryConfig is enable in current context, try to run cleaner"); historyConfigCleaner.cleanHistoryConfig(); LOGGER.warn("history config cleaner successfully"); } catch (Throwable e) { LOGGER.error("clearConfigHistory error : {}", e.toString()); } } else { LOGGER.warn("clearHistoryConfig is disable in current context"); } } } /** * config history clear. */ class DumpAllProcessorRunner implements Runnable { @Override public void run() { dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask()); } } /** * dump all gray processor runner. */ class DumpAllGrayProcessorRunner implements Runnable { @Override public void run() { dumpAllTaskMgr.addTask(DumpAllGrayTask.TASK_ID, new DumpAllGrayTask()); } } protected void dumpOperate() throws NacosException { String dumpFileContext = "CONFIG_DUMP_TO_FILE"; TimerContext.start(dumpFileContext); try { LogUtil.DEFAULT_LOG.warn("DumpService start"); Timestamp currentTime = new Timestamp(System.currentTimeMillis()); try { dumpAllConfigInfoOnStartup(dumpAllProcessor); dumpAllGrayConfigInfoOnStartup(dumpAllGrayProcessor); } catch (Exception e) { LogUtil.FATAL_LOG.error( "Nacos Server did not start because dumpservice bean construction failure :\n" + e); throw new NacosException(NacosException.SERVER_ERROR, "Nacos Server did not start because dumpservice bean construction failure :\n" + e.getMessage(), e); } if (!EnvUtil.getStandaloneMode()) { long initialDelay = ThreadLocalRandom.current().nextInt(INITIAL_DELAY_IN_MINUTE) + 10; LogUtil.DEFAULT_LOG.warn("initialDelay:{}", initialDelay); ConfigExecutor.scheduleConfigTask(new DumpAllProcessorRunner(), initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES); ConfigExecutor.scheduleConfigTask(new DumpAllGrayProcessorRunner(), initialDelay, DUMP_ALL_INTERVAL_IN_MINUTE, TimeUnit.MINUTES); ConfigExecutor.scheduleConfigChangeTask( new DumpChangeConfigWorker(this.configInfoPersistService, this.historyConfigInfoPersistService, this.configMigrateService, currentTime), ThreadLocalRandom.current().nextInt((int) PropertyUtil.getDumpChangeWorkerInterval()), TimeUnit.MILLISECONDS); ConfigExecutor.scheduleConfigChangeTask( new DumpChangeGrayConfigWorker(this.configInfoGrayPersistService, currentTime, this.historyConfigInfoPersistService, this.configMigrateService), ThreadLocalRandom.current().nextInt((int) PropertyUtil.getDumpChangeWorkerInterval()), TimeUnit.MILLISECONDS); } HistoryConfigCleaner cleaner = HistoryConfigCleanerManager.getHistoryConfigCleaner( HistoryConfigCleanerConfig.getInstance().getActiveHistoryConfigCleaner()); ConfigExecutor.scheduleConfigTask(new ConfigHistoryClear(cleaner), 10, 10, TimeUnit.MINUTES); } finally { TimerContext.end(dumpFileContext, LogUtil.DUMP_LOG); } } private void dumpAllConfigInfoOnStartup(DumpAllProcessor dumpAllProcessor) { try { LogUtil.DEFAULT_LOG.info("start clear all config-info."); ConfigDiskServiceFactory.getInstance().clearAll(); dumpAllProcessor.process(new DumpAllTask(true)); } catch (Exception e) { LogUtil.FATAL_LOG.error("dump config fail" + e.getMessage()); throw e; } } private void dumpAllGrayConfigInfoOnStartup(DumpAllGrayProcessor dumpAllGrayProcessor) { try { LogUtil.DEFAULT_LOG.info("start to clear all gray-config-info on startup."); ConfigDiskServiceFactory.getInstance().clearAllGray(); dumpAllGrayProcessor.process(new DumpAllGrayTask()); } catch (Exception e) { LogUtil.FATAL_LOG.error("failed to dump all gray-config-info on startup." + e.getMessage()); throw e; } } /** * dump operation. * * @param dumpRequest dumpRequest. */ public void dump(DumpRequest dumpRequest) { if (StringUtils.isNotBlank(dumpRequest.getGrayName())) { dumpGray(dumpRequest.getDataId(), dumpRequest.getGroup(), dumpRequest.getTenant(), dumpRequest.getGrayName(), dumpRequest.getLastModifiedTs(), dumpRequest.getSourceIp()); } else { dumpFormal(dumpRequest.getDataId(), dumpRequest.getGroup(), dumpRequest.getTenant(), dumpRequest.getLastModifiedTs(), dumpRequest.getSourceIp()); } } /** * dump formal config. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param lastModified lastModified. * @param handleIp handleIp. */ private void dumpFormal(String dataId, String group, String tenant, long lastModified, String handleIp) { String groupKey = GroupKey2.getKey(dataId, group, tenant); String taskKey = groupKey; dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, null, lastModified, handleIp)); DUMP_LOG.info("[dump] add formal task. groupKey={}", groupKey); } /** * dump gray. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param grayName grayName. * @param lastModified lastModified. * @param handleIp handleIp. */ private void dumpGray(String dataId, String group, String tenant, String grayName, long lastModified, String handleIp) { String groupKey = GroupKey2.getKey(dataId, group, tenant); String taskKey = groupKey + "+gray+" + grayName; dumpTaskMgr.addTask(taskKey, new DumpTask(groupKey, grayName, lastModified, handleIp)); DUMP_LOG.info("[dump] add gray task. groupKey={},grayName={}", groupKey, grayName); } public void dumpAll() { dumpAllTaskMgr.addTask(DumpAllTask.TASK_ID, new DumpAllTask()); } /** * Used to determine whether the aggregation task, configuration history cleanup task can be performed. * * @return {@link Boolean} */ protected abstract boolean canExecute(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/EmbeddedDumpService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.common.utils.Observable; import com.alibaba.nacos.common.utils.Observer; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.consistency.ProtocolMetaData; import com.alibaba.nacos.consistency.cp.CPProtocol; import com.alibaba.nacos.consistency.cp.MetadataKey; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.distributed.ProtocolManager; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.core.utils.GlobalExecutor; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage; import com.alibaba.nacos.persistence.constants.PersistenceConstant; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; /** * Embedded dump service. * * @author liaochuntao */ @Conditional(ConditionOnEmbeddedStorage.class) @Component public class EmbeddedDumpService extends DumpService { /** * If it's just a normal reading failure, it can be resolved by retrying. */ final String[] retryMessages = new String[] {"The conformance protocol is temporarily unavailable for reading"}; /** * If the read failed due to an internal problem in the Raft state machine, it cannot be remedied by retrying. */ final String[] errorMessages = new String[] {"FSMCaller is overload.", "STATE_ERROR"}; private final ProtocolManager protocolManager; /** * Here you inject the dependent objects constructively, ensuring that some of the dependent functionality is * initialized ahead of time. * * @param memberManager {@link ServerMemberManager} * @param protocolManager {@link ProtocolManager} */ public EmbeddedDumpService(ConfigInfoPersistService configInfoPersistService, NamespacePersistService namespacePersistService, HistoryConfigInfoPersistService historyConfigInfoPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService, ServerMemberManager memberManager, ProtocolManager protocolManager, ConfigMigrateService configMigrateService) { super(configInfoPersistService, namespacePersistService, historyConfigInfoPersistService, configInfoGrayPersistService, memberManager, configMigrateService); this.protocolManager = protocolManager; } @PostConstruct @Override protected void init() throws Throwable { if (EnvUtil.getStandaloneMode()) { dumpOperate(); return; } CPProtocol protocol = protocolManager.getCpProtocol(); AtomicReference errorReference = new AtomicReference<>(null); CountDownLatch waitDumpFinish = new CountDownLatch(1); // watch path => /nacos_config/leader/ has value ? Observer observer = new Observer() { @Override public void update(Observable o) { if (!(o instanceof ProtocolMetaData.ValueItem)) { return; } final Object arg = ((ProtocolMetaData.ValueItem) o).getData(); GlobalExecutor.executeByCommon(() -> { // must make sure that there is a value here to perform the correct operation that follows if (Objects.isNull(arg)) { return; } // Identify without a timeout mechanism EmbeddedStorageContextHolder.putExtendInfo(PersistenceConstant.EXTEND_NEED_READ_UNTIL_HAVE_DATA, "true"); // Remove your own listening to avoid task accumulation boolean canEnd = false; for (; ; ) { try { dumpOperate(); protocol.protocolMetaData().unSubscribe(PersistenceConstant.CONFIG_MODEL_RAFT_GROUP, MetadataKey.LEADER_META_DATA, this); canEnd = true; } catch (Throwable ex) { if (!shouldRetry(ex)) { errorReference.set(ex); canEnd = true; } } if (canEnd) { ThreadUtils.countDown(waitDumpFinish); break; } ThreadUtils.sleep(500L); } EmbeddedStorageContextHolder.cleanAllContext(); }); } }; protocol.protocolMetaData() .subscribe(PersistenceConstant.CONFIG_MODEL_RAFT_GROUP, MetadataKey.LEADER_META_DATA, observer); // We must wait for the dump task to complete the callback operation before // continuing with the initialization ThreadUtils.latchAwait(waitDumpFinish); // If an exception occurs during the execution of the dump task, the exception // needs to be thrown, triggering the node to start the failed process final Throwable ex = errorReference.get(); if (Objects.nonNull(ex)) { throw ex; } } private boolean shouldRetry(Throwable ex) { final String errMsg = ex.getMessage(); for (String failedMsg : errorMessages) { if (StringUtils.containsIgnoreCase(errMsg, failedMsg)) { return false; } } for (final String retryMsg : retryMessages) { if (StringUtils.containsIgnoreCase(errMsg, retryMsg)) { return true; } } return false; } @Override protected boolean canExecute() { if (EnvUtil.getStandaloneMode()) { return true; } // if is derby + raft mode, only leader can execute CPProtocol protocol = protocolManager.getCpProtocol(); return protocol.isLeader(PersistenceConstant.CONFIG_MODEL_RAFT_GROUP); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/ExternalDumpService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** * External dump service. * * @author liaochuntao */ @Conditional(ConditionOnExternalStorage.class) @Component @DependsOn({"rpcConfigChangeNotifier", "configMigrateService"}) public class ExternalDumpService extends DumpService { /** * Here you inject the dependent objects constructively, ensuring that some of the dependent functionality is * initialized ahead of time. * * @param memberManager {@link ServerMemberManager} */ public ExternalDumpService(ConfigInfoPersistService configInfoPersistService, NamespacePersistService namespacePersistService, HistoryConfigInfoPersistService historyConfigInfoPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService, ServerMemberManager memberManager, ConfigMigrateService configMigrateService) { super(configInfoPersistService, namespacePersistService, historyConfigInfoPersistService, configInfoGrayPersistService, memberManager, configMigrateService); } @PostConstruct @Override protected void init() throws Throwable { dumpOperate(); } @Override protected boolean canExecute() { return memberManager.isFirstIp(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/HistoryConfigCleaner.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; /** * The interface History config cleaner. * @author Sunrisea */ public interface HistoryConfigCleaner { /** * Clean history config. */ public void cleanHistoryConfig(); /** * Gets name. * * @return the name */ public String getName(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/HistoryConfigCleanerConfig.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.core.config.AbstractDynamicConfig; import com.alibaba.nacos.sys.env.EnvUtil; /** * The type History config cleaner config. * @author Sunrisea */ public class HistoryConfigCleanerConfig extends AbstractDynamicConfig { private static final String HISTORY_CONFIG_CLEANER = "historyConfigCleaner"; private static final HistoryConfigCleanerConfig INSTANCE = new HistoryConfigCleanerConfig(); private String activeHistoryConfigCleaner = "nacos"; private HistoryConfigCleanerConfig() { super(HISTORY_CONFIG_CLEANER); resetConfig(); } /** * Gets instance. * * @return the instance */ public static HistoryConfigCleanerConfig getInstance() { return INSTANCE; } @Override protected void getConfigFromEnv() { activeHistoryConfigCleaner = EnvUtil.getProperty("nacos.config.history.clear.name", String.class, "nacos"); if (StringUtils.isBlank(activeHistoryConfigCleaner)) { activeHistoryConfigCleaner = "nacos"; } } /** * Gets active history config cleaner. * * @return the active history config cleaner */ public String getActiveHistoryConfigCleaner() { return activeHistoryConfigCleaner; } /** * Sets active history config cleaner. * * @param activeHistoryConfigCleaner the active history config cleaner */ public void setActiveHistoryConfigCleaner(String activeHistoryConfigCleaner) { this.activeHistoryConfigCleaner = activeHistoryConfigCleaner; } @Override protected String printConfig() { return "activeHistoryConfigCleaner{ " + "activeHistoryConfigCleaner=" + activeHistoryConfigCleaner + "}"; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/HistoryConfigCleanerManager.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.common.spi.NacosServiceLoader; import java.util.HashMap; /** * The type History config cleaner manager. * * @author Sunrisea */ public class HistoryConfigCleanerManager { private static HashMap historyConfigCleanerMap = new HashMap(); static { NacosServiceLoader.load(HistoryConfigCleaner.class).forEach(historyConfigCleaner -> { historyConfigCleanerMap.put(historyConfigCleaner.getName(), historyConfigCleaner); }); historyConfigCleanerMap.put("nacos", new DefaultHistoryConfigCleaner()); } /** * Gets history config cleaner. * * @param name the name * @return the history config cleaner */ public static HistoryConfigCleaner getHistoryConfigCleaner(String name) { return historyConfigCleanerMap.getOrDefault(name, historyConfigCleanerMap.get("nacos")); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/disk/ConfigDiskService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.disk; import java.io.IOException; /** * config disk service. * * @author zunfei.lzf */ public interface ConfigDiskService { /** * Save configuration information to disk. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param content content. * @throws IOException io exception. */ void saveToDisk(String dataId, String group, String tenant, String content) throws IOException; /** * Save gray information to disk. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param grayName grayName. * @param content content. * @throws IOException io exception. */ void saveGrayToDisk(String dataId, String group, String tenant, String grayName, String content) throws IOException; /** * Deletes gray configuration files on disk. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param grayName grayName. */ void removeConfigInfo4Gray(String dataId, String group, String tenant, String grayName); /** * Returns the content of the gray cache file in server. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param grayName grayName. * @return gray content, null if not exist. * @throws IOException io exception. */ String getGrayContent(String dataId, String group, String tenant, String grayName) throws IOException; /** * Deletes configuration files on disk. * * @param dataId dataId. * @param group group. * @param tenant tenant. */ void removeConfigInfo(String dataId, String group, String tenant); /** * Returns the content of the cache file in server. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @return content null if not exist. * @throws IOException io exception. */ String getContent(String dataId, String group, String tenant) throws IOException; /** * Clear all config file. */ void clearAll(); /** * Clear all gray config file. */ void clearAllGray(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/disk/ConfigDiskServiceFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.disk; /** * config disk serve factory. * * @author zunfei.lzf */ public class ConfigDiskServiceFactory { static ConfigDiskService configDiskService; private static final String TYPE_RAW_DISK = "rawdisk"; private static final String TYPE_ROCKSDB = "rocksdb"; /** * get disk service. * * @return */ public static ConfigDiskService getInstance() { if (configDiskService == null) { synchronized (ConfigDiskServiceFactory.class) { if (configDiskService == null) { String type = System.getProperty("config_disk_type", TYPE_RAW_DISK); if (type.equalsIgnoreCase(TYPE_ROCKSDB)) { configDiskService = new ConfigRocksDbDiskService(); } else { configDiskService = new ConfigRawDiskService(); } } return configDiskService; } } return configDiskService; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/disk/ConfigRawDiskService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.disk; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.common.pathencoder.PathEncoderManager; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE_UTF8; /** * config raw disk service. * * @author zunfei.lzf */ public class ConfigRawDiskService implements ConfigDiskService { private static final String BASE_DIR = File.separator + "data" + File.separator + "config-data"; private static final String TENANT_BASE_DIR = File.separator + "data" + File.separator + "tenant-config-data"; private static final String GRAY_DIR = File.separator + "data" + File.separator + "gray-data"; private static final String TENANT_GRAY_DIR = File.separator + "data" + File.separator + "tenant-gray-data"; /** * Save configuration information to disk. */ public void saveToDisk(String dataId, String group, String tenant, String content) throws IOException { File targetFile = targetFile(dataId, group, tenant); FileUtils.writeStringToFile(targetFile, content, ENCODE_UTF8); } /** * Returns the path of the server cache file. */ static File targetFile(String dataId, String group, String tenant) { try { ParamUtils.checkParam(dataId, group, tenant); } catch (Exception e) { throw new NacosRuntimeException(NacosException.CLIENT_INVALID_PARAM, "parameter is invalid."); } // fix https://github.com/alibaba/nacos/issues/10067 dataId = PathEncoderManager.getInstance().encode(dataId); group = PathEncoderManager.getInstance().encode(group); tenant = PathEncoderManager.getInstance().encode(tenant); File file = null; if (StringUtils.isBlank(tenant)) { file = new File(EnvUtil.getNacosHome(), BASE_DIR); } else { file = new File(EnvUtil.getNacosHome(), TENANT_BASE_DIR); file = new File(file, tenant); } file = new File(file, group); file = new File(file, dataId); return file; } /** * Returns the path of the gray cache file in server. */ private static File targetGrayFile(String dataId, String group, String tenant, String grayName) { try { ParamUtils.checkParam(grayName); ParamUtils.checkParam(dataId, group, tenant); } catch (Exception e) { throw new NacosRuntimeException(NacosException.CLIENT_INVALID_PARAM, "parameter is invalid."); } // fix https://github.com/alibaba/nacos/issues/10067 dataId = PathEncoderManager.getInstance().encode(dataId); group = PathEncoderManager.getInstance().encode(group); tenant = PathEncoderManager.getInstance().encode(tenant); File file = null; if (StringUtils.isBlank(tenant)) { file = new File(EnvUtil.getNacosHome(), GRAY_DIR); } else { file = new File(EnvUtil.getNacosHome(), TENANT_GRAY_DIR); file = new File(file, tenant); } file = new File(file, group); file = new File(file, dataId); file = new File(file, grayName); return file; } /** * Returns the path of the gray content cache file in server. */ private static File targetGrayContentFile(String dataId, String group, String tenant, String grayName) { return targetGrayFile(dataId, group, tenant, grayName); } /** * Save gray information to disk. */ public void saveGrayToDisk(String dataId, String group, String tenant, String grayName, String content) throws IOException { File targetGrayContentFile = targetGrayContentFile(dataId, group, tenant, grayName); FileUtils.writeStringToFile(targetGrayContentFile, content, ENCODE_UTF8); } /** * Deletes configuration files on disk. */ public void removeConfigInfo(String dataId, String group, String tenant) { FileUtils.deleteQuietly(targetFile(dataId, group, tenant)); } /** * Deletes gray configuration files on disk. */ public void removeConfigInfo4Gray(String dataId, String group, String tenant, String grayName) { FileUtils.deleteQuietly(targetGrayContentFile(dataId, group, tenant, grayName)); } private static String file2String(File file) throws IOException { if (!file.exists()) { return null; } return FileUtils.readFileToString(file, ENCODE_UTF8); } /** * Returns the content of the gray cache file in server. */ public String getGrayContent(String dataId, String group, String tenant, String grayName) throws IOException { return file2String(targetGrayContentFile(dataId, group, tenant, grayName)); } public String getContent(String dataId, String group, String tenant) throws IOException { File file = targetFile(dataId, group, tenant); if (file.exists()) { FileInputStream fis = null; try { fis = new FileInputStream(file); return IoUtils.toString(fis, ENCODE_UTF8); } catch (FileNotFoundException e) { return null; } finally { IoUtils.closeQuietly(fis); } } else { return null; } } /** * Clear all config file. */ public void clearAll() { File file = new File(EnvUtil.getNacosHome(), BASE_DIR); if (!file.exists() || FileUtils.deleteQuietly(file)) { LogUtil.DEFAULT_LOG.info("clear all config-info success."); } else { LogUtil.DEFAULT_LOG.warn("clear all config-info failed."); } File fileTenant = new File(EnvUtil.getNacosHome(), TENANT_BASE_DIR); if (!fileTenant.exists() || FileUtils.deleteQuietly(fileTenant)) { LogUtil.DEFAULT_LOG.info("clear all config-info-tenant success."); } else { LogUtil.DEFAULT_LOG.warn("clear all config-info-tenant failed."); } } /** * Clear all gray config file. */ public void clearAllGray() { File file = new File(EnvUtil.getNacosHome(), GRAY_DIR); if (!file.exists() || FileUtils.deleteQuietly(file)) { LogUtil.DEFAULT_LOG.info("clear all config-info-gray success."); } else { LogUtil.DEFAULT_LOG.warn("clear all config-info-gray failed."); } File fileTenant = new File(EnvUtil.getNacosHome(), TENANT_GRAY_DIR); if (!fileTenant.exists() || FileUtils.deleteQuietly(fileTenant)) { LogUtil.DEFAULT_LOG.info("clear all config-info-gray-tenant success."); } else { LogUtil.DEFAULT_LOG.warn("clear all config-info-gray-tenant failed."); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/disk/ConfigRocksDbDiskService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.disk; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.sys.env.EnvUtil; import org.rocksdb.BlockBasedTableConfig; import org.rocksdb.ColumnFamilyOptions; import org.rocksdb.DBOptions; import org.rocksdb.Options; import org.rocksdb.RocksDB; import org.rocksdb.RocksDBException; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE_UTF8; import static com.alibaba.nacos.config.server.constant.Constants.NULL; /** * config rocks db disk service. * * @author shiyiyue */ public class ConfigRocksDbDiskService implements ConfigDiskService { private static final String ROCKSDB_DATA = File.separator + "rocksdata" + File.separator; private static final String BASE_DIR = ROCKSDB_DATA + "config-data"; private static final String GRAY_DIR = ROCKSDB_DATA + "gray-data"; private static final long DEFAULT_WRITE_BUFFER_MB = 32; Map rocksDbMap = new HashMap<>(); private void createDirIfNotExist(String dir) { File roskDataDir = new File(EnvUtil.getNacosHome(), "rocksdata"); if (!roskDataDir.exists()) { roskDataDir.mkdirs(); } File baseDir = new File(EnvUtil.getNacosHome(), dir); if (!baseDir.exists()) { baseDir.mkdirs(); } } private void deleteDirIfExist(String dir) { File rockskDataDir = new File(EnvUtil.getNacosHome(), "rocksdata"); if (!rockskDataDir.exists()) { return; } File baseDir = new File(EnvUtil.getNacosHome(), dir); if (baseDir.exists()) { baseDir.delete(); } } public ConfigRocksDbDiskService() { createDirIfNotExist(BASE_DIR); createDirIfNotExist(GRAY_DIR); } private byte[] getKeyByte(String dataId, String group, String tenant, String tag) throws IOException { String[] keys = new String[] {dataId, group, tenant, tag}; return getKeyByte(keys); } private byte[] getKeyByte(String... keys) throws IOException { if (keys == null || keys.length == 0) { return NULL.getBytes(ENCODE_UTF8); } StringBuilder stringBuilder = new StringBuilder(); for (String key : keys) { if (StringUtils.isBlank(key)) { key = ""; } urlEncode(key, stringBuilder); stringBuilder.append("+"); } return stringBuilder.toString().getBytes(ENCODE_UTF8); } /** * + -> %2B % -> %25. */ private static void urlEncode(String str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { char c = str.charAt(idx); if ('+' == c) { sb.append("%2B"); } else if ('%' == c) { sb.append("%25"); } else { sb.append(c); } } } /** * save config to disk. */ public void saveToDiskInner(String type, String dataId, String group, String tenant, String tag, String content) throws IOException { try { initAndGetDB(type).put(getKeyByte(dataId, group, tenant, tag), content.getBytes(ENCODE_UTF8)); } catch (RocksDBException e) { throw new IOException(e); } } /** * save config to disk. */ public void saveToDiskInner(String type, String dataId, String group, String tenant, String content) throws IOException { saveToDiskInner(type, dataId, group, tenant, null, content); } /** * save config to disk. */ public void saveGrayToDiskInner(String type, String dataId, String group, String tenant, String grayName, String content) throws IOException { try { initAndGetDB(type).put(getKeyByte(dataId, group, tenant, grayName), content.getBytes(ENCODE_UTF8)); } catch (RocksDBException e) { throw new IOException(e); } } /** * Save configuration information to disk. */ public void saveToDisk(String dataId, String group, String tenant, String content) throws IOException { saveToDiskInner(BASE_DIR, dataId, group, tenant, content); } /** * Save tag information to disk. */ public void saveGrayToDisk(String dataId, String group, String tenant, String grayName, String content) throws IOException { saveGrayToDiskInner(GRAY_DIR, dataId, group, tenant, grayName, content); } /** * Deletes configuration files on disk. */ public void removeConfigInfo(String dataId, String group, String tenant) { removeContentInner(BASE_DIR, dataId, group, tenant, null); } /** * Deletes gray configuration files on disk. */ public void removeConfigInfo4Gray(String dataId, String group, String tenant, String grayName) { removeGrayInner(GRAY_DIR, dataId, group, tenant, grayName); } private String byte2String(byte[] bytes) throws IOException { if (bytes == null) { return null; } return new String(bytes, ENCODE_UTF8); } RocksDB initAndGetDB(String dir) throws IOException, RocksDBException { if (rocksDbMap.containsKey(dir)) { return rocksDbMap.get(dir); } else { synchronized (this) { if (rocksDbMap.containsKey(dir)) { return rocksDbMap.get(dir); } createDirIfEmpty(EnvUtil.getNacosHome() + dir); rocksDbMap.put(dir, RocksDB.open(createOptions(dir), EnvUtil.getNacosHome() + dir)); return rocksDbMap.get(dir); } } } private void createDirIfEmpty(String filePath) { File file = new File(filePath); if (!file.exists()) { file.mkdirs(); } } private String getContentInner(String type, String dataId, String group, String tenant) throws IOException { byte[] bytes = null; try { bytes = initAndGetDB(type).get(getKeyByte(dataId, group, tenant, null)); String string = byte2String(bytes); return string; } catch (RocksDBException e) { throw new IOException(e); } } private String getGrayInner(String type, String dataId, String group, String tenant, String grayName) throws IOException { byte[] bytes = null; try { bytes = initAndGetDB(type).get(getKeyByte(dataId, group, tenant, grayName)); return byte2String(bytes); } catch (RocksDBException e) { throw new IOException(e); } } private void removeContentInner(String type, String dataId, String group, String tenant, String tag) { try { initAndGetDB(type).delete(getKeyByte(dataId, group, tenant, tag)); } catch (Exception e) { LogUtil.DEFAULT_LOG.warn("Remove dir=[{}] config fail,dataId={},group={},tenant={},error={}", type, dataId, group, tenant, e.getCause()); } } private void removeGrayInner(String type, String dataId, String group, String tenant, String grayName) { try { initAndGetDB(type).delete(getKeyByte(dataId, group, tenant, grayName)); } catch (Exception e) { LogUtil.DEFAULT_LOG.warn("Remove dir=[{}] config fail,dataId={},group={},tenant={},error={}", type, dataId, group, tenant, e.getCause()); } } /** * Returns the path of the gray content cache file in server. */ public String getGrayContent(String dataId, String group, String tenant, String grayName) throws IOException { return getGrayInner(GRAY_DIR, dataId, group, tenant, grayName); } public String getContent(String dataId, String group, String tenant) throws IOException { return getContentInner(BASE_DIR, dataId, group, tenant); } public String getLocalConfigMd5(String dataId, String group, String tenant, String encode) throws IOException { return MD5Utils.md5Hex(getContentInner(BASE_DIR, dataId, group, tenant), encode); } Options createOptions(String dir) { DBOptions dbOptions = new DBOptions(); dbOptions.setMaxBackgroundJobs(Runtime.getRuntime().availableProcessors()); Options options = new Options(dbOptions, createColumnFamilyOptions(dir)); options.setCreateIfMissing(true); return options; } ColumnFamilyOptions createColumnFamilyOptions(String dir) { ColumnFamilyOptions columnFamilyOptions = new ColumnFamilyOptions(); BlockBasedTableConfig tableFormatConfig = new BlockBasedTableConfig(); columnFamilyOptions.setTableFormatConfig(tableFormatConfig); //set more write buffer size to formal config-data, reduce flush to sst file frequency. columnFamilyOptions.setWriteBufferSize(getSuitFormalCacheSizeMB(dir) * 1024 * 1024); //once a stt file is flushed, compact it immediately to avoid too many sst file which will result in read latency. columnFamilyOptions.setLevel0FileNumCompactionTrigger(1); return columnFamilyOptions; } /** * get suit formal buffer size. * * @return */ private long getSuitFormalCacheSizeMB(String dir) { boolean formal = BASE_DIR.equals(dir); long maxHeapSizeMB = Runtime.getRuntime().maxMemory() / 1024 / 1024; if (formal) { long formalWriteBufferSizeMB = 0; if (maxHeapSizeMB < 8 * 1024) { formalWriteBufferSizeMB = 32; } else if (maxHeapSizeMB < 16 * 1024) { formalWriteBufferSizeMB = 64; } else { formalWriteBufferSizeMB = 256; } LogUtil.DEFAULT_LOG.info("init formal rocksdb write buffer size {}M for dir {}, maxHeapSize={}M", formalWriteBufferSizeMB, dir, maxHeapSizeMB); return formalWriteBufferSizeMB; } else { LogUtil.DEFAULT_LOG.info("init default rocksdb write buffer size {}M for dir {}, maxHeapSize={}M", DEFAULT_WRITE_BUFFER_MB, dir, maxHeapSizeMB); return DEFAULT_WRITE_BUFFER_MB; } } /** * Clear all config file. */ public void clearAll() { try { if (rocksDbMap.containsKey(BASE_DIR)) { rocksDbMap.get(BASE_DIR).close(); RocksDB.destroyDB(EnvUtil.getNacosHome() + BASE_DIR, new Options()); } deleteDirIfExist(BASE_DIR); LogUtil.DEFAULT_LOG.info("clear all config-info success."); } catch (RocksDBException e) { LogUtil.DEFAULT_LOG.warn("clear all config-info failed.", e); } } /** * Clear all gray config file. */ public void clearAllGray() { try { if (rocksDbMap.containsKey(GRAY_DIR)) { rocksDbMap.get(GRAY_DIR).close(); RocksDB.destroyDB(EnvUtil.getNacosHome() + GRAY_DIR, new Options()); } deleteDirIfExist(GRAY_DIR); LogUtil.DEFAULT_LOG.info("clear all config-info-gray success."); } catch (RocksDBException e) { LogUtil.DEFAULT_LOG.warn("clear all config-info-gray failed.", e); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/processor/DumpAllGrayProcessor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.processor; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.dump.task.DumpAllGrayTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.api.model.Page; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; import static com.alibaba.nacos.config.server.utils.PropertyUtil.getAllDumpPageSize; /** * Dump all gray processor. * * @author Nacos * @datete 2024/02/20 */ public class DumpAllGrayProcessor implements NacosTaskProcessor { public DumpAllGrayProcessor(ConfigInfoGrayPersistService configInfoGrayPersistService) { this.configInfoGrayPersistService = configInfoGrayPersistService; } @Override public boolean process(NacosTask task) { if (!(task instanceof DumpAllGrayTask)) { DEFAULT_LOG.error( "[all-dump-gray-error] ,invalid task type {},DumpAllGrayProcessor should process DumpAllGrayTask type.", task.getClass().getSimpleName()); return false; } int rowCount = configInfoGrayPersistService.configInfoGrayCount(); int pageCount = (int) Math.ceil(rowCount * 1.0 / PAGE_SIZE); int actualRowCount = 0; for (int pageNo = 1; pageNo <= pageCount; pageNo++) { Page page = configInfoGrayPersistService.findAllConfigInfoGrayForDumpAll(pageNo, PAGE_SIZE); if (page != null) { for (ConfigInfoGrayWrapper cf : page.getPageItems()) { if (StringUtils.isBlank(cf.getTenant())) { continue; } boolean result = ConfigCacheService .dumpGray(cf.getDataId(), cf.getGroup(), cf.getTenant(), cf.getGrayName(), cf.getGrayRule(), cf.getContent(), cf.getLastModified(), cf.getEncryptedDataKey()); LogUtil.DUMP_LOG.info("[dump-all-gray-ok] result={}, {}, {}, length={}, md5={}, grayName={}", result, GroupKey2.getKey(cf.getDataId(), cf.getGroup()), cf.getLastModified(), cf.getContent().length(), cf.getMd5(), cf.getGrayName()); } actualRowCount += page.getPageItems().size(); DEFAULT_LOG.info("[all-dump-gray] {} / {}", actualRowCount, rowCount); } } return true; } static final int PAGE_SIZE = getAllDumpPageSize(); final ConfigInfoGrayPersistService configInfoGrayPersistService; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/processor/DumpAllProcessor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.processor; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.ClientIpWhiteList; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.SwitchService; import com.alibaba.nacos.config.server.service.dump.task.DumpAllTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.api.model.Page; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.SynchronousQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE_UTF8; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; /** * Dump all processor. * * @author Nacos * @date 2020/7/5 12:19 PM */ public class DumpAllProcessor implements NacosTaskProcessor { public DumpAllProcessor(ConfigInfoPersistService configInfoPersistService) { this.configInfoPersistService = configInfoPersistService; } @Override public boolean process(NacosTask task) { if (!(task instanceof DumpAllTask)) { DEFAULT_LOG.error( "[all-dump-error] ,invalid task type {},DumpAllProcessor should process DumpAllTask type.", task.getClass().getSimpleName()); return false; } DumpAllTask dumpAllTask = (DumpAllTask) task; long currentMaxId = configInfoPersistService.findConfigMaxId(); long lastMaxId = 0; ThreadPoolExecutor executorService = null; if (dumpAllTask.isStartUp()) { executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors(), 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(PropertyUtil.getAllDumpPageSize() * 2), r -> new Thread(r, "dump all executor"), new ThreadPoolExecutor.CallerRunsPolicy()); } else { executorService = new ThreadPoolExecutor(1, 1, 60L, TimeUnit.SECONDS, new SynchronousQueue<>(), r -> new Thread(r, "dump all executor"), new ThreadPoolExecutor.CallerRunsPolicy()); } DEFAULT_LOG.info("start dump all config-info..."); while (lastMaxId < currentMaxId) { long start = System.currentTimeMillis(); Page page = configInfoPersistService.findAllConfigInfoFragment(lastMaxId, PropertyUtil.getAllDumpPageSize(), dumpAllTask.isStartUp()); long dbTimeStamp = System.currentTimeMillis(); if (page == null || page.getPageItems() == null || page.getPageItems().isEmpty()) { break; } for (ConfigInfoWrapper cf : page.getPageItems()) { lastMaxId = Math.max(cf.getId(), lastMaxId); if (StringUtils.isBlank(cf.getTenant())) { continue; } //if not start up, page query will not return content, check md5 and lastModified first ,if changed ,get single content info to dump. if (!dumpAllTask.isStartUp()) { final String groupKey = GroupKey2.getKey(cf.getDataId(), cf.getGroup(), cf.getTenant()); boolean newLastModified = cf.getLastModified() > ConfigCacheService.getLastModifiedTs(groupKey); //check md5 & update local disk cache. String localContentMd5 = ConfigCacheService.getContentMd5(groupKey); boolean md5Update = !localContentMd5.equals(cf.getMd5()); if (newLastModified || md5Update) { LogUtil.DUMP_LOG.info("[dump-all] find change config {}, {}, md5={}", groupKey, cf.getLastModified(), cf.getMd5()); cf = configInfoPersistService.findConfigInfo(cf.getDataId(), cf.getGroup(), cf.getTenant()); } else { continue; } } if (cf == null) { continue; } if (cf.getDataId().equals(ClientIpWhiteList.CLIENT_IP_WHITELIST_METADATA)) { ClientIpWhiteList.load(cf.getContent()); } if (cf.getDataId().equals(SwitchService.SWITCH_META_DATA_ID)) { SwitchService.load(cf.getContent()); } final String content = cf.getContent(); final String dataId = cf.getDataId(); final String group = cf.getGroup(); final String tenant = cf.getTenant(); final long lastModified = cf.getLastModified(); final String type = cf.getType(); final String encryptedDataKey = cf.getEncryptedDataKey(); executorService.execute(() -> { final String md5Utf8 = MD5Utils.md5Hex(content, ENCODE_UTF8); boolean result = ConfigCacheService.dumpWithMd5(dataId, group, tenant, content, md5Utf8, lastModified, type, encryptedDataKey); if (result) { LogUtil.DUMP_LOG.info("[dump-all-ok] {}, {}, length={},md5UTF8={}", GroupKey2.getKey(dataId, group), lastModified, content.length(), md5Utf8); } else { LogUtil.DUMP_LOG.info("[dump-all-error] {}", GroupKey2.getKey(dataId, group)); } }); } long diskStamp = System.currentTimeMillis(); DEFAULT_LOG.info("[all-dump] submit all task for {} / {}, dbTime={},diskTime={}", lastMaxId, currentMaxId, (dbTimeStamp - start), (diskStamp - dbTimeStamp)); } //wait all task are finished and then shutdown executor. try { int unfinishedTaskCount = 0; while ((unfinishedTaskCount = executorService.getQueue().size() + executorService.getActiveCount()) > 0) { DEFAULT_LOG.info("[all-dump] wait {} dump tasks to be finished", unfinishedTaskCount); Thread.sleep(1000L); } executorService.shutdown(); } catch (Exception e) { DEFAULT_LOG.error("[all-dump] wait dump tasks to be finished error", e); } DEFAULT_LOG.info("success to dump all config-info。"); return true; } final ConfigInfoPersistService configInfoPersistService; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/processor/DumpProcessor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.processor; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.event.ConfigDumpEvent; import com.alibaba.nacos.config.server.service.dump.DumpConfigHandler; import com.alibaba.nacos.config.server.service.dump.task.DumpTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.LogUtil; import java.util.Objects; /** * dump processor. * * @author Nacos * @date 2020/7/5 12:19 PM */ public class DumpProcessor implements NacosTaskProcessor { final ConfigInfoPersistService configInfoPersistService; final ConfigInfoGrayPersistService configInfoGrayPersistService; public DumpProcessor(ConfigInfoPersistService configInfoPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService) { this.configInfoPersistService = configInfoPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; } @Override public boolean process(NacosTask task) { DumpTask dumpTask = (DumpTask) task; String[] pair = GroupKey2.parseKey(dumpTask.getGroupKey()); String dataId = pair[0]; String group = pair[1]; String tenant = pair[2]; long lastModifiedOut = dumpTask.getLastModified(); String handleIp = dumpTask.getHandleIp(); String grayName = dumpTask.getGrayName(); ConfigDumpEvent.ConfigDumpEventBuilder build = ConfigDumpEvent.builder().namespaceId(tenant).dataId(dataId) .group(group).grayName(grayName).handleIp(handleIp); String type = "formal"; if (StringUtils.isNotBlank(grayName)) { type = grayName; } LogUtil.DUMP_LOG.info("[dump] process {} task. groupKey={}", type, dumpTask.getGroupKey()); if (StringUtils.isNotBlank(grayName)) { ConfigInfoGrayWrapper cf = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, grayName); build.remove(Objects.isNull(cf)); build.content(Objects.isNull(cf) ? null : cf.getContent()); build.type(Objects.isNull(cf) ? null : cf.getType()); build.encryptedDataKey(Objects.isNull(cf) ? null : cf.getEncryptedDataKey()); build.lastModifiedTs(Objects.isNull(cf) ? lastModifiedOut : cf.getLastModified()); build.grayName(grayName); build.grayRule(Objects.isNull(cf) ? null : cf.getGrayRule()); return DumpConfigHandler.configDump(build.build()); } ConfigInfoWrapper cf = configInfoPersistService.findConfigInfo(dataId, group, tenant); build.remove(Objects.isNull(cf)); build.content(Objects.isNull(cf) ? null : cf.getContent()); build.type(Objects.isNull(cf) ? null : cf.getType()); build.encryptedDataKey(Objects.isNull(cf) ? null : cf.getEncryptedDataKey()); build.lastModifiedTs(Objects.isNull(cf) ? lastModifiedOut : cf.getLastModified()); return DumpConfigHandler.configDump(build.build()); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/task/DumpAllBetaTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.task; import com.alibaba.nacos.common.task.AbstractDelayTask; /** * Dump all beta task. * * @author Nacos * @date 2020/7/5 12:19 PM */ public class DumpAllBetaTask extends AbstractDelayTask { @Override public void merge(AbstractDelayTask task) { } public static final String TASK_ID = "dumpAllBetaConfigTask"; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/task/DumpAllGrayTask.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.task; import com.alibaba.nacos.common.task.AbstractDelayTask; /** * Dump all gray task. * * @author Nacos * @date 2024/3/5 */ public class DumpAllGrayTask extends AbstractDelayTask { @Override public void merge(AbstractDelayTask task) { } public static final String TASK_ID = "dumpAllGrayConfigTask"; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/task/DumpAllTagTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.task; import com.alibaba.nacos.common.task.AbstractDelayTask; /** * Dump all tag task. * * @author Nacos * @date 2020/7/5 12:19 PM */ public class DumpAllTagTask extends AbstractDelayTask { @Override public void merge(AbstractDelayTask task) { } public static final String TASK_ID = "dumpAllTagConfigTask"; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/task/DumpAllTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.task; import com.alibaba.nacos.common.task.AbstractDelayTask; /** * Dump all task. * * @author Nacos * @date 2020/7/5 12:17 PM */ public class DumpAllTask extends AbstractDelayTask { private boolean startUp; public DumpAllTask() { } public DumpAllTask(boolean startUp) { this.startUp = startUp; } public boolean isStartUp() { return startUp; } @Override public void merge(AbstractDelayTask task) { } public static final String TASK_ID = "dumpAllConfigTask"; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/dump/task/DumpTask.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.task; import com.alibaba.nacos.common.task.AbstractDelayTask; /** * Dump data task. * * @author Nacos */ public class DumpTask extends AbstractDelayTask { public DumpTask(String groupKey, String grayName, long lastModified, String handleIp) { this.groupKey = groupKey; this.lastModified = lastModified; this.handleIp = handleIp; this.grayName = grayName; //retry interval: 1s setTaskInterval(1000L); } @Override public void merge(AbstractDelayTask task) { } final String groupKey; final long lastModified; final String handleIp; final String grayName; public String getGroupKey() { return groupKey; } public long getLastModified() { return lastModified; } public String getHandleIp() { return handleIp; } public String getGrayName() { return grayName; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/listener/ConfigListenerStateDelegate.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.listener; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import org.springframework.stereotype.Service; /** * Delegate for Config Listener State Service. * * @author xiweng.yy */ @Service public class ConfigListenerStateDelegate { private final LocalConfigListenerStateServiceImpl localService; private final RemoteConfigListenerStateServiceImpl remoteService; public ConfigListenerStateDelegate(LocalConfigListenerStateServiceImpl localService, RemoteConfigListenerStateServiceImpl remoteService) { this.localService = localService; this.remoteService = remoteService; } public ConfigListenerInfo getListenerState(String dataId, String groupName, String namespaceId, boolean aggregation) { ConfigListenerInfo result = localService.getListenerState(dataId, groupName, namespaceId); if (aggregation) { result.getListenersStatus() .putAll(remoteService.getListenerState(dataId, groupName, namespaceId).getListenersStatus()); } return result; } public ConfigListenerInfo getListenerStateByIp(String ip, boolean aggregation) { ConfigListenerInfo result = localService.getListenerStateByIp(ip); if (aggregation) { result.getListenersStatus().putAll(remoteService.getListenerStateByIp(ip).getListenersStatus()); } return result; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/listener/ConfigListenerStateService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.listener; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; /** * Nacos config listener statues service. * * @author xiweng.yy */ public interface ConfigListenerStateService { /** * Get config listener state by dataId, groupName, namespaceId. * * @param dataId data id of config * @param groupName group name of config * @param namespaceId namespace id of config * @return listener state, include listener ip and config md5 */ ConfigListenerInfo getListenerState(String dataId, String groupName, String namespaceId); /** * Get config listener state by listener ip. * * @param ip listener ip * @return listener config information, include dataId, groupName, namespaceId and config md5 */ ConfigListenerInfo getListenerStateByIp(String ip); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/listener/LocalConfigListenerStateServiceImpl.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.listener; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.SampleResult; import com.alibaba.nacos.config.server.remote.ConfigChangeListenContext; import com.alibaba.nacos.config.server.service.LongPollingService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Local implementation for Config listener state service. * * @author xiweng.yy */ @Service public class LocalConfigListenerStateServiceImpl implements ConfigListenerStateService { private final LongPollingService longPollingService; private final ConfigChangeListenContext configChangeListenContext; private final ConnectionManager connectionManager; public LocalConfigListenerStateServiceImpl(LongPollingService longPollingService, ConfigChangeListenContext configChangeListenContext, ConnectionManager connectionManager) { this.longPollingService = longPollingService; this.configChangeListenContext = configChangeListenContext; this.connectionManager = connectionManager; } @Override public ConfigListenerInfo getListenerState(String dataId, String groupName, String namespaceId) { // long polling listeners for 1.x client TODO removed after 3.x not support 1.x client. SampleResult result = longPollingService.getCollectSubscribleInfo(dataId, groupName, namespaceId); // rpc listeners for upper 2.x client. String groupKey = GroupKey2.getKey(dataId, groupName, namespaceId); Set listenersClients = configChangeListenContext.getListeners(groupKey); if (CollectionUtils.isEmpty(listenersClients)) { return buildActualResult(result, ConfigListenerInfo.QUERY_TYPE_CONFIG); } Map listenersGroupkeyStatus = new HashMap<>(listenersClients.size(), 1); for (String connectionId : listenersClients) { Connection client = connectionManager.getConnection(connectionId); if (client != null) { String md5 = configChangeListenContext.getListenKeyMd5(connectionId, groupKey); if (md5 != null) { listenersGroupkeyStatus.put(client.getMetaInfo().getClientIp(), md5); } } } result.getLisentersGroupkeyStatus().putAll(listenersGroupkeyStatus); return buildActualResult(result, ConfigListenerInfo.QUERY_TYPE_CONFIG); } @Override public ConfigListenerInfo getListenerStateByIp(String ip) { // long polling listeners for 1.x client TODO removed after 3.x not support 1.x client. SampleResult result = longPollingService.getCollectSubscribleInfoByIp(ip); // rpc listeners for upper 2.x client. List connectionsByIp = connectionManager.getConnectionByIp(ip); for (Connection connectionByIp : connectionsByIp) { Map listenKeys = configChangeListenContext.getListenKeys( connectionByIp.getMetaInfo().getConnectionId()); if (listenKeys != null) { result.getLisentersGroupkeyStatus().putAll(listenKeys); } } return buildActualResult(result, ConfigListenerInfo.QUERY_TYPE_IP); } private ConfigListenerInfo buildActualResult(SampleResult sampleResult, String type) { ConfigListenerInfo result = new ConfigListenerInfo(); result.setQueryType(type); result.setListenersStatus(sampleResult.getLisentersGroupkeyStatus()); return result; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/listener/RemoteConfigListenerStateServiceImpl.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.listener; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.config.NacosAuthConfigHolder; import com.alibaba.nacos.auth.util.AuthHeaderUtil; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.service.notify.HttpClientManager; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.core.auth.NacosServerAuthConfig; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.sys.env.EnvUtil; import com.fasterxml.jackson.core.type.TypeReference; import org.springframework.stereotype.Service; import java.util.Collections; import java.util.HashMap; import static com.alibaba.nacos.common.constant.RequestUrlConstants.HTTP_PREFIX; /** * Local implementation for Config listener state service. * * @author xiweng.yy */ @Service public class RemoteConfigListenerStateServiceImpl implements ConfigListenerStateService { private static final String CONFIG_LISTENER_STATE_URL = Constants.CONFIG_ADMIN_V3_PATH + "/listener"; private final ServerMemberManager memberManager; private final ConfigListenerInfo emptyConfigListenerInfo; public RemoteConfigListenerStateServiceImpl(ServerMemberManager memberManager) { this.memberManager = memberManager; this.emptyConfigListenerInfo = new ConfigListenerInfo(); this.emptyConfigListenerInfo.setListenersStatus(Collections.emptyMap()); } @Override public ConfigListenerInfo getListenerState(String dataId, String groupName, String namespaceId) { Query query = Query.newInstance().addParam("dataId", dataId).addParam("groupName", groupName) .addParam("namespaceId", namespaceId).addParam("aggregation", false); Header header = buildHeader(); ConfigListenerInfo result = new ConfigListenerInfo(); result.setListenersStatus(new HashMap<>(16)); result.setQueryType(ConfigListenerInfo.QUERY_TYPE_CONFIG); for (Member each : memberManager.allMembersWithoutSelf()) { String url = getUrl(each.getAddress(), CONFIG_LISTENER_STATE_URL); ConfigListenerInfo oneNodeResult = invokeUrl(url, query, header); result.getListenersStatus().putAll(oneNodeResult.getListenersStatus()); } return result; } @Override public ConfigListenerInfo getListenerStateByIp(String ip) { Query query = Query.newInstance().addParam("ip", ip).addParam("aggregation", false); Header header = buildHeader(); ConfigListenerInfo result = new ConfigListenerInfo(); result.setListenersStatus(new HashMap<>(16)); result.setQueryType(ConfigListenerInfo.QUERY_TYPE_IP); for (Member each : memberManager.allMembersWithoutSelf()) { String url = getUrl(each.getAddress(), Constants.LISTENER_CONTROLLER_V3_ADMIN_PATH); ConfigListenerInfo oneNodeResult = invokeUrl(url, query, header); result.getListenersStatus().putAll(oneNodeResult.getListenersStatus()); } return result; } private String getUrl(String ip, String relativePath) { return HTTP_PREFIX + ip + EnvUtil.getContextPath() + relativePath; } private Header buildHeader() { Header header = Header.newInstance(); header.addParam(HttpHeaderConsts.ACCEPT_CHARSET, Constants.ENCODE_UTF8); NacosAuthConfig authConfig = NacosAuthConfigHolder.getInstance() .getNacosAuthConfigByScope(NacosServerAuthConfig.NACOS_SERVER_AUTH_SCOPE); AuthHeaderUtil.addIdentityToHeader(header, authConfig); return header; } private ConfigListenerInfo invokeUrl(String url, Query query, Header header) { try { RestResult restResult = HttpClientManager.getNacosRestTemplate() .get(url, header, query, String.class); if (!restResult.ok()) { LogUtil.DEFAULT_LOG.warn( "Invoke remote server config listener state by url {} failed with code {}, msg {}", url, restResult.getCode(), restResult.getMessage()); return emptyConfigListenerInfo; } Result result = JacksonUtils.toObj(restResult.getData(), new TypeReference<>() { }); return result.getData(); } catch (Exception e) { LogUtil.DEFAULT_LOG.error("Invoke remote server config listener by url {} failed :", url, e); return emptyConfigListenerInfo; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/notify/AsyncNotifyService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.notify; import com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest; import com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.common.task.AbstractDelayTask; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.remote.ConfigClusterRpcClientProxy; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.api.common.NodeState; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.sys.utils.InetUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.core.cluster.MemberMetaDataConstants.SUPPORT_GRAY_MODEL; /** * Async notify service. * * @author Nacos */ @Service public class AsyncNotifyService { private static final Logger LOGGER = LoggerFactory.getLogger(AsyncNotifyService.class); private static final int MIN_RETRY_INTERVAL = 500; private static final int INCREASE_STEPS = 1000; private static final int MAX_COUNT = 6; @Autowired private ConfigClusterRpcClientProxy configClusterRpcClientProxy; private ServerMemberManager memberManager; static final List HEALTHY_CHECK_STATUS = new ArrayList<>(); static { HEALTHY_CHECK_STATUS.add(NodeState.UP); HEALTHY_CHECK_STATUS.add(NodeState.SUSPICIOUS); } @Autowired public AsyncNotifyService(ServerMemberManager memberManager) { this.memberManager = memberManager; // Register ConfigDataChangeEvent to NotifyCenter. NotifyCenter.registerToPublisher(ConfigDataChangeEvent.class, NotifyCenter.ringBufferSize); // Register A Subscriber to subscribe ConfigDataChangeEvent. NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { // Generate ConfigDataChangeEvent concurrently handleConfigDataChangeEvent(event); } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); } void handleConfigDataChangeEvent(Event event) { if (event instanceof ConfigDataChangeEvent) { ConfigDataChangeEvent evt = (ConfigDataChangeEvent) event; MetricsMonitor.incrementConfigChangeCount(evt.tenant, evt.group, evt.dataId); Collection ipList = memberManager.allMembersWithoutSelf(); // In fact, any type of queue here can be Queue rpcQueue = new LinkedList<>(); for (Member member : ipList) { // grpc report data change only NotifySingleRpcTask notifySingleRpcTask = generateTask(evt, member); if (notifySingleRpcTask != null) { rpcQueue.add(notifySingleRpcTask); } } if (!rpcQueue.isEmpty()) { ConfigExecutor.executeAsyncNotify(new AsyncRpcTask(rpcQueue)); } } } private NotifySingleRpcTask generateTask(ConfigDataChangeEvent configDataChangeEvent, Member member) { NotifySingleRpcTask task = new NotifySingleRpcTask(configDataChangeEvent.dataId, configDataChangeEvent.group, configDataChangeEvent.tenant, configDataChangeEvent.grayName, configDataChangeEvent.lastModifiedTs, member); if (PropertyUtil.isGrayCompatibleModel() && StringUtils.isNotBlank(configDataChangeEvent.grayName)) { // old server should set beta or tag flag if (!(Boolean) member.getExtendInfo().getOrDefault(SUPPORT_GRAY_MODEL, Boolean.FALSE)) { String underLine = "_"; task.setBeta(BetaGrayRule.TYPE_BETA.equals(configDataChangeEvent.grayName)); if (configDataChangeEvent.grayName.startsWith(TagGrayRule.TYPE_TAG + underLine)) { task.setTag(configDataChangeEvent.grayName.substring( configDataChangeEvent.grayName.indexOf(TagGrayRule.TYPE_TAG + underLine) + 4)); } } } // compatible with gray model return task; } private boolean isUnHealthy(String targetIp) { return !memberManager.stateCheck(targetIp, HEALTHY_CHECK_STATUS); } void executeAsyncRpcTask(Queue queue) { while (!queue.isEmpty()) { NotifySingleRpcTask task = queue.poll(); ConfigChangeClusterSyncRequest syncRequest = new ConfigChangeClusterSyncRequest(); syncRequest.setDataId(task.getDataId()); syncRequest.setTenant(task.getTenant()); syncRequest.setGroup(task.getGroup()); syncRequest.setLastModified(task.getLastModified()); syncRequest.setGrayName(task.getGrayName()); syncRequest.setBeta(task.isBeta()); syncRequest.setTag(task.getTag()); Member member = task.member; String event = getNotifyEvent(task); if (memberManager.hasMember(member.getAddress())) { // start the health check and there are ips that are not monitored, put them directly in the notification queue, otherwise notify boolean unHealthNeedDelay = isUnHealthy(member.getAddress()); if (unHealthNeedDelay) { // target ip is unhealthy, then put it in the notification list ConfigTraceService.logNotifyEvent(task.getDataId(), task.getGroup(), task.getTenant(), null, task.getLastModified(), InetUtils.getSelfIP(), event, ConfigTraceService.NOTIFY_TYPE_UNHEALTH, 0, member.getAddress()); // get delay time and set fail count to the task asyncTaskExecute(task); } else { // grpc report data change only try { configClusterRpcClientProxy.syncConfigChange(member, syncRequest, new AsyncRpcNotifyCallBack(AsyncNotifyService.this, task)); } catch (Exception e) { MetricsMonitor.getConfigNotifyException().increment(); asyncTaskExecute(task); } } } else { //No nothing if member has offline. } } } public class AsyncRpcTask implements Runnable { private Queue queue; public AsyncRpcTask(Queue queue) { this.queue = queue; } @Override public void run() { executeAsyncRpcTask(queue); } } public static class NotifySingleRpcTask extends AbstractDelayTask { private String dataId; private String group; private String tenant; private long lastModified; private int failCount; private Member member; private String grayName; @Deprecated private boolean isBeta; @Deprecated private String tag; public NotifySingleRpcTask(String dataId, String group, String tenant, String grayName, long lastModified, Member member) { this.dataId = dataId; this.group = group; this.tenant = tenant; this.lastModified = lastModified; this.member = member; this.grayName = grayName; setTaskInterval(3000L); } public boolean isBeta() { return isBeta; } public void setBeta(boolean beta) { isBeta = beta; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public String getGrayName() { return grayName; } public void setGrayName(String grayName) { this.grayName = grayName; } public String getDataId() { return dataId; } public String getGroup() { return group; } public int getFailCount() { return failCount; } public void setFailCount(int failCount) { this.failCount = failCount; } public long getLastModified() { return lastModified; } @Override public void merge(AbstractDelayTask task) { // Perform merge, but do nothing, tasks with the same dataId and group, later will replace the previous } public String getTenant() { return tenant; } } private void asyncTaskExecute(NotifySingleRpcTask task) { int delay = getDelayTime(task); Queue queue = new LinkedList<>(); queue.add(task); AsyncRpcTask asyncTask = new AsyncRpcTask(queue); ConfigExecutor.scheduleAsyncNotify(asyncTask, delay, TimeUnit.MILLISECONDS); } private static String getNotifyEvent(NotifySingleRpcTask task) { String event = ConfigTraceService.NOTIFY_EVENT; if (task.isBeta()) { event = ConfigTraceService.NOTIFY_EVENT_BETA; } else if (!StringUtils.isBlank(task.tag)) { event = ConfigTraceService.NOTIFY_EVENT_TAG + "-" + task.tag; } else if (StringUtils.isNotBlank(task.grayName)) { event = ConfigTraceService.NOTIFY_EVENT + "-" + task.grayName; } return event; } public static class AsyncRpcNotifyCallBack implements RequestCallBack { private NotifySingleRpcTask task; AsyncNotifyService asyncNotifyService; public AsyncRpcNotifyCallBack(AsyncNotifyService asyncNotifyService, NotifySingleRpcTask task) { this.task = task; this.asyncNotifyService = asyncNotifyService; } @Override public Executor getExecutor() { return ConfigExecutor.getConfigSubServiceExecutor(); } @Override public long getTimeout() { return 1000L; } @Override public void onResponse(ConfigChangeClusterSyncResponse response) { String event = getNotifyEvent(task); long delayed = System.currentTimeMillis() - task.getLastModified(); if (response.isSuccess()) { ConfigTraceService.logNotifyEvent(task.getDataId(), task.getGroup(), task.getTenant(), null, task.getLastModified(), InetUtils.getSelfIP(), event, ConfigTraceService.NOTIFY_TYPE_OK, delayed, task.member.getAddress()); } else { LOGGER.error("[notify-error] target:{} dataId:{} group:{} ts:{} code:{}", task.member.getAddress(), task.getDataId(), task.getGroup(), task.getLastModified(), response.getErrorCode()); ConfigTraceService.logNotifyEvent(task.getDataId(), task.getGroup(), task.getTenant(), null, task.getLastModified(), InetUtils.getSelfIP(), event, ConfigTraceService.NOTIFY_TYPE_ERROR, delayed, task.member.getAddress()); //get delay time and set fail count to the task asyncNotifyService.asyncTaskExecute(task); LogUtil.NOTIFY_LOG.error("[notify-retry] target:{} dataId:{} group:{} ts:{}", task.member.getAddress(), task.getDataId(), task.getGroup(), task.getLastModified()); MetricsMonitor.getConfigNotifyException().increment(); } } @Override public void onException(Throwable ex) { String event = getNotifyEvent(task); long delayed = System.currentTimeMillis() - task.getLastModified(); LOGGER.error("[notify-exception] target:{} dataId:{} group:{} ts:{} ex:{}", task.member.getAddress(), task.getDataId(), task.getGroup(), task.getLastModified(), ex); ConfigTraceService.logNotifyEvent(task.getDataId(), task.getGroup(), task.getTenant(), null, task.getLastModified(), InetUtils.getSelfIP(), event, ConfigTraceService.NOTIFY_TYPE_EXCEPTION, delayed, task.member.getAddress()); //get delay time and set fail count to the task asyncNotifyService.asyncTaskExecute(task); LogUtil.NOTIFY_LOG.error("[notify-retry] target:{} dataId:{} group:{} ts:{}", task.member.getAddress(), task.getDataId(), task.getGroup(), task.getLastModified()); MetricsMonitor.getConfigNotifyException().increment(); } } /** * get delayTime and also set failCount to task; The failure time index increases, so as not to retry invalid tasks * in the offline scene, which affects the normal synchronization. * * @param task notify task * @return delay */ private static int getDelayTime(NotifySingleRpcTask task) { int failCount = task.getFailCount(); int delay = MIN_RETRY_INTERVAL + failCount * failCount * INCREASE_STEPS; if (failCount <= MAX_COUNT) { task.setFailCount(failCount + 1); } return delay; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/notify/HttpClientManager.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.notify; import com.alibaba.nacos.common.http.AbstractHttpClientFactory; import com.alibaba.nacos.common.http.HttpClientBeanHolder; import com.alibaba.nacos.common.http.HttpClientConfig; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.config.server.utils.PropertyUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * http client manager. * * @author mai.jh */ public final class HttpClientManager { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientManager.class); /** * Connection timeout and socket timeout with other servers. */ private static final int TIMEOUT = 500; private static final NacosRestTemplate NACOS_REST_TEMPLATE; private static final NacosAsyncRestTemplate NACOS_ASYNC_REST_TEMPLATE; static { // build nacos rest template NACOS_REST_TEMPLATE = HttpClientBeanHolder.getNacosRestTemplate(new ConfigHttpClientFactory(TIMEOUT, TIMEOUT)); NACOS_ASYNC_REST_TEMPLATE = HttpClientBeanHolder.getNacosAsyncRestTemplate( new ConfigHttpClientFactory(PropertyUtil.getNotifyConnectTimeout(), PropertyUtil.getNotifySocketTimeout())); ThreadUtils.addShutdownHook(HttpClientManager::shutdown); } public static NacosRestTemplate getNacosRestTemplate() { return NACOS_REST_TEMPLATE; } public static NacosAsyncRestTemplate getNacosAsyncRestTemplate() { return NACOS_ASYNC_REST_TEMPLATE; } private static void shutdown() { LOGGER.info("[ConfigServer-HttpClientManager] Start destroying NacosRestTemplate"); try { final String httpClientFactoryBeanName = ConfigHttpClientFactory.class.getName(); HttpClientBeanHolder.shutdownNacosSyncRest(httpClientFactoryBeanName); HttpClientBeanHolder.shutdownNacosAsyncRest(httpClientFactoryBeanName); } catch (Exception ex) { LOGGER.error("[ConfigServer-HttpClientManager] An exception occurred when the HTTP client was closed : {}", ExceptionUtil.getStackTrace(ex)); } LOGGER.info("[ConfigServer-HttpClientManager] Completed destruction of NacosRestTemplate"); } /** * http client factory. */ private static class ConfigHttpClientFactory extends AbstractHttpClientFactory { private final int conTimeOutMillis; private final int readTimeOutMillis; public ConfigHttpClientFactory(int conTimeOutMillis, int readTimeOutMillis) { this.conTimeOutMillis = conTimeOutMillis; this.readTimeOutMillis = readTimeOutMillis; } @Override protected HttpClientConfig buildHttpClientConfig() { return HttpClientConfig.builder().setConTimeOutMillis(conTimeOutMillis) .setReadTimeOutMillis(readTimeOutMillis).build(); } @Override protected Logger assignLogger() { return LOGGER; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/ConfigChainRequestExtractorService.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.config.server.exception.NacosConfigException; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Optional; /** * Service class for initializing and retrieving the configuration query request extractor. * * @author Nacos */ public class ConfigChainRequestExtractorService { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigChainRequestExtractorService.class); private static ConfigQueryChainRequestExtractor extractor; static { String curExtractor = EnvUtil.getProperty("nacos.config.query.chain.request.extractor", "nacos"); Optional optionalBuilder = NacosServiceLoader.load(ConfigQueryChainRequestExtractor.class) .stream() .filter(builder -> builder.getName().equals(curExtractor)) .findFirst(); if (optionalBuilder.isPresent()) { extractor = optionalBuilder.get(); LOGGER.info("ConfigQueryRequestExtractor has been initialized successfully with extractor: {}", curExtractor); } else { String errorMessage = "No suitable ConfigQueryRequestExtractor found for name: " + curExtractor; LOGGER.error(errorMessage); throw new NacosConfigException(errorMessage); } } public static ConfigQueryChainRequestExtractor getExtractor() { return extractor; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/ConfigQueryChainRequestExtractor.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import jakarta.servlet.http.HttpServletRequest; /** * Interface for extracting configuration query chain requests from different sources. * * @author Nacos */ public interface ConfigQueryChainRequestExtractor { /** * Gets the name of the current implementation. * * @return the name of the current implementation */ String getName(); /** * Extracts a configuration query chain request from an HTTP request. * * @param request the HTTP request object * @return the extracted configuration query chain request */ ConfigQueryChainRequest extract(HttpServletRequest request); /** * Extracts a configuration query chain request from a configuration query request object. * * @param request the configuration query request object * @param requestMeta the request metadata * @return the extracted configuration query chain request */ ConfigQueryChainRequest extract(ConfigQueryRequest request, RequestMeta requestMeta); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/ConfigQueryChainService.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.config.server.exception.NacosConfigException; import com.alibaba.nacos.config.server.service.query.enums.ResponseCode; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; import java.util.Optional; /** * Service class for initializing and retrieving the configuration query chain builder. * * @author Nacos */ @Service public class ConfigQueryChainService { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigQueryChainService.class); private final ConfigQueryHandlerChain chain; public ConfigQueryChainService() { String curChain = EnvUtil.getProperty("nacos.config.query.chain.builder", "nacos"); Optional optionalBuilder = NacosServiceLoader.load(ConfigQueryHandlerChainBuilder.class) .stream() .filter(builder -> builder.getName().equals(curChain)) .findFirst(); if (optionalBuilder.isPresent()) { chain = optionalBuilder.get().build(); LOGGER.info("ConfigQueryHandlerChain has been initialized successfully with chain: {}", curChain); } else { String errorMessage = "No suitable ConfigQueryHandlerChainBuilder found for name: " + curChain; LOGGER.error(errorMessage); throw new NacosConfigException(errorMessage); } } /** * Handles the configuration query request. * * @param request the configuration query request object * @return the configuration query response object */ public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) { try { return chain.handle(request); } catch (Exception e) { LOGGER.error("[Error] Fail to handle ConfigQueryChainRequest", e); return ConfigQueryChainResponse.buildFailResponse(ResponseCode.FAIL.getCode(), e.getMessage()); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/ConfigQueryHandlerChain.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; import com.alibaba.nacos.config.server.service.query.handler.ConfigQueryHandler; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.Objects; /** * ConfigQueryHandlerChain. * @author Nacos */ public class ConfigQueryHandlerChain { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigQueryHandlerChain.class); private ConfigQueryHandler head; private ConfigQueryHandler tail; public ConfigQueryHandlerChain() { } /** * Adds a new configuration query handler to the chain. * * @param handler the configuration query handler to be added * @return the current configuration query handler chain object, supporting method chaining */ public ConfigQueryHandlerChain addHandler(ConfigQueryHandler handler) { if (Objects.isNull(handler)) { LOGGER.warn("Attempted to add a null config query handler"); return this; } if (head == null) { head = handler; tail = handler; } else { tail.setNextHandler(handler); tail = handler; } return this; } public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException { return head.handle(request); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/ConfigQueryHandlerChainBuilder.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; /** * ConfigQueryHandlerChainBuilder. * * @author Nacos */ public interface ConfigQueryHandlerChainBuilder { /** * Builds the configuration query handler chain. * * @return the configuration query handler chain */ ConfigQueryHandlerChain build(); /** * Gets the name of the builder. * * @return the name of the builder */ String getName(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/DefaultChainRequestExtractor.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.utils.RequestUtil; import jakarta.servlet.http.HttpServletRequest; import java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; /** * DefaultChainRequestExtractor. * * @author Nacos */ public class DefaultChainRequestExtractor implements ConfigQueryChainRequestExtractor { @Override public String getName() { return "nacos"; } @Override public ConfigQueryChainRequest extract(HttpServletRequest request) { final String dataId = request.getParameter("dataId"); final String group = request.getParameter("group"); String tenant = request.getParameter("namespaceId") != null ? request.getParameter("namespaceId") : request.getParameter("tenant"); if (StringUtils.isBlank(tenant)) { tenant = StringUtils.EMPTY; } String tag = request.getParameter("tag"); String autoTag = request.getHeader(VIPSERVER_TAG); String clientIp = RequestUtil.getRemoteIp(request); Map appLabels = new HashMap<>(4); appLabels.put(BetaGrayRule.CLIENT_IP_LABEL, clientIp); if (StringUtils.isNotBlank(tag)) { appLabels.put(TagGrayRule.VIP_SERVER_TAG_LABEL, tag); } else if (StringUtils.isNotBlank(autoTag)) { appLabels.put(TagGrayRule.VIP_SERVER_TAG_LABEL, autoTag); } ConfigQueryChainRequest chainRequest = new ConfigQueryChainRequest(); chainRequest.setDataId(dataId); chainRequest.setGroup(group); chainRequest.setTenant(tenant); chainRequest.setTag(tag); chainRequest.setAppLabels(appLabels); return chainRequest; } @Override public ConfigQueryChainRequest extract(ConfigQueryRequest request, RequestMeta requestMeta) { ConfigQueryChainRequest chainRequest = new ConfigQueryChainRequest(); String tag = request.getTag(); Map appLabels = new HashMap<>(4); appLabels.put(BetaGrayRule.CLIENT_IP_LABEL, requestMeta.getClientIp()); if (StringUtils.isNotBlank(tag)) { appLabels.put(TagGrayRule.VIP_SERVER_TAG_LABEL, tag); } else { appLabels.putAll(requestMeta.getAppLabels()); } chainRequest.setDataId(request.getDataId()); chainRequest.setGroup(request.getGroup()); chainRequest.setTenant(request.getTenant()); chainRequest.setTag(request.getTag()); chainRequest.setAppLabels(appLabels); return chainRequest; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/DefaultConfigQueryHandlerChainBuilder.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; import com.alibaba.nacos.config.server.service.query.handler.ConfigChainEntryHandler; import com.alibaba.nacos.config.server.service.query.handler.ConfigContentTypeHandler; import com.alibaba.nacos.config.server.service.query.handler.FormalHandler; import com.alibaba.nacos.config.server.service.query.handler.GrayRuleMatchHandler; import com.alibaba.nacos.config.server.service.query.handler.SpecialTagNotFoundHandler; /** * DefaultConfigQueryHandlerChainBuilder. * * @author Nacos */ public class DefaultConfigQueryHandlerChainBuilder implements ConfigQueryHandlerChainBuilder { @Override public ConfigQueryHandlerChain build() { ConfigQueryHandlerChain chain = new ConfigQueryHandlerChain(); chain.addHandler(new ConfigChainEntryHandler()) .addHandler(new ConfigContentTypeHandler()) .addHandler(new GrayRuleMatchHandler()) .addHandler(new SpecialTagNotFoundHandler()) .addHandler(new FormalHandler()); return chain; } @Override public String getName() { return "nacos"; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/enums/ResponseCode.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.enums; /** * ResponseCode. * * @author Nacos */ public enum ResponseCode { /** * Request success. */ SUCCESS(200, "Response ok"), /** * Request failed. */ FAIL(500, "Response fail"); int code; String desc; ResponseCode(int code, String desc) { this.code = code; this.desc = desc; } /** * Getter method for property code. * * @return property value of code */ public int getCode() { return code; } /** * Getter method for property desc. * * @return property value of desc */ public String getDesc() { return desc; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/AbstractConfigQueryHandler.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; /** * AbstractConfigQueryHandler. This abstract class provides a base implementation for configuration query handlers. It * implements the {@link ConfigQueryHandler} interface and handles the chaining of handlers. * * @author Nacos */ public abstract class AbstractConfigQueryHandler implements ConfigQueryHandler { public ConfigQueryHandler nextHandler; @Override public ConfigQueryHandler getNextHandler() { return this.nextHandler; } @Override public void setNextHandler(ConfigQueryHandler nextHandler) { this.nextHandler = nextHandler; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/ConfigChainEntryHandler.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.config.server.utils.GroupKey2; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; /** * ConfigChainEntryHandler. * The entry point handler for the responsibility chain, responsible for initializing the chain and handling configuration query requests. * * @author Nacos */ public class ConfigChainEntryHandler extends AbstractConfigQueryHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigChainEntryHandler.class); private static final String CHAIN_ENTRY_HANDLER = "chainEntryHandler"; private static final ThreadLocal CACHE_ITEM_THREAD_LOCAL = new ThreadLocal<>(); @Override public String getName() { return CHAIN_ENTRY_HANDLER; } @Override public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException { request.setTenant(NamespaceUtil.processNamespaceParameter(request.getTenant())); String groupKey = GroupKey2.getKey(request.getDataId(), request.getGroup(), request.getTenant()); int lockResult = ConfigCacheService.tryConfigReadLock(groupKey); CacheItem cacheItem = ConfigCacheService.getContentCache(groupKey); if (lockResult > 0 && cacheItem != null) { try { CACHE_ITEM_THREAD_LOCAL.set(cacheItem); if (nextHandler != null) { return nextHandler.handle(request); } else { LOGGER.warn("chainEntryHandler's next handler is null"); return new ConfigQueryChainResponse(); } } finally { CACHE_ITEM_THREAD_LOCAL.remove(); ConfigCacheService.releaseReadLock(groupKey); } } else if (lockResult == 0 || cacheItem == null) { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); return response; } else { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_QUERY_CONFLICT); return response; } } public static CacheItem getThreadLocalCacheItem() { return CACHE_ITEM_THREAD_LOCAL.get(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/ConfigContentTypeHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.enums.FileTypeEnum; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import java.io.IOException; /** * The type Config content type handler. * @author Sunrisea */ public class ConfigContentTypeHandler extends AbstractConfigQueryHandler { private static final String CONFIG_CONTENT_TYPE_HANDLER_NAME = "ConfigContentTypeHandler"; @Override public String getName() { return CONFIG_CONTENT_TYPE_HANDLER_NAME; } @Override public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException { ConfigQueryChainResponse response = getNextHandler().handle(request); if (response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND || response.getStatus() == ConfigQueryChainResponse.ConfigQueryStatus.SPECIAL_TAG_CONFIG_NOT_FOUND) { return response; } String contentType = response.getContentType() != null ? response.getContentType() : FileTypeEnum.TEXT.getFileType(); FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(contentType); String contentTypeHeader = fileTypeEnum.getContentType(); response.setContentType(contentTypeHeader); return response; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/ConfigQueryHandler.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import java.io.IOException; /** * Configuration Query Handler Interface. * This interface defines the standard methods for handling configuration query requests. * * @author Nacos */ public interface ConfigQueryHandler { /** * Gets the name of the handler. * @return The name of the handler. */ String getName(); /** * Handles the configuration query request. * If the current handler cannot process the request, it should throw an IOException. * @param request The configuration query request. * @return The response to the configuration query. * @throws IOException If an I/O error occurs. */ ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException; /** * Sets the next handler in the chain. * @param nextHandler The next handler to which the request can be passed if the current handler cannot process it. */ void setNextHandler(ConfigQueryHandler nextHandler); /** * Gets the next handler in the chain. * @return The next handler. */ ConfigQueryHandler getNextHandler(); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/FormalHandler.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import java.io.IOException; /** * Formal Handler. This class represents a formal handler in the configuration query processing chain. If the request * has not been processed by previous handlers, it will be handled by this handler. * * @author Nacos */ public class FormalHandler extends AbstractConfigQueryHandler { private static final String FORMAL_HANDLER = "formalHandler"; @Override public String getName() { return FORMAL_HANDLER; } @Override public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); String dataId = request.getDataId(); String group = request.getGroup(); String tenant = request.getTenant(); CacheItem cacheItem = ConfigChainEntryHandler.getThreadLocalCacheItem(); String md5 = cacheItem.getConfigCache().getMd5(); String content = ConfigDiskServiceFactory.getInstance().getContent(dataId, group, tenant); if (StringUtils.isBlank(content)) { response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); return response; } long lastModified = cacheItem.getConfigCache().getLastModifiedTs(); String encryptedDataKey = cacheItem.getConfigCache().getEncryptedDataKey(); String configType = cacheItem.getType(); response.setContent(content); response.setMd5(md5); response.setLastModified(lastModified); response.setEncryptedDataKey(encryptedDataKey); response.setConfigType(configType); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); return response; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/GrayRuleMatchHandler.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import java.io.IOException; /** * GrayRuleMatchHandler. This class represents a gray rule handler in the configuration query processing chain. It * checks if the request matches any gray rules and processes the request accordingly. * * @author Nacos */ public class GrayRuleMatchHandler extends AbstractConfigQueryHandler { private static final String GRAY_RULE_MATCH_HANDLER = "grayRuleMatchHandler"; @Override public String getName() { return GRAY_RULE_MATCH_HANDLER; } @Override public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException { // Check if the request matches any gray rules CacheItem cacheItem = ConfigChainEntryHandler.getThreadLocalCacheItem(); ConfigCacheGray matchedGray = null; if (cacheItem.getSortConfigGrays() != null && !cacheItem.getSortConfigGrays().isEmpty()) { for (ConfigCacheGray configCacheGray : cacheItem.getSortConfigGrays()) { if (configCacheGray.match(request.getAppLabels())) { matchedGray = configCacheGray; break; } } } if (matchedGray != null) { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); long lastModified = matchedGray.getLastModifiedTs(); String md5 = matchedGray.getMd5(); String encryptedDataKey = matchedGray.getEncryptedDataKey(); String content = ConfigDiskServiceFactory.getInstance() .getGrayContent(request.getDataId(), request.getGroup(), request.getTenant(), matchedGray.getGrayName()); response.setContent(content); response.setMd5(md5); response.setLastModified(lastModified); response.setEncryptedDataKey(encryptedDataKey); response.setMatchedGray(matchedGray); response.setConfigType(cacheItem.getType()); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY); return response; } else { return nextHandler.handle(request); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/handler/SpecialTagNotFoundHandler.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import java.io.IOException; /** * SpecialTagNotFound Handler. * This class represents special tag not found handler in the configuration query processing chain. * * @author Nacos */ public class SpecialTagNotFoundHandler extends AbstractConfigQueryHandler { private static final String SPECIAL_TAG_NOT_FOUND_HANDLER = "specialTagNotFoundHandler"; @Override public String getName() { return SPECIAL_TAG_NOT_FOUND_HANDLER; } @Override public ConfigQueryChainResponse handle(ConfigQueryChainRequest request) throws IOException { if (StringUtils.isNotBlank(request.getTag())) { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.SPECIAL_TAG_CONFIG_NOT_FOUND); return response; } else { return nextHandler.handle(request); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/model/ConfigQueryChainRequest.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.model; import java.util.Map; import java.util.Objects; /** * ConfigQueryChainRequest. * * @author Nacos */ public class ConfigQueryChainRequest { private String dataId; private String group; private String tenant; private String tag; private Map appLabels; public String getDataId() { return dataId; } public void setDataId(String dataId) { this.dataId = dataId; } public String getGroup() { return group; } public void setGroup(String group) { this.group = group; } public String getTenant() { return tenant; } public void setTenant(String tenant) { this.tenant = tenant; } public String getTag() { return tag; } public void setTag(String tag) { this.tag = tag; } public Map getAppLabels() { return appLabels; } public void setAppLabels(Map appLabels) { this.appLabels = appLabels; } /** * buildConfigQueryChainRequest. * * @param dataId dataId * @param groupName groupName * @param namespaceId namespaceId * @return ConfigQueryChainRequest */ public static ConfigQueryChainRequest buildConfigQueryChainRequest(String dataId, String groupName, String namespaceId) { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId(dataId); request.setGroup(groupName); request.setTenant(namespaceId); return request; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigQueryChainRequest that = (ConfigQueryChainRequest) o; return Objects.equals(dataId, that.dataId) && Objects.equals(group, that.group) && Objects.equals(tenant, that.tenant) && Objects.equals(tag, that.tag) && Objects.equals(appLabels, that.appLabels); } @Override public int hashCode() { return Objects.hash(dataId, group, tenant, tag, appLabels); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/query/model/ConfigQueryChainResponse.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.model; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.service.query.enums.ResponseCode; import java.util.Objects; /** * ConfigQueryChainResponse. * * @author Nacos */ public class ConfigQueryChainResponse { private String content; private String contentType; private String configType; private String encryptedDataKey; private String md5; private long lastModified; private ConfigCacheGray matchedGray; private int resultCode; private String message; private ConfigQueryStatus status; public enum ConfigQueryStatus { /** * Indicates that the configuration was found and is formal. */ CONFIG_FOUND_FORMAL, /** * Indicates that the configuration was found and is gray. */ CONFIG_FOUND_GRAY, /** * Indicates that the configuration special tag was not found. */ SPECIAL_TAG_CONFIG_NOT_FOUND, /** * Indicates that the configuration was not found. */ CONFIG_NOT_FOUND, /** * Indicates a conflict in the configuration query. */ CONFIG_QUERY_CONFLICT, } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getContentType() { return contentType; } public void setContentType(String contentType) { this.contentType = contentType; } public String getConfigType() { return configType; } public void setConfigType(String configType) { this.configType = configType; } public String getEncryptedDataKey() { return encryptedDataKey; } public void setEncryptedDataKey(String encryptedDataKey) { this.encryptedDataKey = encryptedDataKey; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public long getLastModified() { return lastModified; } public void setLastModified(long lastModified) { this.lastModified = lastModified; } public ConfigCacheGray getMatchedGray() { return matchedGray; } public void setMatchedGray(ConfigCacheGray matchedGray) { this.matchedGray = matchedGray; } public int getResultCode() { return resultCode; } public void setResultCode(int resultCode) { this.resultCode = resultCode; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public ConfigQueryStatus getStatus() { return status; } public void setStatus(ConfigQueryStatus status) { this.status = status; } /** * Build fail response. * * @param errorCode errorCode. * @param message message. * @return response. */ public static ConfigQueryChainResponse buildFailResponse(int errorCode, String message) { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setErrorInfo(errorCode, message); return response; } public void setErrorInfo(int errorCode, String errorMsg) { this.resultCode = ResponseCode.FAIL.getCode(); this.message = errorMsg; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ConfigQueryChainResponse that = (ConfigQueryChainResponse) o; return lastModified == that.lastModified && Objects.equals(content, that.content) && Objects.equals(contentType, that.contentType) && Objects.equals(encryptedDataKey, that.encryptedDataKey) && Objects.equals(md5, that.md5) && Objects.equals(matchedGray, that.matchedGray) && Objects.equals(resultCode, that.resultCode) && Objects.equals(message, that.message) && status == that.status; } @Override public int hashCode() { return Objects.hash(content, contentType, encryptedDataKey, md5, lastModified, matchedGray, resultCode, message, status); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/ConfigInfoBetaPersistService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfo4Beta; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; /** * Database service, providing access to config_info_beta in the database. * Deprecated since 2.5.0,only support on compatibility,replaced with ConfigInfoGray model, will be soon removed on further version. * @author lixiaoshuang */ @Deprecated public interface ConfigInfoBetaPersistService { /** * create Pagination utils. * * @param Generic object * @return {@link PaginationHelper} */ PaginationHelper createPaginationHelper(); //------------------------------------------insert---------------------------------------------// /** * get config info beta. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @return config info state. */ ConfigInfoStateWrapper findConfigInfo4BetaState(final String dataId, final String group, final String tenant); /** * Add beta configuration information and publish data change events. * * @param configInfo config info * @param betaIps ip for push * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult addConfigInfo4Beta(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser); /** * insert or update beta config. * * @param configInfo config info * @param betaIps ip for push * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult insertOrUpdateBeta(final ConfigInfo configInfo, final String betaIps, final String srcIp, final String srcUser); /** * insert or update beta config cas. * * @param configInfo config info * @param betaIps ip for push * @param srcIp remote ip * @param srcUser user * @return success or not. */ ConfigOperateResult insertOrUpdateBetaCas(final ConfigInfo configInfo, final String betaIps, final String srcIp, final String srcUser); //------------------------------------------delete---------------------------------------------// /** * Delete configuration information, physical deletion. * * @param dataId data id * @param group group * @param tenant tenant */ void removeConfigInfo4Beta(final String dataId, final String group, final String tenant); //------------------------------------------update---------------------------------------------// /** * Update beta configuration information. * * @param configInfo config info * @param betaIps ip for push * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult updateConfigInfo4Beta(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser); /** * Update beta configuration information. * * @param configInfo config info * @param betaIps ip for push * @param srcIp remote ip * @param srcUser user * @return success or not. */ ConfigOperateResult updateConfigInfo4BetaCas(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser); //------------------------------------------select---------------------------------------------// /** * Query beta configuration information based on dataId and group. * * @param dataId data id * @param group group * @param tenant tenant * @return {@link ConfigInfo4Beta} */ ConfigInfoBetaWrapper findConfigInfo4Beta(final String dataId, final String group, final String tenant); /** * Returns the number of beta configuration items. * * @return number of configuration items.. */ int configInfoBetaCount(); /** * Query all beta config info for dump task. * * @param pageNo page number * @param pageSize page size * @return {@link Page} with {@link ConfigInfoWrapper} generation */ Page findAllConfigInfoBetaForDumpAll(final int pageNo, final int pageSize); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/ConfigInfoGrayPersistService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; import java.sql.Timestamp; import java.util.List; /** * Database service, providing access to config_info_gray in the database. * * @author rong */ public interface ConfigInfoGrayPersistService { /** * create Pagination utils. * * @param Generic object * @return {@link PaginationHelper} */ PaginationHelper createPaginationHelper(); //------------------------------------------insert---------------------------------------------// /** * get gray config info state. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param grayName gray name. * @return config info state. */ ConfigInfoStateWrapper findConfigInfo4GrayState(final String dataId, final String group, final String tenant, String grayName); /** * Add gray configuration information and publish data change events. * * @param configInfo config info * @param grayName gray name * @param grayRule gray rule * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult addConfigInfo4Gray(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser); /** * Adds configuration information with database atomic operations, minimizing SQL actions and avoiding business * encapsulation. * * @param configGrayId the ID for the gray configuration * @param configInfo the configuration information to be added * @param grayName the name of the gray configuration * @param grayRule the rule of the gray configuration * @param srcIp the IP address of the source * @param srcUser the user who performs the addition */ void addConfigInfoGrayAtomic(final long configGrayId, final ConfigInfo configInfo, final String grayName, final String grayRule, final String srcIp, final String srcUser); /** * insert or update gray config. * * @param configInfo config info * @param grayName gray name * @param grayRule gray rule * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult insertOrUpdateGray(final ConfigInfo configInfo, final String grayName, final String grayRule, final String srcIp, final String srcUser); /** * insert or update gray config cas. * * @param configInfo config info. * @param grayName gray name * @param grayRule gray rule * @param srcIp remote ip. * @param srcUser user. * @return config operation result. */ ConfigOperateResult insertOrUpdateGrayCas(final ConfigInfo configInfo, final String grayName, final String grayRule, final String srcIp, final String srcUser); //------------------------------------------delete---------------------------------------------// /** * Delete configuration; database atomic operation, minimum SQL action, no business encapsulation. * * @param dataId dataId * @param group group * @param tenant tenant * @param grayName gray name * @param srcIp remote ip * @param srcUser user */ void removeConfigInfoGray(final String dataId, final String group, final String tenant, final String grayName, final String srcIp, final String srcUser); //------------------------------------------update---------------------------------------------// /** * Update gray configuration information. * * @param configInfo config info * @param grayName gray name * @param grayRule gray rule * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult updateConfigInfo4Gray(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser); /** * Update gray configuration information. * * @param configInfo config info * @param grayName gray name * @param grayRule gray rule * @param srcIp remote ip * @param srcUser user * @return success or not. */ ConfigOperateResult updateConfigInfo4GrayCas(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser); //------------------------------------------select---------------------------------------------// /** * Query gray configuration information based on dataId and group. * * @param dataId data id * @param group group * @param tenant tenant * @param grayName gray name * @return ConfigInfoGrayWrapper gray model instance. */ ConfigInfoGrayWrapper findConfigInfo4Gray(final String dataId, final String group, final String tenant, final String grayName); /** * Returns the number of gray configuration items. * * @return number of configuration items. */ int configInfoGrayCount(); /** * Query all gray config info for dump task. * * @param pageNo page numbser * @param pageSize page sizxe * @return {@link Page} with {@link ConfigInfoGrayWrapper} generation */ Page findAllConfigInfoGrayForDumpAll(final int pageNo, final int pageSize); /** * Query all gray config info for dump task. * * @param startTime startTime * @param lastMaxId lastMaxId * @param pageSize pageSize * @return {@link Page} with {@link ConfigInfoGrayWrapper} generation */ List findChangeConfig(final Timestamp startTime, long lastMaxId, final int pageSize); /** * found all config grays. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @return */ List findConfigInfoGrays(final String dataId, final String group, final String tenant); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/ConfigInfoPersistService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoBase; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.persistence.repository.PaginationHelper; import java.sql.Timestamp; import java.util.List; import java.util.Map; /** * Database service, providing access to config_info in the database. * * @author lixiaoshuang */ public interface ConfigInfoPersistService { String PATTERN_STR = "*"; Object[] EMPTY_ARRAY = new Object[] {}; /** * create Pagination utils. * * @param Generic object * @return {@link PaginationHelper} */ PaginationHelper createPaginationHelper(); /** * Generate fuzzy search Sql. * * @param s origin string * @return fuzzy search Sql */ String generateLikeArgument(String s); //------------------------------------------insert---------------------------------------------// /** * Add common configuration information and publish data change events. * * @param srcIp remote ip * @param srcUser user * @param configInfo config info * @param configAdvanceInfo advance info * @return config operation result. */ ConfigOperateResult addConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo, final Map configAdvanceInfo); /** * Update config info metadata config operate result. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param configTags the config tags * @param description the description * @return the config operate result * @throws NacosException the nacos exception */ ConfigOperateResult updateConfigInfoMetadata(final String dataId, final String group, final String tenant, final String configTags, final String description) throws NacosException; /** * insert or update. * * @param srcIp remote ip * @param srcUser user * @param configInfo config info * @param configAdvanceInfo advance info * @return config operation result. */ ConfigOperateResult insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Map configAdvanceInfo); /** * Write to the main table, insert or update cas. * * @param srcIp remote ip * @param srcUser user * @param configInfo config info * @param configAdvanceInfo advance info * @return success or not. */ ConfigOperateResult insertOrUpdateCas(String srcIp, String srcUser, ConfigInfo configInfo, Map configAdvanceInfo); /** * Add configuration; database atomic operation, minimum sql action, no business encapsulation. * * @param id id * @param srcIp ip * @param srcUser user * @param configInfo info * @param configAdvanceInfo advance info * @return execute sql result */ long addConfigInfoAtomic(final long id, final String srcIp, final String srcUser, final ConfigInfo configInfo, Map configAdvanceInfo); /** * Add configuration; database atomic operation, minimum sql action, no business encapsulation. * * @param configId id * @param tagName tag * @param dataId data id * @param group group * @param tenant tenant */ void addConfigTagRelationAtomic(long configId, String tagName, String dataId, String group, String tenant); /** * Add configuration; database atomic operation. * * @param configId config id * @param configTags tags * @param dataId dataId * @param group group * @param tenant tenant */ void addConfigTagsRelation(long configId, String configTags, String dataId, String group, String tenant); /** * batch operation,insert or update the format of the returned: succCount: number of successful imports skipCount: * number of import skips (only with skip for the same configs) failData: import failed data (only with abort for * the same configs) skipData: data skipped at import (only with skip for the same configs). * * @param configInfoList config info list * @param srcUser user * @param srcIp remote ip * @param configAdvanceInfo advance info * @param policy {@link SameConfigPolicy} * @return map containing the number of affected rows * @throws NacosException nacos exception */ Map batchInsertOrUpdate(List configInfoList, String srcUser, String srcIp, Map configAdvanceInfo, SameConfigPolicy policy) throws NacosException; //------------------------------------------delete---------------------------------------------// /** * Delete configuration information, physical deletion. * * @param dataId data id * @param group group * @param tenant tenant * @param srcIp remote ip * @param srcUser user */ void removeConfigInfo(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser); /** * Delete config info by ids. * * @param ids id list * @param srcIp remote ip * @param srcUser user * @return {@link ConfigAllInfo} list * @author klw */ @Deprecated List removeConfigInfoByIds(final List ids, final String srcIp, final String srcUser); /** * Delete tag. * * @param id id */ void removeTagByIdAtomic(long id); /** * Remove configuration; database atomic operation, minimum SQL action, no business encapsulation. * * @param dataId dataId * @param group group * @param tenant tenant * @param srcIp ip * @param srcUser user */ void removeConfigInfoAtomic(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser); /** * Remove configuration; database atomic operation, minimum SQL action, no business encapsulation. * * @param ids ids */ void removeConfigInfoByIdsAtomic(final String ids); //------------------------------------------update---------------------------------------------// /** * Update common configuration information. * * @param configInfo config info * @param srcIp remote ip * @param srcUser user * @param configAdvanceInfo advance info * @return config operation result. */ ConfigOperateResult updateConfigInfo(final ConfigInfo configInfo, final String srcIp, final String srcUser, final Map configAdvanceInfo); /** * Update common configuration information. * * @param configInfo config info * @param srcIp remote ip * @param srcUser user * @param configAdvanceInfo advance info * @return config operation result. */ ConfigOperateResult updateConfigInfoCas(final ConfigInfo configInfo, final String srcIp, final String srcUser, final Map configAdvanceInfo); /** * Update configuration; database atomic operation, minimum SQL action, no business encapsulation. * * @param configInfo config info * @param srcIp remote ip * @param srcUser user * @param configAdvanceInfo advance info */ void updateConfigInfoAtomic(final ConfigInfo configInfo, final String srcIp, final String srcUser, Map configAdvanceInfo); //------------------------------------------select---------------------------------------------// /** * Get the maxId. * * @return config max id */ long findConfigMaxId(); /** * Query configuration information by primary key ID. * * @param id id * @return {@link ConfigInfo} */ ConfigInfo findConfigInfo(long id); /** * Query configuration information; database atomic operation, minimum SQL action, no business encapsulation. * * @param dataId dataId * @param group group * @param tenant tenant * @return config info */ ConfigInfoWrapper findConfigInfo(final String dataId, final String group, final String tenant); /** * find config info. * * @param pageNo page number * @param pageSize page size * @param dataId data id * @param group group * @param tenant tenant * @param configAdvanceInfo advance info * @return {@link Page} with {@link ConfigInfo} generation */ Page findConfigInfo4Page(final int pageNo, final int pageSize, final String dataId, final String group, final String tenant, final Map configAdvanceInfo); /** * Returns the number of configuration items. * * @return number of configuration items. */ int configInfoCount(); /** * Returns the number of configuration items. * * @param tenant tenant * @return number of configuration items. */ int configInfoCount(String tenant); /** * get tenant id list by page. * * @param page page number * @param pageSize page size * @return tenant id list */ List getTenantIdList(int page, int pageSize); /** * get group id list by page. * * @param page page number * @param pageSize page size * @return group id list */ List getGroupIdList(int page, int pageSize); /** * Query all config info. * * @param lastMaxId last max id * @param pageSize page size * @param needContent need content or not. * @return {@link Page} with {@link ConfigInfoWrapper} generation */ Page findAllConfigInfoFragment(final long lastMaxId, final int pageSize, boolean needContent); /** * Query config info. * * @param pageNo page number * @param pageSize page size * @param dataId data id * @param group group * @param tenant tenant * @param configAdvanceInfo advance info * @return {@link Page} with {@link ConfigInfo} generation */ Page findConfigInfoLike4Page(final int pageNo, final int pageSize, final String dataId, final String group, final String tenant, final Map configAdvanceInfo); /** * Query change config.order by id asc. * * @param startTime start time * @param lastMaxId lastMaxId * @param pageSize pageSize * @return {@link ConfigInfoWrapper} list */ List findChangeConfig(final Timestamp startTime, long lastMaxId, final int pageSize); /** * Query tag list. * * @param dataId data id * @param group group * @param tenant tenant * @return tag list */ List selectTagByConfig(String dataId, String group, String tenant); /** * find ConfigInfo by ids. * * @param ids id list * @return {@link com.alibaba.nacos.config.server.model.ConfigInfo} list * @author klw * @date 2019/7/5 16:37 */ List findConfigInfosByIds(final String ids); /** * Query configuration information; database atomic operation, minimum SQL action, no business encapsulation. * * @param dataId dataId * @param group group * @param tenant tenant * @return advance info */ ConfigAdvanceInfo findConfigAdvanceInfo(final String dataId, final String group, final String tenant); /** * Query configuration information; database atomic operation, minimum SQL action, no business encapsulation. * * @param dataId dataId * @param group group * @param tenant tenant * @return advance info */ ConfigAllInfo findConfigAllInfo(final String dataId, final String group, final String tenant); /** * get config info state. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @return config info state. */ ConfigInfoStateWrapper findConfigInfoState(final String dataId, final String group, final String tenant); /** * query all configuration information according to group, appName, tenant (for export). * * @param dataId data id * @param group group * @param tenant tenant * @param appName appName * @param ids ids * @return Collection of ConfigInfo objects */ List findAllConfigInfo4Export(final String dataId, final String group, final String tenant, final String appName, final List ids); /** * Query dataId list by namespace. * * @param tenantId tenantId * @return {@link ConfigInfoBase} */ List queryConfigInfoByNamespace(final String tenantId); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/ConfigInfoTagPersistService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfo4Tag; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; import java.util.List; /** * Database service, providing access to config_info_tag in the database. * Deprecated since 2.5.0,only support on compatibility,replaced with ConfigInfoGray model, will be soon removed on further version. * @author lixiaoshuang */ @Deprecated public interface ConfigInfoTagPersistService { /** * create Pagination utils. * * @param Generic object * @return {@link PaginationHelper} */ PaginationHelper createPaginationHelper(); //------------------------------------------insert---------------------------------------------// /** * get config info state. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @param tag tag. * @return config info state. */ ConfigInfoStateWrapper findConfigInfo4TagState(final String dataId, final String group, final String tenant, String tag); /** * Add tag configuration information and publish data change events. * * @param configInfo config info * @param tag tag * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult addConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser); /** * insert or update tag config. * * @param configInfo config info * @param tag tag * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult insertOrUpdateTag(final ConfigInfo configInfo, final String tag, final String srcIp, final String srcUser); /** * insert or update tag config cas. * * @param configInfo config info. * @param tag tag. * @param srcIp remote ip. * @param srcUser user. * @return config operation result. */ ConfigOperateResult insertOrUpdateTagCas(final ConfigInfo configInfo, final String tag, final String srcIp, final String srcUser); //------------------------------------------delete---------------------------------------------// /** * Delete configuration; database atomic operation, minimum SQL action, no business encapsulation. * * @param dataId dataId * @param group group * @param tenant tenant * @param tag tag * @param srcIp remote ip * @param srcUser user */ void removeConfigInfoTag(final String dataId, final String group, final String tenant, final String tag, final String srcIp, final String srcUser); //------------------------------------------update---------------------------------------------// /** * Update tag configuration information. * * @param configInfo config info * @param tag tag * @param srcIp remote ip * @param srcUser user * @return config operation result. */ ConfigOperateResult updateConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser); /** * Update tag configuration information. * * @param configInfo config info * @param tag tag * @param srcIp remote ip * @param srcUser user * @return success or not. */ ConfigOperateResult updateConfigInfo4TagCas(ConfigInfo configInfo, String tag, String srcIp, String srcUser); //------------------------------------------select---------------------------------------------// /** * Query tag configuration information based on dataId and group. * * @param dataId data id * @param group group * @param tenant tenant * @param tag tag * @return {@link ConfigInfo4Tag} */ ConfigInfoTagWrapper findConfigInfo4Tag(final String dataId, final String group, final String tenant, final String tag); /** * Returns the number of beta configuration items. * * @return number of configuration items.. */ int configInfoTagCount(); /** * Query all tag config info for dump task. * * @param pageNo page numbser * @param pageSize page sizxe * @return {@link Page} with {@link ConfigInfoWrapper} generation */ Page findAllConfigInfoTagForDumpAll(final int pageNo, final int pageSize); /** * found all config tags. * * @param dataId dataId. * @param group group. * @param tenant tenant. * @return */ List findConfigInfoTags(final String dataId, final String group, final String tenant); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/ConfigMigratePersistService.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.persistence.repository.PaginationHelper; import java.util.List; /** * The interface Config migrate persist service. * * @author Sunrisea */ public interface ConfigMigratePersistService { /** * Create pagination helper pagination helper. * * @param the type parameter * @return the pagination helper */ PaginationHelper createPaginationHelper(); /** * Config info conflict count integer. * * @param srcUser the src user * @return the integer */ Integer configInfoConflictCount(String srcUser); /** * Config info gray conflict count integer. * * @param srcUser the src user * @return the integer */ Integer configInfoGrayConflictCount(String srcUser); /** * Gets migrate config id list. * * @param startId the start id * @param pageSize the page size * @return the migrate config id list */ List getMigrateConfigInsertIdList(long startId, int pageSize); /** * Gets migrate config gray id list. * * @param startId the start id * @param pageSize the page size * @return the migrate config gray id list */ List getMigrateConfigGrayInsertIdList(long startId, int pageSize); /** * Gets migrate config update list. * * @param startId the start id * @param pageSize the page size * @param srcTenant the src tenant * @param targetTenant the target tenant * @param srcUser the src user * @return the migrate config update list */ List getMigrateConfigUpdateList(long startId, int pageSize, String srcTenant, String targetTenant, String srcUser); /** * Gets migrate config gray update list. * * @param startId the start id * @param pageSize the page size * @param srcTenant the src tenant * @param targetTenant the target tenant * @param srcUser the src user * @return the migrate config gray update list */ List getMigrateConfigGrayUpdateList(long startId, int pageSize, String srcTenant, String targetTenant, String srcUser); /** * Migrate config by ids. * * @param ids the ids * @param srcUser the src user */ void migrateConfigInsertByIds(List ids, String srcUser); /** * Migrate config gray by ids. * * @param ids the ids * @param srcUser the src user */ void migrateConfigGrayInsertByIds(List ids, String srcUser); /** * Sync config gray. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param grayName the gray name * @param targetTenant the target tenant * @param srcUser the src user */ void syncConfigGray(String dataId, String group, String tenant, String grayName, String targetTenant, String srcUser); /** * Sync config. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param targetTenant the target tenant * @param srcUser the src user */ void syncConfig(String dataId, String group, String tenant, String targetTenant, String srcUser); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfo4Beta; import com.alibaba.nacos.config.server.model.ConfigInfo4Tag; import com.alibaba.nacos.config.server.model.ConfigInfoBase; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoChanged; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigKey; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.persistence.repository.RowMapperManager; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Component; import java.sql.ResultSet; import java.sql.SQLException; /** * Config row mapper injector. * * @author xiweng.yy */ @Component public class ConfigRowMapperInjector { public static final RowMapper CONFIG_INFO_WRAPPER_ROW_MAPPER = new ConfigInfoWrapperRowMapper(); public static final ConfigInfoStateWrapperRowMapper CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER = new ConfigInfoStateWrapperRowMapper(); public static final RowMapper CONFIG_KEY_ROW_MAPPER = new ConfigKeyRowMapper(); public static final ConfigInfoBetaWrapperRowMapper CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER = new ConfigInfoBetaWrapperRowMapper(); public static final ConfigInfoTagWrapperRowMapper CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER = new ConfigInfoTagWrapperRowMapper(); public static final ConfigInfoGrayWrapperRowMapper CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER = new ConfigInfoGrayWrapperRowMapper(); public static final ConfigInfoRowMapper CONFIG_INFO_ROW_MAPPER = new ConfigInfoRowMapper(); public static final ConfigAdvanceInfoRowMapper CONFIG_ADVANCE_INFO_ROW_MAPPER = new ConfigAdvanceInfoRowMapper(); public static final ConfigAllInfoRowMapper CONFIG_ALL_INFO_ROW_MAPPER = new ConfigAllInfoRowMapper(); public static final ConfigInfo4BetaRowMapper CONFIG_INFO4BETA_ROW_MAPPER = new ConfigInfo4BetaRowMapper(); public static final ConfigInfo4TagRowMapper CONFIG_INFO4TAG_ROW_MAPPER = new ConfigInfo4TagRowMapper(); public static final ConfigInfoBaseRowMapper CONFIG_INFO_BASE_ROW_MAPPER = new ConfigInfoBaseRowMapper(); public static final ConfigInfoChangedRowMapper CONFIG_INFO_CHANGED_ROW_MAPPER = new ConfigInfoChangedRowMapper(); public static final ConfigHistoryRowMapper HISTORY_LIST_ROW_MAPPER = new ConfigHistoryRowMapper(); public static final ConfigHistoryDetailRowMapper HISTORY_DETAIL_ROW_MAPPER = new ConfigHistoryDetailRowMapper(); static { injectConfigRowMapper(); } public ConfigRowMapperInjector() { } private static void injectConfigRowMapper() { // CONFIG_INFO_WRAPPER_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER); // CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); // CONFIG_KEY_ROW_MAPPER RowMapperManager.registerRowMapper(ConfigRowMapperInjector.CONFIG_KEY_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_KEY_ROW_MAPPER); // CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); // CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); // CONFIG_INFO_ROW_MAPPER RowMapperManager.registerRowMapper(ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER); // CONFIG_ADVANCE_INFO_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_ADVANCE_INFO_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_ADVANCE_INFO_ROW_MAPPER); // CONFIG_ALL_INFO_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_ALL_INFO_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_ALL_INFO_ROW_MAPPER); // CONFIG_INFO4BETA_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO4BETA_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO4BETA_ROW_MAPPER); // CONFIG_INFO4TAG_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO4TAG_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO4TAG_ROW_MAPPER); // CONFIG_INFO_BASE_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO_BASE_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_BASE_ROW_MAPPER); // CONFIG_INFO_CHANGED_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO_CHANGED_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_CHANGED_ROW_MAPPER); // HISTORY_LIST_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.HISTORY_LIST_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.HISTORY_LIST_ROW_MAPPER); // HISTORY_DETAIL_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.HISTORY_DETAIL_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.HISTORY_DETAIL_ROW_MAPPER); // CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER RowMapperManager.registerRowMapper( ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER.getClass().getCanonicalName(), ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } public static final class ConfigInfoWrapperRowMapper implements RowMapper { @Override public ConfigInfoWrapper mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfoWrapper info = new ConfigInfoWrapper(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setAppName(rs.getString("app_name")); try { info.setType(rs.getString("type")); } catch (SQLException ignore) { } try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } try { info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } try { info.setEncryptedDataKey(rs.getString("encrypted_data_key")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfoStateWrapperRowMapper implements RowMapper { @Override public ConfigInfoStateWrapper mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfoStateWrapper info = new ConfigInfoStateWrapper(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); try { info.setMd5(rs.getString("md5")); } catch (SQLException e) { // ignore } try { info.setId(rs.getLong("id")); } catch (SQLException e) { // ignore } return info; } } public static final class ConfigInfoBetaWrapperRowMapper implements RowMapper { @Override public ConfigInfoBetaWrapper mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfoBetaWrapper info = new ConfigInfoBetaWrapper(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setAppName(rs.getString("app_name")); info.setBetaIps(rs.getString("beta_ips")); try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } try { info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } try { info.setEncryptedDataKey(rs.getString("encrypted_data_key")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfoTagWrapperRowMapper implements RowMapper { @Override public ConfigInfoTagWrapper mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfoTagWrapper info = new ConfigInfoTagWrapper(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setTag(rs.getString("tag_id")); info.setAppName(rs.getString("app_name")); try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } try { info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfoGrayWrapperRowMapper implements RowMapper { @Override public ConfigInfoGrayWrapper mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfoGrayWrapper info = new ConfigInfoGrayWrapper(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setGrayName(rs.getString("gray_name")); try { info.setGrayRule(rs.getString("gray_rule")); } catch (SQLException ignore) { } try { info.setAppName(rs.getString("app_name")); } catch (SQLException ignore) { } try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } try { info.setLastModified(rs.getTimestamp("gmt_modified").getTime()); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } try { info.setEncryptedDataKey(rs.getString("encrypted_data_key")); } catch (SQLException ignore) { } try { info.setSrcUser(rs.getString("src_user")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfoRowMapper implements RowMapper { @Override public ConfigInfo mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfo info = new ConfigInfo(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); try { info.setAppName(rs.getString("app_name")); } catch (SQLException ignore) { } try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } try { info.setType(rs.getString("type")); } catch (SQLException ignore) { } try { info.setEncryptedDataKey(rs.getString("encrypted_data_key")); } catch (SQLException ignore) { } // 新增字段映射 try { info.setDesc(rs.getString("c_desc")); } catch (SQLException ignore) { // 字段不存在时设置为 null,保证向后兼容 } try { String configTags = rs.getString("config_tags"); // 处理 GROUP_CONCAT/LISTAGG 的结果,可能为 null info.setConfigTags(StringUtils.isBlank(configTags) ? null : configTags); } catch (SQLException ignore) { // 字段不存在时设置为 null,保证向后兼容 } try { java.sql.Timestamp gmtModified = rs.getTimestamp("gmt_modified"); if (gmtModified != null) { info.setGmtModified(gmtModified.getTime()); } } catch (SQLException ignore) { // 字段不存在时设置为 null,保证向后兼容 } return info; } } public static final class ConfigKeyRowMapper implements RowMapper { @Override public ConfigKey mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigKey info = new ConfigKey(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setAppName(rs.getString("app_name")); return info; } } public static final class ConfigAdvanceInfoRowMapper implements RowMapper { @Override public ConfigAdvanceInfo mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigAdvanceInfo info = new ConfigAdvanceInfo(); info.setCreateTime(rs.getTimestamp("gmt_create").getTime()); info.setModifyTime(rs.getTimestamp("gmt_modified").getTime()); info.setCreateUser(rs.getString("src_user")); info.setCreateIp(rs.getString("src_ip")); info.setDesc(rs.getString("c_desc")); info.setUse(rs.getString("c_use")); info.setEffect(rs.getString("effect")); info.setType(rs.getString("type")); info.setSchema(rs.getString("c_schema")); return info; } } public static final class ConfigAllInfoRowMapper implements RowMapper { @Override public ConfigAllInfo mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigAllInfo info = new ConfigAllInfo(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setAppName(rs.getString("app_name")); try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } info.setCreateTime(rs.getTimestamp("gmt_create").getTime()); info.setModifyTime(rs.getTimestamp("gmt_modified").getTime()); info.setCreateUser(rs.getString("src_user")); info.setCreateIp(rs.getString("src_ip")); info.setDesc(rs.getString("c_desc")); info.setUse(rs.getString("c_use")); info.setEffect(rs.getString("effect")); info.setType(rs.getString("type")); info.setSchema(rs.getString("c_schema")); try { info.setEncryptedDataKey(rs.getString("encrypted_data_key")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfo4BetaRowMapper implements RowMapper { @Override public ConfigInfo4Beta mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfo4Beta info = new ConfigInfo4Beta(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setAppName(rs.getString("app_name")); info.setBetaIps(rs.getString("beta_ips")); try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfo4TagRowMapper implements RowMapper { @Override public ConfigInfo4Tag mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfo4Tag info = new ConfigInfo4Tag(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); info.setTag(rs.getString("tag_id")); info.setAppName(rs.getString("app_name")); try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } try { info.setMd5(rs.getString("md5")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfoBaseRowMapper implements RowMapper { @Override public ConfigInfoBase mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfoBase info = new ConfigInfoBase(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); try { info.setContent(rs.getString("content")); } catch (SQLException ignore) { } try { info.setId(rs.getLong("id")); } catch (SQLException ignore) { } return info; } } public static final class ConfigInfoChangedRowMapper implements RowMapper { @Override public ConfigInfoChanged mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigInfoChanged info = new ConfigInfoChanged(); info.setDataId(rs.getString("data_id")); info.setGroup(rs.getString("group_id")); info.setTenant(rs.getString("tenant_id")); return info; } } public static final class ConfigHistoryRowMapper implements RowMapper { @Override public ConfigHistoryInfo mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setId(rs.getLong("nid")); configHistoryInfo.setDataId(rs.getString("data_id")); configHistoryInfo.setGroup(rs.getString("group_id")); configHistoryInfo.setTenant(rs.getString("tenant_id")); configHistoryInfo.setAppName(rs.getString("app_name")); configHistoryInfo.setSrcIp(rs.getString("src_ip")); configHistoryInfo.setSrcUser(rs.getString("src_user")); configHistoryInfo.setOpType(rs.getString("op_type")); configHistoryInfo.setPublishType(rs.getString("publish_type")); configHistoryInfo.setGrayName(rs.getString("gray_name")); configHistoryInfo.setExtInfo(rs.getString("ext_info")); configHistoryInfo.setCreatedTime(rs.getTimestamp("gmt_create")); configHistoryInfo.setLastModifiedTime(rs.getTimestamp("gmt_modified")); return configHistoryInfo; } } public static final class ConfigHistoryDetailRowMapper implements RowMapper { @Override public ConfigHistoryInfo mapRow(ResultSet rs, int rowNum) throws SQLException { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setId(rs.getLong("nid")); configHistoryInfo.setDataId(rs.getString("data_id")); configHistoryInfo.setGroup(rs.getString("group_id")); configHistoryInfo.setTenant(rs.getString("tenant_id")); configHistoryInfo.setAppName(rs.getString("app_name")); configHistoryInfo.setMd5(rs.getString("md5")); configHistoryInfo.setContent(rs.getString("content")); configHistoryInfo.setSrcUser(rs.getString("src_user")); configHistoryInfo.setSrcIp(rs.getString("src_ip")); configHistoryInfo.setOpType(rs.getString("op_type")); configHistoryInfo.setPublishType(rs.getString("publish_type")); configHistoryInfo.setGrayName(rs.getString("gray_name")); configHistoryInfo.setExtInfo(rs.getString("ext_info")); configHistoryInfo.setCreatedTime(rs.getTimestamp("gmt_create")); configHistoryInfo.setLastModifiedTime(rs.getTimestamp("gmt_modified")); try { configHistoryInfo.setEncryptedDataKey(rs.getString("encrypted_data_key")); } catch (SQLException ignore) { } return configHistoryInfo; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/HistoryConfigInfoPersistService.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; import java.sql.Timestamp; import java.util.List; /** * Database service, providing access to his_config_info in the database. * * @author lixiaoshuang */ public interface HistoryConfigInfoPersistService { /** * create Pagination utils. * * @param Generic object * @return {@link PaginationHelper} */ PaginationHelper createPaginationHelper(); //------------------------------------------insert---------------------------------------------// /** * Update change records; database atomic operations, minimal sql actions, no business encapsulation. * * @param id id * @param configInfo config info * @param srcIp ip * @param srcUser user * @param time time * @param ops ops type * @param publishType publish type * @param grayName gray name * @param extInfo extra config info */ void insertConfigHistoryAtomic(long id, ConfigInfo configInfo, String srcIp, String srcUser, final Timestamp time, String ops, String publishType, String grayName, String extInfo); //------------------------------------------delete---------------------------------------------// /** * Delete data before startTime. * * @param startTime start time * @param limitSize limit size */ void removeConfigHistory(final Timestamp startTime, final int limitSize); //------------------------------------------update---------------------------------------------// //------------------------------------------select---------------------------------------------// /** * Query deleted config. * * @param startTime start time * @param startId last max id * @param size page size * @param publishType publish type * @return {@link ConfigInfoStateWrapper} list */ List findDeletedConfig(final Timestamp startTime, final long startId, int size, String publishType); /** * List configuration history change record. * * @param dataId data Id * @param group group * @param tenant tenant * @param pageNo no * @param pageSize size * @return {@link Page} with {@link ConfigHistoryInfo} generation */ Page findConfigHistory(String dataId, String group, String tenant, int pageNo, int pageSize); /** * Get history config detail. * * @param nid nid * @return {@link ConfigHistoryInfo} */ ConfigHistoryInfo detailConfigHistory(Long nid); /** * Get previous config detail. * * @param id id * @return {@link ConfigHistoryInfo} */ ConfigHistoryInfo detailPreviousConfigHistory(Long id); /** * Get the number of configurations before the specified time. * * @param startTime start time * @return count of history config that meet the conditions */ @Deprecated int findConfigHistoryCountByTime(final Timestamp startTime); /** * Get the next history config detail of the history config. * * @param dataId data Id * @param group group * @param tenant tenant * @param publishType publish type * @param grayName gray name * @param startNid start nid * @return the next history config detail of the history config */ ConfigHistoryInfo getNextHistoryInfo(String dataId, String group, String tenant, String publishType, String grayName, long startNid); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigDumpApplyHook.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.event.ConfigDumpEvent; import com.alibaba.nacos.config.server.service.dump.DumpConfigHandler; import com.alibaba.nacos.consistency.entity.WriteRequest; import com.alibaba.nacos.core.utils.GenericType; import com.alibaba.nacos.persistence.repository.embedded.hook.EmbeddedApplyHook; import org.springframework.stereotype.Component; import java.util.List; import java.util.Map; import java.util.Objects; /** * Embedded apply hook for config dump. * * @author xiweng.yy */ @Component public class EmbeddedConfigDumpApplyHook extends EmbeddedApplyHook { public EmbeddedConfigDumpApplyHook() { NotifyCenter.registerToPublisher(ConfigDumpEvent.class, NotifyCenter.ringBufferSize); NotifyCenter.registerSubscriber(new DumpConfigHandler()); } @Override public void afterApply(WriteRequest log) { handleExtendInfo(log.getExtendInfoMap()); } private void handleExtendInfo(Map extendInfo) { if (extendInfo.containsKey(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT)) { String jsonVal = extendInfo.get(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT); if (StringUtils.isNotBlank(jsonVal)) { NotifyCenter.publishEvent(JacksonUtils.toObj(jsonVal, ConfigDumpEvent.class)); } return; } if (extendInfo.containsKey(Constants.EXTEND_INFOS_CONFIG_DUMP_EVENT)) { String jsonVal = extendInfo.get(Constants.EXTEND_INFOS_CONFIG_DUMP_EVENT); if (StringUtils.isNotBlank(jsonVal)) { List list = JacksonUtils.toObj(jsonVal, new GenericType>() { }.getType()); list.stream().filter(Objects::nonNull).forEach(NotifyCenter::publishEvent); } } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoBetaPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.exception.NacosConfigException; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.model.event.DerbyImportEvent; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedPaginationHelperImpl; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoBetaMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.sql.Timestamp; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; /** * EmbeddedConfigInfoBetaPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnEmbeddedStorage.class) @Service("embeddedConfigInfoBetaPersistServiceImpl") public class EmbeddedConfigInfoBetaPersistServiceImpl implements ConfigInfoBetaPersistService { private DataSourceService dataSourceService; private final DatabaseOperate databaseOperate; private MapperManager mapperManager; /** * The constructor sets the dependency injection order. * * @param databaseOperate databaseOperate. */ public EmbeddedConfigInfoBetaPersistServiceImpl(DatabaseOperate databaseOperate) { this.databaseOperate = databaseOperate; this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); NotifyCenter.registerToSharePublisher(DerbyImportEvent.class); } @Override public PaginationHelper createPaginationHelper() { return new EmbeddedPaginationHelperImpl<>(databaseOperate); } @Override public ConfigInfoStateWrapper findConfigInfo4BetaState(final String dataId, final String group, final String tenant) { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final String sql = configInfoBetaMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } private ConfigOperateResult getBetaOperateResult(String dataId, String group, String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoStateWrapper configInfo4Beta = this.findConfigInfo4BetaState(dataId, group, tenantTmp); if (configInfo4Beta == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfo4Beta.getId(), configInfo4Beta.getLastModified()); } @Override public ConfigOperateResult addConfigInfo4Beta(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String encryptedDataKey = StringUtils.defaultEmptyIfBlank(configInfo.getEncryptedDataKey()); configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); final String sql = configInfoBetaMapper.insert( Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "beta_ips", "src_ip", "src_user", "gmt_create@NOW()", "gmt_modified@NOW()", "encrypted_data_key")); final Object[] args = new Object[]{configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5, betaIps, srcIp, srcUser, encryptedDataKey}; Timestamp time = new Timestamp(System.currentTimeMillis()); EmbeddedStorageContextUtils.onModifyConfigBetaInfo(configInfo, betaIps, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(sql, args); databaseOperate.blockUpdate(); return getBetaOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult insertOrUpdateBeta(final ConfigInfo configInfo, final String betaIps, final String srcIp, final String srcUser) { if (findConfigInfo4BetaState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()) == null) { return addConfigInfo4Beta(configInfo, betaIps, srcIp, srcUser); } else { return updateConfigInfo4Beta(configInfo, betaIps, srcIp, srcUser); } } @Override public ConfigOperateResult insertOrUpdateBetaCas(final ConfigInfo configInfo, final String betaIps, final String srcIp, final String srcUser) { if (findConfigInfo4BetaState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()) == null) { return addConfigInfo4Beta(configInfo, betaIps, srcIp, srcUser); } else { return updateConfigInfo4BetaCas(configInfo, betaIps, srcIp, srcUser); } } @Override public void removeConfigInfo4Beta(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoStateWrapper configInfo = findConfigInfo4BetaState(dataId, group, tenant); if (configInfo != null) { try { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); final String sql = configInfoBetaMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {dataId, group, tenantTmp}; EmbeddedStorageContextUtils.onDeleteConfigBetaInfo(tenantTmp, group, dataId, System.currentTimeMillis()); EmbeddedStorageContextHolder.addSqlContext(sql, args); boolean result = databaseOperate.update(EmbeddedStorageContextHolder.getCurrentSqlContext()); if (!result) { throw new NacosConfigException("[Tag] Configuration deletion failed"); } } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } } @Override public ConfigOperateResult updateConfigInfo4Beta(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String encryptedDataKey = StringUtils.defaultEmptyIfBlank(configInfo.getEncryptedDataKey()); configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); final String sql = configInfoBetaMapper.update( Arrays.asList("content", "md5", "beta_ips", "src_ip", "src_user", "gmt_modified@NOW()", "app_name", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[]{configInfo.getContent(), md5, betaIps, srcIp, srcUser, appNameTmp, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp}; Timestamp time = new Timestamp(System.currentTimeMillis()); EmbeddedStorageContextUtils.onModifyConfigBetaInfo(configInfo, betaIps, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(sql, args); databaseOperate.blockUpdate(); return getBetaOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult updateConfigInfo4BetaCas(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5); context.putUpdateParameter(FieldConstant.BETA_IPS, betaIps); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); MapperResult mapperResult = configInfoBetaMapper.updateConfigInfo4BetaCas(context); final String sql = mapperResult.getSql(); List paramList = mapperResult.getParamList(); final Object[] args = paramList.toArray(); Timestamp time = new Timestamp(System.currentTimeMillis()); EmbeddedStorageContextUtils.onModifyConfigBetaInfo(configInfo, betaIps, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(sql, args); boolean success = databaseOperate.blockUpdate(); if (success) { return getBetaOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } else { return new ConfigOperateResult(false); } } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigInfoBetaWrapper findConfigInfo4Beta(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); final String sql = configInfoBetaMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "beta_ips", "encrypted_data_key", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } @Override public int configInfoBetaCount() { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); String sql = configInfoBetaMapper.count(null); Integer result = databaseOperate.queryOne(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); } return result; } @Override public Page findAllConfigInfoBetaForDumpAll(final int pageNo, final int pageSize) { final int startRow = (pageNo - 1) * pageSize; ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); String sqlCountRows = configInfoBetaMapper.count(null); MapperContext context = new MapperContext(); context.setStartRow(startRow); context.setPageSize(pageSize); MapperResult mapperResult = configInfoBetaMapper.findAllConfigInfoBetaForDumpAllFetchRows(context); String sqlFetchRows = mapperResult.getSql(); PaginationHelper helper = createPaginationHelper(); return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {}, pageNo, pageSize, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoGrayPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; import com.alibaba.nacos.config.server.utils.ConfigExtInfoUtil; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.core.distributed.id.IdGeneratorManager; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.model.event.DerbyImportEvent; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedPaginationHelperImpl; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoGrayMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Conditional; import org.springframework.dao.DataAccessException; import org.springframework.stereotype.Service; import java.sql.Timestamp; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.utils.PropertyUtil.GRAY_MIGRATE_FLAG; /** * EmbeddedConfigInfoGrayPersistServiceImpl. * * @author rong */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnEmbeddedStorage.class) @Service("embeddedConfigInfoGrayPersistServiceImpl") public class EmbeddedConfigInfoGrayPersistServiceImpl implements ConfigInfoGrayPersistService { private static final String RESOURCE_CONFIG_HISTORY_ID = "config-history-id"; private static final String RESOURCE_CONFIG_HISTORY_GRAY_ID = "config-history-gray-id"; private DataSourceService dataSourceService; private final DatabaseOperate databaseOperate; private MapperManager mapperManager; private final IdGeneratorManager idGeneratorManager; private final HistoryConfigInfoPersistService historyConfigInfoPersistService; /** * The constructor sets the dependency injection order. * * @param databaseOperate databaseOperate. */ public EmbeddedConfigInfoGrayPersistServiceImpl(DatabaseOperate databaseOperate, IdGeneratorManager idGeneratorManager, @Qualifier("embeddedHistoryConfigInfoPersistServiceImpl") HistoryConfigInfoPersistService historyConfigInfoPersistService) { this.databaseOperate = databaseOperate; this.idGeneratorManager = idGeneratorManager; this.historyConfigInfoPersistService = historyConfigInfoPersistService; idGeneratorManager.register(RESOURCE_CONFIG_HISTORY_GRAY_ID, RESOURCE_CONFIG_HISTORY_ID); this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); NotifyCenter.registerToSharePublisher(DerbyImportEvent.class); } @Override public PaginationHelper createPaginationHelper() { return new EmbeddedPaginationHelperImpl<>(databaseOperate); } @Override public ConfigInfoStateWrapper findConfigInfo4GrayState(final String dataId, final String group, final String tenant, String grayName) { ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String sql = configInfoGrayMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp, grayNameTmp}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } private ConfigOperateResult getGrayOperateResult(String dataId, String group, String tenant, String grayName) { String tenantTmp = StringUtils.defaultEmptyIfBlank(tenant); ConfigInfoStateWrapper configInfo4Gray = this.findConfigInfo4GrayState(dataId, group, tenantTmp, grayName); if (configInfo4Gray == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfo4Gray.getId(), configInfo4Gray.getLastModified()); } @Override public ConfigOperateResult addConfigInfo4Gray(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); configInfo.setTenant(tenantTmp); try { long configGrayId = idGeneratorManager.nextId(RESOURCE_CONFIG_HISTORY_GRAY_ID); long hisId = idGeneratorManager.nextId(RESOURCE_CONFIG_HISTORY_ID); addConfigInfoGrayAtomic(configGrayId, configInfo, grayNameTmp, grayRuleTmp, srcIp, srcUser); Timestamp now = new Timestamp(System.currentTimeMillis()); if (!GRAY_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(hisId, configInfo, srcIp, srcUser, now, "I", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(grayNameTmp, grayRuleTmp, srcUser)); } EmbeddedStorageContextUtils.onModifyConfigGrayInfo(configInfo, grayNameTmp, grayRuleTmp, srcIp, now); databaseOperate.blockUpdate(); return getGrayOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public void addConfigInfoGrayAtomic(long configGrayId, ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); final String sql = configInfoGrayMapper.insert( Arrays.asList("id", "data_id", "group_id", "tenant_id", "gray_name", "gray_rule", "app_name", "content", "md5", "src_ip", "src_user", "gmt_create", "gmt_modified")); Timestamp time = new Timestamp(System.currentTimeMillis()); final Object[] args = new Object[] {configGrayId, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp, grayRuleTmp, appNameTmp, configInfo.getContent(), md5, srcIp, srcUser, time, time}; EmbeddedStorageContextHolder.addSqlContext(sql, args); } @Override public ConfigOperateResult insertOrUpdateGray(final ConfigInfo configInfo, final String grayName, final String grayRule, final String srcIp, final String srcUser) { if (findConfigInfo4GrayState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), grayName) == null) { return addConfigInfo4Gray(configInfo, grayName, grayRule, srcIp, srcUser); } else { return updateConfigInfo4Gray(configInfo, grayName, grayRule, srcIp, srcUser); } } @Override public ConfigOperateResult insertOrUpdateGrayCas(final ConfigInfo configInfo, final String grayName, final String grayRule, final String srcIp, final String srcUser) { if (findConfigInfo4GrayState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), grayName) == null) { return addConfigInfo4Gray(configInfo, grayName, grayRule, srcIp, srcUser); } else { return updateConfigInfo4GrayCas(configInfo, grayName, grayRule, srcIp, srcUser); } } @Override public void removeConfigInfoGray(final String dataId, final String group, final String tenant, final String grayName, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName; ConfigInfoGrayWrapper oldConfigAllInfo4Gray = findConfigInfo4Gray(dataId, group, tenantTmp, grayNameTmp); if (oldConfigAllInfo4Gray == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", dataId, group, tenant); } } ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); final String sql = configInfoGrayMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")); final Object[] args = new Object[] {dataId, group, tenantTmp, grayNameTmp}; Timestamp now = new Timestamp(System.currentTimeMillis()); if (!GRAY_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo4Gray.getId(), oldConfigAllInfo4Gray, srcIp, srcUser, now, "D", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(oldConfigAllInfo4Gray.getGrayName(), oldConfigAllInfo4Gray.getGrayRule(), oldConfigAllInfo4Gray.getSrcUser())); } EmbeddedStorageContextUtils.onDeleteConfigGrayInfo(tenantTmp, group, dataId, grayNameTmp, srcIp); EmbeddedStorageContextHolder.addSqlContext(sql, args); try { databaseOperate.update(EmbeddedStorageContextHolder.getCurrentSqlContext()); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult updateConfigInfo4Gray(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); configInfo.setTenant(tenantTmp); try { ConfigInfoGrayWrapper oldConfigAllInfo4Gray = findConfigInfo4Gray(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); if (oldConfigAllInfo4Gray == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); Timestamp time = new Timestamp(System.currentTimeMillis()); final String sql = configInfoGrayMapper.update( Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name", "gray_rule"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")); final Object[] args = new Object[] {configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, grayRuleTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp}; if (!GRAY_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo4Gray.getId(), oldConfigAllInfo4Gray, srcIp, srcUser, time, "U", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(oldConfigAllInfo4Gray.getGrayName(), oldConfigAllInfo4Gray.getGrayRule(), oldConfigAllInfo4Gray.getSrcUser())); } EmbeddedStorageContextUtils.onModifyConfigGrayInfo(configInfo, grayNameTmp, grayRuleTmp, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(sql, args); databaseOperate.blockUpdate(); return getGrayOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult updateConfigInfo4GrayCas(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); configInfo.setTenant(tenantTmp); try { final ConfigInfoGrayWrapper oldConfigAllInfo4Gray = findConfigInfo4Gray(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); if (oldConfigAllInfo4Gray == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); Timestamp time = new Timestamp(System.currentTimeMillis()); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, time); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.GRAY_NAME, grayNameTmp); context.putWhereParameter(FieldConstant.GRAY_RULE, grayRuleTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); final MapperResult mapperResult = configInfoGrayMapper.updateConfigInfo4GrayCas(context); EmbeddedStorageContextUtils.onModifyConfigGrayInfo(configInfo, grayNameTmp, grayRuleTmp, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(mapperResult.getSql(), mapperResult.getParamList().toArray()); Boolean success = databaseOperate.blockUpdate(); if (success) { if (!GRAY_MIGRATE_FLAG.get()) { Timestamp now = new Timestamp(System.currentTimeMillis()); historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo4Gray.getId(), oldConfigAllInfo4Gray, srcIp, srcUser, now, "U", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(oldConfigAllInfo4Gray.getGrayName(), oldConfigAllInfo4Gray.getGrayRule(), oldConfigAllInfo4Gray.getSrcUser())); } return getGrayOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); } else { return new ConfigOperateResult(false); } } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigInfoGrayWrapper findConfigInfo4Gray(final String dataId, final String group, final String tenant, final String grayName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); final String sql = configInfoGrayMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "gray_name", "gray_rule", "app_name", "content", "md5", "gmt_modified", "src_user", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp, grayNameTmp}, CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } @Override public int configInfoGrayCount() { ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); String sql = configInfoGrayMapper.count(null); Integer result = databaseOperate.queryOne(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); } return result; } @Override public Page findAllConfigInfoGrayForDumpAll(final int pageNo, final int pageSize) { final int startRow = (pageNo - 1) * pageSize; ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); String sqlCountRows = configInfoGrayMapper.count(null); MapperResult sqlFetchRows = configInfoGrayMapper.findAllConfigInfoGrayForDumpAllFetchRows( new MapperContext(startRow, pageSize)); PaginationHelper helper = createPaginationHelper(); return helper.fetchPageLimit(sqlCountRows, sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), pageNo, pageSize, CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } @Override public List findConfigInfoGrays(String dataId, String group, String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); final String sql = configInfoGrayMapper.select(Collections.singletonList("gray_name"), Arrays.asList("data_id", "group_id", "tenant_id")); return databaseOperate.queryMany(sql, new Object[] {dataId, group, tenantTmp}, String.class); } @Override public List findChangeConfig(final Timestamp startTime, long lastMaxId, final int pageSize) { try { ConfigInfoGrayMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.PAGE_SIZE, pageSize); context.putWhereParameter(FieldConstant.LAST_MAX_ID, lastMaxId); MapperResult mapperResult = configInfoMapper.findChangeConfig(context); return databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.common.constant.Symbols; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.constant.ParametersField; import com.alibaba.nacos.config.server.enums.FileTypeEnum; import com.alibaba.nacos.config.server.exception.NacosConfigException; import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; import com.alibaba.nacos.config.server.utils.ConfigExtInfoUtil; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.core.distributed.id.IdGeneratorManager; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.persistence.model.event.DerbyImportEvent; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedPaginationHelperImpl; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.ContextConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; import com.alibaba.nacos.plugin.datasource.mapper.ConfigTagsRelationMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.sql.Timestamp; 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.Optional; import java.util.Set; import java.util.function.BiConsumer; import java.util.stream.Collectors; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ADVANCE_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ALL_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; import static com.alibaba.nacos.config.server.utils.PropertyUtil.CONFIG_MIGRATE_FLAG; import static com.alibaba.nacos.persistence.repository.RowMapperManager.MAP_ROW_MAPPER; /** * EmbeddedConfigInfoPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnEmbeddedStorage.class) @Service("embeddedConfigInfoPersistServiceImpl") public class EmbeddedConfigInfoPersistServiceImpl implements ConfigInfoPersistService { public static final String SPOT = "."; private static final String RESOURCE_CONFIG_INFO_ID = "config-info-id"; private static final String RESOURCE_CONFIG_HISTORY_ID = "config-history-id"; private static final String RESOURCE_CONFIG_TAG_RELATION_ID = "config-tag-relation-id"; private static final String RESOURCE_APP_CONFIGDATA_RELATION_SUBS = "app-configdata-relation-subs"; private static final String RESOURCE_CONFIG_BETA_ID = "config-beta-id"; private static final String RESOURCE_NAMESPACE_ID = "namespace-id"; private static final String RESOURCE_USER_ID = "user-id"; private static final String RESOURCE_ROLE_ID = "role-id"; private static final String RESOURCE_PERMISSIONS_ID = "permissions_id"; private static final String DATA_ID = "dataId"; private static final String GROUP = "group"; private static final String APP_NAME = "appName"; private static final String CONTENT = "content"; private static final String TENANT = "tenant_id"; private static final Set SYSTEM_GROUP = Set.of("mcp-server", "mcp-server-versions", "mcp-tools"); private final DatabaseOperate databaseOperate; private final IdGeneratorManager idGeneratorManager; MapperManager mapperManager; private DataSourceService dataSourceService; private HistoryConfigInfoPersistService historyConfigInfoPersistService; /** * The constructor sets the dependency injection order. * * @param databaseOperate databaseOperate. * @param idGeneratorManager {@link IdGeneratorManager} */ public EmbeddedConfigInfoPersistServiceImpl(DatabaseOperate databaseOperate, IdGeneratorManager idGeneratorManager, @Qualifier("embeddedHistoryConfigInfoPersistServiceImpl") HistoryConfigInfoPersistService historyConfigInfoPersistService) { this.databaseOperate = databaseOperate; this.idGeneratorManager = idGeneratorManager; idGeneratorManager.register(RESOURCE_CONFIG_INFO_ID, RESOURCE_CONFIG_HISTORY_ID, RESOURCE_CONFIG_TAG_RELATION_ID, RESOURCE_APP_CONFIGDATA_RELATION_SUBS, RESOURCE_CONFIG_BETA_ID, RESOURCE_NAMESPACE_ID, RESOURCE_USER_ID, RESOURCE_ROLE_ID, RESOURCE_PERMISSIONS_ID); this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); this.historyConfigInfoPersistService = historyConfigInfoPersistService; NotifyCenter.registerToSharePublisher(DerbyImportEvent.class); } @Override public PaginationHelper createPaginationHelper() { return new EmbeddedPaginationHelperImpl<>(databaseOperate); } @Override public String generateLikeArgument(String s) { String underscore = "_"; if (s.contains(underscore)) { s = s.replaceAll(underscore, "\\\\_"); } String fuzzySearchSign = "\\*"; String sqlLikePercentSign = "%"; if (s.contains(PATTERN_STR)) { return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); } else { return s; } } @Override public ConfigInfoStateWrapper findConfigInfoState(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } private ConfigOperateResult getConfigInfoOperateResult(String dataId, String group, String tenant) { ConfigInfoStateWrapper configInfo4 = this.findConfigInfoState(dataId, group, tenant); if (configInfo4 == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfo4.getId(), configInfo4.getLastModified()); } @Override public ConfigOperateResult addConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo, final Map configAdvanceInfo) { return addConfigInfo(srcIp, srcUser, configInfo, configAdvanceInfo, null); } private ConfigOperateResult addConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo, final Map configAdvanceInfo, BiConsumer consumer) { try { final String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); configInfo.setTenant(tenantTmp); long configId = idGeneratorManager.nextId(RESOURCE_CONFIG_INFO_ID); long hisId = idGeneratorManager.nextId(RESOURCE_CONFIG_HISTORY_ID); addConfigInfoAtomic(configId, srcIp, srcUser, configInfo, configAdvanceInfo); String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); addConfigTagsRelation(configId, configTags, configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); Timestamp now = new Timestamp(System.currentTimeMillis()); if (!CONFIG_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(hisId, configInfo, srcIp, srcUser, now, "I", Constants.FORMAL, null, ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser)); } EmbeddedStorageContextUtils.onModifyConfigInfo(configInfo, srcIp, now); boolean result = databaseOperate.blockUpdate(consumer); if (!result) { return new ConfigOperateResult(false); } return getConfigInfoOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult updateConfigInfoMetadata(String dataId, String group, String tenant, String configTags, String description) throws NacosException { try { ConfigInfoWrapper configInfoWrapper = findConfigInfo(dataId, group, tenant); if (configInfoWrapper == null) { throw new NacosException(NacosException.NOT_FOUND, "config is not found for dataId=" + dataId + ", group=" + group); } Long configId = configInfoWrapper.getId(); Timestamp now = new Timestamp(System.currentTimeMillis()); if (description != null) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.update(Arrays.asList("gmt_modified@NOW()", "c_desc"), Arrays.asList("id")); final Object[] args = new Object[] {description, configId}; EmbeddedStorageContextHolder.addSqlContext(sql, args); } if (configTags != null) { removeTagByIdAtomic(configId); addConfigTagsRelation(configId, configTags, configInfoWrapper.getDataId(), configInfoWrapper.getGroup(), configInfoWrapper.getTenant()); } EmbeddedStorageContextUtils.onModifyConfigInfo(configInfoWrapper, null, now); databaseOperate.blockUpdate(); return getConfigInfoOperateResult(configInfoWrapper.getDataId(), configInfoWrapper.getGroup(), tenant); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Map configAdvanceInfo) { if (Objects.isNull( findConfigInfoState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()))) { return addConfigInfo(srcIp, srcUser, configInfo, configAdvanceInfo); } else { return updateConfigInfo(configInfo, srcIp, srcUser, configAdvanceInfo); } } @Override public ConfigOperateResult insertOrUpdateCas(String srcIp, String srcUser, ConfigInfo configInfo, Map configAdvanceInfo) { if (Objects.isNull( findConfigInfoState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()))) { return addConfigInfo(srcIp, srcUser, configInfo, configAdvanceInfo); } else { return updateConfigInfoCas(configInfo, srcIp, srcUser, configAdvanceInfo); } } @Override public long addConfigInfoAtomic(final long id, final String srcIp, final String srcUser, final ConfigInfo configInfo, Map configAdvanceInfo) { final String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); final String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); final String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); final String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); final String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); final String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.insert( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user", "gmt_create@NOW()", "gmt_modified@NOW()", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key")); final Object[] args = new Object[] {id, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5Tmp, srcIp, srcUser, desc, use, effect, type, schema, encryptedDataKey}; EmbeddedStorageContextHolder.addSqlContext(sql, args); return id; } @Override public void addConfigTagRelationAtomic(long configId, String tagName, String dataId, String group, String tenant) { ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); final String sql = configTagsRelationMapper.insert( Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {configId, tagName, StringUtils.EMPTY, dataId, group, tenant}; EmbeddedStorageContextHolder.addSqlContext(sql, args); } @Override public void addConfigTagsRelation(long configId, String configTags, String dataId, String group, String tenant) { if (StringUtils.isNotBlank(configTags)) { String[] tagArr = configTags.split(","); for (int i = 0; i < tagArr.length; i++) { addConfigTagRelationAtomic(configId, tagArr[i], dataId, group, tenant); } } } @Override public Map batchInsertOrUpdate(List configInfoList, String srcUser, String srcIp, Map configAdvanceInfo, SameConfigPolicy policy) throws NacosException { int succCount = 0; int skipCount = 0; List> failData = null; List> skipData = null; final BiConsumer callFinally = (result, t) -> { if (t != null) { throw new NacosRuntimeException(0, t); } }; for (int i = 0; i < configInfoList.size(); i++) { ConfigAllInfo configInfo = configInfoList.get(i); try { ParamUtils.checkParam(configInfo.getDataId(), configInfo.getGroup(), "datumId", configInfo.getContent()); } catch (Throwable e) { DEFAULT_LOG.error("data verification failed", e); throw e; } ConfigInfo configInfo2Save = new ConfigInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), configInfo.getAppName(), configInfo.getContent()); configInfo2Save.setEncryptedDataKey( configInfo.getEncryptedDataKey() == null ? "" : configInfo.getEncryptedDataKey()); String type = configInfo.getType(); if (StringUtils.isBlank(type)) { // simple judgment of file type based on suffix if (configInfo.getDataId().contains(SPOT)) { String extName = configInfo.getDataId().substring(configInfo.getDataId().lastIndexOf(SPOT) + 1); FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(extName); type = fileTypeEnum.getFileType(); } else { type = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(null).getFileType(); } } if (configAdvanceInfo == null) { configAdvanceInfo = new HashMap<>(16); } configAdvanceInfo.put("type", type); configAdvanceInfo.put("desc", configInfo.getDesc()); try { ConfigInfoStateWrapper foundCfg = findConfigInfoState(configInfo2Save.getDataId(), configInfo2Save.getGroup(), configInfo2Save.getTenant()); if (foundCfg != null) { throw new Throwable("DuplicateKeyException: config already exists, should be overridden"); } addConfigInfo(srcIp, srcUser, configInfo2Save, configAdvanceInfo, callFinally); succCount++; } catch (Throwable e) { if (!StringUtils.contains(e.toString(), "DuplicateKeyException")) { throw new NacosException(NacosException.SERVER_ERROR, e); } // uniqueness constraint conflict if (SameConfigPolicy.ABORT.equals(policy)) { failData = new ArrayList<>(); skipData = new ArrayList<>(); Map faileditem = new HashMap<>(2); faileditem.put("dataId", configInfo2Save.getDataId()); faileditem.put("group", configInfo2Save.getGroup()); failData.add(faileditem); for (int j = (i + 1); j < configInfoList.size(); j++) { ConfigInfo skipConfigInfo = configInfoList.get(j); Map skipitem = new HashMap<>(2); skipitem.put("dataId", skipConfigInfo.getDataId()); skipitem.put("group", skipConfigInfo.getGroup()); skipData.add(skipitem); skipCount++; } break; } else if (SameConfigPolicy.SKIP.equals(policy)) { skipCount++; if (skipData == null) { skipData = new ArrayList<>(); } Map skipitem = new HashMap<>(2); skipitem.put("dataId", configInfo2Save.getDataId()); skipitem.put("group", configInfo2Save.getGroup()); skipData.add(skipitem); } else if (SameConfigPolicy.OVERWRITE.equals(policy)) { succCount++; updateConfigInfo(configInfo2Save, srcIp, srcUser, configAdvanceInfo); } } } Map result = new HashMap<>(4); result.put("succCount", succCount); result.put("skipCount", skipCount); if (failData != null && !failData.isEmpty()) { result.put("failData", failData); } if (skipData != null && !skipData.isEmpty()) { result.put("skipData", skipData); } return result; } @Override public void removeConfigInfo(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser) { final Timestamp time = new Timestamp(System.currentTimeMillis()); ConfigAllInfo oldConfigAllInfo = findConfigAllInfo(dataId, group, tenant); if (Objects.nonNull(oldConfigAllInfo)) { try { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; removeConfigInfoAtomic(dataId, group, tenantTmp, srcIp, srcUser); removeTagByIdAtomic(oldConfigAllInfo.getId()); if (!CONFIG_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo.getId(), oldConfigAllInfo, srcIp, srcUser, time, "D", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(oldConfigAllInfo)); } EmbeddedStorageContextUtils.onDeleteConfigInfo(tenantTmp, group, dataId, srcIp, time); boolean result = databaseOperate.update(EmbeddedStorageContextHolder.getCurrentSqlContext()); if (!result) { throw new NacosConfigException("config deletion failed"); } } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } } @Override public List removeConfigInfoByIds(final List ids, final String srcIp, final String srcUser) { if (CollectionUtils.isEmpty(ids)) { return null; } ids.removeAll(Collections.singleton(null)); final Timestamp time = new Timestamp(System.currentTimeMillis()); try { String idsStr = StringUtils.join(ids, StringUtils.COMMA); List oldConfigAllInfoList = findAllConfigInfo4Export(null, null, null, null, ids); if (CollectionUtils.isNotEmpty(oldConfigAllInfoList)) { removeConfigInfoByIdsAtomic(idsStr); for (ConfigAllInfo configAllInfo : oldConfigAllInfoList) { removeTagByIdAtomic(configAllInfo.getId()); historyConfigInfoPersistService.insertConfigHistoryAtomic(configAllInfo.getId(), configAllInfo, srcIp, srcUser, time, "D", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo)); } } EmbeddedStorageContextUtils.onBatchDeleteConfigInfo(oldConfigAllInfoList); boolean result = databaseOperate.update(EmbeddedStorageContextHolder.getCurrentSqlContext()); if (!result) { throw new NacosConfigException("Failed to config batch deletion"); } return oldConfigAllInfoList; } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public void removeTagByIdAtomic(long id) { ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); final String sql = configTagsRelationMapper.delete(Collections.singletonList("id")); final Object[] args = new Object[] {id}; EmbeddedStorageContextHolder.addSqlContext(sql, args); } @Override public void removeConfigInfoAtomic(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {dataId, group, tenantTmp}; EmbeddedStorageContextHolder.addSqlContext(sql, args); } @Override public void removeConfigInfoByIdsAtomic(final String ids) { if (StringUtils.isBlank(ids)) { return; } List paramList = new ArrayList<>(); String[] idArr = ids.split(","); for (int i = 0; i < idArr.length; i++) { paramList.add(Long.parseLong(idArr[i])); } ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.IDS, paramList); MapperResult result = configInfoMapper.removeConfigInfoByIdsAtomic(context); EmbeddedStorageContextHolder.addSqlContext(result.getSql(), result.getParamList().toArray()); } @Override public ConfigOperateResult updateConfigInfo(final ConfigInfo configInfo, final String srcIp, final String srcUser, final Map configAdvanceInfo) { try { ConfigAllInfo oldConfigAllInfo = findConfigAllInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (oldConfigAllInfo == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } final String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); oldConfigAllInfo.setTenant(tenantTmp); String appNameTmp = oldConfigAllInfo.getAppName(); // If the appName passed by the user is not empty, the appName of the user is persisted; // otherwise, the appName of db is used. Empty string is required to clear appName if (configInfo.getAppName() == null) { configInfo.setAppName(appNameTmp); } updateConfigInfoAtomic(configInfo, srcIp, srcUser, configAdvanceInfo); String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); if (configTags != null) { // Delete all tags and recreate them removeTagByIdAtomic(oldConfigAllInfo.getId()); addConfigTagsRelation(oldConfigAllInfo.getId(), configTags, configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } Timestamp time = new Timestamp(System.currentTimeMillis()); if (!CONFIG_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo.getId(), oldConfigAllInfo, srcIp, srcUser, time, "U", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(oldConfigAllInfo)); } EmbeddedStorageContextUtils.onModifyConfigInfo(configInfo, srcIp, time); databaseOperate.blockUpdate(); return getConfigInfoOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult updateConfigInfoCas(final ConfigInfo configInfo, final String srcIp, final String srcUser, final Map configAdvanceInfo) { try { ConfigAllInfo oldConfigAllInfo = findConfigAllInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (oldConfigAllInfo == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } final String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); oldConfigAllInfo.setTenant(tenantTmp); String appNameTmp = oldConfigAllInfo.getAppName(); // If the appName passed by the user is not empty, the appName of the user is persisted; // otherwise, the appName of db is used. Empty string is required to clear appName if (configInfo.getAppName() == null) { configInfo.setAppName(appNameTmp); } updateConfigInfoAtomicCas(configInfo, srcIp, srcUser, configAdvanceInfo); String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); if (configTags != null) { // Delete all tags and recreate them removeTagByIdAtomic(oldConfigAllInfo.getId()); addConfigTagsRelation(oldConfigAllInfo.getId(), configTags, configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } Timestamp time = new Timestamp(System.currentTimeMillis()); if (!CONFIG_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo.getId(), oldConfigAllInfo, srcIp, srcUser, time, "U", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(oldConfigAllInfo)); } EmbeddedStorageContextUtils.onModifyConfigInfo(configInfo, srcIp, time); boolean success = databaseOperate.blockUpdate(); if (success) { return getConfigInfoOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } else { return new ConfigOperateResult(false); } } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } private ConfigOperateResult updateConfigInfoAtomicCas(final ConfigInfo configInfo, final String srcIp, final String srcUser, Map configAdvanceInfo) { final String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); final String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); final String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); final String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); final String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); final String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); final String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5Tmp); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); // Only update c_desc when desc is not null (empty string will also update) if (desc != null) { context.putUpdateParameter(FieldConstant.C_DESC, desc); } context.putUpdateParameter(FieldConstant.C_USE, use); context.putUpdateParameter(FieldConstant.EFFECT, effect); context.putUpdateParameter(FieldConstant.TYPE, type); context.putUpdateParameter(FieldConstant.C_SCHEMA, schema); context.putUpdateParameter(FieldConstant.ENCRYPTED_DATA_KEY, encryptedDataKey); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperResult mapperResult = configInfoMapper.updateConfigInfoAtomicCas(context); EmbeddedStorageContextHolder.addSqlContext(Boolean.TRUE, mapperResult.getSql(), mapperResult.getParamList().toArray()); return getConfigInfoOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } @Override public void updateConfigInfoAtomic(final ConfigInfo configInfo, final String srcIp, final String srcUser, Map configAdvanceInfo) { final String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); final String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); final String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); final String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); final String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); final String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); final String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); // Build update columns and parameters dynamically List updateColumns = new ArrayList<>(Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified@NOW()", "app_name")); List updateParams = new ArrayList<>(Arrays.asList(configInfo.getContent(), md5Tmp, srcIp, srcUser, appNameTmp)); // Only update c_desc when desc is not null (empty string will also update) if (desc != null) { updateColumns.add("c_desc"); updateParams.add(desc); } updateColumns.addAll(Arrays.asList("c_use", "effect", "type", "c_schema", "encrypted_data_key")); updateParams.addAll(Arrays.asList(use, effect, type, schema, encryptedDataKey)); // Add where parameters updateParams.addAll(Arrays.asList(configInfo.getDataId(), configInfo.getGroup(), tenantTmp)); final String sql = configInfoMapper.update(updateColumns, Arrays.asList("data_id", "group_id", "tenant_id")); EmbeddedStorageContextHolder.addSqlContext(sql, updateParams.toArray()); } @Override public long findConfigMaxId() { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperResult mapperResult = configInfoMapper.findConfigMaxId(null); return Optional.ofNullable(databaseOperate.queryOne(mapperResult.getSql(), Long.class)).orElse(0L); } @Override public ConfigInfo findConfigInfo(long id) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content"), Collections.singletonList("id")); return databaseOperate.queryOne(sql, new Object[] {id}, CONFIG_INFO_ROW_MAPPER); } @Override public ConfigInfoWrapper findConfigInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "type", "encrypted_data_key", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")); final Object[] args = new Object[] {dataId, group, tenantTmp}; return databaseOperate.queryOne(sql, args, CONFIG_INFO_WRAPPER_ROW_MAPPER); } @Override public Page findConfigInfo4Page(final int pageNo, final int pageSize, final String dataId, final String group, final String tenant, final Map configAdvanceInfo) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String content = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("content"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); MapperResult sql; MapperResult sqlCount; final MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); if (StringUtils.isNotBlank(dataId)) { context.putWhereParameter(FieldConstant.DATA_ID, dataId); } if (StringUtils.isNotBlank(group)) { context.putWhereParameter(FieldConstant.GROUP_ID, group); } if (StringUtils.isNotBlank(appName)) { context.putWhereParameter(FieldConstant.APP_NAME, appName); } if (!StringUtils.isBlank(content)) { context.putWhereParameter(FieldConstant.CONTENT, content); } context.setStartRow((pageNo - 1) * pageSize); context.setPageSize(pageSize); if (StringUtils.isNotBlank(configTags)) { String[] tagArr = configTags.split(","); context.putWhereParameter(FieldConstant.TAG_ARR, tagArr); ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); sqlCount = configTagsRelationMapper.findConfigInfo4PageCountRows(context); sql = configTagsRelationMapper.findConfigInfo4PageFetchRows(context); } else { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); sqlCount = configInfoMapper.findConfigInfo4PageCountRows(context); sql = configInfoMapper.findConfigInfo4PageFetchRows(context); } PaginationHelper helper = createPaginationHelper(); Page page = helper.fetchPageLimit(sqlCount, sql, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); for (ConfigInfo configInfo : page.getPageItems()) { Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), configInfo.getEncryptedDataKey(), configInfo.getContent()); configInfo.setContent(pair.getSecond()); // 查询并设置标签信息 List configTagList = selectTagByConfig(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (CollectionUtils.isNotEmpty(configTagList)) { String configTagsStr = String.join(",", configTagList); configInfo.setConfigTags(configTagsStr); } } return page; } @Override public int configInfoCount() { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); String sql = configInfoMapper.count(null); Integer result = databaseOperate.queryOne(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); } return result; } @Override public int configInfoCount(String tenant) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.TENANT_ID, tenant); MapperResult mapperResult = configInfoMapper.configInfoLikeTenantCount(context); Integer result = databaseOperate.queryOne(mapperResult.getSql(), mapperResult.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); } return result; } @Override public List getTenantIdList(int page, int pageSize) { PaginationHelper> helper = createPaginationHelper(); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); int from = (page - 1) * pageSize; MapperResult mapperResult = configInfoMapper.getTenantIdList(new MapperContext(from, pageSize)); Page> pageList = helper.fetchPageLimit(mapperResult.getSql(), mapperResult.getParamList().toArray(), page, pageSize, MAP_ROW_MAPPER); return pageList.getPageItems().stream().map(map -> String.valueOf(map.get("TENANT_ID"))) .collect(Collectors.toList()); } @Override public List getGroupIdList(int page, int pageSize) { PaginationHelper> helper = createPaginationHelper(); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); int from = (page - 1) * pageSize; MapperResult mapperResult = configInfoMapper.getGroupIdList(new MapperContext(from, pageSize)); Page> pageList = helper.fetchPageLimit(mapperResult.getSql(), mapperResult.getParamList().toArray(), page, pageSize, MAP_ROW_MAPPER); return pageList.getPageItems().stream().map(map -> String.valueOf(map.get("GROUP_ID"))) .collect(Collectors.toList()); } @Override public Page findAllConfigInfoFragment(final long lastMaxId, final int pageSize, boolean needContent) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(0, pageSize); context.putContextParameter(ContextConstant.NEED_CONTENT, String.valueOf(needContent)); context.putWhereParameter(FieldConstant.ID, lastMaxId); MapperResult select = configInfoMapper.findAllConfigInfoFragment(context); PaginationHelper helper = createPaginationHelper(); return helper.fetchPageLimit(select.getSql(), select.getParamList().toArray(), 1, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); } @Override public Page findConfigInfoLike4Page(final int pageNo, final int pageSize, final String dataId, final String group, final String tenant, final Map configAdvanceInfo) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String content = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("content"); final String types = Optional.ofNullable(configAdvanceInfo).map(e -> (String) e.get(ParametersField.TYPES)) .orElse(null); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); MapperResult sqlCountRows; MapperResult sqlFetchRows; MapperContext context = new MapperContext((pageNo - 1) * pageSize, pageSize); context.putWhereParameter(FieldConstant.TENANT_ID, generateLikeArgument(tenantTmp)); if (!StringUtils.isBlank(dataId)) { context.putWhereParameter(FieldConstant.DATA_ID, generateLikeArgument(dataId)); } if (!StringUtils.isBlank(group)) { context.putWhereParameter(FieldConstant.GROUP_ID, generateLikeArgument(group)); } if (!StringUtils.isBlank(appName)) { context.putWhereParameter(FieldConstant.APP_NAME, appName); } if (!StringUtils.isBlank(content)) { context.putWhereParameter(FieldConstant.CONTENT, generateLikeArgument(content)); } if (StringUtils.isNotBlank(types)) { String[] typesArr = types.split(Symbols.COMMA); context.putWhereParameter(FieldConstant.TYPE, typesArr); } if (StringUtils.isNotBlank(configTags)) { String[] tagArr = configTags.split(","); for (int i = 0; i < tagArr.length; i++) { tagArr[i] = generateLikeArgument(tagArr[i]); } context.putWhereParameter(FieldConstant.TAG_ARR, tagArr); ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); sqlCountRows = configTagsRelationMapper.findConfigInfoLike4PageCountRows(context); sqlFetchRows = configTagsRelationMapper.findConfigInfoLike4PageFetchRows(context); } else { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); sqlCountRows = configInfoMapper.findConfigInfoLike4PageCountRows(context); sqlFetchRows = configInfoMapper.findConfigInfoLike4PageFetchRows(context); } PaginationHelper helper = createPaginationHelper(); Page page = helper.fetchPageLimit(sqlCountRows, sqlFetchRows, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); for (ConfigInfo configInfo : page.getPageItems()) { Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), configInfo.getEncryptedDataKey(), configInfo.getContent()); configInfo.setContent(pair.getSecond()); // 查询并设置标签信息 List configTagList = selectTagByConfig(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (CollectionUtils.isNotEmpty(configTagList)) { String configTagsStr = String.join(",", configTagList); configInfo.setConfigTags(configTagsStr); } } return page; } @Override public List findChangeConfig(final Timestamp startTime, long lastMaxId, final int pageSize) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.PAGE_SIZE, pageSize); context.putWhereParameter(FieldConstant.LAST_MAX_ID, lastMaxId); MapperResult mapperResult = configInfoMapper.findChangeConfig(context); return databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } @Override public List selectTagByConfig(String dataId, String group, String tenant) { ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); String sql = configTagsRelationMapper.select(Collections.singletonList("tag_name"), Arrays.asList("data_id", "group_id", "tenant_id")); return databaseOperate.queryMany(sql, new Object[] {dataId, group, tenant}, String.class); } @Override public List findConfigInfosByIds(final String ids) { if (StringUtils.isBlank(ids)) { return null; } List paramList = new ArrayList<>(); String[] idArr = ids.split(","); for (int i = 0; i < idArr.length; i++) { paramList.add(Long.parseLong(idArr[i])); } ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.IDS, paramList); MapperResult mapperResult = configInfoMapper.findConfigInfosByIds(context); return databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_ROW_MAPPER); } @Override public ConfigAdvanceInfo findConfigAdvanceInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; List configTagList = this.selectTagByConfig(dataId, group, tenant); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); ConfigAdvanceInfo configAdvance = databaseOperate.queryOne(configInfoMapper.select( Arrays.asList("gmt_create", "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", "c_schema"), Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_ADVANCE_INFO_ROW_MAPPER); if (CollectionUtils.isNotEmpty(configTagList)) { StringBuilder configTagsTmp = new StringBuilder(); for (String configTag : configTagList) { if (configTagsTmp.length() == 0) { configTagsTmp.append(configTag); } else { configTagsTmp.append(',').append(configTag); } } configAdvance.setConfigTags(configTagsTmp.toString()); } return configAdvance; } @Override public ConfigAllInfo findConfigAllInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "gmt_create", "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")); List configTagList = selectTagByConfig(dataId, group, tenant); ConfigAllInfo configAdvance = databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp}, CONFIG_ALL_INFO_ROW_MAPPER); if (configTagList != null && !configTagList.isEmpty()) { StringBuilder configTagsTmp = new StringBuilder(); for (String configTag : configTagList) { if (configTagsTmp.length() == 0) { configTagsTmp.append(configTag); } else { configTagsTmp.append(',').append(configTag); } } configAdvance.setConfigTags(configTagsTmp.toString()); } return configAdvance; } @Override public List findAllConfigInfo4Export(final String dataId, final String group, final String tenant, final String appName, final List ids) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); if (!CollectionUtils.isEmpty(ids)) { context.putWhereParameter(FieldConstant.IDS, ids); } else { context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); if (!StringUtils.isBlank(dataId)) { context.putWhereParameter(FieldConstant.DATA_ID, generateLikeArgument(dataId)); } if (StringUtils.isNotBlank(group)) { context.putWhereParameter(FieldConstant.GROUP_ID, group); } if (StringUtils.isNotBlank(appName)) { context.putWhereParameter(FieldConstant.APP_NAME, appName); } } MapperResult mapperResult = configInfoMapper.findAllConfigInfo4Export(context); List configAllInfos = databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_ALL_INFO_ROW_MAPPER); if (CollectionUtils.isEmpty(configAllInfos)) { return configAllInfos; } for (ConfigAllInfo configAllInfo : configAllInfos) { List configTagList = selectTagByConfig(configAllInfo.getDataId(), configAllInfo.getGroup(), configAllInfo.getTenant()); if (CollectionUtils.isNotEmpty(configTagList)) { StringBuilder configTags = new StringBuilder(); for (String configTag : configTagList) { if (configTags.length() == 0) { configTags.append(configTag); } else { configTags.append(',').append(configTag); } } configAllInfo.setConfigTags(configTags.toString()); } } return configAllInfos; } @Override public List queryConfigInfoByNamespace(String tenantId) { if (Objects.isNull(tenantId)) { throw new IllegalArgumentException("tenantId can not be null"); } String tenantTmp = StringUtils.isBlank(tenantId) ? StringUtils.EMPTY : tenantId; ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.select( Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "type", "gmt_modified"), Collections.singletonList("tenant_id")); return databaseOperate.queryMany(sql, new Object[] {tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoTagPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.model.event.DerbyImportEvent; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedPaginationHelperImpl; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoTagMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.sql.Timestamp; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER; /** * EmbeddedConfigInfoTagPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnEmbeddedStorage.class) @Service("embeddedConfigInfoTagPersistServiceImpl") public class EmbeddedConfigInfoTagPersistServiceImpl implements ConfigInfoTagPersistService { private DataSourceService dataSourceService; private final DatabaseOperate databaseOperate; private MapperManager mapperManager; /** * The constructor sets the dependency injection order. * * @param databaseOperate databaseOperate. */ public EmbeddedConfigInfoTagPersistServiceImpl(DatabaseOperate databaseOperate) { this.databaseOperate = databaseOperate; this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); NotifyCenter.registerToSharePublisher(DerbyImportEvent.class); } @Override public PaginationHelper createPaginationHelper() { return new EmbeddedPaginationHelperImpl<>(databaseOperate); } @Override public ConfigInfoStateWrapper findConfigInfo4TagState(final String dataId, final String group, final String tenant, String tag) { ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); String sql = configInfoTagMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp, tagTmp}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } private ConfigOperateResult getTagOperateResult(String dataId, String group, String tenant, String tag) { String tenantTmp = StringUtils.defaultEmptyIfBlank(tenant); ConfigInfoStateWrapper configInfo4Tag = this.findConfigInfo4TagState(dataId, group, tenantTmp, tag); if (configInfo4Tag == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfo4Tag.getId(), configInfo4Tag.getLastModified()); } @Override public ConfigOperateResult addConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); final String sql = configInfoTagMapper.insert( Arrays.asList("data_id", "group_id", "tenant_id", "tag_id", "app_name", "content", "md5", "src_ip", "src_user", "gmt_create", "gmt_modified")); Timestamp time = new Timestamp(System.currentTimeMillis()); final Object[] args = new Object[] {configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, appNameTmp, configInfo.getContent(), md5, srcIp, srcUser, time, time}; EmbeddedStorageContextUtils.onModifyConfigTagInfo(configInfo, tagTmp, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(sql, args); databaseOperate.blockUpdate(); return getTagOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult insertOrUpdateTag(final ConfigInfo configInfo, final String tag, final String srcIp, final String srcUser) { if (findConfigInfo4TagState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), tag) == null) { return addConfigInfo4Tag(configInfo, tag, srcIp, srcUser); } else { return updateConfigInfo4Tag(configInfo, tag, srcIp, srcUser); } } @Override public ConfigOperateResult insertOrUpdateTagCas(final ConfigInfo configInfo, final String tag, final String srcIp, final String srcUser) { if (findConfigInfo4TagState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), tag) == null) { return addConfigInfo4Tag(configInfo, tag, srcIp, srcUser); } else { return updateConfigInfo4TagCas(configInfo, tag, srcIp, srcUser); } } @Override public void removeConfigInfoTag(final String dataId, final String group, final String tenant, final String tag, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag; ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); final String sql = configInfoTagMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")); final Object[] args = new Object[] {dataId, group, tenantTmp, tagTmp}; EmbeddedStorageContextUtils.onDeleteConfigTagInfo(tenantTmp, group, dataId, tagTmp, srcIp); EmbeddedStorageContextHolder.addSqlContext(sql, args); try { databaseOperate.update(EmbeddedStorageContextHolder.getCurrentSqlContext()); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult updateConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); Timestamp time = new Timestamp(System.currentTimeMillis()); final String sql = configInfoTagMapper.update( Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name"), Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")); final Object[] args = new Object[] {configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp}; EmbeddedStorageContextUtils.onModifyConfigTagInfo(configInfo, tagTmp, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(sql, args); databaseOperate.blockUpdate(); return getTagOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigOperateResult updateConfigInfo4TagCas(ConfigInfo configInfo, String tag, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); Timestamp time = new Timestamp(System.currentTimeMillis()); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, time); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.TAG_ID, tagTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); final MapperResult mapperResult = configInfoTagMapper.updateConfigInfo4TagCas(context); EmbeddedStorageContextUtils.onModifyConfigTagInfo(configInfo, tagTmp, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(mapperResult.getSql(), mapperResult.getParamList().toArray()); Boolean success = databaseOperate.blockUpdate(); if (success) { return getTagOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); } else { return new ConfigOperateResult(false); } } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } @Override public ConfigInfoTagWrapper findConfigInfo4Tag(final String dataId, final String group, final String tenant, final String tag) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); final String sql = configInfoTagMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "tag_id", "app_name", "content", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")); return databaseOperate.queryOne(sql, new Object[] {dataId, group, tenantTmp, tagTmp}, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } @Override public int configInfoTagCount() { ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); String sql = configInfoTagMapper.count(null); Integer result = databaseOperate.queryOne(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoBetaCount error"); } return result; } @Override public Page findAllConfigInfoTagForDumpAll(final int pageNo, final int pageSize) { final int startRow = (pageNo - 1) * pageSize; ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); String sqlCountRows = configInfoTagMapper.count(null); MapperResult sqlFetchRows = configInfoTagMapper.findAllConfigInfoTagForDumpAllFetchRows( new MapperContext(startRow, pageSize)); PaginationHelper helper = createPaginationHelper(); return helper.fetchPageLimit(sqlCountRows, sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), pageNo, pageSize, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } @Override public List findConfigInfoTags(String dataId, String group, String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); final String sql = configInfoTagMapper.select(Collections.singletonList("tag_id"), Arrays.asList("data_id", "group_id", "tenant_id")); return databaseOperate.queryMany(sql, new Object[] {dataId, group, tenantTmp}, String.class); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigMigratePersistServiceImpl.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigMigratePersistService; import com.alibaba.nacos.config.server.service.sql.EmbeddedStorageContextUtils; import com.alibaba.nacos.core.distributed.id.IdGeneratorManager; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.persistence.model.event.DerbyImportEvent; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedPaginationHelperImpl; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoGrayMapper; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; import com.alibaba.nacos.plugin.datasource.mapper.ConfigMigrateMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.sql.Timestamp; import java.util.Arrays; import java.util.List; import java.util.Map; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER; /** * The type Embedded config migrate persist service. * * @author Sunrisea */ @Conditional(value = ConditionOnEmbeddedStorage.class) @Service("embeddedConfigMigratePersistServiceImpl") public class EmbeddedConfigMigratePersistServiceImpl implements ConfigMigratePersistService { private static final String RESOURCE_CONFIG_INFO_ID = "config-info-id"; private static final String RESOURCE_CONFIG_HISTORY_GRAY_ID = "config-history-gray-id"; private final DatabaseOperate databaseOperate; private final IdGeneratorManager idGeneratorManager; private DataSourceService dataSourceService; private MapperManager mapperManager; private ConfigInfoPersistService configInfoPersistService; private ConfigInfoGrayPersistService configInfoGrayPersistService; /** * Instantiates a new Embedded config migrate persist service. * * @param databaseOperate the database operate * @param idGeneratorManager the id generator manager * @param configInfoPersistService the config info persist service * @param configInfoGrayPersistService the config info gray persist service */ public EmbeddedConfigMigratePersistServiceImpl(DatabaseOperate databaseOperate, IdGeneratorManager idGeneratorManager, @Qualifier("embeddedConfigInfoPersistServiceImpl") ConfigInfoPersistService configInfoPersistService, @Qualifier("embeddedConfigInfoGrayPersistServiceImpl") ConfigInfoGrayPersistService configInfoGrayPersistService) { this.databaseOperate = databaseOperate; this.idGeneratorManager = idGeneratorManager; this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); idGeneratorManager.register(RESOURCE_CONFIG_INFO_ID, RESOURCE_CONFIG_HISTORY_GRAY_ID); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); NotifyCenter.registerToSharePublisher(DerbyImportEvent.class); this.configInfoPersistService = configInfoPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; } @Override public PaginationHelper createPaginationHelper() { return new EmbeddedPaginationHelperImpl<>(databaseOperate); } @Override public Integer configInfoConflictCount(String srcUser) { ConfigMigrateMapper configInfoMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); MapperResult mapperResult = configInfoMigrateMapper.getConfigConflictCount(context); Integer result = databaseOperate.queryOne(mapperResult.getSql(), mapperResult.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoConflictCount error"); } return result; } @Override public Integer configInfoGrayConflictCount(String srcUser) { ConfigMigrateMapper configInfoMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); MapperResult mapperResult = configInfoMigrateMapper.getConfigGrayConflictCount(context); Integer result = databaseOperate.queryOne(mapperResult.getSql(), mapperResult.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoGrayConflictCount error"); } return result; } @Override public List getMigrateConfigInsertIdList(long startId, int pageSize) { ConfigMigrateMapper configInfoMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, startId); context.setPageSize(pageSize); MapperResult mapperResult = configInfoMigrateMapper.findConfigIdNeedInsertMigrate(context); return databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), Long.class); } @Override public List getMigrateConfigGrayInsertIdList(long startId, int pageSize) { ConfigMigrateMapper configInfoMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, startId); context.setPageSize(pageSize); MapperResult mapperResult = configInfoMigrateMapper.findConfigGrayIdNeedInsertMigrate(context); return databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), Long.class); } @Override public List getMigrateConfigUpdateList(long startId, int pageSize, String srcTenant, String targetTenant, String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); context.putWhereParameter(FieldConstant.ID, startId); context.putWhereParameter(FieldConstant.SRC_TENANT, srcTenant); context.putWhereParameter(FieldConstant.TARGET_TENANT, targetTenant); context.setPageSize(pageSize); MapperResult mapperResult = configMigrateMapper.findConfigNeedUpdateMigrate(context); return databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_ROW_MAPPER); } @Override public List getMigrateConfigGrayUpdateList(long startId, int pageSize, String srcTenant, String targetTenant, String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); context.putWhereParameter(FieldConstant.ID, startId); context.putWhereParameter(FieldConstant.SRC_TENANT, srcTenant); context.putWhereParameter(FieldConstant.TARGET_TENANT, targetTenant); context.setPageSize(pageSize); MapperResult mapperResult = configMigrateMapper.findConfigGrayNeedUpdateMigrate(context); return databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } @Override public void migrateConfigInsertByIds(List ids, String srcUser) { ConfigMigrateMapper configInfoMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); for (Long targetId : ids) { long configId = idGeneratorManager.nextId(RESOURCE_CONFIG_INFO_ID); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.TARGET_ID, targetId); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); context.putWhereParameter(FieldConstant.ID, configId); MapperResult result = configInfoMigrateMapper.migrateConfigInsertByIds(context); EmbeddedStorageContextHolder.addSqlContext(result.getSql(), result.getParamList().toArray()); } databaseOperate.blockUpdate(); } @Override public void migrateConfigGrayInsertByIds(List ids, String srcUser) { ConfigMigrateMapper configInfoMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); for (Long targetId : ids) { long configId = idGeneratorManager.nextId(RESOURCE_CONFIG_HISTORY_GRAY_ID); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.TARGET_ID, targetId); context.putWhereParameter(FieldConstant.ID, configId); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); MapperResult result = configInfoMigrateMapper.migrateConfigGrayInsertByIds(context); EmbeddedStorageContextHolder.addSqlContext(result.getSql(), result.getParamList().toArray()); } databaseOperate.blockUpdate(); } @Override public void syncConfig(String dataId, String group, String tenant, String targetTenant, String srcUser) { ConfigInfoWrapper sourceConfigInfoWrapper = configInfoPersistService.findConfigInfo(dataId, group, tenant); ConfigInfoWrapper targetConfigInfoWrapper = configInfoPersistService.findConfigInfo(dataId, group, targetTenant); if (sourceConfigInfoWrapper == null) { configInfoPersistService.removeConfigInfo(dataId, group, targetTenant, null, srcUser); } else { if (targetConfigInfoWrapper == null) { sourceConfigInfoWrapper.setTenant(targetTenant); long configId = idGeneratorManager.nextId(RESOURCE_CONFIG_INFO_ID); configInfoPersistService.addConfigInfoAtomic(configId, null, srcUser, sourceConfigInfoWrapper, null); } else if (sourceConfigInfoWrapper.getLastModified() >= targetConfigInfoWrapper.getLastModified()) { sourceConfigInfoWrapper.setTenant(targetTenant); updateConfigInfoAtomic(sourceConfigInfoWrapper, null, srcUser, null, targetConfigInfoWrapper.getLastModified(), targetConfigInfoWrapper.getMd5()); } } } /** * Update config info atomic. * * @param configInfo the config info * @param srcIp the src ip * @param srcUser the src user * @param configAdvanceInfo the config advance info * @param lastModified the last modified */ public void updateConfigInfoAtomic(final ConfigInfo configInfo, final String srcIp, final String srcUser, Map configAdvanceInfo, long lastModified, final String targetMd5) { final String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); final String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); final String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); final String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); final String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); final String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); final String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); final String sql = configInfoMapper.update( Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified@NOW()", "app_name", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id", "gmt_modified", "md5")); final Object[] args = new Object[] {configInfo.getContent(), md5Tmp, srcIp, srcUser, appNameTmp, desc, use, effect, type, schema, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, new Timestamp(lastModified), targetMd5}; EmbeddedStorageContextHolder.addSqlContext(sql, args); } @Override public void syncConfigGray(String dataId, String group, String tenant, String grayName, String targetTenant, String srcUser) { ConfigInfoGrayWrapper sourceConfigInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, grayName); ConfigInfoGrayWrapper targetConfigInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, targetTenant, grayName); if (sourceConfigInfoGrayWrapper == null) { removeConfigInfoGrayWithoutHistory(dataId, group, targetTenant, grayName, null, srcUser); } else { if (targetConfigInfoGrayWrapper == null) { sourceConfigInfoGrayWrapper.setTenant(targetTenant); long configGrayId = idGeneratorManager.nextId(RESOURCE_CONFIG_HISTORY_GRAY_ID); configInfoGrayPersistService.addConfigInfoGrayAtomic(configGrayId, sourceConfigInfoGrayWrapper, grayName, sourceConfigInfoGrayWrapper.getGrayRule(), null, srcUser); } else if (sourceConfigInfoGrayWrapper.getLastModified() >= targetConfigInfoGrayWrapper.getLastModified()) { sourceConfigInfoGrayWrapper.setTenant(targetTenant); updateConfigInfo4GrayWithoutHistory(sourceConfigInfoGrayWrapper, sourceConfigInfoGrayWrapper.getGrayName(), sourceConfigInfoGrayWrapper.getGrayRule(), null, srcUser, targetConfigInfoGrayWrapper.getLastModified(), targetConfigInfoGrayWrapper.getMd5()); } } } /** * Remove config info gray without history. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param grayName the gray name * @param srcIp the src ip * @param srcUser the src user */ public void removeConfigInfoGrayWithoutHistory(final String dataId, final String group, final String tenant, final String grayName, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName; ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); final String sql = configInfoGrayMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")); final Object[] args = new Object[] {dataId, group, tenantTmp, grayNameTmp}; EmbeddedStorageContextUtils.onDeleteConfigGrayInfo(tenantTmp, group, dataId, grayNameTmp, srcIp); EmbeddedStorageContextHolder.addSqlContext(sql, args); try { databaseOperate.update(EmbeddedStorageContextHolder.getCurrentSqlContext()); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } /** * Update config info 4 gray without history. * * @param configInfo the config info * @param grayName the gray name * @param grayRule the gray rule * @param srcIp the src ip * @param srcUser the src user */ public void updateConfigInfo4GrayWithoutHistory(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser, long lastModified, final String targetMd5) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); configInfo.setTenant(tenantTmp); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); Timestamp time = new Timestamp(System.currentTimeMillis()); final String sql = configInfoGrayMapper.update( Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name", "gray_rule"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name", "gmt_modified", "md5")); final Object[] args = new Object[] {configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, grayRuleTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp, new Timestamp(lastModified), targetMd5}; EmbeddedStorageContextUtils.onModifyConfigGrayInfo(configInfo, grayNameTmp, grayRuleTmp, srcIp, time); EmbeddedStorageContextHolder.addSqlContext(sql, args); databaseOperate.blockUpdate(); } finally { EmbeddedStorageContextHolder.cleanAllContext(); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedHistoryConfigInfoPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.model.event.DerbyImportEvent; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedPaginationHelperImpl; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.HistoryConfigInfoMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_DETAIL_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_LIST_ROW_MAPPER; /** * EmbeddedHistoryConfigInfoPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnEmbeddedStorage.class) @Service("embeddedHistoryConfigInfoPersistServiceImpl") public class EmbeddedHistoryConfigInfoPersistServiceImpl implements HistoryConfigInfoPersistService { private DataSourceService dataSourceService; private final DatabaseOperate databaseOperate; private MapperManager mapperManager; /** * The constructor sets the dependency injection order. * * @param databaseOperate databaseOperate. */ public EmbeddedHistoryConfigInfoPersistServiceImpl(DatabaseOperate databaseOperate) { this.databaseOperate = databaseOperate; this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); NotifyCenter.registerToSharePublisher(DerbyImportEvent.class); } @Override public PaginationHelper createPaginationHelper() { return new EmbeddedPaginationHelperImpl<>(databaseOperate); } @Override public void insertConfigHistoryAtomic(long configHistoryId, ConfigInfo configInfo, String srcIp, String srcUser, final Timestamp time, String ops, String publishType, String grayName, String extInfo) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); String publishTypeTmp = StringUtils.defaultEmptyIfBlank(publishType); String encryptedDataKey = StringUtils.defaultEmptyIfBlank(configInfo.getEncryptedDataKey()); String grayNameTemp = StringUtils.defaultEmptyIfBlank(grayName); HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); final String sql = historyConfigInfoMapper.insert( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user", "gmt_modified", "op_type", "publish_type", "gray_name", "ext_info", "encrypted_data_key")); final Object[] args = new Object[] {configHistoryId, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5Tmp, srcIp, srcUser, time, ops, publishTypeTmp, grayNameTemp, extInfo, encryptedDataKey}; EmbeddedStorageContextHolder.addSqlContext(sql, args); } @Override public void removeConfigHistory(final Timestamp startTime, final int limitSize) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.LIMIT_SIZE, limitSize); MapperResult mapperResult = historyConfigInfoMapper.removeConfigHistory(context); PaginationHelper helper = createPaginationHelper(); helper.updateLimit(mapperResult.getSql(), mapperResult.getParamList().toArray()); } @Override public List findDeletedConfig(final Timestamp startTime, long lastMaxId, final int pageSize, String publishType) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.PAGE_SIZE, pageSize); context.putWhereParameter(FieldConstant.LAST_MAX_ID, lastMaxId); context.putWhereParameter(FieldConstant.PUBLISH_TYPE, publishType); MapperResult mapperResult = historyConfigInfoMapper.findDeletedConfig(context); List configHistoryInfos = databaseOperate.queryMany(mapperResult.getSql(), mapperResult.getParamList().toArray(), HISTORY_DETAIL_ROW_MAPPER); List configInfoStateWrappers = new ArrayList<>(); for (ConfigHistoryInfo configHistoryInfo : configHistoryInfos) { ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setId(configHistoryInfo.getId()); configInfoStateWrapper.setDataId(configHistoryInfo.getDataId()); configInfoStateWrapper.setGroup(configHistoryInfo.getGroup()); configInfoStateWrapper.setTenant(configHistoryInfo.getTenant()); configInfoStateWrapper.setMd5(configHistoryInfo.getMd5()); configInfoStateWrapper.setLastModified(configHistoryInfo.getLastModifiedTime().getTime()); configInfoStateWrapper.setGrayName(configHistoryInfo.getGrayName()); configInfoStateWrappers.add(configInfoStateWrapper); } return configInfoStateWrappers; } @Override public Page findConfigHistory(String dataId, String group, String tenant, int pageNo, int pageSize) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext((pageNo - 1) * pageSize, pageSize); context.putWhereParameter(FieldConstant.DATA_ID, dataId); context.putWhereParameter(FieldConstant.GROUP_ID, group); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); String sqlCountRows = historyConfigInfoMapper.count(Arrays.asList("data_id", "group_id", "tenant_id")); MapperResult sqlFetchRows = historyConfigInfoMapper.pageFindConfigHistoryFetchRows(context); PaginationHelper helper = createPaginationHelper(); return helper.fetchPage(sqlCountRows, sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), pageNo, pageSize, HISTORY_LIST_ROW_MAPPER); } @Override public ConfigHistoryInfo detailConfigHistory(Long nid) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); String sqlFetchRows = historyConfigInfoMapper.select( Arrays.asList("nid", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_user", "src_ip", "op_type", "publish_type", "gray_name", "ext_info", "gmt_create", "gmt_modified", "encrypted_data_key"), Collections.singletonList("nid")); return databaseOperate.queryOne(sqlFetchRows, new Object[] {nid}, HISTORY_DETAIL_ROW_MAPPER); } @Override public ConfigHistoryInfo detailPreviousConfigHistory(Long id) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, id); MapperResult sqlFetchRows = historyConfigInfoMapper.detailPreviousConfigHistory(context); return databaseOperate.queryOne(sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), HISTORY_DETAIL_ROW_MAPPER); } @Override public int findConfigHistoryCountByTime(final Timestamp startTime) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); MapperResult sqlFetchRows = historyConfigInfoMapper.findConfigHistoryCountByTime(context); Integer result = databaseOperate.queryOne(sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("findConfigHistoryCountByTime error"); } return result; } @Override public ConfigHistoryInfo getNextHistoryInfo(String dataId, String group, String tenant, String publishType, String grayName, long startNid) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.DATA_ID, dataId); context.putWhereParameter(FieldConstant.GROUP_ID, group); context.putWhereParameter(FieldConstant.TENANT_ID, tenant); context.putWhereParameter(FieldConstant.PUBLISH_TYPE, publishType); context.putWhereParameter(FieldConstant.GRAY_NAME, grayName); context.putWhereParameter(FieldConstant.NID, startNid); MapperResult sqlFetchRows = historyConfigInfoMapper.getNextHistoryInfo(context); return databaseOperate.queryOne(sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), HISTORY_DETAIL_ROW_MAPPER); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoBetaPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.extrnal.ExternalStoragePaginationHelperImpl; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoBetaMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Conditional; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; /** * ExternalConfigInfoBetaPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnExternalStorage.class) @Service("externalConfigInfoBetaPersistServiceImpl") public class ExternalConfigInfoBetaPersistServiceImpl implements ConfigInfoBetaPersistService { private DataSourceService dataSourceService; protected JdbcTemplate jt; protected TransactionTemplate tjt; private MapperManager mapperManager; public ExternalConfigInfoBetaPersistServiceImpl() { this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jt = dataSourceService.getJdbcTemplate(); this.tjt = dataSourceService.getTransactionTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); } @Override public PaginationHelper createPaginationHelper() { return new ExternalStoragePaginationHelperImpl<>(jt); } @Override public ConfigOperateResult addConfigInfo4Beta(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE); String encryptedDataKey = StringUtils.defaultEmptyIfBlank(configInfo.getEncryptedDataKey()); try { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); jt.update(configInfoBetaMapper.insert( Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "beta_ips", "src_ip", "src_user", "gmt_create@NOW()", "gmt_modified@NOW()", "encrypted_data_key")), configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5, betaIps, srcIp, srcUser, encryptedDataKey); return getBetaOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigOperateResult insertOrUpdateBeta(final ConfigInfo configInfo, final String betaIps, final String srcIp, final String srcUser) { ConfigInfoStateWrapper configInfo4BetaState = this.findConfigInfo4BetaState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (configInfo4BetaState == null) { return addConfigInfo4Beta(configInfo, betaIps, srcIp, srcUser); } else { return updateConfigInfo4Beta(configInfo, betaIps, srcIp, srcUser); } } @Override public ConfigOperateResult insertOrUpdateBetaCas(final ConfigInfo configInfo, final String betaIps, final String srcIp, final String srcUser) { ConfigInfoStateWrapper configInfo4BetaState = this.findConfigInfo4BetaState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (configInfo4BetaState == null) { return addConfigInfo4Beta(configInfo, betaIps, srcIp, srcUser); } else { return updateConfigInfo4BetaCas(configInfo, betaIps, srcIp, srcUser); } } @Override public void removeConfigInfo4Beta(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; tjt.execute(status -> { try { ConfigInfoStateWrapper configInfo = findConfigInfo4BetaState(dataId, group, tenant); if (configInfo != null) { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); jt.update(configInfoBetaMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")), dataId, group, tenantTmp); } } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } return Boolean.TRUE; }); } @Override public ConfigOperateResult updateConfigInfo4Beta(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); String encryptedDataKey = StringUtils.defaultEmptyIfBlank(configInfo.getEncryptedDataKey()); try { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); jt.update(configInfoBetaMapper.update( Arrays.asList("content", "md5", "beta_ips", "src_ip", "src_user", "gmt_modified@NOW()", "app_name", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")), configInfo.getContent(), md5, betaIps, srcIp, srcUser, appNameTmp, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp); return getBetaOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigInfoStateWrapper findConfigInfo4BetaState(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { return this.jt.queryForObject( "SELECT id,data_id,group_id,tenant_id,gmt_modified FROM config_info_beta WHERE data_id=? AND group_id=? AND tenant_id=? ", new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; } } private ConfigOperateResult getBetaOperateResult(String dataId, String group, String tenant) { ConfigInfoStateWrapper configInfo4Beta = this.findConfigInfo4BetaState(dataId, group, tenant); if (configInfo4Beta == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfo4Beta.getId(), configInfo4Beta.getLastModified()); } @Override public ConfigOperateResult updateConfigInfo4BetaCas(ConfigInfo configInfo, String betaIps, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); try { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5); context.putUpdateParameter(FieldConstant.BETA_IPS, betaIps); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); MapperResult mapperResult = configInfoBetaMapper.updateConfigInfo4BetaCas(context); final String sql = mapperResult.getSql(); List paramList = mapperResult.getParamList(); final Object[] args = paramList.toArray(); boolean result = jt.update(sql, args) > 0; if (result) { return getBetaOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp); } else { return new ConfigOperateResult(false); } } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigInfoBetaWrapper findConfigInfo4Beta(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); return this.jt.queryForObject(configInfoBetaMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "beta_ips", "encrypted_data_key", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public int configInfoBetaCount() { ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); String sql = configInfoBetaMapper.count(null); Integer result = jt.queryForObject(sql, Integer.class); return result.intValue(); } @Override public Page findAllConfigInfoBetaForDumpAll(final int pageNo, final int pageSize) { final int startRow = (pageNo - 1) * pageSize; ConfigInfoBetaMapper configInfoBetaMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_BETA); String sqlCountRows = configInfoBetaMapper.count(null); MapperContext context = new MapperContext(); context.setStartRow(startRow); context.setPageSize(pageSize); MapperResult mapperResult = configInfoBetaMapper.findAllConfigInfoBetaForDumpAllFetchRows(context); String sqlFetchRows = mapperResult.getSql(); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(sqlCountRows, sqlFetchRows, new Object[] {}, pageNo, pageSize, CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoGrayPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExtInfoUtil; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.extrnal.ExternalStoragePaginationHelperImpl; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoGrayMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Conditional; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallbackWithoutResult; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Timestamp; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.utils.PropertyUtil.GRAY_MIGRATE_FLAG; /** * ExternalConfigInfoGrayPersistServiceImpl. * * @author rong */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnExternalStorage.class) @Service("externalConfigInfoGrayPersistServiceImpl") public class ExternalConfigInfoGrayPersistServiceImpl implements ConfigInfoGrayPersistService { private DataSourceService dataSourceService; protected JdbcTemplate jt; protected TransactionTemplate tjt; private MapperManager mapperManager; private HistoryConfigInfoPersistService historyConfigInfoPersistService; public ExternalConfigInfoGrayPersistServiceImpl( @Qualifier("externalHistoryConfigInfoPersistServiceImpl") HistoryConfigInfoPersistService historyConfigInfoPersistService) { this.historyConfigInfoPersistService = historyConfigInfoPersistService; this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jt = dataSourceService.getJdbcTemplate(); this.tjt = dataSourceService.getTransactionTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); } @Override public PaginationHelper createPaginationHelper() { return new ExternalStoragePaginationHelperImpl<>(jt); } @Override public ConfigInfoStateWrapper findConfigInfo4GrayState(final String dataId, final String group, final String tenant, String grayName) { ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); try { return this.jt.queryForObject(configInfoGrayMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "gray_rule", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")), new Object[] {dataId, group, tenantTmp, grayNameTmp}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { return null; } } private ConfigOperateResult getGrayOperateResult(String dataId, String group, String tenant, String grayName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoStateWrapper configInfo4Gray = this.findConfigInfo4GrayState(dataId, group, tenantTmp, grayName); if (configInfo4Gray == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfo4Gray.getId(), configInfo4Gray.getLastModified()); } @Override public ConfigOperateResult addConfigInfo4Gray(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { return tjt.execute(status -> { String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant().trim(); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); try { addConfigInfoGrayAtomic(-1, configInfo, grayNameTmp, grayRuleTmp, srcIp, srcUser); if (!GRAY_MIGRATE_FLAG.get()) { Timestamp now = new Timestamp(System.currentTimeMillis()); historyConfigInfoPersistService.insertConfigHistoryAtomic(0, configInfo, srcIp, srcUser, now, "I", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(grayNameTmp, grayRuleTmp, srcUser)); } return getGrayOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); } catch (Exception e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } }); } @Override public void addConfigInfoGrayAtomic(long configGrayId, ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); jt.update(configInfoGrayMapper.insert( Arrays.asList("data_id", "group_id", "tenant_id", "gray_name", "gray_rule", "app_name", "content", "encrypted_data_key", "md5", "src_ip", "src_user", "gmt_create@NOW()", "gmt_modified@NOW()")), configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayName, grayRule, appNameTmp, configInfo.getContent(), encryptedDataKey, md5, srcIp, srcUser); } @Override public ConfigOperateResult insertOrUpdateGray(final ConfigInfo configInfo, final String grayName, final String grayRule, final String srcIp, final String srcUser) { if (findConfigInfo4GrayState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), grayName) == null) { return addConfigInfo4Gray(configInfo, grayName, grayRule, srcIp, srcUser); } else { return updateConfigInfo4Gray(configInfo, grayName, grayRule, srcIp, srcUser); } } @Override public ConfigOperateResult insertOrUpdateGrayCas(final ConfigInfo configInfo, final String grayName, final String grayRule, final String srcIp, final String srcUser) { if (findConfigInfo4GrayState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), grayName) == null) { return addConfigInfo4Gray(configInfo, grayName, grayRule, srcIp, srcUser); } else { return updateConfigInfo4GrayCas(configInfo, grayName, grayRule, srcIp, srcUser); } } @Override public void removeConfigInfoGray(final String dataId, final String group, final String tenant, final String grayName, final String srcIp, final String srcUser) { tjt.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName; try { ConfigInfoGrayWrapper oldConfigAllInfo4Gray = findConfigInfo4Gray(dataId, group, tenantTmp, grayNameTmp); if (oldConfigAllInfo4Gray == null) { return; } ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); jt.update( configInfoGrayMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")), dataId, group, tenantTmp, grayNameTmp); if (!GRAY_MIGRATE_FLAG.get()) { Timestamp now = new Timestamp(System.currentTimeMillis()); historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo4Gray.getId(), oldConfigAllInfo4Gray, srcIp, srcUser, now, "D", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(oldConfigAllInfo4Gray.getGrayName(), oldConfigAllInfo4Gray.getGrayRule(), oldConfigAllInfo4Gray.getSrcUser())); } } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } }); } @Override public ConfigOperateResult updateConfigInfo4Gray(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { return tjt.execute(status -> { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); try { ConfigInfoGrayWrapper oldConfigAllInfo4Gray = findConfigInfo4Gray(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); if (oldConfigAllInfo4Gray == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); jt.update(configInfoGrayMapper.update( Arrays.asList("content", "encrypted_data_key", "md5", "src_ip", "src_user", "gmt_modified@NOW()", "app_name", "gray_rule"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")), configInfo.getContent(), configInfo.getEncryptedDataKey(), md5, srcIp, srcUser, appNameTmp, grayRuleTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); Timestamp now = new Timestamp(System.currentTimeMillis()); if (!GRAY_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo4Gray.getId(), oldConfigAllInfo4Gray, srcIp, srcUser, now, "U", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(oldConfigAllInfo4Gray.getGrayName(), oldConfigAllInfo4Gray.getGrayRule(), oldConfigAllInfo4Gray.getSrcUser())); } return getGrayOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } }); } @Override public ConfigOperateResult updateConfigInfo4GrayCas(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser) { return tjt.execute(status -> { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); try { ConfigInfoGrayWrapper oldConfigAllInfo4Gray = findConfigInfo4Gray(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); if (oldConfigAllInfo4Gray == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.GRAY_NAME, grayNameTmp); context.putWhereParameter(FieldConstant.GRAY_RULE, grayRuleTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); final MapperResult mapperResult = configInfoGrayMapper.updateConfigInfo4GrayCas(context); boolean success = jt.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) > 0; if (success) { if (!GRAY_MIGRATE_FLAG.get()) { Timestamp now = new Timestamp(System.currentTimeMillis()); historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo4Gray.getId(), oldConfigAllInfo4Gray, srcIp, srcUser, now, "U", Constants.GRAY, grayNameTmp, ConfigExtInfoUtil.getExtInfoFromGrayInfo(oldConfigAllInfo4Gray.getGrayName(), oldConfigAllInfo4Gray.getGrayRule(), oldConfigAllInfo4Gray.getSrcUser())); } return getGrayOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp); } else { return new ConfigOperateResult(false); } } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } }); } @Override public ConfigInfoGrayWrapper findConfigInfo4Gray(final String dataId, final String group, final String tenant, final String grayName) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); try { ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); return this.jt.queryForObject(configInfoGrayMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "gray_name", "gray_rule", "app_name", "content", "md5", "encrypted_data_key", "gmt_modified", "src_user"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")), new Object[] {dataId, group, tenantTmp, grayNameTmp}, CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public int configInfoGrayCount() { ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); String sql = configInfoGrayMapper.count(null); Integer result = jt.queryForObject(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoGrayCount error"); } return result; } @Override public Page findAllConfigInfoGrayForDumpAll(final int pageNo, final int pageSize) { final int startRow = (pageNo - 1) * pageSize; ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); String sqlCountRows = configInfoGrayMapper.count(null); MapperResult sqlFetchRows = configInfoGrayMapper.findAllConfigInfoGrayForDumpAllFetchRows( new MapperContext(startRow, pageSize)); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(sqlCountRows, sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), pageNo, pageSize, CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public List findChangeConfig(final Timestamp startTime, long lastMaxId, final int pageSize) { try { ConfigInfoGrayMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.PAGE_SIZE, pageSize); context.putWhereParameter(FieldConstant.LAST_MAX_ID, lastMaxId); MapperResult mapperResult = configInfoMapper.findChangeConfig(context); return jt.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public List findConfigInfoGrays(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); String selectSql = configInfoGrayMapper.select(Collections.singletonList("gray_name"), Arrays.asList("data_id", "group_id", "tenant_id")); return jt.queryForList(selectSql, new Object[] {dataId, group, tenantTmp}, String.class); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.common.constant.Symbols; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.constant.ParametersField; import com.alibaba.nacos.config.server.enums.FileTypeEnum; import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.service.sql.ExternalStorageUtils; import com.alibaba.nacos.config.server.utils.ConfigExtInfoUtil; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.extrnal.ExternalStoragePaginationHelperImpl; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.ContextConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; import com.alibaba.nacos.plugin.datasource.mapper.ConfigTagsRelationMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.commons.collections.CollectionUtils; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Conditional; import org.springframework.dao.DataAccessException; import org.springframework.dao.DataIntegrityViolationException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; 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.Optional; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ADVANCE_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ALL_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.utils.PropertyUtil.CONFIG_MIGRATE_FLAG; /** * ExternalConfigInfoPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnExternalStorage.class) @Service("externalConfigInfoPersistServiceImpl") public class ExternalConfigInfoPersistServiceImpl implements ConfigInfoPersistService { /** * constant variables. */ public static final String SPOT = "."; protected JdbcTemplate jt; protected TransactionTemplate tjt; MapperManager mapperManager; private DataSourceService dataSourceService; private HistoryConfigInfoPersistService historyConfigInfoPersistService; public ExternalConfigInfoPersistServiceImpl( @Qualifier("externalHistoryConfigInfoPersistServiceImpl") HistoryConfigInfoPersistService historyConfigInfoPersistService) { this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jt = dataSourceService.getJdbcTemplate(); this.tjt = dataSourceService.getTransactionTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); this.historyConfigInfoPersistService = historyConfigInfoPersistService; } @Override public PaginationHelper createPaginationHelper() { return new ExternalStoragePaginationHelperImpl<>(jt); } @Override public String generateLikeArgument(String s) { String underscore = "_"; if (s.contains(underscore)) { s = s.replaceAll(underscore, "\\\\_"); } String fuzzySearchSign = "\\*"; String sqlLikePercentSign = "%"; if (s.contains(PATTERN_STR)) { return s.replaceAll(fuzzySearchSign, sqlLikePercentSign); } else { return s; } } @Override public ConfigOperateResult addConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo, final Map configAdvanceInfo) { return tjt.execute(status -> { try { long configId = addConfigInfoAtomic(-1, srcIp, srcUser, configInfo, configAdvanceInfo); String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); addConfigTagsRelation(configId, configTags, configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (!CONFIG_MIGRATE_FLAG.get()) { Timestamp now = new Timestamp(System.currentTimeMillis()); historyConfigInfoPersistService.insertConfigHistoryAtomic(0, configInfo, srcIp, srcUser, now, "I", Constants.FORMAL, null, ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser)); } ConfigInfoStateWrapper configInfoCurrent = this.findConfigInfoState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (configInfoCurrent == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfoCurrent.getId(), configInfoCurrent.getLastModified()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } }); } @Override public ConfigOperateResult updateConfigInfoMetadata(final String dataId, final String group, final String tenant, String configTags, String description) throws NacosException { ConfigInfoWrapper configInfoWrapper = findConfigInfo(dataId, group, tenant); if (configInfoWrapper == null) { throw new NacosException(NacosException.NOT_FOUND, "config is not found for dataId=" + dataId + ", group=" + group); } return tjt.execute(status -> { try { Long configId = configInfoWrapper.getId(); if (description != null) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); jt.update( configInfoMapper.update(Arrays.asList("gmt_modified@NOW()", "c_desc"), Arrays.asList("id")), description, configId); } if (configTags != null) { removeTagByIdAtomic(configId); addConfigTagsRelation(configId, configTags, dataId, group, tenant); } return new ConfigOperateResult(true); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } }); } /** * insert or update config. * * @param srcIp remote ip * @param srcUser user * @param configInfo config info * @param configAdvanceInfo advance info * @return */ @Override public ConfigOperateResult insertOrUpdate(String srcIp, String srcUser, ConfigInfo configInfo, Map configAdvanceInfo) { try { ConfigInfoStateWrapper configInfoState = findConfigInfoState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (configInfoState == null) { return addConfigInfo(srcIp, srcUser, configInfo, configAdvanceInfo); } else { return updateConfigInfo(configInfo, srcIp, srcUser, configAdvanceInfo); } } catch (Exception exception) { LogUtil.FATAL_LOG.error("[db-error] try to update or add config failed, {}", exception.getMessage(), exception); throw exception; } } @Override public ConfigOperateResult insertOrUpdateCas(String srcIp, String srcUser, ConfigInfo configInfo, Map configAdvanceInfo) { try { ConfigInfoStateWrapper configInfoState = findConfigInfoState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (configInfoState == null) { return addConfigInfo(srcIp, srcUser, configInfo, configAdvanceInfo); } else { return updateConfigInfoCas(configInfo, srcIp, srcUser, configAdvanceInfo); } } catch (Exception exception) { LogUtil.FATAL_LOG.error("[db-error] try to update or add config failed, {}", exception.getMessage(), exception); throw exception; } } @Override public long addConfigInfoAtomic(final long configId, final String srcIp, final String srcUser, final ConfigInfo configInfo, Map configAdvanceInfo) { KeyHolder keyHolder = ExternalStorageUtils.createKeyHolder(); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); try { jt.update( connection -> createPsForInsertConfigInfo(srcIp, srcUser, configInfo, configAdvanceInfo, connection, configInfoMapper), keyHolder); Number nu = keyHolder.getKey(); if (nu == null) { throw new IllegalArgumentException("insert config_info fail"); } return nu.longValue(); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } PreparedStatement createPsForInsertConfigInfo(final String srcIp, final String srcUser, final ConfigInfo configInfo, Map configAdvanceInfo, Connection connection, ConfigInfoMapper configInfoMapper) throws SQLException { final String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); final String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); final String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); final String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); final String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); final String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); String insertSql = configInfoMapper.insert( Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user", "gmt_create@NOW()", "gmt_modified@NOW()", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key")); PreparedStatement ps = connection.prepareStatement(insertSql, configInfoMapper.getPrimaryKeyGeneratedKeys()); ps.setString(1, configInfo.getDataId()); ps.setString(2, configInfo.getGroup()); ps.setString(3, tenantTmp); ps.setString(4, appNameTmp); ps.setString(5, configInfo.getContent()); ps.setString(6, md5Tmp); ps.setString(7, srcIp); ps.setString(8, srcUser); ps.setString(9, desc); ps.setString(10, use); ps.setString(11, effect); ps.setString(12, type); ps.setString(13, schema); ps.setString(14, encryptedDataKey); return ps; } @Override public void addConfigTagRelationAtomic(long configId, String tagName, String dataId, String group, String tenant) { try { ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); jt.update(configTagsRelationMapper.insert( Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id")), configId, tagName, StringUtils.EMPTY, dataId, group, tenant); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public void addConfigTagsRelation(long configId, String configTags, String dataId, String group, String tenant) { if (StringUtils.isNotBlank(configTags)) { String[] tagArr = configTags.split(","); for (int i = 0; i < tagArr.length; i++) { addConfigTagRelationAtomic(configId, tagArr[i], dataId, group, tenant); } } } @Override public Map batchInsertOrUpdate(List configInfoList, String srcUser, String srcIp, Map configAdvanceInfo, SameConfigPolicy policy) throws NacosException { int succCount = 0; int skipCount = 0; List> failData = null; List> skipData = null; for (int i = 0; i < configInfoList.size(); i++) { ConfigAllInfo configInfo = configInfoList.get(i); try { ParamUtils.checkParam(configInfo.getDataId(), configInfo.getGroup(), "datumId", configInfo.getContent()); } catch (NacosException e) { LogUtil.DEFAULT_LOG.error("data verification failed", e); throw e; } ConfigInfo configInfo2Save = new ConfigInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), configInfo.getAppName(), configInfo.getContent()); configInfo2Save.setEncryptedDataKey( configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey()); String type = determineConfigType(configInfo); if (configAdvanceInfo == null) { configAdvanceInfo = new HashMap<>(16); } configAdvanceInfo.put("type", type); configAdvanceInfo.put("desc", configInfo.getDesc()); boolean success; try { ConfigOperateResult configOperateResult = addConfigInfo(srcIp, srcUser, configInfo2Save, configAdvanceInfo); success = configOperateResult.isSuccess(); } catch (DataIntegrityViolationException ive) { success = false; } if (success) { succCount++; } else { if (SameConfigPolicy.ABORT.equals(policy)) { failData = new ArrayList<>(); skipData = new ArrayList<>(); Map failedItem = new HashMap<>(2); failedItem.put("dataId", configInfo2Save.getDataId()); failedItem.put("group", configInfo2Save.getGroup()); failData.add(failedItem); for (int j = (i + 1); j < configInfoList.size(); j++) { ConfigInfo skipConfigInfo = configInfoList.get(j); Map skipItem = new HashMap<>(2); skipItem.put("dataId", skipConfigInfo.getDataId()); skipItem.put("group", skipConfigInfo.getGroup()); skipData.add(skipItem); skipCount++; } break; } else if (SameConfigPolicy.SKIP.equals(policy)) { skipCount++; if (skipData == null) { skipData = new ArrayList<>(); } Map skipItem = new HashMap<>(2); skipItem.put("dataId", configInfo2Save.getDataId()); skipItem.put("group", configInfo2Save.getGroup()); skipData.add(skipItem); } else if (SameConfigPolicy.OVERWRITE.equals(policy)) { succCount++; updateConfigInfo(configInfo2Save, srcIp, srcUser, configAdvanceInfo); } } } Map result = new HashMap<>(4); result.put("succCount", succCount); result.put("skipCount", skipCount); if (failData != null && !failData.isEmpty()) { result.put("failData", failData); } if (skipData != null && !skipData.isEmpty()) { result.put("skipData", skipData); } return result; } private String determineConfigType(ConfigAllInfo configInfo) { String type = configInfo.getType(); if (StringUtils.isBlank(type)) { if (configInfo.getDataId().contains(SPOT)) { String extName = configInfo.getDataId().substring(configInfo.getDataId().lastIndexOf(SPOT) + 1); FileTypeEnum fileTypeEnum = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(extName); type = fileTypeEnum.getFileType(); } else { type = FileTypeEnum.getFileTypeEnumByFileExtensionOrFileType(null).getFileType(); } } return type; } @Override public void removeConfigInfo(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser) { tjt.execute(new TransactionCallback() { final Timestamp time = new Timestamp(System.currentTimeMillis()); @Override public Boolean doInTransaction(TransactionStatus status) { try { ConfigAllInfo oldConfigAllInfo = findConfigAllInfo(dataId, group, tenant); if (oldConfigAllInfo != null) { removeConfigInfoAtomic(dataId, group, tenant, srcIp, srcUser); removeTagByIdAtomic(oldConfigAllInfo.getId()); if (!CONFIG_MIGRATE_FLAG.get()) { historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo.getId(), oldConfigAllInfo, srcIp, srcUser, time, "D", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(oldConfigAllInfo)); } } } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } return Boolean.TRUE; } }); } @Override public List removeConfigInfoByIds(final List ids, final String srcIp, final String srcUser) { if (CollectionUtils.isEmpty(ids)) { return null; } ids.removeAll(Collections.singleton(null)); return tjt.execute(new TransactionCallback>() { final Timestamp time = new Timestamp(System.currentTimeMillis()); @Override public List doInTransaction(TransactionStatus status) { try { String idsStr = StringUtils.join(ids, StringUtils.COMMA); List oldConfigAllInfoList = findAllConfigInfo4Export(null, null, null, null, ids); if (!CollectionUtils.isEmpty(oldConfigAllInfoList)) { removeConfigInfoByIdsAtomic(idsStr); for (ConfigAllInfo configAllInfo : oldConfigAllInfoList) { removeTagByIdAtomic(configAllInfo.getId()); historyConfigInfoPersistService.insertConfigHistoryAtomic(configAllInfo.getId(), configAllInfo, srcIp, srcUser, time, "D", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo)); } } return oldConfigAllInfoList; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } }); } @Override public void removeTagByIdAtomic(long id) { try { ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); jt.update(configTagsRelationMapper.delete(Collections.singletonList("id")), id); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public void removeConfigInfoAtomic(final String dataId, final String group, final String tenant, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); jt.update(configInfoMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id")), dataId, group, tenantTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public void removeConfigInfoByIdsAtomic(final String ids) { if (StringUtils.isBlank(ids)) { return; } List paramList = new ArrayList<>(); String[] idArr = ids.split(","); for (int i = 0; i < idArr.length; i++) { paramList.add(Long.parseLong(idArr[i])); } ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.IDS, paramList); MapperResult result = configInfoMapper.removeConfigInfoByIdsAtomic(context); try { jt.update(result.getSql(), result.getParamList().toArray()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigOperateResult updateConfigInfo(final ConfigInfo configInfo, final String srcIp, final String srcUser, final Map configAdvanceInfo) { return tjt.execute(status -> { try { ConfigAllInfo oldConfigAllInfo = findConfigAllInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (oldConfigAllInfo == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } String appNameTmp = oldConfigAllInfo.getAppName(); /* If the appName passed by the user is not empty, use the persistent user's appName, otherwise use db; when emptying appName, you need to pass an empty string */ if (configInfo.getAppName() == null) { configInfo.setAppName(appNameTmp); } updateConfigInfoAtomic(configInfo, srcIp, srcUser, configAdvanceInfo); String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); if (configTags != null) { // delete all tags and then recreate removeTagByIdAtomic(oldConfigAllInfo.getId()); addConfigTagsRelation(oldConfigAllInfo.getId(), configTags, configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } if (!CONFIG_MIGRATE_FLAG.get()) { Timestamp now = new Timestamp(System.currentTimeMillis()); historyConfigInfoPersistService.insertConfigHistoryAtomic(oldConfigAllInfo.getId(), oldConfigAllInfo, srcIp, srcUser, now, "U", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(oldConfigAllInfo)); } return getConfigInfoOperateResult(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } }); } private ConfigOperateResult getConfigInfoOperateResult(String dataId, String group, String tenant) { ConfigInfoStateWrapper configInfoLast = this.findConfigInfoState(dataId, group, tenant); if (configInfoLast == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfoLast.getId(), configInfoLast.getLastModified()); } @Override public ConfigOperateResult updateConfigInfoCas(final ConfigInfo configInfo, final String srcIp, final String srcUser, final Map configAdvanceInfo) { return tjt.execute(status -> { try { ConfigAllInfo oldAllConfigInfo = findConfigAllInfo(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (oldAllConfigInfo == null) { if (LogUtil.FATAL_LOG.isErrorEnabled()) { LogUtil.FATAL_LOG.error("expected config info[dataid:{}, group:{}, tenent:{}] but not found.", configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } return new ConfigOperateResult(false); } String appNameTmp = oldAllConfigInfo.getAppName(); /* If the appName passed by the user is not empty, use the persistent user's appName, otherwise use db; when emptying appName, you need to pass an empty string */ if (configInfo.getAppName() == null) { configInfo.setAppName(appNameTmp); } int rows = updateConfigInfoAtomicCas(configInfo, srcIp, srcUser, configAdvanceInfo); if (rows < 1) { return new ConfigOperateResult(false); } String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); if (configTags != null) { // delete all tags and then recreate removeTagByIdAtomic(oldAllConfigInfo.getId()); addConfigTagsRelation(oldAllConfigInfo.getId(), configTags, configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); } if (!CONFIG_MIGRATE_FLAG.get()) { Timestamp now = new Timestamp(System.currentTimeMillis()); historyConfigInfoPersistService.insertConfigHistoryAtomic(oldAllConfigInfo.getId(), oldAllConfigInfo, srcIp, srcUser, now, "U", Constants.FORMAL, null, ConfigExtInfoUtil.getExtInfoFromAllInfo(oldAllConfigInfo)); } ConfigInfoStateWrapper configInfoLast = this.findConfigInfoState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant()); if (configInfoLast == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfoLast.getId(), configInfoLast.getLastModified()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } }); } private int updateConfigInfoAtomicCas(final ConfigInfo configInfo, final String srcIp, final String srcUser, Map configAdvanceInfo) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE); String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); try { MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5Tmp); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); // Only update c_desc when desc is not null (empty string will also update) if (desc != null) { context.putUpdateParameter(FieldConstant.C_DESC, desc); } context.putUpdateParameter(FieldConstant.C_USE, use); context.putUpdateParameter(FieldConstant.EFFECT, effect); context.putUpdateParameter(FieldConstant.TYPE, type); context.putUpdateParameter(FieldConstant.C_SCHEMA, schema); context.putUpdateParameter(FieldConstant.ENCRYPTED_DATA_KEY, encryptedDataKey); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperResult mapperResult = configInfoMapper.updateConfigInfoAtomicCas(context); return jt.update(mapperResult.getSql(), mapperResult.getParamList().toArray()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public void updateConfigInfoAtomic(final ConfigInfo configInfo, final String srcIp, final String srcUser, Map configAdvanceInfo) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); // Build update columns and parameters dynamically List updateColumns = new ArrayList<>(Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified@NOW()", "app_name")); List updateParams = new ArrayList<>(Arrays.asList(configInfo.getContent(), md5Tmp, srcIp, srcUser, appNameTmp)); // Only update c_desc when desc is not null (empty string will also update) if (desc != null) { updateColumns.add("c_desc"); updateParams.add(desc); } updateColumns.addAll(Arrays.asList("c_use", "effect", "type", "c_schema", "encrypted_data_key")); updateParams.addAll(Arrays.asList(use, effect, type, schema, encryptedDataKey)); // Add where parameters updateParams.addAll(Arrays.asList(configInfo.getDataId(), configInfo.getGroup(), tenantTmp)); jt.update(configInfoMapper.update(updateColumns, Arrays.asList("data_id", "group_id", "tenant_id")), updateParams.toArray()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public long findConfigMaxId() { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperResult mapperResult = configInfoMapper.findConfigMaxId(null); try { return jt.queryForObject(mapperResult.getSql(), Long.class); } catch (NullPointerException e) { return 0; } } @Override public ConfigInfo findConfigInfo(long id) { try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); return this.jt.queryForObject(configInfoMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content"), Collections.singletonList("id")), new Object[] {id}, CONFIG_INFO_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigInfoWrapper findConfigInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); return this.jt.queryForObject(configInfoMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "type", "encrypted_data_key", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public Page findConfigInfo4Page(final int pageNo, final int pageSize, final String dataId, final String group, final String tenant, final Map configAdvanceInfo) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; PaginationHelper helper = createPaginationHelper(); final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String content = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("content"); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); MapperResult sql; MapperResult sqlCount; final MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); if (StringUtils.isNotBlank(dataId)) { context.putWhereParameter(FieldConstant.DATA_ID, dataId); } if (StringUtils.isNotBlank(group)) { context.putWhereParameter(FieldConstant.GROUP_ID, group); } if (StringUtils.isNotBlank(appName)) { context.putWhereParameter(FieldConstant.APP_NAME, appName); } if (!StringUtils.isBlank(content)) { context.putWhereParameter(FieldConstant.CONTENT, content); } context.setStartRow((pageNo - 1) * pageSize); context.setPageSize(pageSize); if (StringUtils.isNotBlank(configTags)) { String[] tagArr = configTags.split(","); context.putWhereParameter(FieldConstant.TAG_ARR, tagArr); ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); sqlCount = configTagsRelationMapper.findConfigInfo4PageCountRows(context); sql = configTagsRelationMapper.findConfigInfo4PageFetchRows(context); } else { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); sqlCount = configInfoMapper.findConfigInfo4PageCountRows(context); sql = configInfoMapper.findConfigInfo4PageFetchRows(context); } try { Page page = helper.fetchPageLimit(sqlCount, sql, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); for (ConfigInfo configInfo : page.getPageItems()) { Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), configInfo.getEncryptedDataKey(), configInfo.getContent()); configInfo.setContent(pair.getSecond()); } return page; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] ", e); throw e; } } @Override public int configInfoCount() { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); String sql = configInfoMapper.count(null); Integer result = jt.queryForObject(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); } return result; } @Override public int configInfoCount(String tenant) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.TENANT_ID, tenant); MapperResult mapperResult = configInfoMapper.configInfoLikeTenantCount(context); Integer result = jt.queryForObject(mapperResult.getSql(), mapperResult.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoCount error"); } return result; } @Override public List getTenantIdList(int page, int pageSize) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); int from = (page - 1) * pageSize; MapperResult mapperResult = configInfoMapper.getTenantIdList(new MapperContext(from, pageSize)); return jt.queryForList(mapperResult.getSql(), mapperResult.getParamList().toArray(), String.class); } @Override public List getGroupIdList(int page, int pageSize) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); int from = (page - 1) * pageSize; MapperResult mapperResult = configInfoMapper.getGroupIdList(new MapperContext(from, pageSize)); return jt.queryForList(mapperResult.getSql(), mapperResult.getParamList().toArray(), String.class); } @Override public Page findAllConfigInfoFragment(final long lastMaxId, final int pageSize, boolean needContent) { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(0, pageSize); context.putContextParameter(ContextConstant.NEED_CONTENT, String.valueOf(needContent)); context.putWhereParameter(FieldConstant.ID, lastMaxId); MapperResult select = configInfoMapper.findAllConfigInfoFragment(context); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(select.getSql(), select.getParamList().toArray(), 1, pageSize, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public Page findConfigInfoLike4Page(final int pageNo, final int pageSize, final String dataId, final String group, final String tenant, final Map configAdvanceInfo) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; final String appName = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("appName"); final String content = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("content"); final String types = Optional.ofNullable(configAdvanceInfo).map(e -> (String) e.get(ParametersField.TYPES)) .orElse(null); final String configTags = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("config_tags"); PaginationHelper helper = createPaginationHelper(); MapperResult sqlCountRows; MapperResult sqlFetchRows; MapperContext context = new MapperContext((pageNo - 1) * pageSize, pageSize); context.putWhereParameter(FieldConstant.TENANT_ID, generateLikeArgument(tenantTmp)); if (!StringUtils.isBlank(dataId)) { context.putWhereParameter(FieldConstant.DATA_ID, generateLikeArgument(dataId)); } if (!StringUtils.isBlank(group)) { context.putWhereParameter(FieldConstant.GROUP_ID, generateLikeArgument(group)); } if (!StringUtils.isBlank(appName)) { context.putWhereParameter(FieldConstant.APP_NAME, appName); } if (!StringUtils.isBlank(content)) { context.putWhereParameter(FieldConstant.CONTENT, generateLikeArgument(content)); } if (StringUtils.isNotBlank(types)) { String[] typesArr = types.split(Symbols.COMMA); context.putWhereParameter(FieldConstant.TYPE, typesArr); } if (StringUtils.isNotBlank(configTags)) { String[] tagArr = configTags.split(","); for (int i = 0; i < tagArr.length; i++) { tagArr[i] = generateLikeArgument(tagArr[i]); } context.putWhereParameter(FieldConstant.TAG_ARR, tagArr); ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); sqlCountRows = configTagsRelationMapper.findConfigInfoLike4PageCountRows(context); sqlFetchRows = configTagsRelationMapper.findConfigInfoLike4PageFetchRows(context); } else { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); sqlCountRows = configInfoMapper.findConfigInfoLike4PageCountRows(context); sqlFetchRows = configInfoMapper.findConfigInfoLike4PageFetchRows(context); } try { Page page = helper.fetchPageLimit(sqlCountRows, sqlFetchRows, pageNo, pageSize, CONFIG_INFO_ROW_MAPPER); for (ConfigInfo configInfo : page.getPageItems()) { Pair pair = EncryptionHandler.decryptHandler(configInfo.getDataId(), configInfo.getEncryptedDataKey(), configInfo.getContent()); configInfo.setContent(pair.getSecond()); } return page; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public List findChangeConfig(final Timestamp startTime, long lastMaxId, final int pageSize) { try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.PAGE_SIZE, pageSize); context.putWhereParameter(FieldConstant.LAST_MAX_ID, lastMaxId); MapperResult mapperResult = configInfoMapper.findChangeConfig(context); return jt.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public List selectTagByConfig(String dataId, String group, String tenant) { ConfigTagsRelationMapper configTagsRelationMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION); String sql = configTagsRelationMapper.select(Collections.singletonList("tag_name"), Arrays.asList("data_id", "group_id", "tenant_id")); try { return jt.queryForList(sql, new Object[] {dataId, group, tenant}, String.class); } catch (EmptyResultDataAccessException e) { return null; } catch (IncorrectResultSizeDataAccessException e) { return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public List findConfigInfosByIds(final String ids) { if (StringUtils.isBlank(ids)) { return null; } List paramList = new ArrayList<>(); String[] idArr = ids.split(","); for (int i = 0; i < idArr.length; i++) { paramList.add(Long.parseLong(idArr[i])); } ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.IDS, paramList); MapperResult mapperResult = configInfoMapper.findConfigInfosByIds(context); try { return this.jt.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigAdvanceInfo findConfigAdvanceInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { List configTagList = this.selectTagByConfig(dataId, group, tenant); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); ConfigAdvanceInfo configAdvance = this.jt.queryForObject(configInfoMapper.select( Arrays.asList("gmt_create", "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", "c_schema"), Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_ADVANCE_INFO_ROW_MAPPER); if (configTagList != null && !configTagList.isEmpty()) { StringBuilder configTagsTmp = new StringBuilder(); for (String configTag : configTagList) { if (configTagsTmp.length() == 0) { configTagsTmp.append(configTag); } else { configTagsTmp.append(',').append(configTag); } } configAdvance.setConfigTags(configTagsTmp.toString()); } return configAdvance; } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigAllInfo findConfigAllInfo(final String dataId, final String group, final String tenant) { final String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { List configTagList = this.selectTagByConfig(dataId, group, tenant); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); ConfigAllInfo configAdvance = this.jt.queryForObject(configInfoMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "gmt_create", "gmt_modified", "src_user", "src_ip", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_ALL_INFO_ROW_MAPPER); if (configTagList != null && !configTagList.isEmpty()) { StringBuilder configTagsTmp = new StringBuilder(); for (String configTag : configTagList) { if (configTagsTmp.length() == 0) { configTagsTmp.append(configTag); } else { configTagsTmp.append(',').append(configTag); } } configAdvance.setConfigTags(configTagsTmp.toString()); } return configAdvance; } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigInfoStateWrapper findConfigInfoState(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); return this.jt.queryForObject( configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")), new Object[] {dataId, group, tenantTmp}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e.toString(), e); throw e; } } @Override public List findAllConfigInfo4Export(final String dataId, final String group, final String tenant, final String appName, final List ids) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); MapperContext context = new MapperContext(); if (!CollectionUtils.isEmpty(ids)) { context.putWhereParameter(FieldConstant.IDS, ids); } else { context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); if (!StringUtils.isBlank(dataId)) { context.putWhereParameter(FieldConstant.DATA_ID, generateLikeArgument(dataId)); } if (StringUtils.isNotBlank(group)) { context.putWhereParameter(FieldConstant.GROUP_ID, group); } if (StringUtils.isNotBlank(appName)) { context.putWhereParameter(FieldConstant.APP_NAME, appName); } } MapperResult mapperResult = configInfoMapper.findAllConfigInfo4Export(context); try { List configAllInfos = jt.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_ALL_INFO_ROW_MAPPER); if (CollectionUtils.isEmpty(configAllInfos)) { return configAllInfos; } for (ConfigAllInfo configAllInfo : configAllInfos) { List configTagList = selectTagByConfig(configAllInfo.getDataId(), configAllInfo.getGroup(), configAllInfo.getTenant()); if (CollectionUtils.isNotEmpty(configTagList)) { StringBuilder configTags = new StringBuilder(); for (String configTag : configTagList) { if (configTags.length() == 0) { configTags.append(configTag); } else { configTags.append(',').append(configTag); } } configAllInfo.setConfigTags(configTags.toString()); } } return configAllInfos; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public List queryConfigInfoByNamespace(String tenant) { if (Objects.isNull(tenant)) { throw new IllegalArgumentException("tenantId can not be null"); } String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); return this.jt.query( configInfoMapper.select(Arrays.asList("data_id", "group_id", "tenant_id", "app_name", "type"), Collections.singletonList("tenant_id")), new Object[] {tenantTmp}, CONFIG_INFO_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return Collections.EMPTY_LIST; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoTagPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.extrnal.ExternalStoragePaginationHelperImpl; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoTagMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Conditional; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Timestamp; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER; /** * ExternalConfigInfoTagPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnExternalStorage.class) @Service("externalConfigInfoTagPersistServiceImpl") public class ExternalConfigInfoTagPersistServiceImpl implements ConfigInfoTagPersistService { private DataSourceService dataSourceService; protected JdbcTemplate jt; protected TransactionTemplate tjt; private MapperManager mapperManager; public ExternalConfigInfoTagPersistServiceImpl() { this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jt = dataSourceService.getJdbcTemplate(); this.tjt = dataSourceService.getTransactionTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); } @Override public PaginationHelper createPaginationHelper() { return new ExternalStoragePaginationHelperImpl<>(jt); } @Override public ConfigInfoStateWrapper findConfigInfo4TagState(final String dataId, final String group, final String tenant, String tag) { ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; try { return this.jt.queryForObject( configInfoTagMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")), new Object[] {dataId, group, tenantTmp, tag}, CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { return null; } } private ConfigOperateResult getTagOperateResult(String dataId, String group, String tenant, String tag) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoStateWrapper configInfo4Tag = this.findConfigInfo4TagState(dataId, group, tenantTmp, tag); if (configInfo4Tag == null) { return new ConfigOperateResult(false); } return new ConfigOperateResult(configInfo4Tag.getId(), configInfo4Tag.getLastModified()); } @Override public ConfigOperateResult addConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); try { ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); Timestamp time = new Timestamp(System.currentTimeMillis()); jt.update(configInfoTagMapper.insert( Arrays.asList("data_id", "group_id", "tenant_id", "tag_id", "app_name", "content", "md5", "src_ip", "src_user", "gmt_create", "gmt_modified")), configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp, appNameTmp, configInfo.getContent(), md5, srcIp, srcUser, time, time); return getTagOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigOperateResult insertOrUpdateTag(final ConfigInfo configInfo, final String tag, final String srcIp, final String srcUser) { if (findConfigInfo4TagState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), tag) == null) { return addConfigInfo4Tag(configInfo, tag, srcIp, srcUser); } else { return updateConfigInfo4Tag(configInfo, tag, srcIp, srcUser); } } @Override public ConfigOperateResult insertOrUpdateTagCas(final ConfigInfo configInfo, final String tag, final String srcIp, final String srcUser) { if (findConfigInfo4TagState(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), tag) == null) { return addConfigInfo4Tag(configInfo, tag, srcIp, srcUser); } else { return updateConfigInfo4TagCas(configInfo, tag, srcIp, srcUser); } } @Override public void removeConfigInfoTag(final String dataId, final String group, final String tenant, final String tag, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag; try { ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); jt.update(configInfoTagMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")), dataId, group, tenantTmp, tagTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigOperateResult updateConfigInfo4Tag(ConfigInfo configInfo, String tag, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); Timestamp time = new Timestamp(System.currentTimeMillis()); jt.update(configInfoTagMapper.update( Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified", "app_name"), Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")), configInfo.getContent(), md5, srcIp, srcUser, time, appNameTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); return getTagOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigOperateResult updateConfigInfo4TagCas(ConfigInfo configInfo, String tag, String srcIp, String srcUser) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); Timestamp time = new Timestamp(System.currentTimeMillis()); MapperContext context = new MapperContext(); context.putUpdateParameter(FieldConstant.CONTENT, configInfo.getContent()); context.putUpdateParameter(FieldConstant.MD5, md5); context.putUpdateParameter(FieldConstant.SRC_IP, srcIp); context.putUpdateParameter(FieldConstant.SRC_USER, srcUser); context.putUpdateParameter(FieldConstant.GMT_MODIFIED, time); context.putUpdateParameter(FieldConstant.APP_NAME, appNameTmp); context.putWhereParameter(FieldConstant.DATA_ID, configInfo.getDataId()); context.putWhereParameter(FieldConstant.GROUP_ID, configInfo.getGroup()); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); context.putWhereParameter(FieldConstant.TAG_ID, tagTmp); context.putWhereParameter(FieldConstant.MD5, configInfo.getMd5()); final MapperResult mapperResult = configInfoTagMapper.updateConfigInfo4TagCas(context); boolean success = jt.update(mapperResult.getSql(), mapperResult.getParamList().toArray()) > 0; if (success) { return getTagOperateResult(configInfo.getDataId(), configInfo.getGroup(), tenantTmp, tagTmp); } else { return new ConfigOperateResult(false); } } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public ConfigInfoTagWrapper findConfigInfo4Tag(final String dataId, final String group, final String tenant, final String tag) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag.trim(); try { ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); return this.jt.queryForObject(configInfoTagMapper.select( Arrays.asList("id", "data_id", "group_id", "tenant_id", "tag_id", "app_name", "content", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id", "tag_id")), new Object[] {dataId, group, tenantTmp, tagTmp}, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } catch (EmptyResultDataAccessException e) { // Indicates that the data does not exist, returns null. return null; } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public int configInfoTagCount() { ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); String sql = configInfoTagMapper.count(null); Integer result = jt.queryForObject(sql, Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoTagCount error"); } return result; } @Override public Page findAllConfigInfoTagForDumpAll(final int pageNo, final int pageSize) { final int startRow = (pageNo - 1) * pageSize; ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); String sqlCountRows = configInfoTagMapper.count(null); MapperResult sqlFetchRows = configInfoTagMapper.findAllConfigInfoTagForDumpAllFetchRows( new MapperContext(startRow, pageSize)); PaginationHelper helper = createPaginationHelper(); try { return helper.fetchPageLimit(sqlCountRows, sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), pageNo, pageSize, CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public List findConfigInfoTags(final String dataId, final String group, final String tenant) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; ConfigInfoTagMapper configInfoTagMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_TAG); String selectSql = configInfoTagMapper.select(Collections.singletonList("tag_id"), Arrays.asList("data_id", "group_id", "tenant_id")); return jt.queryForList(selectSql, new Object[] {dataId, group, tenantTmp}, String.class); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigMigratePersistServiceImpl.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigMigratePersistService; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.extrnal.ExternalStoragePaginationHelperImpl; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoGrayMapper; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; import com.alibaba.nacos.plugin.datasource.mapper.ConfigMigrateMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Conditional; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Timestamp; import java.util.Arrays; import java.util.List; import java.util.Map; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER; /** * The type External config migrate persist service. * * @author Sunrisea */ @Conditional(value = ConditionOnExternalStorage.class) @Service("externalConfigMigratePersistServiceImpl") public class ExternalConfigMigratePersistServiceImpl implements ConfigMigratePersistService { /** * The Jt. */ protected JdbcTemplate jt; /** * The Tjt. */ protected TransactionTemplate tjt; private DataSourceService dataSourceService; private MapperManager mapperManager; private ConfigInfoPersistService configInfoPersistService; private ConfigInfoGrayPersistService configInfoGrayPersistService; /** * Instantiates a new External config migrate persist service. * * @param configInfoPersistService the config info persist service * @param configInfoGrayPersistService the config info gray persist service */ public ExternalConfigMigratePersistServiceImpl( @Qualifier("externalConfigInfoPersistServiceImpl") ConfigInfoPersistService configInfoPersistService, @Qualifier("externalConfigInfoGrayPersistServiceImpl") ConfigInfoGrayPersistService configInfoGrayPersistService) { this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jt = dataSourceService.getJdbcTemplate(); this.tjt = dataSourceService.getTransactionTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); this.configInfoPersistService = configInfoPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; } @Override public PaginationHelper createPaginationHelper() { return new ExternalStoragePaginationHelperImpl<>(jt); } @Override public Integer configInfoConflictCount(String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); MapperResult mapperResult = configMigrateMapper.getConfigConflictCount(context); Integer result = jt.queryForObject(mapperResult.getSql(), mapperResult.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoConflictCount error"); } return result; } @Override public Integer configInfoGrayConflictCount(String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); MapperResult mapperResult = configMigrateMapper.getConfigGrayConflictCount(context); Integer result = jt.queryForObject(mapperResult.getSql(), mapperResult.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("configInfoGrayConflictCount error"); } return result; } @Override public List getMigrateConfigInsertIdList(long startId, int pageSize) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, startId); context.setPageSize(pageSize); MapperResult mapperResult = configMigrateMapper.findConfigIdNeedInsertMigrate(context); return jt.queryForList(mapperResult.getSql(), mapperResult.getParamList().toArray(), Long.class); } @Override public List getMigrateConfigGrayInsertIdList(long startId, int pageSize) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, startId); context.setPageSize(pageSize); MapperResult mapperResult = configMigrateMapper.findConfigGrayIdNeedInsertMigrate(context); return jt.queryForList(mapperResult.getSql(), mapperResult.getParamList().toArray(), Long.class); } @Override public List getMigrateConfigUpdateList(long startId, int pageSize, String srcTenant, String targetTenant, String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); context.putWhereParameter(FieldConstant.ID, startId); context.putWhereParameter(FieldConstant.SRC_TENANT, srcTenant); context.putWhereParameter(FieldConstant.TARGET_TENANT, targetTenant); context.setPageSize(pageSize); MapperResult mapperResult = configMigrateMapper.findConfigNeedUpdateMigrate(context); return jt.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_ROW_MAPPER); } @Override public List getMigrateConfigGrayUpdateList(long startId, int pageSize, String srcTenant, String targetTenant, String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); context.putWhereParameter(FieldConstant.ID, startId); context.putWhereParameter(FieldConstant.SRC_TENANT, srcTenant); context.putWhereParameter(FieldConstant.TARGET_TENANT, targetTenant); context.setPageSize(pageSize); MapperResult mapperResult = configMigrateMapper.findConfigGrayNeedUpdateMigrate(context); return jt.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER); } @Override public void migrateConfigInsertByIds(List ids, String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.IDS, ids); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); MapperResult mapperResult = configMigrateMapper.migrateConfigInsertByIds(context); try { jt.update(mapperResult.getSql(), mapperResult.getParamList().toArray()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] migrateConfigInsertByIds" + e, e); throw e; } } @Override public void migrateConfigGrayInsertByIds(List ids, String srcUser) { ConfigMigrateMapper configMigrateMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.MIGRATE_CONFIG); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.IDS, ids); context.putWhereParameter(FieldConstant.SRC_USER, srcUser); MapperResult mapperResult = configMigrateMapper.migrateConfigGrayInsertByIds(context); try { jt.update(mapperResult.getSql(), mapperResult.getParamList().toArray()); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] migrateConfigGrayInsertByIds" + e, e); throw e; } } @Override public void syncConfigGray(String dataId, String group, String tenant, String grayName, String targetTenant, String srcUser) { tjt.execute(status -> { try { ConfigInfoGrayWrapper sourceConfigInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray( dataId, group, tenant, grayName); ConfigInfoGrayWrapper targetConfigInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray( dataId, group, targetTenant, grayName); if (sourceConfigInfoGrayWrapper == null) { removeConfigInfoGrayWithoutHistory(dataId, group, targetTenant, grayName, null, srcUser); ConfigInfoGrayWrapper configInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray( dataId, group, tenant, grayName); if (configInfoGrayWrapper != null) { throw new Exception("sourceConfigInfoGray has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant + ",grayName=" + grayName); } } else { if (targetConfigInfoGrayWrapper == null) { sourceConfigInfoGrayWrapper.setTenant(targetTenant); configInfoGrayPersistService.addConfigInfoGrayAtomic(-1, sourceConfigInfoGrayWrapper, sourceConfigInfoGrayWrapper.getGrayName(), sourceConfigInfoGrayWrapper.getGrayRule(), null, srcUser); ConfigInfoGrayWrapper configInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray( dataId, group, tenant, grayName); if (!StringUtils.equals(configInfoGrayWrapper.getMd5(), sourceConfigInfoGrayWrapper.getMd5()) || !StringUtils.equals(configInfoGrayWrapper.getGrayRule(), sourceConfigInfoGrayWrapper.getGrayRule())) { throw new Exception( "sourceConfigInfoGray has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant + ",grayName=" + grayName); } } else if (sourceConfigInfoGrayWrapper.getLastModified() >= targetConfigInfoGrayWrapper.getLastModified()) { sourceConfigInfoGrayWrapper.setTenant(targetTenant); updateConfigInfo4GrayWithoutHistory(sourceConfigInfoGrayWrapper, sourceConfigInfoGrayWrapper.getGrayName(), sourceConfigInfoGrayWrapper.getGrayRule(), null, srcUser, targetConfigInfoGrayWrapper.getLastModified(), targetConfigInfoGrayWrapper.getMd5()); ConfigInfoGrayWrapper configInfoGrayWrapper = configInfoGrayPersistService.findConfigInfo4Gray( dataId, group, tenant, grayName); if (!StringUtils.equals(configInfoGrayWrapper.getMd5(), sourceConfigInfoGrayWrapper.getMd5()) || !StringUtils.equals(configInfoGrayWrapper.getGrayRule(), sourceConfigInfoGrayWrapper.getGrayRule())) { throw new Exception( "sourceConfigInfoGray has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant + ",grayName=" + grayName); } } } } catch (Exception e) { LogUtil.FATAL_LOG.error("[db-error] syncConfigGray" + e, e); throw new RuntimeException(e); } return null; }); } /** * Remove config info gray without history. * * @param dataId the data id * @param group the group * @param tenant the tenant * @param grayName the gray name * @param srcIp the src ip * @param srcUser the src user */ public void removeConfigInfoGrayWithoutHistory(final String dataId, final String group, final String tenant, final String grayName, final String srcIp, final String srcUser) { String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName; try { ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); jt.update(configInfoGrayMapper.delete(Arrays.asList("data_id", "group_id", "tenant_id", "gray_name")), dataId, group, tenantTmp, grayNameTmp); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } /** * Update config info 4 gray without history. * * @param configInfo the config info * @param grayName the gray name * @param grayRule the gray rule * @param srcIp the src ip * @param srcUser the src user */ public void updateConfigInfo4GrayWithoutHistory(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, String srcUser, long lastModified, final String targetMd5) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); String grayNameTmp = StringUtils.isBlank(grayName) ? StringUtils.EMPTY : grayName.trim(); String grayRuleTmp = StringUtils.isBlank(grayRule) ? StringUtils.EMPTY : grayRule.trim(); Timestamp modifiedTime = new Timestamp(lastModified); try { String md5 = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); ConfigInfoGrayMapper configInfoGrayMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO_GRAY); jt.update(configInfoGrayMapper.update( Arrays.asList("content", "encrypted_data_key", "md5", "src_ip", "src_user", "gmt_modified@NOW()", "app_name", "gray_rule"), Arrays.asList("data_id", "group_id", "tenant_id", "gray_name", "gmt_modified", "md5")), configInfo.getContent(), configInfo.getEncryptedDataKey(), md5, srcIp, srcUser, appNameTmp, grayRuleTmp, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, grayNameTmp, modifiedTime, targetMd5); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public void syncConfig(String dataId, String group, String tenant, String targetTenant, String srcUser) { tjt.execute(status -> { try { ConfigInfoWrapper sourceConfigInfoWrapper = configInfoPersistService.findConfigInfo(dataId, group, tenant); ConfigInfoWrapper targetConfigInfoWrapper = configInfoPersistService.findConfigInfo(dataId, group, targetTenant); if (sourceConfigInfoWrapper == null) { configInfoPersistService.removeConfigInfoAtomic(dataId, group, targetTenant, null, srcUser); ConfigInfoWrapper configInfoWrapper = configInfoPersistService.findConfigInfo(dataId, group, tenant); if (configInfoWrapper != null) { LogUtil.FATAL_LOG.error( "syncConfig failed, sourceConfigInfo has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); throw new Exception( "syncConfig failed,sourceConfigInfo has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } } else { if (targetConfigInfoWrapper == null) { sourceConfigInfoWrapper.setTenant(targetTenant); configInfoPersistService.addConfigInfoAtomic(-1, null, srcUser, sourceConfigInfoWrapper, null); ConfigInfoWrapper configInfoWrapper = configInfoPersistService.findConfigInfo(dataId, group, tenant); if (!StringUtils.equals(configInfoWrapper.getMd5(), sourceConfigInfoWrapper.getMd5())) { LogUtil.FATAL_LOG.error( "syncConfig failed, sourceConfigInfo has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); throw new Exception( "syncConfig failed, sourceConfigInfo has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } } else if (sourceConfigInfoWrapper.getLastModified() >= targetConfigInfoWrapper.getLastModified()) { sourceConfigInfoWrapper.setTenant(targetTenant); updateConfigInfoAtomic(sourceConfigInfoWrapper, null, srcUser, null, targetConfigInfoWrapper.getLastModified(), targetConfigInfoWrapper.getMd5()); ConfigInfoWrapper configInfoWrapper = configInfoPersistService.findConfigInfo(dataId, group, tenant); if (!StringUtils.equals(configInfoWrapper.getMd5(), sourceConfigInfoWrapper.getMd5())) { LogUtil.FATAL_LOG.error( "syncConfig failed, sourceConfigInfo has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); throw new Exception( "syncConfig failed, sourceConfigInfo has been updated,dataId=" + dataId + ",group=" + group + ",tenant=" + tenant); } } } } catch (Exception e) { LogUtil.FATAL_LOG.error("[db-error] syncConfig" + e, e); throw new RuntimeException(e); } return null; }); } /** * Update config info atomic. * * @param configInfo the config info * @param srcIp the src ip * @param srcUser the src user * @param configAdvanceInfo the config advance info * @param lastModified the last modified */ public void updateConfigInfoAtomic(final ConfigInfo configInfo, final String srcIp, final String srcUser, Map configAdvanceInfo, long lastModified, final String targetMd5) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); String desc = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("desc"); String use = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("use"); String effect = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("effect"); String type = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("type"); String schema = configAdvanceInfo == null ? null : (String) configAdvanceInfo.get("schema"); Timestamp modifiedTime = new Timestamp(lastModified); final String encryptedDataKey = configInfo.getEncryptedDataKey() == null ? StringUtils.EMPTY : configInfo.getEncryptedDataKey(); try { ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); jt.update(configInfoMapper.update( Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified@NOW()", "app_name", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id", "gmt_modified", "md5")), configInfo.getContent(), md5Tmp, srcIp, srcUser, appNameTmp, desc, use, effect, type, schema, encryptedDataKey, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, modifiedTime, targetMd5); } catch (CannotGetJdbcConnectionException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalHistoryConfigInfoPersistServiceImpl.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.PaginationHelper; import com.alibaba.nacos.persistence.repository.extrnal.ExternalStoragePaginationHelperImpl; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.plugin.datasource.constants.FieldConstant; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.HistoryConfigInfoMapper; import com.alibaba.nacos.plugin.datasource.model.MapperContext; import com.alibaba.nacos.plugin.datasource.model.MapperResult; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Conditional; import org.springframework.dao.DataAccessException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_DETAIL_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_LIST_ROW_MAPPER; /** * ExternalHistoryConfigInfoPersistServiceImpl. * * @author lixiaoshuang */ @SuppressWarnings("checkstyle:linelength") @Conditional(value = ConditionOnExternalStorage.class) @Service("externalHistoryConfigInfoPersistServiceImpl") public class ExternalHistoryConfigInfoPersistServiceImpl implements HistoryConfigInfoPersistService { private DataSourceService dataSourceService; protected JdbcTemplate jt; protected TransactionTemplate tjt; private MapperManager mapperManager; public ExternalHistoryConfigInfoPersistServiceImpl() { this.dataSourceService = DynamicDataSource.getInstance().getDataSource(); this.jt = dataSourceService.getJdbcTemplate(); this.tjt = dataSourceService.getTransactionTemplate(); Boolean isDataSourceLogEnable = EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false); this.mapperManager = MapperManager.instance(isDataSourceLogEnable); } @Override public PaginationHelper createPaginationHelper() { return new ExternalStoragePaginationHelperImpl<>(jt); } @Override public void insertConfigHistoryAtomic(long id, ConfigInfo configInfo, String srcIp, String srcUser, final Timestamp time, String ops, String publishType, String grayName, String extInfo) { String appNameTmp = StringUtils.defaultEmptyIfBlank(configInfo.getAppName()); String tenantTmp = StringUtils.defaultEmptyIfBlank(configInfo.getTenant()); final String md5Tmp = MD5Utils.md5Hex(configInfo.getContent(), Constants.ENCODE); String encryptedDataKey = StringUtils.defaultEmptyIfBlank(configInfo.getEncryptedDataKey()); String publishTypeTmp = StringUtils.defaultEmptyIfBlank(publishType); String grayNameTemp = StringUtils.defaultEmptyIfBlank(grayName); try { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); jt.update(historyConfigInfoMapper.insert( Arrays.asList("id", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_ip", "src_user", "gmt_modified", "op_type", "publish_type", "gray_name", "ext_info", "encrypted_data_key")), id, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(), md5Tmp, srcIp, srcUser, time, ops, publishTypeTmp, grayNameTemp, extInfo, encryptedDataKey); } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public void removeConfigHistory(final Timestamp startTime, final int limitSize) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.LIMIT_SIZE, limitSize); MapperResult mapperResult = historyConfigInfoMapper.removeConfigHistory(context); PaginationHelper paginationHelper = createPaginationHelper(); paginationHelper.updateLimit(mapperResult.getSql(), mapperResult.getParamList().toArray()); } @Override public List findDeletedConfig(final Timestamp startTime, long startId, int pageSize, String publishType) { try { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); context.putWhereParameter(FieldConstant.PAGE_SIZE, pageSize); context.putWhereParameter(FieldConstant.LAST_MAX_ID, startId); context.putWhereParameter(FieldConstant.PUBLISH_TYPE, publishType); MapperResult mapperResult = historyConfigInfoMapper.findDeletedConfig(context); List configHistoryInfos = jt.query(mapperResult.getSql(), mapperResult.getParamList().toArray(), HISTORY_DETAIL_ROW_MAPPER); List configInfoStateWrappers = new ArrayList<>(); for (ConfigHistoryInfo configHistoryInfo : configHistoryInfos) { ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setId(configHistoryInfo.getId()); configInfoStateWrapper.setDataId(configHistoryInfo.getDataId()); configInfoStateWrapper.setGroup(configHistoryInfo.getGroup()); configInfoStateWrapper.setTenant(configHistoryInfo.getTenant()); configInfoStateWrapper.setMd5(configHistoryInfo.getMd5()); configInfoStateWrapper.setLastModified(configHistoryInfo.getLastModifiedTime().getTime()); configInfoStateWrapper.setGrayName(configHistoryInfo.getGrayName()); configInfoStateWrappers.add(configInfoStateWrapper); } return configInfoStateWrappers; } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } @Override public Page findConfigHistory(String dataId, String group, String tenant, int pageNo, int pageSize) { PaginationHelper helper = createPaginationHelper(); String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant; MapperContext context = new MapperContext((pageNo - 1) * pageSize, pageSize); context.putWhereParameter(FieldConstant.DATA_ID, dataId); context.putWhereParameter(FieldConstant.GROUP_ID, group); context.putWhereParameter(FieldConstant.TENANT_ID, tenantTmp); HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); String sqlCountRows = historyConfigInfoMapper.count(Arrays.asList("data_id", "group_id", "tenant_id")); MapperResult sqlFetchRows = historyConfigInfoMapper.pageFindConfigHistoryFetchRows(context); Page page; try { page = helper.fetchPage(sqlCountRows, sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), pageNo, pageSize, HISTORY_LIST_ROW_MAPPER); } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[list-config-history] error, dataId:{}, group:{}", new Object[] {dataId, group}, e); throw e; } return page; } @Override public ConfigHistoryInfo detailConfigHistory(Long nid) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); String sqlFetchRows = historyConfigInfoMapper.select( Arrays.asList("nid", "data_id", "group_id", "tenant_id", "app_name", "content", "md5", "src_user", "src_ip", "op_type", "gmt_create", "gmt_modified", "publish_type", "gray_name", "ext_info", "encrypted_data_key"), Collections.singletonList("nid")); try { ConfigHistoryInfo historyInfo = jt.queryForObject(sqlFetchRows, new Object[] {nid}, HISTORY_DETAIL_ROW_MAPPER); return historyInfo; } catch (EmptyResultDataAccessException emptyResultDataAccessException) { return null; } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[detail-config-history] error, nid:{}", new Object[] {nid}, e); throw e; } } @Override public ConfigHistoryInfo detailPreviousConfigHistory(Long id) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.ID, id); MapperResult sqlFetchRows = historyConfigInfoMapper.detailPreviousConfigHistory(context); try { ConfigHistoryInfo historyInfo = jt.queryForObject(sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), HISTORY_DETAIL_ROW_MAPPER); return historyInfo; } catch (EmptyResultDataAccessException emptyResultDataAccessException) { return null; } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[detail-previous-config-history] error, id:{}", new Object[] {id}, e); throw e; } } @Override public int findConfigHistoryCountByTime(final Timestamp startTime) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.START_TIME, startTime); MapperResult mapperResult = historyConfigInfoMapper.findConfigHistoryCountByTime(context); Integer result = jt.queryForObject(mapperResult.getSql(), mapperResult.getParamList().toArray(), Integer.class); if (result == null) { throw new IllegalArgumentException("findConfigHistoryCountByTime error"); } return result; } @Override public ConfigHistoryInfo getNextHistoryInfo(String dataId, String group, String tenant, String publishType, String grayName, long startNid) { HistoryConfigInfoMapper historyConfigInfoMapper = mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.HIS_CONFIG_INFO); MapperContext context = new MapperContext(); context.putWhereParameter(FieldConstant.DATA_ID, dataId); context.putWhereParameter(FieldConstant.GROUP_ID, group); context.putWhereParameter(FieldConstant.TENANT_ID, tenant); context.putWhereParameter(FieldConstant.PUBLISH_TYPE, publishType); context.putWhereParameter(FieldConstant.NID, startNid); context.putWhereParameter(FieldConstant.GRAY_NAME, grayName); MapperResult sqlFetchRows = historyConfigInfoMapper.getNextHistoryInfo(context); try { ConfigHistoryInfo historyInfo = jt.queryForObject(sqlFetchRows.getSql(), sqlFetchRows.getParamList().toArray(), HISTORY_DETAIL_ROW_MAPPER); return historyInfo; } catch (EmptyResultDataAccessException emptyResultDataAccessException) { return null; } catch (DataAccessException e) { LogUtil.FATAL_LOG.error("[db-error] " + e, e); throw e; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/sql/EmbeddedStorageContextUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.sql; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.event.ConfigDumpEvent; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.sys.env.EnvUtil; import java.sql.Timestamp; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Temporarily saves all insert, update, and delete statements under a transaction in the order in which they occur. * * @author liaochuntao */ public class EmbeddedStorageContextUtils { /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param configInfo {@link ConfigInfo} * @param srcIp The IP of the operator * @param time Operating time */ public static void onModifyConfigInfo(ConfigInfo configInfo, String srcIp, Timestamp time) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(false).namespaceId(configInfo.getTenant()) .dataId(configInfo.getDataId()).group(configInfo.getGroup()).isBeta(false) .content(configInfo.getContent()).type(configInfo.getType()).handleIp(srcIp) .lastModifiedTs(time.getTime()).encryptedDataKey(configInfo.getEncryptedDataKey()).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param configInfo {@link ConfigInfo} * @param betaIps Receive client IP for grayscale configuration publishing * @param srcIp The IP of the operator * @param time Operating time */ public static void onModifyConfigBetaInfo(ConfigInfo configInfo, String betaIps, String srcIp, Timestamp time) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(false).namespaceId(configInfo.getTenant()) .dataId(configInfo.getDataId()).group(configInfo.getGroup()).isBeta(true).betaIps(betaIps) .content(configInfo.getContent()).type(configInfo.getType()).handleIp(srcIp) .lastModifiedTs(time.getTime()).encryptedDataKey(configInfo.getEncryptedDataKey()).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param configInfo {@link ConfigInfo} * @param tag tag info * @param srcIp The IP of the operator * @param time Operating time */ public static void onModifyConfigTagInfo(ConfigInfo configInfo, String tag, String srcIp, Timestamp time) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(false).namespaceId(configInfo.getTenant()) .dataId(configInfo.getDataId()).group(configInfo.getGroup()).isBeta(false).tag(tag) .content(configInfo.getContent()).type(configInfo.getType()).handleIp(srcIp) .lastModifiedTs(time.getTime()).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param configInfo {@link ConfigInfo} * @param grayName gray name * @param grayRule gray rule * @param srcIp The IP of the operator * @param time Operating time */ public static void onModifyConfigGrayInfo(ConfigInfo configInfo, String grayName, String grayRule, String srcIp, Timestamp time) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(false).namespaceId(configInfo.getTenant()) .dataId(configInfo.getDataId()).group(configInfo.getGroup()).isBeta(false).grayName(grayName) .grayRule(grayRule).content(configInfo.getContent()).type(configInfo.getType()).handleIp(srcIp) .lastModifiedTs(time.getTime()).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param namespaceId namespaceId * @param group groupName * @param dataId dataId * @param srcIp The IP of the operator * @param time Operating time */ public static void onDeleteConfigInfo(String namespaceId, String group, String dataId, String srcIp, Timestamp time) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(true).namespaceId(namespaceId).group(group) .dataId(dataId).isBeta(false).handleIp(srcIp).lastModifiedTs(time.getTime()).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param configInfos {@link ConfigAllInfo} list */ public static void onBatchDeleteConfigInfo(List configInfos) { if (!EnvUtil.getStandaloneMode()) { List events = new ArrayList<>(); for (ConfigAllInfo configInfo : configInfos) { String namespaceId = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant(); ConfigDumpEvent event = ConfigDumpEvent.builder().remove(true).namespaceId(namespaceId) .group(configInfo.getGroup()).dataId(configInfo.getDataId()).isBeta(false).build(); events.add(event); } Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFOS_CONFIG_DUMP_EVENT, JacksonUtils.toJson(events)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param namespaceId namespaceId * @param group group * @param dataId dataId * @param time Operating time */ public static void onDeleteConfigBetaInfo(String namespaceId, String group, String dataId, long time) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(true).namespaceId(namespaceId).dataId(dataId) .group(group).isBeta(true).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param namespaceId namespaceId * @param group group * @param dataId dataId * @param tag tag info * @param srcIp The IP of the operator */ public static void onDeleteConfigTagInfo(String namespaceId, String group, String dataId, String tag, String srcIp) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(true).namespaceId(namespaceId).group(group) .dataId(dataId).isBeta(true).tag(tag).handleIp(srcIp).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } /** * In the case of the in-cluster storage mode, the logic of horizontal notification is implemented asynchronously * via the raft state machine, along with the information. * * @param namespaceId namespaceId * @param group group * @param dataId dataId * @param grayName gray name * @param srcIp The IP of the operator */ public static void onDeleteConfigGrayInfo(String namespaceId, String group, String dataId, String grayName, String srcIp) { if (!EnvUtil.getStandaloneMode()) { ConfigDumpEvent event = ConfigDumpEvent.builder().remove(true).namespaceId(namespaceId).group(group) .dataId(dataId).isBeta(true).grayName(grayName).handleIp(srcIp).build(); Map extendInfo = new HashMap<>(2); extendInfo.put(Constants.EXTEND_INFO_CONFIG_DUMP_EVENT, JacksonUtils.toJson(event)); EmbeddedStorageContextHolder.putAllExtendInfo(extendInfo); } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/sql/ExternalStorageUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.sql; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; /** * external storage utils. * @author shiyiyue */ public class ExternalStorageUtils { public static KeyHolder createKeyHolder() { return new GeneratedKeyHolder(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/service/trace/ConfigTraceService.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.trace; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.utils.LogUtil; import com.alibaba.nacos.sys.utils.InetUtils; import org.springframework.stereotype.Service; import java.util.concurrent.TimeUnit; /** * Config trace. * * @author Nacos */ @Service public class ConfigTraceService { /** * persist event. */ public static final String PERSISTENCE_EVENT = "persist"; public static final String PERSISTENCE_EVENT_BETA = "persist-beta"; public static final String PERSISTENCE_EVENT_TAG = "persist-tag"; public static final String PERSISTENCE_EVENT_METADATA = "persist-metadata"; /** * persist type. */ public static final String PERSISTENCE_TYPE_PUB = "pub"; public static final String PERSISTENCE_TYPE_REMOVE = "remove"; public static final String PERSISTENCE_TYPE_MERGE = "merge"; /** * notify event. */ public static final String NOTIFY_EVENT = "notify"; public static final String NOTIFY_EVENT_BETA = "notify-beta"; public static final String NOTIFY_EVENT_BATCH = "notify-batch"; public static final String NOTIFY_EVENT_TAG = "notify-tag"; /** * notify type. */ public static final String NOTIFY_TYPE_OK = "ok"; public static final String NOTIFY_TYPE_ERROR = "error"; public static final String NOTIFY_TYPE_UNHEALTH = "unhealth"; public static final String NOTIFY_TYPE_EXCEPTION = "exception"; /** * dump event. */ public static final String DUMP_EVENT = "dump"; public static final String DUMP_EVENT_BETA = "dump-beta"; public static final String DUMP_EVENT_BATCH = "dump-batch"; public static final String DUMP_EVENT_TAG = "dump-tag"; /** * dump type. */ public static final String DUMP_TYPE_OK = "ok"; public static final String DUMP_TYPE_REMOVE_OK = "remove-ok"; public static final String DUMP_TYPE_ERROR = "error"; /** * pull event. */ public static final String PULL_EVENT = "pull"; /** * pull type. */ public static final String PULL_TYPE_OK = "ok"; public static final String PULL_TYPE_NOTFOUND = "not-found"; public static final String PULL_TYPE_CONFLICT = "conflict"; public static final String PULL_TYPE_ERROR = "error"; /** * log persistence event. * * @param dataId data id * @param group group * @param tenant tenant * @param requestIpAppName request ip app name * @param ts ts * @param handleIp remote ip * @param type type * @param content content */ public static void logPersistenceEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String event, String type, String content) { if (!LogUtil.TRACE_LOG.isInfoEnabled()) { return; } // Convenient tlog segmentation. if (StringUtils.isBlank(tenant)) { tenant = null; } //localIp | dataid | group | tenant | requestIpAppName | ts | client ip | event | type | [delayed = -1] | ext // (md5) String md5 = content == null ? null : MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE); LogUtil.TRACE_LOG.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", InetUtils.getSelfIP(), dataId, group, tenant, requestIpAppName, ts, handleIp, event, type, -1, md5); } /** * log notify event. * * @param dataId data id * @param group group * @param tenant tenant * @param requestIpAppName request ip app name * @param ts ts * @param handleIp handle ip * @param type type * @param delayed delayed * @param targetIp target ip */ public static void logNotifyEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String event, String type, long delayed, String targetIp) { if (!LogUtil.TRACE_LOG.isInfoEnabled()) { return; } if (delayed < 0) { delayed = 0; } MetricsMonitor.getNotifyRtTimer().record(delayed, TimeUnit.MILLISECONDS); // Convenient tlog segmentation if (StringUtils.isBlank(tenant)) { tenant = null; } //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed] | ext // (targetIp) LogUtil.TRACE_LOG.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", InetUtils.getSelfIP(), dataId, group, tenant, requestIpAppName, ts, handleIp, event, type, delayed, targetIp); } /** * log dump event. * * @param dataId data id * @param group group * @param tenant tenant * @param requestIpAppName request ip app name * @param ts ts * @param handleIp handle ip * @param type type * @param delayed delayed * @param length length */ public static void logDumpEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String type, long delayed, long length) { logDumpEventInner(dataId, group, tenant, requestIpAppName, ts, handleIp, ConfigTraceService.DUMP_EVENT, type, delayed, length); } public static void logDumpGrayNameEvent(String dataId, String group, String tenant, String grayName, String requestIpAppName, long ts, String handleIp, String type, long delayed, long length) { logDumpEventInner(dataId, group, tenant, requestIpAppName, ts, handleIp, ConfigTraceService.DUMP_EVENT + "-" + grayName, type, delayed, length); } private static void logDumpEventInner(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String event, String type, long delayed, long length) { if (!LogUtil.TRACE_LOG.isInfoEnabled()) { return; } if (delayed < 0) { delayed = 0; } MetricsMonitor.getDumpRtTimer().record(delayed, TimeUnit.MILLISECONDS); // Convenient tlog segmentation if (StringUtils.isBlank(tenant)) { tenant = null; } //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed] | length LogUtil.TRACE_LOG.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", InetUtils.getSelfIP(), dataId, group, tenant, requestIpAppName, ts, handleIp, event, type, delayed, length); } /** * log dump all event. * * @param dataId data id * @param group group * @param tenant tenant * @param requestIpAppName request ip app name * @param ts ts * @param handleIp handle ip * @param type type */ public static void logDumpAllEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String handleIp, String type) { if (!LogUtil.TRACE_LOG.isInfoEnabled()) { return; } // Convenient tlog segmentation if (StringUtils.isBlank(tenant)) { tenant = null; } //localIp | dataid | group | tenant | requestIpAppName | ts | handleIp | event | type | [delayed = -1] LogUtil.TRACE_LOG.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", InetUtils.getSelfIP(), dataId, group, tenant, requestIpAppName, ts, handleIp, "dump-all", type, -1); } /** * log pull event. * * @param dataId data id * @param group group * @param tenant tenant * @param requestIpAppName request ip app name * @param ts ts * @param type type * @param delayed delayed * @param clientIp clientIp * @param isNotify isNotify * @param model model */ public static void logPullEvent(String dataId, String group, String tenant, String requestIpAppName, long ts, String event, String type, long delayed, String clientIp, boolean isNotify, String model) { if (!LogUtil.TRACE_LOG.isInfoEnabled()) { return; } // Convenient tlog segmentation if (StringUtils.isBlank(tenant)) { tenant = null; } if (isNotify && delayed < 0) { delayed = 0; } // localIp | dataid | group | tenant| requestIpAppName| ts | event | type | [delayed] |clientIp| isNotify | mode(http/grpc) LogUtil.TRACE_LOG.info("{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}|{}", InetUtils.getSelfIP(), dataId, group, tenant, requestIpAppName, ts, event, type, delayed, clientIp, isNotify, model); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/AccumulateStatCount.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import java.util.concurrent.atomic.AtomicLong; /** * Accumulate Stat Count. * * @author Nacos */ public class AccumulateStatCount { final AtomicLong total = new AtomicLong(0); long lastStatValue = 0; public long increase() { return total.incrementAndGet(); } /** * accumulate stat. * * @return stat. */ public long stat() { long tmp = total.get() - lastStatValue; lastStatValue += tmp; return tmp; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/AppNameUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.utils.StringUtils; import java.io.File; /** * appName util. * * @author Nacos */ public class AppNameUtils { private static final String PARAM_MARKING_PROJECT = "project.name"; private static final String PARAM_MARKING_JBOSS = "jboss.server.home.dir"; private static final String PARAM_MARKING_JETTY = "jetty.home"; private static final String PARAM_MARKING_TOMCAT = "catalina.base"; private static final String LINUX_ADMIN_HOME = "/home/admin/"; private static final String SERVER_JBOSS = "jboss"; private static final String SERVER_JETTY = "jetty"; private static final String SERVER_TOMCAT = "tomcat"; private static final String SERVER_UNKNOWN = "unknown server"; private static final String DEFAULT_APP_NAME = "unknown"; public static String getAppName() { String appName; appName = getAppNameByProjectName(); if (appName != null) { return appName; } appName = getAppNameByServerHome(); if (appName != null) { return appName; } return DEFAULT_APP_NAME; } private static String getAppNameByProjectName() { return System.getProperty(PARAM_MARKING_PROJECT); } private static String getAppNameByServerHome() { String serverHome = null; if (SERVER_JBOSS.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_JBOSS); } else if (SERVER_JETTY.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_JETTY); } else if (SERVER_TOMCAT.equals(getServerType())) { serverHome = System.getProperty(PARAM_MARKING_TOMCAT); } if (serverHome != null && serverHome.startsWith(LINUX_ADMIN_HOME)) { return StringUtils.substringBetween(serverHome, LINUX_ADMIN_HOME, File.separator); } return null; } private static String getServerType() { String serverType; if (System.getProperty(PARAM_MARKING_JBOSS) != null) { serverType = SERVER_JBOSS; } else if (System.getProperty(PARAM_MARKING_JETTY) != null) { serverType = SERVER_JETTY; } else if (System.getProperty(PARAM_MARKING_TOMCAT) != null) { serverType = SERVER_TOMCAT; } else { serverType = SERVER_UNKNOWN; } return serverType; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigExecutor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.common.utils.ThreadUtils; import com.alibaba.nacos.config.server.Config; import com.alibaba.nacos.core.utils.ClassUtils; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * Config executor. * * @author liaochuntao */ public final class ConfigExecutor { private static final ScheduledExecutorService TIMER_EXECUTOR = ExecutorFactory.Managed.newScheduledExecutorService( ClassUtils.getCanonicalName(Config.class), 8, new NameThreadFactory("com.alibaba.nacos.config.server.timer")); private static final ScheduledExecutorService CAPACITY_MANAGEMENT_EXECUTOR = ExecutorFactory.Managed.newSingleScheduledExecutorService( ClassUtils.getCanonicalName(Config.class), new NameThreadFactory("com.alibaba.nacos.config.CapacityManagement")); private static final ScheduledExecutorService ASYNC_NOTIFY_EXECUTOR = ExecutorFactory.Managed.newScheduledExecutorService( ClassUtils.getCanonicalName(Config.class), 100, new NameThreadFactory("com.alibaba.nacos.config.AsyncNotifyService")); private static final ScheduledExecutorService ASYNC_CONFIG_CHANGE_PLUGIN_EXECUTOR = ExecutorFactory.Managed.newScheduledExecutorService( ClassUtils.getCanonicalName(Config.class), ThreadUtils.getSuitableThreadCount(), new NameThreadFactory("com.alibaba.nacos.config.plugin.AsyncService")); private static final ScheduledExecutorService CONFIG_SUB_SERVICE_EXECUTOR = ExecutorFactory.Managed.newScheduledExecutorService( ClassUtils.getCanonicalName(Config.class), ThreadUtils.getSuitableThreadCount(), new NameThreadFactory("com.alibaba.nacos.config.ConfigSubService")); private static final ScheduledExecutorService LONG_POLLING_EXECUTOR = ExecutorFactory.Managed.newSingleScheduledExecutorService( ClassUtils.getCanonicalName(Config.class), new NameThreadFactory("com.alibaba.nacos.config.LongPolling")); private static final ScheduledExecutorService ASYNC_CONFIG_CHANGE_NOTIFY_EXECUTOR = ExecutorFactory.Managed.newScheduledExecutorService( ClassUtils.getCanonicalName(Config.class), ThreadUtils.getSuitableThreadCount(), new NameThreadFactory("com.alibaba.nacos.config.server.remote.ConfigChangeNotifier")); public static void scheduleConfigTask(Runnable command, long initialDelay, long delay, TimeUnit unit) { TIMER_EXECUTOR.scheduleWithFixedDelay(command, initialDelay, delay, unit); } public static void scheduleConfigChangeTask(Runnable command, long delay, TimeUnit unit) { TIMER_EXECUTOR.schedule(command, delay, unit); } public static void scheduleCorrectUsageTask(Runnable runnable, long initialDelay, long delay, TimeUnit unit) { CAPACITY_MANAGEMENT_EXECUTOR.scheduleWithFixedDelay(runnable, initialDelay, delay, unit); } public static void executeAsyncNotify(Runnable runnable) { ASYNC_NOTIFY_EXECUTOR.execute(runnable); } public static void scheduleAsyncNotify(Runnable command, long delay, TimeUnit unit) { ASYNC_NOTIFY_EXECUTOR.schedule(command, delay, unit); } public static void executeAsyncConfigChangePluginTask(Runnable runnable) { ASYNC_CONFIG_CHANGE_PLUGIN_EXECUTOR.execute(runnable); } public static int asyncNotifyQueueSize() { return ((ScheduledThreadPoolExecutor) ASYNC_NOTIFY_EXECUTOR).getQueue().size(); } public static int asyncConfigChangeClientNotifyQueueSize() { return ((ScheduledThreadPoolExecutor) ASYNC_CONFIG_CHANGE_NOTIFY_EXECUTOR).getQueue().size(); } public static ScheduledExecutorService getConfigSubServiceExecutor() { return CONFIG_SUB_SERVICE_EXECUTOR; } public static ScheduledExecutorService getClientConfigNotifierServiceExecutor() { return ASYNC_CONFIG_CHANGE_NOTIFY_EXECUTOR; } public static ScheduledFuture scheduleClientConfigNotifier(Runnable runnable, long delay, TimeUnit unit) { return ASYNC_CONFIG_CHANGE_NOTIFY_EXECUTOR.schedule(runnable, delay, unit); } public static void scheduleLongPolling(Runnable runnable, long initialDelay, long delay, TimeUnit unit) { LONG_POLLING_EXECUTOR.scheduleWithFixedDelay(runnable, initialDelay, delay, unit); } public static ScheduledFuture scheduleLongPolling(Runnable runnable, long delay, TimeUnit unit) { return LONG_POLLING_EXECUTOR.schedule(runnable, delay, unit); } public static void executeLongPolling(Runnable runnable) { LONG_POLLING_EXECUTOR.execute(runnable); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigExtInfoUtil.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashMap; import java.util.Map; /** * Extra info util. * * @author Nacos */ public class ConfigExtInfoUtil { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigExtInfoUtil.class); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final Map EXTRA_INFO_KEYS_MAPPING = new HashMap<>(); static { EXTRA_INFO_KEYS_MAPPING.put("type", "type"); EXTRA_INFO_KEYS_MAPPING.put("config_tags", "config_tags"); EXTRA_INFO_KEYS_MAPPING.put("src_user", "src_user"); EXTRA_INFO_KEYS_MAPPING.put("desc", "c_desc"); EXTRA_INFO_KEYS_MAPPING.put("use", "c_use"); EXTRA_INFO_KEYS_MAPPING.put("effect", "effect"); EXTRA_INFO_KEYS_MAPPING.put("schema", "c_schema"); } private ConfigExtInfoUtil() { } /** * Extract the extInfo from advance config info. */ public static String getExtraInfoFromAdvanceInfoMap(Map advanceConfigInfoMap, String srcUser) { try { if (advanceConfigInfoMap == null || advanceConfigInfoMap.isEmpty()) { return null; } ObjectNode node = OBJECT_MAPPER.createObjectNode(); if (StringUtils.isNotBlank(srcUser)) { node.put("src_user", srcUser); } for (Map.Entry entry : EXTRA_INFO_KEYS_MAPPING.entrySet()) { String key = entry.getKey(); String mappedKey = entry.getValue(); Object advanceConfigInfoValue = advanceConfigInfoMap.get(key); if (advanceConfigInfoValue instanceof String && StringUtils.isNotBlank( (String) advanceConfigInfoValue)) { node.put(mappedKey, ((String) advanceConfigInfoValue).trim()); } } return OBJECT_MAPPER.writeValueAsString(node); } catch (Exception ex) { LOGGER.error("Failed to get extra info from advance info map", ex); return null; } } /** * Extract the extInfo from all config info. */ public static String getExtInfoFromAllInfo(ConfigAllInfo configAllInfo) { ObjectNode node = OBJECT_MAPPER.createObjectNode(); if (StringUtils.isNotBlank(configAllInfo.getType())) { node.put("type", configAllInfo.getType()); } if (StringUtils.isNotBlank(configAllInfo.getConfigTags())) { node.put("config_tags", configAllInfo.getConfigTags()); } if (StringUtils.isNotBlank(configAllInfo.getEffect())) { node.put("effect", configAllInfo.getEffect()); } if (StringUtils.isNotBlank(configAllInfo.getCreateUser())) { node.put("src_user", configAllInfo.getCreateUser()); } if (StringUtils.isNotBlank(configAllInfo.getDesc())) { node.put("c_desc", configAllInfo.getDesc()); } if (StringUtils.isNotBlank(configAllInfo.getUse())) { node.put("c_use", configAllInfo.getUse()); } if (StringUtils.isNotBlank(configAllInfo.getSchema())) { node.put("c_schema", configAllInfo.getSchema()); } try { return OBJECT_MAPPER.writeValueAsString(node); } catch (Exception ex) { LOGGER.error("Failed to get extra info from all config info", ex); return null; } } /** * Extract the extInfo from gray config info. */ public static String getExtInfoFromGrayInfo(String grayName, String grayRuleTmp, String oldSrcUser) { ObjectNode node = OBJECT_MAPPER.createObjectNode(); ObjectNode grayRuleNode = OBJECT_MAPPER.createObjectNode(); if (StringUtils.isNotBlank(grayName)) { node.put("gray_name", grayName); } if (StringUtils.isNotBlank(oldSrcUser)) { node.put("src_user", oldSrcUser); } if (StringUtils.isNotBlank(grayRuleTmp)) { try { JsonNode parsedGrayRuleNode = OBJECT_MAPPER.readTree(grayRuleTmp); if (parsedGrayRuleNode.has(Constants.GRAY_RULE_TYPE)) { grayRuleNode.put(Constants.GRAY_RULE_TYPE, parsedGrayRuleNode.get(Constants.GRAY_RULE_TYPE).asText()); } if (parsedGrayRuleNode.has(Constants.GRAY_RULE_EXPR)) { grayRuleNode.put(Constants.GRAY_RULE_EXPR, parsedGrayRuleNode.get(Constants.GRAY_RULE_EXPR).asText()); } if (parsedGrayRuleNode.has(Constants.GRAY_RULE_VERSION)) { grayRuleNode.put(Constants.GRAY_RULE_VERSION, parsedGrayRuleNode.get(Constants.GRAY_RULE_VERSION).asText()); } if (parsedGrayRuleNode.has(Constants.GRAY_RULE_PRIORITY)) { grayRuleNode.put(Constants.GRAY_RULE_PRIORITY, parsedGrayRuleNode.get(Constants.GRAY_RULE_PRIORITY).asText()); } node.put("gray_rule", grayRuleNode.toString()); } catch (Exception ex) { LOGGER.error("Failed to parse gray rule as json", ex); return null; } } try { return OBJECT_MAPPER.writeValueAsString(node); } catch (Exception ex) { LOGGER.error("Failed to serialize extra info from gray info", ex); return null; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/ConfigTagUtil.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import java.util.Arrays; /** * Config Tag util. * * @author PoisonGravity */ public class ConfigTagUtil { public static final String VIRTUAL_SERVICE = "virtual-service"; public static final String DESTINATION_RULE = "destination-rule"; private static final String TAGS_DELIMITER = ","; private static final String HYPHEN = "-"; /** *

    Checks if config tags contains "virtual-service" or "destination-rule".

    * @param configTags the tags to check * @return {@code true} if the config tags contains "virtual-service" or "destination-rule". */ public static boolean isIstio(String configTags) { if (configTags == null) { return false; } if (configTags.isEmpty()) { return false; } return Arrays.stream(configTags.split(TAGS_DELIMITER)) .map(tag -> tag.trim().replaceAll(HYPHEN, "")) .anyMatch(tag -> tag.equalsIgnoreCase(VIRTUAL_SERVICE.replaceAll(HYPHEN, "")) || tag.equalsIgnoreCase(DESTINATION_RULE.replaceAll(HYPHEN, ""))); } /** *

    Gets the type of Istio from the config tags.

    * @param configTags the tags to check * @return the type of Istio if it is found, {@code null} otherwise. * @throws IllegalArgumentException if configTags is null. */ public static String getIstioType(String configTags) { if (configTags == null) { throw new IllegalArgumentException("configTags cannot be null."); } if (configTags.isEmpty()) { return null; } return Arrays.stream(configTags.split(TAGS_DELIMITER)) .map(tag -> tag.trim().replaceAll(HYPHEN, "")) .filter(tag -> tag.equalsIgnoreCase(VIRTUAL_SERVICE.replaceAll(HYPHEN, "")) || tag.equalsIgnoreCase(DESTINATION_RULE.replaceAll(HYPHEN, ""))) .findFirst() .orElse(null); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/ContentUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.constant.Constants; import static com.alibaba.nacos.config.server.constant.Constants.WORD_SEPARATOR; /** * Content utils. * * @author Nacos */ public class ContentUtils { /** * verify the pub config content. * * @param content content */ public static void verifyIncrementPubContent(String content) { if (content == null || content.length() == 0) { throw new IllegalArgumentException("The content for publishing or deleting cannot be null!"); } for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (c == '\r' || c == '\n') { throw new IllegalArgumentException("The content for publishing or deleting cannot contain enter and next line symbol!"); } if (c == Constants.WORD_SEPARATOR.charAt(0)) { throw new IllegalArgumentException("The content for publishing or deleting cannot contain (char)2!"); } } } public static String getContentIdentity(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { throw new IllegalArgumentException("The content does not contain separator!"); } return content.substring(0, index); } public static String getContent(String content) { int index = content.indexOf(WORD_SEPARATOR); if (index == -1) { throw new IllegalArgumentException("The content does not contain separator!"); } return content.substring(index + 1); } /** * Truncate the content. * * @param content content * @return content after truncate. */ public static String truncateContent(String content) { if (content == null) { return ""; } else if (content.length() <= LIMIT_CONTENT_SIZE) { return content; } else { return content.substring(0, 100) + "..."; } } private static final int LIMIT_CONTENT_SIZE = 100; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.utils.StringUtils; /** * Synthesize dataId+groupId form. Escape reserved characters in dataId and groupId. * * @author jiuRen */ public class GroupKey { public static String getKey(String dataId, String group) { return doGetKey(dataId, group, ""); } public static String getKey(String dataId, String group, String datumStr) { return doGetKey(dataId, group, datumStr); } public static String getKeyTenant(String dataId, String group, String tenant) { return doGetKey(dataId, group, tenant); } private static String doGetKey(String dataId, String group, String datumStr) { StringBuilder sb = new StringBuilder(); urlEncode(dataId, sb); sb.append('+'); urlEncode(group, sb); if (StringUtils.isNotEmpty(datumStr)) { sb.append('+'); urlEncode(datumStr, sb); } return sb.toString(); } /** * Parse the group key. */ public static String[] parseKey(String groupKey) { StringBuilder sb = new StringBuilder(); String dataId = null; String group = null; String tenant = null; for (int i = 0; i < groupKey.length(); ++i) { char c = groupKey.charAt(i); if ('+' == c) { if (null == dataId) { dataId = sb.toString(); sb.setLength(0); } else if (null == group) { group = sb.toString(); sb.setLength(0); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else if ('%' == c) { char next = groupKey.charAt(++i); char nextnext = groupKey.charAt(++i); if ('2' == next && 'B' == nextnext) { sb.append('+'); } else if ('2' == next && '5' == nextnext) { sb.append('%'); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else { sb.append(c); } } if (StringUtils.isBlank(group)) { group = sb.toString(); } else { tenant = sb.toString(); } if (group.length() == 0) { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } return new String[] {dataId, group, tenant}; } /** * + -> %2B % -> %25. */ static void urlEncode(String str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { char c = str.charAt(idx); if ('+' == c) { sb.append("%2B"); } else if ('%' == c) { sb.append("%25"); } else { sb.append(c); } } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/GroupKey2.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.utils.StringUtils; /** * Group key util. * * @author Nacos */ public class GroupKey2 { public static String getKey(String dataId, String group) { StringBuilder sb = new StringBuilder(); urlEncode(dataId, sb); sb.append('+'); urlEncode(group, sb); return sb.toString(); } public static String getKey(String dataId, String group, String tenant) { StringBuilder sb = new StringBuilder(); urlEncode(dataId, sb); sb.append('+'); urlEncode(group, sb); if (StringUtils.isNotEmpty(tenant)) { sb.append('+'); urlEncode(tenant, sb); } return sb.toString(); } /** * Parse the group key. */ public static String[] parseKey(String groupKey) { StringBuilder sb = new StringBuilder(); String dataId = null; String group = null; String tenant = null; for (int i = 0; i < groupKey.length(); ++i) { char c = groupKey.charAt(i); if ('+' == c) { if (null == dataId) { dataId = sb.toString(); sb.setLength(0); } else if (null == group) { group = sb.toString(); sb.setLength(0); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else if ('%' == c) { char next = groupKey.charAt(++i); char nextnext = groupKey.charAt(++i); if ('2' == next && 'B' == nextnext) { sb.append('+'); } else if ('2' == next && '5' == nextnext) { sb.append('%'); } else { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } } else { sb.append(c); } } if (StringUtils.isBlank(group)) { group = sb.toString(); } else { tenant = sb.toString(); } if (group.length() == 0) { throw new IllegalArgumentException("invalid groupkey:" + groupKey); } return new String[] {dataId, group, tenant}; } /** * + -> %2B % -> %25. */ static void urlEncode(String str, StringBuilder sb) { for (int idx = 0; idx < str.length(); ++idx) { char c = str.charAt(idx); if ('+' == c) { sb.append("%2B"); } else if ('%' == c) { sb.append("%25"); } else { sb.append(c); } } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/LogUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import ch.qos.logback.classic.Level; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Log util. * * @author Nacos */ public class LogUtil { /** * Default log. */ public static final Logger DEFAULT_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.startLog"); /** * Fatal error log, require alarm. */ public static final Logger FATAL_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.fatal"); /** * Http client log. */ public static final Logger PULL_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.pullLog"); public static final Logger PULL_CHECK_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.pullCheckLog"); /** * Dump log. */ public static final Logger DUMP_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.dumpLog"); public static final Logger MEMORY_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.monitorLog"); public static final Logger CLIENT_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.clientLog"); public static final Logger TRACE_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.traceLog"); public static final Logger NOTIFY_LOG = LoggerFactory.getLogger("com.alibaba.nacos.config.notifyLog"); public static void setLogLevel(String logName, String level) { switch (logName) { case "config-server": ((ch.qos.logback.classic.Logger) DEFAULT_LOG).setLevel(Level.valueOf(level)); break; case "config-fatal": ((ch.qos.logback.classic.Logger) FATAL_LOG).setLevel(Level.valueOf(level)); break; case "config-pull": ((ch.qos.logback.classic.Logger) PULL_LOG).setLevel(Level.valueOf(level)); break; case "config-pull-check": ((ch.qos.logback.classic.Logger) PULL_CHECK_LOG).setLevel(Level.valueOf(level)); break; case "config-dump": ((ch.qos.logback.classic.Logger) DUMP_LOG).setLevel(Level.valueOf(level)); break; case "config-memory": ((ch.qos.logback.classic.Logger) MEMORY_LOG).setLevel(Level.valueOf(level)); break; case "config-client-request": ((ch.qos.logback.classic.Logger) CLIENT_LOG).setLevel(Level.valueOf(level)); break; case "config-trace": ((ch.qos.logback.classic.Logger) TRACE_LOG).setLevel(Level.valueOf(level)); break; case "config-notify": ((ch.qos.logback.classic.Logger) NOTIFY_LOG).setLevel(Level.valueOf(level)); break; default: break; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/MD5Util.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.core.utils.StringPool; import com.alibaba.nacos.common.utils.StringUtils; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.CharArrayWriter; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.Writer; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.alibaba.nacos.config.server.constant.Constants.LINE_SEPARATOR; import static com.alibaba.nacos.config.server.constant.Constants.WORD_SEPARATOR; /** * MD5 util. * * @author Nacos */ public class MD5Util { /** * Compare Md5. */ public static Map compareMd5(HttpServletRequest request, HttpServletResponse response, Map clientMd5Map) { return Md5ComparatorDelegate.getInstance().compareMd5(request, response, clientMd5Map); } /** * Compare old Md5. */ public static String compareMd5OldResult(Map changedGroupKeys) { StringBuilder sb = new StringBuilder(); for (Map.Entry entry : changedGroupKeys.entrySet()) { String groupKey = entry.getKey(); String[] dataIdGroupId = GroupKey2.parseKey(groupKey); sb.append(dataIdGroupId[0]); sb.append(':'); sb.append(dataIdGroupId[1]); sb.append(';'); } return sb.toString(); } /** * Join and encode changedGroupKeys string. */ public static String compareMd5ResultString(Map changedGroupKeys) throws IOException { if (null == changedGroupKeys) { return ""; } StringBuilder sb = new StringBuilder(); for (Map.Entry entry : changedGroupKeys.entrySet()) { String groupKey = entry.getKey(); String[] dataIdGroupId = GroupKey2.parseKey(groupKey); sb.append(dataIdGroupId[0]); sb.append(WORD_SEPARATOR); sb.append(dataIdGroupId[1]); if (dataIdGroupId.length == 3) { if (StringUtils.isNotBlank(dataIdGroupId[2]) && !entry.getValue().isNamespaceTransfer()) { sb.append(WORD_SEPARATOR); sb.append(dataIdGroupId[2]); } } sb.append(LINE_SEPARATOR); } // To encode WORD_SEPARATOR and LINE_SEPARATOR invisible characters, encoded value is %02 and %01 return URLEncoder.encode(sb.toString(), "UTF-8"); } /** * Parse the transport protocol, which has two formats (W for field delimiter, L for each data delimiter) old: D w G * w MD5 l new: D w G w MD5 w T l. * * @param configKeysString protocol * @return protocol message */ public static Map getClientMd5Map(String configKeysString) { Map md5Map = new HashMap<>(5); if (null == configKeysString || "".equals(configKeysString)) { return md5Map; } int start = 0; List tmpList = new ArrayList<>(3); for (int i = start; i < configKeysString.length(); i++) { char c = configKeysString.charAt(i); if (c == WORD_SEPARATOR_CHAR) { tmpList.add(configKeysString.substring(start, i)); start = i + 1; if (tmpList.size() > 3) { // Malformed message and return parameter error. throw new IllegalArgumentException("invalid protocol,too much key"); } } else if (c == LINE_SEPARATOR_CHAR) { String endValue = ""; if (start + 1 <= i) { endValue = configKeysString.substring(start, i); } start = i + 1; String tenant; String md5; boolean ifNamespaceTransfer; // If it is the old message, the last digit is MD5. The post-multi-tenant message is tenant if (tmpList.size() == 2) { tenant = ""; md5 = endValue; ifNamespaceTransfer = NamespaceUtil.isNeedTransferNamespace(tenant); tenant = NamespaceUtil.processNamespaceParameter(tenant); } else { tenant = endValue; md5 = tmpList.get(2); ifNamespaceTransfer = NamespaceUtil.isNeedTransferNamespace(tenant); tenant = NamespaceUtil.processNamespaceParameter(tenant); } ConfigListenState configListenState = new ConfigListenState(md5); configListenState.setNamespaceTransfer(ifNamespaceTransfer); String groupKey = GroupKey2.getKey(tmpList.get(0), tmpList.get(1), tenant); groupKey = StringPool.get(groupKey); md5Map.put(groupKey, configListenState); tmpList.clear(); // Protect malformed messages if (md5Map.size() > 10000) { throw new IllegalArgumentException("invalid protocol, too much listener"); } } } return md5Map; } public static String toString(InputStream input, String encoding) throws IOException { return (null == encoding) ? toString(new InputStreamReader(input, Constants.ENCODE)) : toString(new InputStreamReader(input, encoding)); } /** * Reader to String. */ public static String toString(Reader reader) throws IOException { CharArrayWriter sw = new CharArrayWriter(); copy(reader, sw); return sw.toString(); } /** * Copy data to buffer. */ public static long copy(Reader input, Writer output) throws IOException { char[] buffer = new char[1024]; long count = 0; for (int n = 0; (n = input.read(buffer)) >= 0; ) { output.write(buffer, 0, n); count += n; } return count; } static final char WORD_SEPARATOR_CHAR = (char) 2; static final char LINE_SEPARATOR_CHAR = (char) 1; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/Md5Comparator.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.model.ConfigListenState; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.util.Map; /** * The interface Md5 comparator. * * @author Sunrisea */ public interface Md5Comparator { /** * Gets md 5 comparator name. * * @return the md 5 comparator name */ public String getName(); /** * Compare md 5 list. * * @param request the request * @param response the response * @param clientMd5Map the client md 5 map * @return the list */ public Map compareMd5(HttpServletRequest request, HttpServletResponse response, Map clientMd5Map); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/Md5ComparatorDelegate.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.sys.env.EnvUtil; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Map; /** * The type Md5 comparator delegate. * * @author Sunrisea */ public class Md5ComparatorDelegate { private static final Logger LOGGER = LoggerFactory.getLogger(Md5ComparatorDelegate.class); private static final Md5ComparatorDelegate INSTANCE = new Md5ComparatorDelegate(); private String md5ComparatorType = EnvUtil.getProperty("nacos.config.cache.type", "nacos"); private Md5Comparator md5Comparator; private Md5ComparatorDelegate() { Collection md5Comparators = NacosServiceLoader.load(Md5Comparator.class); for (Md5Comparator each : md5Comparators) { if (StringUtils.isEmpty(each.getName())) { LOGGER.warn( "[Md5ComparatorDelegate] Load Md5Comparator({}) Md5ComparatorName(null/empty) fail. Please add Md5ComparatorName to resolve", each.getClass().getName()); continue; } LOGGER.info("[Md5ComparatorDelegate] Load Md5Comparator({}) Md5ComparatorName({}) successfully.", each.getClass().getName(), each.getName()); if (StringUtils.equals(md5ComparatorType, each.getName())) { LOGGER.info("[Md5ComparatorDelegate] Matched Md5Comparator found,set md5Comparator={}", each.getClass().getName()); md5Comparator = each; } } if (md5Comparator == null) { LOGGER.info( "[Md5ComparatorDelegate] Matched Md5Comparator not found, load Default NacosMd5Comparator successfully"); md5Comparator = new NacosMd5Comparator(); } } public static Md5ComparatorDelegate getInstance() { return INSTANCE; } public Map compareMd5(HttpServletRequest request, HttpServletResponse response, Map clientMd5Map) { return md5Comparator.compareMd5(request, response, clientMd5Map); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/NacosMd5Comparator.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.service.ConfigCacheService; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; /** * The type Nacos md5 comparator. * * @author Sunrisea */ public class NacosMd5Comparator implements Md5Comparator { @Override public String getName() { return "nacos"; } @Override public Map compareMd5(HttpServletRequest request, HttpServletResponse response, Map clientMd5Map) { HashMap changedGroupKeys = new HashMap<>(clientMd5Map.size()); String tag = request.getHeader(VIPSERVER_TAG); for (Map.Entry entry : clientMd5Map.entrySet()) { String groupKey = entry.getKey(); String clientMd5 = entry.getValue().getMd5(); String ip = RequestUtil.getRemoteIp(request); boolean isUptodate = ConfigCacheService.isUptodate(groupKey, clientMd5, ip, tag); if (!isUptodate) { changedGroupKeys.put(entry.getKey(), entry.getValue()); } } return changedGroupKeys; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/ParamUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.common.utils.StringUtils; import org.springframework.http.HttpStatus; import java.util.Map; /** * Parameter validity check util. * * @author Nacos */ public class ParamUtils { private static char[] validChars = new char[] {'_', '-', '.', ':'}; private static final int TAG_MAX_LEN = 16; private static final int TENANT_MAX_LEN = 128; private static final String CONFIG_TAGS = "config_tags"; private static final String DESC = "desc"; private static final String USE = "use"; private static final String EFFECT = "effect"; private static final String TYPE = "type"; private static final String SCHEMA = "schema"; private static final String ENCRYPTED_DATA_KEY = "encryptedDataKey"; /** * Whitelist checks that valid parameters can only contain letters, Numbers, and characters in validChars, and * cannot be empty. */ public static boolean isValid(String param) { if (param == null) { return false; } int length = param.length(); for (int i = 0; i < length; i++) { char ch = param.charAt(i); if (!Character.isLetterOrDigit(ch) && !isValidChar(ch)) { return false; } } return true; } private static boolean isValidChar(char ch) { for (char c : validChars) { if (c == ch) { return true; } } return false; } /** * Check the parameter for [v1] and [v2]. */ public static void checkParam(String dataId, String group, String datumId, String content) throws NacosException { if (StringUtils.isBlank(dataId) || !isValid(dataId.trim())) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid dataId : " + dataId); } else if (StringUtils.isBlank(group) || !isValid(group)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid group : " + group); } else if (StringUtils.isBlank(datumId) || !isValid(datumId)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid datumId : " + datumId); } else if (StringUtils.isBlank(content)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "content is blank : " + content); } else if (content.length() > PropertyUtil.getMaxContent()) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid content, over " + PropertyUtil.getMaxContent()); } } /** * Check Config basic Parameters. * * @param dataId data Id * @param group group name * @param namespaceId namespace Id */ public static void checkParam(String dataId, String group, String namespaceId) throws NacosApiException { if (StringUtils.isBlank(dataId) || !isValid(dataId.trim())) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid dataId : " + dataId); } if (StringUtils.isBlank(group) || !isValid(group)) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid group : " + group); } checkTenantV2(namespaceId); } /** * Check the tag for [v1]. */ public static void checkParam(String tag) { if (StringUtils.isNotBlank(tag)) { if (!isValid(tag.trim())) { throw new IllegalArgumentException("invalid tag : " + tag); } if (tag.length() > TAG_MAX_LEN) { throw new IllegalArgumentException("too long tag, over 16"); } } } /** * Check the config info for [v1] and [v2]. */ public static void checkParam(Map configAdvanceInfo) throws NacosException { for (Map.Entry configAdvanceInfoTmp : configAdvanceInfo.entrySet()) { if (CONFIG_TAGS.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null) { String[] tagArr = ((String) configAdvanceInfoTmp.getValue()).split(","); if (tagArr.length > 5) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too much config_tags, over 5"); } for (String tag : tagArr) { if (tag.length() > 64) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long tag, over 64"); } } } } else if (DESC.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 128) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long desc, over 128"); } } else if (USE.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long use, over 32"); } } else if (EFFECT.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long effect, over 32"); } } else if (TYPE.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long type, over 32"); } } else if (SCHEMA.equals(configAdvanceInfoTmp.getKey())) { if (configAdvanceInfoTmp.getValue() != null && ((String) configAdvanceInfoTmp.getValue()).length() > 32768) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long schema, over 32768"); } } else if (ENCRYPTED_DATA_KEY.equals(configAdvanceInfoTmp.getKey())) { // No verification required } else { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid param"); } } } /** * Check the tag for [v2]. */ public static void checkParamV2(String tag) throws NacosApiException { if (StringUtils.isNotBlank(tag)) { if (!isValid(tag.trim())) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid tag : " + tag); } if (tag.length() > TAG_MAX_LEN) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long tag, over 16"); } } } /** * Check the tenant for [v1]. */ public static void checkTenant(String tenant) { if (StringUtils.isNotBlank(tenant)) { if (!isValid(tenant.trim())) { throw new IllegalArgumentException("invalid tenant"); } if (tenant.length() > TENANT_MAX_LEN) { throw new IllegalArgumentException("too long tenant, over 128"); } } } /** * Check the namespaceId for [v2]. */ public static void checkTenantV2(String namespaceId) throws NacosApiException { if (StringUtils.isNotBlank(namespaceId)) { if (!isValid(namespaceId.trim())) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "invalid namespaceId"); } if (namespaceId.length() > TENANT_MAX_LEN) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "too long namespaceId, over 128"); } } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/PropertyUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.constant.PropertiesConstant; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import java.io.BufferedReader; import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.io.IOException; import java.util.Optional; import java.util.concurrent.atomic.AtomicInteger; import static com.alibaba.nacos.config.server.utils.LogUtil.FATAL_LOG; /** * Properties util. * * @author Nacos */ public class PropertyUtil implements ApplicationContextInitializer { private static final Logger LOGGER = LogUtil.DEFAULT_LOG; private static int notifyConnectTimeout = 100; private static int notifySocketTimeout = 200; private static int maxHealthCheckFailCount = 12; private static boolean isHealthCheck = true; private static int maxContent = 10 * 1024 * 1024; /** * Whether to enable capacity management. */ private static boolean isManageCapacity = true; /** * gray compatible model. */ private static boolean grayCompatibleModel = true; public static final ThreadLocal GRAY_MIGRATE_FLAG = ThreadLocal.withInitial(() -> false); public static final ThreadLocal CONFIG_MIGRATE_FLAG = ThreadLocal.withInitial(() -> false); /** * Whether to enable the limit check function of capacity management, including the upper limit of configuration * number, configuration content size limit, etc. */ private static boolean isCapacityLimitCheck = false; /** * The default cluster capacity limit. */ private static int defaultClusterQuota = 100000; /** * the default capacity limit per Group. */ private static int defaultGroupQuota = 200; /** * The default capacity limit per Tenant. */ private static int defaultTenantQuota = 200; /** * The maximum size of the content in the configuration of a single, unit for bytes. */ private static int defaultMaxSize = 100 * 1024; /** * The default Maximum number of aggregated data. */ private static int defaultMaxAggrCount = 10000; /** * The maximum size of content in a single subconfiguration of aggregated data. */ private static int defaultMaxAggrSize = 1024; /** * Initialize the expansion percentage of capacity has reached the limit. */ private static int initialExpansionPercent = 100; /** * Fixed capacity information table usage (usage) time interval, the unit is in seconds. */ private static int correctUsageDelay = 10 * 60; private static boolean dumpChangeOn = true; /** * The number of days to retain the configuration history, the default is 30 days. */ private static int configRententionDays = 30; /** * dumpChangeWorkerInterval, default 30 seconds. */ private static long dumpChangeWorkerInterval = 30 * 1000L; public static boolean isDumpChangeOn() { return dumpChangeOn; } public static void setDumpChangeOn(boolean dumpChangeOn) { PropertyUtil.dumpChangeOn = dumpChangeOn; } public static long getDumpChangeWorkerInterval() { return dumpChangeWorkerInterval; } public static void setDumpChangeWorkerInterval(long dumpChangeWorkerInterval) { PropertyUtil.dumpChangeWorkerInterval = dumpChangeWorkerInterval; } public static int getNotifyConnectTimeout() { return notifyConnectTimeout; } public static void setNotifyConnectTimeout(int notifyConnectTimeout) { PropertyUtil.notifyConnectTimeout = notifyConnectTimeout; } public static int getNotifySocketTimeout() { return notifySocketTimeout; } public static void setNotifySocketTimeout(int notifySocketTimeout) { PropertyUtil.notifySocketTimeout = notifySocketTimeout; } public static int getMaxHealthCheckFailCount() { return maxHealthCheckFailCount; } public static void setMaxHealthCheckFailCount(int maxHealthCheckFailCount) { PropertyUtil.maxHealthCheckFailCount = maxHealthCheckFailCount; } public static boolean isHealthCheck() { return isHealthCheck; } public static void setHealthCheck(boolean isHealthCheck) { PropertyUtil.isHealthCheck = isHealthCheck; } public static int getMaxContent() { return maxContent; } public static void setMaxContent(int maxContent) { PropertyUtil.maxContent = maxContent; } public static boolean isManageCapacity() { return isManageCapacity; } public static void setManageCapacity(boolean isManageCapacity) { PropertyUtil.isManageCapacity = isManageCapacity; } public static int getDefaultClusterQuota() { return defaultClusterQuota; } public static void setDefaultClusterQuota(int defaultClusterQuota) { PropertyUtil.defaultClusterQuota = defaultClusterQuota; } public static boolean isCapacityLimitCheck() { return isCapacityLimitCheck; } public static void setCapacityLimitCheck(boolean isCapacityLimitCheck) { PropertyUtil.isCapacityLimitCheck = isCapacityLimitCheck; } public static int getDefaultGroupQuota() { return defaultGroupQuota; } public static void setDefaultGroupQuota(int defaultGroupQuota) { PropertyUtil.defaultGroupQuota = defaultGroupQuota; } public static int getDefaultTenantQuota() { return defaultTenantQuota; } public static void setDefaultTenantQuota(int defaultTenantQuota) { PropertyUtil.defaultTenantQuota = defaultTenantQuota; } public static int getInitialExpansionPercent() { return initialExpansionPercent; } public static void setInitialExpansionPercent(int initialExpansionPercent) { PropertyUtil.initialExpansionPercent = initialExpansionPercent; } public static int getDefaultMaxSize() { return defaultMaxSize; } public static void setDefaultMaxSize(int defaultMaxSize) { PropertyUtil.defaultMaxSize = defaultMaxSize; } public static int getDefaultMaxAggrCount() { return defaultMaxAggrCount; } public static void setDefaultMaxAggrCount(int defaultMaxAggrCount) { PropertyUtil.defaultMaxAggrCount = defaultMaxAggrCount; } /** * control whether persist beta and tag to old model. * * @return */ public static boolean isGrayCompatibleModel() { return grayCompatibleModel; } public static void setGrayCompatibleModel(boolean grayCompatibleModel) { PropertyUtil.grayCompatibleModel = grayCompatibleModel; } public static int getDefaultMaxAggrSize() { return defaultMaxAggrSize; } public static void setDefaultMaxAggrSize(int defaultMaxAggrSize) { PropertyUtil.defaultMaxAggrSize = defaultMaxAggrSize; } public static int getCorrectUsageDelay() { return correctUsageDelay; } public static void setCorrectUsageDelay(int correctUsageDelay) { PropertyUtil.correctUsageDelay = correctUsageDelay; } public static int getConfigRententionDays() { return configRententionDays; } private void setConfigRententionDays() { String val = getProperty(PropertiesConstant.CONFIG_RENTENTION_DAYS); if (null != val) { int tmp = 0; try { tmp = Integer.parseInt(val); if (tmp > 0) { PropertyUtil.configRententionDays = tmp; } } catch (NumberFormatException nfe) { FATAL_LOG.error("read nacos.config.retention.days wrong", nfe); } } } public static boolean isStandaloneMode() { return EnvUtil.getStandaloneMode(); } private void loadSetting() { try { setNotifyConnectTimeout(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.NOTIFY_CONNECT_TIMEOUT, String.valueOf(notifyConnectTimeout)))); LOGGER.info("notifyConnectTimeout:{}", notifyConnectTimeout); setNotifySocketTimeout(Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.NOTIFY_SOCKET_TIMEOUT, String.valueOf(notifySocketTimeout)))); LOGGER.info("notifySocketTimeout:{}", notifySocketTimeout); setHealthCheck(Boolean.parseBoolean( EnvUtil.getProperty(PropertiesConstant.IS_HEALTH_CHECK, String.valueOf(isHealthCheck)))); LOGGER.info("isHealthCheck:{}", isHealthCheck); setMaxHealthCheckFailCount(Integer.parseInt( EnvUtil.getProperty(PropertiesConstant.MAX_HEALTH_CHECK_FAIL_COUNT, String.valueOf(maxHealthCheckFailCount)))); LOGGER.info("maxHealthCheckFailCount:{}", maxHealthCheckFailCount); setMaxContent( Integer.parseInt(EnvUtil.getProperty(PropertiesConstant.MAX_CONTENT, String.valueOf(maxContent)))); LOGGER.info("maxContent:{}", maxContent); // capacity management setManageCapacity(getBoolean(PropertiesConstant.IS_MANAGE_CAPACITY, isManageCapacity)); setCapacityLimitCheck(getBoolean(PropertiesConstant.IS_CAPACITY_LIMIT_CHECK, isCapacityLimitCheck)); setDefaultClusterQuota(getInt(PropertiesConstant.DEFAULT_CLUSTER_QUOTA, defaultClusterQuota)); setDefaultGroupQuota(getInt(PropertiesConstant.DEFAULT_GROUP_QUOTA, defaultGroupQuota)); setDefaultTenantQuota(getInt(PropertiesConstant.DEFAULT_TENANT_QUOTA, defaultTenantQuota)); setDefaultMaxSize(getInt(PropertiesConstant.DEFAULT_MAX_SIZE, defaultMaxSize)); setDefaultMaxAggrCount(getInt(PropertiesConstant.DEFAULT_MAX_AGGR_COUNT, defaultMaxAggrCount)); setDefaultMaxAggrSize(getInt(PropertiesConstant.DEFAULT_MAX_AGGR_SIZE, defaultMaxAggrSize)); setCorrectUsageDelay(getInt(PropertiesConstant.CORRECT_USAGE_DELAY, correctUsageDelay)); setInitialExpansionPercent(getInt(PropertiesConstant.INITIAL_EXPANSION_PERCENT, initialExpansionPercent)); setConfigRententionDays(); setDumpChangeOn(getBoolean(PropertiesConstant.DUMP_CHANGE_ON, dumpChangeOn)); setDumpChangeWorkerInterval( getLong(PropertiesConstant.DUMP_CHANGE_WORKER_INTERVAL, dumpChangeWorkerInterval)); setGrayCompatibleModel(getBoolean(PropertiesConstant.GRAY_CAPATIBEL_MODEL, grayCompatibleModel)); } catch (Exception e) { LOGGER.error("read application.properties failed", e); throw e; } } private boolean getBoolean(String key, boolean defaultValue) { return Boolean.parseBoolean(getString(key, String.valueOf(defaultValue))); } private int getInt(String key, int defaultValue) { return Integer.parseInt(getString(key, String.valueOf(defaultValue))); } private long getLong(String key, long defaultValue) { return Long.parseLong(getString(key, String.valueOf(defaultValue))); } private String getString(String key, String defaultValue) { String value = getProperty(key); if (value == null) { return defaultValue; } LOGGER.info("{}:{}", key, value); return value; } public String getProperty(String key) { return EnvUtil.getProperty(key); } public String getProperty(String key, String defaultValue) { return EnvUtil.getProperty(key, defaultValue); } @Override public void initialize(ConfigurableApplicationContext configurableApplicationContext) { loadSetting(); } private static final int MAX_DUMP_PAGE = 1000; private static final int MIN_DUMP_PAGE = 50; private static final int PAGE_MEMORY_DIVIDE_MB = 512; private static AtomicInteger allDumpPageSize; public static int getAllDumpPageSize() { if (allDumpPageSize == null) { allDumpPageSize = new AtomicInteger(initAllDumpPageSize()); } return allDumpPageSize.get(); } static int initAllDumpPageSize() { long memLimitMb = getMemLimitMb(); //512MB->50 Page Size int pageSize = (int) ((float) memLimitMb / PAGE_MEMORY_DIVIDE_MB) * MIN_DUMP_PAGE; pageSize = Math.max(pageSize, MIN_DUMP_PAGE); pageSize = Math.min(pageSize, MAX_DUMP_PAGE); LOGGER.info("All dump page size is set to {} according to mem limit {} MB", pageSize, memLimitMb); return pageSize; } public static long getMemLimitMb() { Optional memoryLimit = findMemoryLimitFromFile(); if (memoryLimit.isPresent()) { return memoryLimit.get(); } memoryLimit = findMemoryLimitFromSystem(); return memoryLimit.get(); } private static String limitMemoryFile; private static Optional findMemoryLimitFromFile() { if (limitMemoryFile == null) { limitMemoryFile = EnvUtil.getProperty("memory_limit_file_path", "/sys/fs/cgroup/memory/memory.limit_in_bytes"); } File file = new File(limitMemoryFile); try (BufferedReader reader = Files.newBufferedReader(file.toPath(), StandardCharsets.UTF_8)) { long memoryLimit = Long.parseLong(reader.readLine().trim()); return Optional.of(memoryLimit / 1024L / 1024L); } catch (IOException | NumberFormatException ignored) { return Optional.empty(); } } private static Optional findMemoryLimitFromSystem() { long maxHeapSizeMb = Runtime.getRuntime().maxMemory() / 1024L / 1024L; return Optional.of(maxHeapSizeMb); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/Protocol.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; /** * Used to handle protocol-related operations. * * @author zhidao * @version 1.0 2011/05/03 */ public class Protocol { /** * fix the version number like 2.0.4(fix the version template like major.minor.bug-fix) * * @param version version * @return version. */ public static int getVersionNumber(String version) { if (version == null) { return -1; } String[] vs = version.split("\\."); int sum = 0; for (int i = 0; i < vs.length; i++) { try { sum = sum * 10 + Integer.parseInt(vs[i]); } catch (Exception e) { // ignore } } return sum; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/RegexParser.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; /** * Generic classes for wildcard characters, decisions, and standard canonical transformations that can be supported by * ConfigCenter. * * @author tianhu E-mail: */ public class RegexParser { private static final char QUESTION_MARK = '?'; /** * Replace input string non-regular special characters with standard regular expression strings; Replace '*' with * '.* '? 'is replaced by '{n}', n is the number of consecutive ?; Other special characters that are not alphabetic * or numeric are preceded by '\'. * * @param regex The expression to be formatted * @return format content. */ public static String regexFormat(String regex) { if (regex == null) { throw new NullPointerException("regex string can't be null"); } StringBuilder result = new StringBuilder(); result.append('^'); for (int i = 0; i < regex.length(); i++) { char ch = regex.charAt(i); if (isAsciiAlphanumeric(ch)) { result.append(ch); } else if (ch == '*') { result.append(".*"); } else if (ch == QUESTION_MARK) { int j = 0; for (; j < regex.length() - i && ch == QUESTION_MARK; j++) { ch = regex.charAt(i + j); } if (j == regex.length() - i) { result.append(".{" + j + "}"); break; } else { j -= 1; result.append(".{" + (j) + "}"); i += j - 1; } } else { result.append("\\" + ch); } } result.append('$'); return result.toString(); } public static boolean containsWildcard(String regex) { return (regex.contains("?") || regex.contains("*")); } private static Boolean isAsciiAlphanumeric(final char ch) { return isAsciiAlphaUpper(ch) || isAsciiAlphaLower(ch) || isAsciiNumeric(ch); } private static Boolean isAsciiNumeric(final char ch) { return ch >= '0' && ch <= '9'; } private static Boolean isAsciiAlphaUpper(final char ch) { return ch >= 'A' && ch <= 'Z'; } private static Boolean isAsciiAlphaLower(final char ch) { return ch >= 'a' && ch <= 'z'; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/RequestUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.context.RequestContextHolder; import com.alibaba.nacos.core.utils.WebUtils; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import jakarta.servlet.http.HttpServletRequest; /** * Request util. * * @author Nacos */ public class RequestUtil { public static final String CLIENT_APPNAME_HEADER = "Client-AppName"; /** * Get real client ip from context first, if no value, use * {@link com.alibaba.nacos.core.utils.WebUtils#getRemoteIp(HttpServletRequest)}. * * @param request {@link HttpServletRequest} * @return remote ip address. */ public static String getRemoteIp(HttpServletRequest request) { String remoteIp = RequestContextHolder.getContext().getBasicContext().getAddressContext().getSourceIp(); if (StringUtils.isBlank(remoteIp)) { remoteIp = RequestContextHolder.getContext().getBasicContext().getAddressContext().getRemoteIp(); } if (StringUtils.isBlank(remoteIp)) { remoteIp = WebUtils.getRemoteIp(request); } return remoteIp; } /** * Gets the name of the client application in the header. * * @param request {@link HttpServletRequest} * @return may be return null */ public static String getAppName(HttpServletRequest request) { String result = RequestContextHolder.getContext().getBasicContext().getApp(); return isUnknownApp(result) ? request.getHeader(CLIENT_APPNAME_HEADER) : result; } private static boolean isUnknownApp(String appName) { return StringUtils.isBlank(appName) || StringUtils.equalsIgnoreCase("unknown", appName); } /** * Gets the username of the client application in the Attribute. * * @param request {@link HttpServletRequest} * @return may be return null */ public static String getSrcUserName(HttpServletRequest request) { IdentityContext identityContext = RequestContextHolder.getContext().getAuthContext().getIdentityContext(); String result = StringUtils.EMPTY; if (null != identityContext) { result = (String) identityContext.getParameter( com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID); } // If auth is disabled, get username from parameters by agreed key return StringUtils.isBlank(result) ? request.getParameter(Constants.USERNAME) : result; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/ResponseUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import jakarta.servlet.http.HttpServletResponse; import org.springframework.beans.BeanUtils; import java.io.IOException; import static com.alibaba.nacos.config.server.utils.LogUtil.DEFAULT_LOG; /** * Response Utils. * * @author Nacos */ public class ResponseUtil { /** * Write error msg. */ public static void writeErrMsg(HttpServletResponse response, int httpCode, String msg) { response.setStatus(httpCode); try { response.getWriter().println(msg); } catch (IOException e) { DEFAULT_LOG.error("ResponseUtil:writeErrMsg wrong", e); } } /** * Transfer from {@link ConfigAllInfo} to {@link ConfigDetailInfo} for APIs. * * @param configAllInfo {@link ConfigAllInfo} config all information from storage. * @return {@link ConfigDetailInfo} for APIs response. */ public static ConfigDetailInfo transferToConfigDetailInfo(ConfigAllInfo configAllInfo) { ConfigDetailInfo result = new ConfigDetailInfo(); BeanUtils.copyProperties(configAllInfo, result); result.setNamespaceId(configAllInfo.getTenant()); result.setGroupName(configAllInfo.getGroup()); return result; } /** * Transfer from {@link ConfigInfo} to {@link ConfigBasicInfo} for APIs. * * @param configInfo {@link ConfigInfo} config basic information from storage. * @return {@link ConfigBasicInfo} for APIs response. */ public static ConfigBasicInfo transferToConfigBasicInfo(ConfigInfo configInfo) { ConfigBasicInfo result = new ConfigBasicInfo(); BeanUtils.copyProperties(configInfo, result); result.setNamespaceId(configInfo.getTenant()); result.setGroupName(configInfo.getGroup()); return result; } /** * Transfer from {@link ConfigInfoWrapper} to {@link ConfigBasicInfo} for APIs. * * @param configInfo {@link ConfigInfoWrapper} config basic information from storage. * @return {@link ConfigBasicInfo} for APIs response. */ public static ConfigBasicInfo transferToConfigBasicInfo(ConfigInfoWrapper configInfo) { ConfigBasicInfo result = transferToConfigBasicInfo((ConfigInfo) configInfo); result.setModifyTime(configInfo.getLastModified()); return result; } /** * Transfer from {@link ConfigInfoGrayWrapper} to {@link ConfigGrayInfo} for APIs. * * @param configInfoGray {@link ConfigInfoGrayWrapper} config gray information from storage. * @return {@link ConfigGrayInfo} for APIs response. */ public static ConfigGrayInfo transferToConfigGrayInfo(ConfigInfoGrayWrapper configInfoGray) { ConfigGrayInfo result = new ConfigGrayInfo(); BeanUtils.copyProperties(configInfoGray, result); result.setNamespaceId(configInfoGray.getTenant()); result.setGroupName(configInfoGray.getGroup()); result.setCreateUser(configInfoGray.getSrcUser()); result.setModifyTime(configInfoGray.getLastModified()); return result; } /** * Transfer from {@link ConfigHistoryInfo} to {@link ConfigHistoryBasicInfo} for APIs. * * @param historyInfo {@link ConfigHistoryInfo} config history information from storage. * @return {@link ConfigHistoryBasicInfo} for APIs response. */ public static ConfigHistoryBasicInfo transferToConfigHistoryBasicInfo(ConfigHistoryInfo historyInfo) { ConfigHistoryBasicInfo result = new ConfigHistoryBasicInfo(); BeanUtils.copyProperties(historyInfo, result); injectHistoryBasicInfo(result, historyInfo); return result; } /** * Transfer from {@link ConfigHistoryInfo} to {@link ConfigHistoryDetailInfo} for APIs. * * @param historyInfo {@link ConfigHistoryInfo} config history information from storage. * @return {@link ConfigHistoryDetailInfo} for APIs response. */ public static ConfigHistoryDetailInfo transferToConfigHistoryDetailInfo(ConfigHistoryInfo historyInfo) { ConfigHistoryDetailInfo result = new ConfigHistoryDetailInfo(); BeanUtils.copyProperties(historyInfo, result); injectHistoryBasicInfo(result, historyInfo); return result; } private static void injectHistoryBasicInfo(ConfigHistoryBasicInfo historyBasicInfo, ConfigHistoryInfo historyInfo) { historyBasicInfo.setNamespaceId(historyInfo.getTenant()); historyBasicInfo.setGroupName(historyInfo.getGroup()); historyBasicInfo.setCreateTime(historyInfo.getCreatedTime().getTime()); historyBasicInfo.setModifyTime(historyInfo.getLastModifiedTime().getTime()); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleCache.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * A simple Cache with TTL, not cleared for expired entry. * * @param the cache type * @author fengHan, jiuRen */ public class SimpleCache { final ConcurrentMap> cache = new ConcurrentHashMap<>(); private static class CacheEntry { final long expireTime; final E value; public CacheEntry(E value, long expire) { this.expireTime = expire; this.value = value; } } /** * Put data. */ public void put(String key, E e, long ttlMs) { if (key == null || e == null) { return; } CacheEntry entry = new CacheEntry<>(e, System.currentTimeMillis() + ttlMs); cache.put(key, entry); } /** * Get data. */ public E get(String key) { CacheEntry entry = cache.get(key); if (entry != null && entry.expireTime > System.currentTimeMillis()) { return entry.value; } return null; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleFlowData.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.config.server.Config; import com.alibaba.nacos.core.utils.ClassUtils; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * Simple Flow data. * * @author Nacos */ public class SimpleFlowData { private int index = 0; private AtomicInteger[] data; private int average; private int slotCount; private ScheduledExecutorService timer = ExecutorFactory.Managed .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(Config.class), new NameThreadFactory("com.alibaba.nacos.config.flow.control")); public SimpleFlowData(int slotCount, int interval) { this.slotCount = slotCount; data = new AtomicInteger[slotCount]; for (int i = 0; i < data.length; i++) { data[i] = new AtomicInteger(0); } timer.scheduleAtFixedRate(this::rotateSlot, interval, interval, TimeUnit.MILLISECONDS); } public int addAndGet(int count) { return data[index].addAndGet(count); } public int incrementAndGet() { return data[index].incrementAndGet(); } /** * Rotate the slot. */ public void rotateSlot() { int total = 0; for (int i = 0; i < slotCount; i++) { total += data[i].get(); } average = total / slotCount; index = (index + 1) % slotCount; data[index].set(0); } public int getCurrentCount() { return data[index].get(); } public int getAverageCount() { return this.average; } public int getSlotCount() { return this.slotCount; } public String getSlotInfo() { StringBuilder sb = new StringBuilder(); int index = this.index + 1; for (int i = 0; i < slotCount; i++) { if (i > 0) { sb.append(' '); } sb.append(this.data[(i + index) % slotCount].get()); } return sb.toString(); } public int getCount(int prevStep) { prevStep = prevStep % this.slotCount; int index = (this.index + this.slotCount - prevStep) % this.slotCount; return this.data[index].intValue(); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleIpFlowData.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.executor.ExecutorFactory; import com.alibaba.nacos.common.executor.NameThreadFactory; import com.alibaba.nacos.config.server.Config; import com.alibaba.nacos.core.utils.ClassUtils; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; /** * According to IP flow control, control the number of individual IP and IP total. * * @author leiwen.zh */ public class SimpleIpFlowData { private AtomicInteger[] data; private int slotCount; private int averageCount; private ScheduledExecutorService timer = ExecutorFactory.Managed .newSingleScheduledExecutorService(ClassUtils.getCanonicalName(Config.class), new NameThreadFactory("com.alibaba.nacos.config.flow.control.ip")); public SimpleIpFlowData(int slotCount, int interval) { if (slotCount <= 0) { this.slotCount = 1; } else { this.slotCount = slotCount; } data = new AtomicInteger[slotCount]; for (int i = 0; i < data.length; i++) { data[i] = new AtomicInteger(0); } timer.scheduleAtFixedRate(this::rotateSlot, interval, interval, TimeUnit.MILLISECONDS); } /** * Atomically increments by one the current value. */ public int incrementAndGet(String ip) { int index = 0; if (ip != null) { index = ip.hashCode() % slotCount; } if (index < 0) { index = -index; } return data[index].incrementAndGet(); } /** * Rotate the slot. */ public void rotateSlot() { int totalCount = 0; for (int i = 0; i < slotCount; i++) { totalCount += data[i].get(); data[i].set(0); } this.averageCount = totalCount / this.slotCount; } public int getCurrentCount(String ip) { int index = 0; if (ip != null) { index = ip.hashCode() % slotCount; } if (index < 0) { index = -index; } return data[index].get(); } public int getAverageCount() { return this.averageCount; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLock.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; /** * Simplest read-write lock implementation. Requires locking and unlocking must be called in pairs. * * @author Nacos */ public class SimpleReadWriteLock { /** * Zero means no lock; Negative Numbers mean write locks; Positive Numbers mean read locks, and the numeric value * represents the number of read locks. */ private int status = 0; /** * Try read lock. */ public synchronized boolean tryReadLock() { if (isWriteLocked()) { return false; } else { status++; return true; } } /** * Release the read lock. */ public synchronized void releaseReadLock() { // when status equals 0, it should not decrement to negative numbers if (status == 0) { return; } status--; } /** * Try write lock. */ public synchronized boolean tryWriteLock() { if (!isFree()) { return false; } else { status = -1; return true; } } public synchronized void releaseWriteLock() { status = 0; } private boolean isWriteLocked() { return status < 0; } private boolean isFree() { return status == 0; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/StatConstants.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; /** * Stat constant. * * @author Nacos */ public class StatConstants { private StatConstants() { } public static final String APP_NAME = "nacos"; public static final String STAT_AVERAGE_HTTP_GET_OK = "AverageHttpGet_OK"; public static final String STAT_AVERAGE_HTTP_GET_NOT_MODIFIED = "AverageHttpGet_Not_Modified"; public static final String STAT_AVERAGE_HTTP_GET_OTHER = "AverageHttpGet_Other_Status"; public static final String STAT_AVERAGE_HTTP_POST_CHECK = "AverageHttpPost_Check"; } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/SystemConfig.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.utils.InternetAddressUtil; import com.alibaba.nacos.common.utils.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.net.NetworkInterface; import java.util.Enumeration; /** * System config. * * @author Nacos */ public class SystemConfig { public static final String LOCAL_IP = getHostAddress(); private static final Logger LOGGER = LoggerFactory.getLogger(SystemConfig.class); private static String getHostAddress() { String address = System.getProperty("nacos.server.ip"); if (StringUtils.isNotEmpty(address)) { return address; } else { address = InternetAddressUtil.localHostIp(); } try { Enumeration en = NetworkInterface.getNetworkInterfaces(); while (en.hasMoreElements()) { NetworkInterface ni = en.nextElement(); Enumeration ads = ni.getInetAddresses(); while (ads.hasMoreElements()) { InetAddress ip = ads.nextElement(); // Compatible group does not regulate 11 network segments if (!ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1 /* && ip.isSiteLocalAddress() */) { return ip.getHostAddress(); } } } } catch (Exception e) { LOGGER.error("get local host address error", e); } return address; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/TimeUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.utils.DateFormatUtils; import java.sql.Timestamp; import java.util.Calendar; /** * Time util. * * @author Nacos */ public class TimeUtils { private static final String YYYYMMMDDHHMMSS = "yyyy-MM-dd HH:mm:ss"; public static Timestamp getCurrentTime() { return new Timestamp(System.currentTimeMillis()); } public static String getCurrentTimeStr() { Calendar c = Calendar.getInstance(); c.setTimeInMillis(System.currentTimeMillis()); return DateFormatUtils.format(c.getTime(), YYYYMMMDDHHMMSS); } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/TimeoutUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import java.util.concurrent.atomic.AtomicLong; /** * A utility class that handles timeouts and is used by the client to retrieve the total timeout of the data. After * obtaining the data from the network,totalTime is accumulated. Before obtaining the data from the network, check * whether the totalTime is greater than totalTimeout. If yes, it indicates the totalTimeout * * @author leiwen.zh */ public class TimeoutUtils { /** * Total time to get the data of consumption, the unit of ms. */ private final AtomicLong totalTime = new AtomicLong(0L); private volatile long lastResetTime; private volatile boolean initialized = false; /** * Total timeout to get data, the unit of ms. */ private long totalTimeout; /** * The cumulative expiration time of the time consumed by fetching the data, the unit of ms. */ private long invalidThreshold; public TimeoutUtils(long totalTimeout, long invalidThreshold) { this.totalTimeout = totalTimeout; this.invalidThreshold = invalidThreshold; } /** * Init last reset time. */ public synchronized void initLastResetTime() { if (initialized) { return; } lastResetTime = System.currentTimeMillis(); initialized = true; } /** * Cumulative total time. */ public void addTotalTime(long time) { totalTime.addAndGet(time); } /** * Is timeout. */ public boolean isTimeout() { return totalTime.get() > this.totalTimeout; } /** * Clean the total time. */ public void resetTotalTime() { if (isTotalTimeExpired()) { totalTime.set(0L); lastResetTime = System.currentTimeMillis(); } } public AtomicLong getTotalTime() { return totalTime; } private boolean isTotalTimeExpired() { return System.currentTimeMillis() - lastResetTime > this.invalidThreshold; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/TraceLogUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Trace util. * * @author Nacos */ public class TraceLogUtil { /** * Record requests for each interface of the Server. */ public static Logger requestLog = LoggerFactory.getLogger("com.alibaba.nacos.config.request"); /** * Record polling request records for each client. */ public static Logger pollingLog = LoggerFactory.getLogger("com.alibaba.nacos.config.polling"); } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/UrlAnalysisUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.constant.Constants; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Url util. * * @author leiwen.zh */ public class UrlAnalysisUtils { private static final Pattern URL_PATTERN = Pattern.compile("^(\\w+://)?([\\w\\.]+:)(\\d*)?(\\??.*)"); public static String getContentIdentity(String content) { if (!verifyIncrementPubContent(content)) { return null; } Matcher matcher = URL_PATTERN.matcher(content); StringBuilder buf = new StringBuilder(); if (matcher.find()) { String scheme = matcher.group(1); String address = matcher.group(2); String port = matcher.group(3); if (scheme != null) { buf.append(scheme); } buf.append(address); if (port != null) { buf.append(port); } } return buf.toString(); } private static boolean verifyIncrementPubContent(String content) { if (content == null || content.length() == 0) { return false; } for (int i = 0; i < content.length(); i++) { char c = content.charAt(i); if (c == '\r' || c == '\n') { return false; } if (c == Constants.WORD_SEPARATOR.charAt(0)) { return false; } } return true; } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/YamlParserUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.ConfigMetadata; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.constructor.AbstractConstruct; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.introspector.Property; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.NodeTuple; import org.yaml.snakeyaml.nodes.ScalarNode; import org.yaml.snakeyaml.nodes.SequenceNode; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Representer; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; /** * YamlParserUtil. * * @author Nacos */ public class YamlParserUtil { /** * Serialize a Java object into a YAML string. * * @param object Java object. * @return YAML string. */ public static String dumpObject(Object object) { return new Yaml(new YamlParserConstructor(), new CustomRepresenter()).dumpAsMap(object); } /** * Parse YAML String and produce the corresponding Java object (Standard Java classes and in YamlParserConstructor * specified Construct). * * @param content YAML String * @param type Java object. * @param Java object type. * @return Java object. */ public static T loadObject(String content, Class type) { return new Yaml(new YamlParserConstructor(), new CustomRepresenter()).loadAs(content, type); } public static class YamlParserConstructor extends SafeConstructor { public static final Tag CONFIG_METADATA_TAG = new Tag(ConfigMetadata.class); public YamlParserConstructor() { super(new LoaderOptions()); yamlConstructors.put(CONFIG_METADATA_TAG, new ConstructYamlConfigMetadata()); } } public static class CustomRepresenter extends Representer { public CustomRepresenter() { super(new DumperOptions()); } @Override protected NodeTuple representJavaBeanProperty(Object javaBean, Property property, Object propertyValue, Tag customTag) { if (propertyValue == null) { return null; } else { return super.representJavaBeanProperty(javaBean, property, propertyValue, customTag); } } } public static class ConstructYamlConfigMetadata extends AbstractConstruct { @Override public Object construct(Node node) { if (!YamlParserConstructor.CONFIG_METADATA_TAG.getValue().equals(node.getTag().getValue())) { throw new NacosRuntimeException(NacosException.INVALID_PARAM, "could not determine a constructor for the tag " + node.getTag() + node.getStartMark()); } MappingNode mNode = (MappingNode) node; List value = mNode.getValue(); if (CollectionUtils.isEmpty(value)) { return null; } NodeTuple nodeTuple = value.get(0); ConfigMetadata configMetadata = new ConfigMetadata(); SequenceNode sequenceNode = (SequenceNode) nodeTuple.getValueNode(); if (CollectionUtils.isEmpty(sequenceNode.getValue())) { return configMetadata; } List exportItems = sequenceNode.getValue().stream().map(itemValue -> { ConfigMetadata.ConfigExportItem configExportItem = new ConfigMetadata.ConfigExportItem(); MappingNode itemMap = (MappingNode) itemValue; List propertyValues = itemMap.getValue(); Map metadataMap = new HashMap<>(propertyValues.size()); propertyValues.forEach(metadata -> { ScalarNode keyNode = (ScalarNode) metadata.getKeyNode(); ScalarNode valueNode = (ScalarNode) metadata.getValueNode(); metadataMap.put(keyNode.getValue(), valueNode.getValue()); }); configExportItem.setDataId(metadataMap.get("dataId")); configExportItem.setGroup(metadataMap.get("group")); configExportItem.setType(metadataMap.get("type")); configExportItem.setDesc(metadataMap.get("desc")); configExportItem.setAppName(metadataMap.get("appName")); configExportItem.setConfigTags(metadataMap.get("configTags")); return configExportItem; }).collect(Collectors.toList()); configMetadata.setMetadata(exportItems); return configMetadata; } } } ================================================ FILE: config/src/main/java/com/alibaba/nacos/config/server/utils/ZipUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.constant.Constants; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; /** * ZipUtils for import and export. * * @author klw * @date 2019/5/14 16:59 */ public class ZipUtils { private static final Logger LOGGER = LoggerFactory.getLogger(ZipUtils.class); public static class ZipItem { private String itemName; private String itemData; public ZipItem(String itemName, String itemData) { this.itemName = itemName; this.itemData = itemData; } public String getItemName() { return itemName; } public void setItemName(String itemName) { this.itemName = itemName; } public String getItemData() { return itemData; } public void setItemData(String itemData) { this.itemData = itemData; } } public static class UnZipResult { private List zipItemList; private ZipItem metaDataItem; public UnZipResult(List zipItemList, ZipItem metaDataItem) { this.zipItemList = zipItemList; this.metaDataItem = metaDataItem; } public List getZipItemList() { return zipItemList; } public void setZipItemList(List zipItemList) { this.zipItemList = zipItemList; } public ZipItem getMetaDataItem() { return metaDataItem; } public void setMetaDataItem(ZipItem metaDataItem) { this.metaDataItem = metaDataItem; } } /** * zip method. */ public static byte[] zip(List source) { byte[] result = null; try (ByteArrayOutputStream byteOut = new ByteArrayOutputStream(); ZipOutputStream zipOut = new ZipOutputStream( byteOut)) { for (ZipItem item : source) { zipOut.putNextEntry(new ZipEntry(item.getItemName())); zipOut.write(item.getItemData().getBytes(StandardCharsets.UTF_8)); } zipOut.flush(); zipOut.finish(); result = byteOut.toByteArray(); } catch (IOException e) { LOGGER.error("an error occurred while compressing data.", e); } return result; } /** * unzip method. */ public static UnZipResult unzip(byte[] source) { List itemList = new ArrayList<>(); ZipItem metaDataItem = null; try (ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(source))) { ZipEntry entry; while ((entry = zipIn.getNextEntry()) != null) { if (entry.isDirectory()) { continue; } try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { byte[] buffer = new byte[1024]; int offset; while ((offset = zipIn.read(buffer)) != -1) { out.write(buffer, 0, offset); } String entryName = entry.getName(); if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA.equals(entryName)) { metaDataItem = new ZipItem(entryName, out.toString("UTF-8")); continue; } if (metaDataItem == null && Constants.CONFIG_EXPORT_METADATA_NEW.equals(entryName)) { metaDataItem = new ZipItem(entryName, out.toString("UTF-8")); continue; } itemList.add(new ZipItem(entryName, out.toString("UTF-8"))); } catch (IOException e) { LOGGER.error("unzip error", e); } } } catch (IOException e) { LOGGER.error("unzip error", e); } return new UnZipResult(itemList, metaDataItem); } } ================================================ FILE: config/src/main/resources/META-INF/logback/config-included.xml ================================================ ${LOG_HOME}/config-dump.log true ${LOG_HOME}/config-dump.log.%d{yyyy-MM-dd}.%i 2GB 15 7GB true %date %level %msg%n%n UTF-8 ${LOG_HOME}/config-pull.log true ${LOG_HOME}/config-pull.log.%d{yyyy-MM-dd}.%i 20MB 15 128MB true %date %level %msg%n%n UTF-8 ${LOG_HOME}/config-fatal.log true ${LOG_HOME}/config-fatal.log.%d{yyyy-MM-dd}.%i 20MB 15 128MB true %date %level %msg%n%n UTF-8 ${LOG_HOME}/config-memory.log true ${LOG_HOME}/config-memory.log.%d{yyyy-MM-dd}.%i 20MB 15 128MB true %date %level %msg%n%n UTF-8 ${LOG_HOME}/config-pull-check.log true ${LOG_HOME}/config-pull-check.log.%d{yyyy-MM-dd}.%i 1GB 15 3GB true %msg%n UTF-8 ${LOG_HOME}/config-acl.log true ${LOG_HOME}/config-acl.log.%d{yyyy-MM-dd}.%i 50MB 15 512MB true %date %level %msg%n%n UTF-8 ${LOG_HOME}/config-client-request.log true ${LOG_HOME}/config-client-request.log.%d{yyyy-MM-dd}.%i 2GB 15 7GB true %date|%msg%n UTF-8 ${LOG_HOME}/config-sdk-request.log true ${LOG_HOME}/config-sdk-request.log.%d{yyyy-MM-dd}.%i 1GB 15 3GB true %date|%msg%n UTF-8 ${LOG_HOME}/config-trace.log true ${LOG_HOME}/config-trace.log.%d{yyyy-MM-dd}.%i 2GB 15 7GB true %date|%msg%n UTF-8 ${LOG_HOME}/config-notify.log true ${LOG_HOME}/config-notify.log.%d{yyyy-MM-dd}.%i 1GB 15 3GB true %date %level %msg%n%n UTF-8 ${LOG_HOME}/config-app.log true ${LOG_HOME}/config-app.log.%d{yyyy-MM-dd}.%i 20MB 15 128MB true %date %level %msg%n%n UTF-8 ${LOG_HOME}/config-server.log true ${LOG_HOME}/config-server.log.%d{yyyy-MM-dd}.%i 50MB 15 512MB true %date %level %msg%n%n UTF-8 ================================================ FILE: config/src/main/resources/META-INF/services/com.alibaba.nacos.config.server.model.gray.GrayRule ================================================ # # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.config.server.model.gray.BetaGrayRule com.alibaba.nacos.config.server.model.gray.TagGrayRule ================================================ FILE: config/src/main/resources/META-INF/services/com.alibaba.nacos.config.server.service.query.ConfigQueryChainRequestExtractor ================================================ # # Copyright 1999-$toady.year Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.config.server.service.query.DefaultChainRequestExtractor ================================================ FILE: config/src/main/resources/META-INF/services/com.alibaba.nacos.config.server.service.query.ConfigQueryHandlerChainBuilder ================================================ # # Copyright 1999-$toady.year Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.config.server.service.query.DefaultConfigQueryHandlerChainBuilder ================================================ FILE: config/src/main/resources/META-INF/services/com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor com.alibaba.nacos.config.server.paramcheck.ConfigBlurSearchHttpParamExtractor com.alibaba.nacos.config.server.paramcheck.ConfigListenerHttpParamExtractor ================================================ FILE: config/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector ================================================ # # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.config.server.service.LongPollingConnectionMetricsCollector ================================================ FILE: config/src/main/resources/META-INF/services/com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.config.server.filter.ConfigEnabledFilter ================================================ FILE: config/src/main/resources/META-INF/services/com.alibaba.nacos.sys.module.ModuleStateBuilder ================================================ # # # Copyright 1999-2021 Alibaba Group Holding Ltd. # # 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. # # com.alibaba.nacos.config.server.constant.ConfigModuleStateBuilder ================================================ FILE: config/src/main/resources/META-INF/spring.factories ================================================ org.springframework.context.ApplicationContextInitializer=\ com.alibaba.nacos.config.server.utils.PropertyUtil ================================================ FILE: config/src/main/resources/version/version.txt ================================================ ${project.version} ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/aspect/CapacityManagementAspectTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.config.server.constant.CounterMode; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.capacity.GroupCapacity; import com.alibaba.nacos.config.server.model.capacity.NamespaceCapacity; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.capacity.CapacityService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.sys.env.EnvUtil; import org.aspectj.lang.ProceedingJoinPoint; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) class CapacityManagementAspectTest { final Boolean mockProceedingJoinPointResult = true; final String mockDataId = "mockDataId"; final String mockGroup = "mockGroup"; final String mockTenant = "mockTenant"; @Mock private ConfigForm configForm; @Mock private ConfigRequestInfo configRequestInfo; @Mock ProceedingJoinPoint proceedingJoinPoint; @Mock ProceedingJoinPoint localMockProceedingJoinPoint; RuntimeException mockException = new RuntimeException("mock exception"); CapacityManagementAspect capacityManagementAspect; @Mock CapacityService capacityService; @Mock ConfigInfoPersistService configInfoPersistService; MockedStatic propertyUtilMockedStatic; MockedStatic envUtilMockedStatic; @BeforeEach void before() throws Throwable { // Mock static methods propertyUtilMockedStatic = Mockito.mockStatic(PropertyUtil.class); when(PropertyUtil.getCorrectUsageDelay()).thenReturn(10 * 60); when(PropertyUtil.getDefaultMaxAggrSize()).thenReturn(1024); when(PropertyUtil.getDefaultMaxSize()).thenReturn(10 * 1024); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(EnvUtil.getProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, Boolean.class, false)).thenReturn(true); // Initialize the aspect with mocked dependencies capacityManagementAspect = new CapacityManagementAspect(configInfoPersistService, capacityService); // Mock the behavior of the ProceedingJoinPoint mockException = new RuntimeException("mock exception"); when(localMockProceedingJoinPoint.proceed()).thenThrow(mockException); } @AfterEach void after() { // Close static mocks propertyUtilMockedStatic.close(); envUtilMockedStatic.close(); } @Test void testAroundPublishConfigForInsertAspect() throws Throwable { //test with insert //condition: // 1. has tenant: true // 2. capacity limit check: false when(PropertyUtil.isManageCapacity()).thenReturn(false); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); Mockito.verify(configInfoPersistService, Mockito.times(0)).findConfigInfo(any(), any(), any()); assert localMockResult.equals(mockProceedingJoinPointResult); } @Test void testAroundPublishConfigForInsertAspect1() throws Throwable { //test with insert //condition: // 1. has tenant: true // 2. capacity limit check: true // 3. over cluster quota: true when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn(mockDataId); when(configForm.getGroup()).thenReturn(mockGroup); when(configForm.getNamespaceId()).thenReturn(mockTenant); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(false); Exception exception = assertThrows(NacosException.class, () -> { capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); }); assertEquals("Configuration limit exceeded [group=mockGroup, namespaceId=mockTenant].", exception.getMessage()); Mockito.verify(proceedingJoinPoint, Mockito.times(0)).proceed(); } @Test void testAroundPublishConfigForInsertAspect2Tenant() throws Throwable { //test with insert //condition: // 1. has tenant: true // 2. capacity limit check: true // 3. over cluster quota: false // 4. tenant capacity: null when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); when(configForm.getDataId()).thenReturn(mockDataId); when(configForm.getGroup()).thenReturn(mockGroup); when(configForm.getNamespaceId()).thenReturn(mockTenant); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.getTenantCapacity(eq(mockTenant))).thenReturn(null); when(capacityService.updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant))).thenReturn(true); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(capacityService, Mockito.times(1)).initTenantCapacity(eq(mockTenant)); Mockito.verify(capacityService, Mockito.times(1)).updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant)); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); } @Test void testAroundPublishConfigForInsertAspect2Group() throws Throwable { //test with insert //condition: // 1. has tenant: false // 2. capacity limit check: true // 3. over cluster quota: false // 4. group capacity: null when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.getGroupCapacity(eq(mockGroup))).thenReturn(null); when(capacityService.updateGroupUsage(eq(CounterMode.INCREMENT), eq(mockGroup))).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn(mockDataId); when(configForm.getGroup()).thenReturn(mockGroup); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configRequestInfo.getSrcType()).thenReturn("http"); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(capacityService, Mockito.times(1)).initGroupCapacity(eq(mockGroup)); Mockito.verify(capacityService, Mockito.times(1)).updateGroupUsage(eq(CounterMode.INCREMENT), eq(mockGroup)); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); } @Test void testAroundPublishConfigForInsertAspect3Tenant() throws Throwable { //test with insert //condition: // 1. has tenant: true // 2. capacity limit check: true // 3. over cluster quota: false // 4. tenant capacity: not null // 5. over tenant max size: true/false (if tenant max size is 0, will use default max size) when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); when(configForm.getDataId()).thenReturn(mockDataId); when(configForm.getGroup()).thenReturn(mockGroup); when(configForm.getNamespaceId()).thenReturn(mockTenant); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant))).thenReturn(true); NamespaceCapacity localTenantCapacity = new NamespaceCapacity(); localTenantCapacity.setNamespaceId(mockTenant); localTenantCapacity.setMaxSize(0); localTenantCapacity.setMaxAggrCount(0); when(capacityService.getTenantCapacity(eq(mockTenant))).thenReturn(localTenantCapacity); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(capacityService, Mockito.times(0)).initTenantCapacity(eq(mockTenant)); Mockito.verify(capacityService, Mockito.times(1)).updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant)); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); // 5. over tenant max size: true localTenantCapacity.setMaxSize(1); localTenantCapacity.setMaxAggrCount(1); Exception exception = assertThrows(NacosException.class, () -> { capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); }); assertEquals("Configuration limit exceeded [group=mockGroup, namespaceId=mockTenant].", exception.getMessage()); // 5. over tenant max size: true localTenantCapacity.setMaxSize(10 * 1024); localTenantCapacity.setMaxAggrCount(1024); localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); } @Test void testAroundPublishConfigForInsertAspect3Group() throws Throwable { //test with insert //condition: // 1. has tenant: true // 2. capacity limit check: true // 3. over cluster quota: false // 4. tenant capacity: not null // 5. over tenant max size: true/false (if tenant max size is 0, will use default max size) when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); when(configForm.getDataId()).thenReturn(mockDataId); when(configForm.getGroup()).thenReturn(mockGroup); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.updateGroupUsage(eq(CounterMode.INCREMENT), eq(mockGroup))).thenReturn(true); GroupCapacity localGroupCapacity = new GroupCapacity(); localGroupCapacity.setGroupName(mockGroup); localGroupCapacity.setMaxSize(0); localGroupCapacity.setMaxAggrCount(0); when(capacityService.getGroupCapacity(eq(mockGroup))).thenReturn(localGroupCapacity); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(true, localMockResult); Mockito.verify(capacityService, Mockito.times(0)).initGroupCapacity(eq(mockGroup)); Mockito.verify(capacityService, Mockito.times(1)).updateGroupUsage(eq(CounterMode.INCREMENT), eq(mockGroup)); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); // 5. over tenant max size: true localGroupCapacity.setMaxSize(1); localGroupCapacity.setMaxAggrCount(1); Exception exception = assertThrows(NacosException.class, () -> { capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); }); assertEquals("Configuration limit exceeded [group=mockGroup, namespaceId=null].", exception.getMessage()); // 5. over tenant max size: true localGroupCapacity.setMaxSize(10 * 1024); localGroupCapacity.setMaxAggrCount(1024); localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); } @Test void testAroundPublishAspectTenant() throws Throwable { //condition: // 1. has tenant: true // 2. capacity limit check: true // 3. over cluster quota: false // 4. tenant capacity: not null // 5. over tenant quota: false when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); when(configForm.getDataId()).thenReturn("dataId"); when(configForm.getGroup()).thenReturn("group"); when(configForm.getNamespaceId()).thenReturn("mockTenant"); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(new ConfigInfoWrapper()); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant))).thenReturn(true); NamespaceCapacity localTenantCapacity = new NamespaceCapacity(); localTenantCapacity.setNamespaceId(mockTenant); localTenantCapacity.setMaxSize(10 * 1024); localTenantCapacity.setMaxAggrCount(1024); when(capacityService.getTenantCapacity(eq(mockTenant))).thenReturn(localTenantCapacity); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(capacityService, Mockito.times(0)).initTenantCapacity(eq(mockTenant)); Mockito.verify(capacityService, Mockito.times(0)).updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant)); Mockito.verify(capacityService, Mockito.times(1)).getTenantCapacity(eq(mockTenant)); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); } @Test void testAroundPublishAspectGroup() throws Throwable { //condition: // 1. has tenant: false // 2. capacity limit check: true // 3. over cluster quota: false // 4. tenant capacity: not null // 5. over group quota: false when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); when(configForm.getDataId()).thenReturn("dataId"); when(configForm.getContent()).thenReturn("content"); when(configForm.getGroup()).thenReturn(mockGroup); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configRequestInfo.getSrcType()).thenReturn("http"); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(new ConfigInfoWrapper()); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.updateGroupUsage(eq(CounterMode.INCREMENT), eq(mockGroup))).thenReturn(true); GroupCapacity localGroupCapacity = new GroupCapacity(); localGroupCapacity.setGroupName(mockGroup); localGroupCapacity.setMaxSize(10 * 1024); localGroupCapacity.setMaxAggrCount(1024); when(capacityService.getGroupCapacity(eq(mockGroup))).thenReturn(localGroupCapacity); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(capacityService, Mockito.times(0)).initGroupCapacity(eq(mockGroup)); Mockito.verify(capacityService, Mockito.times(1)).getGroupCapacity(eq(mockGroup)); Mockito.verify(capacityService, Mockito.times(0)).updateGroupUsage(eq(CounterMode.INCREMENT), eq(mockGroup)); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); } @Test void testAroundPublishConfigForInsertRollbackAspect() throws Throwable { //test with insert //condition: // 1. has tenant: true // 2. capacity limit check: true // 3. over cluster quota: false // 4. tenant capacity: not null // 5. over tenant max size: true/false (if tenant max size is 0, will use default max size) when(PropertyUtil.isManageCapacity()).thenReturn(true); when(PropertyUtil.isCapacityLimitCheck()).thenReturn(true); when(localMockProceedingJoinPoint.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn("dataId"); when(configForm.getGroup()).thenReturn("group"); when(configForm.getNamespaceId()).thenReturn("mockTenant"); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.updateClusterUsage(any())).thenReturn(true); when(capacityService.updateTenantUsage(any(), eq(mockTenant))).thenReturn(true); NamespaceCapacity localTenantCapacity = new NamespaceCapacity(); localTenantCapacity.setNamespaceId(mockTenant); localTenantCapacity.setMaxSize(10 * 1024); localTenantCapacity.setMaxAggrCount(1024); when(capacityService.getTenantCapacity(eq(mockTenant))).thenReturn(localTenantCapacity); Boolean localMockResult = null; try { localMockResult = (Boolean) capacityManagementAspect.aroundPublishConfig(localMockProceedingJoinPoint); } catch (Throwable e) { assertEquals(mockException.getMessage(), e.getMessage()); } assertNull(localMockResult); Mockito.verify(capacityService, Mockito.times(0)).initTenantCapacity(eq(mockTenant)); Mockito.verify(capacityService, Mockito.times(1)).updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant)); Mockito.verify(capacityService, Mockito.times(1)).updateTenantUsage(eq(CounterMode.DECREMENT), eq(mockTenant)); Mockito.verify(capacityService, Mockito.times(1)) .insertAndUpdateClusterUsage(eq(CounterMode.INCREMENT), anyBoolean()); Mockito.verify(capacityService, Mockito.times(1)).updateClusterUsage(eq(CounterMode.DECREMENT)); Mockito.verify(localMockProceedingJoinPoint, Mockito.times(1)).proceed(); } @Test void testAroundDeleteConfigForTenant() throws Throwable { when(PropertyUtil.isManageCapacity()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{mockDataId, mockGroup, mockTenant, null}); when(localMockProceedingJoinPoint.getArgs()).thenReturn(new Object[]{mockDataId, mockGroup, mockTenant, null}); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.insertAndUpdateTenantUsage(any(), eq(mockTenant), anyBoolean())).thenReturn(true); when(capacityService.updateClusterUsage(any())).thenReturn(true); when(capacityService.updateTenantUsage(any(), eq(mockTenant))).thenReturn(true); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundDeleteConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(new ConfigInfoWrapper()); localMockResult = (Boolean) capacityManagementAspect.aroundDeleteConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(capacityService, Mockito.times(1)) .insertAndUpdateClusterUsage(eq(CounterMode.DECREMENT), anyBoolean()); Mockito.verify(capacityService, Mockito.times(1)) .insertAndUpdateTenantUsage(eq(CounterMode.DECREMENT), eq(mockTenant), anyBoolean()); Mockito.verify(proceedingJoinPoint, Mockito.times(2)).proceed(); localMockResult = null; try { localMockResult = (Boolean) capacityManagementAspect.aroundDeleteConfig(localMockProceedingJoinPoint); } catch (Throwable e) { assertEquals(mockException.getMessage(), e.getMessage()); } assertNull(localMockResult); Mockito.verify(capacityService, Mockito.times(2)) .insertAndUpdateClusterUsage(eq(CounterMode.DECREMENT), anyBoolean()); Mockito.verify(capacityService, Mockito.times(1)).updateClusterUsage(eq(CounterMode.INCREMENT)); Mockito.verify(capacityService, Mockito.times(2)) .insertAndUpdateTenantUsage(eq(CounterMode.DECREMENT), eq(mockTenant), anyBoolean()); Mockito.verify(capacityService, Mockito.times(1)).updateTenantUsage(eq(CounterMode.INCREMENT), eq(mockTenant)); Mockito.verify(localMockProceedingJoinPoint, Mockito.times(1)).proceed(); } @Test void testAroundDeleteConfigForGroup() throws Throwable { when(PropertyUtil.isManageCapacity()).thenReturn(true); when(proceedingJoinPoint.getArgs()).thenReturn(new Object[]{mockDataId, mockGroup, mockTenant, null}); when(localMockProceedingJoinPoint.getArgs()).thenReturn(new Object[]{mockDataId, mockGroup, mockTenant, null}); when(proceedingJoinPoint.proceed()).thenReturn(mockProceedingJoinPointResult); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(null); when(capacityService.insertAndUpdateClusterUsage(any(), anyBoolean())).thenReturn(true); when(capacityService.insertAndUpdateGroupUsage(any(), eq(mockGroup), anyBoolean())).thenReturn(true); when(capacityService.updateClusterUsage(any())).thenReturn(true); when(capacityService.updateGroupUsage(any(), eq(mockGroup))).thenReturn(true); Boolean localMockResult = (Boolean) capacityManagementAspect.aroundDeleteConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(proceedingJoinPoint, Mockito.times(1)).proceed(); when(configInfoPersistService.findConfigInfo(any(), any(), any())).thenReturn(new ConfigInfoWrapper()); localMockResult = (Boolean) capacityManagementAspect.aroundDeleteConfig(proceedingJoinPoint); assertEquals(mockProceedingJoinPointResult, localMockResult); Mockito.verify(capacityService, Mockito.times(1)) .insertAndUpdateClusterUsage(eq(CounterMode.DECREMENT), anyBoolean()); Mockito.verify(capacityService, Mockito.times(1)) .insertAndUpdateTenantUsage(eq(CounterMode.DECREMENT), eq(mockTenant), anyBoolean()); Mockito.verify(proceedingJoinPoint, Mockito.times(2)).proceed(); localMockResult = null; try { localMockResult = (Boolean) capacityManagementAspect.aroundDeleteConfig(localMockProceedingJoinPoint); } catch (Throwable e) { assertEquals(mockException.getMessage(), e.getMessage()); } assertNull(localMockResult); Mockito.verify(capacityService, Mockito.times(2)) .insertAndUpdateClusterUsage(eq(CounterMode.DECREMENT), anyBoolean()); Mockito.verify(capacityService, Mockito.times(1)).updateClusterUsage(eq(CounterMode.INCREMENT)); Mockito.verify(capacityService, Mockito.times(2)) .insertAndUpdateTenantUsage(eq(CounterMode.DECREMENT), eq(mockTenant), anyBoolean()); Mockito.verify(localMockProceedingJoinPoint, Mockito.times(1)).proceed(); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/aspect/ConfigChangeAspectTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.common.event.ServerConfigChangeEvent; import com.alibaba.nacos.config.server.configuration.ConfigChangeConfigs; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.utils.RequestUtil; import com.alibaba.nacos.plugin.config.ConfigChangePluginManager; import com.alibaba.nacos.plugin.config.constants.ConfigChangeConstants; import com.alibaba.nacos.plugin.config.constants.ConfigChangeExecuteTypes; import com.alibaba.nacos.plugin.config.constants.ConfigChangePointCutTypes; import com.alibaba.nacos.plugin.config.model.ConfigChangeRequest; import com.alibaba.nacos.plugin.config.model.ConfigChangeResponse; import com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService; import com.alibaba.nacos.sys.utils.PropertiesUtil; import jakarta.servlet.http.HttpServletRequest; import org.aspectj.lang.ProceedingJoinPoint; 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.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Properties; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; 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.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) class ConfigChangeAspectTest { ConfigChangeAspect configChangeAspect; ConfigChangeConfigs configChangeConfigs; @Mock ConfigChangePluginService configChangePluginService; MockedStatic propertiesStatic; MockedStatic requestUtilMockedStatic; @Mock private ProceedingJoinPoint pjp; @Mock private ConfigForm configForm; @Mock private ConfigRequestInfo configRequestInfo; @BeforeEach void before() { //mock config change service enabled. propertiesStatic = Mockito.mockStatic(PropertiesUtil.class); requestUtilMockedStatic = Mockito.mockStatic(RequestUtil.class); Properties properties = new Properties(); properties.put("mockedConfigChangeService.enabled", "true"); propertiesStatic.when(() -> PropertiesUtil.getPropertiesWithPrefix(any(), eq(ConfigChangeConstants.NACOS_CORE_CONFIG_PLUGIN_PREFIX))).thenReturn(properties); requestUtilMockedStatic.when(() -> RequestUtil.getSrcUserName(any(HttpServletRequest.class))) .thenReturn("mockedUser"); Mockito.when(configChangePluginService.getServiceType()).thenReturn("mockedConfigChangeService"); Mockito.when(configChangePluginService.pointcutMethodNames()).thenReturn(ConfigChangePointCutTypes.values()); Mockito.when(configChangePluginService.executeType()).thenReturn(ConfigChangeExecuteTypes.EXECUTE_AFTER_TYPE); ConfigChangePluginManager.join(configChangePluginService); configChangeConfigs = new ConfigChangeConfigs(); configChangeAspect = new ConfigChangeAspect(configChangeConfigs); } @AfterEach void after() { propertiesStatic.close(); requestUtilMockedStatic.close(); ConfigChangePluginManager.reset(); } @Test void testPublishOrUpdateConfigAround() throws Throwable { Mockito.when(configChangePluginService.executeType()).thenReturn(ConfigChangeExecuteTypes.EXECUTE_AFTER_TYPE); when(pjp.getArgs()).thenReturn(new Object[] {configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn("dataId"); when(configForm.getGroup()).thenReturn("group"); when(configForm.getNamespaceId()).thenReturn("namespaceId"); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(configRequestInfo.getSrcType()).thenReturn("http"); when(pjp.proceed(any())).thenReturn("Success"); Object o = configChangeAspect.publishOrUpdateConfigAround(pjp); Thread.sleep(20L); // expect service executed. verify(configChangePluginService, Mockito.times(1)) .execute(any(ConfigChangeRequest.class), any(ConfigChangeResponse.class)); //expect join point processed success. assertEquals("Success", o); } @Test void testRemoveConfigByIdAround() throws Throwable { Mockito.when(configChangePluginService.executeType()).thenReturn(ConfigChangeExecuteTypes.EXECUTE_AFTER_TYPE); String dataId = "dataId1"; String group = "group1"; String namespaceId = "namespaceId1"; String tag = "tag1"; String clientIp = "127.0.0.1"; String srcUser = "mockedUser"; String srcType = "http"; when(pjp.getArgs()).thenReturn(new Object[] {dataId, group, namespaceId, tag, clientIp, srcUser, srcType}); Mockito.when(pjp.proceed(any())).thenReturn("mock success return"); Object o = configChangeAspect.removeConfigByIdAround(pjp); Thread.sleep(20L); // expect service executed. verify(configChangePluginService, Mockito.times(1)) .execute(any(ConfigChangeRequest.class), any(ConfigChangeResponse.class)); //expect join point processed success. assertEquals("mock success return", o); } @Test void testDisEnablePluginService() throws Throwable { Properties properties = new Properties(); properties.put("mockedConfigChangeService.enabled", "false"); String dataId = "dataId1"; String group = "group1"; String namespaceId = "namespaceId1"; String tag = "tag1"; String clientIp = "127.0.0.1"; String srcUser = "mockedUser"; String srcType = "http"; when(pjp.getArgs()).thenReturn(new Object[] {dataId, group, namespaceId, tag, clientIp, srcUser, srcType}); propertiesStatic.when(() -> PropertiesUtil.getPropertiesWithPrefix(any(), eq(ConfigChangeConstants.NACOS_CORE_CONFIG_PLUGIN_PREFIX))).thenReturn(properties); configChangeConfigs.onEvent(ServerConfigChangeEvent.newEvent()); assertFalse(Boolean.parseBoolean( configChangeConfigs.getPluginProperties("mockedConfigChangeService").getProperty("enabled"))); Mockito.when(configChangePluginService.executeType()).thenReturn(ConfigChangeExecuteTypes.EXECUTE_BEFORE_TYPE); Mockito.when(configChangePluginService.getServiceType()).thenReturn("mockedConfigChangeService"); ConfigPublishResponse configPublishResponse = ConfigPublishResponse.buildSuccessResponse(); Mockito.when(pjp.proceed()).thenReturn(configPublishResponse); //execute Object o = configChangeAspect.removeConfigByIdAround(pjp); //expect verify(configChangePluginService, Mockito.times(0)) .execute(any(ConfigChangeRequest.class), any(ConfigChangeResponse.class)); assertEquals(configPublishResponse, o); } @Test void testBeforePluginFailurePreventsProceed() throws Throwable { Mockito.when(configChangePluginService.executeType()).thenReturn(ConfigChangeExecuteTypes.EXECUTE_BEFORE_TYPE); Mockito.doAnswer(invocation -> { ConfigChangeResponse response = invocation.getArgument(1); response.setSuccess(false); response.setMsg("Before plugin failed"); return null; }).when(configChangePluginService).execute(any(), any()); when(pjp.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn("dataId"); when(configRequestInfo.getSrcType()).thenReturn("http"); Object result = configChangeAspect.publishOrUpdateConfigAround(pjp); verify(pjp, never()).proceed(); assertEquals(false, result); verify(configChangePluginService).execute(any(), any()); } @Test void testProceedThrowsExceptionHandled() throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn("dataId"); when(configRequestInfo.getSrcType()).thenReturn("http"); when(pjp.proceed(any())).thenThrow(new RuntimeException("Proceed error")); Object result = configChangeAspect.publishOrUpdateConfigAround(pjp); assertEquals(false, result); } @Test void testAfterPluginExecutedAsynchronously() throws Throwable { CountDownLatch latch = new CountDownLatch(1); Mockito.when(configChangePluginService.executeType()).thenReturn(ConfigChangeExecuteTypes.EXECUTE_AFTER_TYPE); Mockito.doAnswer(invocation -> { latch.countDown(); return null; }).when(configChangePluginService).execute(any(), any()); when(pjp.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn("dataId"); when(configRequestInfo.getSrcType()).thenReturn("http"); when(pjp.proceed()).thenReturn("Success"); Object result = configChangeAspect.publishOrUpdateConfigAround(pjp); assertTrue(latch.await(500, TimeUnit.MILLISECONDS)); verify(configChangePluginService).execute(any(), any()); assertEquals(null, result); } @Test void testRpcSourceTypeHandling() throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configRequestInfo.getSrcType()).thenReturn("rpc"); when(configForm.getDataId()).thenReturn("dataId"); when(pjp.proceed()).thenReturn("Success"); configChangeAspect.publishOrUpdateConfigAround(pjp); ArgumentCaptor requestCaptor = ArgumentCaptor.forClass(ConfigChangeRequest.class); verify(configChangePluginService).execute(requestCaptor.capture(), any()); assertEquals(ConfigChangePointCutTypes.PUBLISH_BY_RPC, requestCaptor.getValue().getRequestType()); } @Test void testNoPluginsEnabled() throws Throwable { Properties properties = new Properties(); properties.put("mockedConfigChangeService.enabled", "false"); propertiesStatic.when(() -> PropertiesUtil.getPropertiesWithPrefix(any(), any())).thenReturn(properties); configChangeConfigs.onEvent(ServerConfigChangeEvent.newEvent()); when(pjp.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configRequestInfo.getSrcType()).thenReturn("http"); when(pjp.proceed()).thenReturn("Success"); Object result = configChangeAspect.publishOrUpdateConfigAround(pjp); verify(configChangePluginService, never()).execute(any(), any()); assertEquals("Success", result); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/aspect/RequestLogAspectTest.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.aspect; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.Response; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.monitor.MetricsMonitor; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import org.aspectj.lang.ProceedingJoinPoint; 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.springframework.test.context.junit.jupiter.SpringExtension; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) class RequestLogAspectTest { @Mock private ProceedingJoinPoint pjp; @Mock private ConfigForm configForm; @Mock private ConfigRequestInfo configRequestInfo; @Mock private ConfigQueryChainRequest chainRequest; @Mock private ConfigBatchListenRequest request; @Mock private Response response; @Mock private RequestMeta meta; private RequestLogAspect requestLogAspect; @BeforeEach void setUp() { requestLogAspect = new RequestLogAspect(); } @Test void testInterfacePublishConfig() throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{configForm, configRequestInfo}); when(configForm.getDataId()).thenReturn("dataId"); when(configForm.getGroup()).thenReturn("group"); when(configForm.getNamespaceId()).thenReturn("namespaceId"); when(configForm.getContent()).thenReturn("content"); when(configRequestInfo.getSrcIp()).thenReturn("127.0.0.1"); when(pjp.proceed()).thenReturn("Success"); AtomicInteger publishMonitor = MetricsMonitor.getPublishMonitor(); int initialValue = publishMonitor.get(); Object result = requestLogAspect.interfacePublishConfig(pjp); verify(pjp, times(1)).proceed(); assertEquals("Success", result); assertEquals(initialValue + 1, publishMonitor.get()); } @Test void testInterfaceGetConfig() throws Throwable { when(pjp.getArgs()).thenReturn(new Object[]{chainRequest}); when(chainRequest.getDataId()).thenReturn("dataId"); when(chainRequest.getGroup()).thenReturn("group"); when(chainRequest.getTenant()).thenReturn("tenant"); when(pjp.proceed()).thenReturn("ConfigData"); AtomicInteger configMonitor = MetricsMonitor.getConfigMonitor(); int initialValue = configMonitor.get(); Object result = requestLogAspect.interfaceGetConfig(pjp); verify(pjp, times(1)).proceed(); assertEquals("ConfigData", result); assertEquals(initialValue + 1, configMonitor.get()); } @Test void testInterfaceDeleteConfig() throws Throwable { String dataId = "dataId1"; String group = "group1"; String namespaceId = "namespaceId1"; String tag = "tag1"; String clientIp = "127.0.0.1"; when(pjp.getArgs()).thenReturn(new Object[]{dataId, group, namespaceId, tag, clientIp}); when(pjp.proceed()).thenReturn("Success"); AtomicInteger configMonitor = MetricsMonitor.getConfigMonitor(); int initialValue = configMonitor.get(); Object result = requestLogAspect.interfaceRemoveConfig(pjp); verify(pjp, times(1)).proceed(); assertEquals("Success", result); assertEquals(initialValue + 1, configMonitor.get()); } @Test void testInterfaceListenConfigRpc() throws Throwable { when(meta.getClientIp()).thenReturn("127.0.0.1"); when(request.isListen()).thenReturn(true); when(pjp.proceed()).thenReturn(new ConfigChangeBatchListenResponse()); AtomicInteger configMonitor = MetricsMonitor.getConfigMonitor(); int initialValue = configMonitor.get(); Response result = (Response) requestLogAspect.interfaceListenConfigRpc(pjp, request, meta); assertEquals(result.getResultCode(), 200); assertEquals(initialValue + 1, configMonitor.get()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/configuration/ConfigChangeConfigsTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.configuration; import com.alibaba.nacos.common.event.ServerConfigChangeEvent; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.mock.env.MockEnvironment; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Nacos config change configs test. * * @author liyunfei **/ class ConfigChangeConfigsTest { private ConfigChangeConfigs configChangeConfigs; private MockEnvironment environment; @BeforeEach void setUp() throws Exception { environment = new MockEnvironment(); environment.setProperty("nacos.core.config.plugin.mockPlugin.enabled", "true"); EnvUtil.setEnvironment(environment); configChangeConfigs = new ConfigChangeConfigs(); } @Test void testEnable() { assertTrue(Boolean.parseBoolean(configChangeConfigs.getPluginProperties("mockPlugin").getProperty("enabled"))); } @Test void testUpgradeEnable() { environment.setProperty("nacos.core.config.plugin.mockPlugin.enabled", "false"); configChangeConfigs.onEvent(ServerConfigChangeEvent.newEvent()); assertFalse(Boolean.parseBoolean(configChangeConfigs.getPluginProperties("mockPlugin").getProperty("enabled"))); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/configuration/ConfigCommonConfigTest.java ================================================ /* * Copyright 1999-2020 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.configuration; import com.alibaba.nacos.common.event.ServerConfigChangeEvent; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.mock.env.MockEnvironment; import java.lang.reflect.Constructor; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; /** * Nacos config common configs test. * * @author blake.qiu */ class ConfigCommonConfigTest { private ConfigCommonConfig commonConfig; private MockEnvironment environment; @BeforeEach void setUp() throws Exception { environment = new MockEnvironment(); EnvUtil.setEnvironment(environment); Constructor declaredConstructor = ConfigCommonConfig.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); commonConfig = declaredConstructor.newInstance(); } @Test void getMaxPushRetryTimes() { Integer property = EnvUtil.getProperty("nacos.config.push.maxRetryTime", Integer.class, 50); assertEquals(property.intValue(), commonConfig.getMaxPushRetryTimes()); } @Test void setMaxPushRetryTimes() { int maxPushRetryTimesOld = commonConfig.getMaxPushRetryTimes(); commonConfig.setMaxPushRetryTimes(100); assertEquals(100, commonConfig.getMaxPushRetryTimes()); commonConfig.setMaxPushRetryTimes(maxPushRetryTimesOld); } @Test void testSetDerbyOpsEnabled() { assertFalse(commonConfig.isDerbyOpsEnabled()); commonConfig.setDerbyOpsEnabled(true); assertTrue(commonConfig.isDerbyOpsEnabled()); } @Test void testUpgradeFromEvent() { environment.setProperty("nacos.config.push.maxRetryTime", "100"); environment.setProperty("nacos.config.derby.ops.enabled", "true"); commonConfig.onEvent(ServerConfigChangeEvent.newEvent()); assertEquals(100, commonConfig.getMaxPushRetryTimes()); assertTrue(commonConfig.isDerbyOpsEnabled()); } @Test void testInitConfigFormEnv() throws ReflectiveOperationException { MockEnvironment environment = new MockEnvironment(); EnvUtil.setEnvironment(environment); environment.setProperty("nacos.config.push.maxRetryTime", "6"); Constructor declaredConstructor = ConfigCommonConfig.class.getDeclaredConstructor(); declaredConstructor.setAccessible(true); ConfigCommonConfig configCommonConfig = declaredConstructor.newInstance(); assertEquals(6, configCommonConfig.getMaxPushRetryTimes()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/constant/ConfigModuleStateBuilderTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.persistence.constants.PersistenceConstant; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.module.ModuleState; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.mock.env.MockEnvironment; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; /** * config module state builder test. * * @author 985492783@qq.com * @date 2023/4/7 23:34 */ class ConfigModuleStateBuilderTest { private ConfigurableEnvironment environment; @BeforeEach void setUp() { environment = new MockEnvironment().withProperty(PersistenceConstant.DATASOURCE_PLATFORM_PROPERTY, PersistenceConstant.DERBY) .withProperty(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG, "true"); EnvUtil.setEnvironment(environment); } @Test void testBuild() { ModuleState actual = new ConfigModuleStateBuilder().build(); Map states = actual.getStates(); assertEquals(PersistenceConstant.DERBY, states.get(Constants.DATASOURCE_PLATFORM_PROPERTY_STATE)); assertTrue((Boolean) states.get(Constants.NACOS_PLUGIN_DATASOURCE_LOG_STATE)); assertEquals(PropertyUtil.getNotifyConnectTimeout(), states.get(PropertiesConstant.NOTIFY_CONNECT_TIMEOUT)); assertEquals(PropertyUtil.getNotifySocketTimeout(), states.get(PropertiesConstant.NOTIFY_SOCKET_TIMEOUT)); assertEquals(PropertyUtil.isHealthCheck(), states.get(PropertiesConstant.IS_HEALTH_CHECK)); assertEquals(PropertyUtil.getMaxHealthCheckFailCount(), states.get(PropertiesConstant.MAX_HEALTH_CHECK_FAIL_COUNT)); assertEquals(PropertyUtil.getMaxContent(), states.get(PropertiesConstant.MAX_CONTENT)); assertEquals(PropertyUtil.isManageCapacity(), states.get(PropertiesConstant.IS_MANAGE_CAPACITY)); assertEquals(PropertyUtil.isCapacityLimitCheck(), states.get(PropertiesConstant.IS_CAPACITY_LIMIT_CHECK)); assertEquals(PropertyUtil.getDefaultClusterQuota(), states.get(PropertiesConstant.DEFAULT_CLUSTER_QUOTA)); assertEquals(PropertyUtil.getDefaultGroupQuota(), states.get(PropertiesConstant.DEFAULT_GROUP_QUOTA)); assertEquals(PropertyUtil.getDefaultMaxSize(), states.get(PropertiesConstant.DEFAULT_MAX_SIZE)); assertEquals(PropertyUtil.getDefaultMaxAggrCount(), states.get(PropertiesConstant.DEFAULT_MAX_AGGR_COUNT)); assertEquals(PropertyUtil.getDefaultMaxAggrSize(), states.get(PropertiesConstant.DEFAULT_MAX_AGGR_SIZE)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/constant/ConstantsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; import org.junit.jupiter.api.Test; import static com.alibaba.nacos.config.server.constant.Constants.CAPACITY_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.COMMUNICATION_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.CONFIG_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.HEALTH_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.HISTORY_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.LISTENER_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.METRICS_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.NAMESPACE_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.OPS_CONTROLLER_PATH; import static com.alibaba.nacos.config.server.constant.Constants.RECV_WAIT_TIMEOUT; import static org.junit.jupiter.api.Assertions.assertEquals; class ConstantsTest { @Test void testControllerPathsDefaultValues() { assertEquals("/v1/cs/ops", OPS_CONTROLLER_PATH); assertEquals("/v1/cs/capacity", CAPACITY_CONTROLLER_PATH); assertEquals("/v1/cs/communication", COMMUNICATION_CONTROLLER_PATH); assertEquals("/v1/cs/configs", CONFIG_CONTROLLER_PATH); assertEquals("/v1/cs/health", HEALTH_CONTROLLER_PATH); assertEquals("/v1/cs/history", HISTORY_CONTROLLER_PATH); assertEquals("/v1/cs/listener", LISTENER_CONTROLLER_PATH); assertEquals("/v1/cs/namespaces", NAMESPACE_CONTROLLER_PATH); assertEquals("/v1/cs/metrics", METRICS_CONTROLLER_PATH); } @Test void testRecvWaitTimeoutDefaultValue() { assertEquals(10000, RECV_WAIT_TIMEOUT); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/constant/CounterModeTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.constant; import org.junit.jupiter.api.Test; import org.mockito.Mock; import static org.junit.jupiter.api.Assertions.assertEquals; class CounterModeTest { @Mock CounterMode counterMode; @Test void testReverse() { counterMode = CounterMode.INCREMENT; assertEquals(CounterMode.DECREMENT, counterMode.reverse()); counterMode = CounterMode.DECREMENT; assertEquals(CounterMode.INCREMENT, counterMode.reverse()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/ConfigServletInnerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.constant.HttpHeaderConsts; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.enums.ApiVersionEnum; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.ConfigGrayPersistInfo; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.LongPollingService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.disk.ConfigRocksDbDiskService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.MD5Util; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.sys.env.EnvUtil; 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.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.util.ReflectionTestUtils; import jakarta.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; import static com.alibaba.nacos.config.server.constant.Constants.CONTENT_MD5; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE_UTF8; import static com.alibaba.nacos.config.server.utils.RequestUtil.CLIENT_APPNAME_HEADER; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @WebAppConfiguration class ConfigServletInnerTest { static MockedStatic configDiskServiceFactoryMockedStatic; @InjectMocks ConfigServletInner configServletInner; MockedStatic configCacheServiceMockedStatic; MockedStatic propertyUtilMockedStatic; MockedStatic md5UtilMockedStatic; @Mock private LongPollingService longPollingService; @Mock private ConfigRocksDbDiskService configRocksDbDiskService; @BeforeEach void setUp() { EnvUtil.setEnvironment(new StandardEnvironment()); ReflectionTestUtils.setField(configServletInner, "longPollingService", longPollingService); ReflectionTestUtils.setField(configServletInner, "configQueryChainService", new ConfigQueryChainService()); configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); propertyUtilMockedStatic = Mockito.mockStatic(PropertyUtil.class); propertyUtilMockedStatic.when(PropertyUtil::getMaxContent).thenReturn(1024 * 1000); md5UtilMockedStatic = Mockito.mockStatic(MD5Util.class); configDiskServiceFactoryMockedStatic = Mockito.mockStatic(ConfigDiskServiceFactory.class); when(ConfigDiskServiceFactory.getInstance()).thenReturn(configRocksDbDiskService); } @AfterEach void after() { if (configCacheServiceMockedStatic != null) { configCacheServiceMockedStatic.close(); } if (propertyUtilMockedStatic != null) { propertyUtilMockedStatic.close(); } if (md5UtilMockedStatic != null) { md5UtilMockedStatic.close(); } if (configDiskServiceFactoryMockedStatic != null) { configDiskServiceFactoryMockedStatic.close(); } } @Test void testDoPollingConfig() throws Exception { Map clientMd5Map = new HashMap<>(); MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); HashMap changedGroups = new HashMap<>(); changedGroups.put("1", new ConfigListenState("testMd51")); changedGroups.put("2", new ConfigListenState("testMd52")); md5UtilMockedStatic.when(() -> MD5Util.compareMd5(request, response, clientMd5Map)).thenReturn(changedGroups); md5UtilMockedStatic.when(() -> MD5Util.compareMd5OldResult(changedGroups)).thenReturn("test-old"); md5UtilMockedStatic.when(() -> MD5Util.compareMd5ResultString(changedGroups)).thenReturn("test-new"); String actualValue = configServletInner.doPollingConfig(request, response, clientMd5Map, 1); assertEquals(HttpServletResponse.SC_OK + "", actualValue); assertEquals("test-old", response.getHeader(Constants.PROBE_MODIFY_RESPONSE)); assertEquals("test-new", response.getHeader(Constants.PROBE_MODIFY_RESPONSE_NEW)); assertEquals("no-cache,no-store", response.getHeader("Cache-Control")); } @Test void testDoGetConfigV1Beta() throws Exception { configCacheServiceMockedStatic.when(() -> ConfigCacheService.tryConfigReadLock(anyString())).thenReturn(1); //mock cache item isBeta CacheItem cacheItem = new CacheItem("test"); String dataId = "testDataId135"; String group = "group23"; String tenant = "tenant234"; configCacheServiceMockedStatic.when( () -> ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataId, group, tenant))) .thenReturn(cacheItem); String mockBetaContent = "content3456543"; mockGray4Beta(cacheItem, mockBetaContent, "localhost", "betaKey1234567"); MockHttpServletRequest request = new MockHttpServletRequest(); request.setParameter("dataId", dataId); request.setParameter("group", group); request.setParameter("tenant", tenant); request.setRemoteAddr("localhost"); request.addHeader(CLIENT_APPNAME_HEADER, "test"); MockHttpServletResponse response = new MockHttpServletResponse(); when(configRocksDbDiskService.getGrayContent(dataId, group, tenant, BetaGrayRule.TYPE_BETA)).thenReturn( mockBetaContent); String actualValue = configServletInner.doGetConfig(request, response, dataId, group, tenant, "", "true", "localhost", ApiVersionEnum.V1); assertEquals(HttpServletResponse.SC_OK + "", actualValue); assertEquals("true", response.getHeader("isBeta")); assertEquals(MD5Utils.md5Hex(mockBetaContent, ENCODE_UTF8), response.getHeader(CONTENT_MD5)); assertEquals("betaKey1234567", response.getHeader("Encrypted-Data-Key")); assertEquals(mockBetaContent, response.getContentAsString()); } private void mockGray4Beta(CacheItem cacheItem, String content, String betaIps, String dataKey) { cacheItem.initConfigGrayIfEmpty(BetaGrayRule.TYPE_BETA); ConfigCacheGray configCacheGray = cacheItem.getConfigCacheGray().get(BetaGrayRule.TYPE_BETA); configCacheGray.setMd5(MD5Utils.md5Hex(content, ENCODE_UTF8)); configCacheGray.setEncryptedDataKey(dataKey); ConfigGrayPersistInfo configGrayPersistInfo = new ConfigGrayPersistInfo(BetaGrayRule.TYPE_BETA, BetaGrayRule.VERSION, betaIps, -1000); configCacheGray.resetGrayRule(GrayRuleManager.serializeConfigGrayPersistInfo(configGrayPersistInfo)); cacheItem.sortConfigGray(); } private void mockGray4Tag(CacheItem cacheItem, String content, String tagValue, String dataKey, long ts) { cacheItem.initConfigGrayIfEmpty(TagGrayRule.TYPE_TAG + "_" + tagValue); ConfigCacheGray configCacheGray = cacheItem.getConfigCacheGray().get(TagGrayRule.TYPE_TAG + "_" + tagValue); configCacheGray.setMd5(MD5Utils.md5Hex(content, ENCODE_UTF8)); configCacheGray.setLastModifiedTs(ts); configCacheGray.setEncryptedDataKey(dataKey); ConfigGrayPersistInfo configGrayPersistInfo = new ConfigGrayPersistInfo(TagGrayRule.TYPE_TAG, TagGrayRule.VERSION, tagValue, -999); configCacheGray.resetGrayRule(GrayRuleManager.serializeConfigGrayPersistInfo(configGrayPersistInfo)); cacheItem.sortConfigGray(); } /** * test get config of tag. * * @throws Exception exception. */ @Test void testDoGetConfigV1Tag() throws Exception { String dataId = "dataId123455"; String group = "group"; String tenant = "tenant"; configCacheServiceMockedStatic.when( () -> ConfigCacheService.tryConfigReadLock(GroupKey2.getKey(dataId, group, tenant))).thenReturn(1); //mock cache item with tag. CacheItem cacheItem = new CacheItem("test"); String autoTag = "auto-tag-test"; long autoTagTs = System.currentTimeMillis(); String autoTagContent = "1234566autotag"; mockGray4Tag(cacheItem, autoTagContent, autoTag, "autoTagkey", autoTagTs); String specificTag = "specificTag"; String specificTagContent = "1234566autotag"; long specificTs = System.currentTimeMillis(); mockGray4Tag(cacheItem, specificTagContent, specificTag, "specificTagkey", specificTs); configCacheServiceMockedStatic.when( () -> ConfigCacheService.getContentCache(GroupKey2.getKey(dataId, group, tenant))) .thenReturn(cacheItem); //test auto tag. MockHttpServletRequest request = new MockHttpServletRequest(); request.setParameter("dataId", dataId); request.setParameter("group", group); request.setParameter("tenant", tenant); request.setRemoteAddr("localhost"); request.addHeader(CLIENT_APPNAME_HEADER, "test"); request.addHeader(VIPSERVER_TAG, autoTag); MockHttpServletResponse response = new MockHttpServletResponse(); Mockito.when( configRocksDbDiskService.getGrayContent(dataId, group, tenant, TagGrayRule.TYPE_TAG + "_" + autoTag)) .thenReturn(autoTagContent); String actualValue = configServletInner.doGetConfig(request, response, dataId, group, tenant, null, "true", "localhost", ApiVersionEnum.V1); assertEquals(HttpServletResponse.SC_OK + "", actualValue); assertEquals(autoTagContent, response.getContentAsString()); assertEquals(MD5Utils.md5Hex(autoTagContent, "UTF-8"), response.getHeader(CONTENT_MD5)); assertEquals("autoTagkey", response.getHeader("Encrypted-Data-Key")); //test for specific tag. has higher propority than auto tag. response = new MockHttpServletResponse(); request.setParameter("tag", specificTag); when(configRocksDbDiskService.getGrayContent(dataId, group, tenant, TagGrayRule.TYPE_TAG + "_" + specificTag)).thenReturn(specificTagContent); actualValue = configServletInner.doGetConfig(request, response, dataId, group, tenant, specificTag, "true", "localhost", ApiVersionEnum.V1); assertEquals(HttpServletResponse.SC_OK + "", actualValue); assertEquals(specificTagContent, response.getContentAsString()); assertEquals(MD5Utils.md5Hex(specificTagContent, "UTF-8"), response.getHeader(CONTENT_MD5)); assertEquals("specificTagkey", response.getHeader("Encrypted-Data-Key")); // test for specific tag ,not exist request.setParameter("tag", "auto-tag-test-not-exist"); when(configRocksDbDiskService.getGrayContent(dataId, group, tenant, TagGrayRule.TYPE_TAG + "_" + "auto-tag-test-not-exist")).thenReturn(null); response = new MockHttpServletResponse(); actualValue = configServletInner.doGetConfig(request, response, dataId, group, tenant, "auto-tag-test-not-exist", "true", "localhost", ApiVersionEnum.V1); assertEquals(HttpServletResponse.SC_NOT_FOUND + "", actualValue); String expectedContent = "config data not exist"; String actualContent = response.getContentAsString(); assertTrue(actualContent.contains(expectedContent)); } @Test void testDoGetConfigFormal() throws Exception { String dataId = "dataId1234552333"; String group = "group"; String tenant = "tenant"; configCacheServiceMockedStatic.when( () -> ConfigCacheService.tryConfigReadLock(GroupKey2.getKey(dataId, group, tenant))).thenReturn(1); //mock cache item . CacheItem cacheItem = new CacheItem("test"); String md5 = "md5wertyui"; final String content = "content345678"; cacheItem.getConfigCache().setMd5(md5); long ts = System.currentTimeMillis(); cacheItem.getConfigCache().setLastModifiedTs(ts); cacheItem.getConfigCache().setEncryptedDataKey("key2345678"); configCacheServiceMockedStatic.when( () -> ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataId, group, tenant))) .thenReturn(cacheItem); MockHttpServletRequest request = new MockHttpServletRequest(); request.setParameter("dataId", dataId); request.setParameter("group", group); request.setParameter("tenant", tenant); MockHttpServletResponse response = new MockHttpServletResponse(); when(configRocksDbDiskService.getContent(dataId, group, tenant)).thenReturn(content); String actualValue = configServletInner.doGetConfig(request, response, dataId, group, tenant, null, "true", "localhost", ApiVersionEnum.V1); assertEquals(content, response.getContentAsString()); assertEquals(HttpServletResponse.SC_OK + "", actualValue); assertEquals(md5, response.getHeader(CONTENT_MD5)); assertEquals("key2345678", response.getHeader("Encrypted-Data-Key")); } @Test void testDoGetConfigFormalV2() throws Exception { String dataId = "dataId1234552333V2"; String group = "group"; String tenant = "tenant"; configCacheServiceMockedStatic.when( () -> ConfigCacheService.tryConfigReadLock(GroupKey2.getKey(dataId, group, tenant))).thenReturn(1); //mock cache item . CacheItem cacheItem = new CacheItem("test"); String md5 = "md5wertyui"; final String content = "content345678"; cacheItem.getConfigCache().setMd5(md5); long ts = System.currentTimeMillis(); cacheItem.getConfigCache().setLastModifiedTs(ts); cacheItem.getConfigCache().setEncryptedDataKey("key2345678"); configCacheServiceMockedStatic.when( () -> ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataId, group, tenant))) .thenReturn(cacheItem); MockHttpServletRequest request = new MockHttpServletRequest(); request.setParameter("dataId", dataId); request.setParameter("group", group); request.setParameter("tenant", tenant); MockHttpServletResponse response = new MockHttpServletResponse(); when(configRocksDbDiskService.getContent(dataId, group, tenant)).thenReturn(content); String actualValue = configServletInner.doGetConfig(request, response, dataId, group, tenant, null, "true", "localhost", ApiVersionEnum.V2); assertEquals(JacksonUtils.toJson(Result.success(content)), response.getContentAsString()); assertEquals(HttpServletResponse.SC_OK + "", actualValue); assertEquals(md5, response.getHeader(CONTENT_MD5)); assertEquals("key2345678", response.getHeader("Encrypted-Data-Key")); assertEquals(MediaType.APPLICATION_JSON, response.getHeader(HttpHeaderConsts.CONTENT_TYPE)); } @Test void testDoGetConfigNotExist() throws Exception { String dataId = "test"; String group = "test"; final String tenant = "test"; final String tag = "test"; // if lockResult equals 0,cache item not exist. configCacheServiceMockedStatic.when(() -> ConfigCacheService.tryConfigReadLock(anyString())).thenReturn(0); MockHttpServletRequest request = new MockHttpServletRequest(); request.setParameter("dataId", dataId); request.setParameter("group", group); request.setParameter("tenant", tenant); request.setParameter("tag", tag); MockHttpServletResponse response = new MockHttpServletResponse(); String actualValue = configServletInner.doGetConfig(request, response, dataId, group, tenant, tag, "true", "localhost", ApiVersionEnum.V1); assertEquals(HttpServletResponse.SC_NOT_FOUND + "", actualValue); configCacheServiceMockedStatic.when( () -> ConfigCacheService.getContentCache(GroupKey2.getKey("test", "test", "test"))) .thenReturn(new CacheItem(GroupKey2.getKey("test", "test", "test"))); // if lockResult less than 0 configCacheServiceMockedStatic.when(() -> ConfigCacheService.tryConfigReadLock(anyString())).thenReturn(-1); actualValue = configServletInner.doGetConfig(request, response, "test", "test", "test", "test", "true", "localhost", ApiVersionEnum.V1); assertEquals(HttpServletResponse.SC_CONFLICT + "", actualValue); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/v3/CapacityControllerV3Test.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.form.UpdateCapacityForm; import com.alibaba.nacos.config.server.service.capacity.CapacityService; import com.alibaba.nacos.sys.env.EnvUtil; import jakarta.servlet.ServletContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.sql.Timestamp; 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.eq; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration class CapacityControllerV3Test { @InjectMocks CapacityControllerV3 capacityControllerV3; private MockMvc mockMvc; @Mock private CapacityService capacityService; @Mock private ServletContext servletContext; @BeforeEach void setUp() { EnvUtil.setEnvironment(new StandardEnvironment()); when(servletContext.getContextPath()).thenReturn("/nacos"); ReflectionTestUtils.setField(capacityControllerV3, "capacityService", capacityService); mockMvc = MockMvcBuilders.standaloneSetup(capacityControllerV3).build(); } @Test void testGetCapacityNormal() throws Exception { Capacity capacity = new Capacity(); capacity.setId(1L); capacity.setMaxAggrCount(1); capacity.setMaxSize(1); capacity.setMaxAggrSize(1); capacity.setGmtCreate(new Timestamp(1)); capacity.setGmtModified(new Timestamp(2)); when(capacityService.getCapacityWithDefault(eq("test"), eq("test"))).thenReturn(capacity); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH).param("groupName", "test") .param("namespaceId", "test"); String actualValue = mockMvc.perform(builder).andReturn().getResponse().getContentAsString(); Capacity result = JacksonUtils.toObj(JacksonUtils.toObj(actualValue).get("data").toString(), Capacity.class); assertNotNull(result); assertEquals(capacity.getId(), result.getId()); assertEquals(capacity.getMaxAggrCount(), result.getMaxAggrCount()); assertEquals(capacity.getMaxSize(), result.getMaxSize()); assertEquals(capacity.getMaxAggrSize(), result.getMaxAggrSize()); assertEquals(capacity.getGmtCreate(), result.getGmtCreate()); assertEquals(capacity.getGmtModified(), result.getGmtModified()); } @Test void testGetCapacityException() throws Exception { Capacity capacity = new Capacity(); capacity.setId(1L); capacity.setMaxAggrCount(1); capacity.setMaxSize(1); capacity.setMaxAggrSize(1); capacity.setGmtCreate(new Timestamp(1)); capacity.setGmtModified(new Timestamp(2)); when(capacityService.getCapacityWithDefault(eq("test"), eq("test"))).thenReturn(capacity); // namespaceId & groupName is null MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH); assertThrows(Exception.class, () -> { mockMvc.perform(builder); }); // namespaceId is blank& groupName is null MockHttpServletRequestBuilder builder2 = MockMvcRequestBuilders.get(Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH).param("namespaceId", ""); assertThrows(Exception.class, () -> { mockMvc.perform(builder2); }); // namespaceId is not blank && groupName is not blank when(capacityService.getCapacityWithDefault(eq("g1"), eq("123"))).thenThrow(new NullPointerException()); MockHttpServletRequestBuilder builder3 = MockMvcRequestBuilders.get(Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH).param("namespaceId", "123") .param("groupName", "g1"); String actualValue3 = mockMvc.perform(builder3).andReturn().getResponse().getContentAsString(); } @Test void testUpdateCapacity1x() throws Exception { when(capacityService.insertOrUpdateCapacity("test", "test", 1, 1, 1, 1)).thenReturn(true); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH).param("groupName", "test") .param("namespaceId", "test").param("quota", "1").param("maxSize", "1").param("maxAggrCount", "1").param("maxAggrSize", "1"); String actualValue = mockMvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); assertEquals("0", code); assertEquals("true", data); } @Test void testUpdateCapacity4x() throws Exception { UpdateCapacityForm updateCapacityForm = new UpdateCapacityForm(); updateCapacityForm.setGroupName("test"); updateCapacityForm.setNamespaceId("test"); updateCapacityForm.setQuota(1); updateCapacityForm.setMaxSize(1); updateCapacityForm.setMaxAggrCount(1); updateCapacityForm.setMaxSize(1); when(capacityService.insertOrUpdateCapacity("test", "test", 1, 1, 1, 1)).thenReturn(false); MockHttpServletRequestBuilder builder = post(Constants.CAPACITY_CONTROLLER_V3_ADMIN_PATH).param("groupName", "test") .param("namespaceId", "test").param("quota", "1").param("maxSize", "1").param("maxAggrCount", "1").param("maxAggrSize", "1"); String actualValue = mockMvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); assertEquals("30000", code); assertEquals("null", data); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/v3/ConfigControllerV3Test.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigCloneInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.common.http.param.MediaType; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigMetadata; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.listener.ConfigListenerStateDelegate; import com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.YamlParserUtil; import com.alibaba.nacos.config.server.utils.ZipUtils; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.sys.env.EnvUtil; import com.fasterxml.jackson.databind.JsonNode; import jakarta.servlet.ServletContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration class ConfigControllerV3Test { @InjectMocks ConfigControllerV3 configControllerV3; private MockMvc mockmvc; @Mock private ServletContext servletContext; @Mock private ConfigInfoPersistService configInfoPersistService; @Mock private ConfigInfoBetaPersistService configInfoBetaPersistService; @Mock private ConfigInfoGrayPersistService configInfoGrayPersistService; @Mock private NamespacePersistService namespacePersistService; @Mock private ConfigOperationService configOperationService; @Mock private ConfigListenerStateDelegate configListenerStateDelegate; @Mock private ConfigDetailService configDetailService; @Mock private ConfigMigrateService configMigrateService; @BeforeEach void setUp() { EnvUtil.setEnvironment(new StandardEnvironment()); when(servletContext.getContextPath()).thenReturn("/nacos"); ReflectionTestUtils.setField(configControllerV3, "configListenerStateDelegate", configListenerStateDelegate); ReflectionTestUtils.setField(configControllerV3, "configInfoPersistService", configInfoPersistService); ReflectionTestUtils.setField(configControllerV3, "configInfoBetaPersistService", configInfoBetaPersistService); ReflectionTestUtils.setField(configControllerV3, "configInfoGrayPersistService", configInfoGrayPersistService); ReflectionTestUtils.setField(configControllerV3, "namespacePersistService", namespacePersistService); ReflectionTestUtils.setField(configControllerV3, "configOperationService", configOperationService); ReflectionTestUtils.setField(configControllerV3, "configMigrateService", configMigrateService); mockmvc = MockMvcBuilders.standaloneSetup(configControllerV3).build(); } @Test void testPublishConfig() throws Exception { when(configOperationService.publishConfig(any(), any(), anyString())).thenReturn(true); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(Constants.CONFIG_ADMIN_V3_PATH) .param("dataId", "test").param("groupName", "test").param("namespaceId", "").param("content", "test") .param("tag", "").param("appName", "").param("src_user", "").param("config_tags", "").param("desc", "") .param("use", "").param("effect", "").param("type", "").param("schema", ""); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); assertEquals("0", code); assertEquals("true", data); } @Test void testGetConfig() throws Exception { when(configInfoPersistService.findConfigAllInfo("test", "test", "public")).thenReturn(new ConfigAllInfo()); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CONFIG_ADMIN_V3_PATH) .param("dataId", "test").param("groupName", "test").param("namespaceId", "").param("tag", ""); int actualValue = mockmvc.perform(builder).andReturn().getResponse().getStatus(); assertEquals(200, actualValue); } @Test void testDeleteConfig() throws Exception { when(configOperationService.deleteConfig(anyString(), anyString(), anyString(), anyString(), any(), any(), any())).thenReturn(true); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.CONFIG_ADMIN_V3_PATH) .param("dataId", "test").param("groupName", "test").param("namespaceId", "").param("tag", ""); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); assertEquals("0", code); assertEquals("true", data); } @Test void testDeleteConfigs() throws Exception { final List resultInfos = new ArrayList<>(); ConfigAllInfo configAllInfo = new ConfigAllInfo(); String dataId = "dataId1123"; String groupName = "group34567"; String namespaceId = "tenant45678"; configAllInfo.setDataId(dataId); configAllInfo.setGroup(groupName); configAllInfo.setTenant(namespaceId); resultInfos.add(configAllInfo); Mockito.when(configInfoPersistService.findConfigInfo(eq(1L))).thenReturn(configAllInfo); Mockito.when(configInfoPersistService.findConfigInfo(eq(2L))).thenReturn(configAllInfo); AtomicReference reference = new AtomicReference<>(); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.CONFIG_ADMIN_V3_PATH + "/batch") .param("ids", "1,2"); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); assertEquals("0", code); assertEquals("true", data); Thread.sleep(1200L); } @Test void testGetListeners() throws Exception { Map listenersGroupkeyStatus = new HashMap<>(); listenersGroupkeyStatus.put("test", "test"); ConfigListenerInfo sampleResult = new ConfigListenerInfo(); sampleResult.setQueryType(ConfigListenerInfo.QUERY_TYPE_CONFIG); sampleResult.setListenersStatus(listenersGroupkeyStatus); when(configListenerStateDelegate.getListenerState("test", "test", "public", true)).thenReturn(sampleResult); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CONFIG_ADMIN_V3_PATH + "/listener") .param("dataId", "test").param("groupName", "test").param("namespaceId", "").param("sampleTime", "1"); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); final String code = JacksonUtils.toObj(actualValue).get("code").toString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); ConfigListenerInfo configListenerInfo = JacksonUtils.toObj(data, ConfigListenerInfo.class); assertEquals(ConfigListenerInfo.QUERY_TYPE_CONFIG, configListenerInfo.getQueryType()); assertEquals(1, configListenerInfo.getListenersStatus().size()); assertEquals("test", configListenerInfo.getListenersStatus().get("test")); assertEquals("0", code); } @Test void testSearchConfig() throws Exception { List configInfoList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo("test", "test", "test"); configInfoList.add(configInfo); Page page = new Page<>(); page.setTotalCount(15); page.setPageNumber(1); page.setPagesAvailable(2); page.setPageItems(configInfoList); Map configAdvanceInfo = new HashMap<>(8); when(configDetailService.findConfigInfoPage("accurate", 1, 10, "test", "test", "public", configAdvanceInfo)).thenReturn(page); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CONFIG_ADMIN_V3_PATH + "/list") .param("search", "accurate").param("dataId", "test").param("groupName", "test").param("appName", "") .param("namespaceId", "").param("config_tags", "").param("pageNo", "1").param("pageSize", "10") .param("config_detail", ""); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); JsonNode pageItemsNode = JacksonUtils.toObj(data).get("pageItems"); List resultList = JacksonUtils.toObj(pageItemsNode.toString(), List.class); ConfigBasicInfo resConfigInfo = JacksonUtils.toObj(pageItemsNode.get(0).toString(), ConfigBasicInfo.class); assertEquals(configInfoList.size(), resultList.size()); assertEquals(configInfo.getDataId(), resConfigInfo.getDataId()); assertEquals(configInfo.getGroup(), resConfigInfo.getGroupName()); assertEquals(configInfo.getTenant(), resConfigInfo.getNamespaceId()); } @Test void testFuzzySearchConfig() throws Exception { List configInfoList = new ArrayList<>(); ConfigInfo configInfo = new ConfigInfo("test", "test", "test"); configInfoList.add(configInfo); Page page = new Page<>(); page.setTotalCount(15); page.setPageNumber(1); page.setPagesAvailable(2); page.setPageItems(configInfoList); Map configAdvanceInfo = new HashMap<>(8); when(configDetailService.findConfigInfoPage("blur", 1, 10, "test", "test", "public", configAdvanceInfo)).thenReturn(page); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CONFIG_ADMIN_V3_PATH + "/list") .param("search", "blur").param("dataId", "test").param("groupName", "test").param("appName", "") .param("namespaceId", "").param("config_tags", "").param("pageNo", "1").param("pageSize", "10") .param("config_detail", ""); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); JsonNode pageItemsNode = JacksonUtils.toObj(data).get("pageItems"); List resultList = JacksonUtils.toObj(pageItemsNode.toString(), List.class); ConfigBasicInfo resConfigInfo = JacksonUtils.toObj(pageItemsNode.get(0).toString(), ConfigBasicInfo.class); assertEquals(configInfoList.size(), resultList.size()); assertEquals(configInfo.getDataId(), resConfigInfo.getDataId()); assertEquals(configInfo.getGroup(), resConfigInfo.getGroupName()); assertEquals(configInfo.getTenant(), resConfigInfo.getNamespaceId()); } @Test void testStopBeta() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.delete(Constants.CONFIG_ADMIN_V3_PATH + "/beta") .param("beta", "true").param("dataId", "test").param("groupName", "test").param("namespaceId", ""); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); assertEquals("0", code); assertEquals("true", data); } @Test void testQueryBeta() throws Exception { ConfigInfoGrayWrapper configInfoBetaWrapper = new ConfigInfoGrayWrapper(); configInfoBetaWrapper.setDataId("test"); configInfoBetaWrapper.setGroup("test"); configInfoBetaWrapper.setContent("test"); configInfoBetaWrapper.setGrayName("beta"); configInfoBetaWrapper.setGrayRule( "{\"type\":\"beta\",\"version\":\"1.0.0\",\"expr\":\"127.0.0.1,127.0.0.2\",\"priority\":-1000}"); when(configInfoGrayPersistService.findConfigInfo4Gray("test", "test", "public", "beta")).thenReturn( configInfoBetaWrapper); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CONFIG_ADMIN_V3_PATH + "/beta") .param("beta", "true").param("dataId", "test").param("groupName", "test").param("namespaceId", ""); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); ConfigGrayInfo resConfigInfoBetaWrapper = JacksonUtils.toObj(data, ConfigGrayInfo.class); assertEquals("0", code); assertEquals(configInfoBetaWrapper.getDataId(), resConfigInfoBetaWrapper.getDataId()); assertEquals(configInfoBetaWrapper.getGroup(), resConfigInfoBetaWrapper.getGroupName()); assertEquals(configInfoBetaWrapper.getContent(), resConfigInfoBetaWrapper.getContent()); assertEquals(configInfoBetaWrapper.getGrayName(), resConfigInfoBetaWrapper.getGrayName()); assertEquals("{\"type\":\"beta\",\"version\":\"1.0.0\",\"expr\":\"127.0.0.1,127.0.0.2\",\"priority\":-1000}", resConfigInfoBetaWrapper.getGrayRule()); } @Test void testExportConfig() throws Exception { String dataId = "dataId2.json"; String groupName = "group2"; String namespaceId = "tenant234"; String appname = "appname2"; ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(groupName); configAllInfo.setTenant(namespaceId); configAllInfo.setAppName(appname); configAllInfo.setContent("content1234"); List dataList = new ArrayList<>(); dataList.add(configAllInfo); Mockito.when(configInfoPersistService.findAllConfigInfo4Export(eq(dataId), eq(groupName), eq(namespaceId), eq(appname), eq(Arrays.asList(1L, 2L)))).thenReturn(dataList); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.CONFIG_ADMIN_V3_PATH + "/export") .param("dataId", dataId).param("groupName", groupName).param("namespaceId", namespaceId) .param("appName", appname).param("ids", "1,2"); int actualValue = mockmvc.perform(builder).andReturn().getResponse().getStatus(); assertEquals(200, actualValue); } @Test void testImportAndPublishConfigV2() throws Exception { List zipItems = new ArrayList<>(); String dataId = "dataId23456.json"; String groupName = "group132"; String content = "content1234"; ZipUtils.ZipItem zipItem = new ZipUtils.ZipItem(groupName + "/" + dataId, content); zipItems.add(zipItem); ConfigMetadata configMetadata = new ConfigMetadata(); configMetadata.setMetadata(new ArrayList<>()); ConfigMetadata.ConfigExportItem configExportItem = new ConfigMetadata.ConfigExportItem(); configExportItem.setDataId(dataId); configExportItem.setGroup(groupName); configExportItem.setType("json"); configExportItem.setAppName("appna123"); configMetadata.getMetadata().add(configExportItem); ZipUtils.UnZipResult unziped = new ZipUtils.UnZipResult(zipItems, new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, YamlParserUtil.dumpObject(configMetadata))); MockMultipartFile file = new MockMultipartFile("file", "test.zip", "application/zip", "test".getBytes()); try (MockedStatic zipUtilsMockedStatic = Mockito.mockStatic(ZipUtils.class)) { zipUtilsMockedStatic.when(() -> ZipUtils.unzip(eq(file.getBytes()))).thenReturn(unziped); when(namespacePersistService.tenantInfoCountByTenantId("public")).thenReturn(1); Map map = new HashMap<>(); map.put("test", "test"); when(configInfoPersistService.batchInsertOrUpdate(anyList(), anyString(), anyString(), any(), any())).thenReturn(map); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart( Constants.CONFIG_ADMIN_V3_PATH + "/import").file(file).param("src_user", "test") .param("namespace", "public").param("policy", "ABORT"); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); assertEquals("0", code); Map resultMap = JacksonUtils.toObj(JacksonUtils.toObj(actualValue).get("data").toString(), Map.class); assertEquals(map.get("test"), resultMap.get("test").toString()); } } @Test void testCloneConfig() throws Exception { ConfigCloneInfo cloneInfo = new ConfigCloneInfo(); cloneInfo.setConfigId(1L); cloneInfo.setTargetDataId("test"); cloneInfo.setTargetGroupName("test"); List configBeansList = new ArrayList<>(); configBeansList.add(cloneInfo); when(namespacePersistService.tenantInfoCountByTenantId("public")).thenReturn(1); ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId("test"); configAllInfo.setGroup("test"); List queryedDataList = new ArrayList<>(); queryedDataList.add(configAllInfo); List idList = new ArrayList<>(configBeansList.size()); idList.add(cloneInfo.getConfigId()); when(configInfoPersistService.findAllConfigInfo4Export(null, null, null, null, idList)).thenReturn( queryedDataList); Map map = new HashMap<>(); map.put("test", "test"); when(configInfoPersistService.batchInsertOrUpdate(anyList(), anyString(), anyString(), any(), any())).thenReturn(map); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(Constants.CONFIG_ADMIN_V3_PATH + "/clone") .param("clone", "true").param("src_user", "test").param("namespaceId", "public") .param("policy", "ABORT").content(JacksonUtils.toJson(configBeansList)) .contentType(MediaType.APPLICATION_JSON); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); assertEquals("0", code); Map resultMap = JacksonUtils.toObj(JacksonUtils.toObj(actualValue).get("data").toString(), Map.class); assertEquals(map.get("test"), resultMap.get("test").toString()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/v3/ConfigOpenApiControllerTest.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.ConfigGrayPersistInfo; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.sys.env.EnvUtil; 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.junit.jupiter.MockitoExtension; import org.springframework.mock.env.MockEnvironment; import java.io.UnsupportedEncodingException; 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.when; @ExtendWith(MockitoExtension.class) class ConfigOpenApiControllerTest { @Mock private ConfigQueryChainService configQueryChainService; private ConfigOpenApiController configOpenApiController; @BeforeEach void setUp() { MockEnvironment mockEnvironment = new MockEnvironment(); EnvUtil.setEnvironment(mockEnvironment); configOpenApiController = new ConfigOpenApiController(configQueryChainService); } @Test void testGetConfigExist() throws NacosApiException, UnsupportedEncodingException { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setContent("test"); response.setMd5("testMd5"); response.setConfigType("text"); response.setEncryptedDataKey(null); response.setLastModified(System.currentTimeMillis()); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId("test"); configForm.setGroupName("test"); Result actual = configOpenApiController.getConfig(configForm); assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); assertEquals(ErrorCode.SUCCESS.getMsg(), actual.getMessage()); assertEquals("test", actual.getData().getContent()); assertEquals("testMd5", actual.getData().getMd5()); assertEquals("text", actual.getData().getContentType()); assertEquals(response.getLastModified(), actual.getData().getLastModified()); assertFalse(actual.getData().isBeta()); } @Test void testGetConfigNonExist() throws NacosApiException, UnsupportedEncodingException { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setContent(null); response.setEncryptedDataKey(null); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId("test"); configForm.setGroupName("test"); Result actual = configOpenApiController.getConfig(configForm); assertEquals(ErrorCode.RESOURCE_NOT_FOUND.getCode(), actual.getCode()); assertEquals(ErrorCode.RESOURCE_NOT_FOUND.getMsg(), actual.getMessage()); } @Test void testGetConfigExistBeta() throws NacosApiException, UnsupportedEncodingException { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setContent("test"); response.setMd5("testMd5"); response.setConfigType("text"); response.setEncryptedDataKey(null); response.setLastModified(System.currentTimeMillis()); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY); response.setMatchedGray(new ConfigCacheGray()); BetaGrayRule betaGrayRule = new BetaGrayRule("1.1.1.1", 1); ConfigGrayPersistInfo grayPersistInfo = GrayRuleManager.constructConfigGrayPersistInfo(betaGrayRule); response.getMatchedGray().resetGrayRule(JacksonUtils.toJson(grayPersistInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId("test"); configForm.setGroupName("test"); Result actual = configOpenApiController.getConfig(configForm); assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); assertEquals(ErrorCode.SUCCESS.getMsg(), actual.getMessage()); assertEquals("test", actual.getData().getContent()); assertEquals("testMd5", actual.getData().getMd5()); assertEquals("text", actual.getData().getContentType()); assertEquals(response.getLastModified(), actual.getData().getLastModified()); assertTrue(actual.getData().isBeta()); } @Test void testGetConfigExistGray() throws NacosApiException, UnsupportedEncodingException { ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setContent("test"); response.setMd5("testMd5"); response.setConfigType("text"); response.setEncryptedDataKey(null); response.setLastModified(System.currentTimeMillis()); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY); response.setMatchedGray(new ConfigCacheGray()); TagGrayRule tagGrayRule = new TagGrayRule("1.1.1.1", 1); ConfigGrayPersistInfo grayPersistInfo = GrayRuleManager.constructConfigGrayPersistInfo(tagGrayRule); response.getMatchedGray().resetGrayRule(JacksonUtils.toJson(grayPersistInfo)); when(configQueryChainService.handle(any(ConfigQueryChainRequest.class))).thenReturn(response); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId("test"); configForm.setGroupName("test"); Result actual = configOpenApiController.getConfig(configForm); assertEquals(ErrorCode.SUCCESS.getCode(), actual.getCode()); assertEquals(ErrorCode.SUCCESS.getMsg(), actual.getMessage()); assertEquals("test", actual.getData().getContent()); assertEquals("testMd5", actual.getData().getMd5()); assertEquals("text", actual.getData().getContentType()); assertEquals(response.getLastModified(), actual.getData().getLastModified()); assertFalse(actual.getData().isBeta()); assertEquals("1.1.1.1", actual.getData().getTag()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/v3/ConfigOpsControllerV3Test.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.service.dump.DumpService; import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.persistence.datasource.LocalDataSourceServiceImpl; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.sys.utils.ApplicationUtils; import jakarta.servlet.ServletContext; 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.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.mock.web.MockMultipartFile; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.ArrayList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration class ConfigOpsControllerV3Test { @InjectMocks ConfigOpsControllerV3 configOpsControllerV3; @Mock DumpService dumpService; MockedStatic datasourceConfigurationMockedStatic; MockedStatic dynamicDataSourceMockedStatic; MockedStatic applicationUtilsMockedStatic; private MockMvc mockMvc; @Mock private ServletContext servletContext; @AfterEach void after() { datasourceConfigurationMockedStatic.close(); dynamicDataSourceMockedStatic.close(); applicationUtilsMockedStatic.close(); ConfigCommonConfig.getInstance().setDerbyOpsEnabled(false); } @BeforeEach void init() { when(servletContext.getContextPath()).thenReturn("/nacos"); ReflectionTestUtils.setField(configOpsControllerV3, "dumpService", dumpService); mockMvc = MockMvcBuilders.standaloneSetup(configOpsControllerV3).build(); datasourceConfigurationMockedStatic = Mockito.mockStatic(DatasourceConfiguration.class); dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); applicationUtilsMockedStatic = Mockito.mockStatic(ApplicationUtils.class); } @Test void testUpdateLocalCacheFromStore() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.post(Constants.OPS_CONTROLLER_V3_ADMIN_PATH + "/localCache"); int actualValue = mockMvc.perform(builder).andReturn().getResponse().getStatus(); assertEquals(200, actualValue); } @Test void testSetLogLevel() throws Exception { MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.put(Constants.OPS_CONTROLLER_V3_ADMIN_PATH + "/log").param("logName", "test") .param("logLevel", "test"); int actualValue = mockMvc.perform(builder).andReturn().getResponse().getStatus(); assertEquals(200, actualValue); } @Test void testDerbyOps() throws Exception { ConfigCommonConfig.getInstance().setDerbyOpsEnabled(true); datasourceConfigurationMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true); DynamicDataSource dataSource = Mockito.mock(DynamicDataSource.class); dynamicDataSourceMockedStatic.when(DynamicDataSource::getInstance).thenReturn(dataSource); LocalDataSourceServiceImpl dataSourceService = Mockito.mock(LocalDataSourceServiceImpl.class); when(dataSource.getDataSource()).thenReturn(dataSourceService); JdbcTemplate template = Mockito.mock(JdbcTemplate.class); when(dataSourceService.getJdbcTemplate()).thenReturn(template); when(template.queryForList("SELECT * FROM TEST")).thenReturn(new ArrayList<>()); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.OPS_CONTROLLER_V3_ADMIN_PATH + "/derby") .param("sql", "SELECT * FROM TEST"); String actualValue = mockMvc.perform(builder).andReturn().getResponse().getContentAsString(); assertEquals("0", JacksonUtils.toObj(actualValue).get("code").toString()); } @Test void testImportDerby() throws Exception { ConfigCommonConfig.getInstance().setDerbyOpsEnabled(true); datasourceConfigurationMockedStatic.when(DatasourceConfiguration::isEmbeddedStorage).thenReturn(true); applicationUtilsMockedStatic.when(() -> ApplicationUtils.getBean(DatabaseOperate.class)) .thenReturn(Mockito.mock(DatabaseOperate.class)); MockMultipartFile file = new MockMultipartFile("file", "test.zip", "application/zip", "test".getBytes()); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart(Constants.OPS_CONTROLLER_V3_ADMIN_PATH + "/derby/import") .file(file); int actualValue = mockMvc.perform(builder).andReturn().getResponse().getStatus(); assertEquals(200, actualValue); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/v3/HistoryControllerV3Test.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.service.HistoryService; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.api.model.Page; 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.junit.jupiter.MockitoExtension; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class HistoryControllerV3Test { private static final String TEST_DATA_ID = "test"; private static final String TEST_GROUP = "test"; private static final String TEST_NAMESPACE_ID = ""; private static final String TEST_NAMESPACE_ID_PUBLIC = "public"; private static final String TEST_CONTENT = "test config"; HistoryControllerV3 historyControllerV3; @Mock private HistoryService historyService; @BeforeEach void setUp() { historyControllerV3 = new HistoryControllerV3(historyService); } @Test void testListConfigHistory() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); List configHistoryInfoList = new ArrayList<>(); configHistoryInfoList.add(configHistoryInfo); Page page = new Page<>(); page.setTotalCount(15); page.setPageNumber(1); page.setPagesAvailable(2); page.setPageItems(configHistoryInfoList); when(historyService.listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1, 10)).thenReturn( page); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId(TEST_DATA_ID); configForm.setGroupName(TEST_GROUP); configForm.setNamespaceId(TEST_NAMESPACE_ID); PageForm pageForm = new PageForm(); pageForm.setPageNo(1); pageForm.setPageSize(10); Result> pageResult = historyControllerV3.listConfigHistory(configForm, pageForm); verify(historyService).listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1, 10); List resultList = pageResult.getData().getPageItems(); ConfigHistoryBasicInfo resConfigHistoryInfo = resultList.get(0); assertEquals(ErrorCode.SUCCESS.getCode(), pageResult.getCode()); assertEquals(configHistoryInfoList.size(), resultList.size()); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroupName()); assertEquals(configHistoryInfo.getTenant(), resConfigHistoryInfo.getNamespaceId()); } @Test void testListConfigHistoryWhenNameSpaceIsPublic() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); List configHistoryInfoList = new ArrayList<>(); configHistoryInfoList.add(configHistoryInfo); Page page = new Page<>(); page.setTotalCount(15); page.setPageNumber(1); page.setPagesAvailable(2); page.setPageItems(configHistoryInfoList); when(historyService.listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1, 10)).thenReturn( page); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId(TEST_DATA_ID); configForm.setGroupName(TEST_GROUP); configForm.setNamespaceId(TEST_NAMESPACE_ID_PUBLIC); PageForm pageForm = new PageForm(); pageForm.setPageNo(1); pageForm.setPageSize(10); Result> pageResult = historyControllerV3.listConfigHistory(configForm, pageForm); verify(historyService).listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1, 10); List resultList = pageResult.getData().getPageItems(); ConfigHistoryBasicInfo resConfigHistoryInfo = resultList.get(0); assertEquals(ErrorCode.SUCCESS.getCode(), pageResult.getCode()); assertEquals(configHistoryInfoList.size(), resultList.size()); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroupName()); } @Test void testGetConfigHistoryInfoWhenNameSpaceIsPublic() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setTenant(TEST_NAMESPACE_ID); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyService.getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L)).thenReturn( configHistoryInfo); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId(TEST_DATA_ID); configForm.setGroupName(TEST_GROUP); configForm.setNamespaceId(TEST_NAMESPACE_ID_PUBLIC); Result result = historyControllerV3.getConfigHistoryInfo(configForm, 1L); verify(historyService).getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L); ConfigHistoryDetailInfo resConfigHistoryInfo = result.getData(); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroupName()); assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); } @Test void testGetConfigHistoryInfo() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setTenant(TEST_NAMESPACE_ID); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyService.getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L)).thenReturn( configHistoryInfo); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId(TEST_DATA_ID); configForm.setGroupName(TEST_GROUP); configForm.setNamespaceId(TEST_NAMESPACE_ID); Result result = historyControllerV3.getConfigHistoryInfo(configForm, 1L); verify(historyService).getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L); ConfigHistoryDetailInfo resConfigHistoryInfo = result.getData(); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroupName()); assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); } @Test void testGetPreviousConfigHistoryInfo() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setTenant(TEST_NAMESPACE_ID); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyService.getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L)).thenReturn(configHistoryInfo); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId(TEST_DATA_ID); configForm.setGroupName(TEST_GROUP); configForm.setNamespaceId(TEST_NAMESPACE_ID); Result result = historyControllerV3.getPreviousConfigHistoryInfo(configForm, 1L); verify(historyService).getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L); ConfigHistoryDetailInfo resConfigHistoryInfo = result.getData(); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroupName()); assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); } @Test void testGetPreviousConfigHistoryInfoWhenNameSpaceIsPublic() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setTenant(TEST_NAMESPACE_ID); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyService.getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L)).thenReturn(configHistoryInfo); ConfigFormV3 configForm = new ConfigFormV3(); configForm.setDataId(TEST_DATA_ID); configForm.setGroupName(TEST_GROUP); configForm.setNamespaceId(TEST_NAMESPACE_ID_PUBLIC); Result result = historyControllerV3.getPreviousConfigHistoryInfo(configForm, 1L); verify(historyService).getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_NAMESPACE_ID_PUBLIC, 1L); ConfigHistoryDetailInfo resConfigHistoryInfo = result.getData(); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroupName()); assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); } @Test void testGetConfigListByNamespace() throws NacosApiException { ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); configInfoWrapper.setDataId("test"); configInfoWrapper.setGroup("test"); configInfoWrapper.setContent("test"); List configInfoWrappers = Collections.singletonList(configInfoWrapper); when(historyService.getConfigListByNamespace("test")).thenReturn(configInfoWrappers); Result> result = historyControllerV3.getConfigsByNamespace("test"); verify(historyService).getConfigListByNamespace("test"); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); List actualList = result.getData(); assertEquals(configInfoWrappers.size(), actualList.size()); ConfigBasicInfo actualConfigInfoWrapper = actualList.get(0); assertEquals(configInfoWrapper.getDataId(), actualConfigInfoWrapper.getDataId()); assertEquals(configInfoWrapper.getGroup(), actualConfigInfoWrapper.getGroupName()); } @Test void testGetConfigListByNamespaceWhenIsPublic() throws NacosApiException { ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); configInfoWrapper.setDataId("test"); configInfoWrapper.setGroup("test"); configInfoWrapper.setContent("test"); List configInfoWrappers = Collections.singletonList(configInfoWrapper); when(historyService.getConfigListByNamespace(TEST_NAMESPACE_ID_PUBLIC)).thenReturn(configInfoWrappers); Result> result = historyControllerV3.getConfigsByNamespace(TEST_NAMESPACE_ID_PUBLIC); verify(historyService).getConfigListByNamespace(TEST_NAMESPACE_ID_PUBLIC); assertEquals(ErrorCode.SUCCESS.getCode(), result.getCode()); List actualList = result.getData(); assertEquals(configInfoWrappers.size(), actualList.size()); ConfigBasicInfo actualConfigInfoWrapper = actualList.get(0); assertEquals(configInfoWrapper.getDataId(), actualConfigInfoWrapper.getDataId()); assertEquals(configInfoWrapper.getGroup(), actualConfigInfoWrapper.getGroupName()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/v3/ListenerControllerV3Test.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.service.listener.ConfigListenerStateDelegate; import com.alibaba.nacos.sys.env.EnvUtil; import jakarta.servlet.ServletContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration class ListenerControllerV3Test { @InjectMocks ListenerControllerV3 listenerControllerV3; private MockMvc mockmvc; @Mock private ServletContext servletContext; @Mock private ConfigListenerStateDelegate configListenerStateDelegate; @BeforeEach void setUp() { EnvUtil.setEnvironment(new StandardEnvironment()); when(servletContext.getContextPath()).thenReturn("/nacos"); ReflectionTestUtils.setField(listenerControllerV3, "configListenerStateDelegate", configListenerStateDelegate); mockmvc = MockMvcBuilders.standaloneSetup(listenerControllerV3).build(); } @Test void testGetAllSubClientConfigByIp() throws Exception { ConfigListenerInfo sampleResult = new ConfigListenerInfo(); Map map = new HashMap<>(); map.put("test", "test"); sampleResult.setListenersStatus(map); when(configListenerStateDelegate.getListenerStateByIp("localhost", true)).thenReturn(sampleResult); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.LISTENER_CONTROLLER_V3_ADMIN_PATH) .param("ip", "localhost").param("all", "true").param("namespaceId", "test").param("sampleTime", "1"); String actualValue = mockmvc.perform(builder).andReturn().getResponse().getContentAsString(); assertEquals("0", JacksonUtils.toObj(actualValue).get("code").toString()); String data = JacksonUtils.toObj(actualValue).get("data").toString(); ConfigListenerInfo configListenerInfo = JacksonUtils.toObj(data, ConfigListenerInfo.class); Map resultMap = configListenerInfo.getListenersStatus(); assertEquals(ConfigListenerInfo.QUERY_TYPE_IP, configListenerInfo.getQueryType()); assertEquals(map.get("test"), resultMap.get("test")); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/controller/v3/MetricControllerV3Test.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.controller.v3; import com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.remote.Connection; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.sys.env.EnvUtil; import jakarta.servlet.ServletContext; 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.InjectMocks; import org.mockito.Mock; import org.mockito.Mockito; import org.springframework.core.env.StandardEnvironment; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration class MetricControllerV3Test { @InjectMocks MetricsControllerV3 metricsControllerV3; private MockMvc mockMvc; @Mock private ServerMemberManager memberManager; @Mock private ConnectionManager connectionManager; @Mock private ServletContext servletContext; @BeforeEach void setUp() { System.setProperty("nacos.core.auth.admin.enabled", "false"); EnvUtil.setEnvironment(new StandardEnvironment()); when(servletContext.getContextPath()).thenReturn("/nacos"); ReflectionTestUtils.setField(metricsControllerV3, "serverMemberManager", memberManager); ReflectionTestUtils.setField(metricsControllerV3, "connectionManager", connectionManager); mockMvc = MockMvcBuilders.standaloneSetup(metricsControllerV3).build(); } @AfterEach void tearDown() { System.clearProperty("nacos.core.auth.admin.enabled"); } @Test void testGetClusterMetric() throws Exception { List members = new ArrayList<>(); Member m1 = new Member(); m1.setIp("127.0.0.1"); m1.setPort(8848); members.add(m1); Member m2 = new Member(); m2.setIp("127.0.0.1"); m2.setPort(9848); members.add(m2); Member m3 = new Member(); m3.setIp("127.0.0.1"); m3.setPort(7848); members.add(m3); when(memberManager.allMembers()).thenReturn(members); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.METRICS_CONTROLLER_V3_ADMIN_PATH + "/cluster") .param("ip", "127.0.0.1").param("namespaceId", "test").param("dataId", "test").param("groupName", "test"); String actualValue = mockMvc.perform(builder).andReturn().getResponse().getContentAsString(); String code = JacksonUtils.toObj(actualValue).get("code").toString(); assertEquals("0", code); } @Test void testClusterMetricsCallBack() { Member m1 = new Member(); m1.setIp("127.0.0.1"); m1.setPort(8848); //success result RestResult result1 = new RestResult<>(); HashMap stringObjectHashMap = new HashMap<>(); stringObjectHashMap.put("test", "md5.."); result1.setData(stringObjectHashMap); result1.setCode(200); CountDownLatch latch = new CountDownLatch(5); String dataId = "d1"; String group = "g1"; String tenant = "t1"; String ip = "192.168.0.1"; Map responseMap = new HashMap<>(); MetricsControllerV3.ClusterMetricsCallBack clusterMetricsCallBack = new MetricsControllerV3.ClusterMetricsCallBack( responseMap, latch, dataId, group, tenant, ip, m1); clusterMetricsCallBack.onReceive(result1); //fail result RestResult result2 = new RestResult<>(); HashMap stringObjectHashMap2 = new HashMap<>(); stringObjectHashMap2.put("test2", "md5.."); result2.setData(stringObjectHashMap2); result2.setCode(500); clusterMetricsCallBack.onReceive(result2); //error and cancel clusterMetricsCallBack.onError(new NullPointerException()); clusterMetricsCallBack.onCancel(); clusterMetricsCallBack.onCancel(); assertEquals(stringObjectHashMap, responseMap); assertEquals(0, latch.getCount()); } @Test void testGetCurrentMetric() throws Exception { ClientConfigMetricResponse response = new ClientConfigMetricResponse(); response.putMetric("test", "test"); Connection connection = Mockito.mock(Connection.class); when(connection.request(any(), anyLong())).thenReturn(response); List connections = new ArrayList<>(); connections.add(connection); when(connectionManager.getConnectionByIp(eq("127.0.0.1"))).thenReturn(connections); MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get(Constants.METRICS_CONTROLLER_V3_ADMIN_PATH + "/ip") .param("ip", "127.0.0.1").param("namespaceId", "test").param("dataId", "test").param("groupName", "test"); String actualValue = mockMvc.perform(builder).andReturn().getResponse().getContentAsString(); String data = JacksonUtils.toObj(actualValue).get("data").toString(); assertEquals("{\"test\":\"test\"}", data); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/exception/GlobalExceptionHandlerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.exception; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.config.server.controller.v3.HistoryControllerV3; import com.alibaba.nacos.core.listener.startup.NacosStartUp; import com.alibaba.nacos.core.listener.startup.NacosStartUpManager; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.test.context.bean.override.mockito.MockitoBean; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @WebMvcTest(GlobalExceptionHandlerTest.class) class GlobalExceptionHandlerTest { private MockMvc mockMvc; @Autowired private WebApplicationContext context; @MockitoBean private HistoryControllerV3 historyControllerV3; @BeforeAll static void beforeAll() { NacosStartUpManager.start(NacosStartUp.CORE_START_UP_PHASE); } @BeforeEach void before() { mockMvc = MockMvcBuilders.webAppContextSetup(context).build(); } @Test void testNacosRunTimeExceptionHandler() throws Exception { // 设置HistoryControllerV3的行为,使其抛出NacosRuntimeException并被GlobalExceptionHandler捕获处理 when(historyControllerV3.getConfigsByNamespace(any())).thenThrow( new NacosRuntimeException(NacosException.INVALID_PARAM)) .thenThrow(new NacosRuntimeException(NacosException.SERVER_ERROR)) .thenThrow(new NacosRuntimeException(503)); // 执行请求并验证响应码 (v3 history path) ResultActions resultActions = mockMvc.perform(get("/v3/admin/cs/history/configs").param("namespaceId", "test")); resultActions.andExpect(MockMvcResultMatchers.status().is(NacosException.INVALID_PARAM)); ResultActions resultActions1 = mockMvc.perform(get("/v3/admin/cs/history/configs").param("namespaceId", "test")); resultActions1.andExpect(MockMvcResultMatchers.status().is(NacosException.SERVER_ERROR)); ResultActions resultActions2 = mockMvc.perform(get("/v3/admin/cs/history/configs").param("namespaceId", "test")); resultActions2.andExpect(MockMvcResultMatchers.status().is(503)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/manager/TaskManagerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.manager; import com.alibaba.nacos.common.task.AbstractDelayTask; import com.alibaba.nacos.common.task.NacosTaskProcessor; import com.alibaba.nacos.config.server.constant.Constants; 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.internal.verification.Times; import org.mockito.junit.jupiter.MockitoExtension; import javax.management.ObjectName; import java.lang.management.ManagementFactory; import java.util.Date; import java.util.concurrent.TimeUnit; 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.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class TaskManagerTest { private TaskManager taskManager; @Mock private NacosTaskProcessor taskProcessor; @Mock private NacosTaskProcessor testTaskProcessor; private AbstractDelayTask abstractTask; @BeforeEach void setUp() { taskManager = new TaskManager(TaskManagerTest.class.getName()); taskManager.setDefaultTaskProcessor(taskProcessor); abstractTask = new AbstractDelayTask() { @Override public void merge(AbstractDelayTask task) { } }; } @AfterEach void tearDown() { taskManager.close(); } @Test void testSize() { assertEquals(0, taskManager.size()); taskManager.addTask("test", abstractTask); assertEquals(1, taskManager.size()); taskManager.removeTask("test"); assertEquals(0, taskManager.size()); } @Test void testIsEmpty() { assertTrue(taskManager.isEmpty()); taskManager.addTask("test", abstractTask); assertFalse(taskManager.isEmpty()); taskManager.removeTask("test"); assertTrue(taskManager.isEmpty()); } @Test void testAddProcessor() throws InterruptedException { when(testTaskProcessor.process(abstractTask)).thenReturn(true); taskManager.addProcessor("test", testTaskProcessor); taskManager.addTask("test", abstractTask); TimeUnit.MILLISECONDS.sleep(200); verify(testTaskProcessor).process(abstractTask); verify(taskProcessor, never()).process(abstractTask); } @Test void testRemoveProcessor() throws InterruptedException { when(taskProcessor.process(abstractTask)).thenReturn(true); taskManager.addProcessor("test", testTaskProcessor); taskManager.removeProcessor("test"); taskManager.addTask("test", abstractTask); TimeUnit.MILLISECONDS.sleep(200); verify(testTaskProcessor, never()).process(abstractTask); verify(taskProcessor).process(abstractTask); } @Test void testRetryTaskAfterFail() throws InterruptedException { when(taskProcessor.process(abstractTask)).thenReturn(false, true); taskManager.addTask("test", abstractTask); TimeUnit.MILLISECONDS.sleep(300); verify(taskProcessor, new Times(2)).process(abstractTask); } @Test void testGetTaskInfos() throws InterruptedException { taskManager.addProcessor("test", testTaskProcessor); when(testTaskProcessor.process(abstractTask)).thenReturn(true); taskManager.addTask("test", abstractTask); assertEquals("test:" + new Date(0) + Constants.NACOS_LINE_SEPARATOR, taskManager.getTaskInfos()); TimeUnit.MILLISECONDS.sleep(150); assertEquals("test:finished" + Constants.NACOS_LINE_SEPARATOR, taskManager.getTaskInfos()); } @Test void testInit() throws Exception { taskManager.init(); ObjectName oName = new ObjectName(TaskManagerTest.class.getName() + ":type=" + TaskManager.class.getSimpleName()); assertTrue(ManagementFactory.getPlatformMBeanServer().isRegistered(oName)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/model/ConfigCacheFactoryDelegateTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.sys.env.EnvUtil; 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 java.lang.reflect.Constructor; import java.util.Collections; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ConfigCacheFactoryDelegateTest { MockedStatic envUtilMockedStatic; MockedStatic nacosServiceLoaderMockedStatic; @Mock NacosConfigCacheFactory nacosConfigCacheFactory; @BeforeEach void setUp() { envUtilMockedStatic = mockStatic(EnvUtil.class); nacosServiceLoaderMockedStatic = mockStatic(NacosServiceLoader.class); } @AfterEach void tearDown() { envUtilMockedStatic.close(); nacosServiceLoaderMockedStatic.close(); } @Test public void test() { nacosServiceLoaderMockedStatic.when(() -> NacosServiceLoader.load(ConfigCacheFactory.class)) .thenReturn(Collections.singletonList(nacosConfigCacheFactory)); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")).thenReturn("lalala"); ConfigCache configCache = ConfigCacheFactoryDelegate.getInstance().createConfigCache(); ConfigCache configCache1 = ConfigCacheFactoryDelegate.getInstance().createConfigCache("md5", 123456789L); ConfigCacheGray configCacheGray = ConfigCacheFactoryDelegate.getInstance().createConfigCacheGray("grayName"); ConfigCacheGray configCacheGray1 = ConfigCacheFactoryDelegate.getInstance().createConfigCacheGray(); verify(nacosConfigCacheFactory, times(0)).createConfigCache(); verify(nacosConfigCacheFactory, times(0)).createConfigCacheGray(); } @Test public void test2() throws Exception { when(nacosConfigCacheFactory.getName()).thenReturn("nacos"); when(nacosConfigCacheFactory.createConfigCache()).thenReturn(new ConfigCache()); when(nacosConfigCacheFactory.createConfigCacheGray()).thenReturn(new ConfigCacheGray()); nacosServiceLoaderMockedStatic.when(() -> NacosServiceLoader.load(ConfigCacheFactory.class)) .thenReturn(Collections.singletonList(nacosConfigCacheFactory)); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")).thenReturn("nacos"); Constructor constructor = ConfigCacheFactoryDelegate.class.getDeclaredConstructor(); constructor.setAccessible(true); ConfigCacheFactoryDelegate configCacheFactoryDelegate = (ConfigCacheFactoryDelegate) constructor.newInstance(); configCacheFactoryDelegate.createConfigCache(); configCacheFactoryDelegate.createConfigCache("md5", 123456789L); configCacheFactoryDelegate.createConfigCacheGray("grayName"); configCacheFactoryDelegate.createConfigCacheGray(); verify(nacosConfigCacheFactory, times(2)).createConfigCache(); verify(nacosConfigCacheFactory, times(2)).createConfigCacheGray(); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/model/ConfigCachePostProcessorDelegateTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.sys.env.EnvUtil; 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.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ConfigCachePostProcessorDelegateTest { MockedConstruction mockedConstruction; MockedStatic envUtilMockedStatic; MockedStatic nacosServiceLoaderMockedStatic; @Mock public NacosConfigCachePostProcessor mockConfigCacheMd5PostProcessor; @BeforeEach void setUp() { envUtilMockedStatic = mockStatic(EnvUtil.class); nacosServiceLoaderMockedStatic = mockStatic(NacosServiceLoader.class); } @AfterEach void tearDown() { envUtilMockedStatic.close(); nacosServiceLoaderMockedStatic.close(); } @Test void test1() { envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")).thenReturn("lalala"); nacosServiceLoaderMockedStatic.when(() -> NacosServiceLoader.load(ConfigCachePostProcessor.class)) .thenReturn(Collections.singletonList(mockConfigCacheMd5PostProcessor)); ConfigCachePostProcessorDelegate.getInstance().postProcess(null, null); verify(mockConfigCacheMd5PostProcessor, times(0)).postProcess(null, null); } @Test void test2() throws NoSuchFieldException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { when(mockConfigCacheMd5PostProcessor.getName()).thenReturn("nacos"); doNothing().when(mockConfigCacheMd5PostProcessor).postProcess(null, null); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")).thenReturn("nacos"); nacosServiceLoaderMockedStatic.when(() -> NacosServiceLoader.load(ConfigCachePostProcessor.class)) .thenReturn(Collections.singletonList(mockConfigCacheMd5PostProcessor)); Constructor constructor = ConfigCachePostProcessorDelegate.class.getDeclaredConstructor(); constructor.setAccessible(true); Field field = ConfigCachePostProcessorDelegate.class.getDeclaredField("instance"); field.setAccessible(true); ConfigCachePostProcessorDelegate delegate = (ConfigCachePostProcessorDelegate) constructor.newInstance(); setStaticFinalField(field, delegate); ConfigCachePostProcessorDelegate.getInstance().postProcess(null, null); verify(mockConfigCacheMd5PostProcessor, times(1)).postProcess(null, null); } private void setStaticFinalField(Field finalField, Object value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); getDeclaredFields0.setAccessible(true); Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); Field modifiers = null; for (Field each : fields) { if ("modifiers".equals(each.getName())) { modifiers = each; } } modifiers.setAccessible(true); modifiers.setInt(finalField, finalField.getModifiers() & ~Modifier.FINAL); finalField.setAccessible(true); finalField.set(null, value); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/model/ConfigInfoTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.consistency.IdGenerator; import com.alibaba.nacos.core.distributed.id.SnowFlowerIdGenerator; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.Test; import org.springframework.core.env.StandardEnvironment; import static org.junit.jupiter.api.Assertions.assertEquals; class ConfigInfoTest { @Test void testPrecisionIssue() throws Exception { EnvUtil.setEnvironment(new StandardEnvironment()); IdGenerator generator = new SnowFlowerIdGenerator(); long expected = generator.nextId(); ConfigInfo configInfo = new ConfigInfo(); configInfo.setId(expected); String json = JacksonUtils.toJson(configInfo); ConfigInfo actual = JacksonUtils.toObj(json, ConfigInfo.class); assertEquals(expected, actual.getId()); } @Test void testConfigInfoWithDescAndTags() { ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId("test.properties"); configInfo.setGroup("DEFAULT_GROUP"); configInfo.setTenant("public"); configInfo.setContent("key=value"); configInfo.setDesc("测试配置描述"); configInfo.setConfigTags("tag1,tag2,tag3"); assertEquals("test.properties", configInfo.getDataId()); assertEquals("DEFAULT_GROUP", configInfo.getGroup()); assertEquals("public", configInfo.getTenant()); assertEquals("key=value", configInfo.getContent()); assertEquals("测试配置描述", configInfo.getDesc()); assertEquals("tag1,tag2,tag3", configInfo.getConfigTags()); } @Test void testConfigInfoWithNullDescAndTags() { ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId("test.properties"); configInfo.setGroup("DEFAULT_GROUP"); configInfo.setTenant("public"); configInfo.setContent("key=value"); configInfo.setDesc(null); configInfo.setConfigTags(null); assertEquals("test.properties", configInfo.getDataId()); assertEquals("DEFAULT_GROUP", configInfo.getGroup()); assertEquals("public", configInfo.getTenant()); assertEquals("key=value", configInfo.getContent()); assertEquals(null, configInfo.getDesc()); assertEquals(null, configInfo.getConfigTags()); } @Test void testConfigInfoInheritance() { // 测试 ConfigAllInfo 继承 ConfigInfo 后的字段访问 ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId("test.properties"); configAllInfo.setGroup("DEFAULT_GROUP"); configAllInfo.setDesc("继承的描述字段"); configAllInfo.setConfigTags("inherited,tags"); assertEquals("test.properties", configAllInfo.getDataId()); assertEquals("DEFAULT_GROUP", configAllInfo.getGroup()); assertEquals("继承的描述字段", configAllInfo.getDesc()); assertEquals("inherited,tags", configAllInfo.getConfigTags()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/model/NacosConfigCacheFactoryTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class NacosConfigCacheFactoryTest { @Test public void testCreateConfigCache() { NacosConfigCacheFactory nacosConfigCacheFactory = new NacosConfigCacheFactory(); ConfigCache configCache = nacosConfigCacheFactory.createConfigCache(); assertEquals(ConfigCache.class, configCache.getClass()); ConfigCacheGray configCacheGray = nacosConfigCacheFactory.createConfigCacheGray(); assertEquals(ConfigCacheGray.class, configCacheGray.getClass()); } @Test public void testGetName() { NacosConfigCacheFactory nacosConfigCacheFactory = new NacosConfigCacheFactory(); assertEquals("nacos", nacosConfigCacheFactory.getName()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/model/NacosConfigCachePostProcessorTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class NacosConfigCachePostProcessorTest { @Test public void test() { NacosConfigCachePostProcessor nacosConfigCacheMd5PostProcessor = new NacosConfigCachePostProcessor(); assertEquals("nacos", nacosConfigCacheMd5PostProcessor.getName()); nacosConfigCacheMd5PostProcessor.postProcess(null, null); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/model/form/ConfigFormTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.model.form; 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.assertNotSame; /** * ConfigFormTest * * @author Ken */ public class ConfigFormTest { private ConfigForm original; @BeforeEach void setUp() { // 1. 初始化并填充原始对象的所有类型字段 original = new ConfigForm(); original.setDataId("dataId"); original.setGroup("groupId"); original.setNamespaceId("namespaceId"); original.setContent("content"); original.setTag("tag"); original.setAppName("appName"); original.setSrcUser("srcUser"); original.setConfigTags("configTag"); original.setDesc("desc"); original.setUse("use"); original.setEffect("effect"); original.setType("yaml"); original.setSchema("schema"); original.setEncryptedDataKey("encryptedDataKey"); original.setGrayName("grayName"); original.setGrayRuleExp("grayRuleExp"); original.setGrayVersion("grayVersion"); original.setGrayPriority(5); } @Test void testCloneInstance() { ConfigForm cloned = original.clone(); // 验证非空 assertNotNull(cloned); // 验证内存地址不同(不是同一个引用) assertNotSame(original, cloned); // 验证类类型一致 assertEquals(original.getClass(), cloned.getClass()); } @Test void testCloneFields() { ConfigForm cloned = original.clone(); // 逐一验证关键属性 assertEquals(original.getDataId(), cloned.getDataId()); assertEquals(original.getGroup(), cloned.getGroup()); assertEquals(original.getNamespaceId(), cloned.getNamespaceId()); assertEquals(original.getContent(), cloned.getContent()); assertEquals(original.getAppName(), cloned.getAppName()); assertEquals(original.getGrayPriority(), cloned.getGrayPriority()); assertEquals(original.getGrayName(), cloned.getGrayName()); assertEquals(original.getDesc(), cloned.getDesc()); } @Test void testDeepCopyIndependence() { ConfigForm cloned = original.clone(); // 修改克隆体的属性 String newContent = "new-content-modified"; cloned.setContent(newContent); // 断言原对象的属性没有改变 assertNotEquals(original.getContent(), cloned.getContent()); assertEquals("content", original.getContent()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/paramcheck/ConfigListenerHttpParamExtractorTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.paramcheck; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.model.ConfigInfo; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import jakarta.servlet.http.HttpServletRequest; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.api.common.Constants.LINE_SEPARATOR; import static com.alibaba.nacos.api.common.Constants.WORD_SEPARATOR; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(MockitoExtension.class) class ConfigListenerHttpParamExtractorTest { ConfigListenerHttpParamExtractor configListenerHttpParamExtractor; @Mock HttpServletRequest httpServletRequest; @Test void testNormal() { String listenerConfigsString = getListenerConfigsString(); Mockito.when(httpServletRequest.getParameter(eq("Listening-Configs"))).thenReturn(listenerConfigsString); configListenerHttpParamExtractor = new ConfigListenerHttpParamExtractor(); configListenerHttpParamExtractor.extractParam(httpServletRequest); } @Test void testError() { String listenerConfigsString = getErrorListenerConfigsString(); Mockito.when(httpServletRequest.getParameter(eq("Listening-Configs"))).thenReturn(listenerConfigsString); configListenerHttpParamExtractor = new ConfigListenerHttpParamExtractor(); try { configListenerHttpParamExtractor.extractParam(httpServletRequest); assertTrue(false); } catch (Throwable throwable) { throwable.printStackTrace(); assertTrue(throwable instanceof IllegalArgumentException); } } private String getListenerConfigsString() { ConfigInfo configInfo1 = new ConfigInfo(); configInfo1.setDataId("2345678901"); configInfo1.setGroup("1234445"); configInfo1.setMd5("234567"); configInfo1.setTenant("222345"); ConfigInfo configInfo2 = new ConfigInfo(); configInfo2.setDataId("2345678902"); configInfo2.setGroup("1234445"); configInfo2.setMd5(null); configInfo2.setTenant(null); ConfigInfo configInfo3 = new ConfigInfo(); configInfo3.setDataId("2345678903"); configInfo3.setGroup("1234445"); configInfo3.setMd5("12345"); configInfo3.setTenant(null); ConfigInfo configInfo4 = new ConfigInfo(); configInfo4.setDataId("234567844"); configInfo4.setGroup("1234445"); configInfo4.setMd5("12345"); configInfo4.setTenant(null); List configInfoList = Arrays.asList(configInfo4, configInfo3, configInfo2, configInfo1); StringBuilder sb = new StringBuilder(); for (ConfigInfo configInfo : configInfoList) { sb.append(configInfo.getDataId()).append(WORD_SEPARATOR); sb.append(configInfo.getGroup()).append(WORD_SEPARATOR); if (StringUtils.isBlank(configInfo.getTenant())) { sb.append(configInfo.getMd5()).append(LINE_SEPARATOR); } else { sb.append(configInfo.getMd5()).append(WORD_SEPARATOR); sb.append(configInfo.getTenant()).append(LINE_SEPARATOR); } } return sb.toString(); } private String getErrorListenerConfigsString() { ConfigInfo configInfo1 = new ConfigInfo(); configInfo1.setDataId("2345678901"); List configInfoList = Arrays.asList(configInfo1); StringBuilder sb = new StringBuilder(); for (ConfigInfo configInfo : configInfoList) { sb.append(configInfo.getDataId()).append(WORD_SEPARATOR); } return sb.toString(); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigChangeBatchListenRequestHandlerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest; import com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.core.utils.StringPool; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(MockitoExtension.class) class ConfigChangeBatchListenRequestHandlerTest { @InjectMocks private ConfigChangeBatchListenRequestHandler configQueryRequestHandler; @InjectMocks private ConfigChangeListenContext configChangeListenContext; private RequestMeta requestMeta; @BeforeEach void setUp() { configQueryRequestHandler = new ConfigChangeBatchListenRequestHandler(); ReflectionTestUtils.setField(configQueryRequestHandler, "configChangeListenContext", configChangeListenContext); requestMeta = new RequestMeta(); requestMeta.setClientIp("1.1.1.1"); } @Test void testHandle() { MockedStatic configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String groupKey = GroupKey2.getKey(dataId, group, tenant); groupKey = StringPool.get(groupKey); final String groupKeyCopy = groupKey; configCacheServiceMockedStatic.when( () -> ConfigCacheService.isUptodate(eq(groupKeyCopy), Mockito.any(), Mockito.any(), Mockito.any())).thenReturn(false); ConfigBatchListenRequest configChangeListenRequest = new ConfigBatchListenRequest(); configChangeListenRequest.addConfigListenContext(group, dataId, tenant, " "); try { ConfigChangeBatchListenResponse configChangeBatchListenResponse = configQueryRequestHandler.handle(configChangeListenRequest, requestMeta); boolean hasChange = false; for (ConfigChangeBatchListenResponse.ConfigContext changedConfig : configChangeBatchListenResponse.getChangedConfigs()) { if (changedConfig.getDataId().equals(dataId)) { hasChange = true; break; } } assertTrue(hasChange); } catch (NacosException e) { e.printStackTrace(); } finally { configCacheServiceMockedStatic.close(); } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigChangeClusterSyncRequestHandlerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest; import com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.dump.DumpService; 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.junit.jupiter.MockitoExtension; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @ExtendWith(MockitoExtension.class) class ConfigChangeClusterSyncRequestHandlerTest { private ConfigChangeClusterSyncRequestHandler configChangeClusterSyncRequestHandler; @Mock private DumpService dumpService; @Mock private ConfigMigrateService configMigrateService; @BeforeEach void setUp() throws IOException { configChangeClusterSyncRequestHandler = new ConfigChangeClusterSyncRequestHandler(dumpService, configMigrateService); } @Test void testHandle() throws NacosException { ConfigChangeClusterSyncRequest configChangeSyncRequest = new ConfigChangeClusterSyncRequest(); configChangeSyncRequest.setRequestId(""); configChangeSyncRequest.setDataId("dataId"); configChangeSyncRequest.setTag("tag"); configChangeSyncRequest.setLastModified(1L); configChangeSyncRequest.setBeta(false); RequestMeta meta = new RequestMeta(); meta.setClientIp("1.1.1.1"); ConfigChangeClusterSyncResponse configChangeClusterSyncResponse = configChangeClusterSyncRequestHandler.handle( configChangeSyncRequest, meta); assertEquals(configChangeClusterSyncResponse.getResultCode(), ResponseCode.SUCCESS.getCode()); } @Test void testHandleBetaCompatibleFromOldServer() throws NacosException { ConfigChangeClusterSyncRequest configChangeSyncRequest = new ConfigChangeClusterSyncRequest(); configChangeSyncRequest.setRequestId(""); configChangeSyncRequest.setDataId("dataId"); configChangeSyncRequest.setGroup("group123"); configChangeSyncRequest.setTenant("tenant..."); configChangeSyncRequest.setLastModified(1L); configChangeSyncRequest.setBeta(true); RequestMeta meta = new RequestMeta(); meta.setClientIp("1.1.1.1"); ConfigChangeClusterSyncResponse configChangeClusterSyncResponse = configChangeClusterSyncRequestHandler.handle( configChangeSyncRequest, meta); verify(configMigrateService, times(1)).checkMigrateBeta(configChangeSyncRequest.getDataId(), configChangeSyncRequest.getGroup(), configChangeSyncRequest.getTenant()); assertEquals(configChangeClusterSyncResponse.getResultCode(), ResponseCode.SUCCESS.getCode()); } @Test void testHandleOldCompatibleFromOldServer() throws NacosException { ConfigChangeClusterSyncRequest configChangeSyncRequest = new ConfigChangeClusterSyncRequest(); configChangeSyncRequest.setRequestId(""); configChangeSyncRequest.setDataId("dataId"); configChangeSyncRequest.setGroup("group123"); configChangeSyncRequest.setTenant("tenant..."); configChangeSyncRequest.setTag("tag1234"); configChangeSyncRequest.setLastModified(1L); RequestMeta meta = new RequestMeta(); meta.setClientIp("1.1.1.1"); ConfigChangeClusterSyncResponse configChangeClusterSyncResponse = configChangeClusterSyncRequestHandler.handle( configChangeSyncRequest, meta); verify(configMigrateService, times(1)).checkMigrateTag(configChangeSyncRequest.getDataId(), configChangeSyncRequest.getGroup(), configChangeSyncRequest.getTenant(), configChangeSyncRequest.getTag()); assertEquals(configChangeClusterSyncResponse.getResultCode(), ResponseCode.SUCCESS.getCode()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigChangeListenContextTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.config.server.model.ConfigListenState; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; import java.util.Map; import java.util.Set; 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; @ExtendWith(MockitoExtension.class) class ConfigChangeListenContextTest { private ConfigChangeListenContext configChangeListenContext; @BeforeEach void setUp() throws Exception { configChangeListenContext = new ConfigChangeListenContext(); } @Test void testAddListen() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); Set groupKey = configChangeListenContext.getListeners("groupKey"); assertEquals(1, groupKey.size()); } @Test void testRemoveListen() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); configChangeListenContext.removeListen("groupKey", "connectionId"); Set groupKey = configChangeListenContext.getListeners("groupKey"); assertNull(groupKey); } @Test void testGetListeners() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); Set groupKey = configChangeListenContext.getListeners("groupKey"); assertEquals(1, groupKey.size()); } @Test void testClearContextForConnectionId() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); Map connectionIdBefore = configChangeListenContext.getListenKeys("connectionId"); assertNotNull(connectionIdBefore); configChangeListenContext.clearContextForConnectionId("connectionId"); Map connectionIdAfter = configChangeListenContext.getListenKeys("connectionId"); assertNull(connectionIdAfter); } @Test void testGetListenKeys() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); Set groupKey = configChangeListenContext.getListeners("groupKey"); assertEquals(1, groupKey.size()); } @Test void testGetListenKeyMd5() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); String listenKeyMd5 = configChangeListenContext.getListenKeyMd5("connectionId", "groupKey"); assertEquals("md5", listenKeyMd5); } @Test void testGetConfigListenState() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); ConfigListenState configListenState = configChangeListenContext .getConfigListenState("connectionId", "groupKey"); assertEquals("md5", configListenState.getMd5()); assertFalse(configListenState.isNamespaceTransfer()); } @Test void testGetConfigListenStates() { configChangeListenContext.addListen("groupKey", "md5", "connectionId", false); Map configListenStates = configChangeListenContext .getConfigListenStates("connectionId"); assertEquals(1, configListenStates.size()); assertEquals("md5", configListenStates.get("groupKey").getMd5()); assertFalse(configListenStates.get("groupKey").isNamespaceTransfer()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchChangeNotifierTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.core.remote.grpc.GrpcConnection; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import java.util.Collections; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.CONFIG_CHANGED; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class ConfigFuzzyWatchChangeNotifierTest { ConfigFuzzyWatchChangeNotifier configFuzzyWatchChangeNotifier; @Mock ConnectionManager connectionManager; @Mock RpcPushService rpcPushService; @Mock ConfigFuzzyWatchContextService configFuzzyWatchContextService; MockedStatic tMockedStatic; MockedStatic configCacheServiceMockedStatic; @AfterEach void after() { tMockedStatic.close(); configCacheServiceMockedStatic.close(); } @BeforeEach void setUp() throws IOException { tMockedStatic = Mockito.mockStatic(ConfigExecutor.class); configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); configFuzzyWatchChangeNotifier = new ConfigFuzzyWatchChangeNotifier(connectionManager, rpcPushService, configFuzzyWatchContextService); } @Test void testOnConfigAdd() { String groupKey = GroupKey.getKeyTenant("data1234", "group", "tnnt1234"); when(configFuzzyWatchContextService.syncGroupKeyContext(eq(groupKey), eq(CONFIG_CHANGED))).thenReturn(true); CacheItem cacheItem = Mockito.mock(CacheItem.class); configCacheServiceMockedStatic.when(() -> ConfigCacheService.getContentCache(eq(groupKey))) .thenReturn(cacheItem); String connectionId = "123456"; when(configFuzzyWatchContextService.getMatchedClients(eq(groupKey))).thenReturn( Collections.singleton(connectionId)); GrpcConnection grpcConnection = Mockito.mock(GrpcConnection.class); when(connectionManager.getConnection(eq(connectionId))).thenReturn(grpcConnection); LocalDataChangeEvent localDataChangeEvent = new LocalDataChangeEvent(groupKey); configFuzzyWatchChangeNotifier.onEvent(localDataChangeEvent); tMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchChangeNotifyTask.class), eq(0L), eq(TimeUnit.SECONDS)), times(1)); } @Test void testOnEmptyConnection() { String groupKey = GroupKey.getKeyTenant("data1234", "group", "tnnt1234"); when(configFuzzyWatchContextService.syncGroupKeyContext(eq(groupKey), eq(CONFIG_CHANGED))).thenReturn(true); CacheItem cacheItem = Mockito.mock(CacheItem.class); configCacheServiceMockedStatic.when(() -> ConfigCacheService.getContentCache(eq(groupKey))) .thenReturn(cacheItem); String connectionId = "123456"; when(configFuzzyWatchContextService.getMatchedClients(eq(groupKey))).thenReturn( Collections.singleton(connectionId)); when(connectionManager.getConnection(eq(connectionId))).thenReturn(null); LocalDataChangeEvent localDataChangeEvent = new LocalDataChangeEvent(groupKey); configFuzzyWatchChangeNotifier.onEvent(localDataChangeEvent); tMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchChangeNotifyTask.class), eq(0L), eq(TimeUnit.SECONDS)), times(0)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigFuzzyWatchSyncNotifierTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.model.event.ConfigFuzzyWatchEvent; import com.alibaba.nacos.config.server.service.ConfigFuzzyWatchContextService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class ConfigFuzzyWatchSyncNotifierTest { ConfigFuzzyWatchSyncNotifier configFuzzyWatchSyncNotifier; @Mock ConnectionManager connectionManager; @Mock RpcPushService rpcPushService; @Mock ConfigFuzzyWatchContextService configFuzzyWatchContextService; MockedStatic tMockedStatic; @AfterEach void after() { tMockedStatic.close(); } @BeforeEach void setUp() throws IOException { tMockedStatic = Mockito.mockStatic(ConfigExecutor.class); configFuzzyWatchSyncNotifier = new ConfigFuzzyWatchSyncNotifier(connectionManager, rpcPushService, configFuzzyWatchContextService); } @Test void testInitNotifyWithoutMatchGroupKeys() { String connectionId = "conn12345678"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("dataId*", "group", "tnnt1234"); ConfigFuzzyWatchEvent configFuzzyWatchEvent = new ConfigFuzzyWatchEvent(connectionId, null, groupKeyPattern, true); configFuzzyWatchSyncNotifier.onEvent(configFuzzyWatchEvent); tMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), eq(TimeUnit.SECONDS)), times(1)); } @Test void testInitNotifyWithMatchGroupKeys() { String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("dataId*", "group", "tnnt1234"); Set matchGroupKeys = new HashSet<>(); int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); for (int i = batchSize; i < batchSize * 2; i++) { matchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); } Set clientMatchGroupKeys = new HashSet<>(); for (int i = 0; i < batchSize; i++) { clientMatchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); } when(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern)).thenReturn(matchGroupKeys); when(configFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(false); String connectionId = "conn12345678"; ConfigFuzzyWatchEvent configFuzzyWatchEvent = new ConfigFuzzyWatchEvent(connectionId, clientMatchGroupKeys, groupKeyPattern, true); configFuzzyWatchSyncNotifier.onEvent(configFuzzyWatchEvent); tMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), eq(TimeUnit.SECONDS)), times(2)); } @Test void testInitNotifyWithMatchGroupKeysOnDeleteProtection() { String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("dataId*", "group", "tnnt1234"); Set matchGroupKeys = new HashSet<>(); int batchSize = ConfigCommonConfig.getInstance().getBatchSize(); for (int i = batchSize; i < batchSize * 2; i++) { matchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); } Set clientMatchGroupKeys = new HashSet<>(); for (int i = 0; i < batchSize; i++) { clientMatchGroupKeys.add(GroupKey.getKeyTenant("dataId" + i, "group", "tnnt1234")); } when(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern)).thenReturn(matchGroupKeys); when(configFuzzyWatchContextService.reachToUpLimit(eq(groupKeyPattern))).thenReturn(true); String connectionId = "conn12345678"; ConfigFuzzyWatchEvent configFuzzyWatchEvent = new ConfigFuzzyWatchEvent(connectionId, clientMatchGroupKeys, groupKeyPattern, true); configFuzzyWatchSyncNotifier.onEvent(configFuzzyWatchEvent); tMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), eq(TimeUnit.SECONDS)), times(1)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigPublishRequestHandlerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest; import com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; 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.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ConfigPublishRequestHandlerTest { @Mock ConfigInfoPersistService configInfoPersistService; @Mock ConfigInfoGrayPersistService configInfoGrayPersistService; @Mock ConfigMigrateService configMigrateService; MockedStatic envUtilMockedStatic; private ConfigPublishRequestHandler configPublishRequestHandler; @BeforeEach void setUp() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); ConfigOperationService configOperationService = new ConfigOperationService(configInfoPersistService, configInfoGrayPersistService, configMigrateService); configPublishRequestHandler = new ConfigPublishRequestHandler(configOperationService); DatasourceConfiguration.setEmbeddedStorage(false); } @AfterEach void after() { envUtilMockedStatic.close(); } /** * publish a not-exist config. expect : 1.response return true 2. publish ConfigDataChangeEvent * * @throws Exception exception. */ @Test void testNormalPublishConfigNotCas() throws Exception { String dataId = "testNormalPublishConfigNotCas"; String group = "group"; String tenant = "tenant"; String content = "content"; ConfigPublishRequest configPublishRequest = new ConfigPublishRequest(); configPublishRequest.setDataId(dataId); configPublishRequest.setGroup(group); configPublishRequest.setTenant(tenant); configPublishRequest.setContent(content); Map keyMap = new HashMap<>(); String srcUser = "src_user111"; keyMap.put("src_user", srcUser); configPublishRequest.setAdditionMap(keyMap); RequestMeta requestMeta = new RequestMeta(); String clientIp = "127.0.0.1"; requestMeta.setClientIp(clientIp); AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ConfigDataChangeEvent event1 = (ConfigDataChangeEvent) event; if (event1.dataId.equals(dataId)) { reference.set((ConfigDataChangeEvent) event); } } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); ConfigOperateResult configOperateResult = new ConfigOperateResult(true); long timestamp = System.currentTimeMillis(); long id = timestamp / 1000; configOperateResult.setId(id); configOperateResult.setLastModified(timestamp); when(configInfoPersistService.insertOrUpdate(eq(requestMeta.getClientIp()), eq(srcUser), any(ConfigInfo.class), any(Map.class))).thenReturn(configOperateResult); ConfigPublishResponse response = configPublishRequestHandler.handle(configPublishRequest, requestMeta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); Thread.sleep(500L); assertTrue(reference.get() != null); assertEquals(dataId, reference.get().dataId); assertEquals(group, reference.get().group); assertEquals(tenant, reference.get().tenant); assertEquals(timestamp, reference.get().lastModifiedTs); } /** * publish a exist config. * * @throws Exception exception. */ @Test void testNormalPublishConfigCas() throws Exception { String dataId = "testNormalPublishConfigCas"; String group = "group"; String tenant = "tenant"; String content = "content"; ConfigPublishRequest configPublishRequest = new ConfigPublishRequest(); configPublishRequest.setDataId(dataId); configPublishRequest.setGroup(group); configPublishRequest.setTenant(tenant); configPublishRequest.setContent(content); configPublishRequest.setCasMd5("12314532"); Map keyMap = new HashMap<>(); String srcUser = "src_user111"; keyMap.put("src_user", srcUser); configPublishRequest.setAdditionMap(keyMap); RequestMeta requestMeta = new RequestMeta(); String clientIp = "127.0.0.1"; requestMeta.setClientIp(clientIp); AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ConfigDataChangeEvent event1 = (ConfigDataChangeEvent) event; if (event1.dataId.equals(dataId)) { reference.set((ConfigDataChangeEvent) event); } } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); ConfigOperateResult configOperateResult = new ConfigOperateResult(true); long timestamp = System.currentTimeMillis(); long id = timestamp / 1000; configOperateResult.setId(id); configOperateResult.setLastModified(timestamp); when(configInfoPersistService.insertOrUpdateCas(eq(requestMeta.getClientIp()), eq(srcUser), any(ConfigInfo.class), any(Map.class))).thenReturn(configOperateResult); ConfigPublishResponse response = configPublishRequestHandler.handle(configPublishRequest, requestMeta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); Thread.sleep(500L); assertTrue(reference.get() != null); assertEquals(dataId, reference.get().dataId); assertEquals(group, reference.get().group); assertEquals(tenant, reference.get().tenant); assertEquals(timestamp, reference.get().lastModifiedTs); } /** * publish a exist config. * * @throws Exception exception. */ @Test void testNormalPublishConfigCasError() throws Exception { String dataId = "testNormalPublishConfigCasError"; String group = "group"; String tenant = "tenant"; String content = "content"; ConfigPublishRequest configPublishRequest = new ConfigPublishRequest(); configPublishRequest.setDataId(dataId); configPublishRequest.setGroup(group); configPublishRequest.setTenant(tenant); configPublishRequest.setContent(content); configPublishRequest.setCasMd5("12314532"); Map keyMap = new HashMap<>(); String srcUser = "src_user111"; keyMap.put("src_user", srcUser); configPublishRequest.setAdditionMap(keyMap); RequestMeta requestMeta = new RequestMeta(); String clientIp = "127.0.0.1"; requestMeta.setClientIp(clientIp); ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setId(12345678); long timeStamp = System.currentTimeMillis(); configInfoStateWrapper.setLastModified(timeStamp); AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ConfigDataChangeEvent event1 = (ConfigDataChangeEvent) event; if (event1.dataId.equals(dataId)) { reference.set((ConfigDataChangeEvent) event); } } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); ConfigOperateResult configOperateResult = new ConfigOperateResult(true); long timestamp = System.currentTimeMillis(); long id = timestamp / 1000; configOperateResult.setId(id); configOperateResult.setLastModified(timestamp); when(configInfoPersistService.insertOrUpdateCas(eq(requestMeta.getClientIp()), eq(srcUser), any(ConfigInfo.class), any(Map.class))).thenThrow(new NacosRuntimeException(502, "mock error")); ConfigPublishResponse response = configPublishRequestHandler.handle(configPublishRequest, requestMeta); assertEquals(ResponseCode.FAIL.getCode(), response.getResultCode()); assertTrue(response.getMessage().contains("mock error")); Thread.sleep(500L); assertTrue(reference.get() == null); } @Test void testBetaPublishNotCas() throws NacosException, InterruptedException { String dataId = "testBetaPublish"; String group = "group"; String tenant = "tenant"; String content = "content"; ConfigPublishRequest configPublishRequest = new ConfigPublishRequest(); configPublishRequest.setDataId(dataId); configPublishRequest.setGroup(group); configPublishRequest.setTenant(tenant); configPublishRequest.setContent(content); Map keyMap = new HashMap<>(); String srcUser = "src_user111"; keyMap.put("src_user", srcUser); String betaIps = "127.0.0.1,127.0.0.2"; keyMap.put("betaIps", betaIps); configPublishRequest.setAdditionMap(keyMap); RequestMeta requestMeta = new RequestMeta(); String clientIp = "127.0.0.1"; requestMeta.setClientIp(clientIp); AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ConfigDataChangeEvent event1 = (ConfigDataChangeEvent) event; if (event1.dataId.equals(dataId)) { reference.set((ConfigDataChangeEvent) event); } } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); ConfigOperateResult configOperateResult = new ConfigOperateResult(true); long timestamp = System.currentTimeMillis(); long id = timestamp / 1000; configOperateResult.setId(id); configOperateResult.setLastModified(timestamp); when(configInfoGrayPersistService.insertOrUpdateGray(any(ConfigInfo.class), eq(BetaGrayRule.TYPE_BETA), anyString(), eq(requestMeta.getClientIp()), eq(srcUser))).thenReturn(configOperateResult); ConfigPublishResponse response = configPublishRequestHandler.handle(configPublishRequest, requestMeta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); Thread.sleep(500L); assertTrue(reference.get() != null); assertEquals(dataId, reference.get().dataId); assertEquals(group, reference.get().group); assertEquals(tenant, reference.get().tenant); assertEquals(timestamp, reference.get().lastModifiedTs); assertEquals("beta", reference.get().grayName); } @Test void testBetaPublishCas() throws NacosException, InterruptedException { String dataId = "testBetaPublishCas"; String group = "group"; String tenant = "tenant"; String content = "content"; ConfigPublishRequest configPublishRequest = new ConfigPublishRequest(); configPublishRequest.setDataId(dataId); configPublishRequest.setGroup(group); configPublishRequest.setTenant(tenant); configPublishRequest.setContent(content); configPublishRequest.setCasMd5("12314532"); Map keyMap = new HashMap<>(); String srcUser = "src_user111"; keyMap.put("src_user", srcUser); String betaIps = "127.0.0.1,127.0.0.2"; keyMap.put("betaIps", betaIps); configPublishRequest.setAdditionMap(keyMap); RequestMeta requestMeta = new RequestMeta(); String clientIp = "127.0.0.1"; requestMeta.setClientIp(clientIp); AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ConfigDataChangeEvent event1 = (ConfigDataChangeEvent) event; if (event1.dataId.equals(dataId)) { reference.set((ConfigDataChangeEvent) event); } } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); ConfigOperateResult configOperateResult = new ConfigOperateResult(true); long timestamp = System.currentTimeMillis(); long id = timestamp / 1000; configOperateResult.setId(id); configOperateResult.setLastModified(timestamp); when(configInfoGrayPersistService.insertOrUpdateGrayCas(any(ConfigInfo.class), eq(BetaGrayRule.TYPE_BETA), anyString(), eq(requestMeta.getClientIp()), eq(srcUser))).thenReturn(configOperateResult); ConfigPublishResponse response = configPublishRequestHandler.handle(configPublishRequest, requestMeta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); Thread.sleep(500L); assertTrue(reference.get() != null); assertEquals(dataId, reference.get().dataId); assertEquals(group, reference.get().group); assertEquals(tenant, reference.get().tenant); assertEquals(timestamp, reference.get().lastModifiedTs); assertEquals(tenant, reference.get().tenant); assertEquals("beta", reference.get().grayName); } @Test void testTagPublishNotCas() throws NacosException, InterruptedException { ConfigPublishRequest configPublishRequest = new ConfigPublishRequest(); String dataId = "testTagPublishNotCas"; configPublishRequest.setDataId(dataId); String group = "group"; configPublishRequest.setGroup(group); String tenant = "tenant"; configPublishRequest.setTenant(tenant); Map keyMap = new HashMap<>(); String srcUser = "src_user111"; keyMap.put("src_user", srcUser); String tag = "testTag"; keyMap.put("tag", tag); configPublishRequest.setAdditionMap(keyMap); String content = "content"; configPublishRequest.setContent(content); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { ConfigDataChangeEvent event1 = (ConfigDataChangeEvent) event; if (event1.dataId.equals(dataId)) { reference.set((ConfigDataChangeEvent) event); } } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); ConfigOperateResult configOperateResult = new ConfigOperateResult(true); long timestamp = System.currentTimeMillis(); long id = timestamp / 1000; configOperateResult.setId(id); configOperateResult.setLastModified(timestamp); when(configInfoGrayPersistService.insertOrUpdateGray(any(ConfigInfo.class), eq("tag_" + tag), anyString(), eq(requestMeta.getClientIp()), eq(srcUser))).thenReturn(configOperateResult); ConfigPublishResponse response = configPublishRequestHandler.handle(configPublishRequest, requestMeta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); Thread.sleep(500L); assertTrue(reference.get() != null); assertEquals(dataId, reference.get().dataId); assertEquals(group, reference.get().group); assertEquals(tenant, reference.get().tenant); assertEquals(timestamp, reference.get().lastModifiedTs); assertEquals("tag_" + tag, reference.get().grayName); } @Test void testTagPublishCas() throws NacosException, InterruptedException { String dataId = "testTagPublishCas"; String group = "group"; ConfigPublishRequest configPublishRequest = new ConfigPublishRequest(); configPublishRequest.setDataId(dataId); configPublishRequest.setGroup(group); configPublishRequest.setCasMd5("casmd512"); Map keyMap = new HashMap<>(); String srcUser = "src_user111"; keyMap.put("src_user", srcUser); String tag = "testTag"; keyMap.put("tag", tag); configPublishRequest.setAdditionMap(keyMap); String tenant = "tenant"; configPublishRequest.setTenant(tenant); String content = "content"; configPublishRequest.setContent(content); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { reference.set((ConfigDataChangeEvent) event); } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); ConfigOperateResult configOperateResult = new ConfigOperateResult(true); long timestamp = System.currentTimeMillis(); long id = timestamp / 1000; configOperateResult.setId(id); configOperateResult.setLastModified(timestamp); when(configInfoGrayPersistService.insertOrUpdateGrayCas(any(ConfigInfo.class), eq("tag_" + tag), anyString(), eq(requestMeta.getClientIp()), eq(srcUser))).thenReturn(configOperateResult); ConfigPublishResponse response = configPublishRequestHandler.handle(configPublishRequest, requestMeta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); Thread.sleep(500L); assertTrue(reference.get() != null); assertEquals(dataId, reference.get().dataId); assertEquals(group, reference.get().group); assertEquals(tenant, reference.get().tenant); assertEquals(timestamp, reference.get().lastModifiedTs); assertEquals("tag_" + tag, reference.get().grayName); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigQueryRequestHandlerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.ConfigGrayPersistInfo; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.disk.ConfigRocksDbDiskService; import com.alibaba.nacos.config.server.service.query.ConfigQueryChainService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.sys.env.EnvUtil; 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.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.env.StandardEnvironment; import java.io.IOException; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; import static com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse.CONFIG_NOT_FOUND; 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; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; // todo open the test case @ExtendWith(MockitoExtension.class) class ConfigQueryRequestHandlerTest { static MockedStatic configCacheServiceMockedStatic; static MockedStatic propertyUtilMockedStatic; static MockedStatic configDiskServiceFactoryMockedStatic; String dataId = "dataId" + System.currentTimeMillis(); String group = "group" + System.currentTimeMillis(); String tenant = "tenant" + System.currentTimeMillis(); String content = "content" + System.currentTimeMillis(); private ConfigQueryRequestHandler configQueryRequestHandler; @AfterEach void after() { configCacheServiceMockedStatic.close(); propertyUtilMockedStatic.close(); configDiskServiceFactoryMockedStatic.close(); EnvUtil.setEnvironment(null); } @BeforeEach void setUp() throws IOException { EnvUtil.setEnvironment(new StandardEnvironment()); configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); propertyUtilMockedStatic = Mockito.mockStatic(PropertyUtil.class); configDiskServiceFactoryMockedStatic = Mockito.mockStatic(ConfigDiskServiceFactory.class); configQueryRequestHandler = new ConfigQueryRequestHandler(new ConfigQueryChainService()); final String groupKey = GroupKey2.getKey(dataId, group, Constants.DEFAULT_NAMESPACE_ID); when(ConfigCacheService.tryConfigReadLock(groupKey)).thenReturn(1); propertyUtilMockedStatic.when(PropertyUtil::getMaxContent).thenReturn(1024 * 1000); } /** * get normal config from local disk. * * @throws Exception Exception. */ @Test void testGetNormal() throws Exception { final String groupKey = GroupKey2.getKey(dataId, group, Constants.DEFAULT_NAMESPACE_ID); String content = "content_from_notdirectreadÄãºÃ" + System.currentTimeMillis(); ConfigRocksDbDiskService configRocksDbDiskService = Mockito.mock(ConfigRocksDbDiskService.class); when(ConfigDiskServiceFactory.getInstance()).thenReturn(configRocksDbDiskService); CacheItem cacheItem = new CacheItem(groupKey); cacheItem.getConfigCache().setMd5(MD5Utils.md5Hex(content, "UTF-8")); cacheItem.getConfigCache().setEncryptedDataKey("key_testGetNormal_NotDirectRead"); when(ConfigCacheService.getContentCache(eq(groupKey))).thenReturn(cacheItem); ConfigQueryRequest configQueryRequest = new ConfigQueryRequest(); configQueryRequest.setDataId(dataId); configQueryRequest.setGroup(group); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); when(configRocksDbDiskService.getContent(eq(dataId), eq(group), eq(Constants.DEFAULT_NAMESPACE_ID))).thenReturn( content); ConfigQueryResponse response = configQueryRequestHandler.handle(configQueryRequest, requestMeta); assertEquals(content, response.getContent()); assertEquals(MD5Utils.md5Hex(content, "UTF-8"), response.getMd5()); assertEquals("key_testGetNormal_NotDirectRead", response.getEncryptedDataKey()); assertFalse(response.isBeta()); assertNull(response.getTag()); assertEquals(content, response.getContent()); } /** * get beta config from local disk. * * @throws Exception Exception. */ @Test void testGetBeta() throws Exception { final String groupKey = GroupKey2.getKey(dataId, group, Constants.DEFAULT_NAMESPACE_ID); ConfigRocksDbDiskService configRocksDbDiskService = Mockito.mock(ConfigRocksDbDiskService.class); when(ConfigDiskServiceFactory.getInstance()).thenReturn(configRocksDbDiskService); CacheItem cacheItem = new CacheItem(groupKey); cacheItem.initConfigGrayIfEmpty(BetaGrayRule.TYPE_BETA); String content = "content_from_beta_notdirectreadÄãºÃ" + System.currentTimeMillis(); ConfigCacheGray configCacheGrayBeta = cacheItem.getConfigCacheGray().get(BetaGrayRule.TYPE_BETA); configCacheGrayBeta.setMd5(MD5Utils.md5Hex(content, "UTF-8")); configCacheGrayBeta.setEncryptedDataKey("key_testGetBeta_NotDirectRead"); ConfigGrayPersistInfo configGrayPersistInfo = new ConfigGrayPersistInfo(BetaGrayRule.TYPE_BETA, BetaGrayRule.VERSION, "127.0.0.1", -1000); configCacheGrayBeta.resetGrayRule(GrayRuleManager.serializeConfigGrayPersistInfo(configGrayPersistInfo)); cacheItem.sortConfigGray(); when(ConfigCacheService.getContentCache(eq(groupKey))).thenReturn(cacheItem); ConfigQueryRequest configQueryRequest = new ConfigQueryRequest(); configQueryRequest.setDataId(dataId); configQueryRequest.setGroup(group); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); when(configRocksDbDiskService.getGrayContent(eq(dataId), eq(group), eq(Constants.DEFAULT_NAMESPACE_ID), eq(BetaGrayRule.TYPE_BETA))).thenReturn(content); ConfigQueryResponse response = configQueryRequestHandler.handle(configQueryRequest, requestMeta); //check content&md5 assertEquals(content, response.getContent()); assertEquals(MD5Utils.md5Hex(content, "UTF-8"), response.getMd5()); //check flags. assertTrue(response.isBeta()); assertNull(response.getTag()); } /** * get tag config ,but not found. * * @throws Exception Exception. */ @Test void testGetTagNotFound() throws Exception { final String groupKey = GroupKey2.getKey(dataId, group, Constants.DEFAULT_NAMESPACE_ID); String content = "content_from_tag_withtagÄãºÃ" + System.currentTimeMillis(); ConfigRocksDbDiskService configRocksDbDiskService = Mockito.mock(ConfigRocksDbDiskService.class); when(ConfigDiskServiceFactory.getInstance()).thenReturn(configRocksDbDiskService); CacheItem cacheItem = new CacheItem(groupKey); cacheItem.getConfigCache().setMd5(MD5Utils.md5Hex(content, "UTF-8")); cacheItem.getConfigCache().setEncryptedDataKey("key_testGetTag_NotFound"); when(ConfigCacheService.getContentCache(eq(groupKey))).thenReturn(cacheItem); ConfigQueryRequest configQueryRequest = new ConfigQueryRequest(); configQueryRequest.setDataId(dataId); configQueryRequest.setGroup(group); String specificTag = "specific_tag"; configQueryRequest.setTag(specificTag); String autoTag = "auto_tag111"; configQueryRequest.putHeader(VIPSERVER_TAG, autoTag); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); ConfigQueryResponse response = configQueryRequestHandler.handle(configQueryRequest, requestMeta); //check content&md5 assertNull(response.getContent()); assertNull(response.getMd5()); assertEquals(CONFIG_NOT_FOUND, response.getErrorCode()); assertNull(response.getEncryptedDataKey()); //check flags. assertFalse(response.isBeta()); assertEquals(response.getTag(), specificTag); } /** * get tag config from local disk. * * @throws Exception Exception. */ @Test void testGetTagWithTag() throws Exception { final String groupKey = GroupKey2.getKey(dataId, group, Constants.DEFAULT_NAMESPACE_ID); String content = "content_from_tag_notdirectreadÄãºÃ" + System.currentTimeMillis(); ConfigRocksDbDiskService configRocksDbDiskService = Mockito.mock(ConfigRocksDbDiskService.class); when(ConfigDiskServiceFactory.getInstance()).thenReturn(configRocksDbDiskService); CacheItem cacheItem = new CacheItem(groupKey); cacheItem.getConfigCache().setMd5(MD5Utils.md5Hex(content, "UTF-8")); cacheItem.getConfigCache().setEncryptedDataKey("key_formal"); String specificTag = "specific_tag"; cacheItem.initConfigGrayIfEmpty(TagGrayRule.TYPE_TAG + "_" + specificTag); ConfigCacheGray configCacheGrayTag = cacheItem.getConfigCacheGray() .get(TagGrayRule.TYPE_TAG + "_" + specificTag); String tagContent = "content_from_specific_tag_directreadÄãºÃ" + System.currentTimeMillis(); configCacheGrayTag.setMd5(MD5Utils.md5Hex(tagContent, "UTF-8")); configCacheGrayTag.setEncryptedDataKey("key_testGetTag_NotDirectRead"); ConfigGrayPersistInfo configGrayPersistInfo = new ConfigGrayPersistInfo(TagGrayRule.TYPE_TAG, TagGrayRule.VERSION, specificTag, -999); configCacheGrayTag.resetGrayRule(GrayRuleManager.serializeConfigGrayPersistInfo(configGrayPersistInfo)); cacheItem.sortConfigGray(); //specific tag to get when(ConfigCacheService.getContentCache(eq(groupKey))).thenReturn(cacheItem); ConfigQueryRequest configQueryRequest = new ConfigQueryRequest(); configQueryRequest.setDataId(dataId); configQueryRequest.setGroup(group); configQueryRequest.setTag(specificTag); String autoTag = "auto_tag"; configQueryRequest.putHeader(VIPSERVER_TAG, autoTag); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); //mock disk read. when(configRocksDbDiskService.getGrayContent(eq(dataId), eq(group), eq(Constants.DEFAULT_NAMESPACE_ID), eq(TagGrayRule.TYPE_TAG + "_" + specificTag))).thenReturn(tagContent); ConfigQueryResponse response = configQueryRequestHandler.handle(configQueryRequest, requestMeta); //check content&md5 assertEquals(tagContent, response.getContent()); assertEquals(MD5Utils.md5Hex(tagContent, "UTF-8"), response.getMd5()); assertEquals("key_testGetTag_NotDirectRead", response.getEncryptedDataKey()); //check flags. assertFalse(response.isBeta()); assertEquals(response.getTag(), specificTag); } /** * get tao config of auto tag matchd from local disk. * * @throws Exception Exception. */ @Test void testGetTagAutoTag() throws Exception { final String groupKey = GroupKey2.getKey(dataId, group, Constants.DEFAULT_NAMESPACE_ID); String content = "content_from_tag_notdirectreadÄãºÃ" + System.currentTimeMillis(); ConfigRocksDbDiskService configRocksDbDiskService = Mockito.mock(ConfigRocksDbDiskService.class); when(ConfigDiskServiceFactory.getInstance()).thenReturn(configRocksDbDiskService); String autoTag = "auto_tag"; CacheItem cacheItem = new CacheItem(groupKey); cacheItem.initConfigGrayIfEmpty(TagGrayRule.TYPE_TAG + "_" + autoTag); cacheItem.getConfigCache().setMd5(MD5Utils.md5Hex(content, "UTF-8")); ConfigCacheGray configCacheGrayTag = cacheItem.getConfigCacheGray().get(TagGrayRule.TYPE_TAG + "_" + autoTag); String tagContent = "content_from_specific_tag_directreadÄãºÃ" + System.currentTimeMillis(); configCacheGrayTag.setMd5(MD5Utils.md5Hex(tagContent, "UTF-8")); configCacheGrayTag.setEncryptedDataKey("key_testGetTag_AutoTag_NotDirectRead"); ConfigGrayPersistInfo configGrayPersistInfo = new ConfigGrayPersistInfo(TagGrayRule.TYPE_TAG, TagGrayRule.VERSION, autoTag, -999); configCacheGrayTag.resetGrayRule(GrayRuleManager.serializeConfigGrayPersistInfo(configGrayPersistInfo)); cacheItem.sortConfigGray(); when(ConfigCacheService.getContentCache(eq(groupKey))).thenReturn(cacheItem); ConfigQueryRequest configQueryRequest = new ConfigQueryRequest(); configQueryRequest.setDataId(dataId); configQueryRequest.setGroup(group); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); requestMeta.getAppLabels().put(VIPSERVER_TAG, autoTag); //mock disk read. when(configRocksDbDiskService.getGrayContent(eq(dataId), eq(group), eq(Constants.DEFAULT_NAMESPACE_ID), eq(TagGrayRule.TYPE_TAG + "_" + autoTag))).thenReturn(tagContent); ConfigQueryResponse response = configQueryRequestHandler.handle(configQueryRequest, requestMeta); //check content&md5 assertEquals(tagContent, response.getContent()); assertEquals(MD5Utils.md5Hex(tagContent, "UTF-8"), response.getMd5()); assertEquals("key_testGetTag_AutoTag_NotDirectRead", response.getEncryptedDataKey()); //check flags. assertFalse(response.isBeta()); assertEquals(response.getTag(), autoTag); } /** * get normal config from local disk. * * @throws Exception Exception. */ @Test void testGetConfigNotExistAndConflict() throws Exception { String dataId = "dataId" + System.currentTimeMillis(); String group = "group" + System.currentTimeMillis(); String tenant = "tenant" + System.currentTimeMillis(); //test config not exist configCacheServiceMockedStatic.when( () -> ConfigCacheService.tryConfigReadLock(GroupKey2.getKey(dataId, group, tenant))).thenReturn(0); final String groupKey = GroupKey2.getKey(dataId, group, tenant); when(ConfigCacheService.getContentCache(eq(groupKey))).thenReturn(null); ConfigQueryRequest configQueryRequest = new ConfigQueryRequest(); configQueryRequest.setDataId(dataId); configQueryRequest.setGroup(group); configQueryRequest.setTenant(tenant); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); ConfigQueryResponse response = configQueryRequestHandler.handle(configQueryRequest, requestMeta); assertEquals(CONFIG_NOT_FOUND, response.getErrorCode()); assertNull(response.getContent()); assertNull(response.getMd5()); assertFalse(response.isBeta()); assertNull(response.getTag()); //test config conflict when(ConfigCacheService.getContentCache(eq(groupKey))).thenReturn(new CacheItem(groupKey)); configCacheServiceMockedStatic.when( () -> ConfigCacheService.tryConfigReadLock(GroupKey2.getKey(dataId, group, tenant))).thenReturn(-1); ConfigQueryResponse responseConflict = configQueryRequestHandler.handle(configQueryRequest, requestMeta); assertEquals(ConfigQueryResponse.CONFIG_QUERY_CONFLICT, responseConflict.getErrorCode()); assertNull(responseConflict.getContent()); assertNull(responseConflict.getMd5()); assertFalse(responseConflict.isBeta()); assertNull(responseConflict.getTag()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/ConfigRemoveRequestHandlerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest; import com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.api.remote.response.ResponseCode; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; 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.junit.jupiter.MockitoExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ConfigRemoveRequestHandlerTest { private ConfigRemoveRequestHandler configRemoveRequestHandler; @Mock private ConfigInfoPersistService configInfoPersistService; @Mock private ConfigInfoGrayPersistService configInfoGrayPersistService; @Mock private ConfigOperationService configOperationService; @BeforeEach void setUp() throws Exception { configRemoveRequestHandler = new ConfigRemoveRequestHandler(configInfoPersistService, configInfoGrayPersistService, configOperationService); } @Test void testHandleSuccess() throws Exception { ConfigRemoveRequest configRemoveRequest = new ConfigRemoveRequest(); configRemoveRequest.setRequestId("requestId"); configRemoveRequest.setGroup("group"); configRemoveRequest.setDataId("dataId"); configRemoveRequest.setTenant("tenant"); RequestMeta meta = new RequestMeta(); meta.setClientIp("1.1.1.1"); when(configOperationService.deleteConfig( anyString(), anyString(), anyString(), isNull(), eq("1.1.1.1"), isNull(), eq(Constants.RPC))).thenReturn(true); ConfigRemoveResponse response = configRemoveRequestHandler.handle(configRemoveRequest, meta); assertEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); verify(configOperationService, times(1)).deleteConfig( anyString(), anyString(), anyString(), isNull(), eq("1.1.1.1"), isNull(), eq(Constants.RPC)); } @Test void testHandleException() throws Exception { ConfigRemoveRequest configRemoveRequest = new ConfigRemoveRequest(); configRemoveRequest.setRequestId("requestId"); configRemoveRequest.setGroup("group"); configRemoveRequest.setDataId("dataId"); configRemoveRequest.setTenant("tenant"); RequestMeta meta = new RequestMeta(); meta.setClientIp("1.1.1.1"); when(configOperationService.deleteConfig( anyString(), anyString(), anyString(), isNull(), eq("1.1.1.1"), isNull(), eq(Constants.RPC))).thenThrow(new RuntimeException("test exception")); ConfigRemoveResponse response = configRemoveRequestHandler.handle(configRemoveRequest, meta); assertNotEquals(ResponseCode.SUCCESS.getCode(), response.getResultCode()); assertTrue(response.getMessage().contains("test exception")); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/FuzzyWatchSyncNotifyCallbackTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigFuzzyWatchSyncRequest; import com.alibaba.nacos.common.task.BatchTaskCounter; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.tps.TpsControlManager; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.api.common.Constants.FUZZY_WATCH_INIT_NOTIFY; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class FuzzyWatchSyncNotifyCallbackTest { @Mock ConnectionManager connectionManager; @Mock ControlManagerCenter controlManagerCenter; @Mock TpsControlManager tpsControlManager; MockedStatic controlManagerCenterMockedStatic; @Mock RpcPushService rpcPushService; MockedStatic tMockedStatic; FuzzyWatchSyncNotifyCallback fuzzyWatchSyncNotifyCallback; BatchTaskCounter taskCounter; @Mock ConfigFuzzyWatchSyncRequest configFuzzyWatchSyncRequest; @AfterEach void after() { tMockedStatic.close(); controlManagerCenterMockedStatic.close(); } @BeforeEach void setUp() throws IOException { taskCounter = new BatchTaskCounter(5); tMockedStatic = Mockito.mockStatic(ConfigExecutor.class); controlManagerCenterMockedStatic = Mockito.mockStatic(ControlManagerCenter.class); Mockito.when(ControlManagerCenter.getInstance()).thenReturn(controlManagerCenter); Mockito.when(ControlManagerCenter.getInstance().getTpsControlManager()).thenReturn(tpsControlManager); FuzzyWatchSyncNotifyTask fuzzyWatchSyncNotifyTask = new FuzzyWatchSyncNotifyTask(connectionManager, rpcPushService, configFuzzyWatchSyncRequest, taskCounter, 5, "con1"); fuzzyWatchSyncNotifyCallback = new FuzzyWatchSyncNotifyCallback(fuzzyWatchSyncNotifyTask); } @Test void testOnSuccess() { when(configFuzzyWatchSyncRequest.getSyncType()).thenReturn(FUZZY_WATCH_INIT_NOTIFY); when(configFuzzyWatchSyncRequest.getCurrentBatch()).thenReturn(5); for (int i = 1; i < 5; i++) { taskCounter.batchSuccess(i); } fuzzyWatchSyncNotifyCallback.fuzzyWatchSyncNotifyTask.tryTimes++; fuzzyWatchSyncNotifyCallback.onSuccess(); //create a new init finish task; tMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(0L), eq(TimeUnit.SECONDS)), times(1)); } @Test void testOnFail() { fuzzyWatchSyncNotifyCallback.fuzzyWatchSyncNotifyTask.tryTimes++; fuzzyWatchSyncNotifyCallback.onFail(new RuntimeException()); // schedule self ,after 2 sec. tMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(FuzzyWatchSyncNotifyTask.class), eq(2L), eq(TimeUnit.SECONDS)), times(1)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/remote/RpcConfigChangeNotifierTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.remote; import com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.core.remote.ConnectionManager; import com.alibaba.nacos.core.remote.ConnectionMeta; import com.alibaba.nacos.core.remote.RpcPushService; import com.alibaba.nacos.core.remote.grpc.GrpcConnection; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.tps.TpsControlManager; import com.alibaba.nacos.plugin.control.tps.request.TpsCheckRequest; import com.alibaba.nacos.plugin.control.tps.response.TpsCheckResponse; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.util.ReflectionTestUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; @ExtendWith(MockitoExtension.class) class RpcConfigChangeNotifierTest { static final String POINT_CONFIG_PUSH = "CONFIG_PUSH_COUNT"; static final String POINT_CONFIG_PUSH_SUCCESS = "CONFIG_PUSH_SUCCESS"; static final String POINT_CONFIG_PUSH_FAIL = "CONFIG_PUSH_FAIL"; @Mock ControlManagerCenter controlManagerCenter; @Mock TpsControlManager tpsControlManager; MockedStatic controlManagerCenterMockedStatic; MockedStatic envUtilMockedStatic; private RpcConfigChangeNotifier rpcConfigChangeNotifier; @Mock private ConfigChangeListenContext configChangeListenContext; @Mock private RpcPushService rpcPushService; @Mock private ConnectionManager connectionManager; @BeforeEach void setUp() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("nacos.config.push.maxRetryTime"), eq(Integer.class), anyInt())) .thenReturn(3); controlManagerCenterMockedStatic = Mockito.mockStatic(ControlManagerCenter.class); Mockito.when(ControlManagerCenter.getInstance()).thenReturn(controlManagerCenter); Mockito.when(ControlManagerCenter.getInstance().getTpsControlManager()).thenReturn(tpsControlManager); rpcConfigChangeNotifier = new RpcConfigChangeNotifier(); ReflectionTestUtils.setField(rpcConfigChangeNotifier, "configChangeListenContext", configChangeListenContext); ReflectionTestUtils.setField(rpcConfigChangeNotifier, "rpcPushService", rpcPushService); ReflectionTestUtils.setField(rpcConfigChangeNotifier, "connectionManager", connectionManager); } @AfterEach void after() { envUtilMockedStatic.close(); controlManagerCenterMockedStatic.close(); } @Test void testOnDataEvent() throws InterruptedException { final String groupKey = GroupKey2.getKey("nacos.internal.tps.control_rule_1", "nacos", "tenant"); List betaIps = new ArrayList<>(); betaIps.add("1.1.1.1"); Set mockConnectionIds = new HashSet<>(); mockConnectionIds.add("con1"); mockConnectionIds.add("con2"); mockConnectionIds.add("con3"); GrpcConnection mockConn1 = Mockito.mock(GrpcConnection.class); GrpcConnection mockConn3 = Mockito.mock(GrpcConnection.class); //mock con1 push normal Mockito.when(connectionManager.getConnection(eq("con1"))).thenReturn(mockConn1); Mockito.when(mockConn1.getMetaInfo()) .thenReturn(new ConnectionMeta("con1", "192.168.0.1", "192.168.0.2", 34567, 9848, "GRPC", "2.2.0", null, new HashMap<>())); //mock con1 noy exist Mockito.when(connectionManager.getConnection(eq("con2"))).thenReturn(null); Mockito.when(connectionManager.getConnection(eq("con3"))).thenReturn(mockConn3); Mockito.when(mockConn3.getMetaInfo()) .thenReturn(new ConnectionMeta("con3", "192.168.0.1", "192.168.0.2", 34567, 9848, "GRPC", "2.2.0", null, new HashMap<>())); Mockito.when(configChangeListenContext.getListeners(eq(groupKey))).thenReturn(mockConnectionIds); Mockito.when(configChangeListenContext.getConfigListenState(anyString(), anyString())).thenReturn(new ConfigListenState("111")); //mock push tps passed Mockito.when(tpsControlManager.check(any(TpsCheckRequest.class))).thenReturn(new TpsCheckResponse(true, 200, "success")); rpcConfigChangeNotifier.onEvent(new LocalDataChangeEvent(groupKey)); //wait rpc push executed. Thread.sleep(50L); //expect rpc push task run. Mockito.verify(rpcPushService, times(1)) .pushWithCallback(eq("con1"), any(ConfigChangeNotifyRequest.class), any(RpcConfigChangeNotifier.RpcPushCallback.class), any(Executor.class)); Mockito.verify(rpcPushService, times(1)) .pushWithCallback(eq("con3"), any(ConfigChangeNotifyRequest.class), any(RpcConfigChangeNotifier.RpcPushCallback.class), any(Executor.class)); } @Test void testRpcCallBack() { MockedStatic configExecutorMockedStatic = Mockito.mockStatic(ConfigExecutor.class); try { RpcConfigChangeNotifier.RpcPushTask task = Mockito.mock(RpcConfigChangeNotifier.RpcPushTask.class); Mockito.when(task.getConnectionId()).thenReturn("testconn1"); Mockito.when(connectionManager.getConnection(eq("testconn1"))).thenReturn(Mockito.mock(GrpcConnection.class)); ConfigChangeNotifyRequest notifyRequest = new ConfigChangeNotifyRequest(); notifyRequest.setDataId("d1"); notifyRequest.setGroup("g1"); Mockito.when(task.getNotifyRequest()).thenReturn(notifyRequest); //mock task not overtimes and receive exception on callback Mockito.when(task.isOverTimes()).thenReturn(false); Mockito.when(task.getTryTimes()).thenReturn(2); RpcConfigChangeNotifier.RpcPushCallback rpcPushCallback = new RpcConfigChangeNotifier.RpcPushCallback(task, tpsControlManager, connectionManager); rpcPushCallback.onFail(new RuntimeException()); //expect config push fail be recorded. Mockito.verify(tpsControlManager, times(1)).check(any(TpsCheckRequest.class)); //expect schedule this task next retry times configExecutorMockedStatic.verify( () -> ConfigExecutor.scheduleClientConfigNotifier(any(RpcConfigChangeNotifier.RpcPushTask.class), eq(2 * 2L), eq(TimeUnit.SECONDS))); //mock rpcPushCallback.onSuccess(); //expect config push success be recorded. Mockito.verify(tpsControlManager, times(2)).check(any(TpsCheckRequest.class)); //mock task is over times Mockito.when(task.isOverTimes()).thenReturn(true); rpcPushCallback.onFail(new NullPointerException()); Mockito.verify(connectionManager, times(1)).unregister(eq("testconn1")); } finally { configExecutorMockedStatic.close(); } } @Test void testRegisterTpsPoint() { rpcConfigChangeNotifier.registerTpsPoint(); Mockito.verify(tpsControlManager, Mockito.times(1)).registerTpsPoint(eq(POINT_CONFIG_PUSH)); Mockito.verify(tpsControlManager, Mockito.times(1)).registerTpsPoint(eq(POINT_CONFIG_PUSH_SUCCESS)); Mockito.verify(tpsControlManager, Mockito.times(1)).registerTpsPoint(eq(POINT_CONFIG_PUSH_FAIL)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/ClientTrackServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.sys.env.EnvUtil; 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.MockedStatic; import org.mockito.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(SpringExtension.class) @WebAppConfiguration class ClientTrackServiceTest { MockedStatic envUtilMockedStatic; @BeforeEach void before() { ClientTrackService.clientRecords.clear(); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")) .thenReturn("nacos"); } @AfterEach void after() { envUtilMockedStatic.close(); } @Test void testTrackClientMd5() { String clientIp = "1.1.1.1"; String dataId = "com.taobao.session.xml"; String group = "online"; String groupKey = GroupKey2.getKey(dataId, group); String md5 = "xxxxxxxxxxxxx"; String content = "test"; ConfigCacheService.updateMd5(groupKey, md5, content, System.currentTimeMillis(), ""); ClientTrackService.trackClientMd5(clientIp, groupKey, md5); ClientTrackService.trackClientMd5(clientIp, groupKey, md5); assertTrue(ClientTrackService.isClientUptodate(clientIp).get(groupKey)); assertEquals(1, ClientTrackService.subscribeClientCount()); assertEquals(1, ClientTrackService.subscriberCount()); //服务端数据更新 ConfigCacheService.updateMd5(groupKey, md5 + "111", content, System.currentTimeMillis(), ""); assertFalse(ClientTrackService.isClientUptodate(clientIp).get(groupKey)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/ConfigCacheServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.SimpleReadWriteLock; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.stubbing.OngoingStubbing; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.IOException; import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; 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; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.times; @ExtendWith(SpringExtension.class) class ConfigCacheServiceTest { MockedStatic propertyUtilMockedStatic; MockedStatic configDiskServiceFactoryMockedStatic; @Mock ConfigDiskService configDiskService; MockedStatic envUtilMockedStatic; @BeforeEach void before() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); configDiskServiceFactoryMockedStatic = Mockito.mockStatic(ConfigDiskServiceFactory.class); configDiskServiceFactoryMockedStatic.when(() -> ConfigDiskServiceFactory.getInstance()) .thenReturn(configDiskService); propertyUtilMockedStatic = Mockito.mockStatic(PropertyUtil.class); } @AfterEach void after() { envUtilMockedStatic.close(); propertyUtilMockedStatic.close(); configDiskServiceFactoryMockedStatic.close(); } @Test void testDumpFormal() throws Exception { String dataId = "dataIdtestDumpMd5NewTsNewMd5123"; String group = "group11"; String tenant = "tenant112"; String content = "mockContnet11"; String md5 = "mockmd511"; String groupKey = GroupKey2.getKey(dataId, group, tenant); //make sure not exist prev cache. CacheItem contentCache = ConfigCacheService.getContentCache(groupKey); assertTrue(contentCache == null); long ts = System.currentTimeMillis(); String type = "json"; String encryptedDataKey = "key12345"; boolean result = ConfigCacheService.dumpWithMd5(dataId, group, tenant, content, md5, ts, type, encryptedDataKey); assertTrue(result); //verify cache. CacheItem contentCache1 = ConfigCacheService.getContentCache(groupKey); assertEquals(ts, contentCache1.getConfigCache().getLastModifiedTs()); assertEquals(md5, contentCache1.getConfigCache().getMd5()); assertEquals(type, contentCache1.getType()); assertEquals(encryptedDataKey, contentCache1.getConfigCache().getEncryptedDataKey()); Mockito.verify(configDiskService, times(1)).saveToDisk(eq(dataId), eq(group), eq(tenant), eq(content)); //modified ts and content and md5 String contentNew = content + "11"; long newTs = System.currentTimeMillis() + 12L; ConfigCacheService.dump(dataId, group, tenant, contentNew, newTs, type, encryptedDataKey); //expect save to disk invoked. Mockito.verify(configDiskService, times(1)).saveToDisk(eq(dataId), eq(group), eq(tenant), eq(contentNew)); assertEquals(newTs, contentCache1.getConfigCache().getLastModifiedTs()); String newMd5 = MD5Utils.md5Hex(contentNew, "UTF-8"); assertEquals(newMd5, contentCache1.getConfigCache().getMd5()); //modified ts old long oldTs2 = newTs - 123L; String contentWithOldTs = contentNew + "123456"; ConfigCacheService.dump(dataId, group, tenant, contentWithOldTs, oldTs2, type, encryptedDataKey); //expect save to disk invoked. Mockito.verify(configDiskService, times(0)).saveToDisk(eq(dataId), eq(group), eq(tenant), eq(contentWithOldTs)); //not change ts and md5 assertEquals(newTs, contentCache1.getConfigCache().getLastModifiedTs()); assertEquals(newMd5, contentCache1.getConfigCache().getMd5()); //modified ts new only long newTs2 = newTs + 123L; ConfigCacheService.dump(dataId, group, tenant, contentNew, newTs2, type, encryptedDataKey); assertEquals(newTs2, contentCache1.getConfigCache().getLastModifiedTs()); //save to disk error doThrow(new IOException("No space left on device")).when(configDiskService) .saveToDisk(anyString(), anyString(), anyString(), anyString()); try { long newTs3 = newTs2 + 123L; boolean dumpErrorResult = ConfigCacheService.dump(dataId, group, tenant, contentNew + "234567", newTs3, type, encryptedDataKey); envUtilMockedStatic.verify(() -> EnvUtil.systemExit(), times(1)); assertFalse(dumpErrorResult); } catch (Throwable throwable) { assertFalse(true); } //test remove boolean remove = ConfigCacheService.remove(dataId, group, tenant); assertTrue(remove); Mockito.verify(configDiskService, times(1)).removeConfigInfo(dataId, group, tenant); CacheItem contentCacheAfterRemove = ConfigCacheService.getContentCache(groupKey); assertNull(contentCacheAfterRemove); } @Test public void testDumpGray() throws Exception { String dataId = "dataIdtestDumpBetaNewCache123"; String group = "group11"; String tenant = "tenant112"; String grayName = "grayName"; String grayRule = "{\"type\":\"tag\",\"version\":\"1.0.0\",\"expr\":\"dgray123\",\"priority\":1}"; String content = "mockContent11"; String md5 = MD5Utils.md5Hex(content, "UTF-8"); String groupKey = GroupKey2.getKey(dataId, group, tenant); String encryptedDataKey = "key12345"; long ts = System.currentTimeMillis(); //init gray cache boolean result = ConfigCacheService.dumpGray(dataId, group, tenant, grayName, grayRule, content, ts, encryptedDataKey); assertTrue(result); CacheItem contentCache = ConfigCacheService.getContentCache(groupKey); assertEquals(md5, contentCache.getConfigCacheGray().get(grayName).getMd5()); assertEquals(ts, contentCache.getConfigCacheGray().get(grayName).getLastModifiedTs()); assertEquals(encryptedDataKey, contentCache.getConfigCacheGray().get(grayName).getEncryptedDataKey()); Mockito.verify(configDiskService, times(1)) .saveGrayToDisk(eq(dataId), eq(group), eq(tenant), eq(grayName), eq(content)); //ts newer ,md5 update long tsNew = System.currentTimeMillis(); String contentNew = content + tsNew; String md5New = MD5Utils.md5Hex(contentNew, "UTF-8"); boolean resultNew = ConfigCacheService.dumpGray(dataId, group, tenant, grayName, grayRule, contentNew, tsNew, encryptedDataKey); assertTrue(resultNew); assertEquals(md5New, contentCache.getConfigCacheGray().get(grayName).getMd5()); assertEquals(tsNew, contentCache.getConfigCacheGray().get(grayName).getLastModifiedTs()); assertEquals(encryptedDataKey, contentCache.getConfigCacheGray().get(grayName).getEncryptedDataKey()); Mockito.verify(configDiskService, times(1)) .saveGrayToDisk(eq(dataId), eq(group), eq(tenant), eq(grayName), eq(contentNew)); //ts old ,md5 update long tsOld = tsNew - 1; String contentWithOldTs = "contentWithOldTs" + tsOld; boolean resultOld = ConfigCacheService.dumpGray(dataId, group, tenant, grayName, grayRule, contentWithOldTs, tsOld, encryptedDataKey); assertTrue(resultOld); assertEquals(md5New, contentCache.getConfigCacheGray().get(grayName).getMd5()); assertEquals(tsNew, contentCache.getConfigCacheGray().get(grayName).getLastModifiedTs()); assertEquals(encryptedDataKey, contentCache.getConfigCacheGray().get(grayName).getEncryptedDataKey()); Mockito.verify(configDiskService, times(0)) .saveGrayToDisk(eq(dataId), eq(group), eq(tenant), eq(grayName), eq(contentWithOldTs)); //ts new ,md5 not update,grayRule changes long tsNew2 = tsNew + 1; String grayRuleNew = "{\"type\":\"tag\",\"version\":\"1.0.0\",\"expr\":\"gray1234\",\"priority\":1}"; String contentWithPrev = contentNew; boolean resultNew2 = ConfigCacheService.dumpGray(dataId, group, tenant, grayName, grayRuleNew, contentWithPrev, tsNew2, encryptedDataKey); assertTrue(resultNew2); assertEquals(md5New, contentCache.getConfigCacheGray().get(grayName).getMd5()); assertEquals(tsNew2, contentCache.getConfigCacheGray().get(grayName).getLastModifiedTs()); assertEquals(encryptedDataKey, contentCache.getConfigCacheGray().get(grayName).getEncryptedDataKey()); assertEquals(GrayRuleManager.constructGrayRule(GrayRuleManager.deserializeConfigGrayPersistInfo(grayRuleNew)), contentCache.getConfigCacheGray().get(grayName).getGrayRule()); //ts new only,md5 not update,beta ips not change long tsNew3 = tsNew2 + 1; String contentWithPrev2 = contentNew; String grayRulePrev = grayRuleNew; boolean resultNew3 = ConfigCacheService.dumpGray(dataId, group, tenant, grayName, grayRulePrev, contentWithPrev2, tsNew3, encryptedDataKey); assertTrue(resultNew3); assertEquals(md5New, contentCache.getConfigCacheGray().get(grayName).getMd5()); assertEquals(tsNew3, contentCache.getConfigCacheGray().get(grayName).getLastModifiedTs()); assertEquals(encryptedDataKey, contentCache.getConfigCacheGray().get(grayName).getEncryptedDataKey()); assertEquals(GrayRuleManager.constructGrayRule(GrayRuleManager.deserializeConfigGrayPersistInfo(grayRuleNew)), contentCache.getConfigCacheGray().get(grayName).getGrayRule()); //ts not update,md5 not update,beta ips not change long tsNew4 = tsNew3; String contentWithPrev4 = contentNew; boolean resultNew4 = ConfigCacheService.dumpGray(dataId, group, tenant, grayName, grayRulePrev, contentWithPrev4, tsNew4, encryptedDataKey); assertTrue(resultNew4); assertEquals(md5New, contentCache.getConfigCacheGray().get(grayName).getMd5()); assertEquals(tsNew3, contentCache.getConfigCacheGray().get(grayName).getLastModifiedTs()); assertEquals(encryptedDataKey, contentCache.getConfigCacheGray().get(grayName).getEncryptedDataKey()); assertEquals(GrayRuleManager.constructGrayRule(GrayRuleManager.deserializeConfigGrayPersistInfo(grayRuleNew)), contentCache.getConfigCacheGray().get(grayName).getGrayRule()); //test remove boolean removeBeta = ConfigCacheService.removeGray(dataId, group, tenant, grayName); assertTrue(removeBeta); Mockito.verify(configDiskService, times(1)).removeConfigInfo4Gray(dataId, group, tenant, grayName); Map grayCacheAfterRemove = ConfigCacheService.getContentCache(groupKey) .getConfigCacheGray(); assertNull(grayCacheAfterRemove); } @Test void testTryConfigReadLock() throws Exception { String dataId = "123testTryConfigReadLock"; String group = "1234"; String tenant = "1234"; CacheItem cacheItem = Mockito.mock(CacheItem.class); SimpleReadWriteLock lock = Mockito.mock(SimpleReadWriteLock.class); Mockito.when(cacheItem.getRwLock()).thenReturn(lock); String groupKey = GroupKey2.getKey(dataId, group, tenant); Field cache1 = ConfigCacheService.class.getDeclaredField("CACHE"); cache1.setAccessible(true); ConcurrentHashMap cache = (ConcurrentHashMap) cache1.get(null); cache.put(groupKey, cacheItem); // lock ==0,not exist int readLock = ConfigCacheService.tryConfigReadLock(groupKey + "3245"); assertEquals(0, readLock); //lock == 1 , success get lock Mockito.when(lock.tryReadLock()).thenReturn(true); int readLockSuccess = ConfigCacheService.tryConfigReadLock(groupKey); assertEquals(1, readLockSuccess); //lock ==-1 fail after spin all times; OngoingStubbing when = Mockito.when(lock.tryReadLock()); for (int i = 0; i < 10; i++) { when = when.thenReturn(false); } int readLockFail = ConfigCacheService.tryConfigReadLock(groupKey); assertEquals(-1, readLockFail); //lock ==1 success after serval spin times; OngoingStubbing when2 = Mockito.when(lock.tryReadLock()); for (int i = 0; i < 5; i++) { when2 = when2.thenReturn(false); } when2.thenReturn(true); int readLockSuccessAfterRetry = ConfigCacheService.tryConfigReadLock(groupKey); assertEquals(1, readLockSuccessAfterRetry); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/ConfigChangePublisherTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.notify.listener.Subscriber; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.persistence.configuration.DatasourceConfiguration; import com.alibaba.nacos.sys.env.EnvUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; class ConfigChangePublisherTest { @BeforeEach void startUP() { EnvUtil.setIsStandalone(true); DatasourceConfiguration.setEmbeddedStorage(true); } @Test void testConfigChangeNotify() throws InterruptedException { AtomicReference reference = new AtomicReference<>(); NotifyCenter.registerToPublisher(ConfigDataChangeEvent.class, NotifyCenter.ringBufferSize); NotifyCenter.registerSubscriber(new Subscriber() { @Override public void onEvent(Event event) { reference.set((ConfigDataChangeEvent) event); } @Override public Class subscribeType() { return ConfigDataChangeEvent.class; } }); // nacos is standalone mode and use embedded storage EnvUtil.setIsStandalone(true); DatasourceConfiguration.setEmbeddedStorage(true); ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent("chuntaojun", "chuntaojun", null, System.currentTimeMillis())); Thread.sleep(2000); assertNotNull(reference.get()); reference.set(null); // nacos is standalone mode and use external storage EnvUtil.setIsStandalone(true); DatasourceConfiguration.setEmbeddedStorage(false); ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent("chuntaojun", "chuntaojun", null, System.currentTimeMillis())); Thread.sleep(2000); assertNotNull(reference.get()); reference.set(null); // nacos is cluster mode and use embedded storage EnvUtil.setIsStandalone(false); DatasourceConfiguration.setEmbeddedStorage(true); ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent("chuntaojun", "chuntaojun", null, System.currentTimeMillis())); Thread.sleep(2000); assertNull(reference.get()); reference.set(null); // nacos is cluster mode and use external storage EnvUtil.setIsStandalone(false); DatasourceConfiguration.setEmbeddedStorage(false); ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent("chuntaojun", "chuntaojun", null, System.currentTimeMillis())); Thread.sleep(2000); assertNotNull(reference.get()); reference.set(null); } @AfterEach void tearDown() { EnvUtil.setIsStandalone(true); DatasourceConfiguration.setEmbeddedStorage(true); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/ConfigFuzzyWatchContextServiceTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.FuzzyGroupKeyPattern; import com.alibaba.nacos.config.server.configuration.ConfigCommonConfig; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.sys.env.EnvUtil; 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.MockedStatic; import org.mockito.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.Set; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.ADD_CONFIG; import static com.alibaba.nacos.api.common.Constants.ConfigChangedType.DELETE_CONFIG; import static com.alibaba.nacos.api.model.v2.ErrorCode.FUZZY_WATCH_PATTERN_OVER_LIMIT; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) public class ConfigFuzzyWatchContextServiceTest { MockedStatic envUtilMockedStatic; MockedStatic configCommonConfigMockedStatic; private static int mocMaxPattern = 5; private static int mocMaxPatternConfigCount = 10; /** * before. */ @BeforeEach public void before() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("nacos.config.cache.type"), anyString())) .thenReturn("nacos"); configCommonConfigMockedStatic = Mockito.mockStatic(ConfigCommonConfig.class); ConfigCommonConfig configCommonConfig = Mockito.mock(ConfigCommonConfig.class); when(configCommonConfig.getMaxPatternCount()).thenReturn(mocMaxPattern); when(configCommonConfig.getMaxMatchedConfigCount()).thenReturn(mocMaxPatternConfigCount); configCommonConfigMockedStatic.when(() -> ConfigCommonConfig.getInstance()).thenReturn(configCommonConfig); } @AfterEach public void after() { envUtilMockedStatic.close(); configCommonConfigMockedStatic.close(); } @Test public void testTrimFuzzyWatchContext() throws NacosException { ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); String groupKey = GroupKey.getKeyTenant("data124", "group", "12345"); //init String collectionId = "id"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); //test Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); Assertions.assertTrue(matchedClients.size() == 1); Set notMatchedClients = configFuzzyWatchContextService.getMatchedClients( GroupKey.getKeyTenant("da124", "group", "12345")); Assertions.assertTrue(notMatchedClients.size() == 0); Set matchedGroupKeys = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); Assertions.assertTrue(matchedGroupKeys.size() > 0); Assertions.assertTrue(matchedGroupKeys.contains(groupKey)); // remove connection is watch configFuzzyWatchContextService.clearFuzzyWatchContext(collectionId); //trim once, matchedClients2 is empty,matchedGroupKeys2 is not empty configFuzzyWatchContextService.trimFuzzyWatchContext(); Set matchedClients2 = configFuzzyWatchContextService.getMatchedClients(groupKey); Assertions.assertTrue(matchedClients2 != null && matchedClients2.isEmpty()); Set matchedGroupKeys2 = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); Assertions.assertTrue(matchedGroupKeys2 != null && matchedGroupKeys2.contains(groupKey)); //trim twice, matchedGroupKeys2 is empty configFuzzyWatchContextService.trimFuzzyWatchContext(); Set matchedGroupKeys3 = configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern); Assertions.assertTrue(matchedGroupKeys3.isEmpty()); } @Test public void testSyncGroupKeyContext() throws NacosException { ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); //init String collectionId = "id"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); String keyTenant = GroupKey.getKeyTenant("data1245", "group", "12345"); boolean needNotify1 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, ADD_CONFIG); Assertions.assertTrue(needNotify1); boolean needNotify2 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, ADD_CONFIG); Assertions.assertFalse(needNotify2); boolean needNotify3 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, DELETE_CONFIG); Assertions.assertTrue(needNotify3); boolean needNotify4 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, DELETE_CONFIG); Assertions.assertFalse(needNotify4); } @Test public void testMakeupGroupKeyContext() throws NacosException { ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); //init String collectionId = "id"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); for (int i = 0; i <= mocMaxPatternConfigCount; i++) { String keyTenant = GroupKey.getKeyTenant("data1" + i, "group", "12345"); boolean needNotify1 = configFuzzyWatchContextService.syncGroupKeyContext(keyTenant, ADD_CONFIG); Assertions.assertEquals(i < mocMaxPatternConfigCount ? true : false, needNotify1); } String overLimitKey = GroupKey.getKeyTenant("data1" + mocMaxPatternConfigCount, "group", "12345"); Assertions.assertFalse(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern).contains(overLimitKey)); //sync init cache service ConfigCacheService.dump("data1" + mocMaxPatternConfigCount, "group", "12345", "content", System.currentTimeMillis(), null, null); String deletedKey = GroupKey.getKeyTenant("data1" + 0, "group", "12345"); configFuzzyWatchContextService.syncGroupKeyContext(deletedKey, DELETE_CONFIG); Assertions.assertTrue(configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern).contains(overLimitKey)); } @Test public void testInitGroupKeyContext() throws NacosException { ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); String dataIdPrefix = "testinitD"; // init config for (int i = 0; i <= mocMaxPatternConfigCount; i++) { ConfigCacheService.dump(dataIdPrefix + i, "group", "12345", "content", System.currentTimeMillis(), null, null); } String collectionId = "id"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern(dataIdPrefix + "*", "group", "12345"); // test init config configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); Assertions.assertEquals(mocMaxPatternConfigCount, configFuzzyWatchContextService.matchGroupKeys(groupKeyPattern).size()); for (int i = 1; i < mocMaxPattern; i++) { String groupKeyPattern0 = FuzzyGroupKeyPattern.generatePattern(dataIdPrefix + "*" + i, "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern0, collectionId); } try { String groupKeyPatternOver = FuzzyGroupKeyPattern.generatePattern(dataIdPrefix + "*" + mocMaxPattern, "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPatternOver, collectionId); Assertions.assertTrue(false); } catch (NacosException nacosException) { Assertions.assertEquals(FUZZY_WATCH_PATTERN_OVER_LIMIT.getCode(), nacosException.getErrCode()); Assertions.assertEquals(FUZZY_WATCH_PATTERN_OVER_LIMIT.getMsg(), nacosException.getErrMsg()); } } @Test public void testFuzzyWatch() throws NacosException { ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); //init String collectionId = "id"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); String groupKey = GroupKey.getKeyTenant("data1245", "group", "12345"); boolean needNotify = configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); Assertions.assertTrue(needNotify); Set matchedClients1 = configFuzzyWatchContextService.getMatchedClients(groupKey); Assertions.assertTrue(matchedClients1.contains(collectionId)); configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, collectionId); Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); Assertions.assertTrue(CollectionUtils.isEmpty(matchedClients)); } @Test public void testFuzzyWatchOverLimit() throws NacosException { ConfigFuzzyWatchContextService configFuzzyWatchContextService = new ConfigFuzzyWatchContextService(); //init String collectionId = "id"; String groupKeyPattern = FuzzyGroupKeyPattern.generatePattern("data*", "group", "12345"); configFuzzyWatchContextService.addFuzzyWatch(groupKeyPattern, collectionId); String groupKey = GroupKey.getKeyTenant("data1245", "group", "12345"); boolean needNotify = configFuzzyWatchContextService.syncGroupKeyContext(groupKey, ADD_CONFIG); Assertions.assertTrue(needNotify); configFuzzyWatchContextService.removeFuzzyListen(groupKeyPattern, collectionId); Set matchedClients = configFuzzyWatchContextService.getMatchedClients(groupKey); Assertions.assertTrue(CollectionUtils.isEmpty(matchedClients)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/ConfigOperationServiceTest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.core.env.StandardEnvironment; import org.springframework.dao.DataIntegrityViolationException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyMap; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * ConfigServiceTest. * * @author dongyafei * @date 2022/8/11 */ @ExtendWith(MockitoExtension.class) class ConfigOperationServiceTest { private ConfigOperationService configOperationService; @Mock private ConfigInfoPersistService configInfoPersistService; @Mock private ConfigInfoGrayPersistService configInfoGrayPersistService; @Mock ConfigMigrateService configMigrateService; @BeforeEach void setUp() throws Exception { EnvUtil.setEnvironment(new StandardEnvironment()); this.configOperationService = new ConfigOperationService(configInfoPersistService, configInfoGrayPersistService, configMigrateService); } @Test void testPublishConfigBeta() throws NacosException { ConfigForm configForm = new ConfigForm(); configForm.setDataId("test"); configForm.setGroup("test"); configForm.setContent("test content"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setCasMd5(""); configForm.setTag(""); // if betaIps is not blank and casMd5 is blank configRequestInfo.setBetaIps("test-betaIps"); when(configInfoGrayPersistService.insertOrUpdateGray(any(ConfigInfo.class), eq("beta"), anyString(), eq(configRequestInfo.getSrcIp()), eq(configForm.getSrcUser()))).thenReturn(new ConfigOperateResult()); Boolean eResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); assertTrue(eResult); } @Test void testPublishConfigBetaCas() throws NacosException { ConfigForm configForm = new ConfigForm(); configForm.setDataId("test"); configForm.setGroup("test"); configForm.setContent("test content"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setCasMd5("casMd5"); configForm.setTag(""); // if betaIps is not blank and casMd5 is not blank configRequestInfo.setBetaIps("test-betaIps"); configRequestInfo.setCasMd5("test casMd5"); when(configInfoGrayPersistService.insertOrUpdateGrayCas(any(ConfigInfo.class), eq("beta"), anyString(), eq(configRequestInfo.getSrcIp()), eq(configForm.getSrcUser()))).thenReturn(new ConfigOperateResult()); Boolean fResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); assertTrue(fResult); } @Test void testPublishConfigBetaCasWithMd5Set() throws NacosException { ConfigForm configForm = new ConfigForm(); configForm.setDataId("test"); configForm.setGroup("test"); configForm.setContent("test content"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); String expectedCasMd5 = "test-cas-md5-value"; configRequestInfo.setBetaIps("test-betaIps"); configRequestInfo.setCasMd5(expectedCasMd5); configForm.setTag(""); // Use ArgumentCaptor to capture the ConfigInfo object passed to insertOrUpdateGrayCas ArgumentCaptor configInfoCaptor = ArgumentCaptor.forClass(ConfigInfo.class); when(configInfoGrayPersistService.insertOrUpdateGrayCas(configInfoCaptor.capture(), eq("beta"), anyString(), eq(configRequestInfo.getSrcIp()), eq(configForm.getSrcUser()))).thenReturn(new ConfigOperateResult()); Boolean result = configOperationService.publishConfig(configForm, configRequestInfo, ""); assertTrue(result); // Verify that the md5 field of ConfigInfo is correctly set to the casMd5 value ConfigInfo capturedConfigInfo = configInfoCaptor.getValue(); assertEquals(expectedCasMd5, capturedConfigInfo.getMd5(), "ConfigInfo's md5 should be set to casMd5 value"); assertEquals("test", capturedConfigInfo.getDataId()); assertEquals("test", capturedConfigInfo.getGroup()); assertEquals("test content", capturedConfigInfo.getContent()); } @Test void testPublishConfigTag() throws NacosException { ConfigForm configForm = new ConfigForm(); configForm.setDataId("test"); configForm.setGroup("test"); configForm.setContent("test content"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setCasMd5(""); String tag = "testTag"; configForm.setTag(tag); when(configInfoGrayPersistService.insertOrUpdateGray(any(ConfigInfo.class), eq("tag_" + tag), anyString(), eq(configRequestInfo.getSrcIp()), eq(configForm.getSrcUser()))).thenReturn(new ConfigOperateResult()); Boolean cResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); assertTrue(cResult); } @Test void testPublishConfigTagCas() throws NacosException { ConfigForm configForm = new ConfigForm(); configForm.setDataId("test"); configForm.setGroup("test"); configForm.setContent("test content"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setCasMd5("casMd5"); String tag = "testTag"; configForm.setTag(tag); when(configInfoGrayPersistService.insertOrUpdateGrayCas(any(ConfigInfo.class), eq("tag_" + tag), anyString(), eq(configRequestInfo.getSrcIp()), eq(configForm.getSrcUser()))).thenReturn(new ConfigOperateResult()); Boolean dResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); assertTrue(dResult); } @Test void testPublishConfigTagCasWithMd5Set() throws NacosException { ConfigForm configForm = new ConfigForm(); configForm.setDataId("test"); configForm.setGroup("test"); configForm.setContent("test content"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); String expectedCasMd5 = "test-cas-md5-value"; String tag = "testTag"; configRequestInfo.setCasMd5(expectedCasMd5); configForm.setTag(tag); // Use ArgumentCaptor to capture the ConfigInfo object passed to insertOrUpdateGrayCas ArgumentCaptor configInfoCaptor = ArgumentCaptor.forClass(ConfigInfo.class); when(configInfoGrayPersistService.insertOrUpdateGrayCas(configInfoCaptor.capture(), eq("tag_" + tag), anyString(), eq(configRequestInfo.getSrcIp()), eq(configForm.getSrcUser()))).thenReturn(new ConfigOperateResult()); Boolean result = configOperationService.publishConfig(configForm, configRequestInfo, ""); assertTrue(result); // Verify that the md5 field of ConfigInfo is correctly set to the casMd5 value ConfigInfo capturedConfigInfo = configInfoCaptor.getValue(); assertEquals(expectedCasMd5, capturedConfigInfo.getMd5(), "ConfigInfo's md5 should be set to casMd5 value"); assertEquals("test", capturedConfigInfo.getDataId()); assertEquals("test", capturedConfigInfo.getGroup()); assertEquals("test content", capturedConfigInfo.getContent()); } @Test void testPublishConfig() throws NacosException { ConfigForm configForm = new ConfigForm(); configForm.setDataId("test"); configForm.setGroup("test"); configForm.setContent("test content"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); // if betaIps is blank, tag is blank and casMd5 is blank when(configInfoPersistService.insertOrUpdate(any(), any(), any(ConfigInfo.class), any())).thenReturn( new ConfigOperateResult()); Boolean aResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); verify(configInfoPersistService).insertOrUpdate(any(), any(), any(ConfigInfo.class), any()); assertTrue(aResult); // if betaIps is blank, tag is blank and casMd5 is not blank configRequestInfo.setCasMd5("test casMd5"); when(configInfoPersistService.insertOrUpdateCas(any(), any(), any(ConfigInfo.class), any())).thenReturn( new ConfigOperateResult()); Boolean bResult = configOperationService.publishConfig(configForm, configRequestInfo, ""); verify(configInfoPersistService).insertOrUpdateCas(any(), any(), any(ConfigInfo.class), any()); assertTrue(bResult); configRequestInfo.setCasMd5(""); } @Test void testUpdateForExistTrue() throws Exception { ConfigForm configForm = new ConfigForm(); configForm.setDataId("testDataId"); configForm.setGroup("testGroup"); configForm.setNamespaceId("testNamespaceId"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setSrcType("http"); configRequestInfo.setSrcIp("1.1.1.1"); when(configInfoPersistService.insertOrUpdate(anyString(), isNull(), any(ConfigInfo.class), anyMap())) .thenReturn(new ConfigOperateResult(true)); Boolean result = configOperationService.publishConfig(configForm, configRequestInfo, "encryptedKey"); assertTrue(result); verify(configInfoPersistService, times(1)).insertOrUpdate(anyString(), isNull(), any(ConfigInfo.class), anyMap()); } @Test void testAddConfigInfoSuccess() throws Exception { ConfigForm configForm = new ConfigForm(); configForm.setDataId("testDataId"); configForm.setGroup("testGroup"); configForm.setNamespaceId("testNamespaceId"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setSrcType("http"); configRequestInfo.setSrcIp("1.1.1.1"); configRequestInfo.setUpdateForExist(false); when(configInfoPersistService.addConfigInfo(anyString(), isNull(), any(ConfigInfo.class), anyMap())) .thenReturn(new ConfigOperateResult(true)); Boolean result = configOperationService.publishConfig(configForm, configRequestInfo, "encryptedKey"); assertTrue(result); verify(configInfoPersistService, times(1)).addConfigInfo(anyString(), isNull(), any(ConfigInfo.class), anyMap()); } @Test void testAddConfigInfoThrowsException() { ConfigForm configForm = new ConfigForm(); configForm.setDataId("testDataId"); configForm.setGroup("testGroup"); configForm.setNamespaceId("testNamespaceId"); ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setSrcType("http"); configRequestInfo.setSrcIp("1.1.1.1"); configRequestInfo.setUpdateForExist(false); ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId("testDataId"); configInfo.setGroup("testGroup"); configInfo.setTenant("testNamespaceId"); when(configInfoPersistService.addConfigInfo(eq("1.1.1.1"), isNull(), eq(configInfo), anyMap())) .thenThrow(new DataIntegrityViolationException("Duplicate entry")); NacosException exception = assertThrows(NacosException.class, () -> { configOperationService.publishConfig(configForm, configRequestInfo, "encryptedKey"); }); String expectedMessage = "config already exist, dataId: testDataId, group: testGroup, namespaceId: testNamespaceId"; assertEquals(expectedMessage, exception.getMessage()); verify(configInfoPersistService, times(1)).addConfigInfo(anyString(), isNull(), eq(configInfo), anyMap()); } @Test void testDeleteConfig() { // if tag is blank Boolean aResult = configOperationService.deleteConfig("test", "test", "", "", "1.1.1.1", "test", "http"); verify(configInfoPersistService).removeConfigInfo(eq("test"), eq("test"), eq(""), any(), any()); assertTrue(aResult); // if tag is not blank Boolean bResult = configOperationService.deleteConfig("test", "test", "", "test", "1.1.1.1", "test", "http"); assertTrue(bResult); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/ConfigSubServiceTest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.common.http.HttpRestResult; import com.alibaba.nacos.common.http.client.NacosAsyncRestTemplate; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.common.http.param.Header; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.config.server.model.ListenerCheckResult; import com.alibaba.nacos.config.server.model.SampleResult; import com.alibaba.nacos.config.server.service.notify.HttpClientManager; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CompletionService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(SpringExtension.class) class ConfigSubServiceTest { @Mock ServerMemberManager serverMemberManager; MockedStatic envUtilMockedStatic; MockedStatic httpClientManagerMockedStatic; @Mock NacosRestTemplate nacosRestTemplate; @Mock NacosAsyncRestTemplate nacosAsyncRestTemplate; private ConfigSubService configSubService; @BeforeEach void startUP() { httpClientManagerMockedStatic = Mockito.mockStatic(HttpClientManager.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); configSubService = new ConfigSubService(serverMemberManager); envUtilMockedStatic.when(() -> EnvUtil.getContextPath()).thenReturn("/nacos"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), anyString())).thenReturn("mock string"); EnvUtil.setContextPath("/nacos"); httpClientManagerMockedStatic.when(() -> HttpClientManager.getNacosRestTemplate()) .thenReturn(nacosRestTemplate); httpClientManagerMockedStatic.when(() -> HttpClientManager.getNacosAsyncRestTemplate()) .thenReturn(nacosAsyncRestTemplate); } @AfterEach void after() { if (!envUtilMockedStatic.isClosed()) { envUtilMockedStatic.close(); } httpClientManagerMockedStatic.close(); } @Test void testGetCollectSampleResult() throws Exception { envUtilMockedStatic.close(); EnvUtil.setContextPath("/nacos"); ConfigurableEnvironment environment = Mockito.mock(ConfigurableEnvironment.class); EnvUtil.setEnvironment(environment); Mockito.when(environment.getProperty(anyString(), anyString())).thenReturn("/nacos"); Map mockedMembers = new HashMap<>(); mockedMembers.put("127.0.0.1", createMember("127.0.0.1")); mockedMembers.put("127.0.0.2", createMember("127.0.0.2")); mockedMembers.put("127.0.0.3", createMember("127.0.0.3")); //mock server member Mockito.when(serverMemberManager.allMembers()).thenReturn(mockedMembers.values()); Mockito.when(serverMemberManager.getServerList()).thenReturn(mockedMembers); String dataId = "dataid1234"; String group = "group34567"; String tenant = "tenant456789"; int sampleTimes = 3; //cant mock static method cross thread,so cant verify return obj here. configSubService.getCollectSampleResult(dataId, group, tenant, sampleTimes); configSubService.getCollectSampleResultByIp("127.0.0.1", 3); } @Test void testRunSingleJob() throws Exception { Map params = new HashMap<>(); params.put("dataId", "d1"); params.put("group", "g1"); params.put("tenant", "t1"); HttpRestResult httpRestResult = new HttpRestResult<>(); httpRestResult.setCode(200); httpRestResult.setMessage("success"); SampleResult sampleResult1 = new SampleResult(); Map listener1 = new HashMap<>(); listener1.put("config1", "md51123"); listener1.put("config11", "md5123123"); sampleResult1.setLisentersGroupkeyStatus(listener1); String mockJsonString = JacksonUtils.toJson(sampleResult1); httpRestResult.setData(mockJsonString); //mock success Mockito.when(nacosRestTemplate.get(anyString(), any(Header.class), eq(Query.EMPTY), eq(String.class))) .thenReturn(httpRestResult); String url = "url"; SampleResult returnObj = (SampleResult) ConfigSubService.runSingleJob("127.0.0.1", params, url, SampleResult.class); assertEquals(sampleResult1.getLisentersGroupkeyStatus(), returnObj.getLisentersGroupkeyStatus()); //mock fail response httpRestResult.setCode(500); Mockito.when(nacosRestTemplate.get(anyString(), any(Header.class), eq(Query.EMPTY), eq(String.class))) .thenReturn(httpRestResult); SampleResult returnObj500 = (SampleResult) ConfigSubService.runSingleJob("127.0.0.1", params, url, SampleResult.class); assertNull(returnObj500); //mock get url throw exception Mockito.when(nacosRestTemplate.get(anyString(), any(Header.class), eq(Query.EMPTY), eq(String.class))) .thenThrow(new NacosRuntimeException(500, "timeout")); SampleResult returnObjTimeout = (SampleResult) ConfigSubService.runSingleJob("127.0.0.1", params, url, SampleResult.class); assertNull(returnObjTimeout); } @Test void testClusterListenerJob() throws Exception { Map mockedMembers = new HashMap<>(); mockedMembers.put("127.0.0.1", createMember("127.0.0.1")); mockedMembers.put("127.0.0.2", createMember("127.0.0.2")); mockedMembers.put("127.0.0.3", createMember("127.0.0.3")); //mock server member Mockito.when(serverMemberManager.allMembers()).thenReturn(mockedMembers.values()); Mockito.when(serverMemberManager.getServerList()).thenReturn(mockedMembers); CompletionService mockService = Mockito.mock(CompletionService.class); //mock all success Mockito.when(mockService.poll(anyLong(), any(TimeUnit.class))) .thenReturn(createSampleResultFuture(true, true), createSampleResultFuture(true, true), createSampleResultFuture(true, true)); Map params = new HashMap<>(); ConfigSubService.ClusterListenerJob clusterListenerJob = new ConfigSubService.ClusterListenerJob(params, mockService, serverMemberManager); List sampleResults = clusterListenerJob.runJobs(); assertEquals(3, sampleResults.size()); //mock success with exception Mockito.when(mockService.poll(anyLong(), any(TimeUnit.class))) .thenReturn(createSampleResultFuture(true, true), createSampleResultFuture(false, false)) .thenThrow(new NacosRuntimeException(500, "13")); Map params2 = new HashMap<>(); ConfigSubService.ClusterListenerJob clusterListenerJob2 = new ConfigSubService.ClusterListenerJob(params2, mockService, serverMemberManager); List sampleResults2 = clusterListenerJob2.runJobs(); assertEquals(1, sampleResults2.size()); assertFalse(sampleResults2.get(0).getLisentersGroupkeyStatus().isEmpty()); } @Test void testMergeSampleResult() throws Exception { SampleResult sampleResult1 = new SampleResult(); Map listener1 = new HashMap<>(); listener1.put("config1", "md51123"); listener1.put("config11", "md5123123"); sampleResult1.setLisentersGroupkeyStatus(listener1); SampleResult sampleResult2 = new SampleResult(); Map listener2 = new HashMap<>(); listener2.put("config22", "md51123"); listener2.put("config2", "md5123123"); sampleResult2.setLisentersGroupkeyStatus(listener2); List sampleResults = new ArrayList<>(); sampleResults.add(sampleResult2); SampleResult sampleResult3 = new SampleResult(); Map listener3 = new HashMap<>(); listener3.put("config33", "md51123"); listener3.put("config3", "md5123123"); sampleResult3.setLisentersGroupkeyStatus(listener3); sampleResults.add(sampleResult3); //sampleResult ips is null SampleResult sampleResultMerge1 = configSubService.mergeSampleResult(sampleResult1, sampleResults); assertEquals(6, sampleResultMerge1.getLisentersGroupkeyStatus().size()); SampleResult sampleResultMerge2 = configSubService.mergeSampleResult(new SampleResult(), sampleResults); assertEquals(4, sampleResultMerge2.getLisentersGroupkeyStatus().size()); } @Test void testMergeListenerCheckResult() throws Exception { ListenerCheckResult sampleResult2 = new ListenerCheckResult(); sampleResult2.setHasListener(true); sampleResult2.setCode(200); List sampleResults = new ArrayList(); sampleResults.add(sampleResult2); ListenerCheckResult sampleResult3 = new ListenerCheckResult(); sampleResult3.setHasListener(false); sampleResult3.setCode(200); sampleResults.add(sampleResult3); ListenerCheckResult sampleResult1 = new ListenerCheckResult(); //one ip return true ListenerCheckResult sampleResultMerge1 = configSubService.mergeListenerCheckResult(sampleResult1, sampleResults, 2); assertEquals(200, sampleResultMerge1.getCode()); assertTrue(sampleResultMerge1.isHasListener()); //all ip return false,but not equals member size sampleResult2.setHasListener(false); sampleResult3.setHasListener(false); sampleResult1.setHasListener(false); ListenerCheckResult sampleResultMerge2 = configSubService.mergeListenerCheckResult(sampleResult1, sampleResults, 3); assertEquals(201, sampleResultMerge2.getCode()); assertFalse(sampleResultMerge2.isHasListener()); } private Future createSampleResultFuture(boolean success, boolean lisentersGroupkeyStatus) { Future future = new Future() { @Override public boolean cancel(boolean mayInterruptIfRunning) { return false; } @Override public boolean isCancelled() { return false; } @Override public boolean isDone() { return success ? true : false; } @Override public SampleResult get() { return success ? createSampleResult() : null; } @Override public SampleResult get(long timeout, TimeUnit unit) { return success ? createSampleResult() : null; } SampleResult createSampleResult() { SampleResult sampleResult = new SampleResult(); if (lisentersGroupkeyStatus) { Map listener = new HashMap<>(); listener.put("config1", "md51123"); listener.put("config2", "md5123123"); sampleResult.setLisentersGroupkeyStatus(listener); } return sampleResult; } }; return future; } Member createMember(String ip) { Member member = new Member(); member.setIp(ip); member.setPort(8848); return member; } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/HistoryServiceTest.java ================================================ /* * Copyright 1999-2022 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.enums.OperationType; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigHistoryInfoDetail; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.api.model.Page; 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.junit.jupiter.MockitoExtension; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; /** * HistoryServiceTest. * * @author dongyafei * @date 2022/8/11 */ @ExtendWith(MockitoExtension.class) class HistoryServiceTest { private static final String TEST_DATA_ID = "test"; private static final String TEST_GROUP = "test"; private static final String TEST_TENANT = ""; private static final String TEST_CONTENT = "test config"; private static final String TEST_UPDATED_CONTENT = "test config updated"; private static final String TEST_OP_TYPE = OperationType.UPDATE.getValue(); private static final String TEST_MD5 = "77963b7a931377ad4ab5ad6a9cd718aa"; private static final String TEST_UPDATED_MD5 = "3ba1e44fa18519221f6c70afc0e8ae84"; private HistoryService historyService; @Mock private HistoryConfigInfoPersistService historyConfigInfoPersistService; @Mock private ConfigInfoPersistService configInfoPersistService; @Mock private ConfigInfoGrayPersistService configInfoGrayPersistService; @BeforeEach void setUp() throws Exception { this.historyService = new HistoryService(historyConfigInfoPersistService, configInfoPersistService, configInfoGrayPersistService); } @Test void testListConfigHistory() { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); List configHistoryInfoList = new ArrayList<>(); configHistoryInfoList.add(configHistoryInfo); Page page = new Page<>(); page.setTotalCount(15); page.setPageNumber(1); page.setPagesAvailable(2); page.setPageItems(configHistoryInfoList); when(historyConfigInfoPersistService.findConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1, 10)).thenReturn(page); Page pageResult = historyService.listConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1, 10); verify(historyConfigInfoPersistService).findConfigHistory(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1, 10); List resultList = pageResult.getPageItems(); ConfigHistoryInfo resConfigHistoryInfo = resultList.get(0); assertEquals(configHistoryInfoList.size(), resultList.size()); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); } @Test void testGetConfigHistoryInfo() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setTenant(TEST_TENANT); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyConfigInfoPersistService.detailConfigHistory(1L)).thenReturn(configHistoryInfo); ConfigHistoryInfo resConfigHistoryInfo = historyService.getConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1L); verify(historyConfigInfoPersistService).detailConfigHistory(1L); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); } @Test void testGetPreviousConfigHistoryInfo() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setTenant(TEST_TENANT); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyConfigInfoPersistService.detailPreviousConfigHistory(1L)).thenReturn(configHistoryInfo); ConfigHistoryInfo resConfigHistoryInfo = historyService.getPreviousConfigHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1L); verify(historyConfigInfoPersistService).detailPreviousConfigHistory(1L); assertEquals(configHistoryInfo.getDataId(), resConfigHistoryInfo.getDataId()); assertEquals(configHistoryInfo.getGroup(), resConfigHistoryInfo.getGroup()); assertEquals(configHistoryInfo.getContent(), resConfigHistoryInfo.getContent()); } @Test void testGetConfigListByNamespace() { ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); configInfoWrapper.setDataId("test"); configInfoWrapper.setGroup("test"); configInfoWrapper.setContent("test"); List configInfoWrappers = Collections.singletonList(configInfoWrapper); when(configInfoPersistService.queryConfigInfoByNamespace("test")).thenReturn(configInfoWrappers); List actualList = historyService.getConfigListByNamespace("test"); verify(configInfoPersistService).queryConfigInfoByNamespace("test"); assertEquals(configInfoWrappers.size(), actualList.size()); ConfigInfoWrapper actualConfigInfoWrapper = actualList.get(0); assertEquals(configInfoWrapper.getDataId(), actualConfigInfoWrapper.getDataId()); assertEquals(configInfoWrapper.getGroup(), actualConfigInfoWrapper.getGroup()); assertEquals(configInfoWrapper.getContent(), actualConfigInfoWrapper.getContent()); } @Test void testGetConfigHistoryInfoPair() throws Exception { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setDataId(TEST_DATA_ID); configHistoryInfo.setGroup(TEST_GROUP); configHistoryInfo.setContent(TEST_CONTENT); configHistoryInfo.setTenant(TEST_TENANT); configHistoryInfo.setOpType(TEST_OP_TYPE); configHistoryInfo.setMd5(TEST_MD5); configHistoryInfo.setPublishType(Constants.FORMAL); configHistoryInfo.setGrayName(StringUtils.EMPTY); configHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); configHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyConfigInfoPersistService.detailConfigHistory(1L)).thenReturn(configHistoryInfo); ConfigHistoryInfo nextHistoryInfo = new ConfigHistoryInfo(); nextHistoryInfo.setDataId(TEST_DATA_ID); nextHistoryInfo.setGroup(TEST_GROUP); nextHistoryInfo.setTenant(TEST_TENANT); nextHistoryInfo.setOpType(TEST_OP_TYPE); nextHistoryInfo.setMd5(TEST_UPDATED_MD5); nextHistoryInfo.setContent(TEST_UPDATED_CONTENT); nextHistoryInfo.setPublishType(Constants.FORMAL); nextHistoryInfo.setGrayName(StringUtils.EMPTY); nextHistoryInfo.setCreatedTime(new Timestamp(new Date().getTime())); nextHistoryInfo.setLastModifiedTime(new Timestamp(new Date().getTime())); when(historyConfigInfoPersistService.getNextHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, Constants.FORMAL, StringUtils.EMPTY, 1L)).thenReturn(nextHistoryInfo); ConfigHistoryInfoDetail resConfigHistoryInfoDetail = historyService.getConfigHistoryInfoDetail(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, 1L); verify(historyConfigInfoPersistService).getNextHistoryInfo(TEST_DATA_ID, TEST_GROUP, TEST_TENANT, Constants.FORMAL, StringUtils.EMPTY, 1L); assertEquals(nextHistoryInfo.getDataId(), resConfigHistoryInfoDetail.getDataId()); assertEquals(nextHistoryInfo.getGroup(), resConfigHistoryInfoDetail.getGroup()); assertEquals(nextHistoryInfo.getMd5(), resConfigHistoryInfoDetail.getUpdatedMd5()); assertEquals(nextHistoryInfo.getContent(), resConfigHistoryInfoDetail.getUpdatedContent()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/LongPollingServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.model.SampleResult; import com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.MD5Util; import com.alibaba.nacos.plugin.control.ControlManagerCenter; import com.alibaba.nacos.plugin.control.connection.ConnectionControlManager; import com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckResponse; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoSettings; import org.mockito.quality.Strictness; import jakarta.servlet.AsyncContext; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.Collections; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; @ExtendWith(MockitoExtension.class) // todo remove this @MockitoSettings(strictness = Strictness.LENIENT) class LongPollingServiceTest { LongPollingService longPollingService; MockedStatic configCacheServiceMockedStatic; MockedStatic configExecutorMocked; MockedStatic connectionControlManagerMockedStatic; @Mock ControlManagerCenter controlManagerCenter; @Mock ConnectionControlManager connectionControlManager; MockedStatic switchServiceMockedStatic; MockedStatic envUtilMockedStatic; @BeforeEach void before() { longPollingService = new LongPollingService(); switchServiceMockedStatic = Mockito.mockStatic(SwitchService.class); configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); configExecutorMocked = Mockito.mockStatic(ConfigExecutor.class); connectionControlManagerMockedStatic = Mockito.mockStatic(ControlManagerCenter.class); connectionControlManagerMockedStatic.when(() -> ControlManagerCenter.getInstance()).thenReturn(controlManagerCenter); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")).thenReturn("nacos"); Mockito.when(controlManagerCenter.getConnectionControlManager()).thenReturn(connectionControlManager); } @AfterEach void after() { configCacheServiceMockedStatic.close(); if (!configExecutorMocked.isClosed()) { configExecutorMocked.close(); } connectionControlManagerMockedStatic.close(); switchServiceMockedStatic.close(); envUtilMockedStatic.close(); } @Test void testAddLongPollingClientHasNotEqualsMd5() throws IOException { Map clientMd5Map = new HashMap<>(); String group = "group"; String tenant = "tenat"; String dataIdEquals = "dataIdEquals0"; String groupKeyEquals = GroupKey.getKeyTenant(dataIdEquals, group, tenant); String md5Equals0 = MD5Utils.md5Hex("countEquals0", "UTF-8"); ConfigListenState configListenState1 = new ConfigListenState(md5Equals0); clientMd5Map.put(groupKeyEquals, configListenState1); String md5NotEquals1 = MD5Utils.md5Hex("countNotEquals", "UTF-8"); ConfigListenState configListenState2 = new ConfigListenState(md5NotEquals1); String dataIdNotEquals = "dataIdNotEquals0"; String groupKeyNotEquals = GroupKey.getKeyTenant(dataIdNotEquals, group, tenant); clientMd5Map.put(groupKeyNotEquals, configListenState2); MockedStatic md5UtilMockedStatic = Mockito.mockStatic(MD5Util.class); md5UtilMockedStatic.when(() -> MD5Util.compareMd5(any(), any(), any())) .thenReturn(Collections.singletonMap(groupKeyNotEquals, configListenState2)); HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER))).thenReturn(null); String clientIp = "192.168.0.1"; Mockito.when(httpServletRequest.getHeader(eq("X-Forwarded-For"))).thenReturn(clientIp); configCacheServiceMockedStatic.when( () -> ConfigCacheService.isUptodate(eq(groupKeyNotEquals), eq(md5NotEquals1), eq(clientIp), eq(null))).thenReturn(false); configCacheServiceMockedStatic.when(() -> ConfigCacheService.isUptodate(eq(groupKeyEquals), eq(md5Equals0), eq(clientIp), eq(null))) .thenReturn(true); HttpServletResponse httpServletResponse = Mockito.mock(HttpServletResponse.class); PrintWriter printWriter = Mockito.mock(PrintWriter.class); Mockito.when(httpServletResponse.getWriter()).thenReturn(printWriter); int propSize = 3; longPollingService.addLongPollingClient(httpServletRequest, httpServletResponse, clientMd5Map, propSize); String responseString = MD5Util.compareMd5ResultString(Collections.singletonMap(groupKeyNotEquals, configListenState2)); //expect print not equals group Mockito.verify(printWriter, times(1)).println(eq(responseString)); Mockito.verify(httpServletResponse, times(1)).setStatus(eq(HttpServletResponse.SC_OK)); md5UtilMockedStatic.close(); } @Test void testRejectByConnectionLimit() throws Exception { //mock connection no limit ConnectionCheckResponse connectionCheckResponse = new ConnectionCheckResponse(); connectionCheckResponse.setSuccess(false); Mockito.when(connectionControlManager.check(any())).thenReturn(connectionCheckResponse); HttpServletResponse httpServletResponse = Mockito.mock(HttpServletResponse.class); PrintWriter printWriter = Mockito.mock(PrintWriter.class); Mockito.when(httpServletResponse.getWriter()).thenReturn(printWriter); HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER))).thenReturn(null); String clientIp = "192.168.0.1"; Mockito.when(httpServletRequest.getHeader(eq("X-Forwarded-For"))).thenReturn(clientIp); Mockito.when(httpServletRequest.startAsync()).thenReturn(Mockito.mock(AsyncContext.class)); int propSize = 3; Map clientMd5Map = new HashMap<>(); longPollingService.addLongPollingClient(httpServletRequest, httpServletResponse, clientMd5Map, propSize); Thread.sleep(3000L); //expect response not returned Mockito.verify(httpServletResponse, times(1)).setStatus(eq(503)); } @Test void testAddLongPollingClientAllEqualsMd5() throws IOException { //mock connection no limit ConnectionCheckResponse connectionCheckResponse = new ConnectionCheckResponse(); connectionCheckResponse.setSuccess(true); Mockito.when(connectionControlManager.check(any())).thenReturn(connectionCheckResponse); Map clientMd5Map = new HashMap<>(); String group = "group"; String tenant = "tenat"; String dataIdEquals = "dataIdEquals01"; String groupKeyEquals = GroupKey.getKeyTenant(dataIdEquals, group, tenant); String md5Equals0 = MD5Utils.md5Hex("countEquals01", "UTF-8"); ConfigListenState configListenState1 = new ConfigListenState(md5Equals0); clientMd5Map.put(groupKeyEquals, configListenState1); String md5NotEquals1 = MD5Utils.md5Hex("countNotEquals1", "UTF-8"); ConfigListenState configListenState2 = new ConfigListenState(md5NotEquals1); String dataIdNotEquals = "dataIdNotEquals01"; String groupKeyNotEquals = GroupKey.getKeyTenant(dataIdNotEquals, group, tenant); clientMd5Map.put(groupKeyNotEquals, configListenState2); HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_HEADER))).thenReturn("5000"); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER))).thenReturn(null); String clientIp = "192.168.0.1"; Mockito.when(httpServletRequest.getHeader(eq("X-Forwarded-For"))).thenReturn(clientIp); Mockito.when(httpServletRequest.startAsync()).thenReturn(Mockito.mock(AsyncContext.class)); configCacheServiceMockedStatic.when( () -> ConfigCacheService.isUptodate(eq(groupKeyNotEquals), eq(md5NotEquals1), eq(clientIp), eq(null))).thenReturn(true); configCacheServiceMockedStatic.when(() -> ConfigCacheService.isUptodate(eq(groupKeyEquals), eq(md5Equals0), eq(clientIp), eq(null))) .thenReturn(true); int propSize = 3; HttpServletResponse httpServletResponse = Mockito.mock(HttpServletResponse.class); longPollingService.addLongPollingClient(httpServletRequest, httpServletResponse, clientMd5Map, propSize); //expect response not returned Mockito.verify(httpServletResponse, times(0)).setStatus(anyInt()); //expect to schedule a task configExecutorMocked.verify(() -> ConfigExecutor.executeLongPolling(any(LongPollingService.ClientLongPolling.class)), times(1)); } @Test void testReceiveDataChangeEventAndNotify() throws Exception { configExecutorMocked.close(); //mock connection no limit ConnectionCheckResponse connectionCheckResponse = new ConnectionCheckResponse(); connectionCheckResponse.setSuccess(true); Mockito.when(connectionControlManager.check(any())).thenReturn(connectionCheckResponse); String dataIdChanged = "dataIdChanged"; String group = "group"; String tenant = "tenant"; String groupKeyChanged = GroupKey.getKeyTenant(dataIdChanged, group, tenant); Map clientMd5Map = new HashMap<>(); ConfigListenState configListenState = new ConfigListenState("mockMd5"); clientMd5Map.put(groupKeyChanged, configListenState); HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); HttpServletResponse httpServletResponse = Mockito.mock(HttpServletResponse.class); PrintWriter printWriter = Mockito.mock(PrintWriter.class); Mockito.when(httpServletResponse.getWriter()).thenReturn(printWriter); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_HEADER))).thenReturn("5000"); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER))).thenReturn(null); String clientIp = "192.168.0.1"; Mockito.when(httpServletRequest.getHeader(eq("X-Forwarded-For"))).thenReturn(clientIp); AsyncContext asyncContext = Mockito.mock(AsyncContext.class); Mockito.when(httpServletRequest.startAsync()).thenReturn(asyncContext); Mockito.when(asyncContext.getRequest()).thenReturn(httpServletRequest); Mockito.when(asyncContext.getResponse()).thenReturn(httpServletResponse); configCacheServiceMockedStatic.when(() -> ConfigCacheService.isUptodate(anyString(), anyString(), anyString(), eq(null))) .thenReturn(true); longPollingService.addLongPollingClient(httpServletRequest, httpServletResponse, clientMd5Map, 3); //test getSubscribleInfo by groupKey SampleResult subscribleInfo = longPollingService.getCollectSubscribleInfo(dataIdChanged, group, tenant); Map lisentersGroupkeyStatus = subscribleInfo.getLisentersGroupkeyStatus(); assertFalse(lisentersGroupkeyStatus.isEmpty()); assertEquals("mockMd5", lisentersGroupkeyStatus.get(clientIp)); SampleResult collectSubscribleInfoByIp = longPollingService.getCollectSubscribleInfoByIp(clientIp); Map lisentersGroupkeyStatus1 = collectSubscribleInfoByIp.getLisentersGroupkeyStatus(); assertFalse(lisentersGroupkeyStatus1.isEmpty()); assertEquals("mockMd5", lisentersGroupkeyStatus1.get(groupKeyChanged)); //test receive config change event LocalDataChangeEvent localDataChangeEvent = new LocalDataChangeEvent(groupKeyChanged); NotifyCenter.publishEvent(localDataChangeEvent); Thread.sleep(1100L); String responseString = MD5Util.compareMd5ResultString(Collections.singletonMap(groupKeyChanged, configListenState)); //expect print not equals group Mockito.verify(printWriter, times(1)).println(eq(responseString)); Mockito.verify(asyncContext, times(1)).complete(); } @Test void testLongPollingTimeout() throws Exception { configExecutorMocked.close(); String dataIdChanged = "dataIdChanged"; String group = "group"; String tenant = "tenant"; String groupKeyChanged = GroupKey.getKeyTenant(dataIdChanged, group, tenant); //mock connection no limit ConnectionCheckResponse connectionCheckResponse = new ConnectionCheckResponse(); connectionCheckResponse.setSuccess(true); Mockito.when(connectionControlManager.check(any())).thenReturn(connectionCheckResponse); Map clientMd5Map = new HashMap<>(); ConfigListenState configListenState = new ConfigListenState("md5"); clientMd5Map.put(groupKeyChanged, configListenState); switchServiceMockedStatic.when(() -> SwitchService.getSwitchInteger(eq("MIN_LONG_POOLING_TIMEOUT"), eq(10000))).thenReturn(1000); HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_HEADER))).thenReturn("1000"); Mockito.when(httpServletRequest.getHeader(eq(LongPollingService.LONG_POLLING_NO_HANG_UP_HEADER))).thenReturn(null); String clientIp = "192.168.0.1"; Mockito.when(httpServletRequest.getHeader(eq("X-Forwarded-For"))).thenReturn(clientIp); AsyncContext asyncContext = Mockito.mock(AsyncContext.class); Mockito.when(httpServletRequest.startAsync()).thenReturn(asyncContext); Mockito.when(asyncContext.getRequest()).thenReturn(httpServletRequest); configCacheServiceMockedStatic.when(() -> ConfigCacheService.isUptodate(anyString(), anyString(), anyString(), eq(null))) .thenReturn(true); HttpServletResponse httpServletResponse = Mockito.mock(HttpServletResponse.class); longPollingService.addLongPollingClient(httpServletRequest, httpServletResponse, clientMd5Map, 3); //wait time out condition arrived. Thread.sleep(1200L); //expect print not equals group Mockito.verify(asyncContext, times(1)).complete(); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/NamespaceConfigInfoServiceTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service; import com.alibaba.nacos.config.server.constant.PropertiesConstant; import com.alibaba.nacos.config.server.model.capacity.NamespaceCapacity; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) public class NamespaceConfigInfoServiceTest { @Mock private ConfigInfoPersistService configInfoPersistService; MockedStatic propertyUtilMockedStatic; @BeforeEach void setUp() throws Exception { propertyUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); } @AfterEach void after() throws Exception { propertyUtilMockedStatic.close(); } @Test public void testInjectDetailNotDefault() { String namespaceId = "test1234"; when(EnvUtil.getProperty(eq(PropertiesConstant.DEFAULT_TENANT_QUOTA), eq(Integer.class))).thenReturn(1023); when(configInfoPersistService.configInfoCount(namespaceId)).thenReturn(101); Namespace namespace = new Namespace(namespaceId, "test123ShowName"); namespace.setQuota(200); NamespaceConfigInfoService namespaceConfigInfoService = new NamespaceConfigInfoService( configInfoPersistService); namespaceConfigInfoService.injectDetail(namespace); assertEquals(101, namespace.getConfigCount()); assertEquals(1023, namespace.getQuota()); } @Test public void testInjectDetailDefaultQuota() { String namespaceId = "test1234"; NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setQuota(0); when(configInfoPersistService.configInfoCount(namespaceId)).thenReturn(105); when(EnvUtil.getProperty(eq(PropertiesConstant.DEFAULT_TENANT_QUOTA), eq(Integer.class))).thenReturn(null); Namespace namespace = new Namespace(namespaceId, "test123ShowName"); namespace.setQuota(200); NamespaceConfigInfoService namespaceConfigInfoService = new NamespaceConfigInfoService( configInfoPersistService); namespaceConfigInfoService.injectDetail(namespace); assertEquals(105, namespace.getConfigCount()); assertEquals(200, namespace.getQuota()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/capacity/CapacityServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.config.server.constant.CounterMode; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.capacity.GroupCapacity; import com.alibaba.nacos.config.server.model.capacity.NamespaceCapacity; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.PropertyUtil; 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.Mockito; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.util.ReflectionTestUtils; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) @WebAppConfiguration class CapacityServiceTest { private CapacityService service; @Mock private GroupCapacityPersistService groupCapacityPersistService; @Mock private TenantCapacityPersistService tenantCapacityPersistService; @Mock private ConfigInfoPersistService configInfoPersistService; @BeforeEach void setUp() { service = new CapacityService(); ReflectionTestUtils.setField(service, "groupCapacityPersistService", groupCapacityPersistService); ReflectionTestUtils.setField(service, "tenantCapacityPersistService", tenantCapacityPersistService); ReflectionTestUtils.setField(service, "configInfoPersistService", configInfoPersistService); } @Test void testInit() { service.init(); } @Test void testCorrectUsage() { List groupCapacityList = new ArrayList<>(); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setId(1L); groupCapacity.setGroupName("testGroup"); groupCapacityList.add(groupCapacity); when(groupCapacityPersistService.getCapacityList4CorrectUsage(0L, 100)).thenReturn(groupCapacityList); when(groupCapacityPersistService.getCapacityList4CorrectUsage(1L, 100)).thenReturn(new ArrayList<>()); when(groupCapacityPersistService.correctUsage(eq("testGroup"), any())).thenReturn(true); List tenantCapacityList = new ArrayList<>(); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setId(1L); tenantCapacity.setNamespaceId("testTenant"); tenantCapacityList.add(tenantCapacity); when(tenantCapacityPersistService.getCapacityList4CorrectUsage(0L, 100)).thenReturn(tenantCapacityList); when(tenantCapacityPersistService.getCapacityList4CorrectUsage(1L, 100)).thenReturn(new ArrayList<>()); when(tenantCapacityPersistService.correctUsage(eq("testTenant"), any())).thenReturn(true); service.correctUsage(); Mockito.verify(groupCapacityPersistService, times(1)).getCapacityList4CorrectUsage(0L, 100); Mockito.verify(groupCapacityPersistService, times(1)).getCapacityList4CorrectUsage(1L, 100); Mockito.verify(groupCapacityPersistService, times(1)).correctUsage(eq("testGroup"), any()); Mockito.verify(tenantCapacityPersistService, times(1)).getCapacityList4CorrectUsage(0L, 100); Mockito.verify(tenantCapacityPersistService, times(1)).getCapacityList4CorrectUsage(1L, 100); Mockito.verify(tenantCapacityPersistService, times(1)).correctUsage(eq("testTenant"), any()); } @Test void testCorrectGroupUsage() { when(groupCapacityPersistService.correctUsage(eq("testGroup"), any())).thenReturn(true); service.correctGroupUsage("testGroup"); Mockito.verify(groupCapacityPersistService, times(1)).correctUsage(eq("testGroup"), any()); } @Test void testCorrectTenantUsage() { when(tenantCapacityPersistService.correctUsage(eq("testTenant"), any())).thenReturn(true); service.correctTenantUsage("testTenant"); Mockito.verify(tenantCapacityPersistService, times(1)).correctUsage(eq("testTenant"), any()); } @Test void testInitAllCapacity() { List groupList = new ArrayList<>(); groupList.add("testGroup"); when(configInfoPersistService.getGroupIdList(eq(1), eq(500))).thenReturn(groupList); List tenantList = new ArrayList<>(); tenantList.add("testTenant"); when(configInfoPersistService.getTenantIdList(eq(1), eq(500))).thenReturn(tenantList); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setGroupName("testGroup"); groupCapacity.setUsage(300); when(groupCapacityPersistService.insertGroupCapacity(any())).thenReturn(true); when(groupCapacityPersistService.getGroupCapacity(eq("testGroup"))).thenReturn(groupCapacity); when(groupCapacityPersistService.updateQuota(eq("testGroup"), eq(500))).thenReturn(true); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId("testTenant"); tenantCapacity.setUsage(300); when(tenantCapacityPersistService.insertTenantCapacity(any())).thenReturn(true); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(tenantCapacity); when(tenantCapacityPersistService.updateQuota(eq("testTenant"), eq(500))).thenReturn(true); service.initAllCapacity(); Mockito.verify(groupCapacityPersistService, times(1)).insertGroupCapacity(any()); Mockito.verify(groupCapacityPersistService, times(1)).getGroupCapacity(eq("testGroup")); Mockito.verify(groupCapacityPersistService, times(1)).updateQuota(eq("testGroup"), eq(500)); Mockito.verify(tenantCapacityPersistService, times(1)).insertTenantCapacity(any()); Mockito.verify(tenantCapacityPersistService, times(1)).getTenantCapacity(eq("testTenant")); Mockito.verify(tenantCapacityPersistService, times(1)).updateQuota(eq("testTenant"), eq(500)); } @Test void testInsertAndUpdateClusterUsage() { when(groupCapacityPersistService.insertGroupCapacity(any())).thenReturn(true); when(groupCapacityPersistService.incrementUsage(any())).thenReturn(true); when(groupCapacityPersistService.incrementUsageWithDefaultQuotaLimit(any())).thenReturn(true); when(groupCapacityPersistService.decrementUsage(any())).thenReturn(true); service.insertAndUpdateClusterUsage(CounterMode.INCREMENT, true); Mockito.verify(groupCapacityPersistService, times(1)).incrementUsage(any()); service.insertAndUpdateClusterUsage(CounterMode.INCREMENT, false); Mockito.verify(groupCapacityPersistService, times(1)).incrementUsageWithDefaultQuotaLimit(any()); service.insertAndUpdateClusterUsage(CounterMode.DECREMENT, true); Mockito.verify(groupCapacityPersistService, times(1)).decrementUsage(any()); } @Test void testUpdateClusterUsage() { when(groupCapacityPersistService.incrementUsageWithDefaultQuotaLimit(any())).thenReturn(true); when(groupCapacityPersistService.decrementUsage(any())).thenReturn(true); service.updateClusterUsage(CounterMode.INCREMENT); Mockito.verify(groupCapacityPersistService, times(1)).incrementUsageWithDefaultQuotaLimit(any()); service.updateClusterUsage(CounterMode.DECREMENT); Mockito.verify(groupCapacityPersistService, times(1)).decrementUsage(any()); } @Test void testInsertAndUpdateGroupUsage() { GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setGroupName("testGroup"); groupCapacity.setUsage(300); when(groupCapacityPersistService.getGroupCapacity("testGroup")).thenReturn(groupCapacity); when(groupCapacityPersistService.incrementUsage(any())).thenReturn(true); when(groupCapacityPersistService.incrementUsageWithDefaultQuotaLimit(any())).thenReturn(true); when(groupCapacityPersistService.decrementUsage(any())).thenReturn(true); service.insertAndUpdateGroupUsage(CounterMode.INCREMENT, "testGroup", true); Mockito.verify(groupCapacityPersistService, times(1)).incrementUsage(any()); service.insertAndUpdateClusterUsage(CounterMode.INCREMENT, false); Mockito.verify(groupCapacityPersistService, times(1)).incrementUsageWithDefaultQuotaLimit(any()); service.insertAndUpdateClusterUsage(CounterMode.DECREMENT, true); Mockito.verify(groupCapacityPersistService, times(1)).decrementUsage(any()); } @Test void testUpdateGroupUsage() { when(groupCapacityPersistService.incrementUsageWithDefaultQuotaLimit(any())).thenReturn(true); when(groupCapacityPersistService.decrementUsage(any())).thenReturn(true); service.updateGroupUsage(CounterMode.INCREMENT, "testGroup"); Mockito.verify(groupCapacityPersistService, times(1)).incrementUsageWithDefaultQuotaLimit(any()); service.updateGroupUsage(CounterMode.DECREMENT, "testGroup"); Mockito.verify(groupCapacityPersistService, times(1)).decrementUsage(any()); } @Test void testGetGroupCapacity() { GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setId(1L); groupCapacity.setGroupName("testGroup"); when(groupCapacityPersistService.getGroupCapacity(eq("testGroup"))).thenReturn(groupCapacity); GroupCapacity resGroupCapacity = service.getGroupCapacity("testGroup"); assertEquals(groupCapacity.getId(), resGroupCapacity.getId()); assertEquals(groupCapacity.getGroupName(), resGroupCapacity.getGroupName()); } @Test void testInitGroupCapacity() { GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setGroupName("testGroup"); groupCapacity.setUsage(300); when(groupCapacityPersistService.insertGroupCapacity(any())).thenReturn(true); when(groupCapacityPersistService.getGroupCapacity(eq("testGroup"))).thenReturn(groupCapacity); when(groupCapacityPersistService.updateQuota(eq("testGroup"), eq(500))).thenReturn(true); service.initGroupCapacity("testGroup"); Mockito.verify(groupCapacityPersistService, times(1)).insertGroupCapacity(any()); Mockito.verify(groupCapacityPersistService, times(1)).getGroupCapacity(eq("testGroup")); Mockito.verify(groupCapacityPersistService, times(1)).updateQuota(eq("testGroup"), eq(500)); } @Test void testGetCapacity() { GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setId(1L); when(groupCapacityPersistService.getGroupCapacity(eq("testGroup"))).thenReturn(groupCapacity); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setId(2L); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(tenantCapacity); Capacity resCapacity1 = service.getCapacity("testGroup", null); assertEquals(1L, resCapacity1.getId().longValue()); Capacity resCapacity2 = service.getCapacity(null, "testTenant"); assertEquals(2L, resCapacity2.getId().longValue()); } @Test void testGetCapacityWithDefault() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setQuota(0); tenantCapacity.setMaxSize(0); tenantCapacity.setMaxAggrCount(0); tenantCapacity.setMaxAggrSize(0); when(tenantCapacityPersistService.getTenantCapacity(anyString())).thenReturn(tenantCapacity); GroupCapacity groupCapacity1 = new GroupCapacity(); groupCapacity1.setQuota(0); groupCapacity1.setMaxSize(0); groupCapacity1.setMaxAggrCount(0); groupCapacity1.setMaxAggrSize(0); when(groupCapacityPersistService.getGroupCapacity(anyString())).thenReturn(groupCapacity1); //group is null Capacity resCapacity1 = service.getCapacityWithDefault(null, "testTenant"); assertEquals(PropertyUtil.getDefaultGroupQuota(), resCapacity1.getQuota().intValue()); assertEquals(PropertyUtil.getDefaultMaxSize(), resCapacity1.getMaxSize().intValue()); assertEquals(PropertyUtil.getDefaultMaxAggrCount(), resCapacity1.getMaxAggrCount().intValue()); assertEquals(PropertyUtil.getDefaultMaxAggrSize(), resCapacity1.getMaxAggrSize().intValue()); //group is GroupCapacityPersistService.CLUSTER Capacity resCapacity2 = service.getCapacityWithDefault(GroupCapacityPersistService.CLUSTER, null); assertEquals(PropertyUtil.getDefaultClusterQuota(), resCapacity2.getQuota().intValue()); assertEquals(PropertyUtil.getDefaultMaxSize(), resCapacity2.getMaxSize().intValue()); assertEquals(PropertyUtil.getDefaultMaxAggrCount(), resCapacity2.getMaxAggrCount().intValue()); assertEquals(PropertyUtil.getDefaultMaxAggrSize(), resCapacity2.getMaxAggrSize().intValue()); GroupCapacity groupCapacity2 = new GroupCapacity(); groupCapacity2.setQuota(0); groupCapacity2.setMaxSize(0); groupCapacity2.setMaxAggrCount(0); groupCapacity2.setMaxAggrSize(0); when(groupCapacityPersistService.getGroupCapacity(anyString())).thenReturn(groupCapacity2); //tenant is null Capacity resCapacity3 = service.getCapacityWithDefault("testGroup", null); assertEquals(PropertyUtil.getDefaultGroupQuota(), resCapacity3.getQuota().intValue()); assertEquals(PropertyUtil.getDefaultMaxSize(), resCapacity3.getMaxSize().intValue()); assertEquals(PropertyUtil.getDefaultMaxAggrCount(), resCapacity3.getMaxAggrCount().intValue()); assertEquals(PropertyUtil.getDefaultMaxAggrSize(), resCapacity3.getMaxAggrSize().intValue()); } @Test void testInitCapacityV1() { GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setUsage(300); when(groupCapacityPersistService.getGroupCapacity(eq("testGroup"))).thenReturn(groupCapacity); when(groupCapacityPersistService.insertGroupCapacity(any())).thenReturn(true); when(groupCapacityPersistService.updateQuota(eq("testGroup"), eq(500))).thenReturn(true); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setUsage(300); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(tenantCapacity); when(tenantCapacityPersistService.insertTenantCapacity(any())).thenReturn(true); when(tenantCapacityPersistService.updateQuota(eq("testTenant"), eq(500))).thenReturn(true); service.initCapacity("testGroup", null); Mockito.verify(groupCapacityPersistService, times(1)).getGroupCapacity(eq("testGroup")); Mockito.verify(groupCapacityPersistService, times(1)).insertGroupCapacity(any()); Mockito.verify(groupCapacityPersistService, times(1)).updateQuota(eq("testGroup"), eq(500)); service.initCapacity(null, "testTenant"); Mockito.verify(tenantCapacityPersistService, times(1)).getTenantCapacity(eq("testTenant")); Mockito.verify(tenantCapacityPersistService, times(1)).insertTenantCapacity(any()); Mockito.verify(tenantCapacityPersistService, times(1)).updateQuota(eq("testTenant"), eq(500)); } @Test void testInitCapacityV2() { when(groupCapacityPersistService.insertGroupCapacity(any())).thenReturn(true); service.initCapacity(GroupCapacityPersistService.CLUSTER, null); Mockito.verify(groupCapacityPersistService, times(1)).insertGroupCapacity(any()); } @Test void testInsertAndUpdateTenantUsage() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId("testTenant"); tenantCapacity.setUsage(300); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(tenantCapacity); when(tenantCapacityPersistService.incrementUsage(any())).thenReturn(true); when(tenantCapacityPersistService.incrementUsageWithDefaultQuotaLimit(any())).thenReturn(true); when(tenantCapacityPersistService.decrementUsage(any())).thenReturn(true); service.insertAndUpdateTenantUsage(CounterMode.INCREMENT, "testTenant", true); Mockito.verify(tenantCapacityPersistService, times(1)).incrementUsage(any()); service.insertAndUpdateTenantUsage(CounterMode.INCREMENT, "testTenant", false); Mockito.verify(tenantCapacityPersistService, times(1)).incrementUsageWithDefaultQuotaLimit(any()); service.insertAndUpdateTenantUsage(CounterMode.DECREMENT, "testTenant", true); Mockito.verify(tenantCapacityPersistService, times(1)).decrementUsage(any()); } @Test void testUpdateTenantUsage() { when(tenantCapacityPersistService.incrementUsageWithDefaultQuotaLimit(any())).thenReturn(true); when(tenantCapacityPersistService.decrementUsage(any())).thenReturn(true); service.updateTenantUsage(CounterMode.INCREMENT, "testTenant"); Mockito.verify(tenantCapacityPersistService, times(1)).incrementUsageWithDefaultQuotaLimit(any()); service.updateTenantUsage(CounterMode.DECREMENT, "testTenant"); Mockito.verify(tenantCapacityPersistService, times(1)).decrementUsage(any()); } @Test void testInitTenantCapacityV1() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId("testTenant"); tenantCapacity.setUsage(300); when(tenantCapacityPersistService.insertTenantCapacity(any())).thenReturn(true); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(tenantCapacity); when(tenantCapacityPersistService.updateQuota(eq("testTenant"), eq(500))).thenReturn(true); service.initTenantCapacity("testTenant"); Mockito.verify(tenantCapacityPersistService, times(1)).insertTenantCapacity(any()); Mockito.verify(tenantCapacityPersistService, times(1)).getTenantCapacity(eq("testTenant")); Mockito.verify(tenantCapacityPersistService, times(1)).updateQuota(eq("testTenant"), eq(500)); } @Test void testInitTenantCapacityV2() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId("testTenant"); tenantCapacity.setUsage(300); when(tenantCapacityPersistService.insertTenantCapacity(any())).thenReturn(true); service.initTenantCapacity("testTenant", 0, 0, 0, 0); Mockito.verify(tenantCapacityPersistService, times(1)).insertTenantCapacity(any()); } @Test void testGetTenantCapacity() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setId(1L); tenantCapacity.setNamespaceId("testTenant"); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(tenantCapacity); NamespaceCapacity resTenantCapacity = service.getTenantCapacity("testTenant"); assertEquals(tenantCapacity.getId(), resTenantCapacity.getId()); assertEquals(tenantCapacity.getNamespaceId(), resTenantCapacity.getNamespaceId()); } @Test void testInsertOrUpdateCapacityV1() { //tenant is null GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setUsage(300); when(groupCapacityPersistService.getGroupCapacity(eq("testGroup"))).thenReturn(groupCapacity); when(groupCapacityPersistService.updateGroupCapacity(eq("testGroup"), eq(0), eq(0), eq(0), eq(0))).thenReturn(true); service.insertOrUpdateCapacity("testGroup", null, 0, 0, 0, 0); Mockito.verify(groupCapacityPersistService, times(1)).getGroupCapacity(eq("testGroup")); Mockito.verify(groupCapacityPersistService, times(1)).updateGroupCapacity(eq("testGroup"), eq(0), eq(0), eq(0), eq(0)); //tenant is not null NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId("testTenant"); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(tenantCapacity); when(tenantCapacityPersistService.updateTenantCapacity(eq("testTenant"), eq(0), eq(0), eq(0), eq(0))).thenReturn(true); service.insertOrUpdateCapacity(null, "testTenant", 0, 0, 0, 0); Mockito.verify(tenantCapacityPersistService, times(1)).getTenantCapacity(eq("testTenant")); Mockito.verify(tenantCapacityPersistService, times(1)).updateTenantCapacity(eq("testTenant"), eq(0), eq(0), eq(0), eq(0)); } @Test void testInsertOrUpdateCapacityV2() { when(groupCapacityPersistService.getGroupCapacity(eq("testGroup"))).thenReturn(null); when(groupCapacityPersistService.insertGroupCapacity(any())).thenReturn(true); service.insertOrUpdateCapacity("testGroup", null, 0, 0, 0, 0); Mockito.verify(groupCapacityPersistService, times(1)).getGroupCapacity(eq("testGroup")); Mockito.verify(groupCapacityPersistService, times(1)).insertGroupCapacity(any()); when(tenantCapacityPersistService.getTenantCapacity(eq("testTenant"))).thenReturn(null); when(tenantCapacityPersistService.insertTenantCapacity(any())).thenReturn(true); service.insertOrUpdateCapacity(null, "testTenant", 0, 0, 0, 0); Mockito.verify(tenantCapacityPersistService, times(1)).getTenantCapacity(eq("testTenant")); Mockito.verify(tenantCapacityPersistService, times(1)).insertTenantCapacity(any()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.capacity.Capacity; import com.alibaba.nacos.config.server.model.capacity.GroupCapacity; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.impl.mysql.ConfigInfoMapperByMySql; import com.alibaba.nacos.plugin.datasource.impl.mysql.GroupCapacityMapperByMysql; 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.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.core.RowMapper; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; 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.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) class GroupCapacityPersistServiceTest { MockedStatic timeUtilsMockedStatic; @InjectMocks private GroupCapacityPersistService service; @Mock private JdbcTemplate jdbcTemplate; @Mock private DataSourceService dataSourceService; @Mock private MapperManager mapperManager; @AfterEach void after() { timeUtilsMockedStatic.close(); } @BeforeEach void setUp() { ReflectionTestUtils.setField(service, "jdbcTemplate", jdbcTemplate); ReflectionTestUtils.setField(service, "dataSourceService", dataSourceService); ReflectionTestUtils.setField(service, "mapperManager", mapperManager); when(dataSourceService.getJdbcTemplate()).thenReturn(jdbcTemplate); doReturn(new GroupCapacityMapperByMysql()).when(mapperManager).findMapper(any(), eq(TableConstant.GROUP_CAPACITY)); timeUtilsMockedStatic = Mockito.mockStatic(TimeUtils.class); } @Test void testGetGroupCapacity() { List list = new ArrayList<>(); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setGroupName("test"); list.add(groupCapacity); String groupId = "testId"; when(jdbcTemplate.query(anyString(), any(RowMapper.class), eq(new Object[] {groupId}))).thenReturn(list); GroupCapacity ret = service.getGroupCapacity(groupId); assertEquals(groupCapacity.getGroupName(), ret.getGroupName()); } @Test void testGetClusterCapacity() { List list = new ArrayList<>(); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setId(1L); list.add(groupCapacity); String groupId = GroupCapacityPersistService.CLUSTER; when(jdbcTemplate.query(anyString(), any(RowMapper.class), eq(new Object[] {groupId}))).thenReturn(list); Capacity ret = service.getClusterCapacity(); assertEquals(groupCapacity.getId(), ret.getId()); } @Test void testInsertGroupCapacity() { doReturn(1).when(jdbcTemplate).update(anyString(), eq(""), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null)); // when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test3"))).thenReturn(1); GroupCapacity capacity = new GroupCapacity(); capacity.setGroupName(GroupCapacityPersistService.CLUSTER); assertTrue(service.insertGroupCapacity(capacity)); capacity.setGroupName("test"); doReturn(1).when(jdbcTemplate) .update(anyString(), eq("test"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq("test")); assertTrue(service.insertGroupCapacity(capacity)); } @Test void testGetClusterUsage() { doReturn(new ConfigInfoMapperByMySql()).when(mapperManager).findMapper(any(), eq(TableConstant.CONFIG_INFO)); List list = new ArrayList<>(); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setId(1L); groupCapacity.setUsage(10); list.add(groupCapacity); String groupId = GroupCapacityPersistService.CLUSTER; when(jdbcTemplate.query(anyString(), any(RowMapper.class), eq(new Object[] {groupId}))).thenReturn(list); assertEquals(groupCapacity.getUsage().intValue(), service.getClusterUsage()); when(jdbcTemplate.query(anyString(), any(RowMapper.class), eq(new Object[] {groupId}))).thenReturn(new ArrayList<>()); when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(20); assertEquals(20, service.getClusterUsage()); } @Test void testIncrementUsageWithDefaultQuotaLimit() { GroupCapacity groupCapacity = new GroupCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); groupCapacity.setGmtModified(timestamp); groupCapacity.setGroupName("test"); groupCapacity.setQuota(1); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test"), eq(1))).thenReturn(1); assertTrue(service.incrementUsageWithDefaultQuotaLimit(groupCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test"), eq(1))).thenThrow( new CannotGetJdbcConnectionException("conn fail")); try { service.incrementUsageWithDefaultQuotaLimit(groupCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testIncrementUsageWithQuotaLimit() { GroupCapacity groupCapacity = new GroupCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); groupCapacity.setGmtModified(timestamp); groupCapacity.setGroupName("test2"); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test2"))).thenReturn(1); assertTrue(service.incrementUsageWithQuotaLimit(groupCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test2"))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.incrementUsageWithQuotaLimit(groupCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testIncrementUsage() { GroupCapacity groupCapacity = new GroupCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); groupCapacity.setGmtModified(timestamp); groupCapacity.setGroupName("test3"); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test3"))).thenReturn(1); assertTrue(service.incrementUsage(groupCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test3"))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.incrementUsage(groupCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testDecrementUsage() { GroupCapacity groupCapacity = new GroupCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); groupCapacity.setGmtModified(timestamp); groupCapacity.setGroupName("test4"); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test4"))).thenReturn(1); assertTrue(service.decrementUsage(groupCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test4"))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.decrementUsage(groupCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testUpdateGroupCapacity() { List argList = CollectionUtils.list(); Integer quota = 1; argList.add(quota); Integer maxSize = 2; argList.add(maxSize); Integer maxAggrCount = 3; argList.add(maxAggrCount); Integer maxAggrSize = 4; argList.add(maxAggrSize); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); when(TimeUtils.getCurrentTime()).thenReturn(timestamp); argList.add(timestamp); String group = "test"; argList.add(group); when(jdbcTemplate.update(anyString(), any(Object.class))).thenAnswer((Answer) invocationOnMock -> { if (invocationOnMock.getArgument(1).equals(quota) && invocationOnMock.getArgument(2).equals(maxSize) && invocationOnMock.getArgument(3).equals(maxAggrCount) && invocationOnMock.getArgument(4).equals(maxAggrSize) && invocationOnMock.getArgument(5).equals(timestamp) && invocationOnMock.getArgument(6).equals(group)) { return 1; } return 0; }); assertTrue(service.updateGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize)); //mock get connection fail when(jdbcTemplate.update(anyString(), any(Object.class))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.updateGroupCapacity(group, quota, maxSize, maxAggrCount, maxAggrSize); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testGroupCapacityRowMapper() throws SQLException { GroupCapacityPersistService.GroupCapacityRowMapper groupCapacityRowMapper = new GroupCapacityPersistService.GroupCapacityRowMapper(); ResultSet rs = Mockito.mock(ResultSet.class); int quota = 12345; Mockito.when(rs.getInt(eq("quota"))).thenReturn(quota); int usage = 1244; Mockito.when(rs.getInt(eq("usage"))).thenReturn(usage); int maxSize = 123; Mockito.when(rs.getInt(eq("max_size"))).thenReturn(maxSize); int maxAggrCount = 123; Mockito.when(rs.getInt(eq("max_aggr_count"))).thenReturn(maxAggrCount); int maxAggrSize = 123; Mockito.when(rs.getInt(eq("max_aggr_size"))).thenReturn(maxAggrSize); String group = "testG"; Mockito.when(rs.getString(eq("group_id"))).thenReturn(group); GroupCapacity groupCapacity = groupCapacityRowMapper.mapRow(rs, 1); assertEquals(quota, groupCapacity.getQuota().intValue()); assertEquals(usage, groupCapacity.getUsage().intValue()); assertEquals(maxSize, groupCapacity.getMaxSize().intValue()); assertEquals(maxAggrCount, groupCapacity.getMaxAggrCount().intValue()); assertEquals(maxAggrSize, groupCapacity.getMaxAggrSize().intValue()); assertEquals(group, groupCapacity.getGroupName()); } @Test void testUpdateQuota() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); when(TimeUtils.getCurrentTime()).thenReturn(timestamp); List argList = CollectionUtils.list(); Integer quota = 2; argList.add(quota); String group = "test2"; argList.add(group); when(jdbcTemplate.update(anyString(), eq(2), eq(timestamp), eq(group))).thenReturn(1); assertTrue(service.updateQuota(group, quota)); } @Test void testUpdateMaxSize() { List argList = CollectionUtils.list(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); when(TimeUtils.getCurrentTime()).thenReturn(timestamp); Integer maxSize = 3; argList.add(maxSize); String group = "test3"; argList.add(group); when(jdbcTemplate.update(anyString(), eq(3), eq(timestamp), eq(group))).thenReturn(1); assertTrue(service.updateMaxSize(group, maxSize)); } @Test void testCorrectUsage() { String group = GroupCapacityPersistService.CLUSTER; Timestamp timestamp = new Timestamp(System.currentTimeMillis()); when(jdbcTemplate.update(anyString(), eq(timestamp), eq(group))).thenReturn(1); assertTrue(service.correctUsage(group, timestamp)); group = "test"; when(jdbcTemplate.update(anyString(), eq(group), eq(timestamp), eq(group))).thenReturn(1); assertTrue(service.correctUsage(group, timestamp)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(group), eq(timestamp), eq(group))).thenThrow( new CannotGetJdbcConnectionException("conn fail")); try { service.correctUsage(group, timestamp); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testGetCapacityList4CorrectUsage() { List list = new ArrayList<>(); GroupCapacity groupCapacity = new GroupCapacity(); groupCapacity.setGroupName("test"); list.add(groupCapacity); long lastId = 1; int pageSize = 1; when(jdbcTemplate.query(anyString(), eq(new Object[] {lastId, pageSize}), any(RowMapper.class))).thenReturn(list); List ret = service.getCapacityList4CorrectUsage(lastId, pageSize); assertEquals(list.size(), ret.size()); assertEquals(groupCapacity.getGroupName(), ret.get(0).getGroupName()); //mock get connection fail when(jdbcTemplate.query(anyString(), eq(new Object[] {lastId, pageSize}), any(RowMapper.class))).thenThrow( new CannotGetJdbcConnectionException("conn fail")); try { service.getCapacityList4CorrectUsage(lastId, pageSize); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testDeleteGroupCapacity() { when(jdbcTemplate.update(any(PreparedStatementCreator.class))).thenReturn(1); assertTrue(service.deleteGroupCapacity("test")); //mock get connection fail when(jdbcTemplate.update(any(PreparedStatementCreator.class))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.deleteGroupCapacity("test"); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.capacity; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.config.server.model.capacity.NamespaceCapacity; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.impl.mysql.TenantCapacityMapperByMySql; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.stubbing.Answer; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.core.RowMapper; import org.springframework.mock.web.MockServletContext; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; 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.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = MockServletContext.class) class TenantCapacityPersistServiceTest { @Mock private JdbcTemplate jdbcTemplate; @Mock private DataSourceService dataSourceService; @Mock private MapperManager mapperManager; @InjectMocks private TenantCapacityPersistService service; @BeforeEach void setUp() { ReflectionTestUtils.setField(service, "jdbcTemplate", jdbcTemplate); ReflectionTestUtils.setField(service, "dataSourceService", dataSourceService); ReflectionTestUtils.setField(service, "mapperManager", mapperManager); when(dataSourceService.getJdbcTemplate()).thenReturn(jdbcTemplate); doReturn(new TenantCapacityMapperByMySql()).when(mapperManager).findMapper(any(), eq(TableConstant.TENANT_CAPACITY)); } @Test void testGetTenantCapacity() { List list = new ArrayList<>(); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId("test"); list.add(tenantCapacity); String tenantId = "testId"; when(jdbcTemplate.query(anyString(), any(RowMapper.class), eq(new Object[] {tenantId}))).thenReturn(list); NamespaceCapacity ret = service.getTenantCapacity(tenantId); assertEquals(tenantCapacity.getNamespaceId(), ret.getNamespaceId()); } @Test void testInsertTenantCapacity() { when(jdbcTemplate.update(anyString(), eq("test"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq("test"))).thenReturn(1); NamespaceCapacity capacity = new NamespaceCapacity(); capacity.setNamespaceId("test"); assertTrue(service.insertTenantCapacity(capacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq("test"), eq(null), eq(null), eq(null), eq(null), eq(null), eq(null), eq("test"))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.insertTenantCapacity(capacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testIncrementUsageWithDefaultQuotaLimit() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); tenantCapacity.setGmtModified(timestamp); tenantCapacity.setNamespaceId("test"); tenantCapacity.setQuota(1); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test"), eq(1))).thenReturn(1); assertTrue(service.incrementUsageWithDefaultQuotaLimit(tenantCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test"), eq(1))).thenThrow( new CannotGetJdbcConnectionException("conn fail")); try { service.incrementUsageWithDefaultQuotaLimit(tenantCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testIncrementUsageWithQuotaLimit() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); tenantCapacity.setGmtModified(timestamp); tenantCapacity.setNamespaceId("test2"); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test2"))).thenReturn(1); assertTrue(service.incrementUsageWithQuotaLimit(tenantCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test2"))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.incrementUsageWithQuotaLimit(tenantCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testIncrementUsage() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); tenantCapacity.setGmtModified(timestamp); tenantCapacity.setNamespaceId("test3"); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test3"))).thenReturn(1); assertTrue(service.incrementUsage(tenantCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test3"))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.incrementUsage(tenantCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testDecrementUsage() { NamespaceCapacity tenantCapacity = new NamespaceCapacity(); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); tenantCapacity.setGmtModified(timestamp); tenantCapacity.setNamespaceId("test4"); when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test4"))).thenReturn(1); assertTrue(service.decrementUsage(tenantCapacity)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(timestamp), eq("test4"))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.decrementUsage(tenantCapacity); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testUpdateTenantCapacity() { final MockedStatic timeUtilsMockedStatic = Mockito.mockStatic(TimeUtils.class); List argList = CollectionUtils.list(); Integer quota = 1; argList.add(quota); Integer maxSize = 2; argList.add(maxSize); Integer maxAggrCount = 3; argList.add(maxAggrCount); Integer maxAggrSize = 4; argList.add(maxAggrSize); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); timeUtilsMockedStatic.when(TimeUtils::getCurrentTime).thenReturn(timestamp); argList.add(timestamp); String tenant = "test"; argList.add(tenant); when(jdbcTemplate.update(anyString(), any(Object.class))).thenAnswer((Answer) invocationOnMock -> { if (invocationOnMock.getArgument(1).equals(quota) && invocationOnMock.getArgument(2).equals(maxSize) && invocationOnMock.getArgument(3).equals(maxAggrCount) && invocationOnMock.getArgument(4).equals(maxAggrSize) && invocationOnMock.getArgument(5).equals(timestamp) && invocationOnMock.getArgument(6).equals(tenant)) { return 1; } return 0; }); assertTrue(service.updateTenantCapacity(tenant, quota, maxSize, maxAggrCount, maxAggrSize)); timeUtilsMockedStatic.close(); } @Test void testUpdateQuota() { List argList = CollectionUtils.list(); Integer quota = 2; argList.add(quota); String tenant = "test2"; argList.add(tenant); when(jdbcTemplate.update(anyString(), any(Object.class))).thenAnswer((Answer) invocationOnMock -> { if (invocationOnMock.getArgument(1).equals(quota) && invocationOnMock.getArgument(3).equals(tenant)) { return 1; } return 0; }); assertTrue(service.updateQuota(tenant, quota)); //mock get connection fail when(jdbcTemplate.update(anyString(), any(Object.class))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.updateQuota(tenant, quota); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testCorrectUsage() { String tenant = "test"; Timestamp timestamp = new Timestamp(System.currentTimeMillis()); when(jdbcTemplate.update(anyString(), eq(tenant), eq(timestamp), eq(tenant))).thenReturn(1); assertTrue(service.correctUsage(tenant, timestamp)); //mock get connection fail when(jdbcTemplate.update(anyString(), eq(tenant), eq(timestamp), eq(tenant))).thenThrow( new CannotGetJdbcConnectionException("conn fail")); try { service.correctUsage(tenant, timestamp); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testGetCapacityList4CorrectUsage() { List list = new ArrayList<>(); NamespaceCapacity tenantCapacity = new NamespaceCapacity(); tenantCapacity.setNamespaceId("test"); list.add(tenantCapacity); long lastId = 1; int pageSize = 1; when(jdbcTemplate.query(anyString(), eq(new Object[] {lastId, pageSize}), any(RowMapper.class))).thenReturn(list); List ret = service.getCapacityList4CorrectUsage(lastId, pageSize); assertEquals(list.size(), ret.size()); assertEquals(tenantCapacity.getNamespaceId(), ret.get(0).getNamespaceId()); //mock get connection fail when(jdbcTemplate.query(anyString(), eq(new Object[] {lastId, pageSize}), any(RowMapper.class))).thenThrow( new CannotGetJdbcConnectionException("conn fail")); try { service.getCapacityList4CorrectUsage(lastId, pageSize); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testDeleteTenantCapacity() { when(jdbcTemplate.update(any(PreparedStatementCreator.class))).thenReturn(1); assertTrue(service.deleteTenantCapacity("test")); //mock get connection fail when(jdbcTemplate.update(any(PreparedStatementCreator.class))).thenThrow(new CannotGetJdbcConnectionException("conn fail")); try { service.deleteTenantCapacity("test"); assertTrue(false); } catch (Exception e) { assertEquals("conn fail", e.getMessage()); } } @Test void testTenantCapacityRowMapper() throws SQLException { TenantCapacityPersistService.TenantCapacityRowMapper groupCapacityRowMapper = new TenantCapacityPersistService.TenantCapacityRowMapper(); ResultSet rs = Mockito.mock(ResultSet.class); int quota = 12345; Mockito.when(rs.getInt(eq("quota"))).thenReturn(quota); int usage = 1244; Mockito.when(rs.getInt(eq("usage"))).thenReturn(usage); int maxSize = 123; Mockito.when(rs.getInt(eq("max_size"))).thenReturn(maxSize); int maxAggrCount = 123; Mockito.when(rs.getInt(eq("max_aggr_count"))).thenReturn(maxAggrCount); int maxAggrSize = 123; Mockito.when(rs.getInt(eq("max_aggr_size"))).thenReturn(maxAggrSize); String tenant = "testTeat"; Mockito.when(rs.getString(eq("tenant_id"))).thenReturn(tenant); NamespaceCapacity groupCapacity = groupCapacityRowMapper.mapRow(rs, 1); assertEquals(quota, groupCapacity.getQuota().intValue()); assertEquals(usage, groupCapacity.getUsage().intValue()); assertEquals(maxSize, groupCapacity.getMaxSize().intValue()); assertEquals(maxAggrCount, groupCapacity.getMaxAggrCount().intValue()); assertEquals(maxAggrSize, groupCapacity.getMaxAggrSize().intValue()); assertEquals(tenant, groupCapacity.getNamespaceId()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/DefaultHistoryConfigCleanerTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.ApplicationUtils; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.lang.reflect.Method; import java.sql.Timestamp; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @ExtendWith(SpringExtension.class) public class DefaultHistoryConfigCleanerTest { private DefaultHistoryConfigCleaner defaultHistoryConfigCleaner = new DefaultHistoryConfigCleaner(); @Mock private HistoryConfigInfoPersistService historyConfigInfoPersistService; MockedStatic applicationUtilsMockedStatic; MockedStatic configExecutorMocked; MockedStatic envUtilMockedStatic; /** * Sets up. */ @BeforeEach public void setUp() { applicationUtilsMockedStatic = Mockito.mockStatic(ApplicationUtils.class); applicationUtilsMockedStatic.when(() -> ApplicationUtils.getBean(HistoryConfigInfoPersistService.class)) .thenReturn(historyConfigInfoPersistService); configExecutorMocked = Mockito.mockStatic(ConfigExecutor.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); } /** * End. */ @AfterEach public void end() { applicationUtilsMockedStatic.close(); configExecutorMocked.close(); envUtilMockedStatic.close(); } @Test public void test() { HistoryConfigCleaner configCleaner = HistoryConfigCleanerManager.getHistoryConfigCleaner("nacos"); assertEquals(configCleaner.getName(), "nacos"); } @Test public void testCleanHistoryConfig() throws Exception { defaultHistoryConfigCleaner.cleanHistoryConfig(); Mockito.verify(historyConfigInfoPersistService, Mockito.times(1)) .removeConfigHistory(any(Timestamp.class), anyInt()); } @Test public void testGetRetentionDays() throws Exception { Method method = DefaultHistoryConfigCleaner.class.getDeclaredMethod("getRetentionDays"); method.setAccessible(true); Method setRetentionDaysMethod = PropertyUtil.class.getDeclaredMethod("setConfigRententionDays"); setRetentionDaysMethod.setAccessible(true); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.retention.days")).thenReturn("-1"); setRetentionDaysMethod.invoke(new PropertyUtil()); assertEquals((int) method.invoke(defaultHistoryConfigCleaner), 30); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.retention.days")).thenReturn("30"); setRetentionDaysMethod.invoke(new PropertyUtil()); assertEquals((int) method.invoke(defaultHistoryConfigCleaner), 30); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.retention.days")).thenReturn("1"); setRetentionDaysMethod.invoke(new PropertyUtil()); assertEquals((int) method.invoke(defaultHistoryConfigCleaner), 1); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpChangeConfigWorkerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.disk.ConfigRocksDbDiskService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.File; import java.lang.reflect.Field; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DumpChangeConfigWorkerTest { @Mock DynamicDataSource dynamicDataSource; @Mock DataSourceService dataSourceService; @Mock ConfigInfoPersistService configInfoPersistService; @Mock HistoryConfigInfoPersistService historyConfigInfoPersistService; @Mock ConfigMigrateService configMigrateService; DumpChangeConfigWorker dumpChangeConfigWorker; MockedStatic dynamicDataSourceMockedStatic; MockedStatic envUtilMockedStatic; @BeforeEach void init() throws Exception { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(EnvUtil.getNacosHome()).thenReturn(System.getProperty("user.home") + File.separator + "tmp"); when(EnvUtil.getProperty(eq(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG), eq(Boolean.class), eq(false))).thenReturn(false); dynamicDataSourceMockedStatic.when(DynamicDataSource::getInstance).thenReturn(dynamicDataSource); Field[] declaredFields = ConfigDiskServiceFactory.class.getDeclaredFields(); for (Field filed : declaredFields) { if (filed.getName().equals("configDiskService")) { filed.setAccessible(true); filed.set(null, createDiskService()); } } dumpChangeConfigWorker = new DumpChangeConfigWorker(configInfoPersistService, historyConfigInfoPersistService, configMigrateService, new Timestamp(System.currentTimeMillis())); } protected ConfigDiskService createDiskService() { return new ConfigRocksDbDiskService(); } @AfterEach void after() throws IllegalAccessException { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); ConfigDiskServiceFactory.getInstance().clearAll(); ConfigDiskServiceFactory.getInstance().clearAllGray(); Field[] declaredFields = ConfigDiskServiceFactory.class.getDeclaredFields(); for (Field filed : declaredFields) { if (filed.getName().equals("configDiskService")) { filed.setAccessible(true); filed.set(null, null); } } } @Test void testDumpChangeIfOff() { PropertyUtil.setDumpChangeOn(false); dumpChangeConfigWorker.run(); Mockito.verify(historyConfigInfoPersistService, times(0)).findDeletedConfig(any(), anyLong(), anyInt(), any()); } @Test void testDumpChangeOfDeleteConfigs() { PropertyUtil.setDumpChangeOn(true); dumpChangeConfigWorker.setPageSize(3); //mock delete first page List firstPageDeleted = new ArrayList<>(); Timestamp startTime = dumpChangeConfigWorker.startTime; String dataIdPrefix = "d12345"; firstPageDeleted.add(createConfigInfoStateWrapper(dataIdPrefix, 1, startTime.getTime() + 1)); firstPageDeleted.add(createConfigInfoStateWrapper(dataIdPrefix, 2, startTime.getTime() + 2)); firstPageDeleted.add(createConfigInfoStateWrapper(dataIdPrefix, 3, startTime.getTime() + 3)); //pre set cache for id1 preSetCache(dataIdPrefix, 1, System.currentTimeMillis()); assertEquals("encrykey" + 1, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getEncryptedDataKey()); Mockito.when(historyConfigInfoPersistService.findDeletedConfig(eq(startTime), eq(0L), eq(3), eq("formal"))).thenReturn(firstPageDeleted); //mock delete config query is null Mockito.when(configInfoPersistService.findConfigInfoState(eq(dataIdPrefix + 1), eq("group" + 1), eq("tenant" + 1))) .thenReturn(null); Mockito.when(configInfoPersistService.findConfigInfoState(eq(dataIdPrefix + 2), eq("group" + 2), eq("tenant" + 2))) .thenReturn(null); dumpChangeConfigWorker.run(); //expect delete page return pagesize and will select second page Mockito.verify(historyConfigInfoPersistService, times(1)).findDeletedConfig(eq(startTime), eq(3L), eq(3), eq("formal")); //expect cache to be cleared. assertNull(ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1))); } @Test void testDumpChangeOfChangedConfigsNewTimestampOverride() { PropertyUtil.setDumpChangeOn(true); dumpChangeConfigWorker.setPageSize(3); //mock delete first page Timestamp startTime = dumpChangeConfigWorker.startTime; String dataIdPrefix = "dataId6789087"; //pre set cache for id1 with old timestamp preSetCache(dataIdPrefix, 1, startTime.getTime() - 1); assertEquals(startTime.getTime() - 1, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); List firstChanged = new ArrayList<>(); firstChanged.add(createConfigInfoStateWrapper(dataIdPrefix, 1, startTime.getTime() + 1)); Mockito.when(configInfoPersistService.findChangeConfig(eq(startTime), eq(0L), eq(3))).thenReturn(firstChanged); //mock change config query obj //1 timestamp-new&content-new ConfigInfoWrapper configInfoWrapperNewForId1 = createConfigInfoWrapper(dataIdPrefix, 1, startTime.getTime() + 2); configInfoWrapperNewForId1.setContent("content" + System.currentTimeMillis()); Mockito.when(configInfoPersistService.findConfigInfo(eq(dataIdPrefix + 1), eq("group" + 1), eq("tenant" + 1))) .thenReturn(configInfoWrapperNewForId1); dumpChangeConfigWorker.run(); //expect cache to be cleared. assertEquals(startTime.getTime() + 2, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); assertEquals(MD5Utils.md5Hex(configInfoWrapperNewForId1.getContent(), "UTF-8"), ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getMd5()); } @Test void testDumpChangeOfChangedConfigsNewTimestampEqualMd5() { PropertyUtil.setDumpChangeOn(true); dumpChangeConfigWorker.setPageSize(3); //mock delete first page Timestamp startTime = dumpChangeConfigWorker.startTime; String dataIdPrefix = "dataIdnewtimestamp"; //pre set cache for id1 with old timestamp preSetCache(dataIdPrefix, 1, startTime.getTime() - 1); assertEquals(startTime.getTime() - 1, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); List firstChanged = new ArrayList<>(); firstChanged.add(createConfigInfoStateWrapper(dataIdPrefix, 1, startTime.getTime() + 1)); Mockito.when(configInfoPersistService.findChangeConfig(eq(startTime), eq(0L), eq(3))).thenReturn(firstChanged); //mock change config query obj //1 timestamp-new&content-old ConfigInfoWrapper configInfoWrapperNewForId1 = createConfigInfoWrapper(dataIdPrefix, 1, startTime.getTime() + 2); Mockito.when(configInfoPersistService.findConfigInfo(eq(dataIdPrefix + 1), eq("group" + 1), eq("tenant" + 1))) .thenReturn(configInfoWrapperNewForId1); dumpChangeConfigWorker.run(); //expect cache assertEquals(startTime.getTime() + 2, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); assertEquals(MD5Utils.md5Hex(configInfoWrapperNewForId1.getContent(), "UTF-8"), ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getMd5()); } @Test void testDumpChangeOfChangedConfigsOldTimestamp() { PropertyUtil.setDumpChangeOn(true); dumpChangeConfigWorker.setPageSize(3); //mock delete first page Timestamp startTime = dumpChangeConfigWorker.startTime; String dataIdPrefix = "dataIdOldTimestamp"; //pre set cache for id1 with old timestamp preSetCache(dataIdPrefix, 1, startTime.getTime() - 1); assertEquals(startTime.getTime() - 1, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); List firstChanged = new ArrayList<>(); firstChanged.add(createConfigInfoStateWrapper(dataIdPrefix, 1, startTime.getTime() - 2)); Mockito.when(configInfoPersistService.findChangeConfig(eq(startTime), eq(0L), eq(3))).thenReturn(firstChanged); //mock change config query obj //1 timestamp-new&content-new ConfigInfoWrapper configInfoWrapperNewForId1 = createConfigInfoWrapper(dataIdPrefix, 1, startTime.getTime() - 2); configInfoWrapperNewForId1.setContent("content" + System.currentTimeMillis()); Mockito.when(configInfoPersistService.findConfigInfo(eq(dataIdPrefix + 1), eq("group" + 1), eq("tenant" + 1))) .thenReturn(configInfoWrapperNewForId1); dumpChangeConfigWorker.run(); //expect cache to be cleared. assertEquals(startTime.getTime() - 1, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); assertEquals(MD5Utils.md5Hex("content" + 1, "UTF-8"), ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getMd5()); } @Test void testDumpChangeOfChangedConfigsEqualsTimestampMd5Update() { PropertyUtil.setDumpChangeOn(true); dumpChangeConfigWorker.setPageSize(3); //mock delete first page Timestamp startTime = dumpChangeConfigWorker.startTime; String dataIdPrefix = "dataIdEqualsTimestampMd5Update"; //pre set cache for id1 with old timestamp preSetCache(dataIdPrefix, 1, startTime.getTime() - 1); assertEquals(startTime.getTime() - 1, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); List firstChanged = new ArrayList<>(); firstChanged.add(createConfigInfoStateWrapper(dataIdPrefix, 1, startTime.getTime() - 1)); Mockito.when(configInfoPersistService.findChangeConfig(eq(startTime), eq(0L), eq(3))).thenReturn(firstChanged); //mock change config query obj //1 timestamp-new&content-new ConfigInfoWrapper configInfoWrapperNewForId1 = createConfigInfoWrapper(dataIdPrefix, 1, startTime.getTime() - 1); configInfoWrapperNewForId1.setContent("content" + System.currentTimeMillis()); Mockito.when(configInfoPersistService.findConfigInfo(eq(dataIdPrefix + 1), eq("group" + 1), eq("tenant" + 1))) .thenReturn(configInfoWrapperNewForId1); dumpChangeConfigWorker.run(); //expect cache to be cleared. assertEquals(startTime.getTime() - 1, ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getLastModifiedTs()); assertEquals(MD5Utils.md5Hex(configInfoWrapperNewForId1.getContent(), "UTF-8"), ConfigCacheService.getContentCache(GroupKey.getKeyTenant(dataIdPrefix + 1, "group" + 1, "tenant" + 1)).getConfigCache() .getMd5()); } private void preSetCache(String dataIdPrefix, long id, long timeStamp) { ConfigCacheService.dumpWithMd5(dataIdPrefix + id, "group" + id, "tenant" + id, "content" + id, MD5Utils.md5Hex("content" + id, "UTF-8"), timeStamp, "json", "encrykey" + id); } private ConfigInfoStateWrapper createConfigInfoStateWrapper(String dataIdPreFix, long id, long timeStamp) { ConfigInfoStateWrapper configInfoWrapper = new ConfigInfoStateWrapper(); configInfoWrapper.setDataId(dataIdPreFix + id); configInfoWrapper.setGroup("group" + id); configInfoWrapper.setTenant("md5" + id); configInfoWrapper.setTenant("tenant" + id); configInfoWrapper.setId(id); configInfoWrapper.setLastModified(timeStamp); return configInfoWrapper; } private ConfigInfoWrapper createConfigInfoWrapper(String dataIdPreFix, long id, long timeStamp) { ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); configInfoWrapper.setDataId(dataIdPreFix + id); configInfoWrapper.setGroup("group" + id); configInfoWrapper.setMd5("md5" + id); configInfoWrapper.setContent("content" + id); configInfoWrapper.setTenant("tenant" + id); configInfoWrapper.setId(id); configInfoWrapper.setLastModified(timeStamp); return configInfoWrapper; } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpChangeGrayConfigWorkerTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) public class DumpChangeGrayConfigWorkerTest { DumpChangeGrayConfigWorker dumpGrayConfigWorker; @Mock ConfigInfoGrayPersistService configInfoGrayPersistService; @Mock HistoryConfigInfoPersistService historyConfigInfoPersistService; @Mock ConfigMigrateService configMigrateService; static MockedStatic envUtilMockedStatic; static MockedStatic configCacheServiceMockedStatic; static MockedStatic configExecutorMockedStatic; /** * Clean up. */ @AfterEach public void after() { envUtilMockedStatic.close(); configCacheServiceMockedStatic.close(); configExecutorMockedStatic.close(); } @BeforeEach public void setUp() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); configExecutorMockedStatic = Mockito.mockStatic(ConfigExecutor.class); envUtilMockedStatic.when(() -> EnvUtil.getAvailableProcessors(anyInt())).thenReturn(2); dumpGrayConfigWorker = new DumpChangeGrayConfigWorker(configInfoGrayPersistService, new Timestamp(System.currentTimeMillis()), historyConfigInfoPersistService, configMigrateService); } @Test public void testdumpGrayConfigWorkerRun() { List mockList = new ArrayList<>(); ConfigInfoGrayWrapper mock1 = mock(1); mockList.add(mock1); when(configInfoGrayPersistService.findChangeConfig(any(Timestamp.class), any(long.class), eq(100))).thenReturn( mockList); configCacheServiceMockedStatic.when(() -> ConfigCacheService.getContentMd5( eq(GroupKey.getKeyTenant(mock1.getDataId(), mock1.getGroup(), mock1.getTenant())))).thenReturn(""); dumpGrayConfigWorker.run(); //verify dump gray executed configCacheServiceMockedStatic.verify( () -> ConfigCacheService.dumpGray(eq(mock1.getDataId()), eq(mock1.getGroup()), eq(mock1.getTenant()), eq(mock1.getGrayName()), eq(mock1.getGrayRule()), eq(mock1.getContent()), eq(mock1.getLastModified()), eq(mock1.getEncryptedDataKey()))); //verify task scheduled configExecutorMockedStatic.verify(() -> ConfigExecutor.scheduleConfigChangeTask(any(DumpChangeGrayConfigWorker.class), eq(PropertyUtil.getDumpChangeWorkerInterval()), eq(TimeUnit.MILLISECONDS))); } ConfigInfoGrayWrapper mock(int id) { ConfigInfoGrayWrapper configInfoGrayWrapper = new ConfigInfoGrayWrapper(); configInfoGrayWrapper.setDataId("mockdataid" + id); configInfoGrayWrapper.setGroup("mockgroup" + id); configInfoGrayWrapper.setTenant("tenant" + id); configInfoGrayWrapper.setContent("content" + id); configInfoGrayWrapper.setGrayName("graytags1" + id); configInfoGrayWrapper.setGrayRule( "{\"type\":\"tagv2\",\"version\":\"1.0.0\",\"expr\":\"middleware.server.key\\u003dgray123\",\"priority\":1}"); return configInfoGrayWrapper; } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.disk.ConfigRocksDbDiskService; import com.alibaba.nacos.config.server.service.dump.processor.DumpProcessor; import com.alibaba.nacos.config.server.service.dump.task.DumpTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import java.lang.reflect.Field; 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.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DumpProcessorTest { @Mock DynamicDataSource dynamicDataSource; @Mock DataSourceService dataSourceService; @Mock ConfigInfoPersistService configInfoPersistService; @Mock ConfigInfoGrayPersistService configInfoGrayPersistService; @Mock ConfigMigrateService configMigrateService; ExternalDumpService dumpService; DumpProcessor dumpProcessor; MockedStatic dynamicDataSourceMockedStatic; MockedStatic envUtilMockedStatic; @BeforeEach void init() throws Exception { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(EnvUtil.getNacosHome()).thenReturn(System.getProperty("user.home")); when(EnvUtil.getProperty(eq(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG), eq(Boolean.class), eq(false))).thenReturn(false); when(EnvUtil.getProperty(eq("memory_limit_file_path"), eq("/sys/fs/cgroup/memory/memory.limit_in_bytes"))).thenReturn( "/sys/fs/cgroup/memory/memory.limit_in_bytes"); dynamicDataSourceMockedStatic.when(DynamicDataSource::getInstance).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); dumpService = new ExternalDumpService(configInfoPersistService, null, null, configInfoGrayPersistService, null, configMigrateService); dumpProcessor = new DumpProcessor(configInfoPersistService, configInfoGrayPersistService); Field[] declaredFields = ConfigDiskServiceFactory.class.getDeclaredFields(); for (Field filed : declaredFields) { if (filed.getName().equals("configDiskService")) { filed.setAccessible(true); filed.set(null, createDiskService()); } } } protected ConfigDiskService createDiskService() { return new ConfigRocksDbDiskService(); } @AfterEach void after() throws Exception { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); ConfigDiskServiceFactory.getInstance().clearAll(); ConfigDiskServiceFactory.getInstance().clearAllGray(); Field[] declaredFields = ConfigDiskServiceFactory.class.getDeclaredFields(); for (Field filed : declaredFields) { if (filed.getName().equals("configDiskService")) { filed.setAccessible(true); filed.set(null, null); } } } @Test void testDumpNormalAndRemove() throws IOException { String dataId = "testDataId"; String group = "testGroup"; String tenant = "testTenant"; String content = "testContent你好" + System.currentTimeMillis(); long time = System.currentTimeMillis(); ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); configInfoWrapper.setDataId(dataId); configInfoWrapper.setGroup(group); configInfoWrapper.setTenant(tenant); configInfoWrapper.setContent(content); configInfoWrapper.setLastModified(time); Mockito.when(configInfoPersistService.findConfigInfo(eq(dataId), eq(group), eq(tenant))) .thenReturn(configInfoWrapper); String handlerIp = "127.0.0.1"; long lastModified = System.currentTimeMillis(); DumpTask dumpTask = new DumpTask(GroupKey2.getKey(dataId, group, tenant), null, lastModified, handlerIp); boolean process = dumpProcessor.process(dumpTask); assertTrue(process); //Check cache CacheItem contentCache = ConfigCacheService.getContentCache(GroupKey2.getKey(dataId, group, tenant)); assertEquals(MD5Utils.md5Hex(content, "UTF-8"), contentCache.getConfigCache().getMd5()); assertEquals(time, contentCache.getConfigCache().getLastModifiedTs()); //check disk String contentFromDisk = ConfigDiskServiceFactory.getInstance().getContent(dataId, group, tenant); assertEquals(content, contentFromDisk); // remove Mockito.when(configInfoPersistService.findConfigInfo(eq(dataId), eq(group), eq(tenant))).thenReturn(null); boolean processRemove = dumpProcessor.process(dumpTask); assertTrue(processRemove); //Check cache CacheItem contentCacheAfterRemove = ConfigCacheService.getContentCache(GroupKey2.getKey(dataId, group, tenant)); assertTrue(contentCacheAfterRemove == null); //check disk String contentFromDiskAfterRemove = ConfigDiskServiceFactory.getInstance().getContent(dataId, group, tenant); assertNull(contentFromDiskAfterRemove); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpProcessorUserRwaDiskTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigRawDiskService; 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.junit.jupiter.MockitoExtension; import java.io.IOException; @ExtendWith(MockitoExtension.class) class DumpProcessorUserRwaDiskTest extends DumpProcessorTest { @BeforeEach public void init() throws Exception { super.init(); } @Override protected ConfigDiskService createDiskService() { return new ConfigRawDiskService(); } @AfterEach public void after() throws Exception { super.after(); } @Test public void testDumpNormalAndRemove() throws IOException { super.testDumpNormalAndRemove(); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/DumpServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.config.server.manager.TaskManager; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.dump.task.DumpTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import java.util.concurrent.TimeUnit; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; @ExtendWith(SpringExtension.class) class DumpServiceTest { private static final String BETA_TABLE_NAME = "config_info_beta"; private static final String TAG_TABLE_NAME = "config_info_tag"; @Mock DefaultHistoryConfigCleaner defaultHistoryConfigCleaner = new DefaultHistoryConfigCleaner(); @Mock ConfigInfoPersistService configInfoPersistService; @Mock NamespacePersistService namespacePersistService; @Mock HistoryConfigInfoPersistService historyConfigInfoPersistService; @Mock ConfigInfoGrayPersistService configInfoGrayPersistService; @Mock ServerMemberManager memberManager; @Mock ConfigMigrateService configMigrateService; MockedStatic envUtilMockedStatic; MockedStatic configExecutorMocked; MockedStatic propertyUtilMockedStatic; MockedStatic historyConfigCleanerManagerMockedStatic; @Mock private DataSourceService dataSourceService; private DumpService dumpService; @Mock private TaskManager dumpTaskMgr; @BeforeEach void setUp() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); propertyUtilMockedStatic = Mockito.mockStatic(PropertyUtil.class); propertyUtilMockedStatic.when(() -> PropertyUtil.getAllDumpPageSize()).thenReturn(100); propertyUtilMockedStatic.when(() -> PropertyUtil.getDumpChangeWorkerInterval()).thenReturn(1000 * 60L); ReflectionTestUtils.setField(DynamicDataSource.getInstance(), "localDataSourceService", dataSourceService); ReflectionTestUtils.setField(DynamicDataSource.getInstance(), "basicDataSourceService", dataSourceService); dumpService = new ExternalDumpService(configInfoPersistService, namespacePersistService, historyConfigInfoPersistService, configInfoGrayPersistService, memberManager, configMigrateService); configExecutorMocked = Mockito.mockStatic(ConfigExecutor.class); historyConfigCleanerManagerMockedStatic = Mockito.mockStatic(HistoryConfigCleanerManager.class); historyConfigCleanerManagerMockedStatic.when( () -> HistoryConfigCleanerManager.getHistoryConfigCleaner(anyString())) .thenReturn(defaultHistoryConfigCleaner); } @AfterEach void after() { envUtilMockedStatic.close(); configExecutorMocked.close(); propertyUtilMockedStatic.close(); historyConfigCleanerManagerMockedStatic.close(); } @Test void dumpRequest() throws Throwable { String dataId = "12345667dataId"; String group = "234445group"; DumpRequest dumpRequest = DumpRequest.create(dataId, group, "testtenant", System.currentTimeMillis(), "127.0.0.1"); // TaskManager dumpTaskMgr; ReflectionTestUtils.setField(dumpService, "dumpTaskMgr", dumpTaskMgr); Mockito.doNothing().when(dumpTaskMgr).addTask(any(), any()); dumpService.dump(dumpRequest); Mockito.verify(dumpTaskMgr, times(1)) .addTask(eq(GroupKey.getKeyTenant(dataId, group, dumpRequest.getTenant())), any(DumpTask.class)); dumpRequest.setGrayName("tag_123"); dumpService.dump(dumpRequest); Mockito.verify(dumpTaskMgr, times(1)).addTask( eq(GroupKey.getKeyTenant(dataId, group, dumpRequest.getTenant()) + "+gray+" + dumpRequest.getGrayName()), any(DumpTask.class)); } @Test void dumpOperate() throws Throwable { configExecutorMocked.when( () -> ConfigExecutor.scheduleConfigTask(any(Runnable.class), anyInt(), anyInt(), any(TimeUnit.class))) .thenAnswer(invocation -> null); configExecutorMocked.when( () -> ConfigExecutor.scheduleConfigChangeTask(any(Runnable.class), anyInt(), any(TimeUnit.class))) .thenAnswer(invocation -> null); Mockito.when(namespacePersistService.isExistTable(BETA_TABLE_NAME)).thenReturn(true); Mockito.when(namespacePersistService.isExistTable(TAG_TABLE_NAME)).thenReturn(true); Mockito.when(configInfoPersistService.findConfigMaxId()).thenReturn(300L); dumpService.dumpOperate(); // expect dump Mockito.verify(configInfoPersistService, times(1)).findAllConfigInfoFragment(0, 100, true); Mockito.verify(configInfoPersistService, times(1)).findConfigMaxId(); Mockito.verify(configInfoGrayPersistService, times(1)).configInfoGrayCount(); // expect dump formal,beta,tag,history clear,config change task to be scheduled. // expect config clear history task be scheduled. configExecutorMocked.verify( () -> ConfigExecutor.scheduleConfigTask(any(DumpService.DumpAllProcessorRunner.class), anyLong(), anyLong(), eq(TimeUnit.MINUTES)), times(1)); configExecutorMocked.verify( () -> ConfigExecutor.scheduleConfigTask(any(DumpService.DumpAllGrayProcessorRunner.class), anyLong(), anyLong(), eq(TimeUnit.MINUTES)), times(1)); configExecutorMocked.verify( () -> ConfigExecutor.scheduleConfigChangeTask(any(DumpChangeConfigWorker.class), anyLong(), eq(TimeUnit.MILLISECONDS)), times(1)); configExecutorMocked.verify( () -> ConfigExecutor.scheduleConfigTask(any(DumpService.ConfigHistoryClear.class), anyLong(), anyLong(), eq(TimeUnit.MINUTES)), times(1)); } @Test void clearHistory() { envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("nacos.config.retention.days"))).thenReturn("10"); Mockito.when(memberManager.isFirstIp()).thenReturn(true); DumpService.ConfigHistoryClear configHistoryClear = dumpService.new ConfigHistoryClear( defaultHistoryConfigCleaner); configHistoryClear.run(); Mockito.verify(defaultHistoryConfigCleaner, times(1)).cleanHistoryConfig(); } @Test void testHandleConfigDataChange() { ConfigDataChangeEvent configDataChangeEvent = new ConfigDataChangeEvent("dataId", "group", null, System.currentTimeMillis()); ReflectionTestUtils.setField(dumpService, "dumpTaskMgr", dumpTaskMgr); Mockito.doNothing().when(dumpTaskMgr).addTask(any(), any()); dumpService.handleConfigDataChange(configDataChangeEvent); Mockito.verify(dumpTaskMgr, times(1)).addTask( eq(GroupKey.getKeyTenant(configDataChangeEvent.dataId, configDataChangeEvent.group, configDataChangeEvent.tenant)), any(DumpTask.class)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/HistoryConfigCleanerConfigTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import com.alibaba.nacos.sys.env.EnvUtil; 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.MockedStatic; import org.mockito.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @ExtendWith(SpringExtension.class) class HistoryConfigCleanerConfigTest { MockedStatic envUtilMockedStatic; @BeforeEach public void before() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); } @Test public void test() { envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), any(), anyString())).thenReturn("test"); HistoryConfigCleanerConfig historyConfigCleanerConfig = HistoryConfigCleanerConfig.getInstance(); historyConfigCleanerConfig.getConfigFromEnv(); assertEquals("test", historyConfigCleanerConfig.getActiveHistoryConfigCleaner()); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), any(), anyString())).thenReturn(null); historyConfigCleanerConfig.getConfigFromEnv(); assertEquals("nacos", historyConfigCleanerConfig.getActiveHistoryConfigCleaner()); } @AfterEach public void after() { envUtilMockedStatic.close(); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/HistoryConfigCleanerManagerTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class HistoryConfigCleanerManagerTest { @Test public void testHistoryConfigCleanerManangerTest() { HistoryConfigCleaner cleaner = HistoryConfigCleanerManager.getHistoryConfigCleaner( HistoryConfigCleanerConfig.getInstance().getActiveHistoryConfigCleaner()); assertEquals(cleaner.getName(), "nacos"); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/disk/ConfigDiskServiceFactoryTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.disk; 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.junit.jupiter.MockitoExtension; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(MockitoExtension.class) class ConfigDiskServiceFactoryTest { @BeforeEach void before() throws Exception { clearDiskInstance(); } @AfterEach void after() { } @Test void getRawDiskInstance() { System.setProperty("config_disk_type", "rawdisk"); ConfigDiskService instance = ConfigDiskServiceFactory.getInstance(); assertTrue(instance instanceof ConfigRawDiskService); } @Test void getRockDbDiskInstance() { System.setProperty("config_disk_type", "rocksdb"); ConfigDiskService instance = ConfigDiskServiceFactory.getInstance(); assertTrue(instance instanceof ConfigRocksDbDiskService); } @Test void getDefaultRawDiskInstance() { System.setProperty("config_disk_type", "123"); ConfigDiskService instance = ConfigDiskServiceFactory.getInstance(); assertTrue(instance instanceof ConfigRawDiskService); } private void clearDiskInstance() throws Exception { Field configDiskService = ConfigDiskServiceFactory.class.getDeclaredField("configDiskService"); configDiskService.setAccessible(true); configDiskService.set(null, null); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/disk/ConfigRawDiskServiceTest.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.disk; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.file.Path; import java.nio.file.Paths; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; class ConfigRawDiskServiceTest { private String cachedOsName; @BeforeEach void setUp() throws Exception { cachedOsName = System.getProperty("os.name"); } private boolean isWindows() { return cachedOsName.toLowerCase().startsWith("win"); } /** * 测试获取文件路径. */ @Test void testTargetFile() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method method = ConfigRawDiskService.class.getDeclaredMethod("targetFile", String.class, String.class, String.class); method.setAccessible(true); File result = (File) method.invoke(null, "aaaa-dsaknkf", "aaaa.dsaknkf", "aaaa:dsaknkf"); // 分解路径 Path path = Paths.get(result.getPath()); Path parent = path.getParent(); Path grandParent = parent.getParent(); // 获取最后三段路径 String lastSegment = path.getFileName().toString(); String secondLastSegment = parent.getFileName().toString(); String thirdLastSegment = grandParent.getFileName().toString(); assertEquals(isWindows() ? "aaaa-dsaknkf" : thirdLastSegment, thirdLastSegment); assertEquals(isWindows() ? "aaaa.dsaknkf" : secondLastSegment, secondLastSegment); assertEquals(isWindows() ? "aaaa%A5%dsaknkf" : lastSegment, lastSegment); } @Test void testTargetFileWithInvalidParam() { assertThrows(NacosRuntimeException.class, () -> ConfigRawDiskService.targetFile("../aaa", "testG", "testNS")); assertThrows(NacosRuntimeException.class, () -> ConfigRawDiskService.targetFile("testD", "../aaa", "testNS")); assertThrows(NacosRuntimeException.class, () -> ConfigRawDiskService.targetFile("testD", "testG", "../aaa")); } /** * 测试获取beta文件路径. */ @Test void testTargetGrayFile() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method method = ConfigRawDiskService.class.getDeclaredMethod("targetGrayFile", String.class, String.class, String.class, String.class); method.setAccessible(true); File result = (File) method.invoke(null, "data345678", "group3456", "tenant1234", "graynem4567"); // 分解路径 Path path = Paths.get(result.getPath()); Path parent = path.getParent(); Path grandParent = parent.getParent(); Path grand2Parent = grandParent.getParent(); // 获取最后三段路径 String fourthLastSegment = grand2Parent.getFileName().toString(); assertEquals(fourthLastSegment, "tenant1234"); String thirdLastSegment = grandParent.getFileName().toString(); assertEquals(isWindows() ? "aaaa-dsaknkf" : thirdLastSegment, "group3456"); String secondLastSegment = parent.getFileName().toString(); assertEquals(isWindows() ? "aaaa-dsaknkf" : secondLastSegment, "data345678"); String lastSegment = path.getFileName().toString(); assertEquals(isWindows() ? "aaaa-dsaknkf" : lastSegment, "graynem4567"); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/processor/DumpAllGrayProcessorTest.java ================================================ /* * Copyright 1999-$toady.year Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.processor; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.common.task.NacosTask; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.dump.ExternalDumpService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.task.DumpAllGrayTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; 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.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DumpAllGrayProcessorTest { private static final int PAGE_SIZE = 100; @Mock DynamicDataSource dynamicDataSource; @Mock DataSourceService dataSourceService; @Mock ConfigInfoGrayPersistService configInfoGrayPersistService; DumpAllProcessor dumpAllProcessor; DumpAllGrayProcessor dumpAllGrayProcessor; ExternalDumpService dumpService; MockedStatic dynamicDataSourceMockedStatic; MockedStatic propertyUtilMockedStatic; @Mock ConfigInfoPersistService configInfoPersistService; MockedStatic envUtilMockedStatic; private String mockMem = "tmpmocklimitfile.txt"; @BeforeEach void init() throws Exception { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); propertyUtilMockedStatic = Mockito.mockStatic(PropertyUtil.class); propertyUtilMockedStatic.when(PropertyUtil::getAllDumpPageSize).thenReturn(100); dumpAllGrayProcessor = new DumpAllGrayProcessor(configInfoGrayPersistService); when(EnvUtil.getNacosHome()).thenReturn(System.getProperty("user.home")); when(EnvUtil.getProperty(eq(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG), eq(Boolean.class), eq(false))).thenReturn(false); dynamicDataSourceMockedStatic.when(DynamicDataSource::getInstance).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); dumpService = new ExternalDumpService(configInfoPersistService, null, null, configInfoGrayPersistService, null, null); dumpAllProcessor = new DumpAllProcessor(configInfoPersistService); envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("memory_limit_file_path"), eq("/sys/fs/cgroup/memory/memory.limit_in_bytes"))).thenReturn(mockMem); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); propertyUtilMockedStatic.close(); } @Test void testProcessWithInvalidTaskType() { NacosTask invalidTask = mock(NacosTask.class); boolean result = dumpAllGrayProcessor.process(invalidTask); assertFalse(result); verify(configInfoGrayPersistService, never()).configInfoGrayCount(); } @Test void testProcessWithValidTaskType() throws Exception { final DumpAllGrayTask validTask = mock(DumpAllGrayTask.class); List configList = new ArrayList<>(); configList.add(createGrayWrapper("dataId-1", "group-1")); Page page = new Page<>(); when(configInfoGrayPersistService.configInfoGrayCount()).thenReturn(1); when(configInfoGrayPersistService.findAllConfigInfoGrayForDumpAll(anyInt(), anyInt())).thenReturn(page); boolean result = dumpAllGrayProcessor.process(validTask); assertTrue(result); verify(configInfoGrayPersistService, times(1)).configInfoGrayCount(); verify(configInfoGrayPersistService, times(1)).findAllConfigInfoGrayForDumpAll(anyInt(), anyInt()); } @Test void testPaginationLogic() { int totalConfigs = PAGE_SIZE * 2 + 50; int expectedPage = (int) Math.ceil(totalConfigs * 1.0 / PAGE_SIZE); System.out.println("totalConfigs: " + totalConfigs + " , expectedPage: " + expectedPage); reset(configInfoGrayPersistService); when(configInfoGrayPersistService.configInfoGrayCount()).thenReturn(totalConfigs); Page pageOne = new Page<>(); when(configInfoGrayPersistService.findAllConfigInfoGrayForDumpAll(eq(1), anyInt())).thenReturn(pageOne); DumpAllGrayTask task = mock(DumpAllGrayTask.class); boolean result = dumpAllGrayProcessor.process(task); assertTrue(result); verify(configInfoGrayPersistService, atLeastOnce()).findAllConfigInfoGrayForDumpAll(anyInt(), anyInt()); } @Test void testInteractionWithConfigCacheService() { DumpAllGrayTask task = mock(DumpAllGrayTask.class); Page page = new Page<>(); when(configInfoGrayPersistService.configInfoGrayCount()).thenReturn(1); when(configInfoGrayPersistService.findAllConfigInfoGrayForDumpAll(anyInt(), anyInt())).thenReturn(page); boolean result = dumpAllGrayProcessor.process(task); assertTrue(result); } /** * test dump all for all check task. */ @Test void testDumpAllGrayOnCheckAll() throws Exception { ConfigInfoGrayWrapper configInfoGrayWrapper1 = createGrayWrapper("data-1", "group-1"); ConfigInfoGrayWrapper configInfoGrayWrapper2 = createGrayWrapper("data-2", "group-2"); long timestamp = System.currentTimeMillis(); configInfoGrayWrapper1.setLastModified(timestamp); configInfoGrayWrapper2.setLastModified(timestamp); Page page = new Page<>(); page.setTotalCount(2); page.setPagesAvailable(2); page.setPageNumber(1); List list = Arrays.asList(configInfoGrayWrapper1, configInfoGrayWrapper2); page.setPageItems(list); Mockito.when(configInfoGrayPersistService.configInfoGrayCount()).thenReturn(2); Mockito.when(configInfoGrayPersistService.findAllConfigInfoGrayForDumpAll(anyInt(), anyInt())).thenReturn(page); final String md51 = MD5Utils.md5Hex(configInfoGrayWrapper1.getContent(), "UTF-8"); final String md52 = MD5Utils.md5Hex(configInfoGrayWrapper2.getContent(), "UTF-8"); long latterTimestamp = timestamp + 999; long earlierTimestamp = timestamp - 999; String encryptedDataKey = "testEncryptedDataKey"; String dataId1 = configInfoGrayWrapper1.getDataId(); String group1 = configInfoGrayWrapper1.getGroup(); String grayName1 = configInfoGrayWrapper1.getGrayName(); String grayRule1 = configInfoGrayWrapper1.getGrayRule(); String tenant1 = configInfoGrayWrapper1.getTenant(); String content1 = configInfoGrayWrapper1.getContent(); String dataId2 = configInfoGrayWrapper2.getDataId(); String group2 = configInfoGrayWrapper2.getGroup(); String grayName2 = configInfoGrayWrapper2.getGrayName(); String grayRule2 = configInfoGrayWrapper2.getGrayRule(); String tenant2 = configInfoGrayWrapper2.getTenant(); String content2 = configInfoGrayWrapper2.getContent(); ConfigCacheService.dumpGray(dataId1, group1, tenant1, grayName1, grayRule1, content1, latterTimestamp, encryptedDataKey); ConfigCacheService.dumpGray(dataId2, group2, tenant2, grayName2, grayRule2, content2, earlierTimestamp, encryptedDataKey); DumpAllGrayTask dumpAllTask = new DumpAllGrayTask(); boolean process = dumpAllGrayProcessor.process(dumpAllTask); assertTrue(process); CacheItem contentCache1 = ConfigCacheService.getContentCache( GroupKey2.getKey(configInfoGrayWrapper1.getDataId(), configInfoGrayWrapper1.getGroup(), configInfoGrayWrapper1.getTenant())); assertEquals(md51, contentCache1.getConfigCacheGray().get(grayName1).getMd5()); assertEquals(latterTimestamp, contentCache1.getConfigCacheGray().get(grayName1).getLastModifiedTs()); String contentFromDisk1 = ConfigDiskServiceFactory.getInstance() .getGrayContent(configInfoGrayWrapper1.getDataId(), configInfoGrayWrapper1.getGroup(), configInfoGrayWrapper1.getTenant(), configInfoGrayWrapper1.getGrayName()); assertEquals(configInfoGrayWrapper1.getContent(), contentFromDisk1); CacheItem contentCache2 = ConfigCacheService.getContentCache( GroupKey2.getKey(configInfoGrayWrapper2.getDataId(), configInfoGrayWrapper2.getGroup(), configInfoGrayWrapper2.getTenant())); assertEquals(md52, contentCache2.getConfigCacheGray().get(grayName2).getMd5()); assertEquals(configInfoGrayWrapper2.getLastModified(), contentCache2.getConfigCacheGray().get(grayName2).getLastModifiedTs()); String contentFromDisk2 = ConfigDiskServiceFactory.getInstance() .getGrayContent(configInfoGrayWrapper2.getDataId(), configInfoGrayWrapper2.getGroup(), configInfoGrayWrapper2.getTenant(), configInfoGrayWrapper2.getGrayName()); assertEquals(configInfoGrayWrapper2.getContent(), contentFromDisk2); } private ConfigInfoGrayWrapper createGrayWrapper(String dataId, String group) { ConfigInfoGrayWrapper wrapper = new ConfigInfoGrayWrapper(); wrapper.setDataId(dataId); wrapper.setGroup(group); wrapper.setTenant("tenant"); wrapper.setGrayName("gray-" + dataId); String grayRule = "{\"type\":\"beta\",\"version\":\"1.0.0\",\"expr\":\"0 0/5 * * * ?\",\"priority\":1}"; wrapper.setGrayRule(grayRule); wrapper.setContent("content"); wrapper.setLastModified(System.currentTimeMillis()); wrapper.setEncryptedDataKey("enc-key"); wrapper.setMd5(MD5Utils.md5Hex(wrapper.getContent(), "UTF-8")); return wrapper; } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/dump/processor/DumpAllProcessorTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.dump.processor; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.dump.ExternalDumpService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.dump.task.DumpAllTask; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.plugin.datasource.constants.CommonConstant; import com.alibaba.nacos.sys.env.EnvUtil; 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 org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.BeanUtils; import java.util.Arrays; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) @Disabled(value = "Github CI will crash in this class unit test. " + "It is suspected that the inability to write to the disk is related to the invocation of System.exit.") class DumpAllProcessorTest { private static int newConfigCount = 1; @Mock DynamicDataSource dynamicDataSource; @Mock DataSourceService dataSourceService; @Mock ConfigInfoGrayPersistService configInfoGrayPersistService; ConfigMigrateService configMigrateService; DumpAllProcessor dumpAllProcessor; ExternalDumpService dumpService; MockedStatic dynamicDataSourceMockedStatic; MockedStatic propertyUtilMockedStatic; @Mock ConfigInfoPersistService configInfoPersistService; MockedStatic envUtilMockedStatic; private String mockMem = "tmpmocklimitfile.txt"; @BeforeEach void init() throws Exception { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); propertyUtilMockedStatic = Mockito.mockStatic(PropertyUtil.class); propertyUtilMockedStatic.when(PropertyUtil::getAllDumpPageSize).thenReturn(100); dumpAllProcessor = new DumpAllProcessor(configInfoPersistService); when(EnvUtil.getNacosHome()).thenReturn(System.getProperty("user.home")); when(EnvUtil.getProperty(eq(CommonConstant.NACOS_PLUGIN_DATASOURCE_LOG), eq(Boolean.class), eq(false))).thenReturn(false); dynamicDataSourceMockedStatic.when(DynamicDataSource::getInstance).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); dumpService = new ExternalDumpService(configInfoPersistService, null, null, configInfoGrayPersistService, null, configMigrateService); dumpAllProcessor = new DumpAllProcessor(configInfoPersistService); envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("memory_limit_file_path"), eq("/sys/fs/cgroup/memory/memory.limit_in_bytes"))).thenReturn(mockMem); } @AfterEach void after() throws Exception { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); propertyUtilMockedStatic.close(); } private ConfigInfoWrapper createNewConfig(int id) { ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); String dataId = "dataIdTime" + newConfigCount; configInfoWrapper.setDataId(dataId); String group = "groupTime" + newConfigCount; configInfoWrapper.setGroup(group); String tenant = "tenantTime" + newConfigCount; configInfoWrapper.setTenant(tenant); String content = "content " + newConfigCount; configInfoWrapper.setContent(content); configInfoWrapper.setId(id); newConfigCount++; return configInfoWrapper; } @Test void testDumpAllOnStartUp() throws Exception { ConfigInfoWrapper configInfoWrapper1 = createNewConfig(1); ConfigInfoWrapper configInfoWrapper2 = createNewConfig(2); long timestamp = System.currentTimeMillis(); configInfoWrapper1.setLastModified(timestamp); configInfoWrapper2.setLastModified(timestamp); Page page = new Page<>(); page.setTotalCount(2); page.setPagesAvailable(2); page.setPageNumber(1); List list = Arrays.asList(configInfoWrapper1, configInfoWrapper2); page.setPageItems(list); Mockito.when(configInfoPersistService.findConfigMaxId()).thenReturn(2L); Mockito.when(configInfoPersistService.findAllConfigInfoFragment(0, PropertyUtil.getAllDumpPageSize(), true)) .thenReturn(page); // For config 1, assign a latter time, to make sure that it would be updated. // For config 2, assign an earlier time, to make sure that it is not be updated. String md51 = MD5Utils.md5Hex(configInfoWrapper1.getContent(), "UTF-8"); String md52 = MD5Utils.md5Hex(configInfoWrapper2.getContent(), "UTF-8"); long latterTimestamp = timestamp + 999; long earlierTimestamp = timestamp - 999; String encryptedDataKey = "testEncryptedDataKey"; ConfigCacheService.dumpWithMd5(configInfoWrapper1.getDataId(), configInfoWrapper1.getGroup(), configInfoWrapper1.getTenant(), configInfoWrapper1.getContent(), md51, latterTimestamp, "json", encryptedDataKey); ConfigCacheService.dumpWithMd5(configInfoWrapper2.getDataId(), configInfoWrapper2.getGroup(), configInfoWrapper2.getTenant(), configInfoWrapper2.getContent(), md52, earlierTimestamp, "json", encryptedDataKey); DumpAllTask dumpAllTask = new DumpAllTask(true); boolean process = dumpAllProcessor.process(dumpAllTask); assertTrue(process); //Check cache CacheItem contentCache1 = ConfigCacheService.getContentCache( GroupKey2.getKey(configInfoWrapper1.getDataId(), configInfoWrapper1.getGroup(), configInfoWrapper1.getTenant())); assertEquals(md51, contentCache1.getConfigCache().getMd5()); // check if config1 is updated assertTrue(timestamp < contentCache1.getConfigCache().getLastModifiedTs()); //check disk String contentFromDisk1 = ConfigDiskServiceFactory.getInstance() .getContent(configInfoWrapper1.getDataId(), configInfoWrapper1.getGroup(), configInfoWrapper1.getTenant()); assertEquals(configInfoWrapper1.getContent(), contentFromDisk1); //Check cache CacheItem contentCache2 = ConfigCacheService.getContentCache( GroupKey2.getKey(configInfoWrapper2.getDataId(), configInfoWrapper2.getGroup(), configInfoWrapper2.getTenant())); assertEquals(MD5Utils.md5Hex(configInfoWrapper2.getContent(), "UTF-8"), contentCache2.getConfigCache().getMd5()); // check if config2 is updated assertEquals(timestamp, contentCache2.getConfigCache().getLastModifiedTs()); //check disk String contentFromDisk2 = ConfigDiskServiceFactory.getInstance() .getContent(configInfoWrapper2.getDataId(), configInfoWrapper2.getGroup(), configInfoWrapper2.getTenant()); assertEquals(configInfoWrapper2.getContent(), contentFromDisk2); } /** * test dump all for all check task. */ @Test void testDumpAllOnCheckAll() throws Exception { ConfigInfoWrapper configInfoWrapper1 = createNewConfig(1); ConfigInfoWrapper configInfoWrapper2 = createNewConfig(2); long timestamp = System.currentTimeMillis(); configInfoWrapper1.setLastModified(timestamp); configInfoWrapper2.setLastModified(timestamp); Page page = new Page<>(); page.setTotalCount(2); page.setPagesAvailable(2); page.setPageNumber(1); List list = Arrays.asList(configInfoWrapper1, configInfoWrapper2); page.setPageItems(list); Mockito.when(configInfoPersistService.findConfigMaxId()).thenReturn(2L); Mockito.when(configInfoPersistService.findAllConfigInfoFragment(0, PropertyUtil.getAllDumpPageSize(), false)) .thenReturn(page); ConfigInfoWrapper configInfoWrapperSingle1 = new ConfigInfoWrapper(); BeanUtils.copyProperties(configInfoWrapper1, configInfoWrapperSingle1); configInfoWrapperSingle1.setContent("content123456"); Mockito.when( configInfoPersistService.findConfigInfo(configInfoWrapper1.getDataId(), configInfoWrapper1.getGroup(), configInfoWrapper1.getTenant())).thenReturn(configInfoWrapperSingle1); ConfigInfoWrapper configInfoWrapperSingle2 = new ConfigInfoWrapper(); BeanUtils.copyProperties(configInfoWrapper2, configInfoWrapperSingle2); configInfoWrapperSingle2.setContent("content123456222"); Mockito.when( configInfoPersistService.findConfigInfo(configInfoWrapper2.getDataId(), configInfoWrapper2.getGroup(), configInfoWrapper2.getTenant())).thenReturn(configInfoWrapperSingle2); // For config 1, assign a latter time, to make sure that it would not be updated. // For config 2, assign an earlier time, to make sure that it would be updated. String md51 = MD5Utils.md5Hex(configInfoWrapper1.getContent(), "UTF-8"); String md52 = MD5Utils.md5Hex(configInfoWrapper2.getContent(), "UTF-8"); long latterTimestamp = timestamp + 999; long earlierTimestamp = timestamp - 999; String encryptedDataKey = "testEncryptedDataKey"; ConfigCacheService.dumpWithMd5(configInfoWrapper1.getDataId(), configInfoWrapper1.getGroup(), configInfoWrapper1.getTenant(), configInfoWrapper1.getContent(), md51, latterTimestamp, "json", encryptedDataKey); ConfigCacheService.dumpWithMd5(configInfoWrapper2.getDataId(), configInfoWrapper2.getGroup(), configInfoWrapper2.getTenant(), configInfoWrapper2.getContent(), md52, earlierTimestamp, "json", encryptedDataKey); DumpAllTask dumpAllTask = new DumpAllTask(false); boolean process = dumpAllProcessor.process(dumpAllTask); assertTrue(process); //Check cache CacheItem contentCache1 = ConfigCacheService.getContentCache( GroupKey2.getKey(configInfoWrapper1.getDataId(), configInfoWrapper1.getGroup(), configInfoWrapper1.getTenant())); // check if config1 is not updated assertEquals(md51, contentCache1.getConfigCache().getMd5()); assertEquals(latterTimestamp, contentCache1.getConfigCache().getLastModifiedTs()); //check disk String contentFromDisk1 = ConfigDiskServiceFactory.getInstance() .getContent(configInfoWrapper1.getDataId(), configInfoWrapper1.getGroup(), configInfoWrapper1.getTenant()); assertEquals(configInfoWrapper1.getContent(), contentFromDisk1); //Check cache CacheItem contentCache2 = ConfigCacheService.getContentCache( GroupKey2.getKey(configInfoWrapper2.getDataId(), configInfoWrapper2.getGroup(), configInfoWrapper2.getTenant())); // check if config2 is updated assertEquals(MD5Utils.md5Hex(configInfoWrapperSingle2.getContent(), "UTF-8"), contentCache2.getConfigCache().getMd5()); assertEquals(configInfoWrapper2.getLastModified(), contentCache2.getConfigCache().getLastModifiedTs()); //check disk String contentFromDisk2 = ConfigDiskServiceFactory.getInstance() .getContent(configInfoWrapper2.getDataId(), configInfoWrapper2.getGroup(), configInfoWrapper2.getTenant()); assertEquals(configInfoWrapperSingle2.getContent(), contentFromDisk2); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/notify/AsyncNotifyServiceTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.notify; import com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest; import com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.remote.RequestCallBack; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.remote.ConfigClusterRpcClientProxy; import com.alibaba.nacos.config.server.service.notify.AsyncNotifyService.AsyncRpcNotifyCallBack; import com.alibaba.nacos.config.server.utils.ConfigExecutor; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.api.common.NodeState; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.InetUtils; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.concurrent.TimeUnit; import static com.alibaba.nacos.config.server.service.notify.AsyncNotifyService.HEALTHY_CHECK_STATUS; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; /** * AsyncNotifyServiceTest. * * @author shiyiyue */ @ExtendWith(SpringExtension.class) class AsyncNotifyServiceTest { @Mock ServerMemberManager serverMemberManager; MockedStatic envUtilMocked; MockedStatic configExecutorMocked; MockedStatic inetUtilsMocked; @Mock private ConfigClusterRpcClientProxy configClusterRpcClientProxy; @BeforeEach void setUp() { envUtilMocked = Mockito.mockStatic(EnvUtil.class); configExecutorMocked = Mockito.mockStatic(ConfigExecutor.class); inetUtilsMocked = Mockito.mockStatic(InetUtils.class); inetUtilsMocked.when(InetUtils::getSelfIP).thenReturn("127.0.0.1"); } @AfterEach void after() { envUtilMocked.close(); inetUtilsMocked.close(); configExecutorMocked.close(); } @Test void testSyncConfigChangeCallback() { long timeStamp = System.currentTimeMillis(); Member member1 = new Member(); member1.setIp("testip1" + timeStamp); member1.setState(NodeState.UP); AsyncNotifyService asyncNotifyService = new AsyncNotifyService(serverMemberManager); ReflectionTestUtils.setField(asyncNotifyService, "configClusterRpcClientProxy", configClusterRpcClientProxy); String dataId = "testDataId" + timeStamp; String group = "testGroup"; AsyncNotifyService.NotifySingleRpcTask notifySingleRpcTask = new AsyncNotifyService.NotifySingleRpcTask(dataId, group, null, null, 0, member1); configExecutorMocked.when( () -> ConfigExecutor.scheduleAsyncNotify(any(Runnable.class), anyLong(), any(TimeUnit.class))) .thenAnswer(invocation -> null); notifySingleRpcTask.setTag("test"); notifySingleRpcTask.setBeta(false); AsyncRpcNotifyCallBack asyncRpcNotifyCallBack = new AsyncRpcNotifyCallBack(asyncNotifyService, notifySingleRpcTask); ConfigChangeClusterSyncResponse successResponse = new ConfigChangeClusterSyncResponse(); //1. success response asyncRpcNotifyCallBack.onResponse(successResponse); //2. fail response successResponse.setResultCode(500); asyncRpcNotifyCallBack.onResponse(successResponse); //3. exception asyncRpcNotifyCallBack.onException(new NacosException()); // expect schedule twice fail or exception response. configExecutorMocked.verify( () -> ConfigExecutor.scheduleAsyncNotify(any(AsyncNotifyService.AsyncRpcTask.class), anyLong(), any(TimeUnit.class)), times(2)); } /** * test HandleConfigDataChangeEvent. expect create a AsyncRpcTask and execute in ConfigExecutor. */ @Test void testHandleConfigDataChangeEvent() { long timeStamp = System.currentTimeMillis(); List memberList = new ArrayList<>(); // member1 success Member member1 = new Member(); member1.setIp("testip1" + timeStamp); member1.setState(NodeState.UP); memberList.add(member1); // member2 exception Member member2 = new Member(); member2.setIp("testip2" + timeStamp); member2.setState(NodeState.UP); memberList.add(member2); // member3 unhealth Member member3 = new Member(); member3.setIp("testip3" + timeStamp); member3.setState(NodeState.DOWN); memberList.add(member3); Mockito.when(serverMemberManager.allMembersWithoutSelf()).thenReturn(memberList); configExecutorMocked.when( () -> ConfigExecutor.scheduleAsyncNotify(any(Runnable.class), anyLong(), any(TimeUnit.class))) .thenAnswer(invocation -> null); String dataId = "testDataId" + timeStamp; String group = "testGroup"; AsyncNotifyService asyncNotifyService = new AsyncNotifyService(serverMemberManager); asyncNotifyService.handleConfigDataChangeEvent( new ConfigDataChangeEvent(dataId, group, null, System.currentTimeMillis())); // expect schedule twice fail or exception response. configExecutorMocked.verify(() -> ConfigExecutor.executeAsyncNotify(any(AsyncNotifyService.AsyncRpcTask.class)), times(1)); } @Test void testExecuteAsyncRpcTask() throws Exception { long timeStamp = System.currentTimeMillis(); String dataId = "testDataId" + timeStamp; String group = "testGroup"; List memberList = new ArrayList<>(); // member1 success Member member1 = new Member(); member1.setIp("testip1" + timeStamp); member1.setState(NodeState.UP); memberList.add(member1); // member2 exception Member member2 = new Member(); member2.setIp("testip2" + timeStamp); member2.setState(NodeState.UP); memberList.add(member2); // member3 unhealth Member member3 = new Member(); member3.setIp("testip3" + timeStamp); member3.setState(NodeState.DOWN); memberList.add(member3); Queue rpcQueue = new LinkedList<>(); for (Member member : memberList) { // grpc report data change only rpcQueue.add( new AsyncNotifyService.NotifySingleRpcTask(dataId, group, null, null, System.currentTimeMillis(), member)); } AsyncNotifyService asyncNotifyService = new AsyncNotifyService(serverMemberManager); ReflectionTestUtils.setField(asyncNotifyService, "configClusterRpcClientProxy", configClusterRpcClientProxy); Mockito.when(serverMemberManager.allMembersWithoutSelf()).thenReturn(memberList); Mockito.when(serverMemberManager.hasMember(eq(member1.getAddress()))).thenReturn(true); Mockito.when(serverMemberManager.hasMember(eq(member2.getAddress()))).thenReturn(true); Mockito.when(serverMemberManager.hasMember(eq(member3.getAddress()))).thenReturn(true); Mockito.when(serverMemberManager.stateCheck(eq(member1.getAddress()), eq(HEALTHY_CHECK_STATUS))) .thenReturn(true); Mockito.when(serverMemberManager.stateCheck(eq(member2.getAddress()), eq(HEALTHY_CHECK_STATUS))) .thenReturn(true); // mock stateCheck fail before notify member3 Mockito.when(serverMemberManager.stateCheck(eq(member3.getAddress()), eq(HEALTHY_CHECK_STATUS))) .thenReturn(false); //mock syncConfigChange exception when notify member2 Mockito.doThrow(new NacosException()).when(configClusterRpcClientProxy) .syncConfigChange(eq(member2), any(ConfigChangeClusterSyncRequest.class), any(RequestCallBack.class)); configExecutorMocked.when( () -> ConfigExecutor.scheduleAsyncNotify(any(Runnable.class), anyLong(), any(TimeUnit.class))) .thenAnswer(invocation -> null); asyncNotifyService.executeAsyncRpcTask(rpcQueue); Mockito.verify(configClusterRpcClientProxy, times(1)) .syncConfigChange(eq(member1), any(ConfigChangeClusterSyncRequest.class), any(RequestCallBack.class)); Mockito.verify(configClusterRpcClientProxy, times(1)) .syncConfigChange(eq(member2), any(ConfigChangeClusterSyncRequest.class), any(RequestCallBack.class)); Mockito.verify(configClusterRpcClientProxy, times(0)) .syncConfigChange(eq(member3), any(ConfigChangeClusterSyncRequest.class), any(RequestCallBack.class)); //verify scheduleAsyncNotify member2 & member3 in task when syncConfigChange fail configExecutorMocked.verify( () -> ConfigExecutor.scheduleAsyncNotify(any(AsyncNotifyService.AsyncRpcTask.class), anyLong(), any(TimeUnit.class)), times(2)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/query/DefaultChainRequestExtractorTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query; import com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest; import com.alibaba.nacos.api.remote.request.RequestMeta; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.TagGrayRule; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.utils.RequestUtil; import jakarta.servlet.http.HttpServletRequest; 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.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class DefaultChainRequestExtractorTest { @InjectMocks private DefaultChainRequestExtractor defaultChainRequestExtractor; @Mock private HttpServletRequest request; private MockedStatic requestUtilMockedStatic; @BeforeEach public void setUp() { requestUtilMockedStatic = Mockito.mockStatic(RequestUtil.class); Mockito.reset(request); } @AfterEach public void tearDown() { requestUtilMockedStatic.close(); } @Test public void extractWithAllParametersShouldReturnCorrectConfigQueryChainRequest() { when(request.getParameter("dataId")).thenReturn("dataId"); when(request.getParameter("group")).thenReturn("group"); when(request.getParameter("namespaceId")).thenReturn("testNamespaceId"); when(request.getParameter("tag")).thenReturn("tag"); when(request.getHeader(VIPSERVER_TAG)).thenReturn("autoTag"); requestUtilMockedStatic.when(() -> RequestUtil.getRemoteIp(request)).thenReturn("127.0.0.1"); ConfigQueryChainRequest result = defaultChainRequestExtractor.extract(request); assertEquals("dataId", result.getDataId()); assertEquals("group", result.getGroup()); assertEquals("testNamespaceId", result.getTenant()); assertEquals("tag", result.getTag()); assertEquals("127.0.0.1", result.getAppLabels().get(BetaGrayRule.CLIENT_IP_LABEL)); assertEquals("tag", result.getAppLabels().get(TagGrayRule.VIP_SERVER_TAG_LABEL)); } @Test public void extractWithEmptyTenantShouldReturnCorrectConfigQueryChainRequest() { when(request.getParameter("dataId")).thenReturn("dataId"); when(request.getParameter("group")).thenReturn("group"); when(request.getParameter("namespaceId")).thenReturn(""); when(request.getParameter("tag")).thenReturn("tag"); when(request.getHeader(VIPSERVER_TAG)).thenReturn("autoTag"); requestUtilMockedStatic.when(() -> RequestUtil.getRemoteIp(request)).thenReturn("127.0.0.1"); ConfigQueryChainRequest result = defaultChainRequestExtractor.extract(request); assertEquals("dataId", result.getDataId()); assertEquals("group", result.getGroup()); assertEquals("", result.getTenant()); assertEquals("tag", result.getTag()); assertEquals("127.0.0.1", result.getAppLabels().get(BetaGrayRule.CLIENT_IP_LABEL)); assertEquals("tag", result.getAppLabels().get(TagGrayRule.VIP_SERVER_TAG_LABEL)); } @Test public void extractWithEmptyTagAndAutoTagShouldReturnCorrectConfigQueryChainRequest() { when(request.getParameter("dataId")).thenReturn("dataId"); when(request.getParameter("group")).thenReturn("group"); when(request.getParameter("namespaceId")).thenReturn("testNamespaceId"); when(request.getParameter("tag")).thenReturn(""); when(request.getHeader(VIPSERVER_TAG)).thenReturn(""); requestUtilMockedStatic.when(() -> RequestUtil.getRemoteIp(request)).thenReturn("127.0.0.1"); ConfigQueryChainRequest result = defaultChainRequestExtractor.extract(request); assertEquals("dataId", result.getDataId()); assertEquals("group", result.getGroup()); assertEquals("testNamespaceId", result.getTenant()); assertEquals("", result.getTag()); assertEquals("127.0.0.1", result.getAppLabels().get(BetaGrayRule.CLIENT_IP_LABEL)); assertNull(result.getAppLabels().get(TagGrayRule.VIP_SERVER_TAG_LABEL)); } @Test public void extractWithAutoTagShouldReturnCorrectConfigQueryChainRequest() { when(request.getParameter("dataId")).thenReturn("dataId"); when(request.getParameter("group")).thenReturn("group"); when(request.getParameter("namespaceId")).thenReturn("testNamespaceId"); when(request.getParameter("tag")).thenReturn(""); when(request.getHeader(VIPSERVER_TAG)).thenReturn("autoTag"); when(RequestUtil.getRemoteIp(request)).thenReturn("127.0.0.1"); ConfigQueryChainRequest result = defaultChainRequestExtractor.extract(request); assertEquals("dataId", result.getDataId()); assertEquals("group", result.getGroup()); assertEquals("testNamespaceId", result.getTenant()); assertEquals("", result.getTag()); assertEquals("127.0.0.1", result.getAppLabels().get(BetaGrayRule.CLIENT_IP_LABEL)); assertEquals("autoTag", result.getAppLabels().get(TagGrayRule.VIP_SERVER_TAG_LABEL)); } @Test public void extractWithConfigQueryRequestShouldReturnCorrectConfigQueryChainRequest() { ConfigQueryRequest configQueryRequest = new ConfigQueryRequest(); configQueryRequest.setDataId("dataId"); configQueryRequest.setGroup("group"); configQueryRequest.setTenant("tenant"); configQueryRequest.setTag("tag"); RequestMeta requestMeta = new RequestMeta(); requestMeta.setClientIp("127.0.0.1"); ConfigQueryChainRequest result = defaultChainRequestExtractor.extract(configQueryRequest, requestMeta); assertEquals("dataId", result.getDataId()); assertEquals("group", result.getGroup()); assertEquals("tenant", result.getTenant()); assertEquals("tag", result.getTag()); assertEquals("127.0.0.1", result.getAppLabels().get(BetaGrayRule.CLIENT_IP_LABEL)); assertEquals("tag", result.getAppLabels().get(TagGrayRule.VIP_SERVER_TAG_LABEL)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/query/handler/ConfigChainEntryHandlerTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import com.alibaba.nacos.config.server.utils.GroupKey2; 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.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; 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; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ConfigChainEntryHandlerTest { @InjectMocks private ConfigChainEntryHandler configChainEntryHandler; private MockedStatic mockedStaticGroupKey2; private MockedStatic mockedStaticConfigCacheService; @Mock private ConfigQueryHandler nextHandler; @Mock private CacheItem cacheItem; @BeforeEach public void setUp() { mockedStaticGroupKey2 = Mockito.mockStatic(GroupKey2.class); mockedStaticConfigCacheService = Mockito.mockStatic(ConfigCacheService.class); configChainEntryHandler.setNextHandler(nextHandler); } @AfterEach public void tearDown() { mockedStaticGroupKey2.close(); mockedStaticConfigCacheService.close(); } @Test public void handleLockSuccessAndCacheItemNotNullShouldInvokeNextHandler() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId("dataId"); request.setGroup("group"); request.setTenant("tenant"); String groupKey = "groupKey"; mockedStaticGroupKey2.when(() -> GroupKey2.getKey(anyString(), anyString(), anyString())).thenReturn(groupKey); mockedStaticConfigCacheService.when(() -> ConfigCacheService.tryConfigReadLock(groupKey)).thenReturn(1); mockedStaticConfigCacheService.when(() -> ConfigCacheService.getContentCache(groupKey)).thenReturn(cacheItem); ConfigQueryChainResponse nextResponse = new ConfigQueryChainResponse(); nextResponse.setResultCode(200); when(nextHandler.handle(request)).thenReturn(nextResponse); ConfigQueryChainResponse response = configChainEntryHandler.handle(request); assertEquals(200, response.getResultCode()); verify(nextHandler, times(1)).handle(request); } @Test public void handleLockSuccessAndCacheItemNullShouldReturnNotFound() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId("dataId"); request.setGroup("group"); request.setTenant("tenant"); String groupKey = "groupKey"; mockedStaticGroupKey2.when(() -> GroupKey2.getKey(anyString(), anyString(), anyString())).thenReturn(groupKey); mockedStaticConfigCacheService.when(() -> ConfigCacheService.tryConfigReadLock(groupKey)).thenReturn(1); mockedStaticConfigCacheService.when(() -> ConfigCacheService.getContentCache(groupKey)).thenReturn(null); ConfigQueryChainResponse response = configChainEntryHandler.handle(request); assertEquals(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND, response.getStatus()); verify(nextHandler, never()).handle(any()); } @Test public void handleLockFailureShouldReturnConflict() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId("dataId"); request.setGroup("group"); request.setTenant("tenant"); String groupKey = "groupKey"; mockedStaticGroupKey2.when(() -> GroupKey2.getKey(anyString(), anyString(), anyString())).thenReturn(groupKey); mockedStaticConfigCacheService.when(() -> ConfigCacheService.tryConfigReadLock(groupKey)).thenReturn(-1); mockedStaticConfigCacheService.when(() -> ConfigCacheService.getContentCache(groupKey)).thenReturn(cacheItem); ConfigQueryChainResponse response = configChainEntryHandler.handle(request); assertEquals(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_QUERY_CONFLICT, response.getStatus()); verify(nextHandler, never()).handle(any()); } @Test public void handleLockSuccessAndNextHandlerNullShouldReturnEmptyResponse() throws IOException { configChainEntryHandler.setNextHandler(null); ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId("dataId"); request.setGroup("group"); request.setTenant("tenant"); String groupKey = "groupKey"; mockedStaticGroupKey2.when(() -> GroupKey2.getKey(anyString(), anyString(), anyString())).thenReturn(groupKey); mockedStaticConfigCacheService.when(() -> ConfigCacheService.tryConfigReadLock(groupKey)).thenReturn(1); mockedStaticConfigCacheService.when(() -> ConfigCacheService.getContentCache(groupKey)).thenReturn(cacheItem); ConfigQueryChainResponse response = configChainEntryHandler.handle(request); assertNull(response.getStatus()); verify(nextHandler, never()).handle(any()); } @Test public void testGetName() { assertEquals("chainEntryHandler", configChainEntryHandler.getName()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/query/handler/ConfigContentTypeHandlerTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.enums.FileTypeEnum; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; 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.junit.jupiter.MockitoExtension; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class ConfigContentTypeHandlerTest { private ConfigContentTypeHandler configContentTypeHandler = new ConfigContentTypeHandler(); @Mock private ConfigQueryHandler nextHandler; @BeforeEach public void setUp() { configContentTypeHandler.setNextHandler(nextHandler); } @Test public void handleConfigNotFoundReturnsSameResponse() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND); when(nextHandler.handle(request)).thenReturn(response); ConfigQueryChainResponse actualResponse = configContentTypeHandler.handle(request); assertEquals(response, actualResponse); } @Test public void handleSpecialTagConfigNotFoundReturnsSameResponse() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.SPECIAL_TAG_CONFIG_NOT_FOUND); when(nextHandler.handle(request)).thenReturn(response); ConfigQueryChainResponse actualResponse = configContentTypeHandler.handle(request); assertEquals(response, actualResponse); } @Test public void handleContentTypeIsNullDefaultsToText() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); when(nextHandler.handle(request)).thenReturn(response); ConfigQueryChainResponse actualResponse = configContentTypeHandler.handle(request); assertEquals(FileTypeEnum.TEXT.getContentType(), actualResponse.getContentType()); } @Test public void handleContentTypeIsNotNullSetsCorrectContentType() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); ConfigQueryChainResponse response = new ConfigQueryChainResponse(); response.setStatus(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL); response.setContentType(FileTypeEnum.JSON.name()); when(nextHandler.handle(request)).thenReturn(response); ConfigQueryChainResponse actualResponse = configContentTypeHandler.handle(request); assertEquals(FileTypeEnum.JSON.getContentType(), actualResponse.getContentType()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/query/handler/FormalHandlerTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigCache; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; 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.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class FormalHandlerTest { @InjectMocks private FormalHandler formalHandler; private MockedStatic configDiskServiceFactoryMockedStatic; private MockedStatic configChainEntryHandlerMockedStatic; @Mock private ConfigDiskService configDiskService; @Mock private CacheItem cacheItem; @Mock private ConfigCache configCache; @BeforeEach public void setUp() throws IOException { configDiskServiceFactoryMockedStatic = Mockito.mockStatic(ConfigDiskServiceFactory.class); configChainEntryHandlerMockedStatic = Mockito.mockStatic(ConfigChainEntryHandler.class); configChainEntryHandlerMockedStatic.when(ConfigChainEntryHandler::getThreadLocalCacheItem) .thenReturn(cacheItem); configDiskServiceFactoryMockedStatic.when(ConfigDiskServiceFactory::getInstance).thenReturn(configDiskService); } @AfterEach public void tearDown() { configDiskServiceFactoryMockedStatic.close(); configChainEntryHandlerMockedStatic.close(); } @Test public void handleContentEmptyShouldReturnConfigNotFound() throws IOException { when(cacheItem.getConfigCache()).thenReturn(configCache); when(configCache.getMd5()).thenReturn("mockMd5"); when(configDiskService.getContent("dataId", "group", "tenant")).thenReturn(""); ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId("dataId"); request.setGroup("group"); request.setTenant("tenant"); ConfigQueryChainResponse response = formalHandler.handle(request); assertEquals(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_NOT_FOUND, response.getStatus()); } @Test public void handleContentNotEmptyShouldReturnConfigFoundFormal() throws IOException { when(cacheItem.getConfigCache()).thenReturn(configCache); when(configCache.getMd5()).thenReturn("mockMd5"); when(configCache.getLastModifiedTs()).thenReturn(123456789L); when(configCache.getEncryptedDataKey()).thenReturn("mockEncryptedDataKey"); when(cacheItem.getType()).thenReturn("mockType"); when(configDiskService.getContent("dataId", "group", "tenant")).thenReturn("mockContent"); ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId("dataId"); request.setGroup("group"); request.setTenant("tenant"); ConfigQueryChainResponse response = formalHandler.handle(request); assertEquals("mockContent", response.getContent()); assertEquals("mockMd5", response.getMd5()); assertEquals(123456789L, response.getLastModified()); assertEquals("mockEncryptedDataKey", response.getEncryptedDataKey()); assertEquals("mockType", response.getConfigType()); assertEquals(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_FORMAL, response.getStatus()); } @Test public void testGetName() { assertEquals("formalHandler", formalHandler.getName()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/query/handler/GrayRuleMatchHandlerTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.model.CacheItem; import com.alibaba.nacos.config.server.model.ConfigCacheGray; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskService; import com.alibaba.nacos.config.server.service.dump.disk.ConfigDiskServiceFactory; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; 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.InjectMocks; import org.mockito.Mock; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class GrayRuleMatchHandlerTest { @InjectMocks private GrayRuleMatchHandler grayRuleMatchHandler; private MockedStatic configChainEntryHandlerMockedStatic; private MockedStatic configDiskServiceFactoryMockedStatic; @Mock private CacheItem cacheItem; @Mock private ConfigCacheGray configCacheGray; @Mock private ConfigDiskService configDiskService; @Mock private ConfigQueryHandler nextHandler; @BeforeEach public void setUp() { configChainEntryHandlerMockedStatic = Mockito.mockStatic(ConfigChainEntryHandler.class); configChainEntryHandlerMockedStatic.when(ConfigChainEntryHandler::getThreadLocalCacheItem) .thenReturn(cacheItem); configDiskServiceFactoryMockedStatic = Mockito.mockStatic(ConfigDiskServiceFactory.class); configDiskServiceFactoryMockedStatic.when(ConfigDiskServiceFactory::getInstance).thenReturn(configDiskService); } @AfterEach public void tearDown() { configChainEntryHandlerMockedStatic.close(); configDiskServiceFactoryMockedStatic.close(); } @Test public void handleNoGrayRulesShouldPassToNextHandler() throws IOException { when(cacheItem.getSortConfigGrays()).thenReturn(Collections.emptyList()); ConfigQueryChainRequest request = new ConfigQueryChainRequest(); ConfigQueryChainResponse expectedResponse = new ConfigQueryChainResponse(); expectedResponse.setResultCode(123); // 假设这是下一个处理器的响应 when(nextHandler.handle(request)).thenReturn(expectedResponse); grayRuleMatchHandler.setNextHandler(nextHandler); ConfigQueryChainResponse response = grayRuleMatchHandler.handle(request); assertEquals(expectedResponse, response); } @Test public void handleNoMatchingGrayRuleShouldPassToNextHandler() throws IOException { when(cacheItem.getSortConfigGrays()).thenReturn(Collections.singletonList(configCacheGray)); when(configCacheGray.match(any())).thenReturn(false); ConfigQueryChainRequest request = new ConfigQueryChainRequest(); ConfigQueryChainResponse expectedResponse = new ConfigQueryChainResponse(); expectedResponse.setResultCode(123); // 假设这是下一个处理器的响应 when(nextHandler.handle(request)).thenReturn(expectedResponse); grayRuleMatchHandler.setNextHandler(nextHandler); ConfigQueryChainResponse response = grayRuleMatchHandler.handle(request); assertEquals(expectedResponse, response); } @Test public void handleMatchingGrayRuleShouldReturnConfigResponse() throws IOException { when(cacheItem.getSortConfigGrays()).thenReturn(Collections.singletonList(configCacheGray)); when(configCacheGray.match(any())).thenReturn(true); when(configCacheGray.getLastModifiedTs()).thenReturn(123456L); when(configCacheGray.getMd5()).thenReturn("md5"); when(configCacheGray.getEncryptedDataKey()).thenReturn("encryptedKey"); when(configCacheGray.getGrayName()).thenReturn("grayName"); when(cacheItem.getType()).thenReturn("configType"); when(configDiskService.getGrayContent(anyString(), anyString(), anyString(), anyString())).thenReturn( "content"); ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setDataId("dataId"); request.setGroup("group"); request.setTenant("tenant"); ConfigQueryChainResponse response = grayRuleMatchHandler.handle(request); assertEquals("content", response.getContent()); assertEquals("md5", response.getMd5()); assertEquals(123456L, response.getLastModified()); assertEquals("encryptedKey", response.getEncryptedDataKey()); assertEquals("configType", response.getConfigType()); assertEquals(ConfigQueryChainResponse.ConfigQueryStatus.CONFIG_FOUND_GRAY, response.getStatus()); } @Test public void testGetName() { assertEquals("grayRuleMatchHandler", grayRuleMatchHandler.getName()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/query/handler/SpecialTagNotFoundHandlerTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.query.handler; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainRequest; import com.alibaba.nacos.config.server.service.query.model.ConfigQueryChainResponse; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class SpecialTagNotFoundHandlerTest { @InjectMocks private SpecialTagNotFoundHandler specialTagNotFoundHandler; @Mock private ConfigQueryHandler nextHandler; @Test public void handleTagNotEmptyReturnsSpecialTagNotFoundResponse() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setTag("someTag"); ConfigQueryChainResponse response = specialTagNotFoundHandler.handle(request); assertEquals(ConfigQueryChainResponse.ConfigQueryStatus.SPECIAL_TAG_CONFIG_NOT_FOUND, response.getStatus()); } @Test public void handleTagEmptyDelegatesToNextHandler() throws IOException { ConfigQueryChainRequest request = new ConfigQueryChainRequest(); request.setTag(""); ConfigQueryChainResponse expectedResponse = new ConfigQueryChainResponse(); when(nextHandler.handle(request)).thenReturn(expectedResponse); ConfigQueryChainResponse response = specialTagNotFoundHandler.handle(request); assertEquals(expectedResponse, response); } @Test public void getName() { assertEquals("specialTagNotFoundHandler", specialTagNotFoundHandler.getName()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjectorTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository; import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfo4Beta; import com.alibaba.nacos.config.server.model.ConfigInfo4Tag; import com.alibaba.nacos.config.server.model.ConfigInfoBase; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoChanged; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigKey; import com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.ConfigHistoryDetailRowMapper; import com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.ConfigInfo4BetaRowMapper; import com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.ConfigInfoBetaWrapperRowMapper; import com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.ConfigInfoChangedRowMapper; import com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.ConfigInfoStateWrapperRowMapper; import com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.ConfigInfoTagWrapperRowMapper; import com.alibaba.nacos.persistence.repository.RowMapperManager; import com.mysql.cj.jdbc.result.ResultSetImpl; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.sql.SQLException; import java.sql.Timestamp; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(SpringExtension.class) class ConfigRowMapperInjectorTest { @Test void testInit() { ConfigRowMapperInjector configRowMapperInjector = new ConfigRowMapperInjector(); assertEquals(ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER, RowMapperManager.getRowMapper( ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER.getClass().getCanonicalName())); } @Test void testConfigInfoTagWrapperRowMapper() throws SQLException { ConfigInfoTagWrapper preConfig = new ConfigInfoTagWrapper(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); preConfig.setTag("tag345678"); preConfig.setLastModified(System.currentTimeMillis()); preConfig.setAppName("app_name11111"); preConfig.setType("type55555"); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("encrypted_data_key1324"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(new Timestamp(preConfig.getLastModified())); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); Mockito.when(resultSet.getString(eq("tag_id"))).thenReturn(preConfig.getTag()); ConfigInfoTagWrapperRowMapper configInfoWrapperRowMapper = new ConfigInfoTagWrapperRowMapper(); ConfigInfoTagWrapper configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); assertEquals(preConfig.getTag(), configInfoWrapper.getTag()); } @Test void testConfigInfo4BetaRowMapper() throws SQLException { ConfigInfo4Beta preConfig = new ConfigInfo4Beta(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); preConfig.setAppName("app_name11111"); preConfig.setType("type55555"); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("encrypted_data_key1324"); preConfig.setBetaIps("127.0.0.1,127.0.0.2"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getString(eq("beta_ips"))).thenReturn(preConfig.getBetaIps()); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); ConfigInfo4BetaRowMapper configInfoWrapperRowMapper = new ConfigInfo4BetaRowMapper(); ConfigInfo4Beta configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); assertEquals(preConfig.getBetaIps(), configInfoWrapper.getBetaIps()); } @Test void testConfigInfoBetaWrapperRowMapper() throws SQLException { ConfigInfoBetaWrapper preConfig = new ConfigInfoBetaWrapper(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); preConfig.setLastModified(System.currentTimeMillis()); preConfig.setAppName("app_name11111"); preConfig.setType("type55555"); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("encrypted_data_key1324"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(new Timestamp(preConfig.getLastModified())); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); ConfigInfoBetaWrapperRowMapper configInfoWrapperRowMapper = new ConfigInfoBetaWrapperRowMapper(); ConfigInfoBetaWrapper configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigAdvanceInfoRowMapper() throws SQLException { ConfigAdvanceInfo preConfig = new ConfigAdvanceInfo(); preConfig.setModifyTime(System.currentTimeMillis()); preConfig.setCreateTime(System.currentTimeMillis()); preConfig.setCreateUser("user12345"); preConfig.setCreateIp("1267890"); preConfig.setDesc("desc23"); preConfig.setUse("us345t"); preConfig.setEffect("effect233"); preConfig.setType("type132435"); preConfig.setSchema("scheme344"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("src_ip"))).thenReturn(preConfig.getCreateIp()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getString(eq("c_desc"))).thenReturn(preConfig.getDesc()); Mockito.when(resultSet.getString(eq("effect"))).thenReturn(preConfig.getEffect()); Mockito.when(resultSet.getString(eq("src_user"))).thenReturn(preConfig.getCreateUser()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(new Timestamp(preConfig.getModifyTime())); Mockito.when(resultSet.getTimestamp(eq("gmt_create"))).thenReturn(new Timestamp(preConfig.getCreateTime())); Mockito.when(resultSet.getString(eq("c_use"))).thenReturn(preConfig.getUse()); Mockito.when(resultSet.getString(eq("c_schema"))).thenReturn(preConfig.getSchema()); ConfigRowMapperInjector.ConfigAdvanceInfoRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigAdvanceInfoRowMapper(); ConfigAdvanceInfo configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigAllInfoRowMapper() throws SQLException { ConfigAllInfo preConfig = new ConfigAllInfo(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setModifyTime(System.currentTimeMillis()); preConfig.setCreateTime(System.currentTimeMillis()); preConfig.setId(1243567898L); preConfig.setAppName("app_name11111"); preConfig.setType("type55555"); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("encrypted_data_key1324"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(new Timestamp(preConfig.getModifyTime())); Mockito.when(resultSet.getTimestamp(eq("gmt_create"))).thenReturn(new Timestamp(preConfig.getCreateTime())); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); ConfigRowMapperInjector.ConfigAllInfoRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigAllInfoRowMapper(); ConfigAllInfo configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigInfoRowMapper() throws SQLException { ConfigInfo preConfig = new ConfigInfo(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); preConfig.setAppName("app_name11111"); preConfig.setType("type55555"); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("encrypted_data_key1324"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); ConfigRowMapperInjector.ConfigInfoRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigInfoRowMapper(); ConfigInfo configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigInfoWrapperRowMapper() throws SQLException { ConfigInfoWrapper preConfig = new ConfigInfoWrapper(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setLastModified(System.currentTimeMillis()); preConfig.setId(1243567898L); preConfig.setAppName("app_name11111"); preConfig.setType("type55555"); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("encrypted_data_key1324"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(new Timestamp(preConfig.getLastModified())); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); ConfigRowMapperInjector.ConfigInfoWrapperRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigInfoWrapperRowMapper(); ConfigInfoWrapper configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigInfo4TagRowMapper() throws SQLException { ConfigInfo4Tag preConfig = new ConfigInfo4Tag(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); preConfig.setAppName("app_name11111"); preConfig.setType("type55555"); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("encrypted_data_key1324"); preConfig.setTag("tag567890"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("type"))).thenReturn(preConfig.getType()); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("tag_id"))).thenReturn(preConfig.getTag()); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); ConfigRowMapperInjector.ConfigInfo4TagRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigInfo4TagRowMapper(); ConfigInfo4Tag configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigInfoBaseRowMapper() throws SQLException { ConfigInfoBase preConfig = new ConfigInfoBase(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setId(1243567898L); preConfig.setContent("content1123434t"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); ConfigRowMapperInjector.ConfigInfoBaseRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigInfoBaseRowMapper(); ConfigInfoBase configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigInfoGrayRowMapper() throws SQLException { ConfigInfoGrayWrapper preConfig = new ConfigInfoGrayWrapper(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setContent("content1123434t"); preConfig.setGrayName("grayName"); preConfig.setGrayRule("rule12345"); preConfig.setTenant("tenang34567890"); preConfig.setAppName("app3456789"); preConfig.setEncryptedDataKey("key12345"); Timestamp timestamp = Timestamp.valueOf("2024-12-12 12:34:34"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("gray_name"))).thenReturn(preConfig.getGrayName()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("gray_rule"))).thenReturn(preConfig.getGrayRule()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(timestamp); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getString(eq("app"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); ConfigRowMapperInjector.ConfigInfoGrayWrapperRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigInfoGrayWrapperRowMapper(); ConfigInfoGrayWrapper configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); assertEquals(timestamp.getTime(), configInfoWrapper.getLastModified()); } @Test void testConfigInfoChangedRowMapper() throws SQLException { ConfigInfoChanged preConfig = new ConfigInfoChanged(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenang34567890"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); ConfigInfoChangedRowMapper configInfoWrapperRowMapper = new ConfigInfoChangedRowMapper(); ConfigInfoChanged configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigHistoryRowMapper() throws SQLException { ConfigHistoryInfo preConfig = new ConfigHistoryInfo(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); preConfig.setAppName("app_name11111"); preConfig.setSrcIp("srciprtyui"); preConfig.setSrcUser("234567890user"); preConfig.setOpType("D2345678"); preConfig.setCreatedTime(new Timestamp(System.currentTimeMillis())); preConfig.setLastModifiedTime(new Timestamp(System.currentTimeMillis())); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("op_type"))).thenReturn(preConfig.getOpType()); Mockito.when(resultSet.getString(eq("src_user"))).thenReturn(preConfig.getSrcUser()); Mockito.when(resultSet.getLong(eq("nid"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("src_ip"))).thenReturn(preConfig.getSrcIp()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(preConfig.getLastModifiedTime()); Mockito.when(resultSet.getTimestamp(eq("gmt_create"))).thenReturn(preConfig.getCreatedTime()); ConfigRowMapperInjector.ConfigHistoryRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigHistoryRowMapper(); ConfigHistoryInfo configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigHistoryDetailRowMapper() throws SQLException { ConfigHistoryInfo preConfig = new ConfigHistoryInfo(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); preConfig.setAppName("app_name11111"); preConfig.setContent("content2345678"); preConfig.setMd5("md5234567890"); preConfig.setSrcIp("srciprtyui"); preConfig.setSrcUser("234567890user"); preConfig.setOpType("D2345678"); preConfig.setCreatedTime(new Timestamp(System.currentTimeMillis())); preConfig.setLastModifiedTime(new Timestamp(System.currentTimeMillis())); preConfig.setContent("content1123434t"); preConfig.setMd5("md54567"); preConfig.setEncryptedDataKey("key3456789"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); Mockito.when(resultSet.getString(eq("op_type"))).thenReturn(preConfig.getOpType()); Mockito.when(resultSet.getString(eq("src_user"))).thenReturn(preConfig.getSrcUser()); Mockito.when(resultSet.getString(eq("content"))).thenReturn(preConfig.getContent()); Mockito.when(resultSet.getString(eq("md5"))).thenReturn(preConfig.getMd5()); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn(preConfig.getEncryptedDataKey()); Mockito.when(resultSet.getLong(eq("nid"))).thenReturn(preConfig.getId()); Mockito.when(resultSet.getString(eq("src_ip"))).thenReturn(preConfig.getSrcIp()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(preConfig.getLastModifiedTime()); Mockito.when(resultSet.getTimestamp(eq("gmt_create"))).thenReturn(preConfig.getCreatedTime()); ConfigHistoryDetailRowMapper configInfoWrapperRowMapper = new ConfigHistoryDetailRowMapper(); ConfigHistoryInfo configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigInfoStateWrapperRowMapper() throws SQLException { ConfigInfoStateWrapper preConfig = new ConfigInfoStateWrapper(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setTenant("tenant_id11111"); preConfig.setId(1243567898L); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn(preConfig.getTenant()); Mockito.when(resultSet.getTimestamp(eq("gmt_modified"))).thenReturn(new Timestamp(preConfig.getLastModified())); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(preConfig.getId()); ConfigInfoStateWrapperRowMapper configInfoWrapperRowMapper = new ConfigInfoStateWrapperRowMapper(); ConfigInfoStateWrapper configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigKeyRowMapper() throws SQLException { ConfigKey preConfig = new ConfigKey(); preConfig.setDataId("testDataId"); preConfig.setGroup("group_id11"); preConfig.setAppName("appertyui4567"); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn(preConfig.getDataId()); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn(preConfig.getGroup()); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn(preConfig.getAppName()); ConfigRowMapperInjector.ConfigKeyRowMapper configInfoWrapperRowMapper = new ConfigRowMapperInjector.ConfigKeyRowMapper(); ConfigKey configInfoWrapper = configInfoWrapperRowMapper.mapRow(resultSet, 10); assertEquals(preConfig, configInfoWrapper); } @Test void testConfigInfoRowMapperWithDescAndTags() throws SQLException { ConfigRowMapperInjector.ConfigInfoRowMapper mapper = new ConfigRowMapperInjector.ConfigInfoRowMapper(); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(1L); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn("test.properties"); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn("DEFAULT_GROUP"); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn("public"); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn("testApp"); Mockito.when(resultSet.getString(eq("content"))).thenReturn("key=value"); Mockito.when(resultSet.getString(eq("md5"))).thenReturn("abc123"); Mockito.when(resultSet.getString(eq("type"))).thenReturn("properties"); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn("encKey"); Mockito.when(resultSet.getString(eq("c_desc"))).thenReturn("测试配置描述"); Mockito.when(resultSet.getString(eq("config_tags"))).thenReturn("tag1,tag2,tag3"); ConfigInfo configInfo = mapper.mapRow(resultSet, 1); assertEquals(1L, configInfo.getId()); assertEquals("test.properties", configInfo.getDataId()); assertEquals("DEFAULT_GROUP", configInfo.getGroup()); assertEquals("public", configInfo.getTenant()); assertEquals("testApp", configInfo.getAppName()); assertEquals("key=value", configInfo.getContent()); assertEquals("abc123", configInfo.getMd5()); assertEquals("properties", configInfo.getType()); assertEquals("encKey", configInfo.getEncryptedDataKey()); assertEquals("测试配置描述", configInfo.getDesc()); assertEquals("tag1,tag2,tag3", configInfo.getConfigTags()); } @Test void testConfigInfoRowMapperWithNullDescAndTags() throws SQLException { ConfigRowMapperInjector.ConfigInfoRowMapper mapper = new ConfigRowMapperInjector.ConfigInfoRowMapper(); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); Mockito.when(resultSet.getLong(eq("id"))).thenReturn(1L); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn("test.properties"); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn("DEFAULT_GROUP"); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn("public"); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn("testApp"); Mockito.when(resultSet.getString(eq("content"))).thenReturn("key=value"); Mockito.when(resultSet.getString(eq("md5"))).thenReturn("abc123"); Mockito.when(resultSet.getString(eq("type"))).thenReturn("properties"); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn("encKey"); Mockito.when(resultSet.getString(eq("c_desc"))).thenReturn(null); Mockito.when(resultSet.getString(eq("config_tags"))).thenReturn(null); ConfigInfo configInfo = mapper.mapRow(resultSet, 1); assertEquals(1L, configInfo.getId()); assertEquals("test.properties", configInfo.getDataId()); assertEquals("DEFAULT_GROUP", configInfo.getGroup()); assertEquals("public", configInfo.getTenant()); assertEquals("testApp", configInfo.getAppName()); assertEquals("key=value", configInfo.getContent()); assertEquals("abc123", configInfo.getMd5()); assertEquals("properties", configInfo.getType()); assertEquals("encKey", configInfo.getEncryptedDataKey()); assertEquals(null, configInfo.getDesc()); assertEquals(null, configInfo.getConfigTags()); } @Test void testConfigInfoRowMapperBackwardCompatibility() throws SQLException { ConfigRowMapperInjector.ConfigInfoRowMapper mapper = new ConfigRowMapperInjector.ConfigInfoRowMapper(); ResultSetImpl resultSet = Mockito.mock(ResultSetImpl.class); // 模拟旧版本数据库,没有 c_desc 和 config_tags 字段 Mockito.when(resultSet.getLong(eq("id"))).thenReturn(1L); Mockito.when(resultSet.getString(eq("data_id"))).thenReturn("test.properties"); Mockito.when(resultSet.getString(eq("group_id"))).thenReturn("DEFAULT_GROUP"); Mockito.when(resultSet.getString(eq("tenant_id"))).thenReturn("public"); Mockito.when(resultSet.getString(eq("app_name"))).thenReturn("testApp"); Mockito.when(resultSet.getString(eq("content"))).thenReturn("key=value"); Mockito.when(resultSet.getString(eq("md5"))).thenReturn("abc123"); Mockito.when(resultSet.getString(eq("type"))).thenReturn("properties"); Mockito.when(resultSet.getString(eq("encrypted_data_key"))).thenReturn("encKey"); // 模拟字段不存在的情况 Mockito.when(resultSet.getString(eq("c_desc"))).thenThrow(new SQLException("Column 'c_desc' not found")); Mockito.when(resultSet.getString(eq("config_tags"))).thenThrow(new SQLException("Column 'config_tags' not found")); ConfigInfo configInfo = mapper.mapRow(resultSet, 1); assertEquals(1L, configInfo.getId()); assertEquals("test.properties", configInfo.getDataId()); assertEquals("DEFAULT_GROUP", configInfo.getGroup()); assertEquals("public", configInfo.getTenant()); assertEquals("testApp", configInfo.getAppName()); assertEquals("key=value", configInfo.getContent()); assertEquals("abc123", configInfo.getMd5()); assertEquals("properties", configInfo.getType()); assertEquals("encKey", configInfo.getEncryptedDataKey()); // 新字段应该为 null,保证向后兼容 assertEquals(null, configInfo.getDesc()); assertEquals(null, configInfo.getConfigTags()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoBetaPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.ArrayList; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; /** * test for embedded config beta. * * @author shiyiyue */ @ExtendWith(SpringExtension.class) class EmbeddedConfigInfoBetaPersistServiceImplTest { MockedStatic envUtilMockedStatic; MockedStatic embeddedStorageContextHolderMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; @Mock DatabaseOperate databaseOperate; private EmbeddedConfigInfoBetaPersistServiceImpl embeddedConfigInfoBetaPersistService; @Mock private DataSourceService dataSourceService; @BeforeEach void before() { embeddedStorageContextHolderMockedStatic = Mockito.mockStatic(EmbeddedStorageContextHolder.class); dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getDataSourceType()).thenReturn("derby"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))).thenReturn(false); embeddedConfigInfoBetaPersistService = new EmbeddedConfigInfoBetaPersistServiceImpl(databaseOperate); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); embeddedStorageContextHolderMockedStatic.close(); } @Test void testInsertOrUpdateBetaOfUpdate() { String dataId = "betaDataId113"; String group = "group"; String tenant = "tenant"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); Mockito.when( databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(mockedConfigInfoStateWrapper, mockedConfigInfoStateWrapper); //execute String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); ConfigOperateResult configOperateResult = embeddedConfigInfoBetaPersistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify update to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(configInfo.getEncryptedDataKey()), eq(dataId), eq(group), eq(tenant)), times(1)); } @Test void testInsertOrUpdateBetaOfAdd() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); Mockito.when( databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null).thenReturn(mockedConfigInfoStateWrapper); String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); //execute ConfigOperateResult configOperateResult = embeddedConfigInfoBetaPersistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify add to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getEncryptedDataKey())), times(1)); } @Test void testInsertOrUpdateBetaCasOfUpdate() { String dataId = "betaDataId113"; String group = "group"; String tenant = "tenant"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); Mockito.when( databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(mockedConfigInfoStateWrapper, mockedConfigInfoStateWrapper); //execute String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); configInfo.setMd5("casMd5"); //mock cas update Mockito.when(databaseOperate.blockUpdate()).thenReturn(true); String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; ConfigOperateResult configOperateResult = embeddedConfigInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify cas update to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(betaIps), eq(srcIp), eq(srcUser), eq(appName), eq(dataId), eq(group), eq(tenant), eq(configInfo.getMd5())), times(1)); } @Test void testInsertOrUpdateBetaCasOfAdd() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); Mockito.when( databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null).thenReturn(mockedConfigInfoStateWrapper); String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); //execute ConfigOperateResult configOperateResult = embeddedConfigInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify add to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getEncryptedDataKey())), times(1)); } @Test void testRemoveConfigInfo4Beta() { String dataId = "dataId456789"; String group = "group4567"; String tenant = "tenant56789o0"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); Mockito.when( databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(mockedConfigInfoStateWrapper); //mock remove ok Mockito.when(databaseOperate.update(any(List.class))).thenReturn(true); embeddedConfigInfoBetaPersistService.removeConfigInfo4Beta(dataId, group, tenant); //verity embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant)), times(1)); } @Test void testFindConfigInfo4Beta() { String dataId = "dataId456789"; String group = "group4567"; String tenant = "tenant56789o0"; //mock exist beta ConfigInfoBetaWrapper mockedConfigInfoStateWrapper = new ConfigInfoBetaWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); Mockito.when( databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER))) .thenReturn(mockedConfigInfoStateWrapper); ConfigInfoBetaWrapper configInfo4BetaReturn = embeddedConfigInfoBetaPersistService.findConfigInfo4Beta(dataId, group, tenant); assertEquals(mockedConfigInfoStateWrapper, configInfo4BetaReturn); } @Test void testConfigInfoBetaCount() { Mockito.when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(101); int returnCount = embeddedConfigInfoBetaPersistService.configInfoBetaCount(); assertEquals(101, returnCount); } @Test void testFindAllConfigInfoBetaForDumpAll() { //mock count Mockito.when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(12345); //mock page list List mockList = new ArrayList<>(); mockList.add(new ConfigInfoBetaWrapper()); mockList.add(new ConfigInfoBetaWrapper()); mockList.add(new ConfigInfoBetaWrapper()); mockList.get(0).setLastModified(System.currentTimeMillis()); mockList.get(1).setLastModified(System.currentTimeMillis()); mockList.get(2).setLastModified(System.currentTimeMillis()); Mockito.when(databaseOperate.queryMany(anyString(), eq(new Object[] {}), eq(CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER))) .thenReturn(mockList); int pageNo = 1; int pageSize = 101; Mockito.when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(101); //execute & expect Page pageReturn = embeddedConfigInfoBetaPersistService.findAllConfigInfoBetaForDumpAll(pageNo, pageSize); assertEquals(mockList, pageReturn.getPageItems()); assertEquals(101, pageReturn.getTotalCount()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoGrayPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.core.distributed.id.IdGeneratorManager; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; 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.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; /** * test for embedded config tag. * * @author shiyiyue */ @ExtendWith(SpringExtension.class) public class EmbeddedConfigInfoGrayPersistServiceImplTest { private EmbeddedConfigInfoGrayPersistServiceImpl embeddedConfigInfoGrayPersistService; @Mock private DataSourceService dataSourceService; @Mock private IdGeneratorManager idGeneratorManager; @Mock private HistoryConfigInfoPersistService historyConfigInfoPersistService; MockedStatic envUtilMockedStatic; MockedStatic embeddedStorageContextHolderMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; @Mock DatabaseOperate databaseOperate; /** * before test. */ @BeforeEach public void before() { embeddedStorageContextHolderMockedStatic = Mockito.mockStatic(EmbeddedStorageContextHolder.class); dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getDataSourceType()).thenReturn("derby"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))) .thenReturn(false); embeddedConfigInfoGrayPersistService = new EmbeddedConfigInfoGrayPersistServiceImpl(databaseOperate, idGeneratorManager, historyConfigInfoPersistService); } /** * after each case. */ @AfterEach public void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); embeddedStorageContextHolderMockedStatic.close(); } @Test public void testInsertOrUpdateGrayOfAdd() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //mock query config state empty and return obj after insert ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String grayName = "tag123grayName"; String grayRule = ""; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = embeddedConfigInfoGrayPersistService.insertOrUpdateGray(configInfo, grayName, grayRule, srcIp, srcUser); //mock insert invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), any(), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(grayRule), eq(appName), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), any(Timestamp.class)), times(1)); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configInfo.getId()), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("gray"), eq(grayName), anyString()); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test public void testInsertOrUpdateGrayOfUpdate() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //mock query config state and return obj after update ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String grayName = "tag123grayName"; final String grayRule = "tag123grayrule"; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(new ConfigInfoStateWrapper()) .thenReturn(configInfoStateWrapper); //mock exist config info ConfigInfoGrayWrapper configAllInfo4Gray = new ConfigInfoGrayWrapper(); configAllInfo4Gray.setDataId(dataId); configAllInfo4Gray.setGroup(group); configAllInfo4Gray.setTenant(tenant); configAllInfo4Gray.setMd5("old_md5"); configAllInfo4Gray.setSrcUser("user"); when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(configAllInfo4Gray); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = embeddedConfigInfoGrayPersistService.insertOrUpdateGray(configInfo, grayName, grayRule, srcIp, srcUser); //verify update to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), eq(appName), eq(grayRule), eq(dataId), eq(group), eq(tenant), eq(grayName)), times(1)); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo4Gray.getId()), eq(configAllInfo4Gray), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("U"), eq("gray"), eq(grayName), anyString()); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test public void testInsertOrUpdateGrayCasOfAdd() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); configInfo.setMd5("casMd5"); //mock query config state empty and return obj after insert ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String grayName = "tag123grayName"; String grayRule = ""; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = embeddedConfigInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, grayName, grayRule, srcIp, srcUser); //verify insert to be invoked //mock insert invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), any(), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(grayRule), eq(appName), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), any(Timestamp.class)), times(1)); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configInfo.getId()), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("gray"), eq(grayName), anyString()); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test public void testInsertOrUpdateGrayCasOfUpdate() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); configInfo.setMd5("casMd5"); //mock query config state and return obj after update ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String grayName = "tag123grayName"; final String grayRule = ""; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(new ConfigInfoStateWrapper()) .thenReturn(configInfoStateWrapper); //mock exist config info ConfigInfoGrayWrapper configAllInfo4Gray = new ConfigInfoGrayWrapper(); configAllInfo4Gray.setDataId(dataId); configAllInfo4Gray.setGroup(group); configAllInfo4Gray.setTenant(tenant); configAllInfo4Gray.setMd5("old_md5"); configAllInfo4Gray.setSrcUser("user"); when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(configAllInfo4Gray); String srcIp = "ip345678"; String srcUser = "user1234567"; //mock cas update return 1 Mockito.when(databaseOperate.blockUpdate()).thenReturn(true); ConfigOperateResult configOperateResult = embeddedConfigInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, grayName, grayRule, srcIp, srcUser); //verify update to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), eq(appName), eq(grayRule), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(configInfo.getMd5())), times(1)); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo4Gray.getId()), eq(configAllInfo4Gray), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("U"), eq("gray"), eq(grayName), anyString()); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test public void testRemoveConfigInfoGrayName() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; final String srcIp = "ip345678"; final String srcUser = "user1234567"; final String grayName = "grayName..."; //mock exist config info ConfigInfoGrayWrapper configAllInfo4Gray = new ConfigInfoGrayWrapper(); configAllInfo4Gray.setDataId(dataId); configAllInfo4Gray.setGroup(group); configAllInfo4Gray.setTenant(tenant); configAllInfo4Gray.setMd5("old_md5"); when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(configAllInfo4Gray); embeddedConfigInfoGrayPersistService.removeConfigInfoGray(dataId, group, tenant, grayName, srcIp, srcUser); //verify delete sql invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant), eq(grayName)), times(1)); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo4Gray.getId()), eq(configAllInfo4Gray), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("D"), eq("gray"), eq(grayName), anyString()); } @Test public void testFindConfigInfo4Gray() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; String grayName = "tag123345"; //mock query tag return obj ConfigInfoGrayWrapper configInfoGrayWrapperMocked = new ConfigInfoGrayWrapper(); configInfoGrayWrapperMocked.setLastModified(System.currentTimeMillis()); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(configInfoGrayWrapperMocked); ConfigInfoGrayWrapper configInfo4GrayReturn = embeddedConfigInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, grayName); assertEquals(configInfoGrayWrapperMocked, configInfo4GrayReturn); } @Test public void testConfigInfoGrayCount() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); //mock count Mockito.when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(308); //execute & verify int count = embeddedConfigInfoGrayPersistService.configInfoGrayCount(); assertEquals(308, count); } @Test public void testFindAllConfigInfoGrayForDumpAll() { //mock count Mockito.when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(308); List mockGrayList = new ArrayList<>(); mockGrayList.add(new ConfigInfoGrayWrapper()); mockGrayList.add(new ConfigInfoGrayWrapper()); mockGrayList.add(new ConfigInfoGrayWrapper()); mockGrayList.get(0).setLastModified(System.currentTimeMillis()); mockGrayList.get(1).setLastModified(System.currentTimeMillis()); mockGrayList.get(2).setLastModified(System.currentTimeMillis()); //mock query list Mockito.when( databaseOperate.queryMany(anyString(), eq(new Object[] {}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))) .thenReturn(mockGrayList); int pageNo = 3; int pageSize = 100; //execute & verify Page returnGrayPage = embeddedConfigInfoGrayPersistService.findAllConfigInfoGrayForDumpAll( pageNo, pageSize); assertEquals(308, returnGrayPage.getTotalCount()); assertEquals(mockGrayList, returnGrayPage.getPageItems()); } @Test public void testFindConfigInfoGrays() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; List mockedGrays = Arrays.asList("tags1", "tags11", "tags111"); Mockito.when(databaseOperate.queryMany(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))) .thenReturn(mockedGrays); List configInfoGrays = embeddedConfigInfoGrayPersistService.findConfigInfoGrays(dataId, group, tenant); assertEquals(mockedGrays, configInfoGrays); } @Test public void testFindChangeConfigInfo4Gray() { List mockList = new ArrayList<>(); mockList.add(new ConfigInfoGrayWrapper()); mockList.add(new ConfigInfoGrayWrapper()); mockList.add(new ConfigInfoGrayWrapper()); mockList.get(0).setLastModified(System.currentTimeMillis()); mockList.get(1).setLastModified(System.currentTimeMillis()); mockList.get(2).setLastModified(System.currentTimeMillis()); long lastMaxId = 123; Timestamp timestamp = new Timestamp(System.currentTimeMillis()); when(databaseOperate.queryMany(anyString(), eq(new Object[] {timestamp, lastMaxId, 100}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(mockList) .thenThrow(new CannotGetJdbcConnectionException("mock exception22")); List changeConfig = embeddedConfigInfoGrayPersistService.findChangeConfig(timestamp, lastMaxId, 100); assertTrue(changeConfig.get(0).getLastModified() == mockList.get(0).getLastModified()); assertTrue(changeConfig.get(1).getLastModified() == mockList.get(1).getLastModified()); assertTrue(changeConfig.get(2).getLastModified() == mockList.get(2).getLastModified()); try { embeddedConfigInfoGrayPersistService.findChangeConfig(timestamp, lastMaxId, 100); assertTrue(false); } catch (CannotGetJdbcConnectionException exception) { assertEquals("mock exception22", exception.getMessage()); } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.utils.ConfigExtInfoUtil; import com.alibaba.nacos.core.distributed.id.IdGeneratorManager; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ADVANCE_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ALL_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.persistence.repository.RowMapperManager.MAP_ROW_MAPPER; 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.anyLong; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; /** * EmbeddedConfigInfoPersistServiceImplTest. * * @author shiyiyue */ @ExtendWith(SpringExtension.class) class EmbeddedConfigInfoPersistServiceImplTest { @Mock IdGeneratorManager idGeneratorManager; MockedStatic envUtilMockedStatic; MockedStatic embeddedStorageContextHolderMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; @Mock DatabaseOperate databaseOperate; private EmbeddedConfigInfoPersistServiceImpl embeddedConfigInfoPersistService; @Mock private DataSourceService dataSourceService; @Mock private HistoryConfigInfoPersistService historyConfigInfoPersistService; @BeforeEach void before() { embeddedStorageContextHolderMockedStatic = Mockito.mockStatic(EmbeddedStorageContextHolder.class); dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getDataSourceType()).thenReturn("derby"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))) .thenReturn(false); embeddedConfigInfoPersistService = new EmbeddedConfigInfoPersistServiceImpl(databaseOperate, idGeneratorManager, historyConfigInfoPersistService); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); embeddedStorageContextHolderMockedStatic.close(); } @Test void testInsertOrUpdateOfInsertConfigSuccess() { String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String appName = "appNameNew"; String content = "content132456"; Map configAdvanceInfo = new HashMap<>(); String desc = "testdesc"; String use = "testuse"; String effect = "testeffect"; String type = "testtype"; String schema = "testschema"; configAdvanceInfo.put("config_tags", "tag1,tag2"); configAdvanceInfo.put("desc", desc); configAdvanceInfo.put("use", use); configAdvanceInfo.put("effect", effect); configAdvanceInfo.put("type", type); configAdvanceInfo.put("schema", schema); ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); String encryptedDataKey = "key1234"; configInfo.setEncryptedDataKey(encryptedDataKey); long insertConfigIndoId = 12345678765L; ConfigInfoStateWrapper configInfoStateWrapperFinalSelect = new ConfigInfoStateWrapper(); configInfoStateWrapperFinalSelect.setId(insertConfigIndoId); configInfoStateWrapperFinalSelect.setLastModified(System.currentTimeMillis()); //mock get config state Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null, configInfoStateWrapperFinalSelect); Mockito.when(databaseOperate.blockUpdate(any())).thenReturn(true); String srcIp = "srcIp"; String srcUser = "srcUser"; //mock insert config info Mockito.doNothing().when(historyConfigInfoPersistService) .insertConfigHistoryAtomic(eq(0), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser))); ConfigOperateResult configOperateResult = embeddedConfigInfoPersistService.insertOrUpdate(srcIp, srcUser, configInfo, configAdvanceInfo); assertEquals(configInfoStateWrapperFinalSelect.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapperFinalSelect.getLastModified(), configOperateResult.getLastModified()); //expect insert config info invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq(dataId), eq(group), eq(tenant), eq(appName), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), eq(desc), eq(use), eq(effect), eq(type), eq(schema), eq(encryptedDataKey)), times(1)); //expect insert config tags embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); //expect insert history info Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(0L), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser))); } @Test void testInsertOrUpdateCasOfInsertConfigSuccess() { Map configAdvanceInfo = new HashMap<>(); String desc = "testdesc"; String use = "testuse"; String effect = "testeffect"; String type = "testtype"; String schema = "testschema"; configAdvanceInfo.put("config_tags", "tag1,tag2"); configAdvanceInfo.put("desc", desc); configAdvanceInfo.put("use", use); configAdvanceInfo.put("effect", effect); configAdvanceInfo.put("type", type); configAdvanceInfo.put("schema", schema); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String appName = "appName"; String content = "content132456"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); String encryptedDatakey = "key456"; configInfo.setEncryptedDataKey(encryptedDatakey); long insertConfigIndoId = 12345678765L; ConfigInfoStateWrapper configInfoStateWrapperFinalSelect = new ConfigInfoStateWrapper(); configInfoStateWrapperFinalSelect.setId(insertConfigIndoId); configInfoStateWrapperFinalSelect.setLastModified(System.currentTimeMillis()); //mock get config state Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null, configInfoStateWrapperFinalSelect); Mockito.when(databaseOperate.blockUpdate(any())).thenReturn(true); String srcIp = "iptest"; String srcUser = "users"; ConfigOperateResult configOperateResult = embeddedConfigInfoPersistService.insertOrUpdateCas(srcIp, srcUser, configInfo, configAdvanceInfo); assertEquals(configInfoStateWrapperFinalSelect.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapperFinalSelect.getLastModified(), configOperateResult.getLastModified()); //expect insert config info invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq(dataId), eq(group), eq(tenant), eq(appName), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), eq(desc), eq(use), eq(effect), eq(type), eq(schema), eq(encryptedDatakey)), times(1)); //expect insert config tags embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); //expect insert history info Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(0L), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), argThat(actualJson -> { String expected = ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser); if (expected == null || actualJson == null) { return expected == actualJson; } String[] expectedParts = expected.replaceAll("[{}\"]", "").split(","); String[] actualParts = actualJson.replaceAll("[{}\"]", "").split(","); List expectedList = new ArrayList<>(); List actualList = new ArrayList<>(); for (String part : expectedParts) { expectedList.add(part.trim()); } for (String part : actualParts) { actualList.add(part.trim()); } return expectedList.size() == actualList.size() && actualList.containsAll(expectedList); })); } @Test void testInsertOrUpdateOfUpdateConfigSuccess() { Map configAdvanceInfo = new HashMap<>(); String desc = "testdesc"; String use = "testuse"; String effect = "testeffect"; String type = "testtype"; String schema = "testschema"; configAdvanceInfo.put("config_tags", "tag1,tag2"); configAdvanceInfo.put("desc", desc); configAdvanceInfo.put("use", use); configAdvanceInfo.put("effect", effect); configAdvanceInfo.put("type", type); configAdvanceInfo.put("schema", schema); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String content = "content132456"; String appName = "app1233"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); String encryptedDataKey = "key34567"; configInfo.setEncryptedDataKey(encryptedDataKey); //mock get config state,first and second is not null Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(new ConfigInfoStateWrapper(), new ConfigInfoStateWrapper()); //mock select config info before update ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(group); configAllInfo.setTenant(tenant); configAllInfo.setAppName("old_app"); configAllInfo.setMd5("old_md5"); configAllInfo.setId(12345678765L); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(configAllInfo); String srcIp = "srcIp"; String srcUser = "srcUser"; embeddedConfigInfoPersistService.insertOrUpdate(srcIp, srcUser, configInfo, configAdvanceInfo); //expect update config info invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), eq(appName), eq(desc), eq(use), eq(effect), eq(type), eq(schema), eq(encryptedDataKey), eq(dataId), eq(group), eq(tenant)), times(1)); //expect insert config tags embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); //expect insert history info of U Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), any(ConfigInfo.class), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("U"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); } @Test void testInsertOrUpdateCasOfUpdateConfigSuccess() { Map configAdvanceInfo = new HashMap<>(); String desc = "testdesc11"; String use = "testuse11"; String effect = "testeffe1ct"; String type = "testt1ype"; String schema = "testsch1ema"; configAdvanceInfo.put("config_tags", "tag1,tag2"); configAdvanceInfo.put("desc", desc); configAdvanceInfo.put("use", use); configAdvanceInfo.put("effect", effect); configAdvanceInfo.put("type", type); configAdvanceInfo.put("schema", schema); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String content = "content132456"; String encryptedDataKey = "key34567"; String casMd5 = "casMd5.."; String appName = "app12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setMd5(casMd5); configInfo.setEncryptedDataKey(encryptedDataKey); //mock get config state,first and second is not null Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(new ConfigInfoStateWrapper(), new ConfigInfoStateWrapper()); //mock select config info before update ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(group); configAllInfo.setTenant(tenant); configAllInfo.setAppName("old_app"); configAllInfo.setMd5("old_md5"); configAllInfo.setId(12345678765L); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(configAllInfo); String srcIp = "srcIp"; String srcUser = "srcUser"; embeddedConfigInfoPersistService.insertOrUpdateCas(srcIp, srcUser, configInfo, configAdvanceInfo); //expect update config info invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(eq(Boolean.TRUE), anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), eq(appName), eq(desc), eq(use), eq(effect), eq(type), eq(schema), eq(encryptedDataKey), eq(dataId), eq(group), eq(tenant), eq(casMd5)), times(1)); //expect insert config tags embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), anyLong(), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)), times(1)); //expect insert history info of U Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), any(ConfigInfo.class), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("U"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); } @Test void testRemoveConfigInfo() { String dataId = "dataId4567"; String group = "group3456789"; String tenant = "tenant4567890"; //mock exist config info ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(group); configAllInfo.setTenant(tenant); configAllInfo.setAppName("old_app"); configAllInfo.setMd5("old_md5"); configAllInfo.setId(12345678765L); configAllInfo.setType(ConfigType.JSON.getType()); configAllInfo.setSchema("testschema"); configAllInfo.setCreateUser("testuser"); configAllInfo.setEffect("online"); configAllInfo.setDesc("desc"); configAllInfo.setUse("use124"); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(configAllInfo); String srcIp = "srcIp1234"; String srcUser = "srcUser"; Mockito.when(databaseOperate.update(any())).thenReturn(true); embeddedConfigInfoPersistService.removeConfigInfo(dataId, group, tenant, srcIp, srcUser); //expect delete config to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant)), times(1)); //expect delete config tag to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(configAllInfo.getId())), times(1)); //expect insert delete history Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), eq(configAllInfo), eq(srcIp), eq(srcUser), any(), eq("D"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); } @Test void testRemoveConfigInfoByIds() { //mock exist config info final List configAllInfos = new ArrayList<>(); final ConfigAllInfo configAllInfo1 = new ConfigAllInfo(); final ConfigAllInfo configAllInfo2 = new ConfigAllInfo(); configAllInfo1.setDataId("dataId1"); configAllInfo1.setGroup("group1"); configAllInfo1.setTenant("tenant1"); configAllInfo1.setAppName("app1"); configAllInfo2.setDataId("dataId2"); configAllInfo2.setGroup("group2"); configAllInfo2.setTenant("tenant2"); configAllInfo2.setAppName("app2"); configAllInfos.add(configAllInfo1); configAllInfos.add(configAllInfo2); List deleteIds = Arrays.asList(12344L, 3456789L); configAllInfos.get(0).setId(12344L); configAllInfos.get(1).setId(3456789L); Mockito.when(databaseOperate.queryMany(anyString(), eq(deleteIds.toArray()), eq(CONFIG_ALL_INFO_ROW_MAPPER))) .thenReturn(configAllInfos); String srcIp = "srcIp1234"; String srcUser = "srcUser"; Mockito.when(databaseOperate.update(any())).thenReturn(true); embeddedConfigInfoPersistService.removeConfigInfoByIds(deleteIds, srcIp, srcUser); long deleteId0 = deleteIds.get(0); long deleteId1 = deleteIds.get(1); //expect delete config to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(deleteId0), eq(deleteId1)), times(1)); //expect delete config tag to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(deleteId0)), times(1)); embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(deleteId1)), times(1)); //expect insert delete history Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfos.get(0).getId()), eq(configAllInfos.get(0)), eq(srcIp), eq(srcUser), any(), eq("D"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfos.get(0)))); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfos.get(1).getId()), eq(configAllInfos.get(1)), eq(srcIp), eq(srcUser), any(), eq("D"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfos.get(1)))); } @Test void testBatchInsertOrUpdateOverwrite() throws NacosException { List configInfoList = new ArrayList<>(); //insert direct configInfoList.add(createMockConfigAllInfo(0)); //exist config and overwrite configInfoList.add(createMockConfigAllInfo(1)); //insert direct configInfoList.add(createMockConfigAllInfo(2)); String srcUser = "srcUser1324"; String srcIp = "srcIp1243"; Map configAdvanceInfo = new HashMap<>(); //mock add config 1 success,config 2 fail and skip,config 3 success Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(0).getDataId(), configInfoList.get(0).getGroup(), configInfoList.get(0).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(1).getDataId(), configInfoList.get(1).getGroup(), configInfoList.get(1).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(new ConfigInfoStateWrapper()); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(2).getDataId(), configInfoList.get(2).getGroup(), configInfoList.get(1).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null); //mock query config info during update ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(1).getDataId(), configInfoList.get(1).getGroup(), configInfoList.get(1).getTenant()}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))) .thenReturn(configInfoWrapper); Map stringObjectMap = embeddedConfigInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, configAdvanceInfo, SameConfigPolicy.OVERWRITE); assertEquals(3, stringObjectMap.get("succCount")); assertEquals(0, stringObjectMap.get("skipCount")); } @Test void testBatchInsertOrUpdateSkip() throws NacosException { List configInfoList = new ArrayList<>(); //insert direct configInfoList.add(createMockConfigAllInfo(0)); //exist config and skip configInfoList.add(createMockConfigAllInfo(1)); //insert direct configInfoList.add(createMockConfigAllInfo(2)); String srcUser = "srcUser1324"; String srcIp = "srcIp1243"; Map configAdvanceInfo = new HashMap<>(); //mock add config 1 success,config 2 fail and skip,config 3 success Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(0).getDataId(), configInfoList.get(0).getGroup(), configInfoList.get(0).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(1).getDataId(), configInfoList.get(1).getGroup(), configInfoList.get(1).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(new ConfigInfoStateWrapper()); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(2).getDataId(), configInfoList.get(2).getGroup(), configInfoList.get(1).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null); Map stringObjectMap = embeddedConfigInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, configAdvanceInfo, SameConfigPolicy.SKIP); assertEquals(2, stringObjectMap.get("succCount")); assertEquals(1, stringObjectMap.get("skipCount")); assertEquals(configInfoList.get(1).getDataId(), ((List>) stringObjectMap.get("skipData")).get(0).get("dataId")); } @Test void testBatchInsertOrUpdateAbort() throws NacosException { List configInfoList = new ArrayList<>(); //insert direct configInfoList.add(createMockConfigAllInfo(0)); //exist config and overwrite configInfoList.add(createMockConfigAllInfo(1)); //insert direct configInfoList.add(createMockConfigAllInfo(2)); String srcUser = "srcUser1324"; String srcIp = "srcIp1243"; Map configAdvanceInfo = new HashMap<>(); //mock add config 1 success,config 2 fail and abort,config 3 not operated Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(0).getDataId(), configInfoList.get(0).getGroup(), configInfoList.get(0).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(1).getDataId(), configInfoList.get(1).getGroup(), configInfoList.get(1).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(new ConfigInfoStateWrapper()); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {configInfoList.get(2).getDataId(), configInfoList.get(2).getGroup(), configInfoList.get(1).getTenant()}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(null); Map stringObjectMap = embeddedConfigInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, configAdvanceInfo, SameConfigPolicy.ABORT); assertEquals(1, stringObjectMap.get("succCount")); assertEquals(1, stringObjectMap.get("skipCount")); // config 2 failed assertEquals(configInfoList.get(1).getDataId(), ((List>) stringObjectMap.get("failData")).get(0).get("dataId")); //skip config 3 assertEquals(configInfoList.get(2).getDataId(), ((List>) stringObjectMap.get("skipData")).get(0).get("dataId")); } private ConfigAllInfo createMockConfigAllInfo(long mockId) { ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setTenant("tenantTest"); configAllInfo.setCreateIp("localhost"); configAllInfo.setCreateUser("test"); configAllInfo.setContent("23456789000content"); return configAllInfo; } private ConfigInfoWrapper createMockConfigInfoWrapper(long mockId) { ConfigInfoWrapper configAllInfo = new ConfigInfoWrapper(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setContent("23456789000content"); return configAllInfo; } private ConfigInfoStateWrapper createMockConfigInfoStateWrapper(long mockId) { ConfigInfoStateWrapper configAllInfo = new ConfigInfoStateWrapper(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setLastModified(System.currentTimeMillis()); return configAllInfo; } private ConfigInfo createMockConfigInfo(long mockId) { ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId("test" + mockId + ".yaml"); configInfo.setGroup("test"); configInfo.setContent("23456789000content"); return configInfo; } @Test void testFindConfigMaxId() { Mockito.when(databaseOperate.queryOne(anyString(), eq(Long.class))).thenReturn(123456L); long configMaxId = embeddedConfigInfoPersistService.findConfigMaxId(); assertEquals(123456L, configMaxId); } @Test void testFindConfigMaxId0() { Mockito.when(databaseOperate.queryOne(anyString(), eq(Long.class))).thenReturn(0L); long configMaxId = embeddedConfigInfoPersistService.findConfigMaxId(); assertEquals(0, configMaxId); } @Test void testFindConfigInfoById() { long id = 1234567890876L; ConfigInfo configInfo = new ConfigInfo(); configInfo.setId(id); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {id}), eq(CONFIG_INFO_ROW_MAPPER))) .thenReturn(configInfo); ConfigInfo configReturn = embeddedConfigInfoPersistService.findConfigInfo(id); assertEquals(id, configReturn.getId()); } @Test void testFindConfigInfoByDataId() { String dataId = "dataId4567"; String group = "group3456789"; String tenant = "tenant4567890"; ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); configInfoWrapper.setDataId(dataId); configInfoWrapper.setGroup(group); configInfoWrapper.setTenant(tenant); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenReturn(configInfoWrapper); ConfigInfo configReturn = embeddedConfigInfoPersistService.findConfigInfo(dataId, group, tenant); assertEquals(dataId, configReturn.getDataId()); } @Test void testFindConfigInfo4Page() { String dataId = "dataId4567222"; String group = "group3456789"; String tenant = "tenant4567890"; //mock total count when(databaseOperate.queryOne(anyString(), eq(new Object[] {tenant, dataId, group}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(databaseOperate.queryMany(anyString(), eq(new Object[] {tenant, dataId, group}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Map configAdvanceInfo = new HashMap<>(); Page configInfo4Page = embeddedConfigInfoPersistService.findConfigInfo4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testFindConfigInfo4PageWithTags() { String dataId = "dataId4567222"; String group = "group3456789"; String tenant = "tenant4567890"; Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tags1,tags3"); //mock total count when(databaseOperate.queryOne(anyString(), eq(new Object[] {tenant, dataId, group, "tags1", "tags3"}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(databaseOperate.queryMany(anyString(), eq(new Object[] {tenant, dataId, group, "tags1", "tags3"}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Page configInfo4Page = embeddedConfigInfoPersistService.findConfigInfo4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testConfigInfoCount() { //mock total count when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(new Integer(9)); int count = embeddedConfigInfoPersistService.configInfoCount(); assertEquals(9, count); when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(null); try { embeddedConfigInfoPersistService.configInfoCount(); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } } @Test void testConfigInfoCountByTenant() { String tenant = "tenant124"; //mock total count when(databaseOperate.queryOne(anyString(), eq(new Object[] {tenant}), eq(Integer.class))).thenReturn( new Integer(90)); int count = embeddedConfigInfoPersistService.configInfoCount(tenant); assertEquals(90, count); when(databaseOperate.queryOne(anyString(), eq(new Object[] {tenant}), eq(Integer.class))).thenReturn(null); try { embeddedConfigInfoPersistService.configInfoCount(tenant); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } } @Test void testFindConfigInfoLike4Page() { String dataId = "dataId4567222*"; String group = "group3456789*"; String tenant = "tenant4567890"; String appName = "appName1234"; String content = "content123"; Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("appName", appName); configAdvanceInfo.put("content", content); //mock total count when(databaseOperate.queryOne(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(databaseOperate.queryMany(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Page configInfo4Page = embeddedConfigInfoPersistService.findConfigInfoLike4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testFindConfigInfoLike4PageWithTags() { String appName = "appName1234"; String content = "content123"; Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("appName", appName); configAdvanceInfo.put("content", content); configAdvanceInfo.put("config_tags", "tags,tag2"); String dataId = "dataId4567222*"; String group = "group3456789*"; String tenant = "tenant4567890"; //mock total count when(databaseOperate.queryOne(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content, "tags", "tag2"}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(databaseOperate.queryMany(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content, "tags", "tag2"}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Page configInfo4Page = embeddedConfigInfoPersistService.findConfigInfoLike4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testFindChangeConfig() { //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfoStateWrapper(0)); result.add(createMockConfigInfoStateWrapper(1)); result.add(createMockConfigInfoStateWrapper(2)); Timestamp startTime = new Timestamp(System.currentTimeMillis() - 1000L); long lastMaxId = 10000L; int pageSize = 30; when(databaseOperate.queryMany(anyString(), eq(new Object[] {startTime, lastMaxId, pageSize}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(result); List configInfo4List = embeddedConfigInfoPersistService.findChangeConfig(startTime, lastMaxId, pageSize); assertEquals(result.size(), configInfo4List.size()); } @Test void testSelectTagByConfig() { String dataId = "dataId4567222"; String group = "group3456789"; String tenant = "tenant4567890"; //mock page list List tagStrings = Arrays.asList("", "", ""); when(databaseOperate.queryMany(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenReturn(tagStrings); List configTags = embeddedConfigInfoPersistService.selectTagByConfig(dataId, group, tenant); assertEquals(tagStrings, configTags); } @Test void testFindConfigInfosByIds() { //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(databaseOperate.queryMany(anyString(), eq(new Object[] {123L, 1232345L}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); String ids = "123,1232345"; List configInfosByIds = embeddedConfigInfoPersistService.findConfigInfosByIds(ids); assertEquals(result.size(), configInfosByIds.size()); assertEquals(result.get(2).getDataId(), configInfosByIds.get(2).getDataId()); //blank ids. List nullResultBlankIds = embeddedConfigInfoPersistService.findConfigInfosByIds(""); assertTrue(nullResultBlankIds == null); } @Test void testFindConfigAdvanceInfo() { String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; //mock select tags List mockTags = Arrays.asList("tag1", "tag2", "tag3"); when(databaseOperate.queryMany(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenReturn(mockTags); String schema = "schema12345654"; //mock select config advance ConfigAdvanceInfo mockedAdvance = new ConfigAdvanceInfo(); mockedAdvance.setSchema(schema); when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ADVANCE_INFO_ROW_MAPPER))).thenReturn(mockedAdvance); //execute return mock obj ConfigAdvanceInfo configAdvanceInfo = embeddedConfigInfoPersistService.findConfigAdvanceInfo(dataId, group, tenant); //expect check schema & tags. assertEquals(mockedAdvance.getSchema(), configAdvanceInfo.getSchema()); assertEquals(String.join(",", mockTags), configAdvanceInfo.getConfigTags()); } @Test void testFindConfigAllInfo() { String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; //mock select tags List mockTags = Arrays.asList("tag1", "tag2", "tag3"); when(databaseOperate.queryMany(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenReturn(mockTags); String schema = "schema12345654"; //mock select config advance ConfigAllInfo mockedConfig = new ConfigAllInfo(); mockedConfig.setSchema(schema); when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(mockedConfig); //execute return mock obj ConfigAllInfo configAllInfo = embeddedConfigInfoPersistService.findConfigAllInfo(dataId, group, tenant); //expect check schema & tags. assertEquals(mockedConfig.getSchema(), configAllInfo.getSchema()); assertEquals(String.join(",", mockTags), configAllInfo.getConfigTags()); } @Test void testFindConfigInfoState() { String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; //mock select config state ConfigInfoStateWrapper mockedConfig = new ConfigInfoStateWrapper(); mockedConfig.setLastModified(2345678L); mockedConfig.setId(23456789098765L); when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfig); //execute return mock obj ConfigInfoStateWrapper configInfoStateWrapper = embeddedConfigInfoPersistService.findConfigInfoState(dataId, group, tenant); //expect check schema & tags. assertEquals(mockedConfig.getId(), configInfoStateWrapper.getId()); assertEquals(mockedConfig.getLastModified(), configInfoStateWrapper.getLastModified()); } @Test void testFindAllConfigInfo4Export() { //mock select config state List mockConfigs = new ArrayList<>(); mockConfigs.add(createMockConfigAllInfo(0)); mockConfigs.add(createMockConfigAllInfo(1)); mockConfigs.add(createMockConfigAllInfo(2)); String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; String appName = "appName1243"; List ids = Arrays.asList(132L, 1343L, 245L); when(databaseOperate.queryMany(anyString(), eq(new Object[] {132L, 1343L, 245L}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(mockConfigs); //execute return mock obj List configAllInfosIds = embeddedConfigInfoPersistService.findAllConfigInfo4Export(dataId, group, tenant, appName, ids); //expect check assertEquals(mockConfigs, configAllInfosIds); when(databaseOperate.queryMany(anyString(), eq(new Object[] {tenant, dataId, group, appName}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(mockConfigs); //execute return mock obj List configAllInfosWithDataId = embeddedConfigInfoPersistService.findAllConfigInfo4Export(dataId, group, tenant, appName, null); //expect check assertEquals(mockConfigs, configAllInfosWithDataId); } @Test void testQueryConfigInfoByNamespace() { //mock select config state List mockConfigs = new ArrayList<>(); mockConfigs.add(createMockConfigInfoWrapper(0)); mockConfigs.add(createMockConfigInfoWrapper(1)); mockConfigs.add(createMockConfigInfoWrapper(2)); String tenant = "tenant13245"; when(databaseOperate.queryMany(anyString(), eq(new Object[] {tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenReturn(mockConfigs); //execute return mock obj List configInfoWrappers = embeddedConfigInfoPersistService.queryConfigInfoByNamespace( tenant); //expect check assertEquals(mockConfigs, configInfoWrappers); } @Test void testGetTenantIdList() { //mock select config state List tenantStrings = Arrays.asList("tenant1", "tenant2", "tenant3"); Map g1 = new HashMap<>(); g1.put("TENANT_ID", tenantStrings.get(0)); Map g2 = new HashMap<>(); g2.put("TENANT_ID", tenantStrings.get(1)); Map g3 = new HashMap<>(); g3.put("TENANT_ID", tenantStrings.get(2)); List> params = new ArrayList<>(); params.addAll(Arrays.asList(g1, g2, g3)); when(databaseOperate.queryMany(anyString(), eq(new Object[] {}), eq(MAP_ROW_MAPPER))).thenReturn(params); int page = 10; int pageSize = 100; //execute return mock obj List returnTenants = embeddedConfigInfoPersistService.getTenantIdList(page, pageSize); //expect check assertEquals(tenantStrings, returnTenants); } @Test void testGetGroupIdList() { //mock select config state List groupStrings = Arrays.asList("group1", "group2", "group3"); Map g1 = new HashMap<>(); g1.put("GROUP_ID", groupStrings.get(0)); Map g2 = new HashMap<>(); g2.put("GROUP_ID", groupStrings.get(1)); Map g3 = new HashMap<>(); g3.put("GROUP_ID", groupStrings.get(2)); List> params = new ArrayList<>(); params.addAll(Arrays.asList(g1, g2, g3)); when(databaseOperate.queryMany(anyString(), eq(new Object[] {}), eq(MAP_ROW_MAPPER))).thenReturn(params); int page = 10; int pageSize = 100; //execute return mock obj List returnGroups = embeddedConfigInfoPersistService.getGroupIdList(page, pageSize); //expect check assertEquals(groupStrings, returnGroups); } @Test void testFindAllConfigInfoFragment() { //mock page list List mockConfigs = new ArrayList<>(); mockConfigs.add(createMockConfigInfoWrapper(0)); mockConfigs.add(createMockConfigInfoWrapper(1)); mockConfigs.add(createMockConfigInfoWrapper(2)); long lastId = 10111L; when(databaseOperate.queryMany(anyString(), eq(new Object[] {lastId}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenReturn(mockConfigs); int pageSize = 100; //execute return mock obj Page returnConfigPage = embeddedConfigInfoPersistService.findAllConfigInfoFragment(lastId, pageSize, true); //expect check assertEquals(mockConfigs, returnConfigPage.getPageItems()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoTagPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; /** * test for embedded config tag. * * @author shiyiyue */ @ExtendWith(SpringExtension.class) class EmbeddedConfigInfoTagPersistServiceImplTest { MockedStatic envUtilMockedStatic; MockedStatic embeddedStorageContextHolderMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; @Mock DatabaseOperate databaseOperate; private EmbeddedConfigInfoTagPersistServiceImpl embeddedConfigInfoTagPersistService; @Mock private DataSourceService dataSourceService; @BeforeEach void before() { embeddedStorageContextHolderMockedStatic = Mockito.mockStatic(EmbeddedStorageContextHolder.class); dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getDataSourceType()).thenReturn("derby"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))).thenReturn(false); embeddedConfigInfoTagPersistService = new EmbeddedConfigInfoTagPersistServiceImpl(databaseOperate); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); embeddedStorageContextHolderMockedStatic.close(); } @Test void testInsertOrUpdateTagOfAdd() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //mock query config state empty and return obj after insert ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = embeddedConfigInfoTagPersistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser); //mock insert invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag), eq(appName), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), any(Timestamp.class)), times(1)); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testInsertOrUpdateTagOfUpdate() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //mock query config state and return obj after update ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(new ConfigInfoStateWrapper()).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = embeddedConfigInfoTagPersistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser); //verify update to be invoked embeddedStorageContextHolderMockedStatic.verify(() -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), eq(appName), eq(dataId), eq(group), eq(tenant), eq(tag)), times(1)); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testInsertOrUpdateTagCasOfAdd() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); configInfo.setMd5("casMd5"); //mock query config state empty and return obj after insert ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = embeddedConfigInfoTagPersistService.insertOrUpdateTagCas(configInfo, tag, srcIp, srcUser); //verify insert to be invoked //mock insert invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag), eq(appName), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), any(Timestamp.class)), times(1)); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testInsertOrUpdateTagCasOfUpdate() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); configInfo.setMd5("casMd5"); //mock query config state and return obj after update ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(new ConfigInfoStateWrapper()).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; //mock cas update return 1 Mockito.when(databaseOperate.blockUpdate()).thenReturn(true); ConfigOperateResult configOperateResult = embeddedConfigInfoTagPersistService.insertOrUpdateTagCas(configInfo, tag, srcIp, srcUser); //verify update to be invoked embeddedStorageContextHolderMockedStatic.verify(() -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), eq(appName), eq(dataId), eq(group), eq(tenant), eq(tag), eq(configInfo.getMd5())), times(1)); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testRemoveConfigInfoTag() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; String tag = "tag123345"; String srcIp = "ip345678"; String srcUser = "user1234567"; embeddedConfigInfoTagPersistService.removeConfigInfoTag(dataId, group, tenant, tag, srcIp, srcUser); //verify delete sql invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag)), times(1)); } @Test void testFindConfigInfo4Tag() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; String tag = "tag123345"; //mock query tag return obj ConfigInfoTagWrapper configInfoTagWrapperMocked = new ConfigInfoTagWrapper(); configInfoTagWrapperMocked.setLastModified(System.currentTimeMillis()); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER))).thenReturn(configInfoTagWrapperMocked); ConfigInfoTagWrapper configInfo4TagReturn = embeddedConfigInfoTagPersistService.findConfigInfo4Tag(dataId, group, tenant, tag); assertEquals(configInfoTagWrapperMocked, configInfo4TagReturn); } @Test void testConfigInfoTagCount() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); //mock count Mockito.when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(308); //execute & verify int count = embeddedConfigInfoTagPersistService.configInfoTagCount(); assertEquals(308, count); } @Test void testFindAllConfigInfoTagForDumpAll() { //mock count Mockito.when(databaseOperate.queryOne(anyString(), eq(Integer.class))).thenReturn(308); List mockTagList = new ArrayList<>(); mockTagList.add(new ConfigInfoTagWrapper()); mockTagList.add(new ConfigInfoTagWrapper()); mockTagList.add(new ConfigInfoTagWrapper()); mockTagList.get(0).setLastModified(System.currentTimeMillis()); mockTagList.get(1).setLastModified(System.currentTimeMillis()); mockTagList.get(2).setLastModified(System.currentTimeMillis()); //mock query list Mockito.when(databaseOperate.queryMany(anyString(), eq(new Object[] {}), eq(CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER))) .thenReturn(mockTagList); int pageNo = 3; int pageSize = 100; //execute & verify Page returnTagPage = embeddedConfigInfoTagPersistService.findAllConfigInfoTagForDumpAll(pageNo, pageSize); assertEquals(308, returnTagPage.getTotalCount()); assertEquals(mockTagList, returnTagPage.getPageItems()); } @Test void testFindConfigInfoTags() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; List mockedTags = Arrays.asList("tags1", "tags11", "tags111"); Mockito.when(databaseOperate.queryMany(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))) .thenReturn(mockedTags); List configInfoTags = embeddedConfigInfoTagPersistService.findConfigInfoTags(dataId, group, tenant); assertEquals(mockedTags, configInfoTags); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/embedded/EmbeddedHistoryConfigInfoPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.embedded; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.embedded.EmbeddedStorageContextHolder; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_DETAIL_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_LIST_ROW_MAPPER; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; /** * test for embedded config history. * * @author shiyiyue */ @ExtendWith(SpringExtension.class) class EmbeddedHistoryConfigInfoPersistServiceImplTest { MockedStatic envUtilMockedStatic; MockedStatic embeddedStorageContextHolderMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; @Mock DatabaseOperate databaseOperate; private EmbeddedHistoryConfigInfoPersistServiceImpl embeddedHistoryConfigInfoPersistService; @Mock private DataSourceService dataSourceService; @BeforeEach void before() { embeddedStorageContextHolderMockedStatic = Mockito.mockStatic(EmbeddedStorageContextHolder.class); dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getDataSourceType()).thenReturn("derby"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))) .thenReturn(false); embeddedHistoryConfigInfoPersistService = new EmbeddedHistoryConfigInfoPersistServiceImpl(databaseOperate); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); embeddedStorageContextHolderMockedStatic.close(); } @Test void testInsertConfigHistoryAtomic() { String dataId = "dateId243"; String group = "group243"; String tenant = "tenant243"; String content = "content243"; String appName = "appName243"; long id = 123456787765432L; String srcUser = "user12345"; String srcIp = "ip1234"; String ops = "D"; String publishType = "formal"; String extraInfo = "{\"type\":\"properties\"}"; Timestamp timestamp = new Timestamp(System.currentTimeMillis()); ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //expect insert success,verify insert invoked embeddedHistoryConfigInfoPersistService.insertConfigHistoryAtomic(id, configInfo, srcIp, srcUser, timestamp, ops, publishType, null, extraInfo); //verify insert to be invoked embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(id), eq(dataId), eq(group), eq(tenant), eq(appName), eq(content), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser), eq(timestamp), eq(ops), eq(publishType), eq(""), eq(extraInfo), eq(configInfo.getEncryptedDataKey())), times(1)); } @Test void testRemoveConfigHistory() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); int pageSize = 1233; embeddedHistoryConfigInfoPersistService.removeConfigHistory(timestamp, pageSize); //verify delete by time and size invoked. embeddedStorageContextHolderMockedStatic.verify( () -> EmbeddedStorageContextHolder.addSqlContext(anyString(), eq(timestamp), eq(pageSize)), times(1)); } @Test void testFindDeletedConfig() { //mock query list return ConfigHistoryInfo mockObj1 = new ConfigHistoryInfo(); mockObj1.setDataId("data_id1"); mockObj1.setGroup("group_id1"); mockObj1.setTenant("tenant_id1"); mockObj1.setMd5("md51"); mockObj1.setLastModifiedTime(new Timestamp(System.currentTimeMillis())); List list = new ArrayList<>(); list.add(mockObj1); ConfigHistoryInfo mockObj2 = new ConfigHistoryInfo(); mockObj2.setDataId("data_id2"); mockObj2.setGroup("group_id2"); mockObj2.setTenant("tenant_id2"); mockObj2.setMd5("md52"); mockObj2.setLastModifiedTime(new Timestamp(System.currentTimeMillis())); list.add(mockObj2); int pageSize = 1233; long startId = 23456; Timestamp timestamp = new Timestamp(System.currentTimeMillis()); String publishType = "formal"; Mockito.when( databaseOperate.queryMany(anyString(), eq(new Object[] {publishType, timestamp, startId, pageSize}), eq(HISTORY_DETAIL_ROW_MAPPER))).thenReturn(list); //execute List deletedConfig = embeddedHistoryConfigInfoPersistService.findDeletedConfig( timestamp, startId, pageSize, "formal"); //expect verify assertEquals("data_id1", deletedConfig.get(0).getDataId()); assertEquals("group_id1", deletedConfig.get(0).getGroup()); assertEquals("tenant_id1", deletedConfig.get(0).getTenant()); assertEquals(mockObj1.getLastModifiedTime(), new Timestamp(deletedConfig.get(0).getLastModified())); assertEquals("data_id2", deletedConfig.get(1).getDataId()); assertEquals("group_id2", deletedConfig.get(1).getGroup()); assertEquals("tenant_id2", deletedConfig.get(1).getTenant()); assertEquals(mockObj2.getLastModifiedTime(), new Timestamp(deletedConfig.get(1).getLastModified())); } @Test void testFindConfigHistory() { String dataId = "dataId34567"; String group = "group34567"; String tenant = "tenant34567"; //mock count Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {dataId, group, tenant}), eq(Integer.class))) .thenReturn(300); //mock list List mockList = new ArrayList<>(); mockList.add(createMockConfigHistoryInfo(0)); mockList.add(createMockConfigHistoryInfo(1)); mockList.add(createMockConfigHistoryInfo(2)); Mockito.when(databaseOperate.queryMany(anyString(), eq(new Object[] {dataId, group, tenant}), eq(HISTORY_LIST_ROW_MAPPER))).thenReturn(mockList); int pageSize = 100; int pageNo = 2; //execute & verify Page historyReturn = embeddedHistoryConfigInfoPersistService.findConfigHistory(dataId, group, tenant, pageNo, pageSize); assertEquals(mockList, historyReturn.getPageItems()); assertEquals(300, historyReturn.getTotalCount()); } @Test void testDetailConfigHistory() { long nid = 256789; //mock query ConfigHistoryInfo mockConfigHistoryInfo = createMockConfigHistoryInfo(0); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenReturn(mockConfigHistoryInfo); //execute & verify ConfigHistoryInfo historyReturn = embeddedHistoryConfigInfoPersistService.detailConfigHistory(nid); assertEquals(mockConfigHistoryInfo, historyReturn); } @Test void testDetailPreviousConfigHistory() { long nid = 256789; //mock query ConfigHistoryInfo mockConfigHistoryInfo = createMockConfigHistoryInfo(0); Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenReturn(mockConfigHistoryInfo); //execute & verify ConfigHistoryInfo historyReturn = embeddedHistoryConfigInfoPersistService.detailPreviousConfigHistory(nid); assertEquals(mockConfigHistoryInfo, historyReturn); } @Test void testFindConfigHistoryCountByTime() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); //mock count Mockito.when(databaseOperate.queryOne(anyString(), eq(new Object[] {timestamp}), eq(Integer.class))) .thenReturn(308); //execute & verify int count = embeddedHistoryConfigInfoPersistService.findConfigHistoryCountByTime(timestamp); assertEquals(308, count); } private ConfigHistoryInfo createMockConfigHistoryInfo(long mockId) { ConfigHistoryInfo configAllInfo = new ConfigHistoryInfo(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setContent("23456789000content"); configAllInfo.setOpType("D"); configAllInfo.setEncryptedDataKey("key4567"); configAllInfo.setSrcIp("ip567"); configAllInfo.setSrcUser("user1234"); configAllInfo.setMd5("md52345678"); return configAllInfo; } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoBetaPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.sql.ExternalStorageUtils; import com.alibaba.nacos.config.server.utils.TestCaseUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.support.TransactionTemplate; import java.util.ArrayList; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; 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.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) class ExternalConfigInfoBetaPersistServiceImplTest { MockedStatic envUtilMockedStatic; MockedStatic externalStorageUtilsMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; private ExternalConfigInfoBetaPersistServiceImpl externalConfigInfoBetaPersistService; @Mock private DataSourceService dataSourceService; @Mock private JdbcTemplate jdbcTemplate; private TransactionTemplate transactionTemplate = TestCaseUtils.createMockTransactionTemplate(); @BeforeEach void before() { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); externalStorageUtilsMockedStatic = Mockito.mockStatic(ExternalStorageUtils.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getTransactionTemplate()).thenReturn(transactionTemplate); when(dataSourceService.getJdbcTemplate()).thenReturn(jdbcTemplate); when(dataSourceService.getDataSourceType()).thenReturn("mysql"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))).thenReturn(false); externalConfigInfoBetaPersistService = new ExternalConfigInfoBetaPersistServiceImpl(); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); externalStorageUtilsMockedStatic.close(); } @Test void testInsertOrUpdateBetaOfUpdate() { String dataId = "betaDataId113"; String group = "group"; String tenant = "tenant"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper, mockedConfigInfoStateWrapper); //execute String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); ConfigOperateResult configOperateResult = externalConfigInfoBetaPersistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify update to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(configInfo.getEncryptedDataKey()), eq(dataId), eq(group), eq(tenant)); } @Test void testInsertOrUpdateBetaOfAdd() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)) .thenReturn(mockedConfigInfoStateWrapper); String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); //execute ConfigOperateResult configOperateResult = externalConfigInfoBetaPersistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify add to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(dataId), eq(group), eq(tenant), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getEncryptedDataKey())); } @Test void testInsertOrUpdateBetaOfException() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); // mock update throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(configInfo.getEncryptedDataKey()), eq(dataId), eq(group), eq(tenant))).thenThrow(new CannotGetJdbcConnectionException("mock fail")); //execute of update& expect. try { externalConfigInfoBetaPersistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail", exception.getMessage()); } //mock query return null when(jdbcTemplate.queryForObject(anyString(), eq(new Object[]{dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null); //mock add throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(dataId), eq(group), eq(tenant), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getEncryptedDataKey()))).thenThrow(new CannotGetJdbcConnectionException("mock fail add")); //execute of add& expect. try { externalConfigInfoBetaPersistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail add", exception.getMessage()); } //mock query throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("get c fail")); //execute of add& expect. try { externalConfigInfoBetaPersistService.insertOrUpdateBeta(configInfo, betaIps, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("get c fail", exception.getMessage()); } } @Test void testInsertOrUpdateBetaCasOfUpdate() { String dataId = "betaDataId113"; String group = "group"; String tenant = "tenant"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper, mockedConfigInfoStateWrapper); //execute String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); configInfo.setMd5("casMd5"); //mock cas update when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(betaIps), eq(srcIp), eq(srcUser), eq(appName), eq(dataId), eq(group), eq(tenant), eq(configInfo.getMd5()))).thenReturn(1); ConfigOperateResult configOperateResult = externalConfigInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify cas update to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(betaIps), eq(srcIp), eq(srcUser), eq(appName), eq(dataId), eq(group), eq(tenant), eq(configInfo.getMd5())); } @Test void testInsertOrUpdateBetaCasOfAdd() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)) .thenReturn(mockedConfigInfoStateWrapper); String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); //execute ConfigOperateResult configOperateResult = externalConfigInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, betaIps, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify add to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(dataId), eq(group), eq(tenant), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getEncryptedDataKey())); } @Test void testInsertOrUpdateBetaCasOfException() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); String betaIps = "betaips..."; String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); configInfo.setMd5("casMd5"); // mock update throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(betaIps), eq(srcIp), eq(srcUser), eq(appName), eq(dataId), eq(group), eq(tenant), eq(configInfo.getMd5()))).thenThrow(new CannotGetJdbcConnectionException("mock fail")); //execute of update& expect. try { externalConfigInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, betaIps, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail", exception.getMessage()); } //mock query return null when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null); //mock add throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(dataId), eq(group), eq(tenant), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE)), eq(betaIps), eq(srcIp), eq(srcUser), eq(configInfo.getEncryptedDataKey()))).thenThrow( new CannotGetJdbcConnectionException("mock fail add")); //execute of add& expect. try { externalConfigInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, betaIps, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail add", exception.getMessage()); } //mock query throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("get c fail")); //execute of add& expect. try { externalConfigInfoBetaPersistService.insertOrUpdateBetaCas(configInfo, betaIps, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("get c fail", exception.getMessage()); } } @Test void testRemoveConfigInfo4Beta() { String dataId = "dataId456789"; String group = "group4567"; String tenant = "tenant56789o0"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); externalConfigInfoBetaPersistService.removeConfigInfo4Beta(dataId, group, tenant); //verity Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(dataId), eq(group), eq(tenant)); //mock query throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("mock fail11111")); try { externalConfigInfoBetaPersistService.removeConfigInfo4Beta(dataId, group, tenant); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail11111", exception.getMessage()); } } @Test void testFindConfigInfo4Beta() { String dataId = "dataId456789"; String group = "group4567"; String tenant = "tenant56789o0"; //mock exist beta ConfigInfoBetaWrapper mockedConfigInfoStateWrapper = new ConfigInfoBetaWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); ConfigInfoBetaWrapper configInfo4BetaReturn = externalConfigInfoBetaPersistService.findConfigInfo4Beta(dataId, group, tenant); assertEquals(mockedConfigInfoStateWrapper, configInfo4BetaReturn); //mock query throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("mock fail11111")); try { externalConfigInfoBetaPersistService.findConfigInfo4Beta(dataId, group, tenant); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail11111", exception.getMessage()); } //mock query throw EmptyResultDataAccessException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)); ConfigInfoBetaWrapper configInfo4BetaNull = externalConfigInfoBetaPersistService.findConfigInfo4Beta(dataId, group, tenant); assertNull(configInfo4BetaNull); } @Test void testConfigInfoBetaCount() { when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(101); int returnCount = externalConfigInfoBetaPersistService.configInfoBetaCount(); assertEquals(101, returnCount); } @Test void testFindAllConfigInfoBetaForDumpAll() { //mock count when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(12345); //mock page list List mockList = new ArrayList<>(); mockList.add(new ConfigInfoBetaWrapper()); mockList.add(new ConfigInfoBetaWrapper()); mockList.add(new ConfigInfoBetaWrapper()); mockList.get(0).setLastModified(System.currentTimeMillis()); mockList.get(1).setLastModified(System.currentTimeMillis()); mockList.get(2).setLastModified(System.currentTimeMillis()); when(jdbcTemplate.query(anyString(), eq(new Object[] {}), eq(CONFIG_INFO_BETA_WRAPPER_ROW_MAPPER))).thenReturn(mockList); int pageNo = 1; int pageSize = 101; when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(101); //execute & expect Page pageReturn = externalConfigInfoBetaPersistService.findAllConfigInfoBetaForDumpAll(pageNo, pageSize); assertEquals(mockList, pageReturn.getPageItems()); assertEquals(101, pageReturn.getTotalCount()); //mock count throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenThrow(new CannotGetJdbcConnectionException("345678909fail")); //execute &expect try { externalConfigInfoBetaPersistService.findAllConfigInfoBetaForDumpAll(pageNo, pageSize); assertTrue(false); } catch (Exception exception) { assertEquals("345678909fail", exception.getMessage()); } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoGrayPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.service.sql.ExternalStorageUtils; import com.alibaba.nacos.config.server.utils.TestCaseUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import static com.alibaba.nacos.config.server.constant.Constants.ENCODE; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) public class ExternalConfigInfoGrayPersistServiceImplTest { private ExternalConfigInfoGrayPersistServiceImpl externalConfigInfoGrayPersistService; @Mock private DataSourceService dataSourceService; @Mock private JdbcTemplate jdbcTemplate; @Mock private HistoryConfigInfoPersistService historyConfigInfoPersistService; @Mock DatabaseOperate databaseOperate; private TransactionTemplate transactionTemplate = TestCaseUtils.createMockTransactionTemplate(); MockedStatic envUtilMockedStatic; MockedStatic externalStorageUtilsMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; /** * before each tet case. */ @BeforeEach public void before() { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); externalStorageUtilsMockedStatic = Mockito.mockStatic(ExternalStorageUtils.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getTransactionTemplate()).thenReturn(transactionTemplate); when(dataSourceService.getJdbcTemplate()).thenReturn(jdbcTemplate); when(dataSourceService.getDataSourceType()).thenReturn("mysql"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))) .thenReturn(false); externalConfigInfoGrayPersistService = new ExternalConfigInfoGrayPersistServiceImpl( historyConfigInfoPersistService); } /** * after each test case. */ @AfterEach public void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); externalStorageUtilsMockedStatic.close(); } @Test public void testInsertOrUpdateGrayOfUpdate() { String dataId = "grayDataId113"; String group = "group"; String tenant = "tenant"; //mock exist gray ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); //mock exist config info ConfigInfoGrayWrapper configAllInfo4Gray = new ConfigInfoGrayWrapper(); configAllInfo4Gray.setDataId(dataId); configAllInfo4Gray.setGroup(group); configAllInfo4Gray.setTenant(tenant); configAllInfo4Gray.setMd5("old_md5"); String grayName = "grayName..."; when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(configAllInfo4Gray); String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appName"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); String grayRule = "grayRule..."; ConfigOperateResult configOperateResult = externalConfigInfoGrayPersistService.insertOrUpdateGray(configInfo, grayName, grayRule, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify update to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(configInfo.getContent()), eq(configInfo.getEncryptedDataKey()), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(grayRule), eq(dataId), eq(group), eq(tenant), eq(grayName)); } @Test public void testInsertOrUpdateGrayOfAdd() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); String grayName = "grayName..."; when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)) .thenReturn(mockedConfigInfoStateWrapper); String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); String grayRule = "grayRule..."; //execute ConfigOperateResult configOperateResult = externalConfigInfoGrayPersistService.insertOrUpdateGray(configInfo, grayName, grayRule, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify add to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(grayRule), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getEncryptedDataKey()), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser)); } @Test public void testInsertOrUpdateGrayOfException() { String dataId = "grapDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist gray ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); String grayName = "grayName..."; when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); ConfigInfoGrayWrapper mockedConfigInfoGrayWrapper = new ConfigInfoGrayWrapper(); mockedConfigInfoGrayWrapper.setDataId(dataId); mockedConfigInfoGrayWrapper.setGroup(group); mockedConfigInfoGrayWrapper.setTenant(tenant); mockedConfigInfoGrayWrapper.setId(mockedConfigInfoStateWrapper.getId()); mockedConfigInfoGrayWrapper.setLastModified(mockedConfigInfoStateWrapper.getLastModified()); mockedConfigInfoGrayWrapper.setGrayName(grayName); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoGrayWrapper); String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); configInfo.setMd5("casMd5"); String grayRule = "grayRule..."; // mock update throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(configInfo.getEncryptedDataKey()), eq(MD5Utils.md5Hex(content, ENCODE)), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(grayRule), eq(dataId), eq(group), eq(tenant), eq(grayName))).thenThrow( new CannotGetJdbcConnectionException("mock fail")); //execute of update& expect. try { externalConfigInfoGrayPersistService.insertOrUpdateGray(configInfo, grayName, grayRule, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail", exception.getMessage()); } //mock query return null when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null); //mock add throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(grayRule), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getEncryptedDataKey()), eq(MD5Utils.md5Hex(content, ENCODE)), eq(srcIp), eq(srcUser))).thenThrow( new CannotGetJdbcConnectionException("mock fail add")); //execute of add& expect. try { externalConfigInfoGrayPersistService.insertOrUpdateGray(configInfo, grayName, grayRule, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail add", exception.getMessage()); } //mock query throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow( new CannotGetJdbcConnectionException("get c fail")); //execute of add& expect. try { externalConfigInfoGrayPersistService.insertOrUpdateGray(configInfo, grayName, grayRule, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("get c fail", exception.getMessage()); } } @Test public void testInsertOrUpdateGrayCasOfUpdate() { String dataId = "grayDataId113"; String group = "group"; String tenant = "tenant"; //mock exist gray ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); String grayName = "grayName..."; when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper, mockedConfigInfoStateWrapper); //execute String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); configInfo.setMd5("casMd5"); String grayRule = "grayRule..."; //mock cas update when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(content, ENCODE)), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(grayRule), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(configInfo.getMd5()))).thenReturn(1); //mock exist config info ConfigInfoGrayWrapper configAllInfo4Gray = new ConfigInfoGrayWrapper(); configAllInfo4Gray.setDataId(dataId); configAllInfo4Gray.setGroup(group); configAllInfo4Gray.setTenant(tenant); configAllInfo4Gray.setMd5("old_md5"); String grayName1 = "grayName1..."; when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(configAllInfo4Gray); ConfigOperateResult configOperateResult = externalConfigInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, grayName, grayRule, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify cas update to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(content, ENCODE)), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(grayRule), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(configInfo.getMd5())); } @Test public void testInsertOrUpdateGrayCasOfAdd() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); String grayName = "grayName..."; when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)) .thenReturn(mockedConfigInfoStateWrapper); String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); configInfo.setMd5("csMd5"); String grayRule = "grayRule..."; //execute ConfigOperateResult configOperateResult = externalConfigInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, grayName, grayRule, srcIp, srcUser); //expect return obj assertEquals(mockedConfigInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(mockedConfigInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); //verify add to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(grayRule), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getEncryptedDataKey()), eq(MD5Utils.md5Hex(content, ENCODE)), eq(srcIp), eq(srcUser)); } @Test public void testInsertOrUpdateGrayCasOfException() { String dataId = "betaDataId113"; String group = "group113"; String tenant = "tenant113"; //mock exist beta ConfigInfoStateWrapper mockedConfigInfoStateWrapper = new ConfigInfoStateWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); String grayName = "grayName..."; when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); ConfigInfoGrayWrapper mockedConfigInfoGrayWrapper = new ConfigInfoGrayWrapper(); mockedConfigInfoGrayWrapper.setDataId(dataId); mockedConfigInfoGrayWrapper.setGroup(group); mockedConfigInfoGrayWrapper.setTenant(tenant); mockedConfigInfoGrayWrapper.setId(mockedConfigInfoStateWrapper.getId()); mockedConfigInfoGrayWrapper.setLastModified(mockedConfigInfoStateWrapper.getLastModified()); mockedConfigInfoGrayWrapper.setGrayName(grayName); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoGrayWrapper); String srcIp = "srcUp..."; String srcUser = "srcUser..."; String appName = "appname"; String content = "content111"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key34567"); configInfo.setMd5("casMd5"); String grayRule = "grayRule..."; // mock update throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(content, ENCODE)), eq(srcIp), eq(srcUser), eq(configInfo.getAppName()), eq(grayRule), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(configInfo.getMd5()))).thenThrow( new CannotGetJdbcConnectionException("updat mock fail")); //execute of update& expect. try { externalConfigInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, grayName, grayRule, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("updat mock fail", exception.getMessage()); } //mock query return null when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null); //mock add throw CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(dataId), eq(group), eq(tenant), eq(grayName), eq(grayRule), eq(configInfo.getAppName()), eq(configInfo.getContent()), eq(configInfo.getEncryptedDataKey()), eq(MD5Utils.md5Hex(content, ENCODE)), eq(srcIp), eq(srcUser))).thenThrow( new CannotGetJdbcConnectionException("mock fail add")); //execute of add& expect. try { externalConfigInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, grayName, grayRule, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail add", exception.getMessage()); } //mock query throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow( new CannotGetJdbcConnectionException("get c fail")); //execute of add& expect. try { externalConfigInfoGrayPersistService.insertOrUpdateGrayCas(configInfo, grayName, grayRule, srcIp, srcUser); assertTrue(false); } catch (Exception exception) { assertEquals("get c fail", exception.getMessage()); } } @Test void testRemoveConfigInfo() { String dataId = "dataId4567"; String group = "group3456789"; String tenant = "tenant4567890"; final String grayName = "grayName1"; //mock exist config info ConfigInfoGrayWrapper configAllInfo4Gray = new ConfigInfoGrayWrapper(); configAllInfo4Gray.setDataId(dataId); configAllInfo4Gray.setGroup(group); configAllInfo4Gray.setTenant(tenant); configAllInfo4Gray.setMd5("old_md5"); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(configAllInfo4Gray); Mockito.when(databaseOperate.update(any())).thenReturn(true); String srcIp = "srcIp1234"; String srcUser = "srcUser"; externalConfigInfoGrayPersistService.removeConfigInfoGray(dataId, group, tenant, grayName, srcIp, srcUser); Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(dataId), eq(group), eq(tenant), eq(grayName)); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo4Gray.getId()), eq(configAllInfo4Gray), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("D"), eq("gray"), eq(grayName), anyString()); // Test the exception handling for CannotGetJdbcConnectionException when(jdbcTemplate.update(anyString(), eq(dataId), eq(group), eq(tenant), eq(grayName))).thenThrow( new CannotGetJdbcConnectionException("mock fail11111")); } @Test public void testFindChangeConfigInfo4Gray() { List mockList = new ArrayList<>(); mockList.add(new ConfigInfoGrayWrapper()); mockList.add(new ConfigInfoGrayWrapper()); mockList.add(new ConfigInfoGrayWrapper()); mockList.get(0).setLastModified(System.currentTimeMillis()); mockList.get(1).setLastModified(System.currentTimeMillis()); mockList.get(2).setLastModified(System.currentTimeMillis()); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); long lastMaxId = 123; when(jdbcTemplate.query(anyString(), eq(new Object[] {timestamp, lastMaxId, 100}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(mockList) .thenThrow(new CannotGetJdbcConnectionException("mock exception22")); List changeConfig = externalConfigInfoGrayPersistService.findChangeConfig(timestamp, lastMaxId, 100); assertTrue(changeConfig.get(0).getLastModified() == mockList.get(0).getLastModified()); assertTrue(changeConfig.get(1).getLastModified() == mockList.get(1).getLastModified()); assertTrue(changeConfig.get(2).getLastModified() == mockList.get(2).getLastModified()); try { externalConfigInfoGrayPersistService.findChangeConfig(timestamp, lastMaxId, 100); assertTrue(false); } catch (CannotGetJdbcConnectionException exception) { assertEquals("mock exception22", exception.getMessage()); } } @Test public void testFindConfigInfo4Gray() { String dataId = "dataId456789"; String group = "group4567"; String tenant = "tenant56789o0"; String grayName = "gray12"; //mock exist gray ConfigInfoGrayWrapper mockedConfigInfoStateWrapper = new ConfigInfoGrayWrapper(); mockedConfigInfoStateWrapper.setDataId(dataId); mockedConfigInfoStateWrapper.setGroup(group); mockedConfigInfoStateWrapper.setGrayName(grayName); mockedConfigInfoStateWrapper.setTenant(tenant); mockedConfigInfoStateWrapper.setId(123456L); mockedConfigInfoStateWrapper.setLastModified(System.currentTimeMillis()); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfigInfoStateWrapper); ConfigInfoGrayWrapper configInfo4GrayReturn = externalConfigInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, grayName); assertEquals(mockedConfigInfoStateWrapper, configInfo4GrayReturn); //mock query throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenThrow( new CannotGetJdbcConnectionException("mock fail11111")); try { externalConfigInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, grayName); assertTrue(false); } catch (Exception exception) { assertEquals("mock fail11111", exception.getMessage()); } //mock query throw EmptyResultDataAccessException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, grayName}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)); ConfigInfoGrayWrapper configInfo4GrayNull = externalConfigInfoGrayPersistService.findConfigInfo4Gray(dataId, group, tenant, grayName); assertNull(configInfo4GrayNull); } @Test public void testConfigInfoGrayCount() { when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(101); int returnCount = externalConfigInfoGrayPersistService.configInfoGrayCount(); assertEquals(101, returnCount); } @Test public void testFindAllConfigInfoGrayForDumpAll() { //mock count when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(12345); //mock page list List mockList = new ArrayList<>(); mockList.add(new ConfigInfoGrayWrapper()); mockList.add(new ConfigInfoGrayWrapper()); mockList.add(new ConfigInfoGrayWrapper()); mockList.get(0).setLastModified(System.currentTimeMillis()); mockList.get(1).setLastModified(System.currentTimeMillis()); mockList.get(2).setLastModified(System.currentTimeMillis()); when(jdbcTemplate.query(anyString(), eq(new Object[] {}), eq(CONFIG_INFO_GRAY_WRAPPER_ROW_MAPPER))).thenReturn( mockList); int pageNo = 1; int pageSize = 101; when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(101); //execute & expect Page pageReturn = externalConfigInfoGrayPersistService.findAllConfigInfoGrayForDumpAll( pageNo, pageSize); assertEquals(mockList, pageReturn.getPageItems()); assertEquals(101, pageReturn.getTotalCount()); //mock count throw CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenThrow( new CannotGetJdbcConnectionException("345678909fail")); //execute &expect try { externalConfigInfoGrayPersistService.findAllConfigInfoGrayForDumpAll(pageNo, pageSize); assertTrue(false); } catch (Exception exception) { assertEquals("345678909fail", exception.getMessage()); } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigAdvanceInfo; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService; import com.alibaba.nacos.config.server.service.sql.ExternalStorageUtils; import com.alibaba.nacos.config.server.utils.ConfigExtInfoUtil; import com.alibaba.nacos.config.server.utils.TestCaseUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.plugin.datasource.MapperManager; import com.alibaba.nacos.plugin.datasource.constants.TableConstant; import com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.dao.CannotAcquireLockException; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.PreparedStatementCreator; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.sql.Timestamp; 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 static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ADVANCE_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_ALL_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_WRAPPER_ROW_MAPPER; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) class ExternalConfigInfoPersistServiceImplTest { MockedStatic envUtilMockedStatic; MockedStatic externalStorageUtilsMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; private ExternalConfigInfoPersistServiceImpl externalConfigInfoPersistService; @Mock private HistoryConfigInfoPersistService historyConfigInfoPersistService; @Mock private DataSourceService dataSourceService; @Mock private JdbcTemplate jdbcTemplate; private TransactionTemplate transactionTemplate = TestCaseUtils.createMockTransactionTemplate(); @BeforeEach void before() { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); externalStorageUtilsMockedStatic = Mockito.mockStatic(ExternalStorageUtils.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getTransactionTemplate()).thenReturn(transactionTemplate); when(dataSourceService.getJdbcTemplate()).thenReturn(jdbcTemplate); when(dataSourceService.getDataSourceType()).thenReturn("mysql"); /*when(EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))).thenReturn(false);*/ envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))) .thenReturn(false); externalConfigInfoPersistService = new ExternalConfigInfoPersistServiceImpl(historyConfigInfoPersistService); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); externalStorageUtilsMockedStatic.close(); } @Test void testInsertOrUpdateOfInsertConfigSuccess() { String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String appName = "appNameNew"; String content = "content132456"; Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tag1,tag2"); ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); long insertConfigIndoId = 12345678765L; GeneratedKeyHolder generatedKeyHolder = TestCaseUtils.createGeneratedKeyHolder(insertConfigIndoId); externalStorageUtilsMockedStatic.when(ExternalStorageUtils::createKeyHolder).thenReturn(generatedKeyHolder); //mock get config state Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null, new ConfigInfoStateWrapper()); //mock insert config info Mockito.when(jdbcTemplate.update(any(PreparedStatementCreator.class), eq(generatedKeyHolder))).thenReturn(1); Mockito.when(jdbcTemplate.update( eq(externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant))) .thenReturn(1); Mockito.when(jdbcTemplate.update( eq(externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant))) .thenReturn(1); String srcIp = "srcIp"; String srcUser = "srcUser"; //mock insert config info Mockito.doNothing().when(historyConfigInfoPersistService) .insertConfigHistoryAtomic(eq(0), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser))); externalConfigInfoPersistService.insertOrUpdate(srcIp, srcUser, configInfo, configAdvanceInfo); //expect insert config info Mockito.verify(jdbcTemplate, times(1)).update(any(PreparedStatementCreator.class), eq(generatedKeyHolder)); //expect insert config tags Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); //expect insert history info Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(0L), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser))); } @Test void testInsertOrUpdateCasOfInsertConfigSuccess() { Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tag1,tag2"); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String appName = "appName"; String content = "content132456"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); long insertConfigIndoId = 12345678765L; GeneratedKeyHolder generatedKeyHolder = TestCaseUtils.createGeneratedKeyHolder(insertConfigIndoId); externalStorageUtilsMockedStatic.when(ExternalStorageUtils::createKeyHolder).thenReturn(generatedKeyHolder); //mock get config state Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null, new ConfigInfoStateWrapper()); //mock insert config info Mockito.when(jdbcTemplate.update(any(PreparedStatementCreator.class), eq(generatedKeyHolder))).thenReturn(1); Mockito.when(jdbcTemplate.update( eq(externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant))) .thenReturn(1); Mockito.when(jdbcTemplate.update( eq(externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant))) .thenReturn(1); String srcIp = "srcIp"; String srcUser = "srcUser"; //mock insert config info Mockito.doNothing().when(historyConfigInfoPersistService) .insertConfigHistoryAtomic(eq(0), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser))); externalConfigInfoPersistService.insertOrUpdateCas(srcIp, srcUser, configInfo, configAdvanceInfo); //expect insert config info Mockito.verify(jdbcTemplate, times(1)).update(any(PreparedStatementCreator.class), eq(generatedKeyHolder)); //expect insert config tags Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(insertConfigIndoId), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); //expect insert history info Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(0L), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtraInfoFromAdvanceInfoMap(configAdvanceInfo, srcUser))); } @Test void testInsertOrUpdateOfException() { String dataId = "dataId"; String group = "group"; String tenant = "tenant"; //mock get config state Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null); //mock insert config throw exception long insertConfigIndoId = 12345678765L; GeneratedKeyHolder generatedKeyHolder = TestCaseUtils.createGeneratedKeyHolder(insertConfigIndoId); externalStorageUtilsMockedStatic.when(ExternalStorageUtils::createKeyHolder).thenReturn(generatedKeyHolder); Mockito.when(jdbcTemplate.update(any(PreparedStatementCreator.class), any(KeyHolder.class))) .thenThrow(new CannotGetJdbcConnectionException("mock fail")); Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tag1,tag2"); ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, null, "content"); try { externalConfigInfoPersistService.insertOrUpdate("srcIp", "srcUser", configInfo, configAdvanceInfo); assertTrue(false); } catch (Exception e) { assertEquals("mock fail", e.getMessage()); } } @Test void testInsertOrUpdateOfUpdateConfigSuccess() { Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tag1,tag2"); configAdvanceInfo.put("desc", "desc11"); configAdvanceInfo.put("use", "use2233"); configAdvanceInfo.put("effect", "effect222"); configAdvanceInfo.put("type", "type3"); configAdvanceInfo.put("schema", "schema"); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String content = "content132456"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, null, content); String encryptedDataKey = "key34567"; configInfo.setEncryptedDataKey(encryptedDataKey); //mock get config state,first and second is not null Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(new ConfigInfoStateWrapper(), new ConfigInfoStateWrapper()); ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(group); configAllInfo.setTenant(tenant); configAllInfo.setAppName("old_app"); configAllInfo.setMd5("old_md5"); configAllInfo.setId(12345678765L); //mock get all config info Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(configAllInfo); String srcIp = "srcIp"; String srcUser = "srcUser"; //mock update config info Mockito.when(jdbcTemplate.update( eq(externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO) .update(Arrays.asList("content", "md5", "src_ip", "src_user", "gmt_modified@NOW()", "app_name", "c_desc", "c_use", "effect", "type", "c_schema", "encrypted_data_key"), Arrays.asList("data_id", "group_id", "tenant_id"))), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser), eq(configAllInfo.getAppName()), eq(configAdvanceInfo.get("desc")), eq(configAdvanceInfo.get("use")), eq(configAdvanceInfo.get("effect")), eq(configAdvanceInfo.get("type")), eq(configAdvanceInfo.get("schema")), eq(encryptedDataKey), eq(configInfo.getDataId()), eq(configInfo.getGroup()), eq(tenant))).thenReturn(1); //mock insert config tags. Mockito.when(jdbcTemplate.update( eq(externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(12345678765L), anyString(), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant))).thenReturn(1); //mock insert his config info Mockito.doNothing().when(historyConfigInfoPersistService) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); externalConfigInfoPersistService.insertOrUpdate(srcIp, srcUser, configInfo, configAdvanceInfo); //expect update config tags Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(configAllInfo.getId()), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(configAllInfo.getId()), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); //expect insert history info Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), any(ConfigInfo.class), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("U"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); } @Test void testInsertOrUpdateCasOfUpdateConfigSuccess() { Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tag1,tag2"); configAdvanceInfo.put("desc", "desc11"); configAdvanceInfo.put("use", "use2233"); configAdvanceInfo.put("effect", "effect222"); configAdvanceInfo.put("type", "type3"); configAdvanceInfo.put("schema", "schema"); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String content = "content132456"; String encryptedDataKey = "key34567"; String casMd5 = "casMd5.."; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, null, content); configInfo.setMd5(casMd5); configInfo.setEncryptedDataKey(encryptedDataKey); //mock get config state,first and second is not null Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))) .thenReturn(new ConfigInfoStateWrapper(), new ConfigInfoStateWrapper()); ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(group); configAllInfo.setTenant(tenant); configAllInfo.setAppName("old_app"); configAllInfo.setMd5("old_md5"); configAllInfo.setId(12345678765L); //mock get all config info Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(configAllInfo); String srcIp = "srcIp"; String srcUser = "srcUser"; //mock update config info cas Mockito.when( jdbcTemplate.update(anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), eq(configAllInfo.getAppName()), eq(configAdvanceInfo.get("desc")), eq(configAdvanceInfo.get("use")), eq(configAdvanceInfo.get("effect")), eq(configAdvanceInfo.get("type")), eq(configAdvanceInfo.get("schema")), eq(encryptedDataKey), eq(dataId), eq(group), eq(tenant), eq(casMd5))).thenReturn(1); //mock insert config tags. Mockito.when(jdbcTemplate.update( eq(externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(configAllInfo.getId()), anyString(), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant))) .thenReturn(1); //mock insert his config info Mockito.doNothing().when(historyConfigInfoPersistService) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), eq(configInfo), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("I"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); externalConfigInfoPersistService.insertOrUpdateCas(srcIp, srcUser, configInfo, configAdvanceInfo); //expect update config cas Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(content), eq(MD5Utils.md5Hex(content, Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), eq(configAllInfo.getAppName()), eq(configAdvanceInfo.get("desc")), eq(configAdvanceInfo.get("use")), eq(configAdvanceInfo.get("effect")), eq(configAdvanceInfo.get("type")), eq(configAdvanceInfo.get("schema")), eq(encryptedDataKey), eq(dataId), eq(group), eq(tenant), eq(casMd5)); //expect update config tags Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(configAllInfo.getId()), eq("tag1"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); Mockito.verify(jdbcTemplate, times(1)).update(eq( externalConfigInfoPersistService.mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_TAGS_RELATION) .insert(Arrays.asList("id", "tag_name", "tag_type", "data_id", "group_id", "tenant_id"))), eq(configAllInfo.getId()), eq("tag2"), eq(StringUtils.EMPTY), eq(dataId), eq(group), eq(tenant)); //expect insert history info Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), any(ConfigInfo.class), eq(srcIp), eq(srcUser), any(Timestamp.class), eq("U"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); } @Test void testCreatePsForInsertConfigInfo() throws SQLException { Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tag1,tag2"); configAdvanceInfo.put("desc", "desc11"); configAdvanceInfo.put("use", "use2233"); configAdvanceInfo.put("effect", "effect222"); configAdvanceInfo.put("type", "type3"); configAdvanceInfo.put("schema", "schema"); String dataId = "dataId"; String group = "group"; String tenant = "tenant"; String content = "content132456"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, null, content); Connection mockConnection = Mockito.mock(Connection.class); PreparedStatement preparedStatement = Mockito.mock(PreparedStatement.class); ConfigInfoMapper configInfoMapper = externalConfigInfoPersistService.mapperManager.findMapper( dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); Mockito.when(mockConnection.prepareStatement(anyString(), any(String[].class))).thenReturn(preparedStatement); String srcIp = "srcIp"; String srcUser = "srcUser"; externalConfigInfoPersistService.createPsForInsertConfigInfo(srcIp, srcUser, configInfo, configAdvanceInfo, mockConnection, configInfoMapper); Mockito.verify(preparedStatement, times(14)).setString(anyInt(), anyString()); } @Test void testRemoveConfigInfo() { String dataId = "dataId4567"; String group = "group3456789"; String tenant = "tenant4567890"; //mock exist all config info ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(group); configAllInfo.setTenant(tenant); configAllInfo.setAppName("old_app"); configAllInfo.setMd5("old_md5"); configAllInfo.setId(12345678765L); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(configAllInfo); String srcIp = "srcIp1234"; String srcUser = "srcUser"; externalConfigInfoPersistService.removeConfigInfo(dataId, group, tenant, srcIp, srcUser); //expect delete to be invoked Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(dataId), eq(group), eq(tenant)); //expect delete tags to be invoked Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(configAllInfo.getId())); //expect insert delete history Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfo.getId()), eq(configAllInfo), eq(srcIp), eq(srcUser), any(), eq("D"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo))); } @Test void testRemoveConfigInfoByIds() { //mock exist config info final List configAllInfos = new ArrayList<>(); final ConfigAllInfo configAllInfo1 = new ConfigAllInfo(); final ConfigAllInfo configAllInfo2 = new ConfigAllInfo(); configAllInfo1.setDataId("dataId1"); configAllInfo1.setGroup("group1"); configAllInfo1.setTenant("tenant1"); configAllInfo1.setAppName("app1"); configAllInfo2.setDataId("dataId2"); configAllInfo2.setGroup("group2"); configAllInfo2.setTenant("tenant2"); configAllInfo2.setAppName("app2"); configAllInfos.add(configAllInfo1); configAllInfos.add(configAllInfo2); List deleteIds = Arrays.asList(12344L, 3456789L); configAllInfos.get(0).setId(12344L); configAllInfos.get(1).setId(3456789L); Mockito.when(jdbcTemplate.query(anyString(), eq(deleteIds.toArray()), eq(CONFIG_ALL_INFO_ROW_MAPPER))) .thenReturn(configAllInfos); String srcIp = "srcIp1234"; String srcUser = "srcUser"; externalConfigInfoPersistService.removeConfigInfoByIds(deleteIds, srcIp, srcUser); //expect delete to be invoked Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(deleteIds.get(0)), eq(deleteIds.get(1))); //expect delete tags to be invoked Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(deleteIds.get(0))); Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(deleteIds.get(1))); //expect insert delete history Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfos.get(0).getId()), eq(configAllInfos.get(0)), eq(srcIp), eq(srcUser), any(), eq("D"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfos.get(0)))); Mockito.verify(historyConfigInfoPersistService, times(1)) .insertConfigHistoryAtomic(eq(configAllInfos.get(1).getId()), eq(configAllInfos.get(1)), eq(srcIp), eq(srcUser), any(), eq("D"), eq("formal"), eq(null), eq(ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfos.get(0)))); } @Test void testBatchInsertOrUpdateOverwrite() throws NacosException { List configInfoList = new ArrayList<>(); //insert direct configInfoList.add(createMockConfigAllInfo(0)); //exist config and overwrite configInfoList.add(createMockConfigAllInfo(1)); //insert direct configInfoList.add(createMockConfigAllInfo(2)); String srcUser = "srcUser1324"; String srcIp = "srcIp1243"; Map configAdvanceInfo = new HashMap<>(); //mock transactionTemplate and replace TransactionTemplate transactionTemplateCurrent = Mockito.mock(TransactionTemplate.class); ReflectionTestUtils.setField(externalConfigInfoPersistService, "tjt", transactionTemplateCurrent); //mock add config 1 success,config 2 fail and update success,config 3 success Mockito.when(transactionTemplateCurrent.execute(any())) .thenReturn(new ConfigOperateResult(true), new ConfigOperateResult(false), new ConfigOperateResult(true), new ConfigOperateResult(true)); Map stringObjectMap = externalConfigInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, configAdvanceInfo, SameConfigPolicy.OVERWRITE); assertEquals(3, stringObjectMap.get("succCount")); assertEquals(0, stringObjectMap.get("skipCount")); } @Test void testBatchInsertOrUpdateSkip() throws NacosException { List configInfoList = new ArrayList<>(); //insert direct configInfoList.add(createMockConfigAllInfo(0)); //exist config and overwrite configInfoList.add(createMockConfigAllInfo(1)); //insert direct configInfoList.add(createMockConfigAllInfo(2)); String srcUser = "srcUser1324"; String srcIp = "srcIp1243"; Map configAdvanceInfo = new HashMap<>(); //mock transactionTemplate and replace TransactionTemplate transactionTemplateCurrent = Mockito.mock(TransactionTemplate.class); ReflectionTestUtils.setField(externalConfigInfoPersistService, "tjt", transactionTemplateCurrent); //mock add config 1 success,config 2 fail and skip,config 3 success Mockito.when(transactionTemplateCurrent.execute(any())) .thenReturn(new ConfigOperateResult(true), new ConfigOperateResult(false), new ConfigOperateResult(true)); Map stringObjectMap = externalConfigInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, configAdvanceInfo, SameConfigPolicy.SKIP); assertEquals(2, stringObjectMap.get("succCount")); assertEquals(1, stringObjectMap.get("skipCount")); assertEquals(configInfoList.get(1).getDataId(), ((List>) stringObjectMap.get("skipData")).get(0).get("dataId")); } @Test void testBatchInsertOrUpdateAbort() throws NacosException { List configInfoList = new ArrayList<>(); //insert direct configInfoList.add(createMockConfigAllInfo(0)); //exist config and overwrite configInfoList.add(createMockConfigAllInfo(1)); //insert direct configInfoList.add(createMockConfigAllInfo(2)); String srcUser = "srcUser1324"; String srcIp = "srcIp1243"; Map configAdvanceInfo = new HashMap<>(); //mock transactionTemplate and replace TransactionTemplate transactionTemplateCurrent = Mockito.mock(TransactionTemplate.class); ReflectionTestUtils.setField(externalConfigInfoPersistService, "tjt", transactionTemplateCurrent); //mock add config 1 success,config 2 fail and abort,config 3 not operated Mockito.when(transactionTemplateCurrent.execute(any())) .thenReturn(new ConfigOperateResult(true), new ConfigOperateResult(false)); Map stringObjectMap = externalConfigInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, configAdvanceInfo, SameConfigPolicy.ABORT); assertEquals(1, stringObjectMap.get("succCount")); assertEquals(1, stringObjectMap.get("skipCount")); // config 2 failed assertEquals(configInfoList.get(1).getDataId(), ((List>) stringObjectMap.get("failData")).get(0).get("dataId")); //skip config 3 assertEquals(configInfoList.get(2).getDataId(), ((List>) stringObjectMap.get("skipData")).get(0).get("dataId")); } private ConfigAllInfo createMockConfigAllInfo(long mockId) { ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setCreateIp("localhost"); configAllInfo.setCreateUser("test"); configAllInfo.setContent("23456789000content"); return configAllInfo; } private ConfigInfoWrapper createMockConfigInfoWrapper(long mockId) { ConfigInfoWrapper configAllInfo = new ConfigInfoWrapper(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setContent("23456789000content"); return configAllInfo; } private ConfigInfoStateWrapper createMockConfigInfoStateWrapper(long mockId) { ConfigInfoStateWrapper configAllInfo = new ConfigInfoStateWrapper(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setLastModified(System.currentTimeMillis()); return configAllInfo; } private ConfigInfo createMockConfigInfo(long mockId) { ConfigInfo configInfo = new ConfigInfo(); configInfo.setDataId("test" + mockId + ".yaml"); configInfo.setGroup("test"); configInfo.setContent("23456789000content"); return configInfo; } @Test void testFindConfigMaxId() { Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(Long.class))).thenReturn(123456L); long configMaxId = externalConfigInfoPersistService.findConfigMaxId(); assertEquals(123456L, configMaxId); } @Test void testFindConfigMaxId0() { Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(Long.class))).thenThrow(new NullPointerException()); long configMaxId = externalConfigInfoPersistService.findConfigMaxId(); assertEquals(0, configMaxId); } @Test void testFindConfigInfoById() { long id = 1234567890876L; ConfigInfo configInfo = new ConfigInfo(); configInfo.setId(id); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {id}), eq(CONFIG_INFO_ROW_MAPPER))) .thenReturn(configInfo); ConfigInfo configReturn = externalConfigInfoPersistService.findConfigInfo(id); assertEquals(id, configReturn.getId()); } @Test void testFindConfigInfoByIdNull() { long id = 1234567890876L; ConfigInfo configInfo = new ConfigInfo(); configInfo.setId(id); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {id}), eq(CONFIG_INFO_ROW_MAPPER))) .thenThrow(new EmptyResultDataAccessException(1)); ConfigInfo configReturn = externalConfigInfoPersistService.findConfigInfo(id); assertNull(configReturn); } @Test void testFindConfigInfoByIdGetConFail() { long id = 1234567890876L; ConfigInfo configInfo = new ConfigInfo(); configInfo.setId(id); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {id}), eq(CONFIG_INFO_ROW_MAPPER))) .thenThrow(new CannotGetJdbcConnectionException("mocked exp")); try { ConfigInfo configReturn = externalConfigInfoPersistService.findConfigInfo(id); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); } } @Test void testFindConfigInfoByDataId() { String dataId = "dataId4567"; String group = "group3456789"; String tenant = "tenant4567890"; ConfigInfoWrapper configInfoWrapper = new ConfigInfoWrapper(); configInfoWrapper.setDataId(dataId); configInfoWrapper.setGroup(group); configInfoWrapper.setTenant(tenant); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenReturn(configInfoWrapper); ConfigInfo configReturn = externalConfigInfoPersistService.findConfigInfo(dataId, group, tenant); assertEquals(dataId, configReturn.getDataId()); } @Test void testFindConfigInfoByDataIdNull() { String dataId = "dataId4567"; String group = "group3456789"; String tenant = "tenant4567890"; Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)); ConfigInfoWrapper configReturn = externalConfigInfoPersistService.findConfigInfo(dataId, group, tenant); assertNull(configReturn); } @Test void testFindConfigInfoByDataIdGetConFail() { String dataId = "dataId4567222"; String group = "group3456789"; String tenant = "tenant4567890"; Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("mocked exp")); try { externalConfigInfoPersistService.findConfigInfo(dataId, group, tenant); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); } } @Test void testFindConfigInfo4Page() { String dataId = "dataId4567222"; String group = "group3456789"; String tenant = "tenant4567890"; //mock total count when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {tenant, dataId, group}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant, dataId, group}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Map configAdvanceInfo = new HashMap<>(); Page configInfo4Page = externalConfigInfoPersistService.findConfigInfo4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testFindConfigInfo4PageWithTags() { String dataId = "dataId4567222"; String group = "group3456789"; String tenant = "tenant4567890"; Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "tags1,tags3"); //mock total count when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {tenant, dataId, group, "tags1", "tags3"}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant, dataId, group, "tags1", "tags3"}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Page configInfo4Page = externalConfigInfoPersistService.findConfigInfo4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testConfigInfoCount() { //mock total count when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(new Integer(9)); int count = externalConfigInfoPersistService.configInfoCount(); assertEquals(9, count); when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(null); try { externalConfigInfoPersistService.configInfoCount(); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } } @Test void testConfigInfoCountByTenant() { String tenant = "tenant124"; //mock total count when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {tenant}), eq(Integer.class))).thenReturn( new Integer(90)); int count = externalConfigInfoPersistService.configInfoCount(tenant); assertEquals(90, count); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {tenant}), eq(Integer.class))).thenReturn(null); try { externalConfigInfoPersistService.configInfoCount(tenant); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof IllegalArgumentException); } } @Test void testFindConfigInfoLike4Page() { String dataId = "dataId4567222*"; String group = "group3456789*"; String tenant = "tenant4567890"; String appName = "appName1234"; String content = "content123"; Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("appName", appName); configAdvanceInfo.put("content", content); //mock total count when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Page configInfo4Page = externalConfigInfoPersistService.findConfigInfoLike4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testFindConfigInfoLike4PageWithTags() { String appName = "appName1234"; String content = "content123"; Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("appName", appName); configAdvanceInfo.put("content", content); configAdvanceInfo.put("config_tags", "tags,tag2"); String dataId = "dataId4567222*"; String group = "group3456789*"; String tenant = "tenant4567890"; //mock total count when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content, "tags", "tag2"}), eq(Integer.class))).thenReturn(new Integer(9)); //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant, dataId.replaceAll("\\*", "%"), group.replaceAll("\\*", "%"), appName, content, "tags", "tag2"}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn(result); Page configInfo4Page = externalConfigInfoPersistService.findConfigInfoLike4Page(1, 3, dataId, group, tenant, configAdvanceInfo); assertEquals(result.size(), configInfo4Page.getPageItems().size()); assertEquals(9, configInfo4Page.getTotalCount()); } @Test void testFindChangeConfig() { //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfoStateWrapper(0)); result.add(createMockConfigInfoStateWrapper(1)); result.add(createMockConfigInfoStateWrapper(2)); Timestamp startTime = new Timestamp(System.currentTimeMillis() - 1000L); long lastMaxId = 10000L; int pageSize = 30; when(jdbcTemplate.query(anyString(), eq(new Object[] {startTime, lastMaxId, pageSize}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(result); List configInfo4List = externalConfigInfoPersistService.findChangeConfig(startTime, lastMaxId, pageSize); assertEquals(result.size(), configInfo4List.size()); } @Test void testFindChangeConfigError() { Timestamp startTime = new Timestamp(System.currentTimeMillis() - 1000L); long lastMaxId = 10000L; int pageSize = 30; //mock page list when(jdbcTemplate.query(anyString(), eq(new Object[] {startTime, lastMaxId, pageSize}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new CannotAcquireLockException("mock ex")); try { List configInfo4List = externalConfigInfoPersistService.findChangeConfig(startTime, lastMaxId, pageSize); assertTrue(false); } catch (Exception e) { assertTrue(e instanceof CannotAcquireLockException); } } @Test void testSelectTagByConfig() { String dataId = "dataId4567222"; String group = "group3456789"; String tenant = "tenant4567890"; //mock page list List tagStrings = Arrays.asList("", "", ""); when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenReturn(tagStrings); List configTags = externalConfigInfoPersistService.selectTagByConfig(dataId, group, tenant); assertEquals(tagStrings, configTags); //mock EmptyResultDataAccessException when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenThrow(new EmptyResultDataAccessException(3)); List nullResult = externalConfigInfoPersistService.selectTagByConfig(dataId, group, tenant); assertTrue(nullResult == null); //mock IncorrectResultSizeDataAccessException when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenThrow(new IncorrectResultSizeDataAccessException(3)); List nullResult2 = externalConfigInfoPersistService.selectTagByConfig(dataId, group, tenant); assertTrue(nullResult2 == null); //mock IncorrectResultSizeDataAccessException when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenThrow(new CannotGetJdbcConnectionException("mock exp")); try { externalConfigInfoPersistService.selectTagByConfig(dataId, group, tenant); assertFalse(true); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); } } @Test void testFindConfigInfosByIds() { //mock page list List result = new ArrayList<>(); result.add(createMockConfigInfo(0)); result.add(createMockConfigInfo(1)); result.add(createMockConfigInfo(2)); when(jdbcTemplate.query(anyString(), eq(new Object[] {123L, 1232345L}), eq(CONFIG_INFO_ROW_MAPPER))).thenReturn( result); String ids = "123,1232345"; List configInfosByIds = externalConfigInfoPersistService.findConfigInfosByIds(ids); assertEquals(result.size(), configInfosByIds.size()); assertEquals(result.get(2).getDataId(), configInfosByIds.get(2).getDataId()); //mock EmptyResultDataAccessException when(jdbcTemplate.query(anyString(), eq(new Object[] {123L, 1232345L}), eq(CONFIG_INFO_ROW_MAPPER))).thenThrow( new EmptyResultDataAccessException(3)); List nullResult2 = externalConfigInfoPersistService.findConfigInfosByIds(ids); assertTrue(nullResult2 == null); //blank ids. List nullResultBlankIds = externalConfigInfoPersistService.findConfigInfosByIds(""); assertTrue(nullResultBlankIds == null); //mock CannotGetJdbcConnectionException when(jdbcTemplate.query(anyString(), eq(new Object[] {123L, 1232345L}), eq(CONFIG_INFO_ROW_MAPPER))).thenThrow( new CannotGetJdbcConnectionException("mock exp")); try { externalConfigInfoPersistService.findConfigInfosByIds(ids); assertFalse(true); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); } } @Test void testFindConfigAdvanceInfo() { String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; //mock select tags List mockTags = Arrays.asList("tag1", "tag2", "tag3"); when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenReturn(mockTags); String schema = "schema12345654"; //mock select config advance ConfigAdvanceInfo mockedAdvance = new ConfigAdvanceInfo(); mockedAdvance.setSchema(schema); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ADVANCE_INFO_ROW_MAPPER))).thenReturn(mockedAdvance); //execute return mock obj ConfigAdvanceInfo configAdvanceInfo = externalConfigInfoPersistService.findConfigAdvanceInfo(dataId, group, tenant); //expect check schema & tags. assertEquals(mockedAdvance.getSchema(), configAdvanceInfo.getSchema()); assertEquals(String.join(",", mockTags), configAdvanceInfo.getConfigTags()); //mock EmptyResultDataAccessException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ADVANCE_INFO_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)); //expect return null. assertNull(externalConfigInfoPersistService.findConfigAdvanceInfo(dataId, group, tenant)); //mock CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ADVANCE_INFO_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("mock exp")); //expect throw exception. try { externalConfigInfoPersistService.findConfigAdvanceInfo(dataId, group, tenant); assertFalse(true); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); assertTrue(e.getMessage().endsWith("mock exp")); } } @Test void testFindConfigAllInfo() { String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; //mock select tags List mockTags = Arrays.asList("tag1", "tag2", "tag3"); when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))).thenReturn(mockTags); String schema = "schema12345654"; //mock select config advance ConfigAllInfo mockedConfig = new ConfigAllInfo(); mockedConfig.setSchema(schema); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(mockedConfig); //execute return mock obj ConfigAllInfo configAllInfo = externalConfigInfoPersistService.findConfigAllInfo(dataId, group, tenant); //expect check schema & tags. assertEquals(mockedConfig.getSchema(), configAllInfo.getSchema()); assertEquals(String.join(",", mockTags), configAllInfo.getConfigTags()); //mock EmptyResultDataAccessException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)); //expect return null. assertNull(externalConfigInfoPersistService.findConfigAllInfo(dataId, group, tenant)); //mock CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("mock exp")); //expect throw exception. try { externalConfigInfoPersistService.findConfigAllInfo(dataId, group, tenant); assertFalse(true); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); assertTrue(e.getMessage().endsWith("mock exp")); } } @Test void testFindConfigInfoState() { String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; //mock select config state ConfigInfoStateWrapper mockedConfig = new ConfigInfoStateWrapper(); mockedConfig.setLastModified(2345678L); mockedConfig.setId(23456789098765L); when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(mockedConfig); //execute return mock obj ConfigInfoStateWrapper configInfoStateWrapper = externalConfigInfoPersistService.findConfigInfoState(dataId, group, tenant); //expect check schema & tags. assertEquals(mockedConfig.getId(), configInfoStateWrapper.getId()); assertEquals(mockedConfig.getLastModified(), configInfoStateWrapper.getLastModified()); //mock EmptyResultDataAccessException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)); //expect return null. assertNull(externalConfigInfoPersistService.findConfigInfoState(dataId, group, tenant)); //mock CannotGetJdbcConnectionException when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("mock exp")); //expect throw exception. try { externalConfigInfoPersistService.findConfigInfoState(dataId, group, tenant); assertFalse(true); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); assertTrue(e.getMessage().endsWith("mock exp")); } } @Test void testFindAllConfigInfo4Export() { //mock select config state List mockConfigs = new ArrayList<>(); mockConfigs.add(createMockConfigAllInfo(0)); mockConfigs.add(createMockConfigAllInfo(1)); mockConfigs.add(createMockConfigAllInfo(2)); String dataId = "dataId1324"; String group = "group23546"; String tenant = "tenant13245"; String appName = "appName1243"; List ids = Arrays.asList(132L, 1343L, 245L); when(jdbcTemplate.query(anyString(), eq(new Object[] {132L, 1343L, 245L}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(mockConfigs); //execute return mock obj List configAllInfosIds = externalConfigInfoPersistService.findAllConfigInfo4Export(dataId, group, tenant, appName, ids); //expect check assertEquals(mockConfigs, configAllInfosIds); when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant, dataId, group, appName}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenReturn(mockConfigs); //execute return mock obj List configAllInfosWithDataId = externalConfigInfoPersistService.findAllConfigInfo4Export(dataId, group, tenant, appName, null); //expect check assertEquals(mockConfigs, configAllInfosWithDataId); //mock CannotGetJdbcConnectionException when(jdbcTemplate.query(anyString(), eq(new Object[] {132L, 1343L, 245L}), eq(CONFIG_ALL_INFO_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("mock exp11")); //expect throw exception. try { externalConfigInfoPersistService.findAllConfigInfo4Export(dataId, group, tenant, appName, ids); assertFalse(true); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); assertTrue(e.getMessage().endsWith("mock exp11")); } } @Test void testQueryConfigInfoByNamespace() { //mock select config state List mockConfigs = new ArrayList<>(); mockConfigs.add(createMockConfigInfoWrapper(0)); mockConfigs.add(createMockConfigInfoWrapper(1)); mockConfigs.add(createMockConfigInfoWrapper(2)); String tenant = "tenant13245"; when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenReturn( mockConfigs); //execute return mock obj List configInfoWrappers = externalConfigInfoPersistService.queryConfigInfoByNamespace( tenant); //expect check assertEquals(mockConfigs, configInfoWrappers); //mock CannotGetJdbcConnectionException when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenThrow( new EmptyResultDataAccessException(2)); //execute return mock obj List configInfoWrapperNull = externalConfigInfoPersistService.queryConfigInfoByNamespace( tenant); //expect check assertEquals(Collections.EMPTY_LIST, configInfoWrapperNull); //mock CannotGetJdbcConnectionException when(jdbcTemplate.query(anyString(), eq(new Object[] {tenant}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenThrow( new CannotGetJdbcConnectionException("mock exp1111")); //expect throw exception. try { externalConfigInfoPersistService.queryConfigInfoByNamespace(tenant); assertFalse(true); } catch (Exception e) { assertTrue(e instanceof CannotGetJdbcConnectionException); assertTrue(e.getMessage().endsWith("mock exp1111")); } } @Test void testGetTenantIdList() { int page = 10; int pageSize = 100; //mock select config state List tenantStrings = Arrays.asList("tenant1", "tenant2", "tenant3"); when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {}), eq(String.class))).thenReturn(tenantStrings); //execute return mock obj List returnTenants = externalConfigInfoPersistService.getTenantIdList(page, pageSize); //expect check assertEquals(tenantStrings, returnTenants); } @Test void testGetGroupIdList() { int page = 10; int pageSize = 100; //mock select config state List groupStrings = Arrays.asList("group1", "group2", "group3"); when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {}), eq(String.class))).thenReturn(groupStrings); //execute return mock obj List returnGroups = externalConfigInfoPersistService.getGroupIdList(page, pageSize); //expect check assertEquals(groupStrings, returnGroups); } @Test void testFindAllConfigInfoFragment() { //mock page list List mockConfigs = new ArrayList<>(); mockConfigs.add(createMockConfigInfoWrapper(0)); mockConfigs.add(createMockConfigInfoWrapper(1)); mockConfigs.add(createMockConfigInfoWrapper(2)); long lastId = 10111L; when(jdbcTemplate.query(anyString(), eq(new Object[] {lastId}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenReturn( mockConfigs); int pageSize = 100; //execute return mock obj Page returnConfigPage = externalConfigInfoPersistService.findAllConfigInfoFragment(lastId, pageSize, true); //expect check assertEquals(mockConfigs, returnConfigPage.getPageItems()); when(jdbcTemplate.query(anyString(), eq(new Object[] {lastId}), eq(CONFIG_INFO_WRAPPER_ROW_MAPPER))).thenThrow( new CannotGetJdbcConnectionException("mock fail")); try { externalConfigInfoPersistService.findAllConfigInfoFragment(lastId, pageSize, true); assertTrue(false); } catch (Exception e) { assertEquals("mock fail", e.getMessage()); } } @Test void testBuildFindConfigInfoStateSql() { MapperManager mapperManager = MapperManager.instance(false); ConfigInfoMapper configInfoMapper = mapperManager.findMapper(dataSourceService.getDataSourceType(), TableConstant.CONFIG_INFO); String select = configInfoMapper.select(Arrays.asList("id", "data_id", "group_id", "tenant_id", "gmt_modified"), Arrays.asList("data_id", "group_id", "tenant_id")); assertEquals( "SELECT id,data_id,group_id,tenant_id,gmt_modified FROM config_info WHERE data_id = ? AND group_id = ? AND tenant_id = ?", select); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoTagPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.common.utils.MD5Utils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper; import com.alibaba.nacos.config.server.model.ConfigOperateResult; import com.alibaba.nacos.config.server.service.sql.ExternalStorageUtils; import com.alibaba.nacos.config.server.utils.TestCaseUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER; 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.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) class ExternalConfigInfoTagPersistServiceImplTest { MockedStatic envUtilMockedStatic; MockedStatic externalStorageUtilsMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; private ExternalConfigInfoTagPersistServiceImpl externalConfigInfoTagPersistService; @Mock private DataSourceService dataSourceService; @Mock private JdbcTemplate jdbcTemplate; private TransactionTemplate transactionTemplate = TestCaseUtils.createMockTransactionTemplate(); @BeforeEach void before() { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); externalStorageUtilsMockedStatic = Mockito.mockStatic(ExternalStorageUtils.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getTransactionTemplate()).thenReturn(transactionTemplate); when(dataSourceService.getJdbcTemplate()).thenReturn(jdbcTemplate); when(dataSourceService.getDataSourceType()).thenReturn("mysql"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))).thenReturn(false); externalConfigInfoTagPersistService = new ExternalConfigInfoTagPersistServiceImpl(); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); externalStorageUtilsMockedStatic.close(); } @Test void testInsertOrUpdateTagOfAdd() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //mock query config state empty and return obj after insert ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)) .thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = externalConfigInfoTagPersistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser); //verify insert to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag), eq(appName), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser), any(Timestamp.class), any(Timestamp.class)); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testInsertOrUpdateTagOfUpdate() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //mock query config state and return obj after update ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(new ConfigInfoStateWrapper()).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = externalConfigInfoTagPersistService.insertOrUpdateTag(configInfo, tag, srcIp, srcUser); //verify update to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(configInfo.getContent()), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser), any(Timestamp.class), eq(appName), eq(dataId), eq(group), eq(tenant), eq(tag)); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testInsertOrUpdateTagCasOfAdd() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); configInfo.setMd5("casMd5"); //mock query config state empty and return obj after insert ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)) .thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; ConfigOperateResult configOperateResult = externalConfigInfoTagPersistService.insertOrUpdateTagCas(configInfo, tag, srcIp, srcUser); //verify insert to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag), eq(appName), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), any(Timestamp.class)); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testInsertOrUpdateTagCasOfUpdate() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); configInfo.setMd5("casMd5"); //mock query config state and return obj after update ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(new ConfigInfoStateWrapper()).thenReturn(configInfoStateWrapper); String srcIp = "ip345678"; String srcUser = "user1234567"; //mock cas update return 1 Mockito.when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), eq(appName), eq(dataId), eq(group), eq(tenant), eq(tag), eq(configInfo.getMd5()))).thenReturn(1); ConfigOperateResult configOperateResult = externalConfigInfoTagPersistService.insertOrUpdateTagCas(configInfo, tag, srcIp, srcUser); //verify update to be invoked Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), eq(appName), eq(dataId), eq(group), eq(tenant), eq(tag), eq(configInfo.getMd5())); assertEquals(configInfoStateWrapper.getId(), configOperateResult.getId()); assertEquals(configInfoStateWrapper.getLastModified(), configOperateResult.getLastModified()); } @Test void testInsertOrUpdateTagCasOfException() { String dataId = "dataId111222"; String group = "group"; String tenant = "tenant"; String appName = "appname1234"; String content = "c12345"; ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); configInfo.setMd5("casMd5"); //mock query config state CannotGetJdbcConnectionException ConfigInfoStateWrapper configInfoStateWrapper = new ConfigInfoStateWrapper(); configInfoStateWrapper.setLastModified(System.currentTimeMillis()); configInfoStateWrapper.setId(234567890L); String tag = "tag123"; Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("state query throw exception")); String srcIp = "ip345678"; String srcUser = "user1234567"; try { externalConfigInfoTagPersistService.insertOrUpdateTagCas(configInfo, tag, srcIp, srcUser); assertTrue(false); } catch (Exception e) { assertEquals("state query throw exception", e.getMessage()); } //mock get state return null,and execute add throw CannotGetJdbcConnectionException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(null); Mockito.when(jdbcTemplate.update(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag), eq(appName), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), any(Timestamp.class))).thenThrow(new CannotGetJdbcConnectionException("throw exception add config tag")); try { externalConfigInfoTagPersistService.insertOrUpdateTagCas(configInfo, tag, srcIp, srcUser); assertTrue(false); } catch (Exception e) { assertEquals("throw exception add config tag", e.getMessage()); } //mock get state return obj,and execute update throw CannotGetJdbcConnectionException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_STATE_WRAPPER_ROW_MAPPER))).thenReturn(configInfoStateWrapper); Mockito.when(jdbcTemplate.update(anyString(), eq(configInfo.getContent()), eq(MD5Utils.md5Hex(configInfo.getContent(), Constants.PERSIST_ENCODE)), eq(srcIp), eq(srcUser), any(Timestamp.class), eq(appName), eq(dataId), eq(group), eq(tenant), eq(tag), eq(configInfo.getMd5()))) .thenThrow(new CannotGetJdbcConnectionException("throw exception update config tag")); try { externalConfigInfoTagPersistService.insertOrUpdateTagCas(configInfo, tag, srcIp, srcUser); assertTrue(false); } catch (Exception e) { assertEquals("throw exception update config tag", e.getMessage()); } } @Test void testRemoveConfigInfoTag() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; String tag = "tag123345"; String srcIp = "ip345678"; String srcUser = "user1234567"; //Mockito.when(jdbcTemplate.update(anyString(),eq(dataId),eq(group),eq(tenant),eq(tag))).thenReturn() //verify delete sql invoked. externalConfigInfoTagPersistService.removeConfigInfoTag(dataId, group, tenant, tag, srcIp, srcUser); Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag)); //mock delete throw CannotGetJdbcConnectionException Mockito.when(jdbcTemplate.update(anyString(), eq(dataId), eq(group), eq(tenant), eq(tag))) .thenThrow(new CannotGetJdbcConnectionException("delete fail")); try { externalConfigInfoTagPersistService.removeConfigInfoTag(dataId, group, tenant, tag, srcIp, srcUser); assertTrue(false); } catch (Exception e) { assertEquals("delete fail", e.getMessage()); } } @Test void testFindConfigInfo4Tag() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; String tag = "tag123345"; //mock query tag return obj ConfigInfoTagWrapper configInfoTagWrapperMocked = new ConfigInfoTagWrapper(); configInfoTagWrapperMocked.setLastModified(System.currentTimeMillis()); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER))).thenReturn(configInfoTagWrapperMocked); ConfigInfoTagWrapper configInfo4TagReturn = externalConfigInfoTagPersistService.findConfigInfo4Tag(dataId, group, tenant, tag); assertEquals(configInfoTagWrapperMocked, configInfo4TagReturn); //mock query tag throw EmptyResultDataAccessException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER))).thenThrow(new EmptyResultDataAccessException(1)); ConfigInfoTagWrapper configInfo4Tag = externalConfigInfoTagPersistService.findConfigInfo4Tag(dataId, group, tenant, tag); assertNull(configInfo4Tag); //mock query tag throw CannotGetJdbcConnectionException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant, tag}), eq(CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("con error")); try { externalConfigInfoTagPersistService.findConfigInfo4Tag(dataId, group, tenant, tag); assertTrue(false); } catch (Exception e) { assertEquals("con error", e.getMessage()); } } @Test void testConfigInfoTagCount() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); //mock count Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(308); //execute & verify int count = externalConfigInfoTagPersistService.configInfoTagCount(); assertEquals(308, count); //mock count is null Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(null); //execute & verify try { externalConfigInfoTagPersistService.configInfoTagCount(); assertTrue(false); } catch (Exception e) { assertEquals("configInfoTagCount error", e.getMessage()); } } @Test void testFindAllConfigInfoTagForDumpAll() { //mock count Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))).thenReturn(308); List mockTagList = new ArrayList<>(); mockTagList.add(new ConfigInfoTagWrapper()); mockTagList.add(new ConfigInfoTagWrapper()); mockTagList.add(new ConfigInfoTagWrapper()); mockTagList.get(0).setLastModified(System.currentTimeMillis()); mockTagList.get(1).setLastModified(System.currentTimeMillis()); mockTagList.get(2).setLastModified(System.currentTimeMillis()); //mock query list Mockito.when(jdbcTemplate.query(anyString(), eq(new Object[] {}), eq(CONFIG_INFO_TAG_WRAPPER_ROW_MAPPER))).thenReturn(mockTagList); int pageNo = 3; int pageSize = 100; //execute & verify Page returnTagPage = externalConfigInfoTagPersistService.findAllConfigInfoTagForDumpAll(pageNo, pageSize); assertEquals(308, returnTagPage.getTotalCount()); assertEquals(mockTagList, returnTagPage.getPageItems()); //mock count CannotGetJdbcConnectionException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(Integer.class))) .thenThrow(new CannotGetJdbcConnectionException("conn error111")); //execute & verify try { externalConfigInfoTagPersistService.findAllConfigInfoTagForDumpAll(pageNo, pageSize); assertTrue(false); } catch (Exception e) { assertEquals("conn error111", e.getMessage()); } } @Test void testFindConfigInfoTags() { String dataId = "dataId1112222"; String group = "group22"; String tenant = "tenant2"; List mockedTags = Arrays.asList("tags1", "tags11", "tags111"); Mockito.when(jdbcTemplate.queryForList(anyString(), eq(new Object[] {dataId, group, tenant}), eq(String.class))) .thenReturn(mockedTags); List configInfoTags = externalConfigInfoTagPersistService.findConfigInfoTags(dataId, group, tenant); assertEquals(mockedTags, configInfoTags); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/service/repository/extrnal/ExternalHistoryConfigInfoPersistServiceImplTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.service.repository.extrnal; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper; import com.alibaba.nacos.config.server.service.sql.ExternalStorageUtils; import com.alibaba.nacos.config.server.utils.TestCaseUtils; import com.alibaba.nacos.persistence.datasource.DataSourceService; import com.alibaba.nacos.persistence.datasource.DynamicDataSource; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.sys.env.EnvUtil; 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.Mockito; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.support.TransactionTemplate; import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_DETAIL_ROW_MAPPER; import static com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector.HISTORY_LIST_ROW_MAPPER; 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.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; @ExtendWith(SpringExtension.class) class ExternalHistoryConfigInfoPersistServiceImplTest { MockedStatic envUtilMockedStatic; MockedStatic externalStorageUtilsMockedStatic; MockedStatic dynamicDataSourceMockedStatic; @Mock DynamicDataSource dynamicDataSource; private ExternalHistoryConfigInfoPersistServiceImpl externalHistoryConfigInfoPersistService; @Mock private DataSourceService dataSourceService; @Mock private JdbcTemplate jdbcTemplate; private TransactionTemplate transactionTemplate = TestCaseUtils.createMockTransactionTemplate(); @BeforeEach void before() { dynamicDataSourceMockedStatic = Mockito.mockStatic(DynamicDataSource.class); envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); externalStorageUtilsMockedStatic = Mockito.mockStatic(ExternalStorageUtils.class); when(DynamicDataSource.getInstance()).thenReturn(dynamicDataSource); when(dynamicDataSource.getDataSource()).thenReturn(dataSourceService); when(dataSourceService.getTransactionTemplate()).thenReturn(transactionTemplate); when(dataSourceService.getJdbcTemplate()).thenReturn(jdbcTemplate); when(dataSourceService.getDataSourceType()).thenReturn("mysql"); envUtilMockedStatic.when(() -> EnvUtil.getProperty(anyString(), eq(Boolean.class), eq(false))) .thenReturn(false); externalHistoryConfigInfoPersistService = new ExternalHistoryConfigInfoPersistServiceImpl(); } @AfterEach void after() { dynamicDataSourceMockedStatic.close(); envUtilMockedStatic.close(); externalStorageUtilsMockedStatic.close(); } @Test void testInsertConfigHistoryAtomic() { String dataId = "dateId243"; String group = "group243"; String tenant = "tenant243"; String content = "content243"; String appName = "appName243"; long id = 123456787765432L; String srcUser = "user12345"; String srcIp = "ip1234"; String ops = "D"; String extraInfo = "type\":\"properties"; Timestamp timestamp = new Timestamp(System.currentTimeMillis()); ConfigInfo configInfo = new ConfigInfo(dataId, group, tenant, appName, content); configInfo.setEncryptedDataKey("key23456"); //expect insert success,verify insert invoked externalHistoryConfigInfoPersistService.insertConfigHistoryAtomic(id, configInfo, srcIp, srcUser, timestamp, ops, "formal", null, extraInfo); Mockito.verify(jdbcTemplate, times(1)) .update(anyString(), eq(id), eq(dataId), eq(group), eq(tenant), eq(appName), eq(content), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser), eq(timestamp), eq(ops), eq("formal"), eq(""), eq(extraInfo), eq(configInfo.getEncryptedDataKey())); Mockito.when( jdbcTemplate.update(anyString(), eq(id), eq(dataId), eq(group), eq(tenant), eq(appName), eq(content), eq(configInfo.getMd5()), eq(srcIp), eq(srcUser), eq(timestamp), eq(ops), eq("formal"), eq(""), eq(extraInfo), eq(configInfo.getEncryptedDataKey()))) .thenThrow(new CannotGetJdbcConnectionException("mock ex...")); try { externalHistoryConfigInfoPersistService.insertConfigHistoryAtomic(id, configInfo, srcIp, srcUser, timestamp, ops, "formal", null, extraInfo); assertTrue(false); } catch (Exception e) { assertEquals("mock ex...", e.getMessage()); } } @Test void testRemoveConfigHistory() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); int pageSize = 1233; externalHistoryConfigInfoPersistService.removeConfigHistory(timestamp, pageSize); //verify delete by time and size invoked. Mockito.verify(jdbcTemplate, times(1)).update(anyString(), eq(timestamp), eq(pageSize)); } @Test void testFindDeletedConfig() { //mock query list return ConfigHistoryInfo mockObj1 = new ConfigHistoryInfo(); mockObj1.setDataId("data_id1"); mockObj1.setGroup("group_id1"); mockObj1.setTenant("tenant_id1"); mockObj1.setMd5("md51"); mockObj1.setLastModifiedTime(new Timestamp(System.currentTimeMillis())); List list = new ArrayList<>(); list.add(mockObj1); ConfigHistoryInfo mockObj2 = new ConfigHistoryInfo(); mockObj2.setDataId("data_id2"); mockObj2.setGroup("group_id2"); mockObj2.setTenant("tenant_id2"); mockObj2.setMd5("md52"); mockObj2.setLastModifiedTime(new Timestamp(System.currentTimeMillis())); list.add(mockObj2); int pageSize = 1233; long startId = 23456; Timestamp timestamp = new Timestamp(System.currentTimeMillis()); String publishType = "formal"; Mockito.when(jdbcTemplate.query(anyString(), eq(new Object[] {publishType, timestamp, startId, pageSize}), eq(HISTORY_DETAIL_ROW_MAPPER))).thenReturn(list); //execute List deletedConfig = externalHistoryConfigInfoPersistService.findDeletedConfig( timestamp, startId, pageSize, "formal"); //expect verify assertEquals("data_id1", deletedConfig.get(0).getDataId()); assertEquals("group_id1", deletedConfig.get(0).getGroup()); assertEquals("tenant_id1", deletedConfig.get(0).getTenant()); assertEquals(mockObj1.getLastModifiedTime(), new Timestamp(deletedConfig.get(0).getLastModified())); assertEquals("data_id2", deletedConfig.get(1).getDataId()); assertEquals("group_id2", deletedConfig.get(1).getGroup()); assertEquals("tenant_id2", deletedConfig.get(1).getTenant()); assertEquals(mockObj2.getLastModifiedTime(), new Timestamp(deletedConfig.get(1).getLastModified())); //mock exception Mockito.when(jdbcTemplate.query(anyString(), eq(new Object[] {publishType, timestamp, startId, pageSize}), eq(HISTORY_DETAIL_ROW_MAPPER))).thenThrow(new CannotGetJdbcConnectionException("conn error")); try { externalHistoryConfigInfoPersistService.findDeletedConfig(timestamp, startId, pageSize, "formal"); assertTrue(false); } catch (Exception e) { assertEquals("conn error", e.getMessage()); } } @Test void testFindConfigHistory() { String dataId = "dataId34567"; String group = "group34567"; String tenant = "tenant34567"; //mock count Mockito.when( jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(Integer.class))) .thenReturn(300); //mock list List mockList = new ArrayList<>(); mockList.add(createMockConfigHistoryInfo(0)); mockList.add(createMockConfigHistoryInfo(1)); mockList.add(createMockConfigHistoryInfo(2)); Mockito.when( jdbcTemplate.query(anyString(), eq(new Object[] {dataId, group, tenant}), eq(HISTORY_LIST_ROW_MAPPER))) .thenReturn(mockList); int pageSize = 100; int pageNo = 2; //execute & verify Page historyReturn = externalHistoryConfigInfoPersistService.findConfigHistory(dataId, group, tenant, pageNo, pageSize); assertEquals(mockList, historyReturn.getPageItems()); assertEquals(300, historyReturn.getTotalCount()); //mock exception Mockito.when( jdbcTemplate.queryForObject(anyString(), eq(new Object[] {dataId, group, tenant}), eq(Integer.class))) .thenThrow(new CannotGetJdbcConnectionException("conn error111")); try { externalHistoryConfigInfoPersistService.findConfigHistory(dataId, group, tenant, pageNo, pageSize); assertTrue(false); } catch (Exception e) { assertEquals("conn error111", e.getMessage()); } } @Test void testDetailConfigHistory() { long nid = 256789; //mock query ConfigHistoryInfo mockConfigHistoryInfo = createMockConfigHistoryInfo(0); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenReturn(mockConfigHistoryInfo); //execute & verify ConfigHistoryInfo historyReturn = externalHistoryConfigInfoPersistService.detailConfigHistory(nid); assertEquals(mockConfigHistoryInfo, historyReturn); //mock exception EmptyResultDataAccessException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenThrow(new EmptyResultDataAccessException(1)); ConfigHistoryInfo historyReturnNull = externalHistoryConfigInfoPersistService.detailConfigHistory(nid); assertNull(historyReturnNull); //mock exception CannotGetJdbcConnectionException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenThrow(new CannotGetJdbcConnectionException("conn error111")); try { externalHistoryConfigInfoPersistService.detailConfigHistory(nid); assertTrue(false); } catch (Exception e) { assertEquals("conn error111", e.getMessage()); } } @Test void testDetailPreviousConfigHistory() { long nid = 256789; //mock query ConfigHistoryInfo mockConfigHistoryInfo = createMockConfigHistoryInfo(0); Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenReturn(mockConfigHistoryInfo); //execute & verify ConfigHistoryInfo historyReturn = externalHistoryConfigInfoPersistService.detailPreviousConfigHistory(nid); assertEquals(mockConfigHistoryInfo, historyReturn); //mock exception EmptyResultDataAccessException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenThrow(new EmptyResultDataAccessException(1)); ConfigHistoryInfo historyReturnNull = externalHistoryConfigInfoPersistService.detailPreviousConfigHistory(nid); assertNull(historyReturnNull); //mock exception CannotGetJdbcConnectionException Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {nid}), eq(HISTORY_DETAIL_ROW_MAPPER))) .thenThrow(new CannotGetJdbcConnectionException("conn error111")); try { externalHistoryConfigInfoPersistService.detailPreviousConfigHistory(nid); assertTrue(false); } catch (Exception e) { assertEquals("conn error111", e.getMessage()); } } @Test void testFindConfigHistoryCountByTime() { Timestamp timestamp = new Timestamp(System.currentTimeMillis()); //mock count Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {timestamp}), eq(Integer.class))) .thenReturn(308); //execute & verify int count = externalHistoryConfigInfoPersistService.findConfigHistoryCountByTime(timestamp); assertEquals(308, count); //mock count is null Mockito.when(jdbcTemplate.queryForObject(anyString(), eq(new Object[] {timestamp}), eq(Integer.class))) .thenReturn(null); //execute & verify try { externalHistoryConfigInfoPersistService.findConfigHistoryCountByTime(timestamp); assertTrue(false); } catch (Exception e) { assertEquals("findConfigHistoryCountByTime error", e.getMessage()); } } private ConfigHistoryInfo createMockConfigHistoryInfo(long mockId) { ConfigHistoryInfo configAllInfo = new ConfigHistoryInfo(); configAllInfo.setDataId("test" + mockId + ".yaml"); configAllInfo.setGroup("test"); configAllInfo.setContent("23456789000content"); configAllInfo.setOpType("D"); configAllInfo.setEncryptedDataKey("key4567"); configAllInfo.setSrcIp("ip567"); configAllInfo.setSrcUser("user1234"); configAllInfo.setMd5("md52345678"); return configAllInfo; } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/AccumulateStatCountTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class AccumulateStatCountTest { @Test void testIncrease() { AccumulateStatCount accumulateStatCount = new AccumulateStatCount(); long result = accumulateStatCount.increase(); assertEquals(1, result); } @Test void testStat() { AccumulateStatCount accumulateStatCount = new AccumulateStatCount(); long stat = accumulateStatCount.stat(); assertEquals(0, stat); accumulateStatCount.increase(); stat = accumulateStatCount.stat(); assertEquals(1, stat); accumulateStatCount.increase(); stat = accumulateStatCount.stat(); assertEquals(1, stat); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/AppNameUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; class AppNameUtilsTest { private static final String PARAM_MARKING_PROJECT = "project.name"; private static final String PARAM_MARKING_JBOSS = "jboss.server.home.dir"; private static final String PARAM_MARKING_JETTY = "jetty.home"; private static final String PARAM_MARKING_TOMCAT = "catalina.base"; private static final String LINUX_ADMIN_HOME = "/home/admin/"; private static final String SERVER_JBOSS = "jboss"; private static final String SERVER_JETTY = "jetty"; private static final String SERVER_TOMCAT = "tomcat"; private static final String SERVER_UNKNOWN = "unknown server"; private static final String DEFAULT_APP_NAME = "unknown"; @Test void testGetAppName() { System.setProperty(PARAM_MARKING_PROJECT, SERVER_UNKNOWN); assertEquals(SERVER_UNKNOWN, AppNameUtils.getAppName()); System.clearProperty(PARAM_MARKING_PROJECT); System.setProperty(PARAM_MARKING_JBOSS, LINUX_ADMIN_HOME + SERVER_JBOSS + File.separator); assertEquals(SERVER_JBOSS, AppNameUtils.getAppName()); System.clearProperty(PARAM_MARKING_JBOSS); System.setProperty(PARAM_MARKING_JETTY, LINUX_ADMIN_HOME + SERVER_JETTY + File.separator); assertEquals(SERVER_JETTY, AppNameUtils.getAppName()); System.clearProperty(PARAM_MARKING_JETTY); System.setProperty(PARAM_MARKING_TOMCAT, LINUX_ADMIN_HOME + SERVER_TOMCAT + File.separator); assertEquals(SERVER_TOMCAT, AppNameUtils.getAppName()); System.clearProperty(PARAM_MARKING_TOMCAT); assertEquals(DEFAULT_APP_NAME, AppNameUtils.getAppName()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/ConfigExecutorTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ConfigExecutorTest { @Test void testScheduleConfigTask() throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(); Runnable runnable = atomicInteger::incrementAndGet; ConfigExecutor.scheduleConfigTask(runnable, 0, 10, TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS.sleep(10); assertTrue(atomicInteger.get() >= 1); } @Test void testScheduleCorrectUsageTask() throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(); Runnable runnable = atomicInteger::incrementAndGet; ConfigExecutor.scheduleCorrectUsageTask(runnable, 0, 10, TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS.sleep(10); assertTrue(atomicInteger.get() >= 1); } @Test void testExecuteAsyncNotify() throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(); Runnable runnable = atomicInteger::incrementAndGet; ConfigExecutor.executeAsyncNotify(runnable); TimeUnit.MILLISECONDS.sleep(20); assertEquals(1, atomicInteger.get()); } @Test void testScheduleAsyncNotify() throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(); Runnable runnable = atomicInteger::incrementAndGet; ConfigExecutor.scheduleAsyncNotify(runnable, 20, TimeUnit.MILLISECONDS); assertEquals(0, atomicInteger.get()); TimeUnit.MILLISECONDS.sleep(40); assertEquals(1, atomicInteger.get()); } @Test void testScheduleLongPollingV1() throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(); Runnable runnable = atomicInteger::incrementAndGet; ConfigExecutor.scheduleLongPolling(runnable, 0, 10, TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS.sleep(10); assertTrue(atomicInteger.get() >= 1); } @Test void testScheduleLongPollingV2() throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(); Runnable runnable = atomicInteger::incrementAndGet; ConfigExecutor.scheduleLongPolling(runnable, 20, TimeUnit.MILLISECONDS); assertEquals(0, atomicInteger.get()); TimeUnit.MILLISECONDS.sleep(40); assertEquals(1, atomicInteger.get()); } @Test void testExecuteLongPolling() throws InterruptedException { AtomicInteger atomicInteger = new AtomicInteger(); Runnable runnable = atomicInteger::incrementAndGet; ConfigExecutor.executeLongPolling(runnable); TimeUnit.MILLISECONDS.sleep(20); assertEquals(1, atomicInteger.get()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/ConfigExtInfoUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.model.gray.ConfigGrayPersistInfo; import com.alibaba.nacos.config.server.model.gray.GrayRuleManager; import org.junit.jupiter.api.Test; import static com.alibaba.nacos.config.server.model.gray.BetaGrayRule.PRIORITY; public class ConfigExtInfoUtilTest { @Test void testExt4Formal() { String dataId = "dataId4567"; String group = "group3456789"; String tenant = "tenant4567890"; //mock exist config info ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setDataId(dataId); configAllInfo.setGroup(group); configAllInfo.setTenant(tenant); configAllInfo.setAppName("old_app"); configAllInfo.setMd5("old_md5"); configAllInfo.setId(12345678765L); configAllInfo.setType(ConfigType.JSON.getType()); configAllInfo.setSchema("testschema"); configAllInfo.setCreateUser("testuser"); configAllInfo.setEffect("online"); configAllInfo.setDesc("desc"); configAllInfo.setUse("use124"); configAllInfo.setConfigTags("ctag1,ctag2"); String extraInfoFromAllInfo = ConfigExtInfoUtil.getExtInfoFromAllInfo(configAllInfo); System.out.println(extraInfoFromAllInfo); } @Test void testExt4Gray() { String grayName = "gray124"; ConfigGrayPersistInfo configGrayPersistInfo = new ConfigGrayPersistInfo(BetaGrayRule.TYPE_BETA, BetaGrayRule.VERSION, "127.0.0.1,127.0.0.2", PRIORITY); String grayRule = GrayRuleManager.serializeConfigGrayPersistInfo(configGrayPersistInfo); String oldSrcUser = "user132"; String extraInfoFromAllInfo = ConfigExtInfoUtil.getExtInfoFromGrayInfo(grayName, grayRule, oldSrcUser); System.out.println(extraInfoFromAllInfo); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/ContentUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.constant.Constants; 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.fail; class ContentUtilsTest { @Test void testVerifyIncrementPubContent() { String content = ""; try { ContentUtils.verifyIncrementPubContent(content); fail(); } catch (IllegalArgumentException e) { assertNotNull(e.toString()); } content = "\r"; try { ContentUtils.verifyIncrementPubContent(content); fail(); } catch (IllegalArgumentException e) { assertNotNull(e.toString()); } content = "\n"; try { ContentUtils.verifyIncrementPubContent(content); fail(); } catch (IllegalArgumentException e) { assertNotNull(e.toString()); } content = Constants.WORD_SEPARATOR + "test"; try { ContentUtils.verifyIncrementPubContent(content); fail(); } catch (IllegalArgumentException e) { assertNotNull(e.toString()); } } @Test void testGetContentIdentity() { String content = "abc" + Constants.WORD_SEPARATOR + "edf"; String result = ContentUtils.getContentIdentity(content); assertEquals("abc", result); content = "test"; try { ContentUtils.getContentIdentity(content); fail(); } catch (IllegalArgumentException e) { assertNotNull(e.toString()); } } @Test void testGetContent() { String content = "abc" + Constants.WORD_SEPARATOR + "edf"; String result = ContentUtils.getContent(content); assertEquals("edf", result); content = "test"; try { ContentUtils.getContent(content); fail(); } catch (IllegalArgumentException e) { assertNotNull(e.toString()); } } @Test void testTruncateContent() { String content = "test"; String result = ContentUtils.truncateContent(content); assertEquals(content, result); String content2 = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"; String result2 = ContentUtils.truncateContent(content2); String expected = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuv..."; assertEquals(expected, result2); assertEquals("", ContentUtils.truncateContent(null)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKey2Test.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import static org.junit.jupiter.api.Assertions.assertArrayEquals; 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.junit.jupiter.api.Assertions.fail; @ExtendWith(SpringExtension.class) @WebAppConfiguration class GroupKey2Test { @Test void testParseInvalidGroupKey2() { String key = "11111+222+333333+444"; try { GroupKey2.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111+"; try { GroupKey2.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111%29+222"; try { GroupKey2.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111%2b+222"; try { GroupKey2.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111%25+222"; String[] pair = GroupKey2.parseKey(key); assertEquals("11111%", pair[0]); assertEquals("222", pair[1]); } @Test void testGetKeyByThreeParams() { // Act final String actual = GroupKey2.getKey(",", ",", "3"); // Assert result assertEquals(",+,+3", actual); } @Test void testGetKeyByTwoParams() { // Act final String actual = GroupKey2.getKey("3", "'"); // Assert result assertEquals("3+'", actual); } @Test void testParseKeyBySingleCharacter() { // Act final String[] actual = GroupKey2.parseKey("/"); // Assert result assertArrayEquals(new String[] {null, "/", null}, actual); } @Test void testParseKeyForPlusIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> { GroupKey2.parseKey("+"); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } @Test void testParseKeyForPercentIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> { GroupKey2.parseKey("%%%5\u0000??????????????"); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } @Test void testParseKeyForInvalidStringIndexOutOfBoundsException() { assertThrows(StringIndexOutOfBoundsException.class, () -> { GroupKey2.parseKey("++%"); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } @Test void testUrlEncodePlus() { // Arrange final StringBuilder sb = new StringBuilder("????"); // Act GroupKey2.urlEncode("+", sb); // Assert side effects assertNotNull(sb); assertEquals("????%2B", sb.toString()); } @Test void testUrlEncodeByPercent() { // Arrange final StringBuilder sb = new StringBuilder("??????"); // Act GroupKey2.urlEncode("%", sb); // Assert side effects assertNotNull(sb); assertEquals("??????%25", sb.toString()); } @Test void testUrlEncodeForNullStringBuilder() { assertThrows(NullPointerException.class, () -> { GroupKey2.urlEncode("+", null); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/GroupKeyTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.test.context.web.WebAppConfiguration; import static org.junit.jupiter.api.Assertions.assertArrayEquals; 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.junit.jupiter.api.Assertions.fail; @ExtendWith(SpringExtension.class) @WebAppConfiguration class GroupKeyTest { @Test void testParseInvalidGroupKey() { String key = "11111+222+333333+444"; try { GroupKey.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111+"; try { GroupKey.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111%29+222"; try { GroupKey.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111%2b+222"; try { GroupKey.parseKey(key); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } key = "11111%25+222"; String[] pair = GroupKey.parseKey(key); assertEquals("11111%", pair[0]); assertEquals("222", pair[1]); } @Test void testGetKeyByThreeParams() { // Act final String actual = GroupKey.getKey(",", ",", "3"); // Assert result assertEquals(",+,+3", actual); } @Test void testGetKeyByTwoParams() { // Act final String actual = GroupKey.getKey("3", "'"); // Assert result assertEquals("3+'", actual); } @Test void testGetKeyTenantByPlusThreeParams() { // Act final String actual = GroupKey.getKeyTenant("3", "1", ","); // Assert result assertEquals("3+1+,", actual); } @Test void testGetKeyTenantByPercentThreeParams() { // Act final String actual = GroupKey.getKeyTenant("\u0000\u0000", "%+", null); // Assert result assertEquals("\u0000\u0000+%25%2B", actual); } @Test void testParseKeyBySingleCharacter() { // Act final String[] actual = GroupKey.parseKey("/"); // Assert result assertArrayEquals(new String[] {null, "/", null}, actual); } @Test void testParseKeyForPlusIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.parseKey("+"); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } @Test void testParseKeyForPercentIllegalArgumentException() { assertThrows(IllegalArgumentException.class, () -> { GroupKey.parseKey("%%%5\u0000??????????????"); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } @Test void testParseKeyForInvalidStringIndexOutOfBoundsException() { assertThrows(StringIndexOutOfBoundsException.class, () -> { GroupKey.parseKey("++%"); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } @Test void testUrlEncodePlus() { // Arrange final StringBuilder sb = new StringBuilder("????"); // Act GroupKey.urlEncode("+", sb); // Assert side effects assertNotNull(sb); assertEquals("????%2B", sb.toString()); } @Test void testUrlEncodeByPercent() { // Arrange final StringBuilder sb = new StringBuilder("??????"); // Act GroupKey.urlEncode("%", sb); // Assert side effects assertNotNull(sb); assertEquals("??????%25", sb.toString()); } @Test void testUrlEncodeForNullStringBuilder() { assertThrows(NullPointerException.class, () -> { GroupKey.urlEncode("+", null); // Method is not expected to return due to exception thrown }); // Method is not expected to return due to exception thrown } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/LogUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import ch.qos.logback.classic.Logger; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class LogUtilTest { @Test void testSetLogLevel() { LogUtil.setLogLevel("config-server", "INFO"); ch.qos.logback.classic.Logger defaultLog = (Logger) LogUtil.DEFAULT_LOG; assertEquals("INFO", defaultLog.getLevel().levelStr); LogUtil.setLogLevel("config-fatal", "INFO"); ch.qos.logback.classic.Logger fatalLog = (Logger) LogUtil.FATAL_LOG; assertEquals("INFO", fatalLog.getLevel().levelStr); LogUtil.setLogLevel("config-pull", "INFO"); ch.qos.logback.classic.Logger pullLog = (Logger) LogUtil.PULL_LOG; assertEquals("INFO", pullLog.getLevel().levelStr); LogUtil.setLogLevel("config-pull-check", "INFO"); ch.qos.logback.classic.Logger pullCheckLog = (Logger) LogUtil.PULL_CHECK_LOG; assertEquals("INFO", pullCheckLog.getLevel().levelStr); LogUtil.setLogLevel("config-dump", "INFO"); ch.qos.logback.classic.Logger dumpLog = (Logger) LogUtil.DUMP_LOG; assertEquals("INFO", dumpLog.getLevel().levelStr); LogUtil.setLogLevel("config-memory", "INFO"); ch.qos.logback.classic.Logger memoryLog = (Logger) LogUtil.MEMORY_LOG; assertEquals("INFO", memoryLog.getLevel().levelStr); LogUtil.setLogLevel("config-client-request", "INFO"); ch.qos.logback.classic.Logger clientRequestLog = (Logger) LogUtil.CLIENT_LOG; assertEquals("INFO", clientRequestLog.getLevel().levelStr); LogUtil.setLogLevel("config-trace", "INFO"); ch.qos.logback.classic.Logger traceLog = (Logger) LogUtil.TRACE_LOG; assertEquals("INFO", traceLog.getLevel().levelStr); LogUtil.setLogLevel("config-notify", "INFO"); ch.qos.logback.classic.Logger notifyLog = (Logger) LogUtil.NOTIFY_LOG; assertEquals("INFO", notifyLog.getLevel().levelStr); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/MD5UtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.service.ConfigCacheService; import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.mockito.Mockito; import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import java.io.CharArrayReader; import java.io.CharArrayWriter; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.io.Writer; import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.Map; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class MD5UtilTest { MockedStatic configCacheServiceMockedStatic; MockedStatic md5ComparatorDelegateMockedStatic; @BeforeEach void setUp() { MockEnvironment environment = new MockEnvironment(); environment.setProperty("nacos.config.cache.type", "nacos"); EnvUtil.setEnvironment(environment); configCacheServiceMockedStatic = Mockito.mockStatic(ConfigCacheService.class); md5ComparatorDelegateMockedStatic = Mockito.mockStatic(Md5ComparatorDelegate.class); } @AfterEach void tearDown() { configCacheServiceMockedStatic.close(); md5ComparatorDelegateMockedStatic.close(); } @Test void testCompareMd5() { Md5ComparatorDelegate md5ComparatorDelegate = Mockito.mock(Md5ComparatorDelegate.class); when(Md5ComparatorDelegate.getInstance()).thenReturn(md5ComparatorDelegate); when(ConfigCacheService.isUptodate(anyString(), anyString(), anyString(), anyString())).thenReturn(false); HashMap clientMd5Map = new HashMap<>(); clientMd5Map.put("test", new ConfigListenState("test")); MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader("Vipserver-Tag", "test"); MockHttpServletResponse response = new MockHttpServletResponse(); when(md5ComparatorDelegate.compareMd5(request, response, clientMd5Map)).thenReturn(new HashMap<>()); MD5Util.compareMd5(request, response, clientMd5Map); verify(md5ComparatorDelegate, times(1)).compareMd5(request, response, clientMd5Map); } @Test void testCompareMd5OldResult() { final MockedStatic groupKey2MockedStatic = Mockito.mockStatic(GroupKey2.class); HashMap changedGroupKeys = new HashMap<>(); changedGroupKeys.put("test", new ConfigListenState("testMd5")); String[] arr = new String[3]; arr[0] = "test0"; arr[1] = "test1"; arr[2] = "test2"; when(GroupKey2.parseKey(anyString())).thenReturn(arr); String actualValue = MD5Util.compareMd5OldResult(changedGroupKeys); assertEquals("test0:test1;", actualValue); groupKey2MockedStatic.close(); } @Test void testCompareMd5ResultString() { final MockedStatic groupKey2MockedStatic = Mockito.mockStatic(GroupKey2.class); HashMap changedGroupKeys = new HashMap<>(); changedGroupKeys.put("test", new ConfigListenState("testMd5")); String[] arr = new String[3]; arr[0] = "test0"; arr[1] = "test1"; arr[2] = "test2"; when(GroupKey2.parseKey(anyString())).thenReturn(arr); try { String actualValue = MD5Util.compareMd5ResultString(changedGroupKeys); assertEquals("test0%02test1%02test2%01", actualValue); } catch (IOException e) { System.out.println(e.toString()); } groupKey2MockedStatic.close(); } @Test void testGetClientMd5Map() { String configKeysString = "test0" + MD5Util.WORD_SEPARATOR_CHAR + "test1" + MD5Util.WORD_SEPARATOR_CHAR + "test2" + MD5Util.LINE_SEPARATOR_CHAR; Map actualValueMap = MD5Util.getClientMd5Map(configKeysString); assertEquals("test2", actualValueMap.get("test0+test1+public").getMd5()); } @Test void testGetClientMd5MapForNewProtocol() { String configKeysString = "test0" + MD5Util.WORD_SEPARATOR_CHAR + "test1" + MD5Util.WORD_SEPARATOR_CHAR + "test2" + MD5Util.WORD_SEPARATOR_CHAR + "test3" + MD5Util.LINE_SEPARATOR_CHAR; Map actualValueMap = MD5Util.getClientMd5Map(configKeysString); assertEquals("test2", actualValueMap.get("test0+test1+test3").getMd5()); } @Test void testToStringV1() { try { InputStream input = IOUtils.toInputStream("test", StandardCharsets.UTF_8); String actualValue = MD5Util.toString(input, "UTF-8"); assertEquals("test", actualValue); } catch (IOException e) { System.out.println(e.toString()); } } @Test void testToStringV2() { try { Reader reader = new CharArrayReader("test".toCharArray()); String actualValue = MD5Util.toString(reader); assertEquals("test", actualValue); } catch (IOException e) { System.out.println(e.toString()); } } @Test void testCopy() { try { String content = "test"; Reader input = new CharArrayReader("test".toCharArray()); Writer output = new CharArrayWriter(); long actualValue = MD5Util.copy(input, output); assertEquals(content.length(), actualValue); assertEquals(content, output.toString()); } catch (IOException e) { System.out.println(e.toString()); } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/Md5ComparatorDelegateTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.sys.env.EnvUtil; 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.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.HashMap; import static org.mockito.Mockito.mockConstruction; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class Md5ComparatorDelegateTest { public MockedStatic envUtilMockedStatic; public MockedStatic nacosServiceLoaderMockedStatic; public MockedConstruction nacosMd5ComparatorMockedConstruction; @Mock public NacosMd5Comparator nacosMd5Comparator; @BeforeEach void setUp() { envUtilMockedStatic = mockStatic(EnvUtil.class); nacosServiceLoaderMockedStatic = mockStatic(NacosServiceLoader.class); } @AfterEach void tearDown() { envUtilMockedStatic.close(); nacosServiceLoaderMockedStatic.close(); } @Test public void test() { envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")).thenReturn("lalala"); nacosServiceLoaderMockedStatic.when(() -> NacosServiceLoader.load(Md5Comparator.class)) .thenReturn(Collections.singletonList(nacosMd5Comparator)); MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); HashMap clientMd5Map = new HashMap<>(); nacosMd5ComparatorMockedConstruction = mockConstruction(NacosMd5Comparator.class, (mock, context) -> { when(mock.compareMd5(request, response, clientMd5Map)).thenReturn(null); }); Md5ComparatorDelegate.getInstance().compareMd5(request, response, clientMd5Map); verify(nacosMd5Comparator, times(0)).compareMd5(request, response, clientMd5Map); nacosMd5ComparatorMockedConstruction.close(); } @Test public void test2() throws Exception { when(nacosMd5Comparator.getName()).thenReturn("nacos"); envUtilMockedStatic.when(() -> EnvUtil.getProperty("nacos.config.cache.type", "nacos")).thenReturn("nacos"); nacosServiceLoaderMockedStatic.when(() -> NacosServiceLoader.load(Md5Comparator.class)) .thenReturn(Collections.singletonList(nacosMd5Comparator)); Constructor constructor = Md5ComparatorDelegate.class.getDeclaredConstructor(); constructor.setAccessible(true); Field field = Md5ComparatorDelegate.class.getDeclaredField("INSTANCE"); field.setAccessible(true); Md5ComparatorDelegate delegate = (Md5ComparatorDelegate) constructor.newInstance(); setStaticFinalField(field, delegate); MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); HashMap clientMd5Map = new HashMap<>(); Md5ComparatorDelegate.getInstance().compareMd5(request, response, clientMd5Map); verify(nacosMd5Comparator, times(1)).compareMd5(request, response, clientMd5Map); } private void setStaticFinalField(Field finalField, Object value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { Method getDeclaredFields0 = Class.class.getDeclaredMethod("getDeclaredFields0", boolean.class); getDeclaredFields0.setAccessible(true); Field[] fields = (Field[]) getDeclaredFields0.invoke(Field.class, false); Field modifiers = null; for (Field each : fields) { if ("modifiers".equals(each.getName())) { modifiers = each; } } modifiers.setAccessible(true); modifiers.setInt(finalField, finalField.getModifiers() & ~Modifier.FINAL); finalField.setAccessible(true); finalField.set(null, value); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/NacosMd5ComparatorTest.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.model.ConfigListenState; import com.alibaba.nacos.config.server.service.ConfigCacheService; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; 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 java.util.HashMap; import java.util.Map; import static com.alibaba.nacos.api.common.Constants.VIPSERVER_TAG; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mockStatic; import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) class NacosMd5ComparatorTest { MockedStatic mockRequestUtil; MockedStatic configCacheServiceMockedStatic; @Mock HttpServletRequest request; @Mock HttpServletResponse response; @BeforeEach void setUp() { mockRequestUtil = mockStatic(RequestUtil.class); configCacheServiceMockedStatic = mockStatic(ConfigCacheService.class); } @AfterEach void tearDown() { mockRequestUtil.close(); configCacheServiceMockedStatic.close(); } @Test void getName() { NacosMd5Comparator nacosMd5Comparator = new NacosMd5Comparator(); assertEquals("nacos", nacosMd5Comparator.getName()); } @Test void compareMd5NoChange() { String ip = "127.0.0.1"; String tag = "tag"; when(request.getHeader(VIPSERVER_TAG)).thenReturn(tag); mockRequestUtil.when(() -> RequestUtil.getRemoteIp(request)).thenReturn(ip); String groupKey1 = "groupKey1"; String groupKey2 = "groupKey2"; String clientMd5 = "clientMd5"; HashMap clientMd5Map = new HashMap<>(); clientMd5Map.put(groupKey1, new ConfigListenState(clientMd5)); clientMd5Map.put(groupKey2, new ConfigListenState(clientMd5)); NacosMd5Comparator nacosMd5Comparator = new NacosMd5Comparator(); configCacheServiceMockedStatic.when( () -> ConfigCacheService.isUptodate(anyString(), eq(clientMd5), eq(ip), eq(tag))).thenReturn(true); Map changedGroupKeys = nacosMd5Comparator.compareMd5(request, response, clientMd5Map); assertEquals(0, changedGroupKeys.size()); } @Test void compareMd5Change() { String ip = "127.0.0.1"; String tag = "tag"; when(request.getHeader(VIPSERVER_TAG)).thenReturn(tag); mockRequestUtil.when(() -> RequestUtil.getRemoteIp(request)).thenReturn(ip); String groupKey1 = "groupKey1"; String groupKey2 = "groupKey2"; String clientMd5 = "clientMd5"; HashMap clientMd5Map = new HashMap<>(); clientMd5Map.put(groupKey1, new ConfigListenState(clientMd5)); clientMd5Map.put(groupKey2, new ConfigListenState(clientMd5)); NacosMd5Comparator nacosMd5Comparator = new NacosMd5Comparator(); configCacheServiceMockedStatic.when( () -> ConfigCacheService.isUptodate(anyString(), eq(clientMd5), eq(ip), eq(tag))).thenReturn(false); Map changedGroupKeys = nacosMd5Comparator.compareMd5(request, response, clientMd5Map); assertEquals(2, changedGroupKeys.size()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/ParamUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 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.junit.jupiter.api.Assertions.fail; class ParamUtilsTest { @Test void testIsValid() { assertTrue(ParamUtils.isValid("test")); assertTrue(ParamUtils.isValid("test1234")); assertTrue(ParamUtils.isValid("test_-.:")); assertFalse(ParamUtils.isValid("test!")); assertFalse(ParamUtils.isValid("test~")); } @Test void testCheckParamV1() { //dataId is empty String dataId = ""; String group = "test"; String datumId = "test"; String content = "test"; try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //group is empty dataId = "test"; group = ""; datumId = "test"; content = "test"; try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //datumId is empty dataId = "test"; group = "test"; datumId = ""; content = "test"; try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //content is empty dataId = "test"; group = "test"; datumId = "test"; content = ""; try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //dataId invalid dataId = "test!"; group = "test"; datumId = "test"; content = "test"; try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //group invalid dataId = "test"; group = "test!"; datumId = "test"; content = "test"; try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //datumId invalid dataId = "test"; group = "test"; datumId = "test!"; content = "test"; try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //content over length dataId = "test"; group = "test"; datumId = "test"; int maxContent = 10 * 1024 * 1024; StringBuilder contentBuilder = new StringBuilder(); for (int i = 0; i < maxContent + 1; i++) { contentBuilder.append("t"); } content = contentBuilder.toString(); try { ParamUtils.checkParam(dataId, group, datumId, content); fail(); } catch (NacosException e) { System.out.println(e.toString()); } } @Test void testCheckParamV2() { //tag invalid String tag = "test!"; try { ParamUtils.checkParam(tag); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } //tag over length tag = "testtesttesttest1"; try { ParamUtils.checkParam(tag); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } } @Test void testCheckParamV3() { //tag size over 5 Map configAdvanceInfo = new HashMap<>(); configAdvanceInfo.put("config_tags", "test,test,test,test,test,test"); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //tag length over 5 configAdvanceInfo.clear(); StringBuilder tagBuilder = new StringBuilder(); for (int i = 0; i < 65; i++) { tagBuilder.append("t"); } configAdvanceInfo.put("config_tags", tagBuilder.toString()); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //desc length over 128 configAdvanceInfo.clear(); StringBuilder descBuilder = new StringBuilder(); for (int i = 0; i < 129; i++) { descBuilder.append("t"); } configAdvanceInfo.put("desc", descBuilder.toString()); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //use length over 32 configAdvanceInfo.clear(); StringBuilder useBuilder = new StringBuilder(); for (int i = 0; i < 33; i++) { useBuilder.append("t"); } configAdvanceInfo.put("use", useBuilder.toString()); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //effect length over 32 configAdvanceInfo.clear(); StringBuilder effectBuilder = new StringBuilder(); for (int i = 0; i < 33; i++) { effectBuilder.append("t"); } configAdvanceInfo.put("effect", effectBuilder.toString()); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //type length over 32 configAdvanceInfo.clear(); StringBuilder typeBuilder = new StringBuilder(); for (int i = 0; i < 33; i++) { typeBuilder.append("t"); } configAdvanceInfo.put("type", typeBuilder.toString()); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //schema length over 32768 configAdvanceInfo.clear(); StringBuilder schemaBuilder = new StringBuilder(); for (int i = 0; i < 32769; i++) { schemaBuilder.append("t"); } configAdvanceInfo.put("schema", schemaBuilder.toString()); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } //invalid param configAdvanceInfo.clear(); configAdvanceInfo.put("test", "test"); try { ParamUtils.checkParam(configAdvanceInfo); fail(); } catch (NacosException e) { System.out.println(e.toString()); } } @Test void testCheckTenant() { //tag invalid String tenant = "test!"; try { ParamUtils.checkTenant(tenant); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } //tag over length int tanantMaxLen = 128; StringBuilder tenantBuilder = new StringBuilder(); for (int i = 0; i < tanantMaxLen + 1; i++) { tenantBuilder.append("t"); } tenant = tenantBuilder.toString(); try { ParamUtils.checkTenant(tenant); fail(); } catch (IllegalArgumentException e) { System.out.println(e.toString()); } } @Test void testCheckParamWithNamespaceGroupDataId() { assertThrows(NacosApiException.class, () -> ParamUtils.checkParam("../", "group", "")); assertThrows(NacosApiException.class, () -> ParamUtils.checkParam("dataId", "../", "")); assertThrows(NacosApiException.class, () -> ParamUtils.checkParam("dataId", "group", "../")); assertDoesNotThrow(() -> ParamUtils.checkParam("dataId", "group", "")); assertDoesNotThrow(() -> ParamUtils.checkParam("dataId", "group", UUID.randomUUID().toString())); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/PropertyUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.commons.io.FileUtils; 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.MockedStatic; import org.mockito.Mockito; import org.springframework.security.util.FieldUtils; import org.springframework.test.context.junit.jupiter.SpringExtension; import java.io.File; import java.lang.reflect.Field; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; @ExtendWith(SpringExtension.class) class PropertyUtilTest { MockedStatic envUtilMockedStatic; private String mockMem = "tmpmocklimitfile.txt"; @BeforeEach void setUp() { envUtilMockedStatic = Mockito.mockStatic(EnvUtil.class); envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("memory_limit_file_path"), eq("/sys/fs/cgroup/memory/memory.limit_in_bytes"))).thenReturn(mockMem); } @AfterEach void after() { envUtilMockedStatic.close(); File file = new File(mockMem); if (file.exists()) { file.delete(); } } @Test void testGetPropertyV1() { envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("test"))).thenReturn("test"); assertEquals("test", new PropertyUtil().getProperty("test")); } @Test void testGetPropertyV2() { envUtilMockedStatic.when(() -> EnvUtil.getProperty(eq("test"), eq("default"))).thenReturn("default"); assertEquals("default", new PropertyUtil().getProperty("test", "default")); } private void clearAllDumpFiled() throws Exception { Field allDumpPageSizeFiled = FieldUtils.getField(PropertyUtil.class, "allDumpPageSize"); allDumpPageSizeFiled.setAccessible(true); allDumpPageSizeFiled.set(null, null); Field limitMemoryFileFiled = FieldUtils.getField(PropertyUtil.class, "limitMemoryFile"); limitMemoryFileFiled.setAccessible(true); limitMemoryFileFiled.set(null, null); } @Test void testGetAllDumpPageSize() throws Exception { clearAllDumpFiled(); File file = new File(mockMem); //2G pageSize between 50 to 1000 long gb2 = 2L * 1024L * 1024L * 1024L; FileUtils.writeStringToFile(file, String.valueOf(gb2)); int allDumpPageSizeNormal = PropertyUtil.getAllDumpPageSize(); //expect 2*2*50 assertEquals(200, allDumpPageSizeNormal); clearAllDumpFiled(); // 12G pageSize over 1000 long gb12 = 12L * 1024L * 1024L * 1024L; FileUtils.writeStringToFile(file, String.valueOf(gb12)); int allDumpPageSizeOverMax = PropertyUtil.getAllDumpPageSize(); assertEquals(1000, allDumpPageSizeOverMax); clearAllDumpFiled(); //100MB long mb100 = 100L * 1024L * 1024L; FileUtils.writeStringToFile(file, String.valueOf(mb100)); int allDumpPageSizeUnderMin = PropertyUtil.getAllDumpPageSize(); assertEquals(50, allDumpPageSizeUnderMin); } @Test void testGetAllDumpPageSizeWithJvmArgs() throws Exception { File file = new File(mockMem); if (file.exists()) { file.delete(); } int allDumpPageSizeUnderMin = PropertyUtil.initAllDumpPageSize(); long maxMem = Runtime.getRuntime().maxMemory(); long pageSize = maxMem / 1024 / 1024 / 512 * 50; if (pageSize < 50) { assertEquals(50, allDumpPageSizeUnderMin); } else if (pageSize > 1000) { assertEquals(1000, allDumpPageSizeUnderMin); } else { assertEquals(pageSize, allDumpPageSizeUnderMin); } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/ProtocolTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(SpringExtension.class) class ProtocolTest { @Test void testGetVersionNumber() { assertEquals(-1, Protocol.getVersionNumber(null)); assertEquals(0, Protocol.getVersionNumber("")); assertEquals(120, Protocol.getVersionNumber("1.2.0")); assertEquals(10, Protocol.getVersionNumber("1.A.0")); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/RegexParserTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; 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; import static org.junit.jupiter.api.Assertions.fail; class RegexParserTest { @Test void testRegexFormat() { try { RegexParser.regexFormat(null); fail(); } catch (NullPointerException e) { System.out.println(e.toString()); } assertEquals("^test.*\\!.{1}xxxx$", RegexParser.regexFormat("test*!?xxxx")); } @Test void testContainsWildcard() { assertFalse(RegexParser.containsWildcard("test")); assertTrue(RegexParser.containsWildcard("?")); assertTrue(RegexParser.containsWildcard("*")); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/RequestUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.core.context.RequestContextHolder; import com.alibaba.nacos.plugin.auth.api.IdentityContext; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import jakarta.servlet.http.HttpServletRequest; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; class RequestUtilTest { private static final String X_REAL_IP = "X-Real-IP"; private static final String X_FORWARDED_FOR = "X-Forwarded-For"; @AfterEach void tearDown() { RequestContextHolder.removeContext(); } @Test void testGetRemoteIpFromRequest() { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); Mockito.when(request.getRemoteAddr()).thenReturn("127.0.0.1"); assertEquals("127.0.0.1", RequestUtil.getRemoteIp(request)); Mockito.when(request.getHeader(eq(X_REAL_IP))).thenReturn("127.0.0.2"); assertEquals("127.0.0.2", RequestUtil.getRemoteIp(request)); Mockito.when(request.getHeader(eq(X_FORWARDED_FOR))).thenReturn("127.0.0.3"); assertEquals("127.0.0.3", RequestUtil.getRemoteIp(request)); Mockito.when(request.getHeader(eq(X_FORWARDED_FOR))).thenReturn("127.0.0.3, 127.0.0.4"); assertEquals("127.0.0.3", RequestUtil.getRemoteIp(request)); Mockito.when(request.getHeader(eq(X_FORWARDED_FOR))).thenReturn(""); assertEquals("127.0.0.2", RequestUtil.getRemoteIp(request)); Mockito.when(request.getHeader(eq(X_REAL_IP))).thenReturn(""); assertEquals("127.0.0.1", RequestUtil.getRemoteIp(request)); } @Test void testGetAppNameFromContext() { RequestContextHolder.getContext().getBasicContext().setApp("contextApp"); HttpServletRequest request = Mockito.mock(HttpServletRequest.class); Mockito.when(request.getHeader(eq(RequestUtil.CLIENT_APPNAME_HEADER))).thenReturn("test"); assertEquals("contextApp", RequestUtil.getAppName(request)); } @Test void testGetAppNameFromRequest() { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); Mockito.when(request.getHeader(eq(RequestUtil.CLIENT_APPNAME_HEADER))).thenReturn("test"); assertEquals("test", RequestUtil.getAppName(request)); } @Test void testGetSrcUserNameFromContext() { IdentityContext identityContext = new IdentityContext(); identityContext.setParameter(com.alibaba.nacos.plugin.auth.constant.Constants.Identity.IDENTITY_ID, "test"); RequestContextHolder.getContext().getAuthContext().setIdentityContext(identityContext); HttpServletRequest request = Mockito.mock(HttpServletRequest.class); assertEquals("test", RequestUtil.getSrcUserName(request)); } @Test void testGetSrcUserNameFromRequest() { HttpServletRequest request = Mockito.mock(HttpServletRequest.class); Mockito.when(request.getParameter(eq(Constants.USERNAME))).thenReturn("parameterName"); assertEquals("parameterName", RequestUtil.getSrcUserName(request)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/ResponseUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletResponse; import java.io.UnsupportedEncodingException; import java.sql.Timestamp; import static org.junit.jupiter.api.Assertions.assertEquals; class ResponseUtilTest { String lineSeparator = System.lineSeparator(); @Test void testWriteErrMsg() { MockHttpServletResponse response = new MockHttpServletResponse(); ResponseUtil.writeErrMsg(response, 404, "test"); assertEquals(404, response.getStatus()); try { assertEquals("test" + lineSeparator, response.getContentAsString()); } catch (UnsupportedEncodingException e) { System.out.println(e.toString()); } } @Test void testTransferToConfigDetailInfo() { ConfigAllInfo configAllInfo = new ConfigAllInfo(); configAllInfo.setId(1L); configAllInfo.setTenant("testNs"); configAllInfo.setGroup(Constants.DEFAULT_GROUP); configAllInfo.setDataId("testDs"); configAllInfo.setMd5("testMd5"); configAllInfo.setEncryptedDataKey("testEncryptedDataKey"); configAllInfo.setContent("testContent"); configAllInfo.setDesc("testDesc"); configAllInfo.setType("text"); configAllInfo.setAppName("testAppName"); configAllInfo.setCreateIp("1.1.1.1"); configAllInfo.setCreateUser("testCreateUser"); configAllInfo.setCreateTime(System.currentTimeMillis()); configAllInfo.setModifyTime(System.currentTimeMillis()); configAllInfo.setConfigTags("testConfigTag1,testConfigTag2"); configAllInfo.setUse("testUse"); configAllInfo.setEffect("testEffect"); configAllInfo.setSchema("testSchema"); ConfigDetailInfo configDetailInfo = ResponseUtil.transferToConfigDetailInfo(configAllInfo); assertEquals(configAllInfo.getId(), configDetailInfo.getId()); assertEquals(configAllInfo.getTenant(), configDetailInfo.getNamespaceId()); assertEquals(configAllInfo.getGroup(), configDetailInfo.getGroupName()); assertEquals(configAllInfo.getDataId(), configDetailInfo.getDataId()); assertEquals(configAllInfo.getMd5(), configDetailInfo.getMd5()); assertEquals(configAllInfo.getEncryptedDataKey(), configDetailInfo.getEncryptedDataKey()); assertEquals(configAllInfo.getContent(), configDetailInfo.getContent()); assertEquals(configAllInfo.getDesc(), configDetailInfo.getDesc()); assertEquals(configAllInfo.getType(), configDetailInfo.getType()); assertEquals(configAllInfo.getAppName(), configDetailInfo.getAppName()); assertEquals(configAllInfo.getCreateIp(), configDetailInfo.getCreateIp()); assertEquals(configAllInfo.getCreateUser(), configDetailInfo.getCreateUser()); assertEquals(configAllInfo.getCreateTime(), configDetailInfo.getCreateTime()); assertEquals(configAllInfo.getModifyTime(), configDetailInfo.getModifyTime()); assertEquals(configAllInfo.getConfigTags(), configDetailInfo.getConfigTags()); } @Test void testTransferToConfigBasicInfo() { ConfigInfo configInfo = new ConfigInfo(); configInfo.setId(1L); configInfo.setTenant("testNs"); configInfo.setGroup(Constants.DEFAULT_GROUP); configInfo.setDataId("testDs"); configInfo.setMd5("testMd5"); configInfo.setEncryptedDataKey("testEncryptedDataKey"); configInfo.setContent("testContent"); configInfo.setType("text"); configInfo.setAppName("testAppName"); configInfo.setDesc("testDesc"); configInfo.setConfigTags("tag1,tag2"); ConfigBasicInfo configBasicInfo = ResponseUtil.transferToConfigBasicInfo(configInfo); assertEquals(configInfo.getId(), configBasicInfo.getId()); assertEquals(configInfo.getTenant(), configBasicInfo.getNamespaceId()); assertEquals(configInfo.getGroup(), configBasicInfo.getGroupName()); assertEquals(configInfo.getDataId(), configBasicInfo.getDataId()); assertEquals(configInfo.getMd5(), configBasicInfo.getMd5()); assertEquals(configInfo.getType(), configBasicInfo.getType()); assertEquals(configInfo.getAppName(), configBasicInfo.getAppName()); assertEquals(configInfo.getDesc(), configBasicInfo.getDesc()); assertEquals(configInfo.getConfigTags(), configBasicInfo.getConfigTags()); assertEquals(0L, configBasicInfo.getCreateTime()); assertEquals(0L, configBasicInfo.getModifyTime()); } @Test void testTransferToConfigBasicInfoFromWrapper() { ConfigInfoWrapper configInfo = new ConfigInfoWrapper(); configInfo.setId(1L); configInfo.setTenant("testNs"); configInfo.setGroup(Constants.DEFAULT_GROUP); configInfo.setDataId("testDs"); configInfo.setMd5("testMd5"); configInfo.setEncryptedDataKey("testEncryptedDataKey"); configInfo.setContent("testContent"); configInfo.setType("text"); configInfo.setAppName("testAppName"); configInfo.setDesc("testDesc"); configInfo.setConfigTags("tag1,tag2"); configInfo.setLastModified(System.currentTimeMillis()); ConfigBasicInfo configBasicInfo = ResponseUtil.transferToConfigBasicInfo(configInfo); assertEquals(configInfo.getId(), configBasicInfo.getId()); assertEquals(configInfo.getTenant(), configBasicInfo.getNamespaceId()); assertEquals(configInfo.getGroup(), configBasicInfo.getGroupName()); assertEquals(configInfo.getDataId(), configBasicInfo.getDataId()); assertEquals(configInfo.getMd5(), configBasicInfo.getMd5()); assertEquals(configInfo.getType(), configBasicInfo.getType()); assertEquals(configInfo.getAppName(), configBasicInfo.getAppName()); assertEquals(configInfo.getDesc(), configBasicInfo.getDesc()); assertEquals(configInfo.getConfigTags(), configBasicInfo.getConfigTags()); assertEquals(0L, configBasicInfo.getCreateTime()); assertEquals(configInfo.getLastModified(), configBasicInfo.getModifyTime()); } @Test void testTransferToConfigGrayInfo() { ConfigInfoGrayWrapper configInfoGray = new ConfigInfoGrayWrapper(); configInfoGray.setId(1L); configInfoGray.setTenant("testNs"); configInfoGray.setGroup(Constants.DEFAULT_GROUP); configInfoGray.setDataId("testDs"); configInfoGray.setMd5("testMd5"); configInfoGray.setEncryptedDataKey("testEncryptedDataKey"); configInfoGray.setContent("testContent"); configInfoGray.setType("text"); configInfoGray.setAppName("testAppName"); configInfoGray.setGrayName("testGrayName"); configInfoGray.setGrayRule("testGrayRule"); configInfoGray.setSrcUser("testSrcUser"); configInfoGray.setLastModified(System.currentTimeMillis()); ConfigGrayInfo configGrayInfo = ResponseUtil.transferToConfigGrayInfo(configInfoGray); assertEquals(configInfoGray.getId(), configGrayInfo.getId()); assertEquals(configInfoGray.getTenant(), configGrayInfo.getNamespaceId()); assertEquals(configInfoGray.getGroup(), configGrayInfo.getGroupName()); assertEquals(configInfoGray.getDataId(), configGrayInfo.getDataId()); assertEquals(configInfoGray.getMd5(), configGrayInfo.getMd5()); assertEquals(configInfoGray.getType(), configGrayInfo.getType()); assertEquals(configInfoGray.getEncryptedDataKey(), configGrayInfo.getEncryptedDataKey()); assertEquals(configInfoGray.getAppName(), configGrayInfo.getAppName()); assertEquals(0, configGrayInfo.getCreateTime()); assertEquals(configInfoGray.getLastModified(), configGrayInfo.getModifyTime()); assertEquals(configInfoGray.getSrcUser(), configGrayInfo.getCreateUser()); assertEquals(configInfoGray.getGrayName(), configGrayInfo.getGrayName()); assertEquals(configInfoGray.getGrayRule(), configGrayInfo.getGrayRule()); } @Test void testTransferToConfigHistoryBasicInfo() { ConfigHistoryInfo configHistoryInfo = mockConfigHistoryInfo(); ConfigHistoryBasicInfo configHistoryBasicInfo = ResponseUtil.transferToConfigHistoryBasicInfo( configHistoryInfo); assertConfigHistoryBasicInfo(configHistoryInfo, configHistoryBasicInfo); } @Test void testTransferToConfigHistoryDetialInfo() { ConfigHistoryInfo configHistoryInfo = mockConfigHistoryInfo(); ConfigHistoryDetailInfo configHistoryBasicInfo = ResponseUtil.transferToConfigHistoryDetailInfo( configHistoryInfo); assertConfigHistoryBasicInfo(configHistoryInfo, configHistoryBasicInfo); assertEquals(configHistoryInfo.getContent(), configHistoryBasicInfo.getContent()); assertEquals(configHistoryInfo.getEncryptedDataKey(), configHistoryBasicInfo.getEncryptedDataKey()); assertEquals(configHistoryInfo.getGrayName(), configHistoryBasicInfo.getGrayName()); assertEquals(configHistoryInfo.getExtInfo(), configHistoryBasicInfo.getExtInfo()); } private ConfigHistoryInfo mockConfigHistoryInfo() { ConfigHistoryInfo configHistoryInfo = new ConfigHistoryInfo(); configHistoryInfo.setId(1L); configHistoryInfo.setTenant("testNs"); configHistoryInfo.setGroup(Constants.DEFAULT_GROUP); configHistoryInfo.setDataId("testDs"); configHistoryInfo.setAppName("testAppName"); configHistoryInfo.setMd5("testMd5"); configHistoryInfo.setContent("testContent"); configHistoryInfo.setSrcIp("1.1.1.1"); configHistoryInfo.setSrcUser("testSrcUser"); configHistoryInfo.setOpType("I"); configHistoryInfo.setPublishType("formal"); configHistoryInfo.setGrayName("testGrayName"); configHistoryInfo.setExtInfo("{\"type\":\"text\"}"); configHistoryInfo.setCreatedTime(new Timestamp(System.currentTimeMillis())); configHistoryInfo.setLastModifiedTime(new Timestamp(System.currentTimeMillis())); configHistoryInfo.setEncryptedDataKey("testEncryptedDataKey"); return configHistoryInfo; } private void assertConfigHistoryBasicInfo(ConfigHistoryInfo configHistoryInfo, ConfigHistoryBasicInfo configHistoryBasicInfo) { assertEquals(configHistoryInfo.getId(), configHistoryBasicInfo.getId()); assertEquals(configHistoryInfo.getTenant(), configHistoryBasicInfo.getNamespaceId()); assertEquals(configHistoryInfo.getGroup(), configHistoryBasicInfo.getGroupName()); assertEquals(configHistoryInfo.getDataId(), configHistoryBasicInfo.getDataId()); assertEquals(configHistoryInfo.getAppName(), configHistoryBasicInfo.getAppName()); assertEquals(configHistoryInfo.getMd5(), configHistoryBasicInfo.getMd5()); assertEquals(configHistoryInfo.getSrcIp(), configHistoryBasicInfo.getSrcIp()); assertEquals(configHistoryInfo.getSrcUser(), configHistoryBasicInfo.getSrcUser()); assertEquals(configHistoryInfo.getOpType(), configHistoryBasicInfo.getOpType()); assertEquals(configHistoryInfo.getPublishType(), configHistoryBasicInfo.getPublishType()); assertEquals(configHistoryInfo.getCreatedTime().getTime(), configHistoryBasicInfo.getCreateTime()); assertEquals(configHistoryInfo.getLastModifiedTime().getTime(), configHistoryBasicInfo.getModifyTime()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleCacheTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class SimpleCacheTest { @Test void testPutAndGet() throws InterruptedException { SimpleCache simpleCache = new SimpleCache<>(); simpleCache.put("key", "value", 1000); assertEquals("value", simpleCache.get("key")); //time expire TimeUnit.MILLISECONDS.sleep(1100); Object value = simpleCache.get("key"); assertNull(value); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleFlowDataTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class SimpleFlowDataTest { @Test void testAddAndGet() { SimpleFlowData simpleFlowData = new SimpleFlowData(5, 10000); assertEquals(10, simpleFlowData.addAndGet(10)); assertEquals(20, simpleFlowData.addAndGet(10)); } @Test void testIncrementAndGet() { SimpleFlowData simpleFlowData = new SimpleFlowData(5, 10000); assertEquals(1, simpleFlowData.incrementAndGet()); assertEquals(2, simpleFlowData.incrementAndGet()); assertEquals(3, simpleFlowData.incrementAndGet()); } @Test void testGetSlotInfo() { SimpleFlowData simpleFlowData = new SimpleFlowData(5, 10000); simpleFlowData.incrementAndGet(); simpleFlowData.incrementAndGet(); simpleFlowData.incrementAndGet(); assertEquals("0 0 0 0 3", simpleFlowData.getSlotInfo()); } @Test void testGetSlotInfo2() { SimpleFlowData simpleFlowData = new SimpleFlowData(5, 10000); simpleFlowData.incrementAndGet(); simpleFlowData.rotateSlot(); simpleFlowData.addAndGet(9); simpleFlowData.rotateSlot(); simpleFlowData.incrementAndGet(); assertEquals("0 0 1 9 1", simpleFlowData.getSlotInfo()); assertEquals(1, simpleFlowData.getCurrentCount()); assertEquals(2, simpleFlowData.getAverageCount()); assertEquals(5, simpleFlowData.getSlotCount()); } @Test void testGetCount() { SimpleFlowData simpleFlowData = new SimpleFlowData(5, 10000); simpleFlowData.addAndGet(2); simpleFlowData.rotateSlot(); simpleFlowData.addAndGet(3); simpleFlowData.rotateSlot(); simpleFlowData.incrementAndGet(); assertEquals("0 0 2 3 1", simpleFlowData.getSlotInfo()); assertEquals(2, simpleFlowData.getCount(2)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleIpFlowDataTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class SimpleIpFlowDataTest { @Test void testIncrementAndGet() { SimpleIpFlowData simpleIpFlowData = new SimpleIpFlowData(5, 10000); assertEquals(1, simpleIpFlowData.incrementAndGet("127.0.0.1")); assertEquals(2, simpleIpFlowData.incrementAndGet("127.0.0.1")); assertEquals(3, simpleIpFlowData.incrementAndGet("127.0.0.1")); assertEquals(1, simpleIpFlowData.incrementAndGet("127.0.0.2")); assertEquals(2, simpleIpFlowData.incrementAndGet("127.0.0.2")); } @Test void testGetCurrentCount() { SimpleIpFlowData simpleIpFlowData = new SimpleIpFlowData(3, 10000); simpleIpFlowData.incrementAndGet("127.0.0.1"); simpleIpFlowData.incrementAndGet("127.0.0.1"); simpleIpFlowData.incrementAndGet("127.0.0.1"); assertEquals(3, simpleIpFlowData.getCurrentCount("127.0.0.1")); simpleIpFlowData.rotateSlot(); assertEquals(0, simpleIpFlowData.getCurrentCount("127.0.0.1")); assertEquals(1, simpleIpFlowData.getAverageCount()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/SimpleReadWriteLockTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.test.context.junit.jupiter.SpringExtension; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @ExtendWith(SpringExtension.class) class SimpleReadWriteLockTest { @Test void testDoubleReadLockByAllReleaseAndWriteLock() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertTrue(lock.tryReadLock()); assertTrue(lock.tryReadLock()); lock.releaseReadLock(); lock.releaseReadLock(); assertTrue(lock.tryWriteLock()); } @Test void testAddWriteLock() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertTrue(lock.tryWriteLock()); lock.releaseWriteLock(); } @Test void testDoubleWriteLock() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertTrue(lock.tryWriteLock()); assertFalse(lock.tryWriteLock()); } @Test void testFirstReadLockThenWriteLock() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertTrue(lock.tryReadLock()); assertFalse(lock.tryWriteLock()); } @Test void testFirstWriteLockThenReadLock() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertTrue(lock.tryWriteLock()); assertFalse(lock.tryReadLock()); } @Test void testDoubleReadLockAndOneReleaseOneFailed() { SimpleReadWriteLock lock = new SimpleReadWriteLock(); assertTrue(lock.tryReadLock()); assertTrue(lock.tryReadLock()); lock.releaseReadLock(); assertFalse(lock.tryWriteLock()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/SystemConfigTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class SystemConfigTest { @Test void testGetHostAddress() { System.setProperty("nacos.server.ip", "127.0.0.1"); assertEquals("127.0.0.1", SystemConfig.LOCAL_IP); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/TestCaseUtils.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.mockito.Mockito; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.JdbcTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.support.DefaultTransactionStatus; import org.springframework.transaction.support.TransactionTemplate; import java.util.HashMap; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.when; public class TestCaseUtils { /** * create mocked transaction template with transact ability. * * @return */ public static TransactionTemplate createMockTransactionTemplate() { JdbcTransactionManager transactionManager = Mockito.mock(JdbcTransactionManager.class); when(transactionManager.getTransaction(any(TransactionDefinition.class))).thenReturn( new DefaultTransactionStatus(null, null, true, true, false, false, false, null)); TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); return transactionTemplate; } /** * create mocked transaction template with transact ability. * * @return */ public static GeneratedKeyHolder createGeneratedKeyHolder(long wantedId) { GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder(); HashMap objectObjectHashMap = new HashMap<>(); objectObjectHashMap.put("whatever", wantedId); generatedKeyHolder.getKeyList().add(objectObjectHashMap); return generatedKeyHolder; } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/TimeUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import static org.junit.jupiter.api.Assertions.assertNotNull; class TimeUtilsTest { @Test void testGetCurrentTimeStr() throws ParseException { Date date1 = new Date(TimeUtils.getCurrentTime().getTime()); assertNotNull(date1.toString()); Date date2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(TimeUtils.getCurrentTimeStr()); assertNotNull(date2.toString()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/TimeoutUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; 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 TimeoutUtilsTest { @Test void testAddTotalTime() { TimeoutUtils timeoutUtils = new TimeoutUtils(10, 1); timeoutUtils.initLastResetTime(); timeoutUtils.addTotalTime(1); assertEquals(1L, timeoutUtils.getTotalTime().get()); } @Test void testIsTimeout() { TimeoutUtils timeoutUtils = new TimeoutUtils(10, 1); timeoutUtils.initLastResetTime(); timeoutUtils.addTotalTime(1); assertFalse(timeoutUtils.isTimeout()); timeoutUtils.addTotalTime(10); assertTrue(timeoutUtils.isTimeout()); } @Test void testResetTotalTime() { TimeoutUtils timeoutUtils = new TimeoutUtils(10, -1); timeoutUtils.initLastResetTime(); timeoutUtils.addTotalTime(1); assertEquals(1L, timeoutUtils.getTotalTime().get()); timeoutUtils.resetTotalTime(); assertEquals(0L, timeoutUtils.getTotalTime().get()); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/TraceLogUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import org.slf4j.Logger; import static org.junit.jupiter.api.Assertions.assertTrue; class TraceLogUtilTest { @Test void testRequestLog() { Logger requestLog = TraceLogUtil.requestLog; assertTrue(requestLog instanceof Logger); Logger pollingLog = TraceLogUtil.pollingLog; assertTrue(pollingLog instanceof Logger); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/UrlAnalysisUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; class UrlAnalysisUtilsTest { @Test void testGetContentIdentity() { String url = "http://127.0.0.1:8080/test?paramA=A¶mB=B"; assertEquals("http://127.0.0.1:8080", UrlAnalysisUtils.getContentIdentity(url)); String url2 = "127.0.0.1:8080/test?paramA=A¶mB=B"; assertEquals("127.0.0.1:8080", UrlAnalysisUtils.getContentIdentity(url2)); String url3 = ""; assertNull(UrlAnalysisUtils.getContentIdentity(url3)); } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/YamlParserUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import com.alibaba.nacos.config.server.model.ConfigMetadata; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.constructor.ConstructorException; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; class YamlParserUtilTest { private static final String CONFIG_METADATA_STRING = "metadata:\n" + "- dataId: testData1\n" + " group: testGroup1\n" + " type: text\n" + "- appName: testAppName\n" + " dataId: testData2\n" + " desc: test desc\n" + " group: testGroup2\n" + " type: yaml\n"; private ConfigMetadata.ConfigExportItem item1; private ConfigMetadata.ConfigExportItem item2; @BeforeEach void setUp() { item1 = new ConfigMetadata.ConfigExportItem(); item1.setDataId("testData1"); item1.setGroup("testGroup1"); item1.setType("text"); item2 = new ConfigMetadata.ConfigExportItem(); item2.setDataId("testData2"); item2.setGroup("testGroup2"); item2.setType("yaml"); item2.setAppName("testAppName"); item2.setDesc("test desc"); } @Test void testDumpObject() { ConfigMetadata configMetadata = new ConfigMetadata(); List configMetadataItems = new ArrayList<>(); configMetadataItems.add(item1); configMetadataItems.add(item2); configMetadata.setMetadata(configMetadataItems); String parseString = YamlParserUtil.dumpObject(configMetadata); assertEquals(CONFIG_METADATA_STRING, parseString); } @Test void testLoadObject() { ConfigMetadata configMetadata = YamlParserUtil.loadObject(CONFIG_METADATA_STRING, ConfigMetadata.class); assertNotNull(configMetadata); List metadataList = configMetadata.getMetadata(); assertNotNull(metadataList); assertEquals(2, metadataList.size()); ConfigMetadata.ConfigExportItem configExportItem1 = metadataList.get(0); ConfigMetadata.ConfigExportItem configExportItem2 = metadataList.get(1); assertEquals(configExportItem1, item1); assertEquals(configExportItem2, item2); } @Test void testNotSupportType() { assertThrows(ConstructorException.class, () -> { YamlParserUtil.loadObject("name: test", YamlTest.class); }); } private static class YamlTest { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } } ================================================ FILE: config/src/test/java/com/alibaba/nacos/config/server/utils/ZipUtilsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.config.server.utils; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class ZipUtilsTest { @Test void testZip() { List zipItemList = new ArrayList<>(); zipItemList.add(new ZipUtils.ZipItem("test", "content")); byte[] zip = ZipUtils.zip(zipItemList); assertTrue(zip != null && zip.length > 0); } @Test void testUnzip() { List zipItemList = new ArrayList<>(); zipItemList.add(new ZipUtils.ZipItem("test", "content")); byte[] zip = ZipUtils.zip(zipItemList); assertTrue(zip != null && zip.length > 0); ZipUtils.UnZipResult unZipResult = ZipUtils.unzip(zip); List result = unZipResult.getZipItemList(); assertEquals(zipItemList.size(), result.size()); assertEquals(zipItemList.get(0).getItemName(), result.get(0).getItemName()); assertEquals(zipItemList.get(0).getItemData(), result.get(0).getItemData()); } } ================================================ FILE: config/src/test/resources/application.properties ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # ================================================ FILE: config/src/test/resources/log4j.properties ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # log4j.rootLogger=INFO, ServerDailyRollingFile,stdout log4j.appender.ServerDailyRollingFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.ServerDailyRollingFile.DatePattern='.'yyyy-MM-dd_HH log4j.appender.ServerDailyRollingFile.File=${webapp.root}/WEB-INF/logs/nacos-server.log log4j.appender.ServerDailyRollingFile.layout=org.apache.log4j.PatternLayout log4j.appender.ServerDailyRollingFile.layout.ConversionPattern=[%p] [%t] %d{MM-dd HH:mm:ss,SSS} [%c{1}] - %m%n log4j.appender.ServerDailyRollingFile.Append=true log4j.logger.opLog=INFO, opFile log4j.appender.opFile=org.apache.log4j.DailyRollingFileAppender log4j.appender.opFile.DatePattern='.'yyyy-MM-dd_HH log4j.appender.opFile.File=${webapp.root}/WEB-INF/logs/operation.log log4j.appender.opFile.layout=org.apache.log4j.PatternLayout log4j.appender.opFile.layout.ConversionPattern=[%p] [%t] %d{MM-dd HH:mm:ss,SSS} [%c{1}] - %m%n log4j.appender.opFile.Append=true log4j.logger.com.taobao.config=warn log4j.logger.org.apache.http.wire=warn log4j.logger.java.sql=warn log4j.logger.com.ibatis.common.jdbc=warn ================================================ FILE: config/src/test/resources/logback-test.xml ================================================ ================================================ FILE: config/src/test/resources/user.properties ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # admin=admin ================================================ FILE: consistency/pom.xml ================================================ nacos-consistency kr.motd.maven os-maven-plugin 1.7.1 org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1 com.google.protobuf:protoc:${protobuf-java.version}:exe:${os.detected.classifier} grpc-java io.grpc:protoc-gen-grpc-java:${grpc-java.version}:exe:${os.detected.classifier} compile compile-custom ${project.groupId} nacos-common com.caucho hessian com.google.protobuf protobuf-java 4.0.0 nacos-consistency ${project.version} jar com.alibaba.nacos nacos-all ../pom.xml ${revision} https://nacos.io ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/CommandOperations.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.common.model.RestResult; import com.alibaba.nacos.common.model.RestResultUtils; import java.util.Map; /** * Operation and maintenance command interface. * * @author liaochuntao */ public interface CommandOperations { /** * Operation and maintenance interface operation entry. * * @param commands commands * @return execute success */ default RestResult execute(Map commands) { return RestResultUtils.success(); } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/Config.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import java.io.Serializable; import java.util.Set; /** * Consistent protocol related configuration objects. * *

    {@link RequestProcessor} : The consistency protocol provides services for all businesses, but each business only cares * about the transaction information belonging to that business, and the transaction processing between the various * services should not block each other. Therefore, the LogProcessor is abstracted to implement the parallel processing * of transactions of different services. Corresponding LogProcessor sub-interface: LogProcessor4AP or LogProcessor4CP, * different consistency protocols will actively discover the corresponding LogProcessor * * @author liaochuntao */ public interface Config extends Serializable { /** * Set the cluster node information to initialize,like [ip:port, ip:port, ip:port]. * * @param self local node address information, ip:port * @param members {@link Set} */ void setMembers(String self, Set members); /** * members join. * * @param members {@link Set} */ void addMembers(Set members); /** * members leave. * * @param members {@link Set} */ void removeMembers(Set members); /** * get local node address info. * * @return address */ String getSelfMember(); /** * get the cluster node information. * * @return members info, like [ip:port, ip:port, ip:port] */ Set getMembers(); /** * Add configuration content. * * @param key config key * @param value config value */ void setVal(String key, String value); /** * get configuration content by key. * * @param key config key * @return config value */ String getVal(String key); /** * get configuration content by key, if not found, use default-val. * * @param key config key * @param defaultVal default value * @return config value */ String getValOfDefault(String key, String defaultVal); } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/ConsistencyProtocol.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.Response; import com.alibaba.nacos.consistency.entity.WriteRequest; import java.util.Collection; import java.util.Set; import java.util.concurrent.CompletableFuture; /** * Has nothing to do with the specific implementation of the consistency protocol Initialization sequence: init(Config). * *

      *
    • {@link Config} : Relevant configuration information required by the consistency protocol, * for example, the Raft protocol needs to set the election timeout time, the location where * the Log is stored, and the snapshot task execution interval
    • *
    • {@link ConsistencyProtocol#protocolMetaData()} : Returns metadata information of the consistency * protocol, such as leader, term, and other metadata information in the Raft protocol
    • *
    * * @author liaochuntao */ public interface ConsistencyProtocol extends CommandOperations { /** * Consistency protocol initialization: perform initialization operations based on the incoming. * Config 一致性协议初始化,根据Config 实现类 * * @param config {@link Config} */ void init(T config); /** * Add a request handler. * * @param processors {@link RequestProcessor} */ void addRequestProcessors(Collection

    processors); /** * Copy of metadata information for this consensus protocol. * 该一致性协议的元数据信息 * * @return metaData {@link ProtocolMetaData} */ ProtocolMetaData protocolMetaData(); /** * Obtain data according to the request. * * @param request request * @return data {@link Response} * @throws Exception {@link Exception} */ Response getData(ReadRequest request) throws Exception; /** * Get data asynchronously. * * @param request request * @return data {@link CompletableFuture} */ CompletableFuture aGetData(ReadRequest request); /** * Data operation, returning submission results synchronously. * 同步数据提交,在 Datum 中已携带相应的数据操作信息 * * @param request {@link com.alibaba.nacos.consistency.entity.WriteRequest} * @return submit operation result {@link Response} * @throws Exception {@link Exception} */ Response write(WriteRequest request) throws Exception; /** * Data submission operation, returning submission results asynchronously. * 异步数据提交,在 Datum中已携带相应的数据操作信息,返回一个Future,自行操作,提交发生的异常会在CompleteFuture中 * * @param request {@link com.alibaba.nacos.consistency.entity.WriteRequest} * @return {@link CompletableFuture} submit result * @throws Exception when submit throw Exception */ CompletableFuture writeAsync(WriteRequest request); /** * New member list . * 新的成员节点列表,一致性协议自行处理相应的成员节点是加入还是离开 * * @param addresses [ip:port, ip:port, ...] */ void memberChange(Set addresses); /** * Whether protocol is ready to work, such as contain leader, finish load snapshot and so on. * * @return {@code true} when protocol ready to work, otherwise {@code false} */ boolean isReady(); /** * Consistency agreement service shut down . * 一致性协议服务关闭 */ void shutdown(); } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/DataOperation.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; /** * Apply action. * * @author nkorange */ public enum DataOperation { /** * Data add. */ ADD, /** * Data changed. */ CHANGE, /** * Data deleted. */ DELETE, /** * Data verify. */ VERIFY, /** * Data Snapshot. */ SNAPSHOT, /** * Data query. */ QUERY; } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/IdGenerator.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import java.util.Map; /** * Id generator. * * @author liaochuntao */ public interface IdGenerator { /** * Perform the corresponding initialization operation. */ void init(); /** * current id info. * * @return current id */ long currentId(); /** * worker id info. * * @return worker id */ long workerId(); /** * Get next id. * * @return next id */ long nextId(); /** * Returns information for the current IDGenerator. * * @return {@link Map} */ Map info(); } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/ProtoMessageUtil.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.consistency.entity.GetRequest; import com.alibaba.nacos.consistency.entity.Log; import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.WriteRequest; import com.alibaba.nacos.consistency.exception.ConsistencyException; import com.google.protobuf.Message; /** * protobuf message utils. * * @author liaochuntao */ public class ProtoMessageUtil { /** * should be different from field tags of ReadRequest or WriteQuest. */ public static final int REQUEST_TYPE_FIELD_TAG = 7 << 3; public static final int REQUEST_TYPE_READ = 1; public static final int REQUEST_TYPE_WRITE = 2; /** * Converts the byte array to a specific Protobuf object. * Internally, the protobuf new and old objects are compatible. * * @param bytes An array of bytes * @return Message */ public static Message parse(byte[] bytes) { Message result; try { if (bytes[0] == REQUEST_TYPE_FIELD_TAG) { if (bytes[1] == REQUEST_TYPE_READ) { result = ReadRequest.parseFrom(bytes); } else { result = WriteRequest.parseFrom(bytes); } return result; } } catch (Throwable ignore) { } // old consistency entity, will be @Deprecated in future try { GetRequest request = GetRequest.parseFrom(bytes); return convertToReadRequest(request); } catch (Throwable ignore) { } try { Log log = Log.parseFrom(bytes); return convertToWriteRequest(log); } catch (Throwable ignore) { } throw new ConsistencyException("The current array cannot be serialized to the corresponding object"); } /** * convert Log to WriteRequest. * * @param log log * @return {@link WriteRequest} */ public static WriteRequest convertToWriteRequest(Log log) { return WriteRequest.newBuilder().setKey(log.getKey()).setGroup(log.getGroup()) .setData(log.getData()) .setType(log.getType()) .setOperation(log.getOperation()) .putAllExtendInfo(log.getExtendInfoMap()) .build(); } /** * convert Log to ReadRequest. * * @param request request * @return {@link ReadRequest} */ public static ReadRequest convertToReadRequest(GetRequest request) { return ReadRequest.newBuilder() .setGroup(request.getGroup()) .setData(request.getData()) .putAllExtendInfo(request.getExtendInfoMap()) .build(); } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/ProtocolMetaData.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.common.utils.Observable; import com.alibaba.nacos.common.utils.Observer; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import java.util.Map; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Consistent protocol metadata information, <Key, <Key, Value >> structure Listeners that can register to * listen to changes in value. * * @author liaochuntao */ public final class ProtocolMetaData { private final Map metaDataMap = new ConcurrentHashMap<>(4); /** * used for jackson serialization. * * @return metaMap */ public Map> getMetaDataMap() { return metaDataMap.entrySet().stream().map(entry -> Pair.with(entry.getKey(), entry.getValue().getItemMap().entrySet().stream() .collect(TreeMap::new, (m, e) -> m.put(e.getKey(), e.getValue().getData()), TreeMap::putAll))) .collect(TreeMap::new, (m, e) -> m.put(e.getFirst(), e.getSecond()), TreeMap::putAll); } // Does not guarantee thread safety, there may be two updates of // time-1 and time-2 (time-1 > mapMap) { mapMap.forEach((s, map) -> { metaDataMap.computeIfAbsent(s, MetaData::new); final MetaData data = metaDataMap.get(s); map.forEach(data::put); }); } /** * get protocol metadata by group and key. * * @param group group name * @param subKey key * @return target value */ public Object get(String group, String subKey) { if (StringUtils.isBlank(subKey)) { return metaDataMap.get(group); } else { if (metaDataMap.containsKey(group)) { return metaDataMap.get(group).get(subKey); } return null; } } /** * If MetaData does not exist, actively create a MetaData. */ public void subscribe(final String group, final String key, final Observer observer) { metaDataMap.computeIfAbsent(group, s -> new MetaData(group)).subscribe(key, observer); } public void unSubscribe(final String group, final String key, final Observer observer) { metaDataMap.computeIfAbsent(group, s -> new MetaData(group)).unSubscribe(key, observer); } public static final class MetaData { private final Map itemMap = new ConcurrentHashMap<>(8); private final transient String group; public MetaData(String group) { this.group = group; } public Map getItemMap() { return itemMap; } void put(String key, Object value) { ValueItem item = itemMap.computeIfAbsent(key, s -> new ValueItem(group + "/" + key)); item.setData(value); } public ValueItem get(String key) { return itemMap.get(key); } // If ValueItem does not exist, actively create a ValueItem void subscribe(final String key, final Observer observer) { final ValueItem item = itemMap.computeIfAbsent(key, s -> new ValueItem(group + "/" + key)); item.addObserver(observer); } void unSubscribe(final String key, final Observer observer) { final ValueItem item = itemMap.get(key); if (item == null) { return; } item.deleteObserver(observer); } } public static final class ValueItem extends Observable { private final transient String path; private final transient ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); private final transient ReentrantReadWriteLock.ReadLock readLock = lock.readLock(); private final transient ReentrantReadWriteLock.WriteLock writeLock = lock.writeLock(); private volatile Object data; public ValueItem(String path) { this.path = path; } public Object getData() { readLock.lock(); try { return data; } finally { readLock.unlock(); } } void setData(Object data) { writeLock.lock(); try { this.data = data; setChanged(); notifyObservers(); } finally { writeLock.unlock(); } } public String getPath() { return path; } } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/RequestProcessor.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.Response; import com.alibaba.nacos.consistency.entity.WriteRequest; /** * Can be discovered through SPI or Spring, This interface is just a function definition interface. Different * consistency protocols have their pwd * LogDispatcher. It is not recommended to directly implement this interface. * * @author liaochuntao */ public abstract class RequestProcessor { /** * get data by key. * * @param request request {@link com.alibaba.nacos.consistency.entity.ReadRequest} * @return target type data */ public abstract Response onRequest(ReadRequest request); /** * Process Submitted Log. * * @param log {@link WriteRequest} * @return {@link boolean} */ public abstract Response onApply(WriteRequest log); /** * Irremediable errors that need to trigger business price cuts. * * @param error {@link Throwable} */ public void onError(Throwable error) { } /** * In order for the state machine that handles the transaction to be able to route the Log to the correct * LogProcessor, the LogProcessor needs to have an identity information. * * @return Business unique identification name */ public abstract String group(); } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/SerializeFactory.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.common.spi.NacosServiceLoader; import com.alibaba.nacos.consistency.serialize.HessianSerializer; import java.util.HashMap; import java.util.Map; /** * Serialization factory. * * @author liaochuntao */ public class SerializeFactory { public static final String HESSIAN_INDEX = "Hessian".toLowerCase(); private static final Map SERIALIZER_MAP = new HashMap<>(4); public static final String DEFAULT_SERIALIZER = HESSIAN_INDEX; static { Serializer serializer = new HessianSerializer(); SERIALIZER_MAP.put(HESSIAN_INDEX, serializer); for (Serializer item : NacosServiceLoader.load(Serializer.class)) { SERIALIZER_MAP.put(item.name().toLowerCase(), item); } } public static Serializer getDefault() { return SERIALIZER_MAP.get(DEFAULT_SERIALIZER); } public static Serializer getSerializer(String type) { return SERIALIZER_MAP.get(type.toLowerCase()); } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/Serializer.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import java.lang.reflect.Type; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Serialization interface. * * @author liaochuntao */ public interface Serializer { Map> CLASS_CACHE = new ConcurrentHashMap<>(8); /** * Deserialize the data. * * @param data byte[] * @param class type * @return target object instance */ T deserialize(byte[] data); /** * Deserialize the data. * * @param data byte[] * @param cls class * @param class type * @return target object instance */ T deserialize(byte[] data, Class cls); /** * Deserialize the data. * * @param data byte[] * @param type data type * @param class type * @return target object instance */ T deserialize(byte[] data, Type type); /** * Deserialize the data. * * @param data byte[] * @param classFullName class full name * @param class type * @return target object instance */ default T deserialize(byte[] data, String classFullName) { try { Class cls = CLASS_CACHE.computeIfAbsent(classFullName, name -> { try { return Class.forName(classFullName); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } }); return (T) deserialize(data, cls); } catch (Exception ignore) { return null; } } /** * Serialize the object. * * @param obj target obj * @return byte[] */ byte[] serialize(T obj); /** * The name of the serializer implementer. * * @return name */ String name(); } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/ap/APProtocol.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.ap; import com.alibaba.nacos.consistency.Config; import com.alibaba.nacos.consistency.ConsistencyProtocol; /** * ap protocol. * * @author liaochuntao */ @SuppressWarnings("all") public interface APProtocol extends ConsistencyProtocol { } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/ap/RequestProcessor4AP.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.ap; import com.alibaba.nacos.consistency.RequestProcessor; /** * log processor for ap. * * @author liaochuntao */ @SuppressWarnings("all") public abstract class RequestProcessor4AP extends RequestProcessor { } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/cp/CPProtocol.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.cp; import com.alibaba.nacos.consistency.Config; import com.alibaba.nacos.consistency.ConsistencyProtocol; /** * cp protocol. * * @author liaochuntao */ @SuppressWarnings("all") public interface CPProtocol extends ConsistencyProtocol { /** * Returns whether this node is a leader node * * @param group business module info * @return is leader */ boolean isLeader(String group); } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/cp/MetadataKey.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.cp; /** * Key value of metadata information of CP protocol. * * @author liaochuntao */ public class MetadataKey { public static final String LEADER_META_DATA = "leader"; public static final String TERM_META_DATA = "term"; public static final String RAFT_GROUP_MEMBER = "raftGroupMember"; public static final String ERR_MSG = "errMsg"; } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/cp/RequestProcessor4CP.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.cp; import com.alibaba.nacos.consistency.RequestProcessor; import com.alibaba.nacos.consistency.snapshot.SnapshotOperation; import java.util.Collections; import java.util.List; /** * log processor for cp. * * @author liaochuntao */ @SuppressWarnings("all") public abstract class RequestProcessor4CP extends RequestProcessor { /** * Discovery snapshot handler It is up to LogProcessor to decide which SnapshotOperate should be loaded and saved by * itself. * * @return {@link List } */ public List loadSnapshotOperate() { return Collections.emptyList(); } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/exception/ConsistencyException.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.exception; /** * Conformance protocol internal exceptions. * * @author liaochuntao */ public class ConsistencyException extends RuntimeException { private static final long serialVersionUID = 1935132712388069418L; public ConsistencyException() { super(); } public ConsistencyException(String message) { super(message); } public ConsistencyException(String message, Throwable cause) { super(message, cause); } public ConsistencyException(Throwable cause) { super(cause); } protected ConsistencyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/serialize/HessianSerializer.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.serialize; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import com.alibaba.nacos.common.utils.ByteUtils; import com.alibaba.nacos.consistency.Serializer; import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; import com.caucho.hessian.io.SerializerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Type; /** * Serializer implement by hessian. * * @author liaochuntao */ @SuppressWarnings("all") public class HessianSerializer implements Serializer { private static final String NAME = "Hessian"; private SerializerFactory serializerFactory = new NacosHessianSerializerFactory(); public HessianSerializer() { } @Override public T deserialize(byte[] data) { return deseiralize0(data); } @Override public T deserialize(byte[] data, Class cls) { T result = deserialize(data); if (result == null) { return null; } if (cls.isAssignableFrom(result.getClass())) { return result; } throw new NacosDeserializationException(cls, new ClassCastException( "%s cannot be cast to %s".format(result.getClass().getCanonicalName(), cls.getCanonicalName()))); } @Override public T deserialize(byte[] data, Type type) { return deserialize(data); } private T deseiralize0(byte[] data) { if (ByteUtils.isEmpty(data)) { return null; } Hessian2Input input = new Hessian2Input(new ByteArrayInputStream(data)); input.setSerializerFactory(serializerFactory); Object resultObject; try { resultObject = input.readObject(); input.close(); } catch (IOException e) { throw new RuntimeException("IOException occurred when Hessian serializer decode!", e); } return (T) resultObject; } @Override public byte[] serialize(T obj) { ByteArrayOutputStream byteArray = new ByteArrayOutputStream(); Hessian2Output output = new Hessian2Output(byteArray); output.setSerializerFactory(serializerFactory); try { output.writeObject(obj); output.close(); } catch (IOException e) { throw new RuntimeException("IOException occurred when Hessian serializer encode!", e); } return byteArray.toByteArray(); } @Override public String name() { return NAME; } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/serialize/JacksonSerializer.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.serialize; import com.alibaba.nacos.common.utils.ByteUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.consistency.Serializer; import java.lang.reflect.Type; /** * Serializer implement by jackson. * * @author xiweng.yy */ public class JacksonSerializer implements Serializer { private static final String NAME = "JSON"; @Override public T deserialize(byte[] data) { throw new UnsupportedOperationException("Jackson serializer can't support deserialize json without type"); } @Override public T deserialize(byte[] data, Class cls) { if (ByteUtils.isEmpty(data)) { return null; } return JacksonUtils.toObj(data, cls); } @Override public T deserialize(byte[] data, Type type) { if (ByteUtils.isEmpty(data)) { return null; } return JacksonUtils.toObj(data, type); } @Override public byte[] serialize(T obj) { return JacksonUtils.toJsonBytes(obj); } @Override public String name() { return NAME; } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/serialize/NacosHessianSerializerFactory.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.serialize; import com.caucho.hessian.io.SerializerFactory; import java.text.SimpleDateFormat; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; 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.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicReference; /** * Nacos Hessian Serializer Factory. * * @author xiweng.yy */ public class NacosHessianSerializerFactory extends SerializerFactory { NacosHessianSerializerFactory() { super(); super.getClassFactory().setWhitelist(true); allowBasicType(); allowCollections(); allowConcurrent(); allowTime(); super.getClassFactory().allow("com.alibaba.nacos.*"); } private void allowBasicType() { super.getClassFactory().allow(boolean.class.getCanonicalName()); super.getClassFactory().allow(byte.class.getCanonicalName()); super.getClassFactory().allow(char.class.getCanonicalName()); super.getClassFactory().allow(double.class.getCanonicalName()); super.getClassFactory().allow(float.class.getCanonicalName()); super.getClassFactory().allow(int.class.getCanonicalName()); super.getClassFactory().allow(long.class.getCanonicalName()); super.getClassFactory().allow(short.class.getCanonicalName()); super.getClassFactory().allow(Boolean.class.getCanonicalName()); super.getClassFactory().allow(Byte.class.getCanonicalName()); super.getClassFactory().allow(Character.class.getCanonicalName()); super.getClassFactory().allow(Double.class.getCanonicalName()); super.getClassFactory().allow(Float.class.getCanonicalName()); super.getClassFactory().allow(Integer.class.getCanonicalName()); super.getClassFactory().allow(Long.class.getCanonicalName()); super.getClassFactory().allow(Short.class.getCanonicalName()); super.getClassFactory().allow(Number.class.getCanonicalName()); super.getClassFactory().allow(Class.class.getCanonicalName()); super.getClassFactory().allow(String.class.getCanonicalName()); } private void allowCollections() { super.getClassFactory().allow(List.class.getCanonicalName()); super.getClassFactory().allow(ArrayList.class.getCanonicalName()); super.getClassFactory().allow(LinkedList.class.getCanonicalName()); super.getClassFactory().allow(Set.class.getCanonicalName()); super.getClassFactory().allow(HashSet.class.getCanonicalName()); super.getClassFactory().allow(LinkedHashSet.class.getCanonicalName()); super.getClassFactory().allow(TreeSet.class.getCanonicalName()); super.getClassFactory().allow(Map.class.getCanonicalName()); super.getClassFactory().allow(HashMap.class.getCanonicalName()); super.getClassFactory().allow(LinkedHashMap.class.getCanonicalName()); super.getClassFactory().allow(TreeMap.class.getCanonicalName()); super.getClassFactory().allow(WeakHashMap.class.getCanonicalName()); super.getClassFactory().allow("java.util.Arrays$ArrayList"); super.getClassFactory().allow("java.util.Collections$EmptyList"); super.getClassFactory().allow("java.util.Collections$EmptyMap"); super.getClassFactory().allow("java.util.Collections$SingletonSet"); super.getClassFactory().allow("java.util.Collections$SingletonList"); super.getClassFactory().allow("java.util.Collections$UnmodifiableCollection"); super.getClassFactory().allow("java.util.Collections$UnmodifiableList"); super.getClassFactory().allow("java.util.Collections$UnmodifiableMap"); super.getClassFactory().allow("java.util.Collections$UnmodifiableNavigableMap"); super.getClassFactory().allow("java.util.Collections$UnmodifiableNavigableSet"); super.getClassFactory().allow("java.util.Collections$UnmodifiableRandomAccessList"); super.getClassFactory().allow("java.util.Collections$UnmodifiableSet"); super.getClassFactory().allow("java.util.Collections$UnmodifiableSortedMap"); super.getClassFactory().allow("java.util.Collections$UnmodifiableSortedSet"); } private void allowConcurrent() { super.getClassFactory().allow(AtomicBoolean.class.getCanonicalName()); super.getClassFactory().allow(AtomicInteger.class.getCanonicalName()); super.getClassFactory().allow(AtomicLong.class.getCanonicalName()); super.getClassFactory().allow(AtomicReference.class.getCanonicalName()); super.getClassFactory().allow(ConcurrentMap.class.getCanonicalName()); super.getClassFactory().allow(ConcurrentHashMap.class.getCanonicalName()); super.getClassFactory().allow(ConcurrentSkipListMap.class.getCanonicalName()); super.getClassFactory().allow(CopyOnWriteArrayList.class.getCanonicalName()); } private void allowTime() { super.getClassFactory().allow(SimpleDateFormat.class.getCanonicalName()); super.getClassFactory().allow(DateTimeFormatter.class.getCanonicalName()); super.getClassFactory().allow(Instant.class.getCanonicalName()); super.getClassFactory().allow(LocalDate.class.getCanonicalName()); super.getClassFactory().allow(LocalDateTime.class.getCanonicalName()); super.getClassFactory().allow(LocalTime.class.getCanonicalName()); super.getClassFactory().allow(TimeUnit.class.getCanonicalName()); super.getClassFactory().allow(Date.class.getCanonicalName()); super.getClassFactory().allow(Calendar.class.getCanonicalName()); } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/snapshot/LocalFileMeta.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.snapshot; import java.util.Properties; /** * Meta information for the snapshot file. * * @author liaochuntao */ public class LocalFileMeta { private final Properties fileMeta; public LocalFileMeta() { this.fileMeta = new Properties(); } public LocalFileMeta(Properties properties) { this.fileMeta = properties; } public LocalFileMeta append(Object key, Object value) { fileMeta.put(key, value); return this; } public Object get(String key) { return fileMeta.getProperty(key); } public Properties getFileMeta() { return fileMeta; } @Override public String toString() { return "LocalFileMeta{" + "fileMeta=" + fileMeta + '}'; } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/snapshot/Reader.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.snapshot; import java.util.Collections; import java.util.Map; /** * Read the snapshot file interface. * * @author liaochuntao */ public class Reader { private final String path; private final Map allFiles; public Reader(String path, Map allFiles) { this.path = path; this.allFiles = Collections.unmodifiableMap(allFiles); } public String getPath() { return path; } public Map listFiles() { return allFiles; } public LocalFileMeta getFileMeta(String fileName) { return allFiles.get(fileName); } } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/snapshot/SnapshotOperation.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.snapshot; import java.util.function.BiConsumer; /** * Custom snapshot operation interface Discovery via SPI. * * @author liaochuntao */ public interface SnapshotOperation { /** * do snapshot save operation. * * @param writer {@link Writer} * @param callFinally Callback {@link BiConsumer} when the snapshot operation is complete */ void onSnapshotSave(Writer writer, BiConsumer callFinally); /** * do snapshot load operation. * * @param reader {@link Reader} * @return operation label */ boolean onSnapshotLoad(Reader reader); } ================================================ FILE: consistency/src/main/java/com/alibaba/nacos/consistency/snapshot/Writer.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.snapshot; import java.util.Collections; import java.util.HashMap; import java.util.Map; /** * Snapshot write interface. * * @author liaochuntao */ public class Writer { private final Map files = new HashMap<>(); private String path; public Writer(String path) { this.path = path; } public String getPath() { return path; } /** * Adds a snapshot file without metadata. * * @param fileName file name * @return true on success */ public boolean addFile(final String fileName) { files.put(fileName, new LocalFileMeta().append("file-name", fileName)); return true; } /** * Adds a snapshot file with metadata. * * @param fileName file name * @return true on success */ public boolean addFile(final String fileName, final LocalFileMeta meta) { files.put(fileName, meta); return true; } /** * Remove a snapshot file. * * @param fileName file name * @return true on success */ public boolean removeFile(final String fileName) { files.remove(fileName); return true; } public Map listFiles() { return Collections.unmodifiableMap(files); } } ================================================ FILE: consistency/src/main/proto/Data.proto ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 = "com.alibaba.nacos.consistency.entity"; //Deprecated message Log { string group = 1; string key = 2; bytes data = 3; string type = 4; string operation = 5; map extendInfo = 6; } //Deprecated message GetRequest { string group = 1; bytes data = 2; map extendInfo = 3; } ================================================ FILE: consistency/src/main/proto/consistency.proto ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 = "com.alibaba.nacos.consistency.entity"; message WriteRequest { string group = 1; string key = 2; bytes data = 3; string type = 4; string operation = 5; map extendInfo = 6; } message ReadRequest { string group = 1; bytes data = 2; map extendInfo = 3; } message Response { bytes data = 1; string errMsg = 2; bool success = 3; } ================================================ FILE: consistency/src/main/resources/META-INF/services/com.alibaba.nacos.consistency.Serializer ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.consistency.serialize.JacksonSerializer ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/CommandOperationsTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.common.model.RestResult; import org.junit.jupiter.api.Test; import java.util.Collections; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class CommandOperationsTest { @Test void testExecuteDefault() { CommandOperations ops = new CommandOperations() { }; RestResult result = ops.execute(Collections.emptyMap()); assertTrue(result.ok()); assertEquals(200, result.getCode()); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/DataOperationTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class DataOperationTest { @Test void testValues() { DataOperation[] values = DataOperation.values(); assertEquals(6, values.length); } @Test void testValueOf() { assertEquals(DataOperation.ADD, DataOperation.valueOf("ADD")); assertEquals(DataOperation.CHANGE, DataOperation.valueOf("CHANGE")); assertEquals(DataOperation.DELETE, DataOperation.valueOf("DELETE")); assertEquals(DataOperation.VERIFY, DataOperation.valueOf("VERIFY")); assertEquals(DataOperation.SNAPSHOT, DataOperation.valueOf("SNAPSHOT")); assertEquals(DataOperation.QUERY, DataOperation.valueOf("QUERY")); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/ProtoMessageUtilTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.consistency.entity.GetRequest; import com.alibaba.nacos.consistency.entity.Log; import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.WriteRequest; import com.google.protobuf.ByteString; import org.junit.jupiter.api.Test; import java.nio.ByteBuffer; 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 ProtoMessageUtilTest { @Test void testConstructor() { new ProtoMessageUtil(); } @Test void testParseInvalidBytes() { byte[] invalidBytes = new byte[]{1, 2, 3}; try { ProtoMessageUtil.parse(invalidBytes); fail("Should throw ConsistencyException"); } catch (Exception e) { assertTrue(e instanceof com.alibaba.nacos.consistency.exception.ConsistencyException); } } @Test void testParseWithCorruptRequestTypeField() { byte[] corruptBytes = new byte[]{(byte) ProtoMessageUtil.REQUEST_TYPE_FIELD_TAG, (byte) ProtoMessageUtil.REQUEST_TYPE_READ, (byte) 0x80}; try { ProtoMessageUtil.parse(corruptBytes); } catch (Exception ignored) { } } @Test void testProto() throws Exception { WriteRequest request = WriteRequest.newBuilder().setKey("test-proto-new").build(); byte[] bytes = request.toByteArray(); Log log = Log.parseFrom(bytes); assertEquals(request.getKey(), log.getKey()); } @Test void testParseReadRequestWithRequestTypeField() { String group = "test"; ByteString data = ByteString.copyFrom("data".getBytes()); ReadRequest testCase = ReadRequest.newBuilder().setGroup(group).setData(data).build(); byte[] requestTypeFieldBytes = new byte[2]; requestTypeFieldBytes[0] = ProtoMessageUtil.REQUEST_TYPE_FIELD_TAG; requestTypeFieldBytes[1] = ProtoMessageUtil.REQUEST_TYPE_READ; byte[] dataBytes = testCase.toByteArray(); ByteBuffer byteBuffer = (ByteBuffer) ByteBuffer.allocate(requestTypeFieldBytes.length + dataBytes.length).put(requestTypeFieldBytes) .put(dataBytes).position(0); Object actual = ProtoMessageUtil.parse(byteBuffer.array()); assertEquals(ReadRequest.class, testCase.getClass()); assertEquals(group, ((ReadRequest) actual).getGroup()); assertEquals(data, ((ReadRequest) actual).getData()); } @Test void testParseWriteRequestWithRequestTypeField() { String group = "test"; ByteString data = ByteString.copyFrom("data".getBytes()); WriteRequest testCase = WriteRequest.newBuilder().setGroup(group).setData(data).build(); byte[] requestTypeFieldBytes = new byte[2]; requestTypeFieldBytes[0] = ProtoMessageUtil.REQUEST_TYPE_FIELD_TAG; requestTypeFieldBytes[1] = ProtoMessageUtil.REQUEST_TYPE_WRITE; byte[] dataBytes = testCase.toByteArray(); ByteBuffer byteBuffer = (ByteBuffer) ByteBuffer.allocate(requestTypeFieldBytes.length + dataBytes.length).put(requestTypeFieldBytes) .put(dataBytes).position(0); Object actual = ProtoMessageUtil.parse(byteBuffer.array()); assertEquals(WriteRequest.class, testCase.getClass()); assertEquals(group, ((WriteRequest) actual).getGroup()); assertEquals(data, ((WriteRequest) actual).getData()); } @Test void testParseReadRequest() { String group = "test"; ByteString data = ByteString.copyFrom("data".getBytes()); ReadRequest testCase = ReadRequest.newBuilder().setGroup(group).setData(data).build(); Object actual = ProtoMessageUtil.parse(testCase.toByteArray()); assertEquals(ReadRequest.class, testCase.getClass()); assertEquals(group, ((ReadRequest) actual).getGroup()); assertEquals(data, ((ReadRequest) actual).getData()); } @Test void testParseWriteRequest() { String group = "test"; ByteString data = ByteString.copyFrom("data".getBytes()); WriteRequest testCase = WriteRequest.newBuilder().setGroup(group).setData(data).build(); Object actual = ProtoMessageUtil.parse(testCase.toByteArray()); assertEquals(WriteRequest.class, testCase.getClass()); assertEquals(group, ((WriteRequest) actual).getGroup()); assertEquals(data, ((WriteRequest) actual).getData()); } @Test void testConvertToReadRequest() { ByteString data = ByteString.copyFrom("data".getBytes()); String group = "test"; GetRequest getRequest = GetRequest.newBuilder().setGroup(group).setData(data).putExtendInfo("k", "v").build(); ReadRequest readRequest = ProtoMessageUtil.convertToReadRequest(getRequest); assertEquals(group, readRequest.getGroup()); assertEquals(data, readRequest.getData()); assertEquals(1, readRequest.getExtendInfoCount()); } @Test void testConvertToWriteRequest() { ByteString data = ByteString.copyFrom("data".getBytes()); Log log = Log.newBuilder().setKey("key").setGroup("group").setData(data).setOperation("o").putExtendInfo("k", "v").build(); WriteRequest writeRequest = ProtoMessageUtil.convertToWriteRequest(log); assertEquals(1, writeRequest.getExtendInfoCount()); assertEquals(data, writeRequest.getData()); assertEquals("key", writeRequest.getKey()); assertEquals("group", writeRequest.getGroup()); assertEquals("o", writeRequest.getOperation()); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/ProtocolMetaDataTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.Observer; import org.junit.jupiter.api.Test; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; 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 ProtocolMetaDataTest { @Test void testProtocolMetaData() throws Exception { Map> map = new HashMap<>(); Map data = new HashMap<>(); data.put("test-1", new Date()); data.put("test_2", new Date()); map.put("global", data); ProtocolMetaData metaData = new ProtocolMetaData(); metaData.load(map); String json = JacksonUtils.toJson(metaData); AtomicInteger count = new AtomicInteger(0); CountDownLatch latch = new CountDownLatch(2); metaData.subscribe("global", "test-1", o -> { ProtocolMetaData.ValueItem item = (ProtocolMetaData.ValueItem) o; System.out.println(item.getData()); count.incrementAndGet(); latch.countDown(); }); map = new HashMap<>(); data = new HashMap<>(); data.put("test-1", new Date()); data.put("test_2", new Date()); map.put("global", data); metaData.load(map); latch.await(10_000L, TimeUnit.MILLISECONDS); assertEquals(2, count.get()); } @Test void testGetWithBlankSubKey() { ProtocolMetaData metaData = new ProtocolMetaData(); Map> map = new HashMap<>(); Map data = new HashMap<>(); data.put("key1", "value1"); map.put("group1", data); metaData.load(map); Object result = metaData.get("group1", ""); assertNotNull(result); } @Test void testGetWithSubKey() { ProtocolMetaData metaData = new ProtocolMetaData(); Map> map = new HashMap<>(); Map data = new HashMap<>(); data.put("key1", "value1"); map.put("group1", data); metaData.load(map); Object result = metaData.get("group1", "key1"); assertNotNull(result); } @Test void testGetWithNonExistGroup() { ProtocolMetaData metaData = new ProtocolMetaData(); Object result = metaData.get("nonExist", "key1"); assertNull(result); } @Test void testUnSubscribe() { ProtocolMetaData metaData = new ProtocolMetaData(); Map> map = new HashMap<>(); Map data = new HashMap<>(); data.put("key1", "value1"); map.put("group1", data); metaData.load(map); Observer observer = o -> { }; metaData.subscribe("group1", "key1", observer); metaData.unSubscribe("group1", "key1", observer); } @Test void testUnSubscribeNonExistKey() { ProtocolMetaData metaData = new ProtocolMetaData(); Observer observer = o -> { }; metaData.unSubscribe("newGroup", "nonExistKey", observer); } @Test void testGetMetaDataMap() { ProtocolMetaData metaData = new ProtocolMetaData(); Map> map = new HashMap<>(); Map data = new HashMap<>(); data.put("key1", "value1"); map.put("group1", data); metaData.load(map); Map> result = metaData.getMetaDataMap(); assertNotNull(result); assertEquals(1, result.size()); } @Test void testValueItemGetPath() { ProtocolMetaData.ValueItem item = new ProtocolMetaData.ValueItem("test/path"); assertEquals("test/path", item.getPath()); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/RequestProcessorTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.Response; import com.alibaba.nacos.consistency.entity.WriteRequest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; class RequestProcessorTest { private static final String TEST_GROUP = "test-group"; @Test void testOnError() { ConcreteRequestProcessor processor = new ConcreteRequestProcessor(); processor.onError(new RuntimeException("test error")); // Default empty impl - should not throw } @Test void testGroup() { ConcreteRequestProcessor processor = new ConcreteRequestProcessor(); assertEquals(TEST_GROUP, processor.group()); } @Test void testOnRequest() { ConcreteRequestProcessor processor = new ConcreteRequestProcessor(); Response response = processor.onRequest(ReadRequest.getDefaultInstance()); assertNotNull(response); } @Test void testOnApply() { ConcreteRequestProcessor processor = new ConcreteRequestProcessor(); Response response = processor.onApply(WriteRequest.getDefaultInstance()); assertNotNull(response); } private static class ConcreteRequestProcessor extends RequestProcessor { @Override public Response onRequest(ReadRequest request) { return Response.getDefaultInstance(); } @Override public Response onApply(WriteRequest log) { return Response.getDefaultInstance(); } @Override public String group() { return TEST_GROUP; } } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/SerializeFactoryTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency; import com.alibaba.nacos.consistency.serialize.JacksonSerializer; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertTrue; class SerializeFactoryTest { @Test void testConstructor() { new SerializeFactory(); } @Test void testListSerialize() { Serializer serializer = SerializeFactory.getDefault(); List logsList = new ArrayList<>(); for (int i = 0; i < 4; i++) { logsList.add(i); } byte[] data = serializer.serialize(logsList); assertNotEquals(0, data.length); ArrayList list = serializer.deserialize(data, ArrayList.class); System.out.println(list); } @Test void testMapSerialize() { Serializer serializer = SerializeFactory.getDefault(); Map logsMap = new HashMap<>(); for (int i = 0; i < 4; i++) { logsMap.put(i, i); } byte[] data = serializer.serialize(logsMap); assertNotEquals(0, data.length); Map result = serializer.deserialize(data, HashMap.class); System.out.println(result); } @Test void testSetSerialize() { Serializer serializer = SerializeFactory.getDefault(); Set logsMap = new CopyOnWriteArraySet<>(); for (int i = 0; i < 4; i++) { logsMap.add(i); } byte[] data = serializer.serialize(logsMap); assertNotEquals(0, data.length); Set result = serializer.deserialize(data, CopyOnWriteArraySet.class); System.out.println(result); } @Test void testGetSerializer() { Serializer serializer = SerializeFactory.getSerializer("JSON"); assertTrue(serializer instanceof JacksonSerializer); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/ap/RequestProcessor4APTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.ap; import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.Response; import com.alibaba.nacos.consistency.entity.WriteRequest; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @SuppressWarnings("checkstyle:AbbreviationAsWordInName") class RequestProcessor4APTest { private static final String TEST_GROUP = "test-ap-group"; @Test void testConcreteSubclassWorks() { ConcreteRequestProcessor4AP processor = new ConcreteRequestProcessor4AP(); assertNotNull(processor); } @Test void testOnRequest() { ConcreteRequestProcessor4AP processor = new ConcreteRequestProcessor4AP(); Response response = processor.onRequest(ReadRequest.getDefaultInstance()); assertNotNull(response); } @Test void testOnApply() { ConcreteRequestProcessor4AP processor = new ConcreteRequestProcessor4AP(); Response response = processor.onApply(WriteRequest.getDefaultInstance()); assertNotNull(response); } @Test void testGroup() { ConcreteRequestProcessor4AP processor = new ConcreteRequestProcessor4AP(); assertEquals(TEST_GROUP, processor.group()); } private static class ConcreteRequestProcessor4AP extends RequestProcessor4AP { @Override public Response onRequest(ReadRequest request) { return Response.getDefaultInstance(); } @Override public Response onApply(WriteRequest log) { return Response.getDefaultInstance(); } @Override public String group() { return TEST_GROUP; } } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/cp/MetadataKeyTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.cp; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; class MetadataKeyTest { @Test void testConstants() { new MetadataKey(); assertEquals("leader", MetadataKey.LEADER_META_DATA); assertEquals("term", MetadataKey.TERM_META_DATA); assertEquals("raftGroupMember", MetadataKey.RAFT_GROUP_MEMBER); assertEquals("errMsg", MetadataKey.ERR_MSG); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/cp/RequestProcessor4CPTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.cp; import com.alibaba.nacos.consistency.entity.ReadRequest; import com.alibaba.nacos.consistency.entity.Response; import com.alibaba.nacos.consistency.entity.WriteRequest; import com.alibaba.nacos.consistency.snapshot.SnapshotOperation; import org.junit.jupiter.api.Test; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @SuppressWarnings("checkstyle:AbbreviationAsWordInName") class RequestProcessor4CPTest { private static final String TEST_GROUP = "test-cp-group"; @Test void testLoadSnapshotOperate() { ConcreteRequestProcessor4CP processor = new ConcreteRequestProcessor4CP(); List operations = processor.loadSnapshotOperate(); assertNotNull(operations); assertTrue(operations.isEmpty()); } @Test void testOnError() { ConcreteRequestProcessor4CP processor = new ConcreteRequestProcessor4CP(); processor.onError(new RuntimeException("test error")); // Inherited default onError - should not throw } @Test void testOnRequest() { ConcreteRequestProcessor4CP processor = new ConcreteRequestProcessor4CP(); Response response = processor.onRequest(ReadRequest.getDefaultInstance()); assertNotNull(response); } @Test void testOnApply() { ConcreteRequestProcessor4CP processor = new ConcreteRequestProcessor4CP(); Response response = processor.onApply(WriteRequest.getDefaultInstance()); assertNotNull(response); } @Test void testGroup() { ConcreteRequestProcessor4CP processor = new ConcreteRequestProcessor4CP(); assertEquals(TEST_GROUP, processor.group()); } private static class ConcreteRequestProcessor4CP extends RequestProcessor4CP { @Override public Response onRequest(ReadRequest request) { return Response.getDefaultInstance(); } @Override public Response onApply(WriteRequest log) { return Response.getDefaultInstance(); } @Override public String group() { return TEST_GROUP; } } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/exception/ConsistencyExceptionTest.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.exception; 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; class ConsistencyExceptionTest { @Test void testDefaultConstructor() { ConsistencyException ex = new ConsistencyException(); assertNull(ex.getMessage()); assertNull(ex.getCause()); } @Test void testMessageConstructor() { String message = "test message"; ConsistencyException ex = new ConsistencyException(message); assertEquals(message, ex.getMessage()); assertNull(ex.getCause()); } @Test void testMessageAndCauseConstructor() { String message = "test message"; Throwable cause = new IllegalArgumentException("cause"); ConsistencyException ex = new ConsistencyException(message, cause); assertEquals(message, ex.getMessage()); assertEquals(cause, ex.getCause()); } @Test void testCauseConstructor() { Throwable cause = new IllegalArgumentException("cause"); ConsistencyException ex = new ConsistencyException(cause); assertEquals(cause, ex.getCause()); assertNotNull(ex.getMessage()); } @Test void testProtectedConstructorViaAnonymousSubclass() { String message = "protected message"; Throwable cause = new RuntimeException("cause"); ConsistencyException ex = new ConsistencyException(message, cause, true, true) { }; assertEquals(message, ex.getMessage()); assertEquals(cause, ex.getCause()); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/serialize/HessianSerializerTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.serialize; import com.alibaba.nacos.api.exception.runtime.NacosDeserializationException; import org.apache.hc.core5.http.HttpException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.io.Serializable; 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; /** * {@link HessianSerializer} unit test. * * @author Chenhao26 * @date 2022-08-13 */ class HessianSerializerTest { private HessianSerializer hessianSerializer; @BeforeEach void setUp() { hessianSerializer = new HessianSerializer(); } @Test void testSerializerAndDeserialize() { String data = "xxx"; byte[] bytes = hessianSerializer.serialize(data); try { hessianSerializer.deserialize(bytes); } catch (Exception e) { assertTrue(e instanceof RuntimeException); } String res1 = hessianSerializer.deserialize(bytes, String.class); assertEquals(data, res1); String res2 = hessianSerializer.deserialize(bytes, "java.lang.String"); assertEquals(data, res2); } @Test void testSerializerAndDeserializeForNotAllowClass() { Serializable data = new HttpException(); byte[] bytes = hessianSerializer.serialize(data); try { HttpException res = hessianSerializer.deserialize(bytes); fail("deserialize success which is not expected"); } catch (Exception e) { assertTrue(e instanceof ClassCastException); } try { HttpException res1 = hessianSerializer.deserialize(bytes, HttpException.class); } catch (Exception e) { assertTrue(e instanceof NacosDeserializationException); } } @Test void testName() { assertEquals("Hessian", hessianSerializer.name()); } @Test void testDeserializeWithEmptyData() { assertNull(hessianSerializer.deserialize(new byte[0], String.class)); } @Test void testDeserializeWithType() { String data = "testType"; byte[] bytes = hessianSerializer.serialize(data); String result = hessianSerializer.deserialize(bytes, (java.lang.reflect.Type) String.class); assertEquals(data, result); } @Test void testDeserializeWithNullData() { assertNull(hessianSerializer.deserialize(null, String.class)); } @Test void testDeserializeWithInvalidData() { byte[] validBytes = hessianSerializer.serialize("test"); byte[] invalidBytes = new byte[validBytes.length]; System.arraycopy(validBytes, 0, invalidBytes, 0, validBytes.length); invalidBytes[validBytes.length / 2] = (byte) 0xff; try { hessianSerializer.deserialize(invalidBytes); fail("Should throw RuntimeException"); } catch (RuntimeException e) { assertTrue(e.getMessage().contains("IOException")); } } @Test void testDeserializeWithInvalidClassName() { byte[] bytes = hessianSerializer.serialize("test"); Object result = hessianSerializer.deserialize(bytes, "com.nonexistent.ClassName"); assertNull(result); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/serialize/JacksonSerializerTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.serialize; 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.assertTrue; /** * {@link JacksonSerializer} unit test. * * @author chenglu * @date 2021-07-27 18:32 */ class JacksonSerializerTest { private JacksonSerializer jacksonSerializer; @BeforeEach void setUp() { jacksonSerializer = new JacksonSerializer(); } @Test void testSerializerAndDeserialize() { String data = "xxx"; byte[] bytes = jacksonSerializer.serialize(data); try { jacksonSerializer.deserialize(bytes); } catch (Exception e) { assertTrue(e instanceof UnsupportedOperationException); } String res1 = jacksonSerializer.deserialize(bytes, String.class); assertEquals(data, res1); String res2 = jacksonSerializer.deserialize(bytes, "java.lang.String"); assertEquals(data, res2); } @Test void testName() { assertEquals("JSON", jacksonSerializer.name()); } @Test void testDeserializeWithClassAndEmptyData() { assertNull(jacksonSerializer.deserialize(new byte[0], String.class)); } @Test void testDeserializeWithTypeAndEmptyData() { assertNull(jacksonSerializer.deserialize(new byte[0], (java.lang.reflect.Type) String.class)); } @Test void testDeserializeWithType() { String data = "testType"; byte[] bytes = jacksonSerializer.serialize(data); String result = jacksonSerializer.deserialize(bytes, (java.lang.reflect.Type) String.class); assertEquals(data, result); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/snapshot/LocalFileMetaTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.snapshot; 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.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * {@link LocalFileMeta} unit test. * * @author chenglu * @date 2021-07-27 18:43 */ class LocalFileMetaTest { private LocalFileMeta fileMeta; @BeforeEach void setUp() { fileMeta = new LocalFileMeta(); } @Test void testAppendAndGet() { fileMeta.append("key", "value"); assertEquals("value", fileMeta.get("key")); } @Test void testToString() { LocalFileMeta meta = new LocalFileMeta(); assertNotNull(meta.toString()); assertTrue(meta.toString().contains("LocalFileMeta")); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/snapshot/ReaderTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.snapshot; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.Properties; import static org.junit.jupiter.api.Assertions.assertEquals; /** * {@link Reader} unit test. * * @author chenglu * @date 2021-07-27 18:46 */ class ReaderTest { private Reader reader; @BeforeEach void setUp() { Map map = new HashMap<>(2); Properties properties = new Properties(); properties.put("k", "v"); map.put("a", new LocalFileMeta(properties)); reader = new Reader("test", map); } @Test void test() { assertEquals("test", reader.getPath()); assertEquals(1, reader.listFiles().size()); assertEquals("v", reader.getFileMeta("a").getFileMeta().getProperty("k")); } } ================================================ FILE: consistency/src/test/java/com/alibaba/nacos/consistency/snapshot/WriterTest.java ================================================ /* * Copyright 1999-2021 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.consistency.snapshot; 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; /** * {@link Writer} unit test. * * @author chenglu * @date 2021-07-28 18:50 */ class WriterTest { private Writer writer; @BeforeEach void setUp() { writer = new Writer("test"); } @Test void test() { assertEquals("test", writer.getPath()); assertTrue(writer.addFile("a")); assertTrue(writer.addFile("b", new LocalFileMeta())); assertEquals(2, writer.listFiles().size()); assertTrue(writer.removeFile("a")); assertEquals(1, writer.listFiles().size()); } } ================================================ FILE: console/README.md ================================================ ```shell #开发工具本地启动,增加以下 JVM 参数 -Dnacos.standalone=true ``` ================================================ FILE: console/pom.xml ================================================ 4.0.0 com.alibaba.nacos nacos-all ${revision} nacos-console jar nacos-console ${project.version} https://nacos.io ${project.groupId} nacos-config ${project.groupId} nacos-naming ${project.groupId} nacos-core com.alibaba.nacos nacos-copilot ${revision} com.alibaba.nacos nacos-ai ${project.groupId} nacos-lock com.alibaba.nacos nacos-maintainer-client ${project.groupId} nacos-istio ${project.groupId} nacos-prometheus ${project.groupId} nacos-k8s-sync io.modelcontextprotocol.sdk mcp org.slf4j log4j-over-slf4j org.slf4j jcl-over-slf4j org.slf4j jul-to-slf4j org.springframework.boot spring-boot-starter-security org.apache.derby derby provided org.springframework.boot spring-boot-test-autoconfigure test org.springframework spring-test test org.hamcrest hamcrest test com.alibaba.nacos default-auth-plugin ${project.version} test org.graalvm.buildtools native-maven-plugin ${native-maven-plugin.version} src/main/resources META-INF/** static/** nacos-console.properties nacos-console-banner.txt org/apache/derby/modules.properties librocksdbjni-${hints.os.rocksdb} static/console-ui/.vscode/** com.github.spotbugs spotbugs-maven-plugin dev true com.alibaba.nacos nacos-default-plugin-all ${project.version} native-win64 windows win64.dll native-linux32 unix Linux x86 linux32.so native-linux64 unix Linux x64 linux64.so native-linux-aarch64 unix Linux aarch64 linux-aarch64.so native-linux-ppc64le unix Linux aarch64 linux-ppc64le.so native-linux-s390x unix Linux s390x linux-s390x.so native-osx-arm64 mac arm64 osx-arm64.jnilib native-osx-x86_64 mac x86_64 osx-x86_64.jnilib native org.springframework.boot spring-boot-maven-plugin -agentlib:native-image-agent=config-merge-dir=src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-console/ paketobuildpacks/builder:tiny true process-aot process-aot org.graalvm.buildtools native-maven-plugin nacos-server --add-opens java.base/java.lang=ALL-UNNAMED --enable-url-protocols=http --enable-preview --no-fallback -H:IncludeResources='librocksdbjni-${hints.os.rocksdb}' -H:IncludeResources='.*libnetty_transport_native_epoll_.*\.so' --initialize-at-run-time=com.google.common.util.concurrent.AbstractFuture$Waiter --initialize-at-run-time=com.google.common.util.concurrent.AbstractFuture$Listener --initialize-at-run-time=com.google.protobuf.Any --initialize-at-run-time=com.google.protobuf.Any$Builder --initialize-at-run-time=io.grpc.internal.ServerCallImpl --initialize-at-run-time=io.grpc.ForwardingServerCall --initialize-at-run-time=io.grpc.ForwardingServerCall$SimpleForwardingServerCall --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.WriteQueue --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyClientStream --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyServerStream --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyServerProvider --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder$NettyTransportFactory --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyClientStream --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.WriteBufferingAndExceptionHandler --initialize-at-run-time=io.grpc.netty.shaded.io.grpc.netty.NettyConnection --initialize-at-run-time=io.grpc.internal.ManagedChannelImpl --initialize-at-run-time=io.grpc.internal.ManagedChannelImpl$RealChannel --initialize-at-run-time=io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel --initialize-at-run-time=io.grpc.ServerCall --initialize-at-run-time=io.grpc.stub.ClientCalls --initialize-at-run-time=io.grpc.stub.ClientCalls$UnaryStreamToFuture --initialize-at-run-time=io.grpc.stub.ServerCalls --initialize-at-run-time=io.grpc.stub.ServerCalls$NoopStreamObserver --initialize-at-run-time=io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl --initialize-at-run-time=io.grpc.stub.ServerCalls$StreamingServerCallHandler --initialize-at-run-time=io.grpc.stub.ServerCallStreamObserver --initialize-at-run-time=io.grpc.stub.CallStreamObserver --initialize-at-run-time=io.grpc.stub.StreamObserver --initialize-at-run-time=io.grpc.protobuf.lite.ProtoInputStream --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.Unpooled --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.PoolArena --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.ByteBufUtil --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.PooledByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.ByteBufAllocator --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.PooledSlicedByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.UnpooledHeapByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.PoolArena$DirectArena --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.UnpooledDirectByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.UnpooledByteBufAllocator --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.PooledUnsafeDirectByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.UnpooledUnsafeDirectByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.AbstractPooledDerivedByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.AbstractReferenceCountedByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeDirectByteBuf --initialize-at-run-time=io.grpc.netty.shaded.io.netty.util.AbstractReferenceCounted --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.codec.base64.Base64 --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.codec.http.HttpObjectAggregator --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.OpenSsl --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.SslUtils --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.PemValue --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.PemPrivateKey --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.JdkSslContext --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.PemX509Certificate --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.JdkSslContext$Defaults --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.OpenSslPrivateKeyMethod --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.BouncyCastleAlpnSslUtils --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.OpenSslAsyncPrivateKeyMethod --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.ReferenceCountedOpenSslEngine --initialize-at-run-time=io.grpc.netty.shaded.io.netty.handler.ssl.ReferenceCountedOpenSslContext --initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.SSL --initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.CertificateVerifier --initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.SSLPrivateKeyMethod --initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.AsyncSSLPrivateKeyMethod --initialize-at-run-time=io.grpc.netty.shaded.io.netty.internal.tcnative.CertificateCompressionAlgo --initialize-at-run-time=com.alibaba.nacos.consistency.entity.WriteRequest --initialize-at-run-time=com.alibaba.nacos.consistency.entity.WriteRequest$Builder --initialize-at-run-time=com.alibaba.nacos.consistency.entity.ReadRequest --initialize-at-run-time=com.alibaba.nacos.consistency.entity.ReadRequest$Builder --initialize-at-run-time=com.alibaba.nacos.consistency.entity.Response --initialize-at-run-time=com.alibaba.nacos.consistency.entity.Response$Builder --initialize-at-run-time=com.alibaba.nacos.consistency.entity.GetRequest --initialize-at-run-time=com.alibaba.nacos.consistency.entity.GetRequest$Builder --initialize-at-run-time=com.alibaba.nacos.consistency.entity.Log --initialize-at-run-time=com.alibaba.nacos.consistency.entity.Log$Builder --initialize-at-run-time=com.alipay.sofa.jraft.entity.LocalFileMetaOutter$LocalFileMeta --initialize-at-run-time=com.alipay.sofa.jraft.rpc.ProtobufMsgFactory --initialize-at-run-time=org.apache.derby.jdbc.AutoloadedDriver --initialize-at-run-time=org.apache.derby.jdbc.InternalDriver --initialize-at-run-time=org.apache.derby.jdbc.Driver42 --initialize-at-run-time=org.apache.derby.jdbc.EmbeddedDriver --initialize-at-run-time=org.apache.derby.jdbc.ResourceAdapterImpl --initialize-at-run-time=org.apache.derby.iapi.jdbc.DRDAServerStarter --initialize-at-run-time=org.apache.derby.iapi.jdbc.JDBCBoot --initialize-at-run-time=org.apache.derby.iapi.services.SecurityUtil --initialize-at-run-time=org.apache.derby.iapi.services.monitor.Monitor --initialize-at-run-time=org.apache.derby.impl.services.monitor.ModuleInstance --initialize-at-run-time=org.apache.derby.impl.services.monitor.ProtocolKey --initialize-at-run-time=org.apache.derby.impl.services.monitor.BaseMonitor --initialize-at-run-time=org.apache.derby.impl.services.monitor.TopService --initialize-at-run-time=org.apache.derby.iapi.services.cache.ClassSizeCatalogImpl --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.pojo.HealthCheckInstancePublishInfo --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceData --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.pojo.Service --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl --initialize-at-run-time=com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl$InstanceStoreRequest --initialize-at-run-time=com.alibaba.nacos.api.naming.pojo.Instance --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.Metadata --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.Metadata.Builder --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.Payload --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.Payload.Builder --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamBlockingStub --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamFutureStub --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamImplBase --initialize-at-run-time=com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamStub --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.InstanceRequest --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse --initialize-at-run-time=com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse --initialize-at-run-time=com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse --initialize-at-run-time=com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse --initialize-at-run-time=com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse --initialize-at-run-time=com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse --initialize-at-run-time=com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.ServiceListRequest --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.response.InstanceResponse --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.response.ServiceListResponse --initialize-at-run-time=com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.request.ClientDetectionRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.ConnectionSetupRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.ConnectResetRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.HealthCheckRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.PushAckRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.ServerCheckRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.ServerLoaderInfoRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.ServerReloadRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.request.SetupAckRequest --initialize-at-run-time=com.alibaba.nacos.api.remote.response.ClientDetectionResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.response.ConnectResetResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.response.ErrorResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.response.HealthCheckResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.response.ServerCheckResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.response.ServerLoaderInfoResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.response.ServerReloadResponse --initialize-at-run-time=com.alibaba.nacos.api.remote.response.SetupAckResponse --initialize-at-run-time=com.alibaba.nacos.core.distributed.raft.NacosClosure --initialize-at-run-time=com.alibaba.nacos.core.monitor.GrpcServerThreadPoolMonitor --initialize-at-run-time=com.alibaba.nacos.core.remote.grpc.BaseGrpcServer --initialize-at-run-time=com.alibaba.nacos.core.remote.BaseRpcServer --initialize-at-run-time=com.alibaba.nacos.core.remote.grpc.GrpcBiStreamRequestAcceptor --initialize-at-run-time=com.alibaba.nacos.core.remote.grpc.GrpcClusterServer --initialize-at-run-time=com.alibaba.nacos.core.remote.grpc.GrpcSdkServer --initialize-at-run-time=com.alibaba.nacos.core.cluster.remote.request.MemberReportRequest --initialize-at-run-time=com.alibaba.nacos.core.cluster.remote.response.MemberReportResponse --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest --initialize-at-run-time=com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest$ConfigListenContext --initialize-at-run-time=com.alibaba.nacos.naming.cluster.remote.request.DistroDataRequest --initialize-at-run-time=com.alibaba.nacos.naming.cluster.remote.response.DistroDataResponse --initialize-at-run-time=com.alibaba.nacos.plugin.auth.impl.jwt.NacosJwtPayload --initialize-at-build-time=org.slf4j.LoggerFactory --initialize-at-build-time=ch.qos.logback.core.util.Loader --initialize-at-build-time=ch.qos.logback.core.status.InfoStatus --initialize-at-build-time=ch.qos.logback.classic.Logger --initialize-at-build-time=ch.qos.logback.core.util.StatusPrinter --initialize-at-build-time=ch.qos.logback.core.CoreConstants --initialize-at-build-time=ch.qos.logback.classic.Level --initialize-at-build-time=ch.qos.logback.core.status.StatusBase --initialize-at-build-time=ch.qos.logback.core.util.StatusPrinter2 build-native compile-no-fork package add-reachability-metadata add-reachability-metadata ================================================ FILE: console/src/main/java/com/alibaba/nacos/Nacos.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos; import com.alibaba.nacos.console.aot.NacosRuntimeHints; import com.alibaba.nacos.sys.filter.NacosTypeExcludeFilter; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.TypeExcludeFilter; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ImportRuntimeHints; /** * Nacos Full merged starter. *

    * Use @SpringBootApplication and @ComponentScan at the same time, using CUSTOM type filter to control module enabled. *

    * * @author nacos * @deprecated The old start up class will be removed, please use {@link NacosBootstrap} in nacos-bootstrap module instead. */ @SpringBootApplication @ImportRuntimeHints(NacosRuntimeHints.class) @ComponentScan(basePackages = "com.alibaba.nacos", excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {NacosTypeExcludeFilter.class}), @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})}) @Deprecated public class Nacos { public static void main(String[] args) { SpringApplication.run(Nacos.class, args); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/NacosConsole.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration; import org.springframework.context.annotation.PropertySource; import org.springframework.scheduling.annotation.EnableScheduling; /** * Nacos console starter. * * @author xiweng.yy */ @SpringBootApplication(exclude = LdapAutoConfiguration.class) @PropertySource("classpath:nacos-console.properties") @EnableScheduling public class NacosConsole { public static void main(String[] args) { SpringApplication.run(NacosConsole.class, args); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/NacosConsoleStartUp.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.core.exception.ErrorCode; import com.alibaba.nacos.core.listener.startup.AbstractNacosStartUp; import com.alibaba.nacos.core.listener.startup.NacosStartUp; import com.alibaba.nacos.sys.env.Constants; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.DiskUtils; import com.alibaba.nacos.sys.utils.InetUtils; import org.slf4j.Logger; import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.core.env.ConfigurableEnvironment; import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * Nacos Server Web API start up phase. * * @author xiweng.yy */ public class NacosConsoleStartUp extends AbstractNacosStartUp { private static final String MODE_PROPERTY_KEY_STAND_MODE = "nacos.mode"; private static final String MODE_PROPERTY_KEY_FUNCTION_MODE = "nacos.function.mode"; private static final String NACOS_MODE_STAND_ALONE = "stand alone"; private static final String DEFAULT_FUNCTION_MODE = "All"; private static final String LOCAL_IP_PROPERTY_KEY = "nacos.local.ip"; private static final String NACOS_APPLICATION_CONF = "nacos_application_conf"; private static final Map SOURCES = new ConcurrentHashMap<>(); private boolean isConsoleDeploymentType; public NacosConsoleStartUp() { super(NacosStartUp.CONSOLE_START_UP_PHASE); } @Override protected String getPhaseNameInStartingInfo() { return "Nacos Console"; } @Override public String[] makeWorkDir() { isConsoleDeploymentType = Constants.NACOS_DEPLOYMENT_TYPE_CONSOLE.equals( System.getProperty(Constants.NACOS_DEPLOYMENT_TYPE)); if (isConsoleDeploymentType) { try { Path path = Paths.get(EnvUtil.getNacosHome(), "logs"); DiskUtils.forceMkdir(new File(path.toUri())); } catch (Exception e) { throw new NacosRuntimeException(ErrorCode.IOMakeDirError.getCode(), e); } return new String[] {EnvUtil.getNacosHome() + File.separator + "logs"}; } return super.makeWorkDir(); } @Override public void injectEnvironment(ConfigurableEnvironment environment) { if (isConsoleDeploymentType) { EnvUtil.setEnvironment(environment); } } @Override public void loadPreProperties(ConfigurableEnvironment environment) { if (isConsoleDeploymentType) { try { SOURCES.putAll(EnvUtil.loadProperties(EnvUtil.getApplicationConfFileResource())); environment.getPropertySources() .addLast(new OriginTrackedMapPropertySource(NACOS_APPLICATION_CONF, SOURCES)); } catch (Exception e) { throw new NacosRuntimeException(NacosException.SERVER_ERROR, e); } } } @Override public void initSystemProperty() { if (isConsoleDeploymentType) { System.setProperty(LOCAL_IP_PROPERTY_KEY, InetUtils.getSelfIP()); System.setProperty(MODE_PROPERTY_KEY_STAND_MODE, NACOS_MODE_STAND_ALONE); if (EnvUtil.getFunctionMode() == null) { System.setProperty(MODE_PROPERTY_KEY_FUNCTION_MODE, DEFAULT_FUNCTION_MODE); } else if (EnvUtil.FUNCTION_MODE_CONFIG.equals(EnvUtil.getFunctionMode())) { System.setProperty(MODE_PROPERTY_KEY_FUNCTION_MODE, EnvUtil.FUNCTION_MODE_CONFIG); } else if (EnvUtil.FUNCTION_MODE_NAMING.equals(EnvUtil.getFunctionMode())) { System.setProperty(MODE_PROPERTY_KEY_FUNCTION_MODE, EnvUtil.FUNCTION_MODE_NAMING); } } } @Override public void logStarted(Logger logger) { long endTimestamp = System.currentTimeMillis(); long startupCost = endTimestamp - getStartTimestamp(); logger.info("Nacos Console started successfully in {} ms", startupCost); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/aot/AotConfiguration.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.aot; import org.rocksdb.NativeLibraryLoader; import java.lang.reflect.Field; /** * Help graalvm and spring-aot find specific native file from jar like rocksdb. * * @author Dioxide.CN * @date 2024/8/16 * @since 2.4.0 */ public class AotConfiguration { /** * To help find rocksdb inner fields' value. */ public static String reflectToNativeLibraryLoader() { Class clazz = NativeLibraryLoader.class; try { Field jniLibraryFileNameField = clazz.getDeclaredField("jniLibraryFileName"); jniLibraryFileNameField.setAccessible(true); Field fallbackJniLibraryFileNameField = clazz.getDeclaredField("fallbackJniLibraryFileName"); fallbackJniLibraryFileNameField.setAccessible(true); return (String) jniLibraryFileNameField.get(null); } catch (NoSuchFieldException | IllegalAccessException e) { throw new RuntimeException(e); } } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/aot/NacosRuntimeHints.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.aot; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceView; import org.springframework.aot.hint.MemberCategory; import org.springframework.aot.hint.RuntimeHints; import org.springframework.aot.hint.RuntimeHintsRegistrar; import java.io.Serializable; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; /** * Add nacos runtime hints support. * * @author Dioxide.CN * @date 2024/8/6 * @since 2.4.0 */ @SuppressWarnings("all") public class NacosRuntimeHints implements RuntimeHintsRegistrar { // region Java private final Class[] javaClasses = {byte.class, byte.class, byte[].class, boolean.class, Object.class, Integer.class, com.sun.management.GarbageCollectorMXBean.class, com.sun.management.GcInfo.class, sun.misc.Unsafe.class, java.io.PrintWriter.class, java.lang.Double.class, java.lang.management.MemoryUsage.class, java.lang.management.BufferPoolMXBean.class, java.lang.management.ClassLoadingMXBean.class, java.lang.management.CompilationMXBean.class, java.lang.management.MemoryMXBean.class, java.lang.management.MemoryManagerMXBean.class, java.lang.management.MemoryPoolMXBean.class, java.lang.management.MonitorInfo.class, java.lang.management.ManagementPermission.class, java.lang.management.ThreadMXBean.class, java.lang.management.ThreadInfo.class, java.lang.management.LockInfo.class, java.lang.System.class, java.lang.Thread.class, java.net.InetSocketAddress.class, java.nio.ByteBuffer.class, java.nio.channels.SelectableChannel.class, java.nio.channels.SocketChannel.class, java.nio.channels.FileChannel.class, java.security.AccessController.class, java.sql.Date.class, java.sql.Driver.class, java.sql.DriverManager.class, java.sql.Time.class, java.sql.Timestamp.class, java.util.concurrent.locks.LockSupport.class, java.util.Optional.class, java.util.Properties.class}; // endregion // region Hessian private final Class[] hessianClasses = {com.caucho.hessian.io.Hessian2Input.class, com.caucho.hessian.io.ContextSerializerFactory.class}; // endregion // region SQL // TODO: Replace hard Derby class literals with optional reflection-based registration, // then remove direct Derby compile dependency from console. private final Class[] sqlClasses = {org.apache.derby.impl.store.raw.data.CachedPage.class, org.apache.derby.catalog.types.TypesImplInstanceGetter.class, org.apache.derby.impl.services.uuid.BasicUUIDGetter.class, org.apache.derby.iapi.types.DTSClassInfo.class, org.apache.derby.iapi.services.loader.ClassInfo.class, org.apache.derby.impl.io.DirStorageFactory.class, org.apache.derby.impl.store.raw.log.LogRecord.class, org.apache.derby.impl.store.raw.xact.XactId.class, org.apache.derby.impl.store.raw.log.CheckpointOperation.class, org.apache.derby.impl.store.raw.xact.TransactionTable.class, org.apache.derby.impl.store.raw.xact.TransactionTableEntry.class, org.apache.derby.impl.store.raw.log.LogCounter.class, org.apache.derby.impl.store.raw.log.ChecksumOperation.class, org.apache.derby.impl.store.raw.data.BaseDataFileFactoryJ4.class, org.apache.derby.impl.store.raw.xact.XactFactory.class, org.apache.derby.impl.store.raw.log.ReadOnly.class, org.apache.derby.impl.store.raw.xact.BeginXact.class, org.apache.derby.impl.store.raw.xact.EndXact.class, org.apache.derby.impl.store.raw.data.ContainerOperation.class, org.apache.derby.impl.store.raw.data.InitPageOperation.class, org.apache.derby.impl.store.raw.data.AllocPageOperation.class, org.apache.derby.impl.store.raw.data.InsertOperation.class, org.apache.derby.impl.store.raw.data.LogicalUndoOperation.class, org.apache.derby.impl.store.raw.data.InvalidatePageOperation.class, org.apache.derby.impl.store.raw.data.EncryptContainerOperation.class, org.apache.derby.impl.store.raw.data.EncryptContainerUndoOperation.class, org.apache.derby.impl.store.raw.data.CopyRowsOperation.class, org.apache.derby.impl.store.raw.data.ContainerUndoOperation.class, org.apache.derby.impl.store.raw.data.CompressSpacePageOperation.class, org.apache.derby.impl.store.raw.data.CompressSpacePageOperation10_2.class, org.apache.derby.impl.store.raw.data.ChainAllocPageOperation.class, org.apache.derby.jdbc.AutoloadedDriver.class, org.apache.derby.jdbc.InternalDriver.class, org.apache.derby.jdbc.Driver42.class, org.apache.derby.jdbc.EmbeddedDriver.class, org.apache.derby.jdbc.ResourceAdapterImpl.class, org.apache.derby.iapi.jdbc.DRDAServerStarter.class, org.apache.derby.iapi.jdbc.JDBCBoot.class, org.apache.derby.iapi.security.SecurityUtil.class, org.apache.derby.iapi.services.monitor.Monitor.class, org.apache.derby.iapi.services.stream.InfoStreams.class, org.apache.derby.impl.services.monitor.FileMonitor.class, org.apache.derby.impl.services.jmx.JMXManagementService.class, org.apache.derby.impl.services.cache.ConcurrentCacheFactory.class, org.apache.derby.impl.services.locks.ConcurrentPool.class, org.apache.derby.impl.services.jce.JCECipherFactoryBuilder.class, org.apache.derby.iapi.types.DataValueFactoryImpl.class, org.apache.derby.impl.store.raw.data.BaseDataFileFactory.class, org.apache.derby.impl.store.replication.master.MasterController.class, org.apache.derby.impl.sql.execute.RealResultSetStatisticsFactory.class, org.apache.derby.impl.jdbc.authentication.NoneAuthenticationServiceImpl.class, org.apache.derby.iapi.services.property.PropertyValidation.class, org.apache.derby.impl.sql.conn.GenericLanguageConnectionFactory.class, org.apache.derby.impl.sql.compile.OptimizerFactoryImpl.class, org.apache.derby.impl.services.bytecode.BCJava.class, org.apache.derby.impl.sql.execute.xplain.XPLAINFactory.class, org.apache.derby.impl.sql.compile.TypeCompilerFactoryImpl.class, org.apache.derby.impl.sql.catalog.DataDictionaryImpl.class, org.apache.derby.impl.sql.execute.GenericExecutionFactory.class, org.apache.derby.impl.services.daemon.SingleThreadDaemonFactory.class, org.apache.derby.impl.services.timer.SingletonTimerFactory.class, org.apache.derby.impl.jdbc.authentication.BasicAuthenticationServiceImpl.class, org.apache.derby.impl.db.BasicDatabase.class, org.apache.derby.impl.services.stream.SingleStream.class, org.apache.derby.impl.jdbc.authentication.NativeAuthenticationServiceImpl.class, org.apache.derby.impl.store.access.sort.ExternalSortFactory.class, org.apache.derby.impl.jdbc.authentication.SpecificAuthenticationServiceImpl.class, org.apache.derby.impl.store.access.sort.UniqueWithDuplicateNullsExternalSortFactory.class, org.apache.derby.impl.services.reflect.ReflectClassesJava2.class, org.apache.derby.impl.jdbc.authentication.JNDIAuthenticationService.class, org.apache.derby.impl.store.raw.log.LogToFile.class, org.apache.derby.impl.store.access.heap.HeapConglomerateFactory.class, org.apache.derby.impl.db.SlaveDatabase.class, org.apache.derby.impl.services.jmxnone.NoManagementService.class, org.apache.derby.impl.store.access.RllRAMAccessManager.class, org.apache.derby.impl.store.replication.slave.SlaveController.class, org.apache.derby.impl.sql.GenericLanguageFactory.class, org.apache.derby.impl.services.uuid.BasicUUIDFactory.class, org.apache.derby.impl.store.raw.RawStore.class, org.apache.derby.impl.store.access.btree.index.B2IFactory.class, com.zaxxer.hikari.HikariConfig.class, // hard code Class.forName("org.apache.derby.impl.services.monitor.ModuleInstance"), Class.forName("org.apache.derby.impl.services.monitor.ProtocolKey"), Class.forName("org.apache.derby.impl.services.monitor.TopService"), Class.forName("org.apache.derby.iapi.services.cache.ClassSizeCatalogImpl"), com.alibaba.nacos.persistence.datasource.ExternalDataSourceProperties.class}; // endregion // region JRaft Entity private final Class[] jraftDataClasses = {com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.class, com.alipay.sofa.jraft.entity.LocalFileMetaOutter.LocalFileMeta.Builder.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.LogPBMeta.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.LogPBMeta.Builder.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.StablePBMeta.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.StablePBMeta.Builder.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.ConfigurationPBMeta.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.ConfigurationPBMeta.Builder.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.LocalSnapshotPbMeta.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.LocalSnapshotPbMeta.Builder.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.LocalSnapshotPbMeta.File.class, com.alipay.sofa.jraft.entity.LocalStorageOutter.LocalSnapshotPbMeta.File.Builder.class, com.alipay.sofa.jraft.entity.RaftOutter.EntryMeta.class, com.alipay.sofa.jraft.entity.RaftOutter.EntryMeta.Builder.class, com.alipay.sofa.jraft.entity.RaftOutter.SnapshotMeta.class, com.alipay.sofa.jraft.entity.RaftOutter.SnapshotMeta.Builder.class, com.alipay.sofa.jraft.entity.codec.v2.LogOutter.PBLogEntry.class, com.alipay.sofa.jraft.entity.codec.v2.LogOutter.PBLogEntry.Builder.class,}; // endregion // region JRaft RPC private final Class[] jraftRpcClasses = {com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesRequest.class, com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesRequest.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesResponse.class, com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesResponse.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesRequestHeader.class, com.alipay.sofa.jraft.rpc.RpcRequests.AppendEntriesRequestHeader.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.PingRequest.class, com.alipay.sofa.jraft.rpc.RpcRequests.PingRequest.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.ErrorResponse.class, com.alipay.sofa.jraft.rpc.RpcRequests.ErrorResponse.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.InstallSnapshotRequest.class, com.alipay.sofa.jraft.rpc.RpcRequests.InstallSnapshotRequest.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.InstallSnapshotResponse.class, com.alipay.sofa.jraft.rpc.RpcRequests.InstallSnapshotResponse.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.TimeoutNowRequest.class, com.alipay.sofa.jraft.rpc.RpcRequests.TimeoutNowRequest.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.TimeoutNowResponse.class, com.alipay.sofa.jraft.rpc.RpcRequests.TimeoutNowResponse.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.RequestVoteRequest.class, com.alipay.sofa.jraft.rpc.RpcRequests.RequestVoteRequest.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.RequestVoteResponse.class, com.alipay.sofa.jraft.rpc.RpcRequests.RequestVoteResponse.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.GetFileRequest.class, com.alipay.sofa.jraft.rpc.RpcRequests.GetFileRequest.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.GetFileResponse.class, com.alipay.sofa.jraft.rpc.RpcRequests.GetFileResponse.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.ReadIndexRequest.class, com.alipay.sofa.jraft.rpc.RpcRequests.ReadIndexRequest.Builder.class, com.alipay.sofa.jraft.rpc.RpcRequests.ReadIndexResponse.class, com.alipay.sofa.jraft.rpc.RpcRequests.ReadIndexResponse.Builder.class}; // endregion // region JRaft CLI private final Class[] jraftCliClasses = {com.alipay.sofa.jraft.rpc.CliRequests.LearnersOpResponse.class, com.alipay.sofa.jraft.rpc.CliRequests.LearnersOpResponse.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.ResetLearnersRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.ResetLearnersRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.RemoveLearnersRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.RemoveLearnersRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.AddLearnersRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.AddLearnersRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.GetPeersResponse.class, com.alipay.sofa.jraft.rpc.CliRequests.GetPeersResponse.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.GetPeersRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.GetPeersRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.GetLeaderResponse.class, com.alipay.sofa.jraft.rpc.CliRequests.GetLeaderResponse.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.GetLeaderRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.GetLeaderRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.TransferLeaderRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.TransferLeaderRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.ResetPeerRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.ResetPeerRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.SnapshotRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.SnapshotRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.ChangePeersResponse.class, com.alipay.sofa.jraft.rpc.CliRequests.ChangePeersResponse.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.ChangePeersRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.ChangePeersRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.RemovePeerResponse.class, com.alipay.sofa.jraft.rpc.CliRequests.RemovePeerResponse.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.RemovePeerRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.RemovePeerRequest.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.AddPeerResponse.class, com.alipay.sofa.jraft.rpc.CliRequests.AddPeerResponse.Builder.class, com.alipay.sofa.jraft.rpc.CliRequests.AddPeerRequest.class, com.alipay.sofa.jraft.rpc.CliRequests.AddPeerRequest.Builder.class}; // endregion // region JRaft Service private final Class[] jraftUtilClasses = {com.alipay.sofa.jraft.rpc.ProtobufMsgFactory.class, com.alipay.sofa.jraft.rpc.RpcRequestClosure.class, com.alipay.sofa.jraft.rpc.impl.AbstractClientService.class, com.alipay.sofa.jraft.rpc.impl.BoltRaftRpcFactory.class, com.alipay.sofa.jraft.rpc.impl.GrpcRaftRpcFactory.class, com.alipay.sofa.jraft.util.JRaftSignalHandler.class, com.alipay.sofa.jraft.util.concurrent.MpscSingleThreadExecutor.class, com.alipay.sofa.jraft.util.timer.DefaultRaftTimerFactory.class, com.alipay.sofa.jraft.util.internal.ThrowUtil.class, com.alipay.sofa.jraft.core.DefaultJRaftServiceFactory.class, com.alipay.sofa.jraft.core.NodeImpl.class, com.alipay.sofa.jraft.core.Replicator.class, com.alipay.sofa.jraft.storage.snapshot.local.LocalSnapshotReader.class}; // endregion // region gRpc private final Class[] grpcClasses = {com.google.protobuf.Any.class, com.google.protobuf.Any.Builder.class, com.google.protobuf.ByteString.class, com.google.protobuf.Message.class, com.google.protobuf.ByteString.class, com.google.protobuf.CodedInputStream.class, com.google.common.util.concurrent.AbstractFuture.class, com.google.common.util.concurrent.ListenableFuture.class, io.grpc.KnownLength.class, io.grpc.ServerCall.class, io.grpc.ServerBuilder.class, io.grpc.stub.ClientCalls.class, io.grpc.stub.ServerCalls.class, io.grpc.stub.ServerCallStreamObserver.class, io.grpc.stub.CallStreamObserver.class, io.grpc.stub.StreamObserver.class, io.grpc.internal.ReadableBuffers.class, io.grpc.internal.ServerImplBuilder.class, io.grpc.internal.ClientStreamListener.class, io.grpc.netty.shaded.io.netty.bootstrap.ServerBootstrap.class, io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator.class, io.grpc.netty.shaded.io.netty.buffer.AbstractReferenceCountedByteBuf.class, io.grpc.netty.shaded.io.netty.buffer.ByteBufAllocator.class, io.grpc.netty.shaded.io.netty.buffer.ByteBufUtil.class, io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator.class, io.grpc.netty.shaded.io.netty.buffer.UnpooledDirectByteBuf.class, io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder.class, io.grpc.netty.shaded.io.grpc.netty.NettyServerProvider.class, io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder.class, io.grpc.netty.shaded.io.grpc.netty.NettyChannelProvider.class, io.grpc.netty.shaded.io.grpc.netty.NettyConnectionHelper.class, io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel.class, io.grpc.netty.shaded.io.netty.util.AttributeKey.class, io.grpc.ForwardingServerCall.class, io.grpc.ForwardingServerCall.SimpleForwardingServerCall.class, Class.forName("io.grpc.internal.ServerCallImpl"), Class.forName("io.grpc.netty.shaded.io.grpc.netty.WriteQueue"), Class.forName("io.grpc.netty.shaded.io.grpc.netty.NettyServerStream")}; // endregion // region Nacos Hints private final Class[] nacosClasses = { // reflect com.alibaba.nacos.common.notify.SlowEvent.class, com.alibaba.nacos.common.packagescan.PackageScan.class, com.alibaba.nacos.common.packagescan.DefaultPackageScan.class, com.alibaba.nacos.naming.core.v2.event.metadata.MetadataEvent.ServiceMetadataEvent.class, com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl.class, com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl.InstanceStoreRequest.class, com.alibaba.nacos.persistence.datasource.LocalDataSourceServiceImpl.class, com.alibaba.nacos.persistence.configuration.condition.ConditionStandaloneEmbedStorage.class, com.alibaba.nacos.consistency.snapshot.LocalFileMeta.class, com.alibaba.nacos.consistency.ProtocolMetaData.class, com.alibaba.nacos.common.remote.client.grpc.GrpcUtils.class, com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest.class, com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest.class, com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest.ConfigListenContext.class, com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse.class, com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse.ConfigContext.class, com.alibaba.nacos.api.remote.response.ClientDetectionResponse.class, com.alibaba.nacos.api.remote.response.ConnectResetResponse.class, com.alibaba.nacos.api.remote.response.ErrorResponse.class, com.alibaba.nacos.api.remote.response.HealthCheckResponse.class, com.alibaba.nacos.api.remote.response.Response.class, com.alibaba.nacos.api.remote.response.ServerCheckResponse.class, com.alibaba.nacos.api.remote.response.ServerLoaderInfoResponse.class, com.alibaba.nacos.api.remote.response.ServerReloadResponse.class, com.alibaba.nacos.api.remote.response.SetupAckResponse.class, // cluster com.alibaba.nacos.consistency.ap.APProtocol.class, com.alibaba.nacos.consistency.cp.CPProtocol.class, com.alibaba.nacos.core.distributed.raft.RaftConfig.class, com.alibaba.nacos.core.distributed.raft.RaftEvent.class, com.alibaba.nacos.api.ability.ServerAbilities.class, com.alibaba.nacos.api.config.ability.ServerConfigAbility.class, com.alibaba.nacos.api.naming.ability.ServerNamingAbility.class, com.alibaba.nacos.consistency.DataOperation.class, com.alibaba.nacos.core.cluster.Member.class, com.alibaba.nacos.core.cluster.remote.request.AbstractClusterRequest.class, com.alibaba.nacos.core.cluster.remote.request.MemberReportRequest.class, com.alibaba.nacos.core.cluster.remote.response.MemberReportResponse.class, com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy.class, com.alibaba.nacos.core.distributed.distro.entity.DistroData.class, com.alibaba.nacos.core.distributed.distro.entity.DistroKey.class, com.alibaba.nacos.naming.cluster.remote.request.DistroDataRequest.class, com.alibaba.nacos.naming.cluster.remote.response.DistroDataResponse.class, com.alibaba.nacos.naming.core.v2.client.ClientSyncDatumSnapshot.class, com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo.class, // proto com.alibaba.nacos.consistency.entity.WriteRequest.class, com.alibaba.nacos.consistency.entity.WriteRequest.Builder.class, com.alibaba.nacos.consistency.entity.ReadRequest.class, com.alibaba.nacos.consistency.entity.ReadRequest.Builder.class, com.alibaba.nacos.consistency.entity.Response.class, com.alibaba.nacos.consistency.entity.Response.Builder.class, com.alibaba.nacos.consistency.entity.GetRequest.class, com.alibaba.nacos.consistency.entity.GetRequest.Builder.class, com.alibaba.nacos.consistency.entity.Log.class, com.alibaba.nacos.consistency.entity.Log.Builder.class, // grpc com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.class, com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamBlockingStub.class, com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamFutureStub.class, com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamImplBase.class, com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc.BiRequestStreamStub.class, com.alibaba.nacos.api.grpc.auto.Metadata.class, com.alibaba.nacos.api.grpc.auto.Metadata.Builder.class, com.alibaba.nacos.api.grpc.auto.NacosGrpcService.class, com.alibaba.nacos.api.grpc.auto.Payload.class, com.alibaba.nacos.api.grpc.auto.Payload.Builder.class, com.alibaba.nacos.api.grpc.auto.RequestGrpc.class, com.alibaba.nacos.api.grpc.auto.RequestGrpc.RequestStub.class, com.alibaba.nacos.api.grpc.auto.RequestGrpc.RequestBlockingStub.class, com.alibaba.nacos.api.grpc.auto.RequestGrpc.RequestFutureStub.class, com.alibaba.nacos.api.grpc.auto.RequestGrpc.RequestImplBase.class, com.alibaba.nacos.api.naming.remote.request.InstanceRequest.class, com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest.class, com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest.class, com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest.class, com.alibaba.nacos.api.config.remote.request.ClientConfigMetricRequest.class, com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest.class, com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest.class, com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest.class, com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest.class, com.alibaba.nacos.api.config.remote.response.ClientConfigMetricResponse.class, com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse.class, com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse.class, com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse.class, com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse.class, com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse.class, com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse.class, com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest.class, com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest.class, com.alibaba.nacos.api.naming.remote.request.ServiceListRequest.class, com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest.class, com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest.class, com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse.class, com.alibaba.nacos.api.naming.remote.response.InstanceResponse.class, com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse.class, com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse.class, com.alibaba.nacos.api.naming.remote.response.ServiceListResponse.class, com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse.class, com.alibaba.nacos.api.remote.request.ClientDetectionRequest.class, com.alibaba.nacos.api.remote.request.ConnectionSetupRequest.class, com.alibaba.nacos.api.remote.request.ConnectResetRequest.class, com.alibaba.nacos.api.remote.request.HealthCheckRequest.class, com.alibaba.nacos.api.remote.request.InternalRequest.class, com.alibaba.nacos.api.remote.request.PushAckRequest.class, com.alibaba.nacos.api.remote.request.Request.class, com.alibaba.nacos.api.remote.request.RequestMeta.class, com.alibaba.nacos.api.remote.request.ServerCheckRequest.class, com.alibaba.nacos.api.remote.request.ServerLoaderInfoRequest.class, com.alibaba.nacos.api.remote.request.ServerReloadRequest.class, com.alibaba.nacos.api.remote.request.SetupAckRequest.class, com.alibaba.nacos.api.remote.response.ClientDetectionResponse.class, com.alibaba.nacos.api.remote.response.ConnectResetResponse.class, com.alibaba.nacos.api.remote.response.ErrorResponse.class, com.alibaba.nacos.api.remote.response.HealthCheckResponse.class, com.alibaba.nacos.api.remote.response.ServerCheckResponse.class, com.alibaba.nacos.api.remote.response.ServerLoaderInfoResponse.class, com.alibaba.nacos.api.remote.response.ServerReloadResponse.class, com.alibaba.nacos.api.remote.response.SetupAckResponse.class, com.alibaba.nacos.core.distributed.raft.NacosClosure.class, com.alibaba.nacos.core.monitor.GrpcServerThreadPoolMonitor.class, com.alibaba.nacos.core.remote.grpc.BaseGrpcServer.class, com.alibaba.nacos.core.remote.BaseRpcServer.class, com.alibaba.nacos.core.remote.grpc.GrpcBiStreamRequestAcceptor.class, com.alibaba.nacos.core.remote.grpc.GrpcClusterServer.class, com.alibaba.nacos.core.remote.grpc.GrpcSdkServer.class, com.alibaba.nacos.core.cluster.remote.request.MemberReportRequest.class, com.alibaba.nacos.core.cluster.remote.response.MemberReportResponse.class, com.alibaba.nacos.naming.cluster.remote.request.DistroDataRequest.class, com.alibaba.nacos.naming.cluster.remote.response.DistroDataResponse.class, // serializer com.alibaba.nacos.consistency.serialize.HessianSerializer.class, com.alibaba.nacos.consistency.serialize.JacksonSerializer.class, com.alibaba.nacos.consistency.serialize.NacosHessianSerializerFactory.class, com.alibaba.nacos.naming.core.v2.client.ClientSyncData.class, com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata.class, com.alibaba.nacos.naming.core.v2.pojo.HealthCheckInstancePublishInfo.class, com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo.class, com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceData.class, com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo.class, com.alibaba.nacos.naming.core.v2.pojo.Service.class, com.alibaba.nacos.api.naming.pojo.Cluster.class, com.alibaba.nacos.api.naming.pojo.Instance.class, com.alibaba.nacos.api.naming.pojo.ListView.class, com.alibaba.nacos.api.naming.pojo.Service.class, com.alibaba.nacos.api.naming.pojo.ServiceInfo.class, com.alibaba.nacos.api.naming.pojo.builder.InstanceBuilder.class, com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker.class, com.alibaba.nacos.api.naming.pojo.healthcheck.HealthCheckerFactory.class, com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http.class, com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Mysql.class, com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp.class, com.alibaba.nacos.api.remote.request.Request.class, ServiceView.class, com.alibaba.nacos.naming.pojo.Subscriber.class, com.alibaba.nacos.naming.pojo.Subscribers.class, com.alibaba.nacos.naming.pojo.ClusterInfo.class, com.alibaba.nacos.naming.pojo.InstanceOperationInfo.class, com.alibaba.nacos.naming.pojo.IpAddressInfo.class, com.alibaba.nacos.naming.pojo.Record.class, com.alibaba.nacos.naming.pojo.ServiceDetailInfo.class, com.alibaba.nacos.naming.pojo.ServiceNameView.class, // sys and plugin com.alibaba.nacos.config.server.filter.ConfigEnabledFilter.class, com.alibaba.nacos.naming.config.NamingEnabledFilter.class, com.alibaba.nacos.istio.config.IstioEnabledFilter.class, com.alibaba.nacos.config.server.Config.class, com.alibaba.nacos.naming.NamingApp.class, com.alibaba.nacos.cmdb.CmdbApp.class, com.alibaba.nacos.istio.IstioApp.class, com.alibaba.nacos.prometheus.PrometheusApp.class, com.alibaba.nacos.sys.filter.NacosTypeExcludeFilter.class, com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter.class, com.alibaba.nacos.plugin.control.ControlManagerCenter.class, com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector.class, com.alibaba.nacos.config.server.service.LongPollingConnectionMetricsCollector.class, com.alibaba.nacos.core.remote.RuntimeConnectionEjector.class, com.alibaba.nacos.core.remote.ConnectionManager.class, com.alibaba.nacos.config.server.remote.RpcConfigChangeNotifier.class,}; // endregion // region Nacos Serializer private final List> serializer = List.of(byte.class, byte[].class, String.class, ConcurrentHashMap.class, com.alibaba.nacos.api.grpc.auto.Metadata.class, com.alibaba.nacos.api.grpc.auto.Payload.class, com.alibaba.nacos.naming.core.v2.client.ClientSyncData.class, com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata.class, com.alibaba.nacos.naming.core.v2.pojo.HealthCheckInstancePublishInfo.class, com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo.class, com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceData.class, com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo.class, com.alibaba.nacos.naming.core.v2.pojo.Service.class, com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl.InstanceStoreRequest.class, com.alibaba.nacos.api.naming.pojo.Cluster.class, com.alibaba.nacos.api.naming.pojo.Instance.class, com.alibaba.nacos.api.naming.pojo.Service.class, com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Http.class, com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Mysql.class, com.alibaba.nacos.api.naming.pojo.healthcheck.impl.Tcp.class, com.alibaba.nacos.naming.pojo.Subscriber.class, com.alibaba.nacos.naming.pojo.Subscribers.class, com.alibaba.nacos.naming.pojo.ClusterInfo.class, com.alibaba.nacos.naming.pojo.IpAddressInfo.class, com.alibaba.nacos.naming.pojo.Record.class, com.alibaba.nacos.naming.pojo.ServiceDetailInfo.class, com.alibaba.nacos.consistency.entity.WriteRequest.class, com.alibaba.nacos.consistency.entity.ReadRequest.class, com.alibaba.nacos.consistency.entity.Response.class, com.alibaba.nacos.consistency.entity.GetRequest.class, com.alibaba.nacos.consistency.entity.Log.class); // endregion private final String[] resourcePattern = {AotConfiguration.reflectToNativeLibraryLoader(), ".*libnetty_transport_native_epoll_.*\\.so", ".*\\.desc$", ".*\\.html$", ".*\\.css$", ".*\\.js$", ".*\\.js.map$", ".*\\.png$", ".*\\.svg$", ".*\\.eot$", ".*\\.woff$", ".*\\.woff2$", ".*\\.ttf$", "org/apache/derby/modules.properties", "application.properties",}; public NacosRuntimeHints() throws ClassNotFoundException { } @Override public void registerHints(RuntimeHints hints, ClassLoader classLoader) { Stream.of(javaClasses, hessianClasses, sqlClasses, grpcClasses, jraftDataClasses, jraftRpcClasses, jraftCliClasses, jraftUtilClasses, nacosClasses).flatMap(Stream::of).forEach(type -> hints.reflection() .registerType(type, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS, MemberCategory.DECLARED_CLASSES)); // Register optional plugin classes by name to avoid compile-time dependency registerOptionalClass(hints, "com.alibaba.nacos.plugin.auth.impl.jwt.NacosJwtPayload"); // Register optional legacy adapter naming controllers (when api-legacy-adapter is on classpath, e.g. via bootstrap) registerOptionalClass(hints, "com.alibaba.nacos.legacy.adapter.naming.CatalogController"); for (String pattern : resourcePattern) { hints.resources().registerPattern(pattern); } serializer.forEach(type -> hints.serialization().registerType(type)); } private void registerOptionalClass(RuntimeHints hints, String className) { try { Class clazz = Class.forName(className); hints.reflection().registerType(clazz, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_DECLARED_METHODS, MemberCategory.DECLARED_FIELDS, MemberCategory.DECLARED_CLASSES); } catch (ClassNotFoundException e) { // Optional plugin class not available, skip registration } } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/cluster/RemoteServerMemberManager.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.cluster; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.notify.Event; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.MemberLookup; import com.alibaba.nacos.core.cluster.MembersChangeEvent; import com.alibaba.nacos.core.cluster.NacosMemberManager; import com.alibaba.nacos.core.cluster.lookup.LookupFactory; import com.alibaba.nacos.core.utils.Loggers; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import java.util.Collection; import java.util.HashSet; import java.util.concurrent.ConcurrentSkipListMap; /** * Nacos remote server members manager. Only working on console mode to keep and update the remote server members. * * @author xiweng.yy */ @Service @EnabledRemoteHandler public class RemoteServerMemberManager implements NacosMemberManager { /** * Nacos remote servers cluster node list. */ private volatile ConcurrentSkipListMap serverList; /** * Addressing pattern instances. */ private MemberLookup lookup; public RemoteServerMemberManager() { this.serverList = new ConcurrentSkipListMap<>(); } @PostConstruct public void init() throws NacosException { initAndStartLookup(); } private void initAndStartLookup() throws NacosException { this.lookup = LookupFactory.createLookUp(); this.lookup.injectMemberManager(this); this.lookup.start(); } @Override public synchronized boolean memberChange(Collection members) { ConcurrentSkipListMap newServerList = new ConcurrentSkipListMap<>(); for (Member each : members) { newServerList.put(each.getAddress(), each); } Loggers.CLUSTER.info("[serverlist] nacos remote server members changed to : {}", newServerList); this.serverList = newServerList; Event event = MembersChangeEvent.builder().members(members).build(); NotifyCenter.publishEvent(event); return true; } @Override public Collection allMembers() { return new HashSet<>(serverList.values()); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/ConsoleAuthModuleStateBuilder.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.config.NacosAuthConfigHolder; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginManager; import com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService; import com.alibaba.nacos.sys.module.AbstractConsoleModuleStateBuilder; import com.alibaba.nacos.sys.module.ModuleState; import java.util.Optional; /** * Module state builder for auth module. * * @author xiweng.yy */ public class ConsoleAuthModuleStateBuilder extends AbstractConsoleModuleStateBuilder { public static final String AUTH_MODULE = "console_auth"; public static final String AUTH_ENABLED = "auth_enabled"; public static final String LOGIN_PAGE_ENABLED = "login_page_enabled"; public static final String AUTH_SYSTEM_TYPE = "auth_system_type"; private boolean cacheable; @Override public ModuleState build() { ModuleState result = new ModuleState(AUTH_MODULE); NacosAuthConfig authConfig = NacosAuthConfigHolder.getInstance() .getNacosAuthConfigByScope(NacosConsoleAuthConfig.NACOS_CONSOLE_AUTH_SCOPE); result.newState(AUTH_ENABLED, authConfig.isAuthEnabled()); result.newState(LOGIN_PAGE_ENABLED, isLoginPageEnabled(authConfig)); result.newState(AUTH_SYSTEM_TYPE, authConfig.getNacosAuthSystemType()); return result; } @Override public boolean isCacheable() { return cacheable; } private Boolean isLoginPageEnabled(NacosAuthConfig authConfigs) { Optional authPluginService = AuthPluginManager.getInstance() .findAuthServiceSpiImpl(authConfigs.getNacosAuthSystemType()); return authPluginService.map(AuthPluginService::isLoginEnabled).orElse(false); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/ConsoleCorsConfig.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.sys.env.EnvUtil; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Nacos console cors configurations. * * @author zhan7236 */ public class ConsoleCorsConfig { private static final String CONSOLE_CORS_PREFIX = "nacos.console.cors."; private static final String ALLOW_CREDENTIALS_KEY = CONSOLE_CORS_PREFIX + "allow-credentials"; private static final String ALLOWED_HEADERS_KEY = CONSOLE_CORS_PREFIX + "allowed-headers"; private static final String MAX_AGE_KEY = CONSOLE_CORS_PREFIX + "max-age"; private static final String ALLOWED_METHODS_KEY = CONSOLE_CORS_PREFIX + "allowed-methods"; private static final String ALLOWED_ORIGINS_KEY = CONSOLE_CORS_PREFIX + "allowed-origins"; private static final boolean DEFAULT_ALLOW_CREDENTIALS = true; private static final long DEFAULT_MAX_AGE = 18000L; private final boolean allowCredentials; private final List allowedHeaders; private final long maxAge; private final List allowedMethods; private final List allowedOrigins; public ConsoleCorsConfig() { this.allowCredentials = EnvUtil.getProperty(ALLOW_CREDENTIALS_KEY, Boolean.class, DEFAULT_ALLOW_CREDENTIALS); this.allowedHeaders = parseListProperty(ALLOWED_HEADERS_KEY); this.maxAge = EnvUtil.getProperty(MAX_AGE_KEY, Long.class, DEFAULT_MAX_AGE); this.allowedMethods = parseListProperty(ALLOWED_METHODS_KEY); this.allowedOrigins = parseListProperty(ALLOWED_ORIGINS_KEY); } private List parseListProperty(String key) { String value = EnvUtil.getProperty(key, ""); if (StringUtils.isNotBlank(value)) { return Arrays.asList(value.split(",")); } return Collections.emptyList(); } public boolean isAllowCredentials() { return allowCredentials; } public List getAllowedHeaders() { return allowedHeaders; } public long getMaxAge() { return maxAge; } public List getAllowedMethods() { return allowedMethods; } public List getAllowedOrigins() { return allowedOrigins; } @Override public String toString() { return "ConsoleCorsConfig{" + "allowCredentials=" + allowCredentials + ", allowedHeaders=" + allowedHeaders + ", maxAge=" + maxAge + ", allowedMethods=" + allowedMethods + ", allowedOrigins=" + allowedOrigins + '}'; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/ConsoleDeploymentConfig.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.core.code.ControllerMethodsCache; import com.alibaba.nacos.naming.selector.SelectorManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Load Beans for {@link com.alibaba.nacos.sys.env.DeploymentType#CONSOLE} type. * * @author xiweng.yy */ @Configuration @EnabledRemoteHandler public class ConsoleDeploymentConfig { @Bean public ControllerMethodsCache controllerMethodsCache() { return new ControllerMethodsCache(); } @Bean public SelectorManager selectorManager() { return new SelectorManager(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/ConsoleFunctionEnabledConfig.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.naming.selector.SelectorManager; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Do some config and bean initialize when `functionMode` set. Such as `config` and `naming`. * * @author xiweng.yy */ @Configuration public class ConsoleFunctionEnabledConfig { /** * If `functionMode` set as `config`, * the naming module bean will not be loaded, but console api required {@link SelectorManager} to do selector parser. * * @return {@link SelectorManager} bean */ @Bean @ConditionalOnMissingBean public SelectorManager selectorManager() { return new SelectorManager(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/ConsoleModuleStateBuilder.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.module.AbstractConsoleModuleStateBuilder; import com.alibaba.nacos.sys.module.ModuleState; /** * Console module state builder. * * @author xiweng.yy */ public class ConsoleModuleStateBuilder extends AbstractConsoleModuleStateBuilder { public static final String CONSOLE_MODULE = "console"; private static final String CONSOLE_UI_ENABLED = "console_ui_enabled"; @Override public ModuleState build() { ModuleState result = new ModuleState(CONSOLE_MODULE); try { boolean consoleUiEnabled = EnvUtil.getProperty("nacos.console.ui.enabled", Boolean.class, true); result.newState(CONSOLE_UI_ENABLED, consoleUiEnabled); } catch (Exception ignored) { } return result; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/ConsolePackageExcludeFilter.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.console.NacosConsole; import com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter; import java.util.Set; /** * Console module package exclude filter. Only Basic and Web contexts use NacosTypeExcludeFilter with * basePackages = "com.alibaba.nacos"; Console context uses default scan (console package only) and does not * apply this filter. So when this filter runs, it is always Basic/Web context and console package should be excluded. * * @author xiweng.yy */ public class ConsolePackageExcludeFilter implements NacosPackageExcludeFilter { @Override public String getResponsiblePackagePrefix() { return NacosConsole.class.getPackage().getName(); } @Override public boolean isExcluded(String className, Set annotationNames) { return true; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/ConsoleWebConfig.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.auth.config.NacosAuthConfigHolder; import com.alibaba.nacos.core.exception.NacosApiExceptionHandler; import com.alibaba.nacos.console.filter.NacosConsoleAuthFilter; import com.alibaba.nacos.console.filter.XssFilter; import com.alibaba.nacos.core.code.ControllerMethodsCache; import com.alibaba.nacos.core.paramcheck.ParamCheckerFilter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer; import org.springframework.security.web.SecurityFilterChain; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.filter.CorsFilter; import javax.annotation.PostConstruct; import java.time.ZoneId; /** * Console config. * * @author yshen * @author nkorange * @since 1.2.0 */ @Configuration public class ConsoleWebConfig { private final ControllerMethodsCache methodsCache; public ConsoleWebConfig(ControllerMethodsCache methodsCache) { this.methodsCache = methodsCache; } /** * Init. */ @PostConstruct public void init() { methodsCache.initClassMethod("com.alibaba.nacos.console.controller"); } @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); ConsoleCorsConfig corsConfig = new ConsoleCorsConfig(); config.setAllowCredentials(corsConfig.isAllowCredentials()); if (corsConfig.getAllowedHeaders().isEmpty()) { config.addAllowedHeader("*"); } else { config.setAllowedHeaders(corsConfig.getAllowedHeaders()); } config.setMaxAge(corsConfig.getMaxAge()); if (corsConfig.getAllowedMethods().isEmpty()) { config.addAllowedMethod("*"); } else { config.setAllowedMethods(corsConfig.getAllowedMethods()); } if (corsConfig.getAllowedOrigins().isEmpty()) { config.addAllowedOriginPattern("*"); } else { config.setAllowedOrigins(corsConfig.getAllowedOrigins()); } UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } @Bean public XssFilter xssFilter() { return new XssFilter(); } @Bean public FilterRegistrationBean authFilterRegistration(NacosConsoleAuthFilter authFilter) { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(authFilter); registration.addUrlPatterns("/*"); registration.setName("consoleAuthFilter"); registration.setOrder(6); return registration; } @Bean public NacosConsoleAuthFilter consoleAuthFilter(ControllerMethodsCache methodsCache) { return new NacosConsoleAuthFilter(NacosAuthConfigHolder.getInstance() .getNacosAuthConfigByScope(NacosConsoleAuthConfig.NACOS_CONSOLE_AUTH_SCOPE), methodsCache); } @Bean public FilterRegistrationBean consoleParamCheckerFilterRegistration( ParamCheckerFilter consoleParamCheckerFilter) { FilterRegistrationBean registration = new FilterRegistrationBean<>(); registration.setFilter(consoleParamCheckerFilter); registration.addUrlPatterns("/*"); registration.setName("consoleParamCheckerFilter"); registration.setOrder(8); return registration; } @Bean public ParamCheckerFilter consoleParamCheckerFilter(ControllerMethodsCache methodsCache) { return new ParamCheckerFilter(methodsCache); } @Bean public Jackson2ObjectMapperBuilderCustomizer jacksonObjectMapperCustomization() { return jacksonObjectMapperBuilder -> jacksonObjectMapperBuilder.timeZone(ZoneId.systemDefault().toString()); } @Bean @ConditionalOnMissingBean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http.authorizeHttpRequests((authorizeHttpRequests) -> authorizeHttpRequests.requestMatchers("/**").permitAll()); http.csrf(AbstractHttpConfigurer::disable); return http.build(); } @Bean public NacosApiExceptionHandler nacosApiExceptionHandler() { return new NacosApiExceptionHandler(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/NacosConsoleAuthConfig.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.config.AbstractDynamicConfig; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.Constants; import com.alibaba.nacos.sys.env.EnvUtil; /** * Nacos console auth configurations. * * @author xiweng.yy */ public class NacosConsoleAuthConfig extends AbstractDynamicConfig implements NacosAuthConfig { public static final String NACOS_CONSOLE_AUTH_SCOPE = ApiType.CONSOLE_API.name(); /** * Whether console auth enabled. */ private boolean authEnabled; /** * Which auth system is in use. */ private String nacosAuthSystemType; private String serverIdentityKey; private String serverIdentityValue; public NacosConsoleAuthConfig() { super("NacosConsoleAuth"); resetConfig(); } @Override public String getAuthScope() { return NACOS_CONSOLE_AUTH_SCOPE; } @Override public boolean isAuthEnabled() { return authEnabled; } @Override public String getNacosAuthSystemType() { return nacosAuthSystemType; } @Override public boolean isSupportServerIdentity() { return StringUtils.isNotBlank(serverIdentityKey) && StringUtils.isNotBlank(serverIdentityValue); } @Override public String getServerIdentityKey() { return serverIdentityKey; } @Override public String getServerIdentityValue() { return serverIdentityValue; } @Override protected void getConfigFromEnv() { authEnabled = EnvUtil.getProperty(Constants.Auth.NACOS_CORE_AUTH_CONSOLE_ENABLED, Boolean.class, true); nacosAuthSystemType = EnvUtil.getProperty(Constants.Auth.NACOS_CORE_AUTH_SYSTEM_TYPE, ""); serverIdentityKey = EnvUtil.getProperty(Constants.Auth.NACOS_CORE_AUTH_SERVER_IDENTITY_KEY, ""); serverIdentityValue = EnvUtil.getProperty(Constants.Auth.NACOS_CORE_AUTH_SERVER_IDENTITY_VALUE, ""); } @Override protected String printConfig() { return toString(); } @Override public String toString() { return "NacosConsoleAuthConfig{" + "authEnabled=" + authEnabled + ", nacosAuthSystemType='" + nacosAuthSystemType + '\'' + '}'; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/config/NacosConsoleBeanPostProcessorConfiguration.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.config; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.sys.env.Constants; import com.alibaba.nacos.sys.env.NacosDuplicateConfigurationBeanPostProcessor; import com.alibaba.nacos.sys.env.NacosDuplicateSpringBeanPostProcessor; import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Bean Post Processor Configuration for nacos console. * * @author xiweng.yy */ @Configuration @ConditionalOnProperty(value = Constants.NACOS_DUPLICATE_BEAN_ENHANCEMENT_ENABLED, havingValue = "true", matchIfMissing = true) @EnabledInnerHandler public class NacosConsoleBeanPostProcessorConfiguration { @Bean public InstantiationAwareBeanPostProcessor nacosDuplicateSpringBeanPostProcessor( ConfigurableApplicationContext context) { return new NacosDuplicateSpringBeanPostProcessor(context); } @Bean public InstantiationAwareBeanPostProcessor nacosDuplicateConfigurationBeanPostProcessor( ConfigurableApplicationContext context) { return new NacosDuplicateConfigurationBeanPostProcessor(context); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ConsoleHealthController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor; import com.alibaba.nacos.console.proxy.HealthProxy; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Controller class for handling health check operations. * * @author zhangyukun on:2024/8/27 */ @NacosApi @RestController() @RequestMapping("/v3/console/health") @ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class) public class ConsoleHealthController { private final HealthProxy healthProxy; public ConsoleHealthController(HealthProxy healthProxy) { this.healthProxy = healthProxy; } /** * Whether the Nacos is in broken states or not, and cannot recover except by being restarted. * * @return HTTP code equal to 200 indicates that Nacos is in right states. HTTP code equal to 500 indicates that * Nacos is in broken states. */ @GetMapping("/liveness") public Result liveness() { return Result.success("ok"); } /** * Ready to receive the request or not. * * @return HTTP code equal to 200 indicates that Nacos is ready. HTTP code equal to 500 indicates that Nacos is not * ready. */ @GetMapping("/readiness") public ResponseEntity> readiness() throws NacosException { Result ret = healthProxy.checkReadiness(); if (ret.getCode() == 0) { return ResponseEntity.ok().body(ret); } else { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(ret); } } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ConsoleServerStateController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.api.model.v2.SupportedLanguage; import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor; import com.alibaba.nacos.console.proxy.ServerStateProxy; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Map; /** * Controller for managing server state-related operations. * * @author zhangyukun on:2024/8/27 */ @NacosApi @RestController @RequestMapping("/v3/console/server") @ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class) public class ConsoleServerStateController { private final ServerStateProxy serverStateProxy; public ConsoleServerStateController(ServerStateProxy serverStateProxy) { this.serverStateProxy = serverStateProxy; } /** * Get server state of current server. * * @return state json. */ @GetMapping("/state") public ResponseEntity> serverState() throws NacosException { Map serverState = serverStateProxy.getServerState(); return ResponseEntity.ok().body(serverState); } /** * Get the announcement content based on the specified language. * * @param language Language for the announcement (default: "zh-CN") * @return Announcement content as a string wrapped in a Result object */ @GetMapping("/announcement") public Result getAnnouncement( @RequestParam(required = false, name = "language", defaultValue = "zh-CN") String language) { // Validate the language parameter if (!SupportedLanguage.isSupported(language)) { return Result.failure("Unsupported language: " + language); } String announcement = serverStateProxy.getAnnouncement(language); return Result.success(announcement); } /** * Get the console UI guide information. * * @return Console UI guide information as a string wrapped in a Result object */ @GetMapping("/guide") public Result getConsoleUiGuide() { String guideInformation = serverStateProxy.getConsoleUiGuide(); return Result.success(guideInformation); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/ConsoleA2aController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.ai.param.AgentHttpParamExtractor; import com.alibaba.nacos.ai.utils.AgentRequestUtil; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.console.proxy.ai.A2aProxy; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * Console A2a Controller. * * @author KiteSoar */ @NacosApi @RestController @RequestMapping(Constants.A2A.CONSOLE_PATH) @ExtractorManager.Extractor(httpExtractor = AgentHttpParamExtractor.class) public class ConsoleA2aController { private final A2aProxy a2aProxy; public ConsoleA2aController(A2aProxy a2aProxy) { this.a2aProxy = a2aProxy; } /** * register agent. * * @param form the agent card form to register * @return result of the registration operation * @throws NacosException if the agent registration fails due to invalid input or internal error */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result registerAgent(AgentCardForm form) throws NacosException { form.validate(); AgentCard agentCard = AgentRequestUtil.parseAgentCard(form); a2aProxy.registerAgent(agentCard, form); return Result.success("ok"); } /** * get agent card. * * @param form the agent form to get * @return result of the get operation * @throws NacosApiException if the agent get fails due to invalid input or internal error */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result getAgentCard(AgentForm form) throws NacosException { form.validate(); return Result.success(a2aProxy.getAgentCard(form)); } /** * update agent. * * @param form the agent update form to update * @return result of the update operation * @throws NacosException if the agent update fails due to invalid input or internal error */ @PutMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result updateAgentCard(AgentCardUpdateForm form) throws NacosException { form.validate(); AgentCard agentCard = AgentRequestUtil.parseAgentCard(form); a2aProxy.updateAgentCard(agentCard, form); return Result.success("ok"); } /** * delete agent. * * @param form the agent form to delete * @return result of the deletion operation * @throws NacosException if the agent deletion fails due to invalid input or internal error */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result deleteAgent(AgentForm form) throws NacosException { form.validate(); a2aProxy.deleteAgent(form); return Result.success("ok"); } /** * list agents. * * @param agentListForm the agent list form to list * @param pageForm the page form to list * @return result of the list operation * @throws NacosException if the agent list fails due to invalid input or internal error */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result> listAgents(AgentListForm agentListForm, PageForm pageForm) throws NacosException { agentListForm.validate(); pageForm.validate(); return Result.success(a2aProxy.listAgents(agentListForm, pageForm)); } /** * List all versions for target Agent. * * @param agentForm agent form * @return all version for target agent. * @throws NacosException nacos exception */ @GetMapping("/version/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.ADMIN_API) public Result> listAgentVersions(AgentForm agentForm) throws NacosException { agentForm.validate(); return Result.success(a2aProxy.listAgentVersions(agentForm.getNamespaceId(), agentForm.getAgentName())); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/ConsoleCopilotConfigController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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, express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.copilot.config.CopilotAgentManager; import com.alibaba.nacos.copilot.config.CopilotConfigStorage; import com.alibaba.nacos.copilot.config.CopilotProperties; import com.alibaba.nacos.copilot.constant.CopilotConstants; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Console Copilot configuration controller. * * @author nacos */ @NacosApi @RestController @RequestMapping(CopilotConstants.COPILOT_CONSOLE_PATH + "/config") public class ConsoleCopilotConfigController { private final CopilotConfigStorage configStorage; private final CopilotAgentManager agentManager; @Autowired public ConsoleCopilotConfigController(CopilotConfigStorage configStorage, CopilotAgentManager agentManager) { this.configStorage = configStorage; this.agentManager = agentManager; } /** * Get current Copilot configuration. * Only returns apiKey, model, studioUrl and studioProject fields. * * @return Simplified CopilotProperties with only apiKey, model, studioUrl and studioProject */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result getConfig() throws NacosException { CopilotProperties config = configStorage.getConfig(); if (config == null) { // Return default empty config if not configured config = new CopilotProperties(); } // Create simplified config with only apiKey, model, studioUrl and studioProject CopilotProperties simplifiedConfig = new CopilotProperties(); simplifiedConfig.setApiKey(config.getApiKey()); simplifiedConfig.setModel(config.getModel()); simplifiedConfig.setStudioUrl(config.getStudioUrl()); simplifiedConfig.setStudioProject(config.getStudioProject()); return Result.success(simplifiedConfig); } /** * Create or update Copilot configuration. * Only accepts apiKey, model, studioUrl and studioProject fields, other fields use defaults. * * @param config Simplified CopilotProperties with only apiKey, model, studioUrl and studioProject * @return success result */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result saveConfig(@RequestBody CopilotProperties config) throws NacosException { if (config == null) { throw new NacosException(NacosException.INVALID_PARAM, "Configuration cannot be null"); } // Get existing config to preserve other fields, or create new one with defaults CopilotProperties existingConfig = configStorage.getConfig(); CopilotProperties fullConfig; if (existingConfig != null) { // Use existing config and only update apiKey, model, studioUrl and studioProject fullConfig = existingConfig; } else { // Create new config with default values fullConfig = new CopilotProperties(); } // Update only apiKey, model, studioUrl and studioProject if (config.getApiKey() != null) { fullConfig.setApiKey(config.getApiKey()); } if (config.getModel() != null) { fullConfig.setModel(config.getModel()); } if (config.getStudioUrl() != null) { fullConfig.setStudioUrl(config.getStudioUrl()); } if (config.getStudioProject() != null) { fullConfig.setStudioProject(config.getStudioProject()); } boolean success = configStorage.saveConfig(fullConfig); if (success) { // Refresh configuration after config update agentManager.refreshConfig(); } return Result.success(success); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/ConsoleCopilotController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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, express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.copilot.adapter.StreamResponseCallback; import com.alibaba.nacos.copilot.constant.CopilotConstants; import com.alibaba.nacos.copilot.form.PromptDebugForm; import com.alibaba.nacos.copilot.form.PromptOptimizationForm; import com.alibaba.nacos.copilot.form.SkillGenerationForm; import com.alibaba.nacos.copilot.form.SkillOptimizationForm; import com.alibaba.nacos.copilot.model.PromptDebugRequest; import com.alibaba.nacos.copilot.model.PromptDebugResponse; import com.alibaba.nacos.copilot.model.PromptOptimizationRequest; import com.alibaba.nacos.copilot.model.PromptOptimizationResponse; import com.alibaba.nacos.copilot.model.SkillGenerationRequest; import com.alibaba.nacos.copilot.model.SkillGenerationResponse; import com.alibaba.nacos.copilot.model.SkillOptimizationRequest; import com.alibaba.nacos.copilot.model.SkillOptimizationResponse; import com.alibaba.nacos.copilot.service.PromptDebugService; import com.alibaba.nacos.copilot.service.PromptOptimizationService; import com.alibaba.nacos.copilot.service.SkillGenerationService; import com.alibaba.nacos.copilot.service.SkillOptimizationService; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillResource; import java.util.Map; import java.util.HashMap; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; /** * Console Copilot controller. * * @author nacos */ @NacosApi @RestController @RequestMapping(CopilotConstants.COPILOT_CONSOLE_PATH) @ExtractorManager.Extractor(httpExtractor = CopilotHttpParamExtractor.class) public class ConsoleCopilotController { private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleCopilotController.class); private final SkillOptimizationService skillOptimizationService; private final SkillGenerationService skillGenerationService; private final PromptOptimizationService promptOptimizationService; private final PromptDebugService promptDebugService; @Autowired public ConsoleCopilotController(SkillOptimizationService skillOptimizationService, SkillGenerationService skillGenerationService, PromptOptimizationService promptOptimizationService, PromptDebugService promptDebugService) { this.skillOptimizationService = skillOptimizationService; this.skillGenerationService = skillGenerationService; this.promptOptimizationService = promptOptimizationService; this.promptDebugService = promptDebugService; } /** * Optimize skill with stream response (SSE). * * @param form skill optimization form * @return SSE emitter for stream response * @throws NacosException if validation fails */ @PostMapping(value = CopilotConstants.SKILL_OPTIMIZE_PATH, produces = MediaType.TEXT_EVENT_STREAM_VALUE) @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) @SuppressWarnings("PMD.MethodTooLongRule") public SseEmitter optimizeSkillStream(@RequestBody(required = false) SkillOptimizationForm form) { // Create SSE emitter with 5 minutes timeout SseEmitter emitter = new SseEmitter(300000L); // Handle null form or missing request body if (form == null) { try { SkillOptimizationResponse errorResponse = new SkillOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("请求体不能为空"); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.completeWithError(ioException); } return emitter; } try { form.validate(); } catch (Exception e) { LOGGER.error("Form validation failed", e); try { SkillOptimizationResponse errorResponse = new SkillOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("请求验证失败:" + e.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send validation error SSE event", ioException); emitter.complete(); } return emitter; } // Build request SkillOptimizationRequest request = new SkillOptimizationRequest(); request.setSkill(form.getSkill()); request.setOptimizationGoal(form.getOptimizationGoal()); request.setConversationHistory(form.getConversationHistory()); request.setTargetFileName(form.getTargetFileName()); // Set selectedMcpTools to params if provided if (form.getSelectedMcpTools() != null && !form.getSelectedMcpTools().isEmpty()) { java.util.Map params = new java.util.HashMap<>(); params.put("selectedMcpTools", form.getSelectedMcpTools()); request.setParams(params); } // Call optimization service with stream callback skillOptimizationService.optimizeSkillStream(request, new StreamResponseCallback() { @Override public void onNext(SkillOptimizationResponse response) { try { // Filter out SKILL.md from resources before sending to frontend if (response != null && response.getOptimizedSkill() != null) { Skill optimizedSkill = response.getOptimizedSkill(); if (optimizedSkill.getResource() != null && !optimizedSkill.getResource().isEmpty()) { Map filteredResources = new HashMap<>(optimizedSkill.getResource().size()); boolean hasFiltered = false; for (Map.Entry entry : optimizedSkill.getResource().entrySet()) { String key = entry.getKey(); SkillResource resource = entry.getValue(); // Check if resource name or key is SKILL.md (case-insensitive) String resourceName = resource != null && resource.getName() != null ? resource.getName() : ""; String resourceKey = key != null ? key : ""; boolean isSkillMd = "SKILL.MD".equalsIgnoreCase(resourceName) || "SKILL.MD".equalsIgnoreCase(resourceKey) || resourceName.toUpperCase().contains("SKILL.MD") || resourceKey.toUpperCase().contains("SKILL.MD"); if (isSkillMd) { hasFiltered = true; LOGGER.warn("Filtered out SKILL.md resource: key={}, name={}", key, resourceName); continue; } filteredResources.put(key, resource); } if (hasFiltered) { optimizedSkill.setResource(filteredResources); response.setOptimizedSkill(optimizedSkill); } } } // Send SSE event emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(response)) .name("message")); } catch (IOException e) { LOGGER.error("Failed to send SSE event", e); try { SkillOptimizationResponse errorResponse = new SkillOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("流式响应发送失败:" + e.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.complete(); } } } @Override public void onError(Throwable t) { LOGGER.error("Error in skill optimization stream", t); try { // Send error response SkillOptimizationResponse errorResponse = new SkillOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("优化失败:" + t.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException e) { LOGGER.error("Failed to send error SSE event", e); emitter.complete(); } } @Override public void onComplete() { emitter.complete(); } }); return emitter; } /** * Generate skill from background information with stream response (SSE). * * @param form skill generation form * @return SSE emitter for stream response * @throws NacosException if validation fails */ @PostMapping(value = CopilotConstants.SKILL_GENERATE_PATH, produces = MediaType.TEXT_EVENT_STREAM_VALUE) @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) @SuppressWarnings("PMD.MethodTooLongRule") public SseEmitter generateSkillStream(@RequestBody(required = false) SkillGenerationForm form) { // Create SSE emitter with 5 minutes timeout SseEmitter emitter = new SseEmitter(300000L); // Handle null form or missing request body if (form == null) { try { SkillGenerationResponse errorResponse = new SkillGenerationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("请求体不能为空"); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.completeWithError(ioException); } return emitter; } try { form.validate(); } catch (Exception e) { LOGGER.error("Form validation failed", e); try { SkillGenerationResponse errorResponse = new SkillGenerationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("请求验证失败:" + e.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send validation error SSE event", ioException); emitter.complete(); } return emitter; } // Build request SkillGenerationRequest request = new SkillGenerationRequest(); request.setBackgroundInfo(form.getBackgroundInfo()); request.setSelectedMcpTools(form.getSelectedMcpTools()); request.setConversationHistory(form.getConversationHistory()); // Call generation service with stream callback skillGenerationService.generateSkillStream(request, new StreamResponseCallback() { @Override public void onNext(SkillGenerationResponse response) { try { // Send SSE event emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(response)) .name("message")); } catch (IOException e) { LOGGER.error("Failed to send SSE event", e); try { SkillGenerationResponse errorResponse = new SkillGenerationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("流式响应发送失败:" + e.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.complete(); } } } @Override public void onError(Throwable t) { LOGGER.error("Error in skill generation stream", t); try { // Send error response SkillGenerationResponse errorResponse = new SkillGenerationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("生成失败:" + t.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException e) { LOGGER.error("Failed to send error SSE event", e); emitter.complete(); } } @Override public void onComplete() { emitter.complete(); } }); return emitter; } /** * Optimize prompt with stream response (SSE). * * @param form prompt optimization form * @return SSE emitter for stream response * @throws NacosException if validation fails */ @PostMapping(value = CopilotConstants.PROMPT_OPTIMIZE_PATH, produces = MediaType.TEXT_EVENT_STREAM_VALUE) @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) @SuppressWarnings("PMD.MethodTooLongRule") public SseEmitter optimizePromptStream(@RequestBody(required = false) PromptOptimizationForm form) { // Create SSE emitter with 5 minutes timeout SseEmitter emitter = new SseEmitter(300000L); // Handle null form or missing request body if (form == null) { try { PromptOptimizationResponse errorResponse = new PromptOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("请求体不能为空"); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.completeWithError(ioException); } return emitter; } try { form.validate(); } catch (Exception e) { LOGGER.error("Form validation failed", e); try { PromptOptimizationResponse errorResponse = new PromptOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("请求验证失败:" + e.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send validation error SSE event", ioException); emitter.complete(); } return emitter; } // Build request PromptOptimizationRequest request = new PromptOptimizationRequest(); request.setPrompt(form.getPrompt()); request.setOptimizationGoal(form.getOptimizationGoal()); // Call optimization service with stream callback promptOptimizationService.optimizePromptStream(request, new StreamResponseCallback() { @Override public void onNext(PromptOptimizationResponse response) { try { // Send SSE event emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(response)) .name("message")); } catch (IOException e) { LOGGER.error("Failed to send SSE event", e); try { PromptOptimizationResponse errorResponse = new PromptOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("流式响应发送失败:" + e.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.complete(); } } } @Override public void onError(Throwable t) { LOGGER.error("Error in prompt optimization stream", t); try { // Send error response PromptOptimizationResponse errorResponse = new PromptOptimizationResponse(); errorResponse.setDone(true); errorResponse.setExplanation("优化失败:" + t.getMessage()); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException e) { LOGGER.error("Failed to send error SSE event", e); emitter.complete(); } } @Override public void onComplete() { emitter.complete(); } }); return emitter; } /** * Debug prompt with stream response (SSE). * This allows testing a prompt with user input and returns the model's response including thinking. * * @param form prompt debug form containing prompt and user input * @return SSE emitter for stream response * @throws NacosException if validation fails */ @PostMapping(value = CopilotConstants.PROMPT_DEBUG_PATH, produces = MediaType.TEXT_EVENT_STREAM_VALUE) @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) @SuppressWarnings("PMD.MethodTooLongRule") public SseEmitter debugPromptStream(@RequestBody(required = false) PromptDebugForm form) { // Create SSE emitter with 5 minutes timeout SseEmitter emitter = new SseEmitter(300000L); // Handle null form or missing request body if (form == null) { try { PromptDebugResponse errorResponse = new PromptDebugResponse(); errorResponse.setDone(true); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.completeWithError(ioException); } return emitter; } try { form.validate(); } catch (Exception e) { LOGGER.error("Form validation failed", e); try { PromptDebugResponse errorResponse = new PromptDebugResponse(); errorResponse.setDone(true); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send validation error SSE event", ioException); emitter.complete(); } return emitter; } // Build request PromptDebugRequest request = new PromptDebugRequest(); request.setPrompt(form.getPrompt()); request.setUserInput(form.getUserInput()); // Call debug service with stream callback promptDebugService.debugPromptStream(request, new StreamResponseCallback() { @Override public void onNext(PromptDebugResponse response) { try { // Send SSE event emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(response)) .name("message")); } catch (IOException e) { LOGGER.error("Failed to send SSE event", e); try { PromptDebugResponse errorResponse = new PromptDebugResponse(); errorResponse.setDone(true); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send error SSE event", ioException); emitter.complete(); } } } @Override public void onError(Throwable t) { LOGGER.error("Error in prompt debug stream", t); try { // Send error response PromptDebugResponse errorResponse = new PromptDebugResponse(); errorResponse.setDone(true); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException e) { LOGGER.error("Failed to send error SSE event", e); emitter.complete(); } } @Override public void onComplete() { emitter.complete(); } }); return emitter; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/ConsoleMcpController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.mcp.admin.McpDetailForm; import com.alibaba.nacos.ai.form.mcp.admin.McpForm; import com.alibaba.nacos.ai.form.mcp.admin.McpImportForm; import com.alibaba.nacos.ai.form.mcp.admin.McpListForm; import com.alibaba.nacos.ai.form.mcp.admin.McpUpdateForm; import com.alibaba.nacos.ai.param.McpHttpParamExtractor; import com.alibaba.nacos.ai.utils.McpRequestUtil; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.api.utils.StringUtils; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.console.proxy.ai.McpProxy; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import io.modelcontextprotocol.client.McpClient; import io.modelcontextprotocol.client.McpSyncClient; import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport; import io.modelcontextprotocol.client.transport.HttpClientStreamableHttpTransport; import io.modelcontextprotocol.spec.McpClientTransport; import io.modelcontextprotocol.spec.McpSchema; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.time.Duration; import java.util.List; import static com.alibaba.nacos.api.ai.constant.AiConstants.Mcp.MCP_PROTOCOL_SSE; import static com.alibaba.nacos.api.ai.constant.AiConstants.Mcp.MCP_PROTOCOL_STREAMABLE; /** * Nacos Console AI MCP Server Constants. * * @author xiweng.yy */ @NacosApi @RestController @RequestMapping(Constants.MCP_CONSOLE_PATH) @ExtractorManager.Extractor(httpExtractor = McpHttpParamExtractor.class) public class ConsoleMcpController { private final McpProxy mcpProxy; public ConsoleMcpController(McpProxy mcpProxy) { this.mcpProxy = mcpProxy; } /** * List mcp server. * * @param mcpListForm list mcp servers request form * @param pageForm page info * @return mcp server list wrapper with {@link Result} * @throws NacosApiException if request parameter is invalid or handle error */ @GetMapping(value = "/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result> listMcpServers(McpListForm mcpListForm, PageForm pageForm) throws NacosException { mcpListForm.validate(); pageForm.validate(); return Result.success( mcpProxy.listMcpServers(mcpListForm.getNamespaceId(), mcpListForm.getMcpName(), mcpListForm.getSearch(), pageForm.getPageNo(), pageForm.getPageSize())); } /** * Import tools from mcp result. * * @param transportType the transport type * @param baseUrl the base url * @param endpoint the endpoint * @return the result * @throws NacosException the nacos exception */ @GetMapping("/importToolsFromMcp") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result> importToolsFromMcp(@RequestParam String transportType, @RequestParam String baseUrl, @RequestParam String endpoint, @RequestParam(required = false) String authToken) throws NacosException { McpClientTransport transport = null; if (StringUtils.equals(transportType, MCP_PROTOCOL_SSE)) { HttpClientSseClientTransport.Builder transportBuilder = HttpClientSseClientTransport.builder(baseUrl) .sseEndpoint(endpoint); if (!StringUtils.isBlank(authToken)) { transportBuilder.customizeRequest(req -> req.header("Authorization", "Bearer " + authToken)); } transport = transportBuilder.build(); } else if (StringUtils.equals(transportType, MCP_PROTOCOL_STREAMABLE)) { HttpClientStreamableHttpTransport.Builder transportBuilder = HttpClientStreamableHttpTransport.builder( baseUrl).endpoint(endpoint); if (!StringUtils.isBlank(authToken)) { transportBuilder.customizeRequest(req -> req.header("Authorization", "Bearer " + authToken)); } transport = transportBuilder.build(); } else { return Result.failure(ErrorCode.SERVER_ERROR.getCode(), "Unsupported transport type: " + transportType, null); } try (McpSyncClient client = McpClient.sync(transport).requestTimeout(Duration.ofSeconds(10)).build()) { client.initialize(); McpSchema.ListToolsResult tools = client.listTools(); return Result.success(tools.tools()); } catch (Exception e) { // 可以记录日志或抛出 NacosException throw new NacosException(NacosException.SERVER_ERROR, "Failed to import tools from MCP server", e); } } /** * Get specified mcp server detail info. * * @param mcpForm get mcp server request form * @return detail info with {@link McpServerDetailInfo} * @throws NacosException any exception during handling */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result getMcpServer(McpForm mcpForm) throws NacosException { mcpForm.validate(); return Result.success(mcpProxy.getMcpServer(mcpForm.getNamespaceId(), mcpForm.getMcpName(), mcpForm.getMcpId(), mcpForm.getVersion())); } /** * Create new mcp server. * * @param mcpForm create mcp server request form * @throws NacosException any exception during handling */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result createMcpServer(McpDetailForm mcpForm) throws NacosException { mcpForm.validate(); McpServerBasicInfo basicInfo = McpRequestUtil.parseMcpServerBasicInfo(mcpForm); McpToolSpecification mcpTools = McpRequestUtil.parseMcpTools(mcpForm); McpEndpointSpec endpointSpec = McpRequestUtil.parseMcpEndpointSpec(basicInfo, mcpForm); String mcpId = mcpProxy.createMcpServer(mcpForm.getNamespaceId(), basicInfo, mcpTools, endpointSpec); return Result.success(mcpId); } /** * Update existed mcp server. * *

    * `namespaceId` and `mcpName` can't be changed. *

    * * @param mcpForm update mcp servers request form * @throws NacosException any exception during handling */ @PutMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result updateMcpServer(McpUpdateForm mcpForm) throws NacosException { mcpForm.validate(); McpServerBasicInfo basicInfo = McpRequestUtil.parseMcpServerBasicInfo(mcpForm); McpToolSpecification mcpTools = McpRequestUtil.parseMcpTools(mcpForm); McpEndpointSpec endpointSpec = McpRequestUtil.parseMcpEndpointSpec(basicInfo, mcpForm); mcpProxy.updateMcpServer(mcpForm.getNamespaceId(), mcpForm.getLatest(), basicInfo, mcpTools, endpointSpec, mcpForm.isOverrideExisting()); return Result.success("ok"); } /** * Delete existed mcp server. * * @param mcpForm delete mcp server request form * @throws NacosException any exception during handling */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result deleteMcpServer(McpForm mcpForm) throws NacosException { mcpForm.validate(); mcpProxy.deleteMcpServer(mcpForm.getNamespaceId(), mcpForm.getMcpName(), mcpForm.getMcpId(), mcpForm.getVersion()); return Result.success("ok"); } /** * Validate MCP server import request. * * @param mcpImportForm import request form * @return validation result with details about potential issues * @throws NacosException any exception during validation */ @PostMapping("/import/validate") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result validateImport(McpImportForm mcpImportForm) throws NacosException { mcpImportForm.validate(); McpServerImportRequest request = convertToImportRequest(mcpImportForm); McpServerImportValidationResult result = mcpProxy.validateImport(mcpImportForm.getNamespaceId(), request); return Result.success(result); } /** * Execute MCP server import operation. * * @param mcpImportForm import request form * @return import response with results and statistics * @throws NacosException any exception during import execution */ @PostMapping("/import/execute") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result executeImport(McpImportForm mcpImportForm) throws NacosException { mcpImportForm.validate(); McpServerImportRequest request = convertToImportRequest(mcpImportForm); McpServerImportResponse response = mcpProxy.executeImport(mcpImportForm.getNamespaceId(), request); return Result.success(response); } /** * Convert McpImportForm to McpServerImportRequest. * * @param form the form from HTTP request * @return the import request for service layer */ private McpServerImportRequest convertToImportRequest(McpImportForm form) { McpServerImportRequest request = new McpServerImportRequest(); request.setImportType(form.getImportType()); request.setData(form.getData()); request.setOverrideExisting(form.isOverrideExisting()); request.setValidateOnly(form.isValidateOnly()); request.setSkipInvalid(form.isSkipInvalid()); request.setSelectedServers(form.getSelectedServers()); // Optional URL pagination parameters request.setCursor(form.getCursor()); request.setLimit(form.getLimit()); // Optional registry search parameter request.setSearch(form.getSearch()); return request; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/ConsolePromptController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.prompt.PromptForm; import com.alibaba.nacos.ai.form.prompt.PromptHistoryForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelBindForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelForm; import com.alibaba.nacos.ai.form.prompt.PromptListForm; import com.alibaba.nacos.ai.form.prompt.PromptMetadataForm; import com.alibaba.nacos.ai.form.prompt.PromptPublishForm; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.ai.param.PromptHttpParamExtractor; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.console.proxy.ai.PromptProxy; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import jakarta.servlet.http.HttpServletRequest; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Console prompt controller. * *

    Provides REST APIs for prompt management operations in console.

    * * @author nacos */ @NacosApi @RestController @RequestMapping(Constants.Prompt.CONSOLE_PATH) @ExtractorManager.Extractor(httpExtractor = PromptHttpParamExtractor.class) public class ConsolePromptController { private final PromptProxy promptProxy; public ConsolePromptController(PromptProxy promptProxy) { this.promptProxy = promptProxy; } /** * Publish a new version of prompt. * * @param form the prompt publish form * @param request HTTP request for getting client info * @return result of the publish operation * @throws NacosException if the prompt publish fails */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result publishPrompt(PromptPublishForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptProxy.publishPrompt(form, srcUser, srcIp); return Result.success(success); } @GetMapping("/metadata") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result getPromptMeta(PromptForm form) throws NacosException { form.validate(); PromptMetaInfo detail = promptProxy.getPromptMeta(form); return Result.success(detail); } /** * Query prompt detail by label/version/latest with priority label > version > latest. */ @GetMapping("/detail") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result queryPromptDetail(PromptQueryForm form) throws NacosException { form.validate(); PromptVersionInfo detail = promptProxy.queryPromptDetail(form); return Result.success(detail); } /** * Bind label to a specified prompt version. */ @PutMapping("/label") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result bindLabel(PromptLabelBindForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptProxy.bindLabel(form, srcUser, srcIp); return Result.success(success); } /** * Unbind label from prompt. */ @DeleteMapping("/label") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result unbindLabel(PromptLabelForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptProxy.unbindLabel(form, srcUser, srcIp); return Result.success(success); } /** * Delete prompt. * * @param form the prompt form * @param request HTTP request for getting client info * @return result of the deletion operation * @throws NacosException if the prompt deletion fails */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result deletePrompt(PromptForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptProxy.deletePrompt(form, srcUser, srcIp); return Result.success(success); } /** * List prompts with pagination. * * @param form the prompt list form * @return result of the list operation * @throws NacosException if the prompt list fails */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result> listPrompts(PromptListForm form) throws NacosException { form.validate(); Page result = promptProxy.listPrompts(form); return Result.success(result); } /** * List prompt versions with pagination. * * @param form the prompt history form * @return result of the version list operation * @throws NacosException if the version list fails */ @GetMapping("/versions") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result> listPromptVersions(PromptHistoryForm form) throws NacosException { form.validate(); Page result = promptProxy.listPromptVersions(form); return Result.success(result); } /** * Update prompt metadata (description only). * * @param form the prompt metadata form * @param request HTTP request for getting client info * @return result of the update operation * @throws NacosException if the update fails */ @PutMapping("/metadata") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result updatePromptMetadata(PromptMetadataForm form, HttpServletRequest request) throws NacosException { form.validate(); String srcUser = request.getRemoteUser(); String srcIp = request.getRemoteAddr(); boolean success = promptProxy.updatePromptMetadata(form, srcUser, srcIp); return Result.success(success); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/ConsoleSkillController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.ai.form.skills.admin.SkillForm; import com.alibaba.nacos.ai.form.skills.admin.SkillListForm; import com.alibaba.nacos.ai.form.skills.admin.SkillUpdateForm; import com.alibaba.nacos.ai.param.SkillHttpParamExtractor; import com.alibaba.nacos.ai.utils.SkillRequestUtil; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.console.proxy.ai.SkillProxy; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import jakarta.servlet.http.HttpServletRequest; /** * Console skill controller. * * @author nacos */ @NacosApi @RestController @RequestMapping(Constants.Skills.CONSOLE_PATH) @ExtractorManager.Extractor(httpExtractor = SkillHttpParamExtractor.class) public class ConsoleSkillController { private final SkillProxy skillProxy; public ConsoleSkillController(SkillProxy skillProxy) { this.skillProxy = skillProxy; } /** * Register skill. * * @param form the skill detail form to register * @return result of the registration operation * @throws NacosException if the skill registration fails */ @PostMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result registerSkill(SkillDetailForm form) throws NacosException { form.validate(); Skill skill = SkillRequestUtil.parseSkill(form); skillProxy.registerSkill(skill, form); return Result.success("ok"); } /** * Get skill. * * @param form the skill form to get * @return result of the get operation * @throws NacosException if the skill get fails */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result getSkill(SkillForm form) throws NacosException { form.validate(); return Result.success(skillProxy.getSkill(form)); } /** * Update skill. * * @param form the skill update form to update * @return result of the update operation * @throws NacosException if the skill update fails */ @PutMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result updateSkill(SkillUpdateForm form) throws NacosException { form.validate(); Skill skill = SkillRequestUtil.parseSkill(form); skillProxy.updateSkill(skill, form); return Result.success("ok"); } /** * Delete skill. * * @param form the skill form to delete * @return result of the deletion operation * @throws NacosException if the skill deletion fails */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result deleteSkill(SkillForm form) throws NacosException { form.validate(); skillProxy.deleteSkill(form); return Result.success("ok"); } /** * List skills. * * @param skillListForm the skill list form to list * @param pageForm the page form to list * @return result of the list operation * @throws NacosException if the skill list fails */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.AI, apiType = ApiType.CONSOLE_API) public Result> listSkills(SkillListForm skillListForm, PageForm pageForm) throws NacosException { skillListForm.validate(); pageForm.validate(); return Result.success(skillProxy.listSkills(skillListForm, pageForm)); } /** * Upload skill from zip file. * * @param request HTTP servlet request * @param namespaceId namespace ID * @param file zip file containing skill * @return result of the upload operation * @throws NacosException if the upload fails */ @PostMapping(value = "/upload", consumes = "multipart/form-data") @Secured(action = ActionTypes.WRITE, signType = SignType.AI, apiType = ApiType.CONSOLE_API) @ExtractorManager.Extractor(httpExtractor = ExtractorManager.DefaultHttpExtractor.class) public Result uploadSkill(HttpServletRequest request, @RequestParam(value = "namespaceId", required = false) String namespaceId, @RequestParam("file") MultipartFile file) throws NacosException { namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); byte[] zipBytes = SkillRequestUtil.validateAndExtractZipBytes(file); String skillName = skillProxy.uploadSkillFromZip(namespaceId, zipBytes); return Result.success(skillName); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/CopilotHttpParamExtractor.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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, express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.util.Collections; import java.util.List; /** * Copilot HTTP parameter extractor. * * @author nacos */ public class CopilotHttpParamExtractor extends AbstractHttpParamExtractor { private static final String HTTP_METHOD_POST = "POST"; private static final String SKILL_JSON_KEY = "\"skill\""; @Override public List extractParam(HttpServletRequest request) throws NacosException { ParamInfo paramInfo = new ParamInfo(); // Try to extract skill name from request body for optimization requests if (HTTP_METHOD_POST.equalsIgnoreCase(request.getMethod())) { try { StringBuilder body = new StringBuilder(); try (BufferedReader reader = request.getReader()) { String line; while ((line = reader.readLine()) != null) { body.append(line); } } if (body.length() > 0) { // Parse JSON body to extract skill name String bodyStr = body.toString(); if (bodyStr.contains(SKILL_JSON_KEY)) { // Extract skill from request body try { java.util.Map bodyMap = JacksonUtils.toObj(bodyStr, java.util.Map.class); java.util.Map skillMap = (java.util.Map) bodyMap.get("skill"); if (skillMap != null) { Skill skill = JacksonUtils.toObj(JacksonUtils.toJson(skillMap), Skill.class); if (skill != null && StringUtils.isNotBlank(skill.getName())) { paramInfo.setAgentName(skill.getName()); paramInfo.setNamespaceId(skill.getNamespaceId()); } } } catch (Exception e) { // Ignore parsing errors } } } } catch (Exception e) { // Ignore errors } } // Fallback to query parameters if (StringUtils.isBlank(paramInfo.getAgentName())) { paramInfo.setAgentName(request.getParameter("skillName")); } if (StringUtils.isBlank(paramInfo.getNamespaceId())) { paramInfo.setNamespaceId(request.getParameter("namespaceId")); } return Collections.singletonList(paramInfo); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/ai/CopilotSseExceptionHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.ai; import com.alibaba.nacos.copilot.model.SkillOptimizationResponse; import com.alibaba.nacos.common.utils.JacksonUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import jakarta.servlet.http.HttpServletRequest; import java.io.IOException; /** * Exception handler for Copilot SSE endpoints. * This handler has higher priority than NacosApiExceptionHandler to ensure * all exceptions are returned as SSE events. * * @author nacos */ @Order(-2) @ControllerAdvice(basePackages = "com.alibaba.nacos.console.controller.v3.ai") public class CopilotSseExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(CopilotSseExceptionHandler.class); /** * Handle all exceptions for SSE endpoints. * This ensures exceptions are returned as SSE events instead of Result objects. * Only handles SSE endpoints (requests that accept text/event-stream or have SSE path). * * @param e exception * @param request HTTP request * @return SSE emitter with error event, or rethrow exception for non-SSE requests */ @ExceptionHandler(Exception.class) @ResponseBody public Object handleException(Exception e, HttpServletRequest request) { // Only handle SSE requests - check Accept header or request path String acceptHeader = request.getHeader("Accept"); String requestPath = request.getRequestURI(); // Check if this is an SSE endpoint (optimize or generate endpoint) or accepts SSE boolean isSseRequest = (acceptHeader != null && acceptHeader.contains(MediaType.TEXT_EVENT_STREAM_VALUE)) || (requestPath != null && (requestPath.contains("/skill/optimize") || requestPath.contains("/skill/generate"))); if (!isSseRequest) { // Not an SSE request, rethrow to let other exception handlers process it if (e instanceof RuntimeException) { throw (RuntimeException) e; } else { throw new RuntimeException(e); } } LOGGER.error("Exception in Copilot SSE endpoint", e); SseEmitter emitter = new SseEmitter(1000L); try { SkillOptimizationResponse errorResponse = new SkillOptimizationResponse(); errorResponse.setDone(true); String errorMsg = e.getMessage(); if (errorMsg == null || errorMsg.isEmpty()) { errorMsg = e.getClass().getSimpleName(); } errorResponse.setExplanation("请求处理失败:" + errorMsg); emitter.send(SseEmitter.event() .data(JacksonUtils.toJson(errorResponse)) .name("error")); emitter.complete(); } catch (IOException ioException) { LOGGER.error("Failed to send exception SSE event", ioException); emitter.complete(); } return emitter; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/config/ConsoleConfigController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.config; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.config.ConfigType; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.constant.ParametersField; import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.paramcheck.ConfigBlurSearchHttpParamExtractor; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.config.server.utils.ParamUtils; import com.alibaba.nacos.config.server.utils.RequestUtil; import com.alibaba.nacos.console.proxy.config.ConfigProxy; import com.alibaba.nacos.core.model.form.AggregationForm; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.alibaba.nacos.config.server.utils.RequestUtil.getRemoteIp; /** * Controller for handling HTTP requests related to configuration operations. * * @author zhangyukun */ @NacosApi @RestController @RequestMapping("/v3/console/cs/config") @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class ConsoleConfigController { private final ConfigProxy configProxy; public ConsoleConfigController(ConfigProxy configProxy) { this.configProxy = configProxy; } /** * Get the specific configuration information. * * @param configForm config form * @return Result containing detailed configuration information. * @throws NacosException If a Nacos-specific error occurs. */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result getConfigDetail(ConfigFormV3 configForm) throws NacosException { configForm.validate(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); return Result.success(configProxy.getConfigDetail(dataId, groupName, namespaceId)); } /** * Add or update configuration. * * @param request HTTP servlet request. * @param configForm Configuration form. * @return Result containing success status. * @throws NacosException If a Nacos-specific error occurs. */ @PostMapping() @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result publishConfig(HttpServletRequest request, ConfigFormV3 configForm) throws NacosException { // check required field configForm.validateWithContent(); final boolean namespaceTransferred = NamespaceUtil.isNeedTransferNamespace(configForm.getNamespaceId()); configForm.setNamespaceId(NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId())); // check param ParamUtils.checkParam(configForm.getDataId(), configForm.getGroup(), "datumId", configForm.getContent()); ParamUtils.checkParamV2(configForm.getTag()); if (StringUtils.isBlank(configForm.getSrcUser())) { configForm.setSrcUser(RequestUtil.getSrcUserName(request)); } if (!ConfigType.isValidType(configForm.getType())) { configForm.setType(ConfigType.getDefaultType().getType()); } ConfigRequestInfo configRequestInfo = new ConfigRequestInfo(); configRequestInfo.setSrcIp(RequestUtil.getRemoteIp(request)); configRequestInfo.setRequestIpApp(RequestUtil.getAppName(request)); configRequestInfo.setBetaIps(request.getHeader("betaIps")); configRequestInfo.setCasMd5(request.getHeader("casMd5")); configRequestInfo.setNamespaceTransferred(namespaceTransferred); return Result.success(configProxy.publishConfig(configForm, configRequestInfo)); } /** * Delete configuration. * * @param request HTTP servlet request. * @param configForm Config form. * @return Result containing success status. * @throws NacosException If a Nacos-specific error occurs. */ @DeleteMapping @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result deleteConfig(HttpServletRequest request, ConfigFormV3 configForm) throws NacosException { configForm.validate(); //fix issue #9783 String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); ParamUtils.checkParamV2(configForm.getTag()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String tag = configForm.getTag(); String clientIp = RequestUtil.getRemoteIp(request); String srcUser = RequestUtil.getSrcUserName(request); return Result.success(configProxy.deleteConfig(dataId, groupName, namespaceId, tag, clientIp, srcUser)); } /** * Batch delete configurations. * * @param request HTTP servlet request. * @param ids List of config IDs. * @return Result containing success status. * @throws NacosException If a Nacos-specific error occurs. */ @DeleteMapping("/batchDelete") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result batchDeleteConfigs(HttpServletRequest request, @RequestParam(value = "ids") List ids) throws NacosException { String clientIp = RequestUtil.getRemoteIp(request); String srcUser = RequestUtil.getSrcUserName(request); return Result.success(configProxy.batchDeleteConfigs(ids, clientIp, srcUser)); } /** * Get configure information list. * * @param configForm config form * @param pageForm page form * @return Result containing the configuration information. * @throws ServletException If a servlet-specific error occurs. * @throws IOException If an I/O error occurs. * @throws NacosException If a Nacos-specific error occurs. */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) @ExtractorManager.Extractor(httpExtractor = ConfigBlurSearchHttpParamExtractor.class) public Result> getConfigList(ConfigFormV3 configForm, PageForm pageForm) throws IOException, ServletException, NacosException { configForm.blurSearchValidate(); pageForm.validate(); Map configAdvanceInfo = new HashMap<>(100); if (StringUtils.isNotBlank(configForm.getAppName())) { configAdvanceInfo.put("appName", configForm.getAppName()); } if (StringUtils.isNotBlank(configForm.getConfigTags())) { configAdvanceInfo.put("config_tags", configForm.getConfigTags()); } if (StringUtils.isNotBlank(configForm.getType())) { configAdvanceInfo.put(ParametersField.TYPES, configForm.getType()); } int pageNo = pageForm.getPageNo(); int pageSize = pageForm.getPageSize(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); return Result.success( configProxy.getConfigList(pageNo, pageSize, dataId, groupName, namespaceId, configAdvanceInfo)); } /** * Search config list by config detail. * * @param configForm config form * @param pageForm page form * @param configDetail Configuration detail string value. * @param search Search type. * @return Result containing the configuration list by content. * @throws NacosException If a Nacos-specific error occurs. */ @GetMapping("/searchDetail") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) @ExtractorManager.Extractor(httpExtractor = ConfigBlurSearchHttpParamExtractor.class) public Result> getConfigListByContent(ConfigFormV3 configForm, PageForm pageForm, String configDetail, @RequestParam(defaultValue = "blur") String search) throws NacosException { configForm.blurSearchValidate(); pageForm.validate(); Map configAdvanceInfo = new HashMap<>(100); if (StringUtils.isNotBlank(configForm.getAppName())) { configAdvanceInfo.put("appName", configForm.getAppName()); } if (StringUtils.isNotBlank(configForm.getConfigTags())) { configAdvanceInfo.put("config_tags", configForm.getConfigTags()); } if (StringUtils.isNotBlank(configForm.getType())) { configAdvanceInfo.put(ParametersField.TYPES, configForm.getType()); } if (StringUtils.isNotBlank(configDetail)) { configAdvanceInfo.put("content", configDetail); } int pageNo = pageForm.getPageNo(); int pageSize = pageForm.getPageSize(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); return Result.success( configProxy.getConfigListByContent(search, pageNo, pageSize, dataId, groupName, namespaceId, configAdvanceInfo)); } /** * Subscribe to configured client information. * * @param configForm config form * @param aggregationForm aggregation form * @return Result containing listener status. * @throws Exception If an error occurs during the operation. */ @GetMapping("/listener") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result getListeners(ConfigFormV3 configForm, AggregationForm aggregationForm) throws Exception { configForm.validate(); aggregationForm.validate(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String groupName = configForm.getGroupName(); String dataId = configForm.getDataId(); return Result.success( configProxy.getListeners(dataId, groupName, namespaceId, aggregationForm.isAggregation())); } /** * Get subscribe information from client side. */ @GetMapping("/listener/ip") @Secured(resource = Constants.LISTENER_CONTROLLER_PATH, action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result getAllSubClientConfigByIp(@RequestParam("ip") String ip, @RequestParam(value = "all", required = false) boolean all, @RequestParam(value = "namespaceId", required = false) String namespaceId, AggregationForm aggregationForm) throws NacosException { namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); return Result.success( configProxy.getAllSubClientConfigByIp(ip, all, namespaceId, aggregationForm.isAggregation())); } /** * New version export config adds metadata.yml file to record config metadata. * * @param configForm config form * @param ids List of config IDs. * @return ResponseEntity containing the exported configuration. * @throws Exception If an error occurs during the export. */ @GetMapping("/export2") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public ResponseEntity exportConfigV2(ConfigFormV3 configForm, @RequestParam(value = "ids", required = false) List ids) throws Exception { configForm.blurSearchValidate(); ids.removeAll(Collections.singleton(null)); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String appName = configForm.getAppName(); return configProxy.exportConfigV2(dataId, groupName, namespaceId, appName, ids); } /** * Import and publish configuration. * * @param request HTTP servlet request. * @param srcUser Source user string value. * @param namespaceId Namespace string value. * @param policy Policy model. * @param file Multipart file containing the configuration data. * @return Result containing a map of the import status. * @throws NacosException If a Nacos-specific error occurs. */ @PostMapping("/import") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result> importAndPublishConfig(HttpServletRequest request, @RequestParam(required = false) String srcUser, @RequestParam(value = "namespaceId", required = false) String namespaceId, @RequestParam(value = "policy", defaultValue = "ABORT") SameConfigPolicy policy, MultipartFile file) throws NacosException { namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); if (StringUtils.isBlank(srcUser)) { srcUser = RequestUtil.getSrcUserName(request); } final String srcIp = RequestUtil.getRemoteIp(request); String requestIpApp = RequestUtil.getAppName(request); return configProxy.importAndPublishConfig(srcUser, namespaceId, policy, file, srcIp, requestIpApp); } /** * Clone configuration. * * @param request HTTP servlet request. * @param srcUser Source user string value. * @param namespaceId Namespace string value. * @param configBeansList List of configuration beans. * @param policy Policy model. * @return Result containing a map of the clone status. * @throws NacosException If a Nacos-specific error occurs. */ @PostMapping("/clone") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API, tags = { com.alibaba.nacos.plugin.auth.constant.Constants.Tag.SECURED_SPECIAL_TAGS}) public Result> cloneConfig(HttpServletRequest request, @RequestParam(required = false) String srcUser, @RequestParam(value = "targetNamespaceId") String namespaceId, @RequestBody List configBeansList, @RequestParam(value = "policy", defaultValue = "ABORT") SameConfigPolicy policy) throws NacosException { configBeansList.removeAll(Collections.singleton(null)); namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); if (StringUtils.isBlank(srcUser)) { srcUser = RequestUtil.getSrcUserName(request); } final String srcIp = RequestUtil.getRemoteIp(request); String requestIpApp = RequestUtil.getAppName(request); return configProxy.cloneConfig(srcUser, namespaceId, configBeansList, policy, srcIp, requestIpApp); } /** * Execute to remove beta operation. * * @param httpServletRequest HTTP request containing client details. * @param configForm config form * @return Result indicating the outcome of the operation. * @throws NacosException If a Nacos-specific error occurs. */ @DeleteMapping("/beta") @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG) public Result stopBeta(HttpServletRequest httpServletRequest, ConfigFormV3 configForm) throws NacosException { configForm.validate(); String remoteIp = getRemoteIp(httpServletRequest); String requestIpApp = RequestUtil.getAppName(httpServletRequest); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); String srcUser = RequestUtil.getSrcUserName(httpServletRequest); boolean success = configProxy.removeBetaConfig(dataId, groupName, namespaceId, remoteIp, requestIpApp, srcUser); if (!success) { return Result.failure(HttpStatus.INTERNAL_SERVER_ERROR.value(), HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(), false); } return Result.success(true); } /** * Execute to query beta operation. * * @param configForm config form * @return Result containing the ConfigInfo4Beta details. * @throws NacosException If a Nacos-specific error occurs. */ @GetMapping("/beta") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG) public Result queryBeta(ConfigFormV3 configForm) throws NacosException { configForm.validate(); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); return Result.success(configProxy.queryBetaConfig(dataId, groupName, namespaceId)); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/config/ConsoleHistoryController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.config; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.config.server.model.form.ConfigFormV3; import com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor; import com.alibaba.nacos.console.proxy.config.HistoryProxy; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * Controller for handling HTTP requests related to history operations. * * @author zhangyukun on:2024/8/16 */ @NacosApi @RestController @RequestMapping("/v3/console/cs/history") @ExtractorManager.Extractor(httpExtractor = ConfigDefaultHttpParamExtractor.class) public class ConsoleHistoryController { private final HistoryProxy historyProxy; @Autowired public ConsoleHistoryController(HistoryProxy historyProxy) { this.historyProxy = historyProxy; } /** * Query the detailed configuration history information. notes: * * @param nid history_config_info nid * @param configForm config form * @return history config info */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result getConfigHistoryInfo(ConfigFormV3 configForm, @RequestParam("nid") Long nid) throws NacosException { configForm.validate(); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); return Result.success(historyProxy.getConfigHistoryInfo(dataId, groupName, namespaceId, nid)); } /** * Query the list history config. notes: * * @param configForm config form * @param pageForm page form * @return the page of history config. */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result> listConfigHistory(ConfigFormV3 configForm, PageForm pageForm) throws NacosException { configForm.validate(); pageForm.validate(); int pageSize = Math.min(500, pageForm.getPageSize()); int pageNo = pageForm.getPageNo(); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); return Result.success(historyProxy.listConfigHistory(dataId, groupName, namespaceId, pageNo, pageSize)); } /** * Query previous config history information. notes: * * @param id config_info id * @param configForm config form * @return history config info */ @GetMapping(value = "/previous") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result getPreviousConfigHistoryInfo(ConfigFormV3 configForm, @RequestParam("id") Long id) throws NacosException { configForm.validate(); String dataId = configForm.getDataId(); String groupName = configForm.getGroupName(); String namespaceId = NamespaceUtil.processNamespaceParameter(configForm.getNamespaceId()); return Result.success(historyProxy.getPreviousConfigHistoryInfo(dataId, groupName, namespaceId, id)); } /** * Query configs list by namespace. * * @param namespaceId config_info namespace * @return list */ @GetMapping(value = "/configs") @Secured(action = ActionTypes.READ, signType = SignType.CONFIG, apiType = ApiType.CONSOLE_API) public Result> getConfigsByTenant(@RequestParam("namespaceId") String namespaceId) throws NacosException { namespaceId = NamespaceUtil.processNamespaceParameter(namespaceId); return Result.success(historyProxy.getConfigsByTenant(namespaceId)); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/core/ConsoleClusterController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.core; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.NacosMember; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.console.proxy.core.ClusterProxy; import com.alibaba.nacos.core.utils.Commons; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.Collection; /** * Controller for handling HTTP requests related to cluster operations. * * @author zhangyukun on:2024/8/16 */ @NacosApi @RestController @RequestMapping("/v3/console/core/cluster") public class ConsoleClusterController { private final ClusterProxy clusterProxy; /** * Constructs a new ConsoleClusterController with the provided ClusterProxy. * * @param clusterProxy the proxy used for handling cluster-related operations */ public ConsoleClusterController(ClusterProxy clusterProxy) { this.clusterProxy = clusterProxy; } /** * The console displays the list of cluster members. * * @param ipKeyWord search keyWord * @return all members */ @GetMapping(value = "/nodes") @Secured(resource = Commons.NACOS_CORE_CONTEXT + "/cluster", action = ActionTypes.READ, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result> getNodeList(@RequestParam(value = "keyword", required = false) String ipKeyWord) throws NacosException { Collection result = clusterProxy.getNodeList(ipKeyWord); return Result.success(result); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/core/ConsoleNamespaceController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.core; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor; import com.alibaba.nacos.console.proxy.core.NamespaceProxy; import com.alibaba.nacos.core.namespace.model.form.CreateNamespaceForm; import com.alibaba.nacos.core.namespace.model.form.NamespaceForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.Constants; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * Controller for handling HTTP requests related to namespace operations. * * @author zhangyukun on:2024/8/27 */ @NacosApi @RestController @RequestMapping("/v3/console/core/namespace") @ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class) public class ConsoleNamespaceController { private final NamespaceProxy namespaceProxy; public ConsoleNamespaceController(NamespaceProxy namespaceProxy) { this.namespaceProxy = namespaceProxy; } /** * Get namespace list. * * @return namespace list */ @GetMapping("/list") @Secured(resource = Constants.Resource.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.READ, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API, tags = Constants.Tag.ONLY_IDENTITY) public Result> getNamespaceList() throws NacosException { return Result.success(namespaceProxy.getNamespaceList()); } /** * get namespace all info by namespace id. * * @param namespaceId namespaceId * @return namespace all info */ @GetMapping() @Secured(resource = Constants.Resource.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.READ, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result getNamespaceDetail(@RequestParam("namespaceId") String namespaceId) throws NacosException { return Result.success(namespaceProxy.getNamespaceDetail(namespaceId)); } /** * create namespace. * * @param namespaceForm create namespace form. * @return whether create ok */ @PostMapping @Secured(resource = Constants.Resource.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result createNamespace(CreateNamespaceForm namespaceForm) throws NacosException { namespaceForm.validate(); String namespaceId = namespaceForm.getCustomNamespaceId(); String namespaceName = namespaceForm.getNamespaceName(); String namespaceDesc = namespaceForm.getNamespaceDesc(); return Result.success(namespaceProxy.createNamespace(namespaceId, namespaceName, namespaceDesc)); } /** * edit namespace. * * @param namespaceForm namespace form * @return whether edit ok */ @PutMapping @Secured(resource = Constants.Resource.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result updateNamespace(NamespaceForm namespaceForm) throws NacosException { namespaceForm.validate(); return Result.success(namespaceProxy.updateNamespace(namespaceForm)); } /** * delete namespace by id. * * @param namespaceId namespace ID * @return whether delete ok */ @DeleteMapping @Secured(resource = Constants.Resource.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.WRITE, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result deleteNamespace(@RequestParam("namespaceId") String namespaceId) throws NacosException { return Result.success(namespaceProxy.deleteNamespace(namespaceId)); } /** * check namespaceId exist. * * @param namespaceId namespace id * @return true if exist, otherwise false */ @GetMapping("/exist") @Secured(resource = Constants.Resource.CONSOLE_RESOURCE_NAME_PREFIX + "namespaces", action = ActionTypes.READ, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API, tags = Constants.Tag.ONLY_IDENTITY) public Result checkNamespaceIdExist(@RequestParam("customNamespaceId") String namespaceId) throws NacosException { // customNamespaceId if blank means create new namespace with uuid. if (StringUtils.isBlank(namespaceId)) { return Result.success(false); } return Result.success(namespaceProxy.checkNamespaceIdExist(namespaceId)); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/core/ConsolePluginController.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.core; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor; import com.alibaba.nacos.console.proxy.core.PluginProxy; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.core.plugin.model.form.PluginConfigForm; import com.alibaba.nacos.core.plugin.model.vo.PluginDetailVO; import com.alibaba.nacos.core.plugin.model.vo.PluginInfoVO; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.SignType; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.Map; /** * Controller for handling HTTP requests related to plugin operations. * * @author WangzJi */ @NacosApi @RestController @RequestMapping("/v3/console/plugin") @ExtractorManager.Extractor(httpExtractor = ConsoleDefaultHttpParamExtractor.class) public class ConsolePluginController { private final PluginProxy pluginProxy; public ConsolePluginController(PluginProxy pluginProxy) { this.pluginProxy = pluginProxy; } /** * Get plugin list. * * @param pluginType plugin type filter (optional) * @return plugin list */ @GetMapping("/list") @Secured(action = ActionTypes.READ, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result> getPluginList( @RequestParam(value = "pluginType", required = false) String pluginType) throws NacosException { return Result.success(pluginProxy.listPlugins(pluginType)); } /** * Get plugin detail. * * @param pluginType plugin type * @param pluginName plugin name * @return plugin detail */ @GetMapping @Secured(action = ActionTypes.READ, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result getPluginDetail( @RequestParam("pluginType") String pluginType, @RequestParam("pluginName") String pluginName) throws NacosException { return Result.success(pluginProxy.getPluginDetail(pluginType, pluginName)); } /** * Enable or disable plugin. * * @param pluginType plugin type * @param pluginName plugin name * @param enabled enable or disable * @return success result */ @PutMapping("/status") @Secured(action = ActionTypes.WRITE, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result updatePluginStatus( @RequestParam("pluginType") String pluginType, @RequestParam("pluginName") String pluginName, @RequestParam("enabled") boolean enabled, @RequestParam(value = "localOnly", defaultValue = "false") boolean localOnly) throws NacosException { pluginProxy.updatePluginStatus(pluginType, pluginName, enabled, localOnly); return Result.success("Plugin status updated successfully"); } /** * Update plugin configuration. * * @param form plugin config form * @return success result */ @PutMapping("/config") @Secured(action = ActionTypes.WRITE, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result updatePluginConfig(PluginConfigForm form) throws NacosException { if (StringUtils.isBlank(form.getPluginType()) || StringUtils.isBlank(form.getPluginName())) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "Plugin type and name are required"); } if (form.getConfig() == null) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_VALIDATE_ERROR, "Plugin configuration is required"); } pluginProxy.updatePluginConfig(form.getPluginType(), form.getPluginName(), form.getConfig(), form.isLocalOnly()); return Result.success("Plugin configuration updated successfully"); } /** * Get plugin availability across cluster nodes. * * @param pluginType plugin type * @param pluginName plugin name * @return node availability map */ @GetMapping("/availability") @Secured(action = ActionTypes.READ, signType = SignType.CONSOLE, apiType = ApiType.CONSOLE_API) public Result> getPluginAvailability( @RequestParam("pluginType") String pluginType, @RequestParam("pluginName") String pluginName) throws NacosException { return Result.success(pluginProxy.getPluginAvailability(pluginType, pluginName)); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/naming/ConsoleInstanceController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.naming; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.api.naming.pojo.builder.InstanceBuilder; import com.alibaba.nacos.api.naming.utils.NamingUtils; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.console.proxy.naming.InstanceProxy; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.model.form.InstanceForm; import com.alibaba.nacos.naming.model.form.InstanceListForm; import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; import com.alibaba.nacos.naming.web.CanDistro; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * Controller for handling HTTP requests related to instance operations. * * @author zhangyukun on:2024/8/16 */ @NacosApi @RestController @RequestMapping("/v3/console/ns/instance") @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) public class ConsoleInstanceController { private final InstanceProxy instanceProxy; /** * Constructs a new ConsoleInstanceController with the provided InstanceProxy. * * @param instanceProxy the proxy used for handling instance-related operations */ public ConsoleInstanceController(InstanceProxy instanceProxy) { this.instanceProxy = instanceProxy; } /** * List instances of special service. * * @param instanceForm instance list form * @param pageForm Page form * @return instances information */ @Secured(action = ActionTypes.READ, apiType = ApiType.CONSOLE_API) @RequestMapping("/list") public Result> getInstanceList(InstanceListForm instanceForm, PageForm pageForm) throws NacosException { instanceForm.validate(); pageForm.validate(); Page instancePage = instanceProxy.listInstances(instanceForm.getNamespaceId(), instanceForm.getServiceName(), instanceForm.getGroupName(), instanceForm.getClusterName(), pageForm.getPageNo(), pageForm.getPageSize()); return Result.success(instancePage); } /** * Update instance. */ @CanDistro @PutMapping @TpsControl(pointName = "NamingInstanceUpdate", name = "HttpNamingInstanceUpdate") @Secured(action = ActionTypes.WRITE, apiType = ApiType.CONSOLE_API) public Result updateInstance(InstanceForm instanceForm) throws NacosException { // check param instanceForm.validate(); checkWeight(instanceForm.getWeight()); // build instance Instance instance = buildInstance(instanceForm); instanceProxy.updateInstance(instanceForm, instance); return Result.success("ok"); } private void checkWeight(Double weight) throws NacosException { if (weight > com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE || weight < com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE) { throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.WEIGHT_ERROR, "instance format invalid: The weights range from " + com.alibaba.nacos.naming.constants.Constants.MIN_WEIGHT_VALUE + " to " + com.alibaba.nacos.naming.constants.Constants.MAX_WEIGHT_VALUE); } } private Instance buildInstance(InstanceForm instanceForm) throws NacosException { Instance instance = InstanceBuilder.newBuilder().setServiceName(buildCompositeServiceName(instanceForm)) .setIp(instanceForm.getIp()).setClusterName(instanceForm.getClusterName()) .setPort(instanceForm.getPort()).setHealthy(instanceForm.getHealthy()) .setWeight(instanceForm.getWeight()).setEnabled(instanceForm.getEnabled()) .setMetadata(UtilsAndCommons.parseMetadata(instanceForm.getMetadata())) .setEphemeral(instanceForm.getEphemeral()).build(); if (instanceForm.getEphemeral() == null) { // register instance by console default is persistent instance. instance.setEphemeral(false); } return instance; } private String buildCompositeServiceName(InstanceForm instanceForm) { return NamingUtils.getGroupedName(instanceForm.getServiceName(), instanceForm.getGroupName()); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/controller/v3/naming/ConsoleServiceController.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.controller.v3.naming; import com.alibaba.nacos.api.annotation.NacosApi; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker; import com.alibaba.nacos.api.naming.pojo.healthcheck.HealthCheckerFactory; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceDetailInfo; import com.alibaba.nacos.api.naming.pojo.maintainer.SubscriberInfo; import com.alibaba.nacos.api.selector.Selector; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.proxy.naming.ServiceProxy; import com.alibaba.nacos.core.control.TpsControl; import com.alibaba.nacos.core.model.form.AggregationForm; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.core.paramcheck.ExtractorManager; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.misc.UtilsAndCommons; import com.alibaba.nacos.naming.model.form.ServiceForm; import com.alibaba.nacos.naming.model.form.ServiceListForm; import com.alibaba.nacos.naming.model.form.UpdateClusterForm; import com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor; import com.alibaba.nacos.naming.selector.NoneSelector; import com.alibaba.nacos.naming.selector.SelectorManager; import com.alibaba.nacos.plugin.auth.constant.ActionTypes; import com.alibaba.nacos.plugin.auth.constant.ApiType; import com.alibaba.nacos.plugin.auth.constant.Constants; import com.fasterxml.jackson.databind.JsonNode; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.net.URLDecoder; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; /** * Controller for handling HTTP requests related to service operations. * * @author zhangyukun on:2024/8/16 */ @NacosApi @RestController @RequestMapping("/v3/console/ns/service") @ExtractorManager.Extractor(httpExtractor = NamingDefaultHttpParamExtractor.class) public class ConsoleServiceController { private final ServiceProxy serviceProxy; private final SelectorManager selectorManager; public ConsoleServiceController(ServiceProxy serviceProxy, SelectorManager selectorManager) { this.serviceProxy = serviceProxy; this.selectorManager = selectorManager; } /** * Create a new service. This API will create a persistence service. */ @PostMapping() @TpsControl(pointName = "NamingServiceRegister", name = "HttpNamingServiceRegister") @Secured(action = ActionTypes.WRITE, apiType = ApiType.CONSOLE_API) public Result createService(ServiceForm serviceForm) throws Exception { serviceForm.validate(); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setProtectThreshold(serviceForm.getProtectThreshold()); serviceMetadata.setSelector(parseSelector(serviceForm.getSelector())); serviceMetadata.setExtendData(UtilsAndCommons.parseMetadata(serviceForm.getMetadata())); serviceMetadata.setEphemeral(serviceForm.getEphemeral()); serviceProxy.createService(serviceForm, serviceMetadata); return Result.success("ok"); } /** * Remove service. */ @DeleteMapping() @TpsControl(pointName = "NamingServiceDeregister", name = "HttpNamingServiceDeregister") @Secured(action = ActionTypes.WRITE, apiType = ApiType.CONSOLE_API) public Result deleteService(ServiceForm serviceForm) throws Exception { serviceForm.validate(); serviceProxy.deleteService(serviceForm.getNamespaceId(), serviceForm.getServiceName(), serviceForm.getGroupName()); return Result.success("ok"); } /** * Update service. */ @PutMapping() @TpsControl(pointName = "NamingServiceUpdate", name = "HttpNamingServiceUpdate") @Secured(action = ActionTypes.WRITE, apiType = ApiType.CONSOLE_API) public Result updateService(ServiceForm serviceForm) throws Exception { serviceForm.validate(); Map metadata = UtilsAndCommons.parseMetadata(serviceForm.getMetadata()); ServiceMetadata serviceMetadata = new ServiceMetadata(); serviceMetadata.setProtectThreshold(serviceForm.getProtectThreshold()); serviceMetadata.setExtendData(metadata); serviceMetadata.setSelector(parseSelector(serviceForm.getSelector())); serviceProxy.updateService(serviceForm, serviceMetadata); return Result.success("ok"); } /** * Get all {@link Selector} types. * * @return {@link Selector} types. */ @GetMapping("/selector/types") @Secured(resource = Constants.Resource.CONSOLE_RESOURCE_NAME_PREFIX + "naming", action = ActionTypes.READ, apiType = ApiType.CONSOLE_API, tags = Constants.Tag.ONLY_IDENTITY) public Result> getSelectorTypeList() throws NacosException { return Result.success(serviceProxy.getSelectorTypeList()); } /** * get subscriber list. * * @param serviceForm service form data * @param pageForm page form data * @param aggregationForm whether aggregation form data * @return subscribes result data. * @throws Exception any exception during get subscriber list. */ @GetMapping("/subscribers") @Secured(action = ActionTypes.READ, apiType = ApiType.CONSOLE_API) public Result> subscribers(ServiceForm serviceForm, PageForm pageForm, AggregationForm aggregationForm) throws Exception { serviceForm.validate(); pageForm.validate(); int pageNo = pageForm.getPageNo(); int pageSize = pageForm.getPageSize(); String namespaceId = serviceForm.getNamespaceId(); String serviceName = serviceForm.getServiceName(); String groupName = serviceForm.getGroupName(); boolean aggregation = aggregationForm.isAggregation(); Page subscribers = serviceProxy.getSubscribers(pageNo, pageSize, namespaceId, serviceName, groupName, aggregation); return Result.success(subscribers); } /** * List service detail information. * * @param serviceListForm service list form * @param pageForm page form * @return list service detail, depend on withInstances parameters, return ServiceDetailInfo or ServiceView. */ @Secured(action = ActionTypes.READ, apiType = ApiType.CONSOLE_API) @GetMapping("/list") public Result getServiceList(ServiceListForm serviceListForm, PageForm pageForm) throws NacosException { serviceListForm.validate(); pageForm.validate(); String namespaceId = serviceListForm.getNamespaceId(); String serviceName = serviceListForm.getServiceNameParam(); String groupName = serviceListForm.getGroupNameParam(); boolean hasIpCount = serviceListForm.isIgnoreEmptyService(); boolean withInstances = serviceListForm.isWithInstances(); return Result.success( serviceProxy.getServiceList(withInstances, namespaceId, pageForm.getPageNo(), pageForm.getPageSize(), serviceName, groupName, hasIpCount)); } /** * Get service detail. * * @param serviceForm service form data * @return service detail information * @throws NacosException nacos exception */ @Secured(action = ActionTypes.READ, apiType = ApiType.CONSOLE_API) @GetMapping() public Result getServiceDetail(ServiceForm serviceForm) throws NacosException { serviceForm.validate(); ServiceDetailInfo result = serviceProxy.getServiceDetail(serviceForm.getNamespaceId(), serviceForm.getServiceName(), serviceForm.getGroupName()); return Result.success(result); } /** * Update cluster. * * @param updateClusterForm update cluster form. * @return 'ok' if success * @throws Exception if failed */ @PutMapping("/cluster") @Secured(action = ActionTypes.WRITE, apiType = ApiType.CONSOLE_API) public Result updateCluster(UpdateClusterForm updateClusterForm) throws Exception { updateClusterForm.validate(); final String namespaceId = updateClusterForm.getNamespaceId(); final String clusterName = updateClusterForm.getClusterName(); final String serviceName = updateClusterForm.getServiceName(); final String groupName = updateClusterForm.getGroupName(); ClusterMetadata clusterMetadata = new ClusterMetadata(); clusterMetadata.setHealthyCheckPort(updateClusterForm.getCheckPort()); clusterMetadata.setUseInstancePortForCheck(updateClusterForm.isUseInstancePort4Check()); AbstractHealthChecker healthChecker = HealthCheckerFactory.deserialize(updateClusterForm.getHealthChecker()); clusterMetadata.setHealthChecker(healthChecker); clusterMetadata.setHealthyCheckType(healthChecker.getType()); clusterMetadata.setExtendData(UtilsAndCommons.parseMetadata(updateClusterForm.getMetadata())); serviceProxy.updateClusterMetadata(namespaceId, groupName, serviceName, clusterName, clusterMetadata); return Result.success("ok"); } private Selector parseSelector(String selectorJsonString) throws Exception { if (StringUtils.isBlank(selectorJsonString)) { return new NoneSelector(); } JsonNode selectorJson = JacksonUtils.toObj(URLDecoder.decode(selectorJsonString, "UTF-8")); String type = Optional.ofNullable(selectorJson.get("type")).orElseThrow( () -> new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SELECTOR_ERROR, "not match any type of selector!")).asText(); String expression = Optional.ofNullable(selectorJson.get("expression")).map(JsonNode::asText).orElse(null); Selector selector = selectorManager.parseSelector(type, expression); if (Objects.isNull(selector)) { throw new NacosApiException(NacosException.INVALID_PARAM, ErrorCode.SELECTOR_ERROR, "not match any type of selector!"); } return selector; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/exception/ConsoleExceptionHandler.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.exception; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.plugin.auth.exception.AccessException; import com.alibaba.nacos.common.model.RestResultUtils; import com.alibaba.nacos.common.utils.ExceptionUtil; import com.alibaba.nacos.core.utils.Commons; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.util.HtmlUtils; import jakarta.servlet.http.HttpServletRequest; /** * Exception handler for console module. * * @author nkorange * @since 1.2.0 */ @ControllerAdvice public class ConsoleExceptionHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleExceptionHandler.class); @ExceptionHandler(AccessException.class) private ResponseEntity handleAccessException(AccessException e) { LOGGER.error("got exception. {}", e.getErrMsg()); return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg()); } @ExceptionHandler(IllegalArgumentException.class) private ResponseEntity handleIllegalArgumentException(IllegalArgumentException e) { return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(ExceptionUtil.getAllExceptionMsg(e)); } @ExceptionHandler(NacosRuntimeException.class) private ResponseEntity handleNacosRuntimeException(NacosRuntimeException e) { LOGGER.error("got exception. {}", e.getMessage()); return ResponseEntity.status(e.getErrCode()).body(ExceptionUtil.getAllExceptionMsg(e)); } @ExceptionHandler(Exception.class) private ResponseEntity handleException(HttpServletRequest request, Exception e) { String uri = request.getRequestURI(); LOGGER.error("CONSOLE {}", uri, e); if (uri.contains(Commons.NACOS_SERVER_VERSION_V2)) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(RestResultUtils.failed(HtmlUtils.htmlEscape(ExceptionUtil.getAllExceptionMsg(e), "utf-8"))); } return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(HtmlUtils.htmlEscape(ExceptionUtil.getAllExceptionMsg(e), "utf-8")); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/filter/NacosConsoleAuthFilter.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.filter; import com.alibaba.nacos.auth.annotation.Secured; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.serveridentity.ServerIdentityResult; import com.alibaba.nacos.core.auth.AbstractWebAuthFilter; import com.alibaba.nacos.core.code.ControllerMethodsCache; import jakarta.servlet.http.HttpServletRequest; /** * Nacos Console web auth filter. * * @author xiweng.yy */ public class NacosConsoleAuthFilter extends AbstractWebAuthFilter { private final NacosAuthConfig authConfig; public NacosConsoleAuthFilter(NacosAuthConfig authConfig, ControllerMethodsCache methodsCache) { super(authConfig, methodsCache); this.authConfig = authConfig; } @Override protected boolean isAuthEnabled() { return authConfig.isAuthEnabled(); } @Override protected ServerIdentityResult checkServerIdentity(HttpServletRequest request, Secured secured) { return ServerIdentityResult.noMatched(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/filter/XssFilter.java ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.filter; import org.springframework.web.filter.OncePerRequestFilter; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; /** * XSS filter. * @author onewe */ public class XssFilter extends OncePerRequestFilter { private static final String CONTENT_SECURITY_POLICY_HEADER = "Content-Security-Policy"; private static final String CONTENT_SECURITY_POLICY = "script-src 'self'"; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { response.setHeader(CONTENT_SECURITY_POLICY_HEADER, CONTENT_SECURITY_POLICY); filterChain.doFilter(request, response); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/HealthHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; /** * Interface for handling health check operations. * * @author zhangyukun */ public interface HealthHandler { /** * Perform readiness check to determine if Nacos is ready to handle requests. * * @return readiness result * @throws NacosException if an error occurs during readiness check */ Result checkReadiness() throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/ServerStateHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler; import com.alibaba.nacos.api.exception.NacosException; import java.util.Map; /** * Interface for handling server state operations. * * @author zhangyukun */ public interface ServerStateHandler { /** * Get the current state of the server. * * @return a map containing the server state * @throws NacosException if an error occurs while retrieving the server state */ Map getServerState() throws NacosException; /** * Get the announcement content based on the language. * * @param language the language for the announcement * @return the announcement content */ String getAnnouncement(String language); /** * Get the console UI guide information. * * @return the console UI guide information */ String getConsoleUiGuide(); } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/ai/A2aHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.ai; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.core.model.form.PageForm; import java.util.List; /** * A2a handler. * * @author KiteSoar */ public interface A2aHandler { /** * Register agent. * * @param agentCard registered Agent Card * @param agentCardForm agent card form * @throws NacosException nacos exception */ void registerAgent(AgentCard agentCard, AgentCardForm agentCardForm) throws NacosException; /** * Get agent card with versions. * * @param form agent form * @return agent card * @throws NacosException nacos exception */ AgentCardDetailInfo getAgentCardWithVersions(AgentForm form) throws NacosException; /** * Delete agent. * * @param form agent form * @throws NacosException nacos exception */ void deleteAgent(AgentForm form) throws NacosException; /** * Update agent card. * * @param agentCard agent card to updated * @param form agent update form * @throws NacosException nacos exception */ void updateAgentCard(AgentCard agentCard, AgentCardUpdateForm form) throws NacosException; /** * List agents. * * @param agentListForm agent list form * @param pageForm page form * @return agent card list * @throws NacosException nacos exception */ Page listAgents(AgentListForm agentListForm, PageForm pageForm) throws NacosException; /** * List agent versions. * @param namespaceId namespace id of target agent * @param name name of target agent * @return agent version detail list * @throws NacosException nacos exception */ List listAgentVersions(String namespaceId, String name) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/ai/EnabledAiHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.ai; import com.alibaba.nacos.ai.config.AiEnabledFilter; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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; /** * Using Inner handler to handle console API request. * * @author xiweng.yy */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @ConditionalOnProperty(value = AiEnabledFilter.AI_ENABLED_KEY, havingValue = "true", matchIfMissing = true) public @interface EnabledAiHandler { } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/ai/McpHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.ai; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpTool; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; /** * Actual Handler class for handling AI MCP operations. * * @author xiweng.yy */ public interface McpHandler { /** * List mcp server. * * @param namespaceId namespace id of mcp servers * @param mcpName mcp name pattern, if null or empty, filter all mcp servers. * @param search search type `blur` or `accurate`, means whether to search by fuzzy or exact match by * `mcpName`. * @param pageNo page number, start from 1 * @param pageSize page size each page * @return list of {@link McpServerBasicInfo} matched input parameters. * @throws NacosException any exception during handling */ Page listMcpServers(String namespaceId, String mcpName, String search, int pageNo, int pageSize) throws NacosException; /** * Get specified mcp server detail info. * * @param namespaceId namespace id of mcp server * @param mcpName name of mcp server * @param mcpId id of mcp server * @param version version of the mcp server * @return detail info with {@link McpServerDetailInfo} * @throws NacosException any exception during handling */ McpServerDetailInfo getMcpServer(String namespaceId, String mcpName, String mcpId, String version) throws NacosException; /** * Create new mcp server. * * @param namespaceId namespace id of mcp server * @param serverSpecification mcp server specification, see {@link McpServerBasicInfo} * @param toolSpecification mcp server included tools, see {@link McpTool}, optional * @param endpointSpecification mcp server endpoint specification, see {@link McpEndpointSpec}, optional * @return mcp server id of the new mcp server * @throws NacosException any exception during handling */ String createMcpServer(String namespaceId, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException; /** * Update existed mcp server. * *

    * `namespaceId` and `mcpName` can't be changed. *

    * * @param namespaceId namespace id of mcp server, used to mark which mcp server to update * @param isPublish publish the current version to latest * @param serverSpecification mcp server specification, see {@link McpServerBasicInfo} * @param toolSpecification mcp server included tools, see {@link McpTool}, optional * @param endpointSpecification mcp server endpoint specification, see {@link McpEndpointSpec}, optional * @param overrideExisting if replace all the instances when update the mcp server * @throws NacosException any exception during handling */ void updateMcpServer(String namespaceId, boolean isPublish, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException; /** * Delete existed mcp server. * * @param namespaceId namespace id of mcp server * @param mcpId id of the mcp server * @param version version of the mcp server * @param mcpName name of mcp server * @throws NacosException any exception during handling */ void deleteMcpServer(String namespaceId, String mcpName, String mcpId, String version) throws NacosException; /** * Validate MCP server import request. * * @param namespaceId namespace id for mcp servers * @param request import request containing data and settings * @return validation result with details about potential issues * @throws NacosException any exception during validation */ McpServerImportValidationResult validateImport(String namespaceId, McpServerImportRequest request) throws NacosException; /** * Execute MCP server import operation. * * @param namespaceId namespace id for mcp servers * @param request import request containing data and settings * @return import response with results and statistics * @throws NacosException any exception during import execution */ McpServerImportResponse executeImport(String namespaceId, McpServerImportRequest request) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/ai/PromptHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.ai; import com.alibaba.nacos.ai.form.prompt.PromptForm; import com.alibaba.nacos.ai.form.prompt.PromptHistoryForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelBindForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelForm; import com.alibaba.nacos.ai.form.prompt.PromptListForm; import com.alibaba.nacos.ai.form.prompt.PromptMetadataForm; import com.alibaba.nacos.ai.form.prompt.PromptPublishForm; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; /** * Prompt handler interface. * * @author nacos */ public interface PromptHandler { /** * Publish a new version of prompt. * * @param form prompt publish form * @param srcUser source user * @param srcIp source IP * @return true if publish success * @throws NacosException if publish fails */ boolean publishPrompt(PromptPublishForm form, String srcUser, String srcIp) throws NacosException; /** * Gets prompt meta. * * @param form the form * @return the prompt meta * @throws NacosException the nacos exception */ PromptMetaInfo getPromptMeta(PromptForm form) throws NacosException; /** * Query prompt detail prompt version info. * * @param form the form * @return the prompt version info * @throws NacosException the nacos exception */ PromptVersionInfo queryPromptDetail(PromptQueryForm form) throws NacosException; /** * Bind prompt label to version. * * @param form bind form * @param srcUser source user * @param srcIp source ip * @return true if bind success * @throws NacosException if bind fails */ boolean bindLabel(PromptLabelBindForm form, String srcUser, String srcIp) throws NacosException; /** * Unbind prompt label. * * @param form label form * @param srcUser source user * @param srcIp source ip * @return true if unbind success * @throws NacosException if unbind fails */ boolean unbindLabel(PromptLabelForm form, String srcUser, String srcIp) throws NacosException; /** * Delete prompt. * * @param form prompt form * @param srcUser source user * @param srcIp source IP * @return true if delete success * @throws NacosException if delete fails */ boolean deletePrompt(PromptForm form, String srcUser, String srcIp) throws NacosException; /** * List prompts with pagination. * * @param form prompt list form * @return prompt list page * @throws NacosException if list fails */ Page listPrompts(PromptListForm form) throws NacosException; /** * List prompt versions page. * * @param form the form * @return the page * @throws NacosException the nacos exception */ Page listPromptVersions(PromptHistoryForm form) throws NacosException; /** * Update prompt metadata (description only). * * @param form prompt metadata form * @param srcUser source user * @param srcIp source IP * @return true if update success * @throws NacosException if update fails */ boolean updatePromptMetadata(PromptMetadataForm form, String srcUser, String srcIp) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/ai/SkillHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.ai; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.ai.form.skills.admin.SkillForm; import com.alibaba.nacos.ai.form.skills.admin.SkillListForm; import com.alibaba.nacos.ai.form.skills.admin.SkillUpdateForm; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.core.model.form.PageForm; /** * Skill handler. * * @author nacos */ public interface SkillHandler { /** * Register skill. * * @param skill skill to register * @param form skill detail form * @throws NacosException nacos exception */ void registerSkill(Skill skill, SkillDetailForm form) throws NacosException; /** * Get skill. * * @param form skill form * @return skill * @throws NacosException nacos exception */ Skill getSkill(SkillForm form) throws NacosException; /** * Delete skill. * * @param form skill form * @throws NacosException nacos exception */ void deleteSkill(SkillForm form) throws NacosException; /** * Update skill. * * @param skill skill to update * @param form skill update form * @throws NacosException nacos exception */ void updateSkill(Skill skill, SkillUpdateForm form) throws NacosException; /** * List skills. * * @param skillListForm skill list form * @param pageForm page form * @return skill list * @throws NacosException nacos exception */ Page listSkills(SkillListForm skillListForm, PageForm pageForm) throws NacosException; /** * Upload skill from zip file. * * @param namespaceId namespace ID * @param zipBytes zip file bytes * @return skill name * @throws NacosException if upload failed */ String uploadSkillFromZip(String namespaceId, byte[] zipBytes) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/config/ConfigHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import jakarta.servlet.ServletException; import org.springframework.http.ResponseEntity; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.List; import java.util.Map; /** * Interface for handling configuration operations. * * @author zhangyukun */ public interface ConfigHandler { /** * Retrieves the configuration based on the specified parameters. * * @param pageNo The page number for pagination. * @param pageSize The number of items per page. * @param dataId The identifier of the configuration data. * @param group The group to which the configuration belongs. * @param namespaceId The namespace identifier. * @param configAdvanceInfo Additional advanced search criteria. * @return ConfigInfo containing all details of the specified configuration. * @throws IOException If an input or output exception occurs. * @throws ServletException If a servlet-specific exception occurs. * @throws NacosException If an error related to Nacos configuration occurs. */ Page getConfigList(int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws IOException, ServletException, NacosException; /** * Retrieves detailed information about a specific configuration. * * @param dataId The identifier of the configuration data. * @param group The group to which the configuration belongs. * @param namespaceId The namespace identifier. * @return A ConfigAllInfo object containing all details of the specified configuration. * @throws NacosException If an error related to Nacos configuration occurs. */ ConfigDetailInfo getConfigDetail(String dataId, String group, String namespaceId) throws NacosException; /** * Publishes a new configuration or updates an existing configuration. * * @param configForm The form object containing configuration details. * @param configRequestInfo Additional request information related to the configuration. * @return A Boolean indicating whether the publish operation was successful. * @throws NacosException If an error related to Nacos configuration occurs. */ Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo) throws NacosException; /** * Deletes a specific configuration. * * @param dataId The identifier of the configuration data to delete. * @param group The group to which the configuration belongs. * @param namespaceId The namespace identifier. * @param tag The tag associated with the configuration. * @param clientIp The IP address of the client requesting the deletion. * @param srcUser The source user requesting the deletion. * @return A Boolean indicating whether the deletion was successful. * @throws NacosException If an error related to Nacos configuration occurs. */ Boolean deleteConfig(String dataId, String group, String namespaceId, String tag, String clientIp, String srcUser) throws NacosException; /** * Deletes multiple configurations based on their IDs. * * @param ids A list of IDs of the configurations to delete. * @param clientIp The IP address of the client requesting the deletion. * @param srcUser The source user requesting the deletion. * @return A Boolean indicating whether the deletion was successful. * @throws NacosException If an error related to Nacos configuration occurs. */ Boolean batchDeleteConfigs(List ids, String clientIp, String srcUser) throws NacosException; /** * Exports the configuration with metadata based on the specified parameters. * * @param dataId The identifier of the configuration data. * @param group The group to which the configuration belongs. * @param namespaceId The namespace identifier. * @param appName The application name associated with the configuration. * @param ids A list of IDs of the configurations to export. * @return A ResponseEntity containing the exported configuration as a byte array. * @throws Exception If an unexpected error occurs during the export process. */ ResponseEntity exportConfig(String dataId, String group, String namespaceId, String appName, List ids) throws Exception; /** * Searches for configurations based on detailed criteria. * * @param search The search keyword. * @param pageNo The page number for pagination. * @param pageSize The number of items per page. * @param dataId The identifier of the configuration data. * @param group The group to which the configuration belongs. * @param namespaceId The namespace identifier. * @param configAdvanceInfo Additional advanced search criteria. * @return A Page object containing a list of ConfigInfo that matches the search criteria. * @throws NacosException If an error related to Nacos configuration occurs. */ Page getConfigListByContent(String search, int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws NacosException; /** * Retrieves the status of listeners for a specific configuration. * * @param dataId The identifier of the configuration data. * @param group The group to which the configuration belongs. * @param namespaceId The namespace identifier. * @param aggregation whether aggregation from other servers * @return A ConfigListenerInfo object containing the status of the listeners. * @throws Exception If an unexpected error occurs. */ ConfigListenerInfo getListeners(String dataId, String group, String namespaceId, boolean aggregation) throws Exception; /** * Get subscription information based on IP, tenant, and other parameters. * * @param ip IP address of the client * @param all Whether to retrieve all configurations * @param namespaceId Tenant information * @param aggregation whether aggregation from other servers * @return ConfigListenerInfo object containing subscription information * @throws NacosException If an error occurs while retrieving the subscription information. */ ConfigListenerInfo getAllSubClientConfigByIp(String ip, boolean all, String namespaceId, boolean aggregation) throws NacosException; /** * Imports and publishes a configuration from a file. * * @param srcUser The source user performing the import. * @param namespaceId The namespace identifier. * @param policy The policy for handling existing configurations. * @param file The file containing the configuration to import. * @param srcIp The IP address of the source. * @param requestIpApp The IP address of the requester. * @return A Result object containing the status and additional information about the operation. * @throws NacosException If an error related to Nacos configuration occurs. */ Result> importAndPublishConfig(String srcUser, String namespaceId, SameConfigPolicy policy, MultipartFile file, String srcIp, String requestIpApp) throws NacosException; /** * Clones an existing configuration to a different namespace. * * @param srcUser The source user performing the clone operation. * @param namespaceId The namespace identifier where the configuration will be cloned to. * @param configBeansList A list of configurations to be cloned. * @param policy The policy for handling existing configurations in the target namespace. * @param srcIp The IP address of the source. * @param requestIpApp The IP address of the requester. * @return A Result object containing the status and additional information about the operation. * @throws NacosException If an error related to Nacos configuration occurs. */ Result> cloneConfig(String srcUser, String namespaceId, List configBeansList, SameConfigPolicy policy, String srcIp, String requestIpApp) throws NacosException; /** * Remove beta configuration based on dataId, group, and namespaceId. * * @param dataId the dataId * @param group the group * @param namespaceId the namespaceId * @param remoteIp the IP address of the client making the request * @param requestIpApp the name of the application making the request * @param srcUser the src user performing the operation * @return true if the beta configuration is successfully removed * @throws NacosException if an error occurs while removing the beta configuration */ boolean removeBetaConfig(String dataId, String group, String namespaceId, String remoteIp, String requestIpApp, String srcUser) throws NacosException; /** * Query beta configuration based on dataId, group, and namespaceId. * * @param dataId the dataId * @param group the group * @param namespaceId the namespaceId * @return ConfigInfo4Beta containing the beta configuration details * @throws NacosException if an error occurs while querying the beta configuration */ ConfigGrayInfo queryBetaConfig(String dataId, String group, String namespaceId) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/config/HistoryHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import java.util.List; /** * Interface for handling configuration history related operations. * * @author zhangyukun */ public interface HistoryHandler { /** * Query the detailed configuration history information. * * @param dataId the ID of the data * @param group the group ID * @param namespaceId the namespace ID * @param nid the history record ID * @return the detailed configuration history information * @throws NacosException if any error occurs during the operation */ ConfigHistoryDetailInfo getConfigHistoryInfo(String dataId, String group, String namespaceId, Long nid) throws NacosException; /** * Query the list of configuration history. * * @param dataId the ID of the data * @param group the group ID * @param namespaceId the namespace ID * @param pageNo the page number * @param pageSize the number of items per page * @return the paginated list of configuration history * @throws NacosException if any error occurs during the operation */ Page listConfigHistory(String dataId, String group, String namespaceId, Integer pageNo, Integer pageSize) throws NacosException; /** * Query the previous configuration history information. * * @param dataId the ID of the data * @param group the group ID * @param namespaceId the namespace ID * @param id the configuration ID * @return the previous configuration history information * @throws NacosException if any error occurs during the operation */ ConfigHistoryDetailInfo getPreviousConfigHistoryInfo(String dataId, String group, String namespaceId, Long id) throws NacosException; /** * Query the list of configurations by namespace. * * @param namespaceId the namespace ID * @return the list of configurations * @throws NacosException if any error occurs during the operation */ List getConfigsByTenant(String namespaceId) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/core/ClusterHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.NacosMember; import java.util.Collection; /** * Interface for handling cluster-related operations. * * @author zhangyukun */ public interface ClusterHandler { /** * Retrieve a list of cluster members with an optional search keyword. * * @param ipKeyWord the search keyword for filtering members * @return a collection of matching members * @throws NacosException if an error occurs during the operation */ Collection getNodeList(String ipKeyWord) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/core/NamespaceHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.core.namespace.model.form.NamespaceForm; import java.util.List; /** * Interface for handling namespace-related operations. * * @author zhangyukun */ public interface NamespaceHandler { /** * Get a list of namespaces. * * @return list of namespaces * @throws NacosException if there is an issue fetching the namespaces */ List getNamespaceList() throws NacosException; /** * Get details of a specific namespace. * * @param namespaceId the ID of the namespace * @return namespace details * @throws NacosException if there is an issue fetching the namespace */ Namespace getNamespaceDetail(String namespaceId) throws NacosException; /** * Create a new namespace. * * @param namespaceId the ID of the namespace * @param namespaceName the name of the namespace * @param namespaceDesc the description of the namespace * @return true if the namespace was successfully created, otherwise false * @throws NacosException if there is an issue creating the namespace */ Boolean createNamespace(String namespaceId, String namespaceName, String namespaceDesc) throws NacosException; /** * Update an existing namespace. * * @param namespaceForm the form containing the updated namespace details * @return true if the namespace was successfully updated, otherwise false * @throws NacosException if there is an issue updating the namespace */ Boolean updateNamespace(NamespaceForm namespaceForm) throws NacosException; /** * Delete a namespace by its ID. * * @param namespaceId the ID of the namespace * @return true if the namespace was successfully deleted, otherwise false * @throws NacosException if there is an issue deleting the namespace */ Boolean deleteNamespace(String namespaceId) throws NacosException; /** * Check if a namespace ID exists. * * @param namespaceId the ID of the namespace to check * @return true if the namespace exists, otherwise false * @throws NacosException if there is an issue checking the namespace */ Boolean checkNamespaceIdExist(String namespaceId) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/core/PluginHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.core.plugin.model.vo.PluginDetailVO; import com.alibaba.nacos.core.plugin.model.vo.PluginInfoVO; import java.util.List; import java.util.Map; /** * Interface for handling plugin-related operations. * * @author WangzJi */ public interface PluginHandler { /** * Get a list of plugins. * * @param pluginType optional plugin type filter * @return list of plugin info VOs * @throws NacosException if there is an issue fetching the plugins */ List listPlugins(String pluginType) throws NacosException; /** * Get plugin detail. * * @param pluginType plugin type * @param pluginName plugin name * @return plugin detail VO * @throws NacosException if there is an issue fetching the plugin detail */ PluginDetailVO getPluginDetail(String pluginType, String pluginName) throws NacosException; /** * Update plugin enabled/disabled status. * * @param pluginType plugin type * @param pluginName plugin name * @param enabled whether to enable * @param localOnly whether only apply to local node * @throws NacosException if there is an issue updating the plugin status */ void updatePluginStatus(String pluginType, String pluginName, boolean enabled, boolean localOnly) throws NacosException; /** * Update plugin configuration. * * @param pluginType plugin type * @param pluginName plugin name * @param config configuration map * @param localOnly whether only apply to local node * @throws NacosException if there is an issue updating the plugin config */ void updatePluginConfig(String pluginType, String pluginName, Map config, boolean localOnly) throws NacosException; /** * Get plugin availability across cluster nodes. * * @param pluginType plugin type * @param pluginName plugin name * @return node availability map * @throws NacosException if there is an issue fetching the availability */ Map getPluginAvailability(String pluginType, String pluginName) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/AbstractServerStateHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl; import com.alibaba.nacos.console.handler.ServerStateHandler; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.utils.DiskUtils; import java.io.File; import static com.alibaba.nacos.common.utils.StringUtils.FOLDER_SEPARATOR; import static com.alibaba.nacos.common.utils.StringUtils.TOP_PATH; import static com.alibaba.nacos.common.utils.StringUtils.WINDOWS_FOLDER_SEPARATOR; /** * Abstract Server state handler. * * @author xiweng.yy */ public abstract class AbstractServerStateHandler implements ServerStateHandler { private static final String ANNOUNCEMENT_FILE = "announcement.conf"; private static final String GUIDE_FILE = "console-guide.conf"; @Override public String getAnnouncement(String language) { String file = ANNOUNCEMENT_FILE.substring(0, ANNOUNCEMENT_FILE.length() - 5) + "_" + language + ".conf"; if (file.contains(TOP_PATH) || file.contains(FOLDER_SEPARATOR) || file.contains(WINDOWS_FOLDER_SEPARATOR)) { throw new IllegalArgumentException("Invalid filename"); } File announcementFile = new File(EnvUtil.getConfPath(), file); String announcement = null; if (announcementFile.exists() && announcementFile.isFile()) { announcement = DiskUtils.readFile(announcementFile); } return announcement; } @Override public String getConsoleUiGuide() { File guideFile = new File(EnvUtil.getConfPath(), GUIDE_FILE); String guideInformation = null; if (guideFile.exists() && guideFile.isFile()) { guideInformation = DiskUtils.readFile(guideFile); } return guideInformation; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/ConditionFunctionEnabled.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.sys.env.EnvUtil; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; /** * The condition of target function or module is enabled. * When target module such as `naming`, `config` or `ai` is disabled or dependency module is disabled * The target handler should not be loaded and should use noop handler replaced. * * @author xiweng.yy */ public class ConditionFunctionEnabled implements Condition { private final String targetFunctionMode; public ConditionFunctionEnabled(String targetFunctionMode) { this.targetFunctionMode = targetFunctionMode; } @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String functionMode = EnvUtil.getFunctionMode(); // empty function mode setting means all function is enabled if (StringUtils.isEmpty(functionMode)) { return true; } // configured function mode not empty and equals target function mode, means target function is enabled return functionMode.equalsIgnoreCase(targetFunctionMode); } public static class ConditionNamingEnabled extends ConditionFunctionEnabled { public ConditionNamingEnabled() { super(EnvUtil.FUNCTION_MODE_NAMING); } } public static class ConditionConfigEnabled extends ConditionFunctionEnabled { public ConditionConfigEnabled() { super(EnvUtil.FUNCTION_MODE_CONFIG); } } public static class ConditionAiEnabled extends ConditionFunctionEnabled { public ConditionAiEnabled() { super(""); } } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/EnabledInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner; import com.alibaba.nacos.sys.env.Constants; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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; /** * Using Inner handler to handle console API request. * * @author xiweng.yy */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @ConditionalOnProperty(value = Constants.NACOS_DEPLOYMENT_TYPE, havingValue = Constants.NACOS_DEPLOYMENT_TYPE_MERGED, matchIfMissing = true) public @interface EnabledInnerHandler { } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/HealthInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.console.handler.HealthHandler; import com.alibaba.nacos.core.cluster.health.ModuleHealthCheckerHolder; import com.alibaba.nacos.core.cluster.health.ReadinessResult; import org.springframework.stereotype.Service; /** * Implementation of HealthHandler that performs health check operations. * * @author zhangyukun */ @Service @EnabledInnerHandler public class HealthInnerHandler implements HealthHandler { @Override public Result checkReadiness() { ReadinessResult result = ModuleHealthCheckerHolder.getInstance().checkReadiness(); if (result.isSuccess()) { return Result.success("ok"); } return Result.failure(result.getResultMessage()); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/ServerStateInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner; import com.alibaba.nacos.console.handler.impl.AbstractServerStateHandler; import com.alibaba.nacos.core.service.NacosServerStateService; import org.springframework.stereotype.Service; import java.util.Map; /** * Implementation of ServerStateHandler that performs server state operations. * * @author zhangyukun */ @Service @EnabledInnerHandler public class ServerStateInnerHandler extends AbstractServerStateHandler { private final NacosServerStateService stateService; public ServerStateInnerHandler(NacosServerStateService stateService) { this.stateService = stateService; } public Map getServerState() { return stateService.getServerState(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/ai/A2aInnerHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.ai; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.ai.service.a2a.A2aServerOperationService; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.A2aHandler; import com.alibaba.nacos.console.handler.ai.EnabledAiHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.core.model.form.PageForm; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Component; import java.util.List; /** * A2a inner handler. * * @author KiteSoar */ @Component @EnabledInnerHandler @EnabledAiHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class A2aInnerHandler implements A2aHandler { private final A2aServerOperationService a2aServerOperationService; public A2aInnerHandler(A2aServerOperationService a2aServerOperationService) { this.a2aServerOperationService = a2aServerOperationService; } @Override public void registerAgent(AgentCard agentCard, AgentCardForm agentCardForm) throws NacosException { a2aServerOperationService.registerAgent(agentCard, agentCardForm.getNamespaceId(), agentCardForm.getRegistrationType()); } @Override public AgentCardDetailInfo getAgentCardWithVersions(AgentForm form) throws NacosException { return a2aServerOperationService.getAgentCard(form.getNamespaceId(), form.getAgentName(), form.getVersion(), form.getRegistrationType()); } @Override public void deleteAgent(AgentForm form) throws NacosException { a2aServerOperationService.deleteAgent(form.getNamespaceId(), form.getAgentName(), form.getVersion()); } @Override public void updateAgentCard(AgentCard agentCard, AgentCardUpdateForm form) throws NacosException { a2aServerOperationService.updateAgentCard(agentCard, form.getNamespaceId(), form.getRegistrationType(), form.getSetAsLatest()); } @Override public Page listAgents(AgentListForm agentListForm, PageForm pageForm) throws NacosException { return a2aServerOperationService.listAgents(agentListForm.getNamespaceId(), agentListForm.getAgentName(), agentListForm.getSearch(), pageForm.getPageNo(), pageForm.getPageSize()); } @Override public List listAgentVersions(String namespaceId, String name) throws NacosException { return a2aServerOperationService.listAgentVersions(namespaceId, name); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/ai/McpInnerHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.ai; import com.alibaba.nacos.ai.service.McpServerImportService; import com.alibaba.nacos.ai.service.McpServerOperationService; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.EnabledAiHandler; import com.alibaba.nacos.console.handler.ai.McpHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; /** * Inner implementation of Mcp handler. * * @author xiweng.yy */ @Service @EnabledInnerHandler @EnabledAiHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class McpInnerHandler implements McpHandler { private final McpServerOperationService mcpServerOperationService; private final McpServerImportService mcpServerImportService; public McpInnerHandler(McpServerOperationService mcpServerOperationService, McpServerImportService mcpServerImportService) { this.mcpServerOperationService = mcpServerOperationService; this.mcpServerImportService = mcpServerImportService; } @Override public Page listMcpServers(String namespaceId, String mcpName, String search, int pageNo, int pageSize) { return mcpServerOperationService.listMcpServerWithPage(namespaceId, mcpName, search, pageNo, pageSize); } @Override public McpServerDetailInfo getMcpServer(String namespaceId, String mcpName, String mcpServerId, String version) throws NacosException { return mcpServerOperationService.getMcpServerDetail(namespaceId, mcpServerId, mcpName, version); } @Override public String createMcpServer(String namespaceId, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { return mcpServerOperationService.createMcpServer(namespaceId, serverSpecification, toolSpecification, endpointSpecification); } @Override public void updateMcpServer(String namespaceId, boolean isPublish, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException { mcpServerOperationService.updateMcpServer(namespaceId, isPublish, serverSpecification, toolSpecification, endpointSpecification, overrideExisting); } @Override public void deleteMcpServer(String namespaceId, String mcpName, String mcpServerId, String version) throws NacosException { mcpServerOperationService.deleteMcpServer(namespaceId, mcpName, mcpServerId, version); } @Override public McpServerImportValidationResult validateImport(String namespaceId, McpServerImportRequest request) throws NacosException { return mcpServerImportService.validateImport(namespaceId, request); } @Override public McpServerImportResponse executeImport(String namespaceId, McpServerImportRequest request) throws NacosException { return mcpServerImportService.executeImport(namespaceId, request); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/ai/PromptInnerHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.ai; import com.alibaba.nacos.ai.form.prompt.PromptForm; import com.alibaba.nacos.ai.form.prompt.PromptHistoryForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelBindForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelForm; import com.alibaba.nacos.ai.form.prompt.PromptListForm; import com.alibaba.nacos.ai.form.prompt.PromptMetadataForm; import com.alibaba.nacos.ai.form.prompt.PromptPublishForm; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVariable; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.ai.service.prompt.PromptAdminOperationService; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.handler.ai.EnabledAiHandler; import com.alibaba.nacos.console.handler.ai.PromptHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.fasterxml.jackson.core.type.TypeReference; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Component; import java.util.ArrayList; import java.util.List; /** * Prompt inner handler implementation. * * @author nacos */ @Component @EnabledInnerHandler @EnabledAiHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class PromptInnerHandler implements PromptHandler { private final PromptAdminOperationService promptOperationService; public PromptInnerHandler(PromptAdminOperationService promptOperationService) { this.promptOperationService = promptOperationService; } @Override public boolean publishPrompt(PromptPublishForm form, String srcUser, String srcIp) throws NacosException { return promptOperationService.publishPromptVersion( form.getNamespaceId(), form.getPromptKey(), form.getVersion(), form.getTemplate(), form.getCommitMsg(), form.getDescription(), parseBizTags(form.getBizTags()), parseVariables(form.getVariables()), srcUser, srcIp ); } @Override public PromptMetaInfo getPromptMeta(PromptForm form) throws NacosException { return promptOperationService.getPromptMeta(form.getNamespaceId(), form.getPromptKey()); } @Override public PromptVersionInfo queryPromptDetail(PromptQueryForm form) throws NacosException { return promptOperationService.queryPromptDetail( form.getNamespaceId(), form.getPromptKey(), form.getVersion(), form.getLabel() ); } @Override public boolean bindLabel(PromptLabelBindForm form, String srcUser, String srcIp) throws NacosException { return promptOperationService.bindLabel( form.getNamespaceId(), form.getPromptKey(), form.getLabel(), form.getVersion(), srcUser, srcIp ); } @Override public boolean unbindLabel(PromptLabelForm form, String srcUser, String srcIp) throws NacosException { return promptOperationService.unbindLabel( form.getNamespaceId(), form.getPromptKey(), form.getLabel(), srcUser, srcIp ); } @Override public boolean deletePrompt(PromptForm form, String srcUser, String srcIp) throws NacosException { return promptOperationService.deletePrompt(form.getNamespaceId(), form.getPromptKey(), srcUser, srcIp); } @Override public Page listPrompts(PromptListForm form) throws NacosException { return promptOperationService.listPrompts( form.getNamespaceId(), form.getPromptKey(), form.getSearch(), form.getBizTags(), form.getPageNo(), form.getPageSize() ); } @Override public Page listPromptVersions(PromptHistoryForm form) throws NacosException { return promptOperationService.listPromptVersions( form.getNamespaceId(), form.getPromptKey(), form.getPageNo(), form.getPageSize() ); } @Override public boolean updatePromptMetadata(PromptMetadataForm form, String srcUser, String srcIp) throws NacosException { return promptOperationService.updatePromptMetadata( form.getNamespaceId(), form.getPromptKey(), form.getDescription(), parseBizTags(form.getBizTags()), srcUser, srcIp ); } private List parseBizTags(String bizTags) { if (bizTags == null) { return null; } if (bizTags.trim().isEmpty()) { return new ArrayList<>(0); } String[] split = bizTags.split(","); List result = new ArrayList<>(split.length); for (String each : split) { if (each != null && !each.trim().isEmpty()) { result.add(each.trim()); } } return result; } private List parseVariables(String variables) { if (StringUtils.isBlank(variables)) { return null; } return JacksonUtils.toObj(variables, new TypeReference>() { }); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/ai/SkillInnerHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.ai; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.ai.form.skills.admin.SkillForm; import com.alibaba.nacos.ai.form.skills.admin.SkillListForm; import com.alibaba.nacos.ai.form.skills.admin.SkillUpdateForm; import com.alibaba.nacos.ai.service.skills.SkillOperationService; import com.alibaba.nacos.console.handler.ai.SkillHandler; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.EnabledAiHandler; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.core.model.form.PageForm; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Component; /** * Skill inner handler. * * @author nacos */ @Component @EnabledInnerHandler @EnabledAiHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class SkillInnerHandler implements SkillHandler { private final SkillOperationService skillOperationService; public SkillInnerHandler(SkillOperationService skillOperationService) { this.skillOperationService = skillOperationService; } @Override public void registerSkill(Skill skill, SkillDetailForm form) throws NacosException { skillOperationService.registerSkill(skill, form.getNamespaceId()); } @Override public Skill getSkill(SkillForm form) throws NacosException { return skillOperationService.getSkillDetail(form.getNamespaceId(), form.getSkillName()); } @Override public void deleteSkill(SkillForm form) throws NacosException { skillOperationService.deleteSkill(form.getNamespaceId(), form.getSkillName()); } @Override public void updateSkill(Skill skill, SkillUpdateForm form) throws NacosException { skillOperationService.updateSkill(skill, form.getNamespaceId()); } @Override public Page listSkills(SkillListForm skillListForm, PageForm pageForm) throws NacosException { return skillOperationService.listSkills(skillListForm.getNamespaceId(), skillListForm.getSkillName(), skillListForm.getSearch(), pageForm.getPageNo(), pageForm.getPageSize()); } @Override public String uploadSkillFromZip(String namespaceId, byte[] zipBytes) throws NacosException { return skillOperationService.uploadSkillFromZip(namespaceId, zipBytes); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/config/ConfigInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.utils.DateFormatUtils; import com.alibaba.nacos.common.utils.NamespaceUtil; import com.alibaba.nacos.common.utils.Pair; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean; import com.alibaba.nacos.config.server.model.ConfigAllInfo; import com.alibaba.nacos.config.server.model.ConfigInfo; import com.alibaba.nacos.config.server.model.ConfigInfoGrayWrapper; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.model.ConfigMetadata; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.event.ConfigDataChangeEvent; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.config.server.model.gray.BetaGrayRule; import com.alibaba.nacos.config.server.service.ConfigChangePublisher; import com.alibaba.nacos.config.server.service.ConfigDetailService; import com.alibaba.nacos.config.server.service.ConfigMigrateService; import com.alibaba.nacos.config.server.service.ConfigOperationService; import com.alibaba.nacos.config.server.service.listener.ConfigListenerStateDelegate; import com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoGrayPersistService; import com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService; import com.alibaba.nacos.config.server.service.trace.ConfigTraceService; import com.alibaba.nacos.config.server.utils.GroupKey; import com.alibaba.nacos.config.server.utils.GroupKey2; import com.alibaba.nacos.config.server.utils.PropertyUtil; import com.alibaba.nacos.config.server.utils.ResponseUtil; import com.alibaba.nacos.config.server.utils.TimeUtils; import com.alibaba.nacos.config.server.utils.YamlParserUtil; import com.alibaba.nacos.config.server.utils.ZipUtils; import com.alibaba.nacos.console.handler.config.ConfigHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.core.namespace.repository.NamespacePersistService; import com.alibaba.nacos.plugin.encryption.handler.EncryptionHandler; import com.alibaba.nacos.sys.utils.InetUtils; import jakarta.servlet.ServletException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Conditional; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.sql.Timestamp; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; /** * Implementation of ConfigHandler for handling internal configuration operations. * * @author zhangyukun */ @Service @EnabledInnerHandler @Conditional(ConditionFunctionEnabled.ConditionConfigEnabled.class) public class ConfigInnerHandler implements ConfigHandler { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigInnerHandler.class); private static final String EXPORT_CONFIG_FILE_NAME = "nacos_config_export_"; private static final String EXPORT_CONFIG_FILE_NAME_EXT = ".zip"; private static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyyMMddHHmmss"; private final ConfigInfoPersistService configInfoPersistService; private final ConfigOperationService configOperationService; private final ConfigDetailService configDetailService; private final ConfigListenerStateDelegate configListenerStateDelegate; private final ConfigMigrateService configMigrateService; private NamespacePersistService namespacePersistService; private ConfigInfoBetaPersistService configInfoBetaPersistService; private ConfigInfoGrayPersistService configInfoGrayPersistService; /** * Flag to indicate if the table `config_info_beta` exists, which means the old version of table schema is used. */ private boolean oldTableVersion; public ConfigInnerHandler(ConfigOperationService configOperationService, ConfigInfoPersistService configInfoPersistService, ConfigDetailService configDetailService, NamespacePersistService namespacePersistService, ConfigInfoBetaPersistService configInfoBetaPersistService, ConfigInfoGrayPersistService configInfoGrayPersistService, ConfigListenerStateDelegate configListenerStateDelegate, ConfigMigrateService configMigrateService) { this.configOperationService = configOperationService; this.configInfoPersistService = configInfoPersistService; this.configDetailService = configDetailService; this.namespacePersistService = namespacePersistService; this.configInfoBetaPersistService = configInfoBetaPersistService; this.configInfoGrayPersistService = configInfoGrayPersistService; this.configListenerStateDelegate = configListenerStateDelegate; this.configMigrateService = configMigrateService; this.oldTableVersion = namespacePersistService.isExistTable("config_info_beta"); } @Override public Page getConfigList(int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws IOException, ServletException, NacosException { Page result = configInfoPersistService.findConfigInfoLike4Page(pageNo, pageSize, dataId, group, namespaceId, configAdvanceInfo); return transferToConfigBasicInfo(result); } @Override public ConfigDetailInfo getConfigDetail(String dataId, String group, String namespaceId) throws NacosException { ConfigAllInfo configAllInfo = configInfoPersistService.findConfigAllInfo(dataId, group, namespaceId); if (null == configAllInfo) { return null; } return ResponseUtil.transferToConfigDetailInfo(configAllInfo); } @Override public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo) throws NacosException { String encryptedDataKeyFinal = configForm.getEncryptedDataKey(); if (StringUtils.isBlank(encryptedDataKeyFinal)) { // encrypted Pair pair = EncryptionHandler.encryptHandler(configForm.getDataId(), configForm.getContent()); configForm.setContent(pair.getSecond()); encryptedDataKeyFinal = pair.getFirst(); } return configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKeyFinal); } @Override public Boolean deleteConfig(String dataId, String group, String namespaceId, String tag, String clientIp, String srcUser) throws NacosException { return configOperationService.deleteConfig(dataId, group, namespaceId, tag, clientIp, srcUser, Constants.HTTP); } @Override public Boolean batchDeleteConfigs(List ids, String clientIp, String srcUser) { for (Long id : ids) { ConfigInfo configInfo = configInfoPersistService.findConfigInfo(id); if (configInfo == null) { LOGGER.warn("[deleteConfigs] configInfo is null, id: {}", id); continue; } configOperationService.deleteConfig(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), null, clientIp, srcUser, Constants.HTTP); } return true; } @Override public Page getConfigListByContent(String search, int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws NacosException { try { Page result = configDetailService.findConfigInfoPage(search, pageNo, pageSize, dataId, group, namespaceId, configAdvanceInfo); return transferToConfigBasicInfo(result); } catch (Exception e) { String errorMsg = "serialize page error, dataId=" + dataId + ", group=" + group; LOGGER.error(errorMsg, e); throw e; } } @Override public ConfigListenerInfo getListeners(String dataId, String group, String namespaceId, boolean aggregation) throws Exception { return configListenerStateDelegate.getListenerState(dataId, group, namespaceId, aggregation); } @Override public ConfigListenerInfo getAllSubClientConfigByIp(String ip, boolean all, String namespaceId, boolean aggregation) { ConfigListenerInfo result = configListenerStateDelegate.getListenerStateByIp(ip, aggregation); result.setQueryType(ConfigListenerInfo.QUERY_TYPE_IP); Map configMd5Status = new HashMap<>(100); if (result.getListenersStatus() == null || result.getListenersStatus().isEmpty()) { return result; } Map status = result.getListenersStatus(); for (Map.Entry config : status.entrySet()) { if (!StringUtils.isBlank(namespaceId) && config.getKey().contains(namespaceId)) { configMd5Status.put(config.getKey(), config.getValue()); continue; } if (all) { configMd5Status.put(config.getKey(), config.getValue()); } else { String[] configKeys = GroupKey2.parseKey(config.getKey()); if (StringUtils.isBlank(configKeys[2])) { configMd5Status.put(config.getKey(), config.getValue()); } } } result.setListenersStatus(configMd5Status); return result; } @Override public ResponseEntity exportConfig(String dataId, String group, String namespaceId, String appName, List ids) throws Exception { List dataList = configInfoPersistService.findAllConfigInfo4Export(dataId, group, namespaceId, appName, ids); List zipItemList = new ArrayList<>(); List configMetadataItems = new ArrayList<>(); for (ConfigAllInfo ci : dataList) { ConfigMetadata.ConfigExportItem configMetadataItem = new ConfigMetadata.ConfigExportItem(); configMetadataItem.setAppName(ci.getAppName()); configMetadataItem.setDataId(ci.getDataId()); configMetadataItem.setDesc(ci.getDesc()); configMetadataItem.setGroup(ci.getGroup()); configMetadataItem.setType(ci.getType()); configMetadataItems.add(configMetadataItem); Pair pair = EncryptionHandler.decryptHandler(ci.getDataId(), ci.getEncryptedDataKey(), ci.getContent()); String itemName = ci.getGroup() + Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR + ci.getDataId(); zipItemList.add(new ZipUtils.ZipItem(itemName, pair.getSecond())); } ConfigMetadata configMetadata = new ConfigMetadata(); configMetadata.setMetadata(configMetadataItems); zipItemList.add( new ZipUtils.ZipItem(Constants.CONFIG_EXPORT_METADATA_NEW, YamlParserUtil.dumpObject(configMetadata))); HttpHeaders headers = new HttpHeaders(); String fileName = EXPORT_CONFIG_FILE_NAME + DateFormatUtils.format(new Date(), EXPORT_CONFIG_FILE_NAME_DATE_FORMAT) + EXPORT_CONFIG_FILE_NAME_EXT; headers.add("Content-Disposition", "attachment;filename=" + fileName); return new ResponseEntity<>(ZipUtils.zip(zipItemList), headers, HttpStatus.OK); } @Override public Result> importAndPublishConfig(String srcUser, String namespaceId, SameConfigPolicy policy, MultipartFile file, String srcIp, String requestIpApp) throws NacosException { Map failedData = new HashMap<>(4); if (Objects.isNull(file)) { return Result.failure(ErrorCode.DATA_EMPTY, failedData); } if (StringUtils.isNotBlank(namespaceId) && !NamespaceUtil.isDefaultNamespaceId(namespaceId) && namespacePersistService.tenantInfoCountByTenantId(namespaceId) <= 0) { failedData.put("succCount", 0); return Result.failure(ErrorCode.NAMESPACE_NOT_EXIST, failedData); } List configInfoList = new ArrayList<>(); List> unrecognizedList = new ArrayList<>(); try { ZipUtils.UnZipResult unziped = ZipUtils.unzip(file.getBytes()); Result> errorResult; errorResult = parseImportDataV2(srcUser, unziped, configInfoList, unrecognizedList, namespaceId); if (errorResult != null) { return errorResult; } } catch (IOException e) { failedData.put("succCount", 0); LOGGER.error("parsing data failed", e); return Result.failure(ErrorCode.PARSING_DATA_FAILED, failedData); } if (CollectionUtils.isEmpty(configInfoList)) { failedData.put("succCount", 0); return Result.failure(ErrorCode.DATA_EMPTY, failedData); } final Timestamp time = TimeUtils.getCurrentTime(); Map saveResult = configInfoPersistService.batchInsertOrUpdate(configInfoList, srcUser, srcIp, null, policy); for (ConfigInfo configInfo : configInfoList) { ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), time.getTime())); ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), requestIpApp, time.getTime(), InetUtils.getSelfIP(), ConfigTraceService.PERSISTENCE_EVENT, ConfigTraceService.PERSISTENCE_TYPE_PUB, configInfo.getContent()); } // unrecognizedCount if (!unrecognizedList.isEmpty()) { saveResult.put("unrecognizedCount", unrecognizedList.size()); saveResult.put("unrecognizedData", unrecognizedList); } return Result.success(saveResult); } /** * new version import config add .metadata.yml file. * * @param unziped export file. * @param configInfoList parse file result. * @param unrecognizedList unrecognized file. * @param namespace import namespace. * @return error result. */ private Result> parseImportDataV2(String srcUser, ZipUtils.UnZipResult unziped, List configInfoList, List> unrecognizedList, String namespace) { ZipUtils.ZipItem metaDataItem = unziped.getMetaDataItem(); String metaData = metaDataItem.getItemData(); Map failedData = new HashMap<>(4); ConfigMetadata configMetadata = YamlParserUtil.loadObject(metaData, ConfigMetadata.class); if (configMetadata == null || CollectionUtils.isEmpty(configMetadata.getMetadata())) { failedData.put("succCount", 0); return Result.failure(ErrorCode.METADATA_ILLEGAL, failedData); } List configExportItems = configMetadata.getMetadata(); // check config metadata for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) { if (StringUtils.isBlank(configExportItem.getDataId()) || StringUtils.isBlank(configExportItem.getGroup()) || StringUtils.isBlank(configExportItem.getType())) { failedData.put("succCount", 0); return Result.failure(ErrorCode.METADATA_ILLEGAL, failedData); } } List zipItemList = unziped.getZipItemList(); Set metaDataKeys = configExportItems.stream() .map(metaItem -> GroupKey.getKey(metaItem.getDataId(), metaItem.getGroup())) .collect(Collectors.toSet()); Map configContentMap = new HashMap<>(zipItemList.size()); int itemNameLength = 2; zipItemList.forEach(item -> { String itemName = item.getItemName(); String[] groupAdnDataId = itemName.split(Constants.CONFIG_EXPORT_ITEM_FILE_SEPARATOR); if (groupAdnDataId.length != itemNameLength) { Map unrecognizedItem = new HashMap<>(2); unrecognizedItem.put("itemName", item.getItemName()); unrecognizedList.add(unrecognizedItem); return; } String group = groupAdnDataId[0]; String dataId = groupAdnDataId[1]; String key = GroupKey.getKey(dataId, group); // metadata does not contain config file if (!metaDataKeys.contains(key)) { Map unrecognizedItem = new HashMap<>(2); unrecognizedItem.put("itemName", "未在元数据中找到: " + item.getItemName()); unrecognizedList.add(unrecognizedItem); return; } String itemData = item.getItemData(); configContentMap.put(key, itemData); }); for (ConfigMetadata.ConfigExportItem configExportItem : configExportItems) { String dataId = configExportItem.getDataId(); String group = configExportItem.getGroup(); String content = configContentMap.get(GroupKey.getKey(dataId, group)); // config file not in metadata if (content == null) { Map unrecognizedItem = new HashMap<>(2); unrecognizedItem.put("itemName", "未在文件中找到: " + group + "/" + dataId); unrecognizedList.add(unrecognizedItem); continue; } // encrypted Pair pair = EncryptionHandler.encryptHandler(dataId, content); content = pair.getSecond(); ConfigAllInfo ci = new ConfigAllInfo(); ci.setGroup(group); ci.setDataId(dataId); ci.setContent(content); ci.setType(configExportItem.getType()); ci.setDesc(configExportItem.getDesc()); ci.setAppName(configExportItem.getAppName()); ci.setTenant(namespace); ci.setEncryptedDataKey(pair.getFirst()); ci.setCreateUser(srcUser); configInfoList.add(ci); } return null; } @Override public Result> cloneConfig(String srcUser, String namespaceId, List configBeansList, SameConfigPolicy policy, String srcIp, String requestIpApp) throws NacosException { Map failedData = new HashMap<>(4); if (CollectionUtils.isEmpty(configBeansList)) { failedData.put("succCount", 0); return Result.failure(ErrorCode.NO_SELECTED_CONFIG, failedData); } if (StringUtils.isNotBlank(namespaceId) && !NamespaceUtil.isDefaultNamespaceId(namespaceId) && namespacePersistService.tenantInfoCountByTenantId(namespaceId) <= 0) { failedData.put("succCount", 0); return Result.failure(ErrorCode.NAMESPACE_NOT_EXIST, failedData); } List idList = new ArrayList<>(configBeansList.size()); Map configBeansMap = configBeansList.stream() .collect(Collectors.toMap(SameNamespaceCloneConfigBean::getCfgId, cfg -> { idList.add(cfg.getCfgId()); return cfg; }, (k1, k2) -> k1)); List queryedDataList = configInfoPersistService.findAllConfigInfo4Export(null, null, null, null, idList); if (queryedDataList == null || queryedDataList.isEmpty()) { failedData.put("succCount", 0); return Result.failure(ErrorCode.DATA_EMPTY, failedData); } List configInfoList4Clone = new ArrayList<>(queryedDataList.size()); for (ConfigAllInfo ci : queryedDataList) { SameNamespaceCloneConfigBean paramBean = configBeansMap.get(ci.getId()); ConfigAllInfo ci4save = new ConfigAllInfo(); ci4save.setTenant(namespaceId); ci4save.setType(ci.getType()); ci4save.setGroup((paramBean != null && StringUtils.isNotBlank(paramBean.getGroup())) ? paramBean.getGroup() : ci.getGroup()); ci4save.setDataId( (paramBean != null && StringUtils.isNotBlank(paramBean.getDataId())) ? paramBean.getDataId() : ci.getDataId()); ci4save.setContent(ci.getContent()); if (StringUtils.isNotBlank(ci.getAppName())) { ci4save.setAppName(ci.getAppName()); } ci4save.setDesc(ci.getDesc()); ci4save.setEncryptedDataKey( ci.getEncryptedDataKey() == null ? StringUtils.EMPTY : ci.getEncryptedDataKey()); configInfoList4Clone.add(ci4save); } final Timestamp time = TimeUtils.getCurrentTime(); Map saveResult = configInfoPersistService.batchInsertOrUpdate(configInfoList4Clone, srcUser, srcIp, null, policy); for (ConfigInfo configInfo : configInfoList4Clone) { ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), time.getTime())); ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(), configInfo.getTenant(), requestIpApp, time.getTime(), InetUtils.getSelfIP(), ConfigTraceService.PERSISTENCE_EVENT, ConfigTraceService.PERSISTENCE_TYPE_PUB, configInfo.getContent()); } return Result.success(saveResult); } @Override public boolean removeBetaConfig(String dataId, String group, String namespaceId, String remoteIp, String requestIpApp, String srcUser) { try { configInfoGrayPersistService.removeConfigInfoGray(dataId, group, namespaceId, BetaGrayRule.TYPE_BETA, remoteIp, srcUser); configMigrateService.removeConfigInfoGrayMigrate(dataId, group, namespaceId, BetaGrayRule.TYPE_BETA, remoteIp, srcUser); } catch (Throwable e) { LOGGER.error("remove beta data error", e); return false; } ConfigTraceService.logPersistenceEvent(dataId, group, namespaceId, requestIpApp, System.currentTimeMillis(), remoteIp, ConfigTraceService.PERSISTENCE_EVENT_BETA, ConfigTraceService.PERSISTENCE_TYPE_REMOVE, null); if (PropertyUtil.isGrayCompatibleModel() && oldTableVersion) { configInfoBetaPersistService.removeConfigInfo4Beta(dataId, group, namespaceId); } ConfigChangePublisher.notifyConfigChange( new ConfigDataChangeEvent(dataId, group, namespaceId, BetaGrayRule.TYPE_BETA, System.currentTimeMillis())); return true; } @Override public ConfigGrayInfo queryBetaConfig(String dataId, String group, String namespaceId) throws NacosException { ConfigInfoGrayWrapper beta4Gray = configInfoGrayPersistService.findConfigInfo4Gray(dataId, group, namespaceId, "beta"); if (Objects.nonNull(beta4Gray)) { String encryptedDataKey = beta4Gray.getEncryptedDataKey(); Pair pair = EncryptionHandler.decryptHandler(dataId, encryptedDataKey, beta4Gray.getContent()); beta4Gray.setContent(pair.getSecond()); //find the corresponding production config to get the `type` field, because the `type` field is not stored in the beta table ConfigInfoWrapper productionConfig = configInfoPersistService.findConfigInfo(dataId, group, namespaceId); if (Objects.nonNull(productionConfig)) { beta4Gray.setType(productionConfig.getType()); } return ResponseUtil.transferToConfigGrayInfo(beta4Gray); } return null; } private Page transferToConfigBasicInfo(Page configInfoPage) { Page result = new Page<>(); result.setTotalCount(configInfoPage.getTotalCount()); result.setPagesAvailable(configInfoPage.getPagesAvailable()); result.setPageNumber(configInfoPage.getPageNumber()); result.setPageItems(configInfoPage.getPageItems().stream().map(ResponseUtil::transferToConfigBasicInfo) .collect(Collectors.toList())); return result; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/config/HistoryInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.config.server.model.ConfigHistoryInfo; import com.alibaba.nacos.config.server.model.ConfigInfoWrapper; import com.alibaba.nacos.config.server.service.HistoryService; import com.alibaba.nacos.config.server.utils.ResponseUtil; import com.alibaba.nacos.console.handler.config.HistoryHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Conditional; import org.springframework.dao.DataAccessException; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; /** * . * * @author zhangyukun on:2024/8/16 */ @Service @EnabledInnerHandler @Conditional(ConditionFunctionEnabled.ConditionConfigEnabled.class) public class HistoryInnerHandler implements HistoryHandler { private final HistoryService historyService; @Autowired public HistoryInnerHandler(HistoryService historyService) { this.historyService = historyService; } @Override public ConfigHistoryDetailInfo getConfigHistoryInfo(String dataId, String group, String namespaceId, Long nid) throws NacosException { ConfigHistoryDetailInfo result; try { ConfigHistoryInfo configHistoryInfo = historyService.getConfigHistoryInfo(dataId, group, namespaceId, nid); result = ResponseUtil.transferToConfigHistoryDetailInfo(configHistoryInfo); } catch (DataAccessException e) { throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, "certain config history for nid = " + nid + " not exist"); } return result; } @Override public Page listConfigHistory(String dataId, String group, String namespaceId, Integer pageNo, Integer pageSize) throws NacosException { Page configHistoryInfoPage = historyService.listConfigHistory(dataId, group, namespaceId, pageNo, pageSize); Page result = new Page<>(); result.setPagesAvailable(configHistoryInfoPage.getPagesAvailable()); result.setPageNumber(configHistoryInfoPage.getPageNumber()); result.setTotalCount(configHistoryInfoPage.getTotalCount()); result.setPageItems( configHistoryInfoPage.getPageItems().stream().map(ResponseUtil::transferToConfigHistoryBasicInfo) .collect(Collectors.toList())); return result; } @Override public ConfigHistoryDetailInfo getPreviousConfigHistoryInfo(String dataId, String group, String namespaceId, Long id) throws NacosException { ConfigHistoryDetailInfo result; try { ConfigHistoryInfo configHistoryInfo = historyService.getPreviousConfigHistoryInfo(dataId, group, namespaceId, id); result = ResponseUtil.transferToConfigHistoryDetailInfo(configHistoryInfo); } catch (DataAccessException e) { throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, "previous config history for id = " + id + " not exist"); } return result; } @Override public List getConfigsByTenant(String namespaceId) { List configListByNamespace = historyService.getConfigListByNamespace(namespaceId); return configListByNamespace.stream().map(ResponseUtil::transferToConfigBasicInfo).toList(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/core/ClusterInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.core; import com.alibaba.nacos.api.model.response.NacosMember; import com.alibaba.nacos.console.handler.core.ClusterHandler; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.core.cluster.ServerMemberManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Collection; /** * Implementation of ClusterHandler that handles cluster-related operations. * * @author zhangyukun */ @Service @EnabledInnerHandler public class ClusterInnerHandler implements ClusterHandler { private final ServerMemberManager memberManager; /** * Constructs a new ClusterInnerHandler with the provided dependencies. * * @param memberManager the manager for server members */ @Autowired public ClusterInnerHandler(ServerMemberManager memberManager) { this.memberManager = memberManager; } @Override public Collection getNodeList(String ipKeyWord) { return memberManager.allMembers(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/core/NamespaceInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.console.handler.core.NamespaceHandler; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.core.namespace.model.form.NamespaceForm; import com.alibaba.nacos.core.service.NamespaceOperationService; import org.springframework.stereotype.Service; import java.util.List; /** * Implementation of NamespaceHandler that handles namespace-related operations. * * @author zhangyukun */ @Service @EnabledInnerHandler public class NamespaceInnerHandler implements NamespaceHandler { private final NamespaceOperationService namespaceOperationService; public NamespaceInnerHandler(NamespaceOperationService namespaceOperationService) { this.namespaceOperationService = namespaceOperationService; } @Override public List getNamespaceList() { return namespaceOperationService.getNamespaceList(); } @Override public Namespace getNamespaceDetail(String namespaceId) throws NacosException { return namespaceOperationService.getNamespace(namespaceId); } @Override public Boolean createNamespace(String namespaceId, String namespaceName, String namespaceDesc) throws NacosException { return namespaceOperationService.createNamespace(namespaceId, namespaceName, namespaceDesc); } @Override public Boolean updateNamespace(NamespaceForm namespaceForm) throws NacosException { return namespaceOperationService.editNamespace(namespaceForm.getNamespaceId(), namespaceForm.getNamespaceName(), namespaceForm.getNamespaceDesc()); } @Override public Boolean deleteNamespace(String namespaceId) { return namespaceOperationService.removeNamespace(namespaceId); } @Override public Boolean checkNamespaceIdExist(String namespaceId) { return namespaceOperationService.namespaceExists(namespaceId); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/core/PluginInnerHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.core; import com.alibaba.nacos.api.common.NodeState; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.plugin.PluginType; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.handler.core.PluginHandler; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.ServerMemberManager; import com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy; import com.alibaba.nacos.core.cluster.remote.request.PluginAvailabilityRequest; import com.alibaba.nacos.core.cluster.remote.response.PluginAvailabilityResponse; import com.alibaba.nacos.core.plugin.PluginManager; import com.alibaba.nacos.core.plugin.model.PluginInfo; import com.alibaba.nacos.core.plugin.model.vo.PluginDetailVO; import com.alibaba.nacos.core.plugin.model.vo.PluginInfoVO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import java.util.Collection; import java.util.List; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.LongAdder; import java.util.stream.Collectors; /** * Inner implementation of PluginHandler for handling plugin-related operations. * * @author WangzJi */ @Service @EnabledInnerHandler public class PluginInnerHandler implements PluginHandler { private static final Logger LOGGER = LoggerFactory.getLogger(PluginInnerHandler.class); private final PluginManager pluginManager; private final ServerMemberManager memberManager; private final ClusterRpcClientProxy rpcClientProxy; public PluginInnerHandler(PluginManager pluginManager, ServerMemberManager memberManager, ClusterRpcClientProxy rpcClientProxy) { this.pluginManager = pluginManager; this.memberManager = memberManager; this.rpcClientProxy = rpcClientProxy; } @Override public List listPlugins(String pluginType) throws NacosException { List localList = pluginManager.listAllPlugins().stream() .filter(p -> StringUtils.isBlank(pluginType) || pluginType.equals(p.getPluginType().getType())) .map(this::convertToVO) .collect(Collectors.toList()); Collection members = memberManager.allMembers(); int totalNodeCount = members.size(); Map availableCountMap = new ConcurrentHashMap<>(localList.size()); List> futures = members.stream() .map(member -> CompletableFuture.runAsync(() -> { Map memberAvailability = queryMemberAvailability(member); if (memberAvailability != null) { memberAvailability.forEach((pluginId, available) -> { if (Boolean.TRUE.equals(available)) { availableCountMap.computeIfAbsent(pluginId, k -> new LongAdder()).increment(); } }); } })) .collect(Collectors.toList()); awaitCompletion(futures); localList.forEach(vo -> { vo.setTotalNodeCount(totalNodeCount); LongAdder adder = availableCountMap.get(vo.getPluginId()); vo.setAvailableNodeCount(adder != null ? adder.intValue() : 0); }); return localList; } private Map queryMemberAvailability(Member member) { try { if (memberManager.getSelf().equals(member)) { return pluginManager.listAllPlugins().stream() .collect(Collectors.toMap(PluginInfo::getPluginId, PluginInfo::isEnabled)); } if (!NodeState.UP.equals(member.getState())) { return null; } PluginAvailabilityRequest request = new PluginAvailabilityRequest(); request.setQueryAll(true); PluginAvailabilityResponse response = (PluginAvailabilityResponse) rpcClientProxy.sendRequest( member, request); if (response == null) { LOGGER.warn("Received null response when querying plugin availability from node {}", member.getAddress()); return null; } return response.getPluginAvailabilityMap(); } catch (Exception e) { LOGGER.warn("Failed to query plugin availability from node {}", member.getAddress(), e); return null; } } @Override public PluginDetailVO getPluginDetail(String pluginType, String pluginName) throws NacosException { String pluginId = pluginType + ":" + pluginName; return pluginManager.getPlugin(pluginId) .map(this::convertToDetailVO) .orElseThrow(() -> new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, "Plugin not found: " + pluginId)); } @Override public void updatePluginStatus(String pluginType, String pluginName, boolean enabled, boolean localOnly) throws NacosException { String pluginId = pluginType + ":" + pluginName; pluginManager.setPluginEnabled(pluginId, enabled, localOnly); } @Override public void updatePluginConfig(String pluginType, String pluginName, Map config, boolean localOnly) throws NacosException { String pluginId = pluginType + ":" + pluginName; pluginManager.updatePluginConfig(pluginId, config, localOnly); } @Override public Map getPluginAvailability(String pluginType, String pluginName) throws NacosException { String pluginId = pluginType + ":" + pluginName; if (!pluginManager.isPluginAvailable(pluginId)) { throw new NacosApiException(HttpStatus.NOT_FOUND.value(), ErrorCode.RESOURCE_NOT_FOUND, "Plugin not found: " + pluginId); } Collection members = memberManager.allMembers(); Map nodeAvailability = new ConcurrentHashMap<>(members.size()); List> futures = members.stream() .map(member -> CompletableFuture.runAsync(() -> { String address = member.getAddress(); nodeAvailability.put(address, checkMemberPluginAvailability(member, pluginId)); })) .collect(Collectors.toList()); awaitCompletion(futures); return nodeAvailability; } private boolean checkMemberPluginAvailability(Member member, String pluginId) { if (memberManager.getSelf().equals(member)) { return pluginManager.isPluginAvailable(pluginId); } if (!NodeState.UP.equals(member.getState())) { return false; } try { PluginAvailabilityRequest request = new PluginAvailabilityRequest(); request.setPluginId(pluginId); PluginAvailabilityResponse response = (PluginAvailabilityResponse) rpcClientProxy.sendRequest( member, request); if (response == null) { LOGGER.warn("Received null response when querying plugin {} availability from node {}", pluginId, member.getAddress()); return false; } return response.isAvailable(); } catch (Exception e) { LOGGER.warn("Failed to query plugin {} availability from node {}: {}", pluginId, member.getAddress(), e.getMessage()); return false; } } private void awaitCompletion(List> futures) { try { CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) .get(5, TimeUnit.SECONDS); } catch (TimeoutException e) { LOGGER.warn("Timeout waiting for plugin availability responses from some nodes"); } catch (Exception e) { LOGGER.error("Error collecting plugin availability from cluster", e); } } private PluginInfoVO convertToVO(PluginInfo pluginInfo) { PluginInfoVO vo = new PluginInfoVO(); vo.setPluginId(pluginInfo.getPluginId()); vo.setPluginType(pluginInfo.getPluginType().getType()); vo.setPluginName(pluginInfo.getPluginName()); vo.setEnabled(pluginInfo.isEnabled()); vo.setCritical(pluginInfo.isCritical()); vo.setConfigurable(pluginInfo.isConfigurable()); vo.setExclusive(isExclusiveType(pluginInfo.getPluginType())); return vo; } /** * Check if the plugin type is exclusive (only one can be active at a time). * Exclusive types: AUTH, DATASOURCE_DIALECT. * * @param type plugin type * @return true if exclusive */ private boolean isExclusiveType(PluginType type) { return type == PluginType.AUTH || type == PluginType.DATASOURCE_DIALECT; } private PluginDetailVO convertToDetailVO(PluginInfo pluginInfo) { PluginDetailVO vo = new PluginDetailVO(); vo.setPluginId(pluginInfo.getPluginId()); vo.setPluginType(pluginInfo.getPluginType().getType()); vo.setPluginName(pluginInfo.getPluginName()); vo.setEnabled(pluginInfo.isEnabled()); vo.setCritical(pluginInfo.isCritical()); vo.setConfigurable(pluginInfo.isConfigurable()); vo.setConfig(pluginInfo.getConfig()); vo.setConfigDefinitions(pluginInfo.getConfigDefinitions()); return vo; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/naming/InstanceInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.event.naming.UpdateInstanceTraceEvent; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.console.handler.naming.InstanceHandler; import com.alibaba.nacos.core.utils.PageUtil; import com.alibaba.nacos.naming.core.CatalogService; import com.alibaba.nacos.naming.core.CatalogServiceV2Impl; import com.alibaba.nacos.naming.core.InstanceOperatorClientImpl; import com.alibaba.nacos.naming.model.form.InstanceForm; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.util.List; /** * Implementation of InstanceHandler that handles instance-related operations. * * @author zhangyukun */ @Service @EnabledInnerHandler @Conditional(ConditionFunctionEnabled.ConditionNamingEnabled.class) public class InstanceInnerHandler implements InstanceHandler { private final CatalogService catalogService; private final InstanceOperatorClientImpl instanceServiceV2; /** * Constructs a new InstanceInnerHandler with the provided dependencies. * * @param catalogServiceV2 the service for catalog-related operations */ public InstanceInnerHandler(CatalogServiceV2Impl catalogServiceV2, InstanceOperatorClientImpl instanceServiceV2) { this.catalogService = catalogServiceV2; this.instanceServiceV2 = instanceServiceV2; } @Override public Page listInstances(String namespaceId, String serviceNameWithoutGroup, String groupName, String clusterName, int page, int pageSize) throws NacosException { List instances = catalogService.listInstances(namespaceId, groupName, serviceNameWithoutGroup, clusterName); return PageUtil.subPage(instances, page, pageSize); } @Override public void updateInstance(InstanceForm instanceForm, Instance instance) throws NacosException { instanceServiceV2.updateInstance(instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(), instance); NotifyCenter.publishEvent( new UpdateInstanceTraceEvent(System.currentTimeMillis(), "", instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(), instance.getIp(), instance.getPort(), instance.getMetadata())); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/inner/naming/ServiceInnerHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.inner.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceDetailInfo; import com.alibaba.nacos.api.naming.pojo.maintainer.SubscriberInfo; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.trace.event.naming.DeregisterServiceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.RegisterServiceTraceEvent; import com.alibaba.nacos.common.trace.event.naming.UpdateServiceTraceEvent; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.inner.EnabledInnerHandler; import com.alibaba.nacos.console.handler.naming.ServiceHandler; import com.alibaba.nacos.naming.core.CatalogServiceV2Impl; import com.alibaba.nacos.naming.core.ClusterOperatorV2Impl; import com.alibaba.nacos.naming.core.ServiceOperatorV2Impl; import com.alibaba.nacos.naming.core.v2.ServiceManager; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.model.form.ServiceForm; import com.alibaba.nacos.naming.selector.SelectorManager; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Conditional; import java.util.List; /** * Implementation of ServiceHandler that handles service-related operations. * * @author zhangyukun */ @org.springframework.stereotype.Service @EnabledInnerHandler @Conditional(ConditionFunctionEnabled.ConditionNamingEnabled.class) public class ServiceInnerHandler implements ServiceHandler { private final ServiceOperatorV2Impl serviceOperatorV2; private final SelectorManager selectorManager; private final CatalogServiceV2Impl catalogServiceV2; private final ClusterOperatorV2Impl clusterOperatorV2; @Autowired public ServiceInnerHandler(ServiceOperatorV2Impl serviceOperatorV2, SelectorManager selectorManager, CatalogServiceV2Impl catalogServiceV2, ClusterOperatorV2Impl clusterOperatorV2) { this.serviceOperatorV2 = serviceOperatorV2; this.selectorManager = selectorManager; this.catalogServiceV2 = catalogServiceV2; this.clusterOperatorV2 = clusterOperatorV2; } @Override public void createService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { serviceOperatorV2.create(com.alibaba.nacos.naming.core.v2.pojo.Service.newService(serviceForm.getNamespaceId(), serviceForm.getGroupName(), serviceForm.getServiceName(), serviceForm.getEphemeral()), serviceMetadata); NotifyCenter.publishEvent( new RegisterServiceTraceEvent(System.currentTimeMillis(), serviceForm.getNamespaceId(), serviceForm.getGroupName(), serviceForm.getServiceName())); } @Override public void deleteService(String namespaceId, String serviceName, String groupName) throws Exception { serviceOperatorV2.delete( com.alibaba.nacos.naming.core.v2.pojo.Service.newService(namespaceId, groupName, serviceName)); NotifyCenter.publishEvent( new DeregisterServiceTraceEvent(System.currentTimeMillis(), namespaceId, groupName, serviceName)); } @Override public void updateService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { Service service = Service.newService(serviceForm.getNamespaceId(), serviceForm.getGroupName(), serviceForm.getServiceName()); if (!ServiceManager.getInstance().containSingleton(service)) { throw new NacosApiException(NacosException.NOT_FOUND, ErrorCode.SERVICE_NOT_EXIST, "service %s is not exist.".formatted(service.toString())); } service = ServiceManager.getInstance().getSingleton(service); serviceOperatorV2.update(service, serviceMetadata); NotifyCenter.publishEvent(new UpdateServiceTraceEvent(System.currentTimeMillis(), serviceForm.getNamespaceId(), serviceForm.getGroupName(), serviceForm.getServiceName(), serviceMetadata.getExtendData())); } @Override public List getSelectorTypeList() throws NacosException { return selectorManager.getAllSelectorTypes(); } @Override public Page getSubscribers(int pageNo, int pageSize, String namespaceId, String serviceName, String groupName, boolean aggregation) throws Exception { return serviceOperatorV2.getSubscribers(namespaceId, serviceName, groupName, aggregation, pageNo, pageSize); } @Override public Object getServiceList(boolean withInstances, String namespaceId, int pageNo, int pageSize, String serviceName, String groupName, boolean ignoreEmptyService) throws NacosException { if (withInstances) { return catalogServiceV2.pageListServiceDetail(namespaceId, groupName, serviceName, pageNo, pageSize); } return catalogServiceV2.listService(namespaceId, groupName, serviceName, pageNo, pageSize, ignoreEmptyService); } @Override public ServiceDetailInfo getServiceDetail(String namespaceId, String serviceName, String groupName) throws NacosException { return catalogServiceV2.getServiceDetail(namespaceId, groupName, serviceName); } @Override public void updateClusterMetadata(String namespaceId, String groupName, String serviceName, String clusterName, ClusterMetadata clusterMetadata) throws Exception { clusterOperatorV2.updateClusterMetadata(namespaceId, groupName, serviceName, clusterName, clusterMetadata); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/ai/A2aNoopHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.ai; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.console.handler.ai.A2aHandler; import com.alibaba.nacos.core.model.form.PageForm; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Component; import java.util.List; /** * A2a inner handler. * * @author KiteSoar */ @Component @ConditionalOnMissingBean(value = A2aHandler.class, ignored = A2aNoopHandler.class) public class A2aNoopHandler implements A2aHandler { private static final String A2A_NOT_ENABLED_MESSAGE = "Nacos AI A2A module and API required both `naming` and `config` module."; @Override public void registerAgent(AgentCard agentCard, AgentCardForm agentCardForm) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, A2A_NOT_ENABLED_MESSAGE); } @Override public AgentCardDetailInfo getAgentCardWithVersions(AgentForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, A2A_NOT_ENABLED_MESSAGE); } @Override public void deleteAgent(AgentForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, A2A_NOT_ENABLED_MESSAGE); } @Override public void updateAgentCard(AgentCard agentCard, AgentCardUpdateForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, A2A_NOT_ENABLED_MESSAGE); } @Override public Page listAgents(AgentListForm agentListForm, PageForm pageForm) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, A2A_NOT_ENABLED_MESSAGE); } @Override public List listAgentVersions(String namespaceId, String name) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, A2A_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/ai/McpNoopHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.ai; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.console.handler.ai.McpHandler; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Service; /** * Noop implementation of Mcp handler. * Used when `naming` or `config` module are not enabled. * * @author xiweng.yy */ @Service @ConditionalOnMissingBean(value = McpHandler.class, ignored = McpNoopHandler.class) public class McpNoopHandler implements McpHandler { private static final String MCP_NOT_ENABLED_MESSAGE = "Nacos AI MCP module and API required both `naming` and `config` module."; @Override public Page listMcpServers(String namespaceId, String mcpName, String search, int pageNo, int pageSize) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public McpServerDetailInfo getMcpServer(String namespaceId, String mcpName, String mcpId, String version) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public String createMcpServer(String namespaceId, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public void updateMcpServer(String namespaceId, boolean isPublish, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public void deleteMcpServer(String namespaceId, String mcpName, String mcpId, String version) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public McpServerImportValidationResult validateImport(String namespaceId, McpServerImportRequest request) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public McpServerImportResponse executeImport(String namespaceId, McpServerImportRequest request) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/ai/PromptNoopHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.ai; import com.alibaba.nacos.ai.form.prompt.PromptForm; import com.alibaba.nacos.ai.form.prompt.PromptHistoryForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelBindForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelForm; import com.alibaba.nacos.ai.form.prompt.PromptListForm; import com.alibaba.nacos.ai.form.prompt.PromptMetadataForm; import com.alibaba.nacos.ai.form.prompt.PromptPublishForm; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.console.handler.ai.PromptHandler; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Service; /** * Noop implementation of Prompt handler. * Used when AI module is not enabled or both `naming` and `config` modules are not available. * * @author nacos */ @Service @ConditionalOnMissingBean(value = PromptHandler.class, ignored = PromptNoopHandler.class) public class PromptNoopHandler implements PromptHandler { private static final String PROMPT_NOT_ENABLED_MESSAGE = "Nacos AI Prompt module and API required both `naming` and `config` module."; @Override public boolean publishPrompt(PromptPublishForm form, String srcUser, String srcIp) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public PromptMetaInfo getPromptMeta(PromptForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public PromptVersionInfo queryPromptDetail(PromptQueryForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public boolean bindLabel(PromptLabelBindForm form, String srcUser, String srcIp) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public boolean unbindLabel(PromptLabelForm form, String srcUser, String srcIp) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public boolean deletePrompt(PromptForm form, String srcUser, String srcIp) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public Page listPrompts(PromptListForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public Page listPromptVersions(PromptHistoryForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } @Override public boolean updatePromptMetadata(PromptMetadataForm form, String srcUser, String srcIp) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, PROMPT_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/ai/SkillNoopHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.ai; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.ai.form.skills.admin.SkillForm; import com.alibaba.nacos.ai.form.skills.admin.SkillListForm; import com.alibaba.nacos.ai.form.skills.admin.SkillUpdateForm; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.console.handler.ai.SkillHandler; import com.alibaba.nacos.core.model.form.PageForm; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Service; /** * Noop implementation of Skill handler. * Used when AI module is not enabled or both `naming` and `config` modules are not available. * * @author nacos */ @Service @ConditionalOnMissingBean(value = SkillHandler.class, ignored = SkillNoopHandler.class) public class SkillNoopHandler implements SkillHandler { private static final String SKILL_NOT_ENABLED_MESSAGE = "Nacos AI Skill module and API required both `naming` and `config` module."; @Override public void registerSkill(Skill skill, SkillDetailForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, SKILL_NOT_ENABLED_MESSAGE); } @Override public Skill getSkill(SkillForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, SKILL_NOT_ENABLED_MESSAGE); } @Override public void deleteSkill(SkillForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, SKILL_NOT_ENABLED_MESSAGE); } @Override public void updateSkill(Skill skill, SkillUpdateForm form) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, SKILL_NOT_ENABLED_MESSAGE); } @Override public Page listSkills(SkillListForm skillListForm, PageForm pageForm) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, SKILL_NOT_ENABLED_MESSAGE); } @Override public String uploadSkillFromZip(String namespaceId, byte[] zipBytes) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, SKILL_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/config/ConfigNoopHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.console.handler.config.ConfigHandler; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.util.List; import java.util.Map; /** * Noop Implementation of ConfigHandler for handling internal configuration operations. * Used when `config` module is disabled(functionMode is `naming`) * * @author xiweng.yy */ @Service @ConditionalOnMissingBean(value = ConfigHandler.class, ignored = ConfigNoopHandler.class) public class ConfigNoopHandler implements ConfigHandler { private static final String MCP_NOT_ENABLED_MESSAGE = "Current functionMode is `naming`, config module is disabled."; @Override public Page getConfigList(int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public ConfigDetailInfo getConfigDetail(String dataId, String group, String namespaceId) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Boolean deleteConfig(String dataId, String group, String namespaceId, String tag, String clientIp, String srcUser) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Boolean batchDeleteConfigs(List ids, String clientIp, String srcUser) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Page getConfigListByContent(String search, int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public ConfigListenerInfo getListeners(String dataId, String group, String namespaceId, boolean aggregation) throws Exception { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public ConfigListenerInfo getAllSubClientConfigByIp(String ip, boolean all, String namespaceId, boolean aggregation) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public ResponseEntity exportConfig(String dataId, String group, String namespaceId, String appName, List ids) throws Exception { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Result> importAndPublishConfig(String srcUser, String namespaceId, SameConfigPolicy policy, MultipartFile file, String srcIp, String requestIpApp) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Result> cloneConfig(String srcUser, String namespaceId, List configBeansList, SameConfigPolicy policy, String srcIp, String requestIpApp) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public boolean removeBetaConfig(String dataId, String group, String namespaceId, String remoteIp, String requestIpApp, String srcUser) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public ConfigGrayInfo queryBetaConfig(String dataId, String group, String namespaceId) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/config/HistoryNoopHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.console.handler.config.HistoryHandler; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Service; import java.util.List; /** * Noop Implementation of HistoryHandler for handling internal configuration operations. * Used when `config` module is disabled(functionMode is `naming`) * * @author xiweng.yy */ @Service @ConditionalOnMissingBean(value = HistoryHandler.class, ignored = HistoryNoopHandler.class) public class HistoryNoopHandler implements HistoryHandler { private static final String MCP_NOT_ENABLED_MESSAGE = "Current functionMode is `naming`, config module is disabled."; @Override public ConfigHistoryDetailInfo getConfigHistoryInfo(String dataId, String group, String namespaceId, Long nid) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Page listConfigHistory(String dataId, String group, String namespaceId, Integer pageNo, Integer pageSize) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public ConfigHistoryDetailInfo getPreviousConfigHistoryInfo(String dataId, String group, String namespaceId, Long id) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public List getConfigsByTenant(String namespaceId) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/naming/InstanceNoopHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.console.handler.naming.InstanceHandler; import com.alibaba.nacos.naming.model.form.InstanceForm; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.stereotype.Service; /** * Noop Implementation of InstanceHandler that handles instance-related operations. * Used when `naming` module is disabled(functionMode is `config`) * * @author xiweng.yy */ @Service @ConditionalOnMissingBean(value = InstanceHandler.class, ignored = InstanceNoopHandler.class) public class InstanceNoopHandler implements InstanceHandler { private static final String MCP_NOT_ENABLED_MESSAGE = "Current functionMode is `config`, naming module is disabled."; @Override public Page listInstances(String namespaceId, String serviceNameWithoutGroup, String groupName, String clusterName, int page, int pageSize) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public void updateInstance(InstanceForm instanceForm, Instance instance) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/noop/naming/ServiceNoopHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.noop.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceDetailInfo; import com.alibaba.nacos.api.naming.pojo.maintainer.SubscriberInfo; import com.alibaba.nacos.console.handler.naming.ServiceHandler; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.model.form.ServiceForm; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import java.util.List; /** * Noop Implementation of ServiceHandler that handles service-related operations. * Used when `naming` module is disabled(functionMode is `config`) * * @author xiweng.yy */ @org.springframework.stereotype.Service @ConditionalOnMissingBean(value = ServiceHandler.class, ignored = ServiceNoopHandler.class) public class ServiceNoopHandler implements ServiceHandler { private static final String MCP_NOT_ENABLED_MESSAGE = "Current functionMode is `config`, naming module is disabled."; @Override public void createService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public void deleteService(String namespaceId, String serviceName, String groupName) throws Exception { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public void updateService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public List getSelectorTypeList() throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Page getSubscribers(int pageNo, int pageSize, String namespaceId, String serviceName, String groupName, boolean aggregation) throws Exception { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public Object getServiceList(boolean withInstances, String namespaceId, int pageNo, int pageSize, String serviceName, String groupName, boolean ignoreEmptyService) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public ServiceDetailInfo getServiceDetail(String namespaceId, String serviceName, String groupName) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } @Override public void updateClusterMetadata(String namespaceId, String groupName, String serviceName, String clusterName, ClusterMetadata clusterMetadata) throws Exception { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, MCP_NOT_ENABLED_MESSAGE); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/ConsoleMaintainerClientAuthPlugin.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.auth.config.NacosAuthConfigHolder; import com.alibaba.nacos.common.http.client.NacosRestTemplate; import com.alibaba.nacos.console.config.NacosConsoleAuthConfig; import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext; import com.alibaba.nacos.plugin.auth.api.RequestResource; import com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService; import java.util.List; import java.util.Properties; /** * Client Auth Plugin implementation for console remote maintainer client. * * @author xiweng.yy */ public class ConsoleMaintainerClientAuthPlugin extends AbstractClientAuthService { private LoginIdentityContext identityContext = new LoginIdentityContext(); @Override public Boolean login(Properties properties) { NacosConsoleAuthConfig authConfig = (NacosConsoleAuthConfig) NacosAuthConfigHolder.getInstance() .getNacosAuthConfigByScope(NacosConsoleAuthConfig.NACOS_CONSOLE_AUTH_SCOPE); if (authConfig.isSupportServerIdentity()) { identityContext.setParameter(authConfig.getServerIdentityKey(), authConfig.getServerIdentityValue()); } return true; } @Override public void setServerList(List serverList) { } @Override public void setNacosRestTemplate(NacosRestTemplate nacosRestTemplate) { } @Override public LoginIdentityContext getLoginIdentityContext(RequestResource resource) { return identityContext; } @Override public void shutdown() throws NacosException { } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/EnabledRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote; import com.alibaba.nacos.sys.env.Constants; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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; /** * Using Remote handler to call Nacos Admin API to handle console API request. * * @author xiweng.yy */ @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @ConditionalOnProperty(value = Constants.NACOS_DEPLOYMENT_TYPE, havingValue = Constants.NACOS_DEPLOYMENT_TYPE_CONSOLE) public @interface EnabledRemoteHandler { } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/HealthRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.console.handler.HealthHandler; import org.springframework.stereotype.Service; /** * Remote Implementation of HealthHandler that performs health check operations. * * @author xiweng.yy */ @Service @EnabledRemoteHandler public class HealthRemoteHandler implements HealthHandler { private final NacosMaintainerClientHolder clientHolder; public HealthRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public Result checkReadiness() throws NacosException { Boolean result = clientHolder.getNamingMaintainerService().readiness(); return result ? Result.success("ok") : Result.failure("Nacos server readiness failed."); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/NacosMaintainerClientHolder.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote; import com.alibaba.nacos.api.PropertyKeyConst; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.client.utils.ContextPathUtil; import com.alibaba.nacos.common.notify.NotifyCenter; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.cluster.RemoteServerMemberManager; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.MemberChangeListener; import com.alibaba.nacos.core.cluster.MembersChangeEvent; import com.alibaba.nacos.maintainer.client.ai.AiMaintainerFactory; import com.alibaba.nacos.maintainer.client.ai.AiMaintainerService; import com.alibaba.nacos.maintainer.client.config.ConfigMaintainerFactory; import com.alibaba.nacos.maintainer.client.config.ConfigMaintainerService; import com.alibaba.nacos.maintainer.client.naming.NamingMaintainerFactory; import com.alibaba.nacos.maintainer.client.naming.NamingMaintainerService; import com.alibaba.nacos.sys.env.EnvUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.util.List; import java.util.Properties; /** * Nacos maintainer client holder. * * @author xiweng.yy */ @Component @EnabledRemoteHandler public class NacosMaintainerClientHolder extends MemberChangeListener { private static final Logger LOGGER = LoggerFactory.getLogger(NacosMaintainerClientHolder.class); private static final String REMOTE_SERVER_CONTEXT_PATH_KEY = "nacos.console.remote.server.context-path"; private static final String DEFAULT_REMOTE_SERVER_CONTEXT_PATH = "/nacos"; private static final String PATH_SEPARATOR = "/"; private static final int ROOT_PATH_LENGTH = 1; private final RemoteServerMemberManager memberManager; private volatile NamingMaintainerService namingMaintainerService; private volatile ConfigMaintainerService configMaintainerService; private volatile AiMaintainerService aiMaintainerService; public NacosMaintainerClientHolder(RemoteServerMemberManager memberManager) throws NacosException { this.memberManager = memberManager; buildMaintainerService(); NotifyCenter.registerSubscriber(this); } private void buildMaintainerService() throws NacosException { List memberAddress = memberManager.allMembers().stream().map(Member::getAddress).toList(); String memberAddressString = StringUtils.join(memberAddress, ","); Properties properties = new Properties(); properties.setProperty(PropertyKeyConst.SERVER_ADDR, memberAddressString); String remoteContextPath = resolveRemoteContextPath(); properties.setProperty(PropertyKeyConst.CONTEXT_PATH, remoteContextPath); namingMaintainerService = NamingMaintainerFactory.createNamingMaintainerService(properties); configMaintainerService = ConfigMaintainerFactory.createConfigMaintainerService(properties); aiMaintainerService = AiMaintainerFactory.createAiMaintainerService(properties); } static String resolveRemoteContextPath() { String remoteContextPath = EnvUtil.getProperty(REMOTE_SERVER_CONTEXT_PATH_KEY, DEFAULT_REMOTE_SERVER_CONTEXT_PATH); remoteContextPath = StringUtils.trim(remoteContextPath); remoteContextPath = ContextPathUtil.normalizeContextPath(remoteContextPath); while (remoteContextPath.endsWith(PATH_SEPARATOR) && remoteContextPath.length() > ROOT_PATH_LENGTH) { remoteContextPath = remoteContextPath.substring(0, remoteContextPath.length() - 1); } return remoteContextPath; } public NamingMaintainerService getNamingMaintainerService() { return namingMaintainerService; } public ConfigMaintainerService getConfigMaintainerService() { return configMaintainerService; } public AiMaintainerService getAiMaintainerService() { return aiMaintainerService; } @Override public void onEvent(MembersChangeEvent event) { try { buildMaintainerService(); } catch (NacosException e) { LOGGER.warn("Nacos Server members changed, but build new maintain client failed with: ", e); } } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/RemoteServerConnector.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote; import com.alibaba.nacos.api.common.NodeState; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.model.response.NacosMember; import com.alibaba.nacos.auth.config.NacosAuthConfig; import com.alibaba.nacos.auth.config.NacosAuthConfigHolder; import com.alibaba.nacos.common.utils.CollectionUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.config.NacosConsoleAuthConfig; import com.alibaba.nacos.console.handler.core.ClusterHandler; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.cluster.NacosMemberManager; import com.alibaba.nacos.sys.env.EnvUtil; import org.apache.hc.core5.http.HttpRequest; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Map; import java.util.stream.Collectors; /** * Common connector for remote server operations in console deployment mode. * *

    Provides shared functionality for remote HTTP forwarding services, including * healthy member selection, authentication identity injection, and server context path resolution.

    * * @author nacos */ @Component @EnabledRemoteHandler public class RemoteServerConnector { private final NacosMemberManager memberManager; private final ClusterHandler remoteClusterHandler; public RemoteServerConnector(NacosMemberManager memberManager, ClusterHandler remoteClusterHandler) { this.memberManager = memberManager; this.remoteClusterHandler = remoteClusterHandler; } /** * Add authentication identity headers to the HTTP request. * * @param request the HTTP request to add auth headers to */ public void addAuthIdentity(HttpRequest request) { NacosAuthConfig authConfig = NacosAuthConfigHolder.getInstance() .getNacosAuthConfigByScope(NacosConsoleAuthConfig.NACOS_CONSOLE_AUTH_SCOPE); if (StringUtils.isNotBlank(authConfig.getServerIdentityKey())) { request.setHeader(authConfig.getServerIdentityKey(), authConfig.getServerIdentityValue()); } } /** * Get the server context path for remote server. * * @return server context path, defaults to "/nacos" */ public String getServerContextPath() { return EnvUtil.getProperty("nacos.console.remote.server.context-path", "/nacos"); } /** * Randomly select one healthy member from the cluster. * * @return a healthy cluster member * @throws NacosException if no healthy server node is found */ public Member randomOneHealthyMember() throws NacosException { Collection allMembers = memberManager.allMembers(); Collection membersWithState = remoteClusterHandler.getNodeList(""); Map nodeStateMap = membersWithState.stream() .collect(Collectors.toMap(NacosMember::getAddress, NacosMember::getState)); allMembers.removeIf(node -> !NodeState.UP.equals(nodeStateMap.get(node.getAddress()))); if (CollectionUtils.isEmpty(allMembers)) { throw new NacosRuntimeException(NacosException.SERVER_ERROR, "No healthy server node found."); } return allMembers.parallelStream().findAny().orElseThrow(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/ServerStateRemoteHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.console.handler.impl.AbstractServerStateHandler; import com.alibaba.nacos.sys.env.Constants; import com.alibaba.nacos.sys.env.EnvUtil; import com.alibaba.nacos.sys.module.ModuleState; import com.alibaba.nacos.sys.module.ModuleStateHolder; import org.springframework.stereotype.Service; import java.util.Map; /** * Remote Implementation of ServerStateHandler that performs server state operations. * * @author xiweng.yy */ @Service @EnabledRemoteHandler public class ServerStateRemoteHandler extends AbstractServerStateHandler { private final NacosMaintainerClientHolder clientHolder; public ServerStateRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } public Map getServerState() throws NacosException { Map serverState = this.clientHolder.getNamingMaintainerService().getServerState(); serverState.put(Constants.SERVER_PORT_STATE, EnvUtil.getProperty("nacos.console.port", "8080")); // Add current console states for (ModuleState each : ModuleStateHolder.getInstance().getAllModuleStates()) { each.getStates().forEach((s, o) -> serverState.put(s, null == o ? null : o.toString())); } return serverState; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/ai/A2aRemoteHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.ai; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.A2aHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import com.alibaba.nacos.core.model.form.PageForm; import com.alibaba.nacos.maintainer.client.ai.AiMaintainerService; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.util.List; /** * A2aRemoteHandler. * * @author KiteSoar */ @Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class A2aRemoteHandler implements A2aHandler { private final NacosMaintainerClientHolder clientHolder; public A2aRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public void registerAgent(AgentCard agentCard, AgentCardForm agentCardForm) throws NacosException { clientHolder.getAiMaintainerService() .registerAgent(agentCard, agentCardForm.getNamespaceId(), agentCardForm.getRegistrationType()); } @Override public AgentCardDetailInfo getAgentCardWithVersions(AgentForm form) throws NacosException { return clientHolder.getAiMaintainerService() .getAgentCard(form.getAgentName(), form.getNamespaceId(), form.getRegistrationType()); } @Override public void deleteAgent(AgentForm form) throws NacosException { clientHolder.getAiMaintainerService().deleteAgent(form.getAgentName(), form.getNamespaceId()); } @Override public void updateAgentCard(AgentCard agentCard, AgentCardUpdateForm form) throws NacosException { clientHolder.getAiMaintainerService() .updateAgentCard(agentCard, form.getNamespaceId(), form.getSetAsLatest(), form.getRegistrationType()); } @Override public Page listAgents(AgentListForm agentListForm, PageForm pageForm) throws NacosException { AiMaintainerService aiMaintainerService = clientHolder.getAiMaintainerService(); return Constants.MCP_LIST_SEARCH_BLUR.equalsIgnoreCase(agentListForm.getSearch()) ? aiMaintainerService.searchAgentCardsByName(agentListForm.getNamespaceId(), agentListForm.getAgentName(), pageForm.getPageNo(), pageForm.getPageSize()) : aiMaintainerService.listAgentCards(agentListForm.getNamespaceId(), agentListForm.getAgentName(), pageForm.getPageNo(), pageForm.getPageSize()); } @Override public List listAgentVersions(String namespaceId, String name) throws NacosException { return clientHolder.getAiMaintainerService().listAllVersionOfAgent(name, namespaceId); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/ai/McpRemoteHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.ai; import com.alibaba.nacos.ai.constant.Constants; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.api.NacosApiException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.ErrorCode; import com.alibaba.nacos.console.handler.ai.McpHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; /** * Remote implementation of Mcp handler. * * @author xiweng.yy */ @Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class McpRemoteHandler implements McpHandler { private final NacosMaintainerClientHolder clientHolder; public McpRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public Page listMcpServers(String namespaceId, String mcpName, String search, int pageNo, int pageSize) throws NacosException { if (Constants.MCP_LIST_SEARCH_ACCURATE.equalsIgnoreCase(search)) { return clientHolder.getAiMaintainerService().listMcpServer(namespaceId, mcpName, pageNo, pageSize); } else { return clientHolder.getAiMaintainerService().searchMcpServer(namespaceId, mcpName, pageNo, pageSize); } } @Override public McpServerDetailInfo getMcpServer(String namespaceId, String mcpName, String mcpId, String version) throws NacosException { return clientHolder.getAiMaintainerService().getMcpServerDetail(namespaceId, mcpName, mcpId, version); } @Override public String createMcpServer(String namespaceId, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { return clientHolder.getAiMaintainerService() .createMcpServer(namespaceId, serverSpecification.getName(), serverSpecification, toolSpecification, endpointSpecification); } @Override public void updateMcpServer(String namespaceId, boolean isPublish, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException { clientHolder.getAiMaintainerService() .updateMcpServer(namespaceId, serverSpecification.getName(), isPublish, serverSpecification, toolSpecification, endpointSpecification, overrideExisting); } @Override public void deleteMcpServer(String namespaceId, String mcpName, String mcpId, String version) throws NacosException { clientHolder.getAiMaintainerService().deleteMcpServer(namespaceId, mcpName, mcpId, version); } @Override public McpServerImportValidationResult validateImport(String namespaceId, McpServerImportRequest request) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, "MCP import functionality is not supported in remote mode"); } @Override public McpServerImportResponse executeImport(String namespaceId, McpServerImportRequest request) throws NacosException { throw new NacosApiException(NacosException.SERVER_NOT_IMPLEMENTED, ErrorCode.API_FUNCTION_DISABLED, "MCP import functionality is not supported in remote mode"); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/ai/PromptRemoteHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.ai; import com.alibaba.nacos.ai.form.prompt.PromptForm; import com.alibaba.nacos.ai.form.prompt.PromptHistoryForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelBindForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelForm; import com.alibaba.nacos.ai.form.prompt.PromptListForm; import com.alibaba.nacos.ai.form.prompt.PromptMetadataForm; import com.alibaba.nacos.ai.form.prompt.PromptPublishForm; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.PromptHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; /** * Remote implementation of Prompt handler. * *

    Calls remote Nacos server through maintainer client for Prompt operations.

    * * @author nacos */ @Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class PromptRemoteHandler implements PromptHandler { private final NacosMaintainerClientHolder clientHolder; public PromptRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public boolean publishPrompt(PromptPublishForm form, String srcUser, String srcIp) throws NacosException { return clientHolder.getAiMaintainerService().publishPrompt( form.getNamespaceId(), form.getPromptKey(), form.getVersion(), form.getTemplate(), form.getCommitMsg(), form.getDescription(), form.getBizTags(), form.getVariables() ); } @Override public PromptMetaInfo getPromptMeta(PromptForm form) throws NacosException { return clientHolder.getAiMaintainerService().getPromptMeta( form.getNamespaceId(), form.getPromptKey() ); } @Override public PromptVersionInfo queryPromptDetail(PromptQueryForm form) throws NacosException { return clientHolder.getAiMaintainerService().queryPromptDetail( form.getNamespaceId(), form.getPromptKey(), form.getVersion(), form.getLabel() ); } @Override public boolean bindLabel(PromptLabelBindForm form, String srcUser, String srcIp) throws NacosException { return clientHolder.getAiMaintainerService().bindLabel( form.getNamespaceId(), form.getPromptKey(), form.getLabel(), form.getVersion() ); } @Override public boolean unbindLabel(PromptLabelForm form, String srcUser, String srcIp) throws NacosException { return clientHolder.getAiMaintainerService().unbindLabel( form.getNamespaceId(), form.getPromptKey(), form.getLabel() ); } @Override public boolean deletePrompt(PromptForm form, String srcUser, String srcIp) throws NacosException { return clientHolder.getAiMaintainerService().deletePrompt( form.getNamespaceId(), form.getPromptKey() ); } @Override public Page listPrompts(PromptListForm form) throws NacosException { return clientHolder.getAiMaintainerService().listPrompts( form.getNamespaceId(), form.getPromptKey(), form.getSearch(), form.getBizTags(), form.getPageNo(), form.getPageSize() ); } @Override public Page listPromptVersions(PromptHistoryForm form) throws NacosException { return clientHolder.getAiMaintainerService().listPromptVersions( form.getNamespaceId(), form.getPromptKey(), form.getPageNo(), form.getPageSize() ); } @Override public boolean updatePromptMetadata(PromptMetadataForm form, String srcUser, String srcIp) throws NacosException { return clientHolder.getAiMaintainerService().updatePromptMetadata( form.getNamespaceId(), form.getPromptKey(), form.getDescription(), form.getBizTags() ); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/ai/SkillRemoteHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.ai; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.ai.form.skills.admin.SkillForm; import com.alibaba.nacos.ai.form.skills.admin.SkillListForm; import com.alibaba.nacos.ai.form.skills.admin.SkillUpdateForm; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.SkillHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import com.alibaba.nacos.core.model.form.PageForm; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; /** * Remote implementation of Skill handler. * *

    Calls remote Nacos server through maintainer client for Skill operations.

    * * @author nacos */ @Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionAiEnabled.class) public class SkillRemoteHandler implements SkillHandler { private final NacosMaintainerClientHolder clientHolder; public SkillRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public void registerSkill(Skill skill, SkillDetailForm form) throws NacosException { clientHolder.getAiMaintainerService().registerSkill(form.getNamespaceId(), skill); } @Override public Skill getSkill(SkillForm form) throws NacosException { return clientHolder.getAiMaintainerService().getSkillDetail( form.getNamespaceId(), form.getSkillName() ); } @Override public void deleteSkill(SkillForm form) throws NacosException { clientHolder.getAiMaintainerService().deleteSkill( form.getNamespaceId(), form.getSkillName() ); } @Override public void updateSkill(Skill skill, SkillUpdateForm form) throws NacosException { clientHolder.getAiMaintainerService().updateSkill(form.getNamespaceId(), skill); } @Override public Page listSkills(SkillListForm skillListForm, PageForm pageForm) throws NacosException { return clientHolder.getAiMaintainerService().listSkills( skillListForm.getNamespaceId(), skillListForm.getSkillName(), skillListForm.getSearch(), pageForm.getPageNo(), pageForm.getPageSize() ); } @Override public String uploadSkillFromZip(String namespaceId, byte[] zipBytes) throws NacosException { return clientHolder.getAiMaintainerService().uploadSkillFromZip(namespaceId, zipBytes); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/config/ConfigImportAndExportService.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.config; import com.alibaba.nacos.api.common.Constants; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.http.HttpUtils; import com.alibaba.nacos.common.http.param.Query; import com.alibaba.nacos.common.utils.IoUtils; import com.alibaba.nacos.common.utils.JacksonUtils; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.utils.RequestUtil; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.RemoteServerConnector; import com.alibaba.nacos.core.cluster.Member; import com.alibaba.nacos.core.utils.WebUtils; import com.fasterxml.jackson.core.type.TypeReference; import org.apache.hc.client5.http.HttpResponseException; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.classic.methods.HttpPost; import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder; import org.apache.hc.client5.http.impl.classic.AbstractHttpClientResponseHandler; import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.ClassicHttpResponse; import org.apache.hc.core5.http.ContentType; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.ProtocolException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Map; /** * Nacos config import and export service. * * @author xiweng.yy */ @Service @EnabledRemoteHandler public class ConfigImportAndExportService { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigImportAndExportService.class); private static final String REMOTE_CONFIG_IMPORT_URL = "http://%s%s/v3/admin/cs/config/import"; private static final String REMOTE_CONFIG_EXPORT_URL = "http://%s%s/v3/admin/cs/config/export"; private final RemoteServerConnector remoteServerConnector; public ConfigImportAndExportService(RemoteServerConnector remoteServerConnector) { this.remoteServerConnector = remoteServerConnector; } /** * Do import config to remote server. * * @param sourceUser source user from console request * @param namespaceId namespace id from console request * @param policy conflict policy * @param importFile imported config file * @param sourceIp source ip from console request * @param sourceApp source app from console request * @return Maps of import success and failed count */ public Result> importConfig(String sourceUser, String namespaceId, SameConfigPolicy policy, MultipartFile importFile, String sourceIp, String sourceApp) throws NacosException { String serverContextPath = remoteServerConnector.getServerContextPath(); Member serverMember = remoteServerConnector.randomOneHealthyMember(); String url = String.format(REMOTE_CONFIG_IMPORT_URL, serverMember.getAddress(), serverContextPath); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { Query query = Query.newInstance().addParam("namespaceId", namespaceId).addParam("srcUser", sourceUser); URI uri = HttpUtils.buildUri(url, query); HttpPost httpPost = new HttpPost(uri); httpPost.setHeader(WebUtils.X_FORWARDED_FOR, sourceIp); httpPost.setHeader(RequestUtil.CLIENT_APPNAME_HEADER, sourceApp); remoteServerConnector.addAuthIdentity(httpPost); String contentTypeString = null == importFile.getContentType() ? MediaType.MULTIPART_FORM_DATA_VALUE : importFile.getContentType(); ContentType contentType = ContentType.create(contentTypeString, Constants.ENCODE); MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); multipartEntityBuilder.addBinaryBody("file", importFile.getInputStream(), contentType, importFile.getOriginalFilename()); multipartEntityBuilder.addTextBody("policy", policy.name(), contentType); HttpEntity entity = multipartEntityBuilder.build(); httpPost.setEntity(entity); String executeResult = httpClient.execute(httpPost, new BasicHttpClientResponseHandler()); return JacksonUtils.toObj(executeResult, new TypeReference<>() { }); } catch (HttpResponseException responseException) { LOGGER.error("Import config to server {} failed with code {}: ", serverMember.getAddress(), responseException.getStatusCode()); throw new NacosRuntimeException(responseException.getStatusCode(), responseException.getMessage()); } catch (IOException | URISyntaxException e) { LOGGER.error("Import config to server {} failed: ", serverMember.getAddress(), e); throw new NacosRuntimeException(NacosException.SERVER_ERROR, "Import config to server failed."); } } /** * Do export config to from server. * * @param dataId data id of export config * @param group group name of export config * @param namespaceId namespace of export config * @param appName app name of export config * @param ids storage id of export config * @return export file entity * @throws Exception any exception during export config */ public ResponseEntity exportConfig(String dataId, String group, String namespaceId, String appName, List ids) throws Exception { String serverContextPath = remoteServerConnector.getServerContextPath(); Member serverMember = remoteServerConnector.randomOneHealthyMember(); String url = String.format(REMOTE_CONFIG_EXPORT_URL, serverMember.getAddress(), serverContextPath); try (CloseableHttpClient httpClient = HttpClients.createDefault()) { Query query = Query.newInstance().addParam("namespaceId", namespaceId).addParam("dataId", dataId) .addParam("groupName", group).addParam("ids", StringUtils.join(ids, ",")); URI uri = HttpUtils.buildUri(url, query); HttpGet httpGet = new HttpGet(uri); remoteServerConnector.addAuthIdentity(httpGet); return httpClient.execute(httpGet, new ExportHttpClientResponseHandler()); } catch (HttpResponseException responseException) { LOGGER.error("Export config from server {} failed with code {}: ", serverMember.getAddress(), responseException.getStatusCode()); throw new NacosRuntimeException(responseException.getStatusCode(), responseException.getMessage()); } catch (IOException | URISyntaxException e) { LOGGER.error("Export config from server {} failed: ", serverMember.getAddress(), e); throw new NacosRuntimeException(NacosException.SERVER_ERROR, "Export config to server failed."); } } static class ExportHttpClientResponseHandler extends AbstractHttpClientResponseHandler> { private String contentDisposition; @Override public ResponseEntity handleResponse(ClassicHttpResponse response) throws IOException { try { contentDisposition = response.getHeader("Content-Disposition").getValue(); } catch (ProtocolException e) { throw new NacosRuntimeException(NacosException.SERVER_ERROR, "Export config from server, parse response file name failed; ", e); } return super.handleResponse(response); } @Override public ResponseEntity handleEntity(HttpEntity entity) throws IOException { InputStream inputStream = entity.getContent(); try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { IoUtils.copy(inputStream, outputStream); byte[] responseBody = outputStream.toByteArray(); return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM) .header("Content-Disposition", contentDisposition).body(responseBody); } } } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/config/ConfigRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigCloneInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.config.server.constant.Constants; import com.alibaba.nacos.config.server.constant.ParametersField; import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.console.handler.config.ConfigHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import com.alibaba.nacos.maintainer.client.config.ConfigMaintainerService; import org.springframework.context.annotation.Conditional; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.util.ArrayList; import java.util.List; import java.util.Map; import static com.alibaba.nacos.api.common.Constants.ALL_PATTERN; /** * Remote Implementation of ConfigHandler for handling internal configuration operations. * * @author xiweng.yy */ @Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionConfigEnabled.class) public class ConfigRemoteHandler implements ConfigHandler { private final NacosMaintainerClientHolder clientHolder; private final ConfigImportAndExportService importAndExportService; public ConfigRemoteHandler(NacosMaintainerClientHolder clientHolder, ConfigImportAndExportService importAndExportService) { this.clientHolder = clientHolder; this.importAndExportService = importAndExportService; } @Override public Page getConfigList(int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws NacosException { String search = dataId.contains(ALL_PATTERN) ? Constants.CONFIG_SEARCH_BLUR : Constants.CONFIG_SEARCH_ACCURATE; return listConfigInfo(search, pageNo, pageSize, dataId, group, namespaceId, configAdvanceInfo); } @Override public ConfigDetailInfo getConfigDetail(String dataId, String group, String namespaceId) throws NacosException { try { return clientHolder.getConfigMaintainerService().getConfig(dataId, group, namespaceId); } catch (NacosException e) { if (NacosException.NOT_FOUND == e.getErrCode()) { return null; } throw e; } } @Override public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo) throws NacosException { ConfigMaintainerService configMaintainerService = clientHolder.getConfigMaintainerService(); if (StringUtils.isBlank(configRequestInfo.getBetaIps())) { return configMaintainerService.publishConfig(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getContent(), configForm.getAppName(), configForm.getSrcUser(), configForm.getConfigTags(), configForm.getDesc(), configForm.getType()); } else { return configMaintainerService.publishBetaConfig(configForm.getDataId(), configForm.getGroup(), configForm.getNamespaceId(), configForm.getContent(), configForm.getAppName(), configForm.getSrcUser(), configForm.getConfigTags(), configForm.getDesc(), configForm.getType(), configRequestInfo.getBetaIps()); } } @Override public Boolean deleteConfig(String dataId, String group, String namespaceId, String tag, String clientIp, String srcUser) throws NacosException { return clientHolder.getConfigMaintainerService().deleteConfig(dataId, group, namespaceId); } @Override public Boolean batchDeleteConfigs(List ids, String clientIp, String srcUser) throws NacosException { return clientHolder.getConfigMaintainerService().deleteConfigs(ids); } @Override public Page getConfigListByContent(String search, int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws NacosException { return listConfigInfo(search, pageNo, pageSize, dataId, group, namespaceId, configAdvanceInfo); } @Override public ConfigListenerInfo getListeners(String dataId, String group, String namespaceId, boolean aggregation) throws Exception { return clientHolder.getConfigMaintainerService().getListeners(dataId, group, namespaceId, aggregation); } @Override public ConfigListenerInfo getAllSubClientConfigByIp(String ip, boolean all, String namespaceId, boolean aggregation) throws NacosException { return clientHolder.getConfigMaintainerService().getAllSubClientConfigByIp(ip, all, namespaceId, aggregation); } @Override public ResponseEntity exportConfig(String dataId, String group, String namespaceId, String appName, List ids) throws Exception { return importAndExportService.exportConfig(dataId, group, namespaceId, appName, ids); } @Override public Result> importAndPublishConfig(String srcUser, String namespaceId, SameConfigPolicy policy, MultipartFile file, String srcIp, String requestIpApp) throws NacosException { return importAndExportService.importConfig(srcUser, namespaceId, policy, file, srcIp, requestIpApp); } @Override public Result> cloneConfig(String srcUser, String namespaceId, List configBeansList, SameConfigPolicy policy, String srcIp, String requestIpApp) throws NacosException { List configInfos = new ArrayList<>(configBeansList.size()); configBeansList.forEach(sameNamespaceCloneConfigBean -> { ConfigCloneInfo configCloneInfo = new ConfigCloneInfo(); configCloneInfo.setConfigId(sameNamespaceCloneConfigBean.getCfgId()); configCloneInfo.setTargetDataId(sameNamespaceCloneConfigBean.getDataId()); configCloneInfo.setTargetGroupName(sameNamespaceCloneConfigBean.getGroup()); configInfos.add(configCloneInfo); }); return Result.success( clientHolder.getConfigMaintainerService().cloneConfig(namespaceId, configInfos, srcUser, policy)); } @Override public boolean removeBetaConfig(String dataId, String group, String namespaceId, String remoteIp, String requestIpApp, String srcUser) throws NacosException { return clientHolder.getConfigMaintainerService().stopBeta(dataId, group, namespaceId); } @Override public ConfigGrayInfo queryBetaConfig(String dataId, String group, String namespaceId) throws NacosException { try { return clientHolder.getConfigMaintainerService().queryBeta(dataId, group, namespaceId); } catch (NacosException e) { if (NacosException.NOT_FOUND == e.getErrCode()) { // admin api return 404, means the config is not in beta. return null; } // other exception throw it. throw e; } } private Page listConfigInfo(String search, int pageNo, int pageSize, String dataId, String groupName, String namespaceId, Map configAdvanceInfo) throws NacosException { String type = getInfoFromAdvanceInfo(configAdvanceInfo, ParametersField.TYPES); String appName = getInfoFromAdvanceInfo(configAdvanceInfo, "appName"); String configTags = getInfoFromAdvanceInfo(configAdvanceInfo, "config_tags"); String configDetail = getInfoFromAdvanceInfo(configAdvanceInfo, "content"); return clientHolder.getConfigMaintainerService() .searchConfigByDetails(dataId, groupName, namespaceId, search, configDetail, type, configTags, appName, pageNo, pageSize); } private String getInfoFromAdvanceInfo(Map configAdvanceInfo, String key) { return configAdvanceInfo.containsKey(key) ? (String) configAdvanceInfo.get(key) : StringUtils.EMPTY; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/config/HistoryRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.config.HistoryHandler; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.util.List; /** * Remote Implementation of HistoryHandler for handling internal configuration operations. * * @author xiweng.yy */ @Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionConfigEnabled.class) public class HistoryRemoteHandler implements HistoryHandler { private final NacosMaintainerClientHolder clientHolder; public HistoryRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public ConfigHistoryDetailInfo getConfigHistoryInfo(String dataId, String group, String namespaceId, Long nid) throws NacosException { return clientHolder.getConfigMaintainerService().getConfigHistoryInfo(dataId, group, namespaceId, nid); } @Override public Page listConfigHistory(String dataId, String group, String namespaceId, Integer pageNo, Integer pageSize) throws NacosException { return clientHolder.getConfigMaintainerService() .listConfigHistory(dataId, group, namespaceId, pageNo, pageSize); } @Override public ConfigHistoryDetailInfo getPreviousConfigHistoryInfo(String dataId, String group, String namespaceId, Long id) throws NacosException { return clientHolder.getConfigMaintainerService().getPreviousConfigHistoryInfo(dataId, group, namespaceId, id); } @Override public List getConfigsByTenant(String namespaceId) throws NacosException { return clientHolder.getConfigMaintainerService().getConfigListByNamespace(namespaceId); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/core/ClusterRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.NacosMember; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.handler.core.ClusterHandler; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import org.springframework.stereotype.Service; import java.util.Collection; /** * Remote Implementation of ClusterHandler that handles cluster-related operations. * * @author xiweng.yy */ @Service @EnabledRemoteHandler public class ClusterRemoteHandler implements ClusterHandler { private final NacosMaintainerClientHolder clientHolder; public ClusterRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } /** * Retrieves a list of cluster members with an optional search keyword. * * @param ipKeyWord the search keyword for filtering members * @return a collection of matching members */ @Override public Collection getNodeList(String ipKeyWord) throws NacosException { return clientHolder.getNamingMaintainerService().listClusterNodes(StringUtils.EMPTY, StringUtils.EMPTY); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/core/NamespaceRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.console.handler.core.NamespaceHandler; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import com.alibaba.nacos.core.namespace.model.form.NamespaceForm; import org.springframework.stereotype.Service; import java.util.List; /** * Remote Implementation of NamespaceHandler that handles namespace-related operations. * * @author xiweng.yy */ @Service @EnabledRemoteHandler public class NamespaceRemoteHandler implements NamespaceHandler { private final NacosMaintainerClientHolder clientHolder; public NamespaceRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public List getNamespaceList() throws NacosException { return clientHolder.getNamingMaintainerService().getNamespaceList(); } @Override public Namespace getNamespaceDetail(String namespaceId) throws NacosException { return clientHolder.getNamingMaintainerService().getNamespace(namespaceId); } @Override public Boolean createNamespace(String namespaceId, String namespaceName, String namespaceDesc) throws NacosException { return clientHolder.getNamingMaintainerService().createNamespace(namespaceId, namespaceName, namespaceDesc); } @Override public Boolean updateNamespace(NamespaceForm namespaceForm) throws NacosException { return clientHolder.getNamingMaintainerService() .updateNamespace(namespaceForm.getNamespaceId(), namespaceForm.getNamespaceName(), namespaceForm.getNamespaceDesc()); } @Override public Boolean deleteNamespace(String namespaceId) throws NacosException { return clientHolder.getNamingMaintainerService().deleteNamespace(namespaceId); } @Override public Boolean checkNamespaceIdExist(String namespaceId) throws NacosException { return clientHolder.getNamingMaintainerService().checkNamespaceIdExist(namespaceId); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/core/PluginRemoteHandler.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.plugin.ConfigItemDefinition; import com.alibaba.nacos.api.plugin.ConfigItemType; import com.alibaba.nacos.console.handler.core.PluginHandler; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import com.alibaba.nacos.core.plugin.model.vo.PluginDetailVO; import com.alibaba.nacos.core.plugin.model.vo.PluginInfoVO; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Remote implementation of PluginHandler that handles plugin-related operations via HTTP. * * @author WangzJi */ @Service @EnabledRemoteHandler public class PluginRemoteHandler implements PluginHandler { private static final String FIELD_PLUGIN_ID = "pluginId"; private static final String FIELD_PLUGIN_TYPE = "pluginType"; private static final String FIELD_PLUGIN_NAME = "pluginName"; private static final String FIELD_ENABLED = "enabled"; private static final String FIELD_CRITICAL = "critical"; private static final String FIELD_CONFIGURABLE = "configurable"; private static final String FIELD_EXCLUSIVE = "exclusive"; private static final String FIELD_TOTAL_NODE_COUNT = "totalNodeCount"; private static final String FIELD_AVAILABLE_NODE_COUNT = "availableNodeCount"; private static final String FIELD_CONFIG = "config"; private static final String FIELD_CONFIG_DEFINITIONS = "configDefinitions"; private static final String FIELD_KEY = "key"; private static final String FIELD_NAME = "name"; private static final String FIELD_DESCRIPTION = "description"; private static final String FIELD_DEFAULT_VALUE = "defaultValue"; private static final String FIELD_REQUIRED = "required"; private static final String FIELD_TYPE = "type"; private static final String FIELD_ENUM_VALUES = "enumValues"; private final NacosMaintainerClientHolder clientHolder; public PluginRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public List listPlugins(String pluginType) throws NacosException { List> rawList = clientHolder.getNamingMaintainerService().listPlugins(pluginType); List result = new ArrayList<>(rawList.size()); for (Map raw : rawList) { result.add(convertToPluginInfoVO(raw)); } return result; } @Override public PluginDetailVO getPluginDetail(String pluginType, String pluginName) throws NacosException { Map raw = clientHolder.getNamingMaintainerService().getPluginDetail(pluginType, pluginName); return convertToPluginDetailVO(raw); } @Override public void updatePluginStatus(String pluginType, String pluginName, boolean enabled, boolean localOnly) throws NacosException { clientHolder.getNamingMaintainerService().updatePluginStatus(pluginType, pluginName, enabled, localOnly); } @Override public void updatePluginConfig(String pluginType, String pluginName, Map config, boolean localOnly) throws NacosException { clientHolder.getNamingMaintainerService().updatePluginConfig(pluginType, pluginName, config, localOnly); } @Override public Map getPluginAvailability(String pluginType, String pluginName) throws NacosException { return clientHolder.getNamingMaintainerService().getPluginAvailability(pluginType, pluginName); } private PluginInfoVO convertToPluginInfoVO(Map raw) { PluginInfoVO vo = new PluginInfoVO(); vo.setPluginId((String) raw.get(FIELD_PLUGIN_ID)); vo.setPluginType((String) raw.get(FIELD_PLUGIN_TYPE)); vo.setPluginName((String) raw.get(FIELD_PLUGIN_NAME)); vo.setEnabled(Boolean.TRUE.equals(raw.get(FIELD_ENABLED))); vo.setCritical(Boolean.TRUE.equals(raw.get(FIELD_CRITICAL))); vo.setConfigurable(Boolean.TRUE.equals(raw.get(FIELD_CONFIGURABLE))); vo.setExclusive(Boolean.TRUE.equals(raw.get(FIELD_EXCLUSIVE))); if (raw.get(FIELD_TOTAL_NODE_COUNT) != null) { vo.setTotalNodeCount(((Number) raw.get(FIELD_TOTAL_NODE_COUNT)).intValue()); } if (raw.get(FIELD_AVAILABLE_NODE_COUNT) != null) { vo.setAvailableNodeCount(((Number) raw.get(FIELD_AVAILABLE_NODE_COUNT)).intValue()); } return vo; } @SuppressWarnings("unchecked") private PluginDetailVO convertToPluginDetailVO(Map raw) { PluginDetailVO vo = new PluginDetailVO(); vo.setPluginId((String) raw.get(FIELD_PLUGIN_ID)); vo.setPluginType((String) raw.get(FIELD_PLUGIN_TYPE)); vo.setPluginName((String) raw.get(FIELD_PLUGIN_NAME)); vo.setEnabled(Boolean.TRUE.equals(raw.get(FIELD_ENABLED))); vo.setCritical(Boolean.TRUE.equals(raw.get(FIELD_CRITICAL))); vo.setConfigurable(Boolean.TRUE.equals(raw.get(FIELD_CONFIGURABLE))); if (raw.get(FIELD_CONFIG) != null) { vo.setConfig((Map) raw.get(FIELD_CONFIG)); } if (raw.get(FIELD_CONFIG_DEFINITIONS) != null) { List> rawDefinitions = (List>) raw.get(FIELD_CONFIG_DEFINITIONS); vo.setConfigDefinitions(convertToConfigItemDefinitions(rawDefinitions)); } return vo; } @SuppressWarnings("unchecked") private List convertToConfigItemDefinitions(List> rawList) { List result = new ArrayList<>(rawList.size()); for (Map raw : rawList) { ConfigItemDefinition definition = new ConfigItemDefinition(); definition.setKey((String) raw.get(FIELD_KEY)); definition.setName((String) raw.get(FIELD_NAME)); definition.setDescription((String) raw.get(FIELD_DESCRIPTION)); definition.setDefaultValue((String) raw.get(FIELD_DEFAULT_VALUE)); definition.setRequired(Boolean.TRUE.equals(raw.get(FIELD_REQUIRED))); if (raw.get(FIELD_TYPE) != null) { String typeStr = raw.get(FIELD_TYPE).toString(); try { definition.setType(ConfigItemType.valueOf(typeStr)); } catch (IllegalArgumentException e) { definition.setType(ConfigItemType.STRING); } } if (raw.get(FIELD_ENUM_VALUES) != null) { definition.setEnumValues((List) raw.get(FIELD_ENUM_VALUES)); } result.add(definition); } return result; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/naming/InstanceRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import com.alibaba.nacos.console.handler.naming.InstanceHandler; import com.alibaba.nacos.core.utils.PageUtil; import com.alibaba.nacos.naming.model.form.InstanceForm; import org.springframework.context.annotation.Conditional; import org.springframework.stereotype.Service; import java.util.List; /** * Remote Implementation of InstanceHandler that handles instance-related operations. * * @author xiweng.yy */ @Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionNamingEnabled.class) public class InstanceRemoteHandler implements InstanceHandler { private final NacosMaintainerClientHolder clientHolder; public InstanceRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public Page listInstances(String namespaceId, String serviceNameWithoutGroup, String groupName, String clusterName, int page, int pageSize) throws NacosException { List instances = clientHolder.getNamingMaintainerService() .listInstances(namespaceId, groupName, serviceNameWithoutGroup, clusterName, false); return PageUtil.subPage(instances, page, pageSize); } @Override public void updateInstance(InstanceForm instanceForm, Instance instance) throws NacosException { clientHolder.getNamingMaintainerService() .updateInstance(instanceForm.getNamespaceId(), instanceForm.getGroupName(), instanceForm.getServiceName(), instance); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/impl/remote/naming/ServiceRemoteHandler.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.impl.remote.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.Service; import com.alibaba.nacos.api.naming.pojo.maintainer.ClusterInfo; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceDetailInfo; import com.alibaba.nacos.api.naming.pojo.maintainer.SubscriberInfo; import com.alibaba.nacos.console.handler.impl.ConditionFunctionEnabled; import com.alibaba.nacos.console.handler.impl.remote.EnabledRemoteHandler; import com.alibaba.nacos.console.handler.impl.remote.NacosMaintainerClientHolder; import com.alibaba.nacos.console.handler.naming.ServiceHandler; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.model.form.ServiceForm; import org.springframework.context.annotation.Conditional; import java.util.List; /** * Remote Implementation of ServiceHandler that handles service-related operations. * * @author xiweng.yy */ @org.springframework.stereotype.Service @EnabledRemoteHandler @Conditional(ConditionFunctionEnabled.ConditionNamingEnabled.class) public class ServiceRemoteHandler implements ServiceHandler { private final NacosMaintainerClientHolder clientHolder; public ServiceRemoteHandler(NacosMaintainerClientHolder clientHolder) { this.clientHolder = clientHolder; } @Override public void createService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { Service service = buildService(serviceForm, serviceMetadata); clientHolder.getNamingMaintainerService().createService(service); } @Override public void deleteService(String namespaceId, String serviceName, String groupName) throws Exception { clientHolder.getNamingMaintainerService().removeService(namespaceId, groupName, serviceName); } @Override public void updateService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { Service servicePojo = buildService(serviceForm, serviceMetadata); clientHolder.getNamingMaintainerService().updateService(servicePojo); } @Override public List getSelectorTypeList() throws NacosException { return clientHolder.getNamingMaintainerService().listSelectorTypes(); } @Override public Page getSubscribers(int pageNo, int pageSize, String namespaceId, String serviceName, String groupName, boolean aggregation) throws Exception { return clientHolder.getNamingMaintainerService() .getSubscribers(namespaceId, groupName, serviceName, pageNo, pageSize, aggregation); } @Override public Object getServiceList(boolean withInstances, String namespaceId, int pageNo, int pageSize, String serviceName, String groupName, boolean ignoreEmptyService) throws NacosException { if (withInstances) { return clientHolder.getNamingMaintainerService() .listServicesWithDetail(namespaceId, groupName, serviceName, pageNo, pageSize); } return clientHolder.getNamingMaintainerService() .listServices(namespaceId, groupName, serviceName, ignoreEmptyService, pageNo, pageSize); } @Override public ServiceDetailInfo getServiceDetail(String namespaceId, String serviceName, String groupName) throws NacosException { return clientHolder.getNamingMaintainerService().getServiceDetail(namespaceId, groupName, serviceName); } @Override public void updateClusterMetadata(String namespaceId, String groupName, String serviceName, String clusterName, ClusterMetadata clusterMetadata) throws Exception { Service service = new Service(); service.setNamespaceId(namespaceId); service.setGroupName(groupName); service.setName(serviceName); ClusterInfo clusterInfo = new ClusterInfo(); clusterInfo.setClusterName(clusterName); clusterInfo.setHealthChecker(clusterMetadata.getHealthChecker()); clusterInfo.setMetadata(clusterMetadata.getExtendData()); clusterInfo.setUseInstancePortForCheck(clusterMetadata.isUseInstancePortForCheck()); clusterInfo.setHealthyCheckPort(clusterMetadata.getHealthyCheckPort()); clientHolder.getNamingMaintainerService().updateCluster(service, clusterInfo); } private Service buildService(ServiceForm serviceForm, ServiceMetadata metadata) { Service service = new Service(); service.setNamespaceId(serviceForm.getNamespaceId()); service.setName(serviceForm.getServiceName()); service.setGroupName(serviceForm.getGroupName()); service.setProtectThreshold(serviceForm.getProtectThreshold()); service.setEphemeral(serviceForm.getEphemeral()); service.setMetadata(metadata.getExtendData()); service.setSelector(metadata.getSelector()); return service; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/naming/InstanceHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.naming.model.form.InstanceForm; /** * Interface for handling instance-related operations. * * @author zhangyukun */ public interface InstanceHandler { /** * Retrieve a list of instances for a specific service and returns as an ObjectNode. * * @param namespaceId the namespace ID * @param serviceNameWithoutGroup the service name without group * @param groupName the group name * @param clusterName the cluster name * @param page the page number * @param pageSize the size of the page * @return the page object of {@link Instance} * @throws NacosException if the list operation fails */ Page listInstances(String namespaceId, String serviceNameWithoutGroup, String groupName, String clusterName, int page, int pageSize) throws NacosException; /** * Update an instance. * * @param instanceForm the form containing instance data * @param instance the instance to update * @throws NacosException if the update operation fails */ void updateInstance(InstanceForm instanceForm, Instance instance) throws NacosException; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/handler/naming/ServiceHandler.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.handler.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceDetailInfo; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceView; import com.alibaba.nacos.api.naming.pojo.maintainer.SubscriberInfo; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.model.form.ServiceForm; import java.util.List; /** * Interface for handling service-related operations. * * @author zhangyukun */ public interface ServiceHandler { /** * Create a new service. * * @param serviceForm the service form containing the service details * @param serviceMetadata the service metadata created from serviceForm * @throws Exception if an error occurs during service creation */ void createService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception; /** * Delete an existing service. * * @param namespaceId the namespace ID * @param serviceName the service name * @param groupName the group name * @throws Exception if an error occurs during service deletion */ void deleteService(String namespaceId, String serviceName, String groupName) throws Exception; /** * Update an existing service. * * @param serviceForm the service form containing the service details * @param serviceMetadata the service metadata created from serviceForm * @throws Exception if an error occurs during service update */ void updateService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception; /** * Get all selector types. * * @return a list of selector types * @throws NacosException if an error occurs during get selector types */ List getSelectorTypeList() throws NacosException; /** * Get the list of subscribers for a service. * * @param pageNo the page number * @param pageSize the size of the page * @param namespaceId the namespace ID * @param serviceName the service name * @param groupName the group name * @param aggregation whether to aggregate the results * @return a JSON node containing the list of subscribers * @throws Exception if an error occurs during fetching subscribers */ Page getSubscribers(int pageNo, int pageSize, String namespaceId, String serviceName, String groupName, boolean aggregation) throws Exception; /** * List service detail information. * * @param withInstances whether to include instances * @param namespaceId the namespace ID * @param pageNo the page number * @param pageSize the size of the page * @param serviceName the service name * @param groupName the group name * @param ignoreEmptyService whether to filter services with empty instances * @return if withInstances is {@code true}, return Page of {@link ServiceDetailInfo}, otherwise return Page of {@link ServiceView} * @throws NacosException if an error occurs during fetching service details */ Object getServiceList(boolean withInstances, String namespaceId, int pageNo, int pageSize, String serviceName, String groupName, boolean ignoreEmptyService) throws NacosException; /** * Get the detail of a specific service. * * @param namespaceId the namespace ID * @param serviceName the service name without group * @param groupName the group name * @return service detail information * @throws NacosException if an error occurs during fetching service details */ ServiceDetailInfo getServiceDetail(String namespaceId, String serviceName, String groupName) throws NacosException; /** * Update the metadata of a cluster. * * @param namespaceId the namespace ID * @param groupName the group name * @param serviceName the service name * @param clusterName the cluster name * @param clusterMetadata the metadata for the cluster * @throws Exception if the update operation fails */ void updateClusterMetadata(String namespaceId, String groupName, String serviceName, String clusterName, ClusterMetadata clusterMetadata) throws Exception; } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/paramcheck/ConsoleDefaultHttpParamExtractor.java ================================================ /* * Copyright 1999-2023 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.paramcheck; import com.alibaba.nacos.common.paramcheck.ParamInfo; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor; import jakarta.servlet.http.HttpServletRequest; import java.util.ArrayList; import java.util.List; /** * Console default http param extractor. * * @author zhuoguang */ public class ConsoleDefaultHttpParamExtractor extends AbstractHttpParamExtractor { @Override public List extractParam(HttpServletRequest request) { ParamInfo paramInfo = new ParamInfo(); paramInfo.setNamespaceId(getAliasNamespaceId(request)); paramInfo.setNamespaceShowName(getAliasNamespaceShowName(request)); ArrayList paramInfos = new ArrayList<>(); paramInfos.add(paramInfo); return paramInfos; } private String getAliasNamespaceId(HttpServletRequest request) { String namespaceId = request.getParameter("namespaceId"); if (StringUtils.isBlank(namespaceId)) { namespaceId = request.getParameter("customNamespaceId"); } return namespaceId; } private String getAliasNamespaceShowName(HttpServletRequest request) { return request.getParameter("namespaceName"); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/HealthProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.console.handler.HealthHandler; import org.springframework.stereotype.Service; /** * Proxy class for handling health check operations. * * @author zhangyukun */ @Service public class HealthProxy { private final HealthHandler healthHandler; public HealthProxy(HealthHandler healthHandler) { this.healthHandler = healthHandler; } /** * Perform readiness check to determine if Nacos is ready to handle requests. * * @return readiness result */ public Result checkReadiness() throws NacosException { return healthHandler.checkReadiness(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/ServerStateProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.console.handler.ServerStateHandler; import org.springframework.stereotype.Service; import java.util.Map; /** * Proxy class for handling server state operations. * * @author zhangyukun */ @Service public class ServerStateProxy { private final ServerStateHandler serverStateHandler; public ServerStateProxy(ServerStateHandler serverStateHandler) { this.serverStateHandler = serverStateHandler; } /** * Get the current state of the server. * * @return the server state as a Map */ public Map getServerState() throws NacosException { return serverStateHandler.getServerState(); } /** * Get the announcement content based on the language. * * @param language the language for the announcement * @return the announcement content as a String */ public String getAnnouncement(String language) { return serverStateHandler.getAnnouncement(language); } /** * Get the console UI guide information. * * @return the console UI guide information as a String */ public String getConsoleUiGuide() { return serverStateHandler.getConsoleUiGuide(); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/ai/A2aProxy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.ai; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentCardUpdateForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentForm; import com.alibaba.nacos.ai.form.a2a.admin.AgentListForm; import com.alibaba.nacos.api.ai.model.a2a.AgentCard; import com.alibaba.nacos.api.ai.model.a2a.AgentCardDetailInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentCardVersionInfo; import com.alibaba.nacos.api.ai.model.a2a.AgentVersionDetail; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.A2aHandler; import com.alibaba.nacos.core.model.form.PageForm; import org.springframework.stereotype.Component; import java.util.List; /** * A2a proxy. * * @author KiteSoar */ @Component public class A2aProxy { private final A2aHandler a2aHandler; public A2aProxy(A2aHandler a2aHandler) { this.a2aHandler = a2aHandler; } /** * Register agent card. * * @param agentCard agent card to register * @param agentCardForm agent card form * @throws NacosException exception when register agent card */ public void registerAgent(AgentCard agentCard, AgentCardForm agentCardForm) throws NacosException { a2aHandler.registerAgent(agentCard, agentCardForm); } public AgentCardDetailInfo getAgentCard(AgentForm form) throws NacosException { return a2aHandler.getAgentCardWithVersions(form); } public void deleteAgent(AgentForm form) throws NacosException { a2aHandler.deleteAgent(form); } public void updateAgentCard(AgentCard agentCard, AgentCardUpdateForm form) throws NacosException { a2aHandler.updateAgentCard(agentCard, form); } public Page listAgents(AgentListForm agentListForm, PageForm pageForm) throws NacosException { return a2aHandler.listAgents(agentListForm, pageForm); } public List listAgentVersions(String namespaceId, String name) throws NacosException { return a2aHandler.listAgentVersions(namespaceId, name); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/ai/McpProxy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.ai; import com.alibaba.nacos.api.ai.model.mcp.McpEndpointSpec; import com.alibaba.nacos.api.ai.model.mcp.McpServerBasicInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerDetailInfo; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportRequest; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportResponse; import com.alibaba.nacos.api.ai.model.mcp.McpServerImportValidationResult; import com.alibaba.nacos.api.ai.model.mcp.McpToolSpecification; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.McpHandler; import org.springframework.stereotype.Service; /** * Proxy class for handling AI MCP operations. * * @author xiweng.yy */ @Service public class McpProxy { private final McpHandler mcpHandler; public McpProxy(McpHandler mcpHandler) { this.mcpHandler = mcpHandler; } /** * List mcp server. * * @param namespaceId namespace id of mcp servers * @param mcpName mcp name pattern, if null or empty, filter all mcp servers. * @param search search type `blur` or `accurate`, means whether to search by fuzzy or exact match by * `mcpName`. * @param pageNo page number, start from 1 * @param pageSize page size each page * @return list of {@link McpServerBasicInfo} matched input parameters. */ public Page listMcpServers(String namespaceId, String mcpName, String search, int pageNo, int pageSize) throws NacosException { return mcpHandler.listMcpServers(namespaceId, mcpName, search, pageNo, pageSize); } /** * Get specified mcp server detail info. * * @param namespaceId namespace id of mcp server * @param mcpName name of mcp server * @return detail info with {@link McpServerDetailInfo} * @throws NacosException any exception during handling */ public McpServerDetailInfo getMcpServer(String namespaceId, String mcpName, String mcpId, String version) throws NacosException { return mcpHandler.getMcpServer(namespaceId, mcpName, mcpId, version); } /** * Create new mcp server. * * @param namespaceId namespace id of mcp server * @param serverSpecification mcp server specification, see {@link McpServerBasicInfo} * @param toolSpecification mcp server included tools, see {@link McpToolSpecification}, optional * @param endpointSpecification mcp server endpoint specification, see {@link McpEndpointSpec}, optional * @return mcp server id of the new mcp server * @throws NacosException any exception during handling */ public String createMcpServer(String namespaceId, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification) throws NacosException { return mcpHandler.createMcpServer(namespaceId, serverSpecification, toolSpecification, endpointSpecification); } /** * Update existed mcp server. * *

    * `namespaceId` and `mcpName` can't be changed. *

    * * @param namespaceId namespace id of mcp server, used to mark which mcp server to update * @param isPublish if publish the mcp server or just save the mcp * @param serverSpecification mcp server specification, see {@link McpServerBasicInfo} * @param toolSpecification mcp server included tools, see {@link McpToolSpecification}, optional * @param endpointSpecification mcp server endpoint specification, see {@link McpEndpointSpec}, optional * @param overrideExisting if replace all the instances when update the mcp server * @throws NacosException any exception during handling */ public void updateMcpServer(String namespaceId, boolean isPublish, McpServerBasicInfo serverSpecification, McpToolSpecification toolSpecification, McpEndpointSpec endpointSpecification, boolean overrideExisting) throws NacosException { mcpHandler.updateMcpServer(namespaceId, isPublish, serverSpecification, toolSpecification, endpointSpecification, overrideExisting); } /** * Delete existed mcp server. * * @param namespaceId namespace id of mcp server * @param mcpServerId name of mcp server * @param version version of the mcp server * @throws NacosException any exception during handling */ public void deleteMcpServer(String namespaceId, String mcpName, String mcpServerId, String version) throws NacosException { mcpHandler.deleteMcpServer(namespaceId, mcpName, mcpServerId, version); } /** * Validate MCP server import request. * * @param namespaceId namespace id for mcp servers * @param request import request containing data and settings * @return validation result with details about potential issues * @throws NacosException any exception during validation */ public McpServerImportValidationResult validateImport(String namespaceId, McpServerImportRequest request) throws NacosException { return mcpHandler.validateImport(namespaceId, request); } /** * Execute MCP server import operation. * * @param namespaceId namespace id for mcp servers * @param request import request containing data and settings * @return import response with results and statistics * @throws NacosException any exception during import execution */ public McpServerImportResponse executeImport(String namespaceId, McpServerImportRequest request) throws NacosException { return mcpHandler.executeImport(namespaceId, request); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/ai/PromptProxy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.ai; import com.alibaba.nacos.ai.form.prompt.PromptForm; import com.alibaba.nacos.ai.form.prompt.PromptHistoryForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelBindForm; import com.alibaba.nacos.ai.form.prompt.PromptLabelForm; import com.alibaba.nacos.ai.form.prompt.PromptListForm; import com.alibaba.nacos.ai.form.prompt.PromptMetadataForm; import com.alibaba.nacos.ai.form.prompt.PromptPublishForm; import com.alibaba.nacos.ai.form.prompt.PromptQueryForm; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptMetaSummary; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionInfo; import com.alibaba.nacos.api.ai.model.prompt.PromptVersionSummary; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.ai.PromptHandler; import org.springframework.stereotype.Component; /** * Prompt proxy for console. * * @author nacos */ @Component public class PromptProxy { private final PromptHandler promptHandler; public PromptProxy(PromptHandler promptHandler) { this.promptHandler = promptHandler; } /** * Publish a new version of prompt. * * @param form prompt publish form * @param srcUser source user * @param srcIp source IP * @return true if publish success * @throws NacosException if publish fails */ public boolean publishPrompt(PromptPublishForm form, String srcUser, String srcIp) throws NacosException { return promptHandler.publishPrompt(form, srcUser, srcIp); } public PromptMetaInfo getPromptMeta(PromptForm form) throws NacosException { return promptHandler.getPromptMeta(form); } public PromptVersionInfo queryPromptDetail(PromptQueryForm form) throws NacosException { return promptHandler.queryPromptDetail(form); } public boolean bindLabel(PromptLabelBindForm form, String srcUser, String srcIp) throws NacosException { return promptHandler.bindLabel(form, srcUser, srcIp); } public boolean unbindLabel(PromptLabelForm form, String srcUser, String srcIp) throws NacosException { return promptHandler.unbindLabel(form, srcUser, srcIp); } /** * Delete prompt. * * @param form prompt form * @param srcUser source user * @param srcIp source IP * @return true if delete success * @throws NacosException if delete fails */ public boolean deletePrompt(PromptForm form, String srcUser, String srcIp) throws NacosException { return promptHandler.deletePrompt(form, srcUser, srcIp); } /** * List prompts with pagination. * * @param form prompt list form * @return prompt list page * @throws NacosException if list fails */ public Page listPrompts(PromptListForm form) throws NacosException { return promptHandler.listPrompts(form); } public Page listPromptVersions(PromptHistoryForm form) throws NacosException { return promptHandler.listPromptVersions(form); } /** * Update prompt metadata (description only). * * @param form prompt metadata form * @param srcUser source user * @param srcIp source IP * @return true if update success * @throws NacosException if update fails */ public boolean updatePromptMetadata(PromptMetadataForm form, String srcUser, String srcIp) throws NacosException { return promptHandler.updatePromptMetadata(form, srcUser, srcIp); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/ai/SkillProxy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.ai; import com.alibaba.nacos.ai.form.skills.admin.SkillDetailForm; import com.alibaba.nacos.ai.form.skills.admin.SkillForm; import com.alibaba.nacos.ai.form.skills.admin.SkillListForm; import com.alibaba.nacos.ai.form.skills.admin.SkillUpdateForm; import com.alibaba.nacos.console.handler.ai.SkillHandler; import com.alibaba.nacos.api.ai.model.skills.Skill; import com.alibaba.nacos.api.ai.model.skills.SkillBasicInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.core.model.form.PageForm; import org.springframework.stereotype.Component; /** * Skill proxy. * * @author nacos */ @Component public class SkillProxy { private final SkillHandler skillHandler; public SkillProxy(SkillHandler skillHandler) { this.skillHandler = skillHandler; } /** * Register skill. * * @param skill skill to register * @param form skill detail form * @throws NacosException exception when register skill */ public void registerSkill(Skill skill, SkillDetailForm form) throws NacosException { skillHandler.registerSkill(skill, form); } public Skill getSkill(SkillForm form) throws NacosException { return skillHandler.getSkill(form); } public void deleteSkill(SkillForm form) throws NacosException { skillHandler.deleteSkill(form); } public void updateSkill(Skill skill, SkillUpdateForm form) throws NacosException { skillHandler.updateSkill(skill, form); } public Page listSkills(SkillListForm skillListForm, PageForm pageForm) throws NacosException { return skillHandler.listSkills(skillListForm, pageForm); } public String uploadSkillFromZip(String namespaceId, byte[] zipBytes) throws NacosException { return skillHandler.uploadSkillFromZip(namespaceId, zipBytes); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/config/ConfigProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigDetailInfo; import com.alibaba.nacos.api.config.model.ConfigGrayInfo; import com.alibaba.nacos.api.config.model.ConfigListenerInfo; import com.alibaba.nacos.api.config.model.SameConfigPolicy; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.model.v2.Result; import com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean; import com.alibaba.nacos.config.server.model.ConfigRequestInfo; import com.alibaba.nacos.config.server.model.form.ConfigForm; import com.alibaba.nacos.console.handler.config.ConfigHandler; import jakarta.servlet.ServletException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.IOException; import java.util.List; import java.util.Map; /** * Proxy class for handling configuration operations. * * @author zhangyukun */ @Service public class ConfigProxy { private final ConfigHandler configHandler; @Autowired public ConfigProxy(ConfigHandler configHandler) { this.configHandler = configHandler; } /** * Get configure information list. */ public Page getConfigList(int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws IOException, ServletException, NacosException { return configHandler.getConfigList(pageNo, pageSize, dataId, group, namespaceId, configAdvanceInfo); } /** * Get the specific configuration information. */ public ConfigDetailInfo getConfigDetail(String dataId, String group, String namespaceId) throws NacosException { return configHandler.getConfigDetail(dataId, group, namespaceId); } /** * Add or update configuration. */ public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo) throws NacosException { return configHandler.publishConfig(configForm, configRequestInfo); } /** * Delete configuration. */ public Boolean deleteConfig(String dataId, String group, String namespaceId, String tag, String clientIp, String srcUser) throws NacosException { return configHandler.deleteConfig(dataId, group, namespaceId, tag, clientIp, srcUser); } /** * Batch delete configurations. */ public Boolean batchDeleteConfigs(List ids, String clientIp, String srcUser) throws NacosException { return configHandler.batchDeleteConfigs(ids, clientIp, srcUser); } /** * Search config list by config detail. */ public Page getConfigListByContent(String search, int pageNo, int pageSize, String dataId, String group, String namespaceId, Map configAdvanceInfo) throws NacosException { return configHandler.getConfigListByContent(search, pageNo, pageSize, dataId, group, namespaceId, configAdvanceInfo); } /** * Subscribe to configured client information. */ public ConfigListenerInfo getListeners(String dataId, String group, String namespaceId, boolean aggregation) throws Exception { return configHandler.getListeners(dataId, group, namespaceId, aggregation); } /** * Get subscription information based on IP, tenant, and other parameters. */ public ConfigListenerInfo getAllSubClientConfigByIp(String ip, boolean all, String namespaceId, boolean aggregation) throws NacosException { return configHandler.getAllSubClientConfigByIp(ip, all, namespaceId, aggregation); } /** * New version export config adds metadata.yml file to record config metadata. */ public ResponseEntity exportConfigV2(String dataId, String group, String namespaceId, String appName, List ids) throws Exception { return configHandler.exportConfig(dataId, group, namespaceId, appName, ids); } /** * Imports and publishes a configuration from a file. */ public Result> importAndPublishConfig(String srcUser, String namespaceId, SameConfigPolicy policy, MultipartFile file, String srcIp, String requestIpApp) throws NacosException { return configHandler.importAndPublishConfig(srcUser, namespaceId, policy, file, srcIp, requestIpApp); } /** * Clone configuration. */ public Result> cloneConfig(String srcUser, String namespaceId, List configBeansList, SameConfigPolicy policy, String srcIp, String requestIpApp) throws NacosException { return configHandler.cloneConfig(srcUser, namespaceId, configBeansList, policy, srcIp, requestIpApp); } /** * Remove beta configuration based on dataId, group, and namespaceId. */ public boolean removeBetaConfig(String dataId, String group, String namespaceId, String remoteIp, String requestIpApp, String srcUser) throws NacosException { return configHandler.removeBetaConfig(dataId, group, namespaceId, remoteIp, requestIpApp, srcUser); } /** * Query beta configuration based on dataId, group, and namespaceId. */ public ConfigGrayInfo queryBetaConfig(String dataId, String group, String namespaceId) throws NacosException { return configHandler.queryBetaConfig(dataId, group, namespaceId); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/config/HistoryProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.config; import com.alibaba.nacos.api.config.model.ConfigBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryBasicInfo; import com.alibaba.nacos.api.config.model.ConfigHistoryDetailInfo; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.console.handler.config.HistoryHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * . * * @author zhangyukun on:2024/8/16 */ @Service public class HistoryProxy { private final HistoryHandler historyHandler; /** * Constructs a new HistoryProxy with the given HistoryInnerHandler and ConsoleConfig. * * @param historyHandler the default implementation of HistoryHandler */ @Autowired public HistoryProxy(HistoryHandler historyHandler) { this.historyHandler = historyHandler; } /** * Query the detailed configuration history information. * * @param dataId the ID of the data * @param group the group ID * @param namespaceId the namespace ID * @param nid the history record ID * @return the detailed configuration history information * @throws NacosException if any error occurs during the operation */ public ConfigHistoryDetailInfo getConfigHistoryInfo(String dataId, String group, String namespaceId, Long nid) throws NacosException { return historyHandler.getConfigHistoryInfo(dataId, group, namespaceId, nid); } /** * Query the list of configuration history. * * @param dataId the ID of the data * @param group the group ID * @param namespaceId the namespace ID * @param pageNo the page number * @param pageSize the number of items per page * @return the paginated list of configuration history * @throws NacosException if any error occurs during the operation */ public Page listConfigHistory(String dataId, String group, String namespaceId, Integer pageNo, Integer pageSize) throws NacosException { return historyHandler.listConfigHistory(dataId, group, namespaceId, pageNo, pageSize); } /** * Query the previous configuration history information. * * @param dataId the ID of the data * @param group the group ID * @param namespaceId the namespace ID * @param id the configuration ID * @return the previous configuration history information * @throws NacosException if any error occurs during the operation */ public ConfigHistoryDetailInfo getPreviousConfigHistoryInfo(String dataId, String group, String namespaceId, Long id) throws NacosException { return historyHandler.getPreviousConfigHistoryInfo(dataId, group, namespaceId, id); } /** * Query the list of configurations by namespace. * * @param namespaceId the namespace ID * @return the list of configurations * @throws NacosException if any error occurs during the operation */ public List getConfigsByTenant(String namespaceId) throws NacosException { return historyHandler.getConfigsByTenant(namespaceId); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/core/ClusterProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.response.NacosMember; import com.alibaba.nacos.common.utils.StringUtils; import com.alibaba.nacos.console.handler.core.ClusterHandler; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; /** * Proxy class for handling cluster-related operations. * * @author zhangyukun */ @Service public class ClusterProxy { private final ClusterHandler clusterHandler; /** * Constructs a new ClusterProxy with the given ClusterInnerHandler and ConsoleConfig. * * @param clusterHandler the default implementation of ClusterHandler */ public ClusterProxy(ClusterHandler clusterHandler) { this.clusterHandler = clusterHandler; } /** * Retrieve a list of cluster members with an optional search keyword. * * @param ipKeyWord the search keyword for filtering members * @return a collection of matching members * @throws IllegalArgumentException if the deployment type is invalid */ public Collection getNodeList(String ipKeyWord) throws NacosException { Collection members = clusterHandler.getNodeList(ipKeyWord); List result = new ArrayList<>(); members.forEach(member -> { if (StringUtils.isBlank(ipKeyWord)) { result.add(member); return; } final String address = member.getAddress(); if (StringUtils.equals(address, ipKeyWord) || StringUtils.startsWith(address, ipKeyWord)) { result.add(member); } }); result.sort(Comparator.comparing(NacosMember::getAddress)); return result; } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/core/NamespaceProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.console.handler.core.NamespaceHandler; import com.alibaba.nacos.api.model.response.Namespace; import com.alibaba.nacos.core.namespace.model.form.NamespaceForm; import org.springframework.stereotype.Service; import java.util.List; /** * Proxy class for handling namespace operations. * * @author zhangyukun */ @Service public class NamespaceProxy { private final NamespaceHandler namespaceHandler; public NamespaceProxy(NamespaceHandler namespaceHandler) { this.namespaceHandler = namespaceHandler; } /** * Get namespace list. */ public List getNamespaceList() throws NacosException { return namespaceHandler.getNamespaceList(); } /** * Get the specific namespace information. */ public Namespace getNamespaceDetail(String namespaceId) throws NacosException { return namespaceHandler.getNamespaceDetail(namespaceId); } /** * Create or update namespace. */ public Boolean createNamespace(String namespaceId, String namespaceName, String namespaceDesc) throws NacosException { return namespaceHandler.createNamespace(namespaceId, namespaceName, namespaceDesc); } /** * Edit namespace. */ public Boolean updateNamespace(NamespaceForm namespaceForm) throws NacosException { return namespaceHandler.updateNamespace(namespaceForm); } /** * Delete namespace. */ public Boolean deleteNamespace(String namespaceId) throws NacosException { return namespaceHandler.deleteNamespace(namespaceId); } /** * Check if namespace exists. */ public Boolean checkNamespaceIdExist(String namespaceId) throws NacosException { return namespaceHandler.checkNamespaceIdExist(namespaceId); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/core/PluginProxy.java ================================================ /* * Copyright 1999-2025 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.core; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.console.handler.core.PluginHandler; import com.alibaba.nacos.core.plugin.model.vo.PluginDetailVO; import com.alibaba.nacos.core.plugin.model.vo.PluginInfoVO; import org.springframework.stereotype.Service; import java.util.List; import java.util.Map; /** * Proxy class for handling plugin operations. * * @author WangzJi */ @Service public class PluginProxy { private final PluginHandler pluginHandler; public PluginProxy(PluginHandler pluginHandler) { this.pluginHandler = pluginHandler; } /** * Get a list of plugins. * * @param pluginType optional plugin type filter * @return list of plugin info VOs * @throws NacosException if there is an issue fetching the plugins */ public List listPlugins(String pluginType) throws NacosException { return pluginHandler.listPlugins(pluginType); } /** * Get plugin detail. * * @param pluginType plugin type * @param pluginName plugin name * @return plugin detail VO * @throws NacosException if there is an issue fetching the plugin detail */ public PluginDetailVO getPluginDetail(String pluginType, String pluginName) throws NacosException { return pluginHandler.getPluginDetail(pluginType, pluginName); } /** * Update plugin enabled/disabled status. * * @param pluginType plugin type * @param pluginName plugin name * @param enabled whether to enable * @param localOnly whether only apply to local node * @throws NacosException if there is an issue updating the plugin status */ public void updatePluginStatus(String pluginType, String pluginName, boolean enabled, boolean localOnly) throws NacosException { pluginHandler.updatePluginStatus(pluginType, pluginName, enabled, localOnly); } /** * Update plugin configuration. * * @param pluginType plugin type * @param pluginName plugin name * @param config configuration map * @param localOnly whether only apply to local node * @throws NacosException if there is an issue updating the plugin config */ public void updatePluginConfig(String pluginType, String pluginName, Map config, boolean localOnly) throws NacosException { pluginHandler.updatePluginConfig(pluginType, pluginName, config, localOnly); } /** * Get plugin availability across cluster nodes. * * @param pluginType plugin type * @param pluginName plugin name * @return node availability map * @throws NacosException if there is an issue fetching the availability */ public Map getPluginAvailability(String pluginType, String pluginName) throws NacosException { return pluginHandler.getPluginAvailability(pluginType, pluginName); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/naming/InstanceProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.Instance; import com.alibaba.nacos.console.handler.naming.InstanceHandler; import com.alibaba.nacos.naming.model.form.InstanceForm; import org.springframework.stereotype.Service; /** * Proxy class for handling instance-related operations. * * @author zhangyukun */ @Service public class InstanceProxy { private final InstanceHandler instanceHandler; /** * Constructs a new InstanceProxy with the given InstanceInnerHandler and ConsoleConfig. * * @param instanceHandler the default implementation of InstanceHandler */ public InstanceProxy(InstanceHandler instanceHandler) { this.instanceHandler = instanceHandler; } /** * Retrieve a list of instances for a specific service and returns as an ObjectNode. * * @param namespaceId the namespace ID * @param serviceNameWithoutGroup the service name without group * @param groupName the group name * @param clusterName the cluster name * @param page the page number * @param pageSize the size of the page * @return the page object of {@link Instance} * @throws IllegalArgumentException if the deployment type is invalid * @throws NacosException if the list operation fails */ public Page listInstances(String namespaceId, String serviceNameWithoutGroup, String groupName, String clusterName, int page, int pageSize) throws NacosException { return instanceHandler.listInstances(namespaceId, serviceNameWithoutGroup, groupName, clusterName, page, pageSize); } /** * Updates an instance. * * @param instanceForm the form containing instance data * @param instance the instance to update * @throws NacosException if the update operation fails * @throws IllegalArgumentException if the deployment type is invalid */ public void updateInstance(InstanceForm instanceForm, Instance instance) throws NacosException { instanceHandler.updateInstance(instanceForm, instance); } } ================================================ FILE: console/src/main/java/com/alibaba/nacos/console/proxy/naming/ServiceProxy.java ================================================ /* * Copyright 1999-2024 Alibaba Group Holding Ltd. * * 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 com.alibaba.nacos.console.proxy.naming; import com.alibaba.nacos.api.exception.NacosException; import com.alibaba.nacos.api.model.Page; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceDetailInfo; import com.alibaba.nacos.api.naming.pojo.maintainer.ServiceView; import com.alibaba.nacos.api.naming.pojo.maintainer.SubscriberInfo; import com.alibaba.nacos.console.handler.naming.ServiceHandler; import com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata; import com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata; import com.alibaba.nacos.naming.model.form.ServiceForm; import org.springframework.stereotype.Service; import java.util.List; /** * Proxy class for handling service-related operations. * * @author zhangyukun */ @Service public class ServiceProxy { private final ServiceHandler serviceHandler; /** * Constructs a new ServiceProxy with the given ServiceInnerHandler and ConsoleConfig. The handler is mapped to a * deployment type key. * * @param serviceHandler the default implementation of ServiceHandler */ public ServiceProxy(ServiceHandler serviceHandler) { this.serviceHandler = serviceHandler; } /** * Creates a new service by delegating the operation to the appropriate handler. * * @param serviceForm the service form containing the service details * @throws Exception if an error occurs during service creation */ public void createService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { serviceHandler.createService(serviceForm, serviceMetadata); } /** * Deletes an existing service by delegating the operation to the appropriate handler. * * @param namespaceId the namespace ID * @param serviceName the service name * @param groupName the group name * @throws Exception if an error occurs during service deletion */ public void deleteService(String namespaceId, String serviceName, String groupName) throws Exception { serviceHandler.deleteService(namespaceId, serviceName, groupName); } /** * Updates an existing service by delegating the operation to the appropriate handler. * * @param serviceForm the service form containing the service details * @param serviceMetadata the service metadata created from serviceForm * @throws Exception if an error occurs during service update */ public void updateService(ServiceForm serviceForm, ServiceMetadata serviceMetadata) throws Exception { serviceHandler.updateService(serviceForm, serviceMetadata); } /** * Retrieves all selector types by delegating the operation to the appropriate handler. * * @return a list of selector types */ public List getSelectorTypeList() throws NacosException { return serviceHandler.getSelectorTypeList(); } /** * Retrieves the list of subscribers for a service by delegating the operation to the appropriate handler. * * @param pageNo the page number * @param pageSize the size of the page * @param namespaceId the namespace ID * @param serviceName the service name * @param groupName the group name * @param aggregation whether to aggregate the results * @return a JSON node containing the list of subscribers * @throws Exception if an error occurs during fetching subscribers */ public Page getSubscribers(int pageNo, int pageSize, String namespaceId, String serviceName, String groupName, boolean aggregation) throws Exception { return serviceHandler.getSubscribers(pageNo, pageSize, namespaceId, serviceName, groupName, aggregation); } /** * Retrieves the list of services and their details by delegating the operation to the appropriate handler. * * @param withInstances whether to include instances * @param namespaceId the namespace ID * @param pageNo the page number * @param pageSize the size of the page * @param serviceName the service name * @param groupName the group name * @param hasIpCount whether to filter services with empty instances * @return if withInstances is {@code true}, return List of {@link ServiceDetailInfo}, otherwise return List of {@link ServiceView} * @throws NacosException if an error occurs during fetching service details */ public Object getServiceList(boolean withInstances, String namespaceId, int pageNo, int pageSize, String serviceName, String groupName, boolean hasIpCount) throws NacosException { return serviceHandler.getServiceList(withInstances, namespaceId, pageNo, pageSize, serviceName, groupName, hasIpCount); } /** * Retrieves the details of a specific service by delegating the operation to the appropriate handler. * * @param namespaceId the namespace ID * @param serviceName the service name without group * @param groupName the group name * @return service detail information * @throws NacosException if an error occurs during fetching service details */ public ServiceDetailInfo getServiceDetail(String namespaceId, String serviceName, String groupName) throws NacosException { return serviceHandler.getServiceDetail(namespaceId, serviceName, groupName); } /** * Updates the metadata of a cluster. * * @param namespaceId the namespace ID * @param groupName the group name * @param serviceName the service name * @param clusterName the cluster name * @param clusterMetadata the metadata for the cluster * @throws Exception if the update operation fails * @throws IllegalArgumentException if the deployment type is invalid */ public void updateClusterMetadata(String namespaceId, String groupName, String serviceName, String clusterName, ClusterMetadata clusterMetadata) throws Exception { serviceHandler.updateClusterMetadata(namespaceId, groupName, serviceName, clusterName, clusterMetadata); } } ================================================ FILE: console/src/main/resources/META-INF/nacos-default.properties ================================================ # # Copyright 1999-2018 Alibaba Group Holding Ltd. # # 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. # # Console Default Properties spring.mvc.view.prefix=/jsp/ # the default suffix of page spring.mvc.view.suffix=.jsp spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration #logging.level.root=DEBUG # P0 key,For Debug. whether use address-server; true:use; false:not use;default:true useAddressServer=true # whether open interInterFaceFilter; true:open; false:close; if open, others can't call inner interface. default:false openInnerInterfaceFilter=false # quickStart stip dumpAll;only dump change config isQuickStart=false # server notify each otherd notifyConnectTimeout=200 # server notify each other notifySocketTimeout=8000 # whether health check isHealthCheck=true # health check max fail count maxHealthCheckFailCount=12 # whether open spas; true:open; false:close OPEN_SPAS=true nacos.cmdb.dumpTaskInterval=3600 nacos.cmdb.eventTaskInterval=10 nacos.cmdb.labelTaskInterval=300 nacos.cmdb.loadDataAtStart=false #management.endpoints.web.exposure.include=* #spring.security.enabled=false #management.security=false #security.basic.enabled=false #nacos.security.ignore.urls=/** nacos.security.ignore.urls=/,/**/*.css,/**/*.js,/**/*.html,/**/*.map,/**/*.svg,/**/*.png,/**/*.ico,/console-ui/public/**,/v1/auth/login,/v1/console/health,/v1/cs/**,/v1/ns/**,/v1/cmdb/**,/actuator/** management.metrics.export.elastic.enabled=false #management.metrics.export.elastic.host=http://localhost:9200 # metrics for influx management.metrics.export.influx.enabled=false #management.metrics.export.influx.db=springboot #management.metrics.export.influx.uri=http://localhost:8086 #management.metrics.export.influx.auto-create-db=true #management.metrics.export.influx.consistency=one #management.metrics.export.influx.compressed=true server.tomcat.accesslog.enabled=true server.tomcat.accesslog.pattern=%h %l %u %t "%r" %s %b %D # default current work dir server.tomcat.basedir=file:. ================================================ FILE: console/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-console/jni-config.json ================================================ [ { "name":"[Lcom.sun.management.internal.DiagnosticCommandArgumentInfo;" }, { "name":"[Lcom.sun.management.internal.DiagnosticCommandInfo;" }, { "name":"com.alibaba.nacos.Nacos", "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] }, { "name":"com.sun.management.internal.DiagnosticCommandArgumentInfo", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","boolean","boolean","boolean","int"] }] }, { "name":"com.sun.management.internal.DiagnosticCommandInfo", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","boolean","java.util.List"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.ChannelException" }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultFileRegion", "fields":[{"name":"file"}, {"name":"transferred"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.LinuxSocket" }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.Native" }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.NativeDatagramPacketArray$NativeDatagramPacket", "fields":[{"name":"count"}, {"name":"memoryAddress"}, {"name":"recipientAddr"}, {"name":"recipientAddrLen"}, {"name":"recipientPort"}, {"name":"recipientScopeId"}, {"name":"segmentSize"}, {"name":"senderAddr"}, {"name":"senderAddrLen"}, {"name":"senderPort"}, {"name":"senderScopeId"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.NativeStaticallyReferencedJniMethods" }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.Buffer" }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.DatagramSocketAddress", "methods":[{"name":"","parameterTypes":["byte[]","int","int","int","io.grpc.netty.shaded.io.netty.channel.unix.DatagramSocketAddress"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.DomainDatagramSocketAddress", "methods":[{"name":"","parameterTypes":["byte[]","int","io.grpc.netty.shaded.io.netty.channel.unix.DomainDatagramSocketAddress"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods" }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.FileDescriptor" }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.LimitsStaticallyReferencedJniMethods" }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.PeerCredentials", "methods":[{"name":"","parameterTypes":["int","int","int[]"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.Socket" }, { "name":"java.io.FileDescriptor", "fields":[{"name":"fd"}] }, { "name":"java.io.IOException" }, { "name":"java.lang.Boolean", "methods":[{"name":"getBoolean","parameterTypes":["java.lang.String"] }] }, { "name":"java.lang.InternalError", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, { "name":"java.lang.OutOfMemoryError" }, { "name":"java.lang.RuntimeException" }, { "name":"java.lang.String", "methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }] }, { "name":"java.lang.System", "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"java.net.InetSocketAddress", "methods":[{"name":"","parameterTypes":["java.lang.String","int"] }] }, { "name":"java.net.PortUnreachableException" }, { "name":"java.nio.Buffer", "fields":[{"name":"limit"}, {"name":"position"}], "methods":[{"name":"limit","parameterTypes":[] }, {"name":"position","parameterTypes":[] }] }, { "name":"java.nio.DirectByteBuffer" }, { "name":"java.nio.channels.ClosedChannelException", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.util.Arrays", "methods":[{"name":"asList","parameterTypes":["java.lang.Object[]"] }] }, { "name":"org.springframework.boot.SpringApplicationAotProcessor", "methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }] }, { "name":"sun.instrument.InstrumentationImpl", "methods":[{"name":"","parameterTypes":["long","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }] }, { "name":"sun.management.VMManagementImpl", "fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}] }, { "name":"sun.nio.ch.FileChannelImpl", "fields":[{"name":"fd"}] } ] ================================================ FILE: console/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-console/predefined-classes-config.json ================================================ [ { "type":"agent-extracted", "classes":[ ] } ] ================================================ FILE: console/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-console/proxy-config.json ================================================ [ { "interfaces":["java.lang.reflect.GenericArrayType","org.springframework.core.SerializableTypeWrapper$SerializableTypeProxy","java.io.Serializable"] }, { "interfaces":["java.lang.reflect.ParameterizedType","org.springframework.core.SerializableTypeWrapper$SerializableTypeProxy","java.io.Serializable"] }, { "interfaces":["java.lang.reflect.TypeVariable","org.springframework.core.SerializableTypeWrapper$SerializableTypeProxy","java.io.Serializable"] }, { "interfaces":["java.lang.reflect.WildcardType","org.springframework.core.SerializableTypeWrapper$SerializableTypeProxy","java.io.Serializable"] }, { "interfaces":["java.sql.Connection"] }, { "interfaces":["org.springframework.aot.hint.annotation.RegisterReflectionForBinding"] }, { "interfaces":["org.springframework.boot.actuate.endpoint.annotation.Endpoint"] }, { "interfaces":["org.springframework.boot.actuate.endpoint.annotation.EndpointExtension"] }, { "interfaces":["org.springframework.boot.context.properties.ConfigurationProperties"] }, { "interfaces":["org.springframework.jdbc.datasource.ConnectionProxy"] }, { "interfaces":["org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity"] }, { "interfaces":["org.springframework.web.bind.annotation.ControllerAdvice"] }, { "interfaces":["org.springframework.web.bind.annotation.RequestMapping"] }, { "interfaces":["org.springframework.web.bind.annotation.RequestParam"] } ] ================================================ FILE: console/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-console/reflect-config.json ================================================ [ { "name":"[B" }, { "name":"[C" }, { "name":"[D" }, { "name":"[F" }, { "name":"[I" }, { "name":"[J" }, { "name":"[Lcom.fasterxml.jackson.annotation.JsonSubTypes$Type;" }, { "name":"[Lcom.fasterxml.jackson.databind.deser.BeanDeserializerModifier;" }, { "name":"[Lcom.fasterxml.jackson.databind.deser.Deserializers;" }, { "name":"[Lcom.fasterxml.jackson.databind.deser.KeyDeserializers;" }, { "name":"[Lcom.fasterxml.jackson.databind.deser.ValueInstantiators;" }, { "name":"[Lcom.fasterxml.jackson.databind.ser.BeanSerializerModifier;" }, { "name":"[Lcom.fasterxml.jackson.databind.ser.Serializers;" }, { "name":"[Lcom.zaxxer.hikari.util.ConcurrentBag$IConcurrentBagEntry;" }, { "name":"[Ljava.lang.Class;" }, { "name":"[Ljava.lang.Object;" }, { "name":"[Ljava.lang.String;" }, { "name":"[Ljava.lang.annotation.Annotation;" }, { "name":"[Ljava.lang.reflect.Constructor;" }, { "name":"[Ljava.lang.reflect.Field;" }, { "name":"[Ljava.lang.reflect.Method;" }, { "name":"[Ljava.rmi.server.ObjID;" }, { "name":"[Ljava.sql.Statement;" }, { "name":"[Ljavax.management.openmbean.CompositeData;" }, { "name":"[Lorg.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure;" }, { "name":"[Lorg.springframework.boot.context.config.ConfigDataLocation;" }, { "name":"[Lorg.springframework.core.annotation.AnnotationAttributes;" }, { "name":"[Lorg.springframework.core.annotation.TypeMappedAnnotation;" }, { "name":"[Lorg.springframework.jmx.export.metadata.ManagedOperationParameter;" }, { "name":"[Lorg.springframework.util.ConcurrentReferenceHashMap$Segment;" }, { "name":"[Lorg.springframework.web.bind.annotation.RequestMethod;" }, { "name":"[S" }, { "name":"[Z" }, { "name":"android.app.Application" }, { "name":"apple.security.AppleProvider", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"boolean", "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.lang.Boolean"] }, {"name":"from","parameterTypes":["java.lang.Boolean"] }, {"name":"of","parameterTypes":["java.lang.Boolean"] }, {"name":"valueOf","parameterTypes":["java.lang.Boolean"] }] }, { "name":"brave.Tracer" }, { "name":"ch.qos.logback.classic.BasicConfigurator", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.LoggerContext" }, { "name":"ch.qos.logback.classic.encoder.PatternLayoutEncoder", "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.joran.SerializedModelConfigurator", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.model.ConfigurationModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.classic.model.LoggerModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.classic.model.RootLoggerModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.classic.pattern.DateConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.LevelConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.LineSeparatorConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.LoggerConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.MessageConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.pattern.ThreadConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.classic.util.DefaultJoranConfigurator", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.core.ConsoleAppender", "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.core.FileAppender", "methods":[{"name":"setAppend","parameterTypes":["boolean"] }, {"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"ch.qos.logback.core.OutputStreamAppender", "methods":[{"name":"setEncoder","parameterTypes":["ch.qos.logback.core.encoder.Encoder"] }] }, { "name":"ch.qos.logback.core.encoder.Encoder", "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"ch.qos.logback.core.encoder.LayoutWrappingEncoder", "methods":[{"name":"setCharset","parameterTypes":["java.nio.charset.Charset"] }, {"name":"setParent","parameterTypes":["ch.qos.logback.core.spi.ContextAware"] }] }, { "name":"ch.qos.logback.core.model.AppenderModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.AppenderRefModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.ComponentModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.ImplicitModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.IncludeModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.Model", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.NamedComponentModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.NamedModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.PropertyModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.model.StatusListenerModel", "allDeclaredFields":true }, { "name":"ch.qos.logback.core.pattern.PatternLayoutEncoderBase", "methods":[{"name":"setPattern","parameterTypes":["java.lang.String"] }] }, { "name":"ch.qos.logback.core.rolling.RollingFileAppender", "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setFile","parameterTypes":["java.lang.String"] }, {"name":"setRollingPolicy","parameterTypes":["ch.qos.logback.core.rolling.RollingPolicy"] }] }, { "name":"ch.qos.logback.core.rolling.RollingPolicy", "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"ch.qos.logback.core.rolling.RollingPolicyBase", "methods":[{"name":"setFileNamePattern","parameterTypes":["java.lang.String"] }, {"name":"setParent","parameterTypes":["ch.qos.logback.core.FileAppender"] }] }, { "name":"ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy", "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setMaxFileSize","parameterTypes":["ch.qos.logback.core.util.FileSize"] }] }, { "name":"ch.qos.logback.core.rolling.TimeBasedRollingPolicy", "methods":[{"name":"setCleanHistoryOnStart","parameterTypes":["boolean"] }, {"name":"setMaxHistory","parameterTypes":["int"] }, {"name":"setTotalSizeCap","parameterTypes":["ch.qos.logback.core.util.FileSize"] }] }, { "name":"ch.qos.logback.core.rolling.helper.DateTokenConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.core.rolling.helper.IntegerTokenConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.core.spi.ContextAware", "methods":[{"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"ch.qos.logback.core.status.NopStatusListener", "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"ch.qos.logback.core.util.FileSize", "methods":[{"name":"","parameterTypes":[] }, {"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"co.elastic.clients.elasticsearch.ElasticsearchClient" }, { "name":"co.elastic.clients.transport.ElasticsearchTransport" }, { "name":"com.alibaba.nacos.Nacos", "allDeclaredFields":true, "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"main","parameterTypes":["java.lang.String[]"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.Nacos$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.Nacos$NacosRuntimeHints", "queryAllDeclaredMethods":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.ability.ServerAbilities", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getConfigAbility","parameterTypes":[] }, {"name":"getNamingAbility","parameterTypes":[] }, {"name":"getRemoteAbility","parameterTypes":[] }, {"name":"setConfigAbility","parameterTypes":["com.alibaba.nacos.api.config.ability.ServerConfigAbility"] }, {"name":"setNamingAbility","parameterTypes":["com.alibaba.nacos.api.naming.ability.ServerNamingAbility"] }, {"name":"setRemoteAbility","parameterTypes":["com.alibaba.nacos.api.remote.ability.ServerRemoteAbility"] }] }, { "name":"com.alibaba.nacos.api.ability.constant.AbilityKey" }, { "name":"com.alibaba.nacos.api.annotation.NacosApi", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.api.cmdb.pojo.Entity" }, { "name":"com.alibaba.nacos.api.config.ability.ServerConfigAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"isSupportRemoteMetrics","parameterTypes":[] }, {"name":"setSupportRemoteMetrics","parameterTypes":["boolean"] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.AbstractConfigRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setDataId","parameterTypes":["java.lang.String"] }, {"name":"setGroup","parameterTypes":["java.lang.String"] }, {"name":"setTenant","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setConfigListenContexts","parameterTypes":["java.util.List"] }, {"name":"setListen","parameterTypes":["boolean"] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest$ConfigListenContext", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setDataId","parameterTypes":["java.lang.String"] }, {"name":"setGroup","parameterTypes":["java.lang.String"] }, {"name":"setMd5","parameterTypes":["java.lang.String"] }, {"name":"setTenant","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigChangeNotifyRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getDataId","parameterTypes":[] }, {"name":"getGroup","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] }, {"name":"getTenant","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setAdditionMap","parameterTypes":["java.util.Map"] }, {"name":"setCasMd5","parameterTypes":["java.lang.String"] }, {"name":"setContent","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.request.cluster.ConfigChangeClusterSyncRequest" }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getChangedConfigs","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigChangeBatchListenResponse$ConfigContext", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getDataId","parameterTypes":[] }, {"name":"getGroup","parameterTypes":[] }, {"name":"getTenant","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigChangeNotifyResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigPublishResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigQueryResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getContent","parameterTypes":[] }, {"name":"getContentType","parameterTypes":[] }, {"name":"getEncryptedDataKey","parameterTypes":[] }, {"name":"getLastModified","parameterTypes":[] }, {"name":"getMd5","parameterTypes":[] }, {"name":"getTag","parameterTypes":[] }, {"name":"isBeta","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.config.remote.response.ConfigRemoveResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.api.config.remote.response.cluster.ConfigChangeClusterSyncResponse" }, { "name":"com.alibaba.nacos.api.exception.NacosException" }, { "name":"com.alibaba.nacos.api.exception.api.NacosApiException" }, { "name":"com.alibaba.nacos.api.exception.runtime.NacosRuntimeException" }, { "name":"com.alibaba.nacos.api.grpc.auto.BiRequestStreamGrpc$BiRequestStreamImplBase", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"bindService","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.grpc.auto.Metadata", "methods":[{"name":"getClientIp","parameterTypes":[] }, {"name":"getClientIpBytes","parameterTypes":[] }, {"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"getTypeBytes","parameterTypes":[] }, {"name":"newBuilder","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.grpc.auto.Metadata$Builder", "methods":[{"name":"clearClientIp","parameterTypes":[] }, {"name":"clearType","parameterTypes":[] }, {"name":"getClientIp","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"setClientIp","parameterTypes":["java.lang.String"] }, {"name":"setClientIpBytes","parameterTypes":["com.google.protobuf.ByteString"] }, {"name":"setType","parameterTypes":["java.lang.String"] }, {"name":"setTypeBytes","parameterTypes":["com.google.protobuf.ByteString"] }] }, { "name":"com.alibaba.nacos.api.grpc.auto.Payload", "methods":[{"name":"getBody","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"hasBody","parameterTypes":[] }, {"name":"hasMetadata","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.grpc.auto.Payload$Builder", "methods":[{"name":"clearBody","parameterTypes":[] }, {"name":"clearMetadata","parameterTypes":[] }, {"name":"getBody","parameterTypes":[] }, {"name":"getBodyBuilder","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"getMetadataBuilder","parameterTypes":[] }, {"name":"hasBody","parameterTypes":[] }, {"name":"hasMetadata","parameterTypes":[] }, {"name":"setBody","parameterTypes":["com.google.protobuf.Any"] }, {"name":"setMetadata","parameterTypes":["com.alibaba.nacos.api.grpc.auto.Metadata"] }] }, { "name":"com.alibaba.nacos.api.grpc.auto.RequestGrpc$RequestImplBase", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"bindService","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.model.v2.Result", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.api.naming.ability.ServerNamingAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"isSupportJraft","parameterTypes":[] }, {"name":"setSupportJraft","parameterTypes":["boolean"] }] }, { "name":"com.alibaba.nacos.api.naming.pojo.Instance", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "unsafeAllocated":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getClusterName","parameterTypes":[] }, {"name":"getInstanceHeartBeatInterval","parameterTypes":[] }, {"name":"getInstanceHeartBeatTimeOut","parameterTypes":[] }, {"name":"getInstanceId","parameterTypes":[] }, {"name":"getInstanceIdGenerator","parameterTypes":[] }, {"name":"getIp","parameterTypes":[] }, {"name":"getIpDeleteTimeout","parameterTypes":[] }, {"name":"getMetadata","parameterTypes":[] }, {"name":"getPort","parameterTypes":[] }, {"name":"getServiceName","parameterTypes":[] }, {"name":"getWeight","parameterTypes":[] }, {"name":"isEnabled","parameterTypes":[] }, {"name":"isEphemeral","parameterTypes":[] }, {"name":"isHealthy","parameterTypes":[] }, {"name":"setClusterName","parameterTypes":["java.lang.String"] }, {"name":"setEnabled","parameterTypes":["boolean"] }, {"name":"setEphemeral","parameterTypes":["boolean"] }, {"name":"setHealthy","parameterTypes":["boolean"] }, {"name":"setIp","parameterTypes":["java.lang.String"] }, {"name":"setMetadata","parameterTypes":["java.util.Map"] }, {"name":"setPort","parameterTypes":["int"] }, {"name":"setServiceName","parameterTypes":["java.lang.String"] }, {"name":"setWeight","parameterTypes":["double"] }] }, { "name":"com.alibaba.nacos.api.naming.pojo.InstanceHessianDeserializer" }, { "name":"com.alibaba.nacos.api.naming.pojo.ServiceInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getCacheMillis","parameterTypes":[] }, {"name":"getChecksum","parameterTypes":[] }, {"name":"getClusters","parameterTypes":[] }, {"name":"getGroupName","parameterTypes":[] }, {"name":"getHosts","parameterTypes":[] }, {"name":"getLastRefTime","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"isAllIps","parameterTypes":[] }, {"name":"isReachProtectionThreshold","parameterTypes":[] }, {"name":"isValid","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.pojo.healthcheck.AbstractHealthChecker", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.api.naming.remote.request.AbstractNamingRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setGroupName","parameterTypes":["java.lang.String"] }, {"name":"setNamespace","parameterTypes":["java.lang.String"] }, {"name":"setServiceName","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.api.naming.remote.request.BatchInstanceRequest" }, { "name":"com.alibaba.nacos.api.naming.remote.request.InstanceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setInstance","parameterTypes":["com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"setType","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.api.naming.remote.request.NotifySubscriberRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getGroupName","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] }, {"name":"getNamespace","parameterTypes":[] }, {"name":"getServiceInfo","parameterTypes":[] }, {"name":"getServiceName","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.request.PersistentInstanceRequest" }, { "name":"com.alibaba.nacos.api.naming.remote.request.ServiceListRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setPageNo","parameterTypes":["int"] }, {"name":"setPageSize","parameterTypes":["int"] }] }, { "name":"com.alibaba.nacos.api.naming.remote.request.ServiceQueryRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setCluster","parameterTypes":["java.lang.String"] }, {"name":"setHealthyOnly","parameterTypes":["boolean"] }, {"name":"setUdpPort","parameterTypes":["int"] }] }, { "name":"com.alibaba.nacos.api.naming.remote.request.SubscribeServiceRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setClusters","parameterTypes":["java.lang.String"] }, {"name":"setSubscribe","parameterTypes":["boolean"] }] }, { "name":"com.alibaba.nacos.api.naming.remote.response.BatchInstanceResponse" }, { "name":"com.alibaba.nacos.api.naming.remote.response.InstanceResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.response.NotifySubscriberResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.response.QueryServiceResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getServiceInfo","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.response.ServiceListResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getCount","parameterTypes":[] }, {"name":"getServiceNames","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.naming.remote.response.SubscribeServiceResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getServiceInfo","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.Payload", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.api.remote.PushCallBack" }, { "name":"com.alibaba.nacos.api.remote.RequestCallBack" }, { "name":"com.alibaba.nacos.api.remote.Requester", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.api.remote.ability.ServerRemoteAbility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"isGrpcReportEnabled","parameterTypes":[] }, {"name":"isSupportRemoteConnection","parameterTypes":[] }, {"name":"setGrpcReportEnabled","parameterTypes":["boolean"] }, {"name":"setSupportRemoteConnection","parameterTypes":["boolean"] }] }, { "name":"com.alibaba.nacos.api.remote.request.ConnectionSetupRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getAbilityTable","parameterTypes":[] }, {"name":"getClientVersion","parameterTypes":[] }, {"name":"getLabels","parameterTypes":[] }, {"name":"getTenant","parameterTypes":[] }, {"name":"setAbilityTable","parameterTypes":["java.util.Map"] }, {"name":"setClientVersion","parameterTypes":["java.lang.String"] }, {"name":"setLabels","parameterTypes":["java.util.Map"] }, {"name":"setTenant","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.api.remote.request.HealthCheckRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.request.InternalRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getModule","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.request.Request", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getHeaders","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.request.RequestMeta" }, { "name":"com.alibaba.nacos.api.remote.request.ServerCheckRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.api.remote.request.ServerLoaderInfoRequest" }, { "name":"com.alibaba.nacos.api.remote.request.ServerReloadRequest" }, { "name":"com.alibaba.nacos.api.remote.request.ServerRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.api.remote.request.SetupAckRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getAbilityTable","parameterTypes":[] }, {"name":"getModule","parameterTypes":[] }, {"name":"setAbilityTable","parameterTypes":["java.util.Map"] }] }, { "name":"com.alibaba.nacos.api.remote.response.ErrorResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.api.remote.response.HealthCheckResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.api.remote.response.Response", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getErrorCode","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] }, {"name":"getRequestId","parameterTypes":[] }, {"name":"getResultCode","parameterTypes":[] }, {"name":"isSuccess","parameterTypes":[] }, {"name":"setErrorCode","parameterTypes":["int"] }, {"name":"setRequestId","parameterTypes":["java.lang.String"] }, {"name":"setResultCode","parameterTypes":["int"] }] }, { "name":"com.alibaba.nacos.api.remote.response.ServerCheckResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getConnectionId","parameterTypes":[] }, {"name":"isSupportAbilityNegotiation","parameterTypes":[] }, {"name":"setConnectionId","parameterTypes":["java.lang.String"] }, {"name":"setSupportAbilityNegotiation","parameterTypes":["boolean"] }] }, { "name":"com.alibaba.nacos.api.remote.response.ServerLoaderInfoResponse" }, { "name":"com.alibaba.nacos.api.remote.response.ServerReloadResponse" }, { "name":"com.alibaba.nacos.api.selector.Selector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.auth.annotation.Secured", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.auth.config.AuthConfigs", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"subscribeType","parameterTypes":[] }, {"name":"validate","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.auth.config.AuthConfigs$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.auth.config.AuthModuleStateBuilder" }, { "name":"com.alibaba.nacos.cmdb.controllers.OperationController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.cmdb.core.SwitchAndOptions", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.cmdb.memory.CmdbProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"queryEntitiesByLabel","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"queryEntity","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"queryLabel","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.cmdb.service.CmdbReader", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.cmdb.service.CmdbWriter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.ability.AbstractAbilityControlManager" }, { "name":"com.alibaba.nacos.common.event.ServerConfigChangeEvent" }, { "name":"com.alibaba.nacos.common.model.RestResult", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getCode","parameterTypes":[] }, {"name":"getData","parameterTypes":[] }, {"name":"getMessage","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.notify.DefaultPublisher", "fields":[{"name":"lastEventSequence"}], "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.notify.Event" }, { "name":"com.alibaba.nacos.common.notify.listener.SmartSubscriber", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"ignoreExpireEvent","parameterTypes":[] }, {"name":"subscribeType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.notify.listener.Subscriber", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"executor","parameterTypes":[] }, {"name":"ignoreExpireEvent","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"scopeMatches","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }] }, { "name":"com.alibaba.nacos.common.paramcheck.DefaultParamChecker" }, { "name":"com.alibaba.nacos.common.remote.ConnectionType" }, { "name":"com.alibaba.nacos.common.remote.TlsConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getCertChainFile","parameterTypes":[] }, {"name":"getCertPrivateKey","parameterTypes":[] }, {"name":"getCertPrivateKeyPassword","parameterTypes":[] }, {"name":"getCiphers","parameterTypes":[] }, {"name":"getEnableTls","parameterTypes":[] }, {"name":"getMutualAuthEnable","parameterTypes":[] }, {"name":"getProtocols","parameterTypes":[] }, {"name":"getSslProvider","parameterTypes":[] }, {"name":"getTrustAll","parameterTypes":[] }, {"name":"getTrustCollectionCertFile","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.remote.client.RpcClient" }, { "name":"com.alibaba.nacos.common.remote.client.RpcClientTlsConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.common.task.AbstractExecuteTask", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"shouldProcess","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.common.task.NacosTask", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.common.task.NacosTaskProcessor" }, { "name":"com.alibaba.nacos.config.server.aspect.ConfigChangeAspect", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.configuration.ConfigChangeConfigs"] }, {"name":"publishConfigAroundRpc","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest","com.alibaba.nacos.api.remote.request.RequestMeta"] }, {"name":"publishOrUpdateConfigAround","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigAroundRpc","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.config.server.aspect.ConfigOpFailureAspect", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.aspect.RequestLogAspect", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"interfaceGetConfigRpc","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","com.alibaba.nacos.api.config.remote.request.ConfigQueryRequest","com.alibaba.nacos.api.remote.request.RequestMeta"] }, {"name":"interfaceListenConfigRpc","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","com.alibaba.nacos.api.config.remote.request.ConfigBatchListenRequest","com.alibaba.nacos.api.remote.request.RequestMeta"] }, {"name":"interfacePublishSingle","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"interfacePublishSingleRpc","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","com.alibaba.nacos.api.config.remote.request.ConfigPublishRequest","com.alibaba.nacos.api.remote.request.RequestMeta"] }, {"name":"interfaceRemoveAllRpc","parameterTypes":["org.aspectj.lang.ProceedingJoinPoint","com.alibaba.nacos.api.config.remote.request.ConfigRemoveRequest","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.config.server.configuration.ConfigChangeConfigs", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"subscribeType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.configuration.ConfigChangeConfigs$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.configuration.NacosConfigConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"nacosWebFilter","parameterTypes":[] }, {"name":"nacosWebFilterRegistration","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.config.server.configuration.NacosConfigConfiguration$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.configuration.NacosConfigConfiguration$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.config.server.configuration.NacosConfigConfiguration$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.config.server.constant.ConfigModuleStateBuilder" }, { "name":"com.alibaba.nacos.config.server.constant.CounterMode" }, { "name":"com.alibaba.nacos.config.server.controller.CapacityController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.capacity.CapacityService"] }] }, { "name":"com.alibaba.nacos.config.server.controller.CapacityController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.ClientMetricsController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager","com.alibaba.nacos.core.remote.ConnectionManager"] }] }, { "name":"com.alibaba.nacos.config.server.controller.ClientMetricsController$1" }, { "name":"com.alibaba.nacos.config.server.controller.ClientMetricsController$ClusterMetricsCallBack" }, { "name":"com.alibaba.nacos.config.server.controller.ClientMetricsController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.CommunicationController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.LongPollingService","com.alibaba.nacos.config.server.remote.ConfigChangeListenContext","com.alibaba.nacos.core.remote.ConnectionManager"] }] }, { "name":"com.alibaba.nacos.config.server.controller.CommunicationController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.ConfigController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.controller.ConfigServletInner","com.alibaba.nacos.config.server.service.ConfigOperationService","com.alibaba.nacos.config.server.service.ConfigSubService","com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService","com.alibaba.nacos.core.namespace.repository.NamespacePersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService"] }, {"name":"detailConfigInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"fuzzySearchConfig","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","int","int"] }, {"name":"publishConfig","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.config.server.controller.ConfigController$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.controller.ConfigController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.ConfigOpsController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.dump.DumpService"] }] }, { "name":"com.alibaba.nacos.config.server.controller.ConfigOpsController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.ConfigServletInner", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.LongPollingService"] }] }, { "name":"com.alibaba.nacos.config.server.controller.ConfigServletInner__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.HealthController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.controller.HealthController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.HistoryController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.HistoryService"] }, {"name":"getConfigHistoryInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.Long"] }, {"name":"getDataIds","parameterTypes":["java.lang.String"] }, {"name":"listConfigHistory","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.Integer","java.lang.Integer","org.springframework.ui.ModelMap"] }] }, { "name":"com.alibaba.nacos.config.server.controller.HistoryController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.ListenerController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.ConfigSubService"] }] }, { "name":"com.alibaba.nacos.config.server.controller.ListenerController__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.parameters.SameNamespaceCloneConfigBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.config.server.controller.v2.ConfigControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.controller.ConfigServletInner","com.alibaba.nacos.config.server.service.ConfigOperationService","com.alibaba.nacos.config.server.service.ConfigDetailService"] }] }, { "name":"com.alibaba.nacos.config.server.controller.v2.ConfigControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.controller.v2.HistoryControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.HistoryService"] }] }, { "name":"com.alibaba.nacos.config.server.controller.v2.HistoryControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.config.server.exception.GlobalExceptionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.filter.CircuitFilter" }, { "name":"com.alibaba.nacos.config.server.filter.NacosWebFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"init","parameterTypes":["jakarta.servlet.FilterConfig"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.model.CacheItem" }, { "name":"com.alibaba.nacos.config.server.model.ConfigAdvanceInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.config.server.model.ConfigAllInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getConfigTags","parameterTypes":[] }, {"name":"getCreateIp","parameterTypes":[] }, {"name":"getCreateTime","parameterTypes":[] }, {"name":"getCreateUser","parameterTypes":[] }, {"name":"getDesc","parameterTypes":[] }, {"name":"getEffect","parameterTypes":[] }, {"name":"getModifyTime","parameterTypes":[] }, {"name":"getSchema","parameterTypes":[] }, {"name":"getUse","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.model.ConfigHistoryInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getAppName","parameterTypes":[] }, {"name":"getContent","parameterTypes":[] }, {"name":"getCreatedTime","parameterTypes":[] }, {"name":"getDataId","parameterTypes":[] }, {"name":"getEncryptedDataKey","parameterTypes":[] }, {"name":"getGroup","parameterTypes":[] }, {"name":"getId","parameterTypes":[] }, {"name":"getLastId","parameterTypes":[] }, {"name":"getLastModifiedTime","parameterTypes":[] }, {"name":"getMd5","parameterTypes":[] }, {"name":"getOpType","parameterTypes":[] }, {"name":"getSrcIp","parameterTypes":[] }, {"name":"getSrcUser","parameterTypes":[] }, {"name":"getTenant","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.model.ConfigInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getAppName","parameterTypes":[] }, {"name":"getTenant","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.model.ConfigInfo4Beta", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.config.server.model.ConfigInfoBase", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"getContent","parameterTypes":[] }, {"name":"getDataId","parameterTypes":[] }, {"name":"getEncryptedDataKey","parameterTypes":[] }, {"name":"getGroup","parameterTypes":[] }, {"name":"getId","parameterTypes":[] }, {"name":"getMd5","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.model.ConfigInfoBetaWrapper" }, { "name":"com.alibaba.nacos.config.server.model.ConfigInfoStateWrapper" }, { "name":"com.alibaba.nacos.config.server.model.ConfigInfoTagWrapper" }, { "name":"com.alibaba.nacos.config.server.model.ConfigInfoWrapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getLastModified","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.model.ConfigMetadata$ConfigExportItem" }, { "name":"com.alibaba.nacos.config.server.model.ConfigOperateResult" }, { "name":"com.alibaba.nacos.config.server.model.ConfigRequestInfo" }, { "name":"com.alibaba.nacos.config.server.model.GroupkeyListenserStatus", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.config.server.model.ListenerCheckResult" }, { "name":"com.alibaba.nacos.config.server.model.SameConfigPolicy" }, { "name":"com.alibaba.nacos.config.server.model.SampleResult", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.config.server.model.capacity.Capacity", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.config.server.model.capacity.GroupCapacity" }, { "name":"com.alibaba.nacos.config.server.model.capacity.TenantCapacity" }, { "name":"com.alibaba.nacos.config.server.model.event.LocalDataChangeEvent" }, { "name":"com.alibaba.nacos.config.server.model.form.ConfigForm" }, { "name":"com.alibaba.nacos.config.server.monitor.ConfigDynamicMeterRefreshService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"refreshTopnConfigChangeCount","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.monitor.MemoryMonitor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.notify.AsyncNotifyService"] }] }, { "name":"com.alibaba.nacos.config.server.monitor.collector.ConfigSubscriberMetricsCollector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.LongPollingService","com.alibaba.nacos.config.server.remote.ConfigChangeListenContext"] }] }, { "name":"com.alibaba.nacos.config.server.paramcheck.ConfigBlurSearchHttpParamExtractor" }, { "name":"com.alibaba.nacos.config.server.paramcheck.ConfigDefaultHttpParamExtractor" }, { "name":"com.alibaba.nacos.config.server.paramcheck.ConfigListenerHttpParamExtractor" }, { "name":"com.alibaba.nacos.config.server.remote.ConfigChangeBatchListenRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigChangeBatchListenRequestHandler$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigChangeClusterSyncRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.dump.DumpService"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigChangeListenContext", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigClusterRpcClientProxy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigConnectionEventListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.remote.ConfigChangeListenContext"] }, {"name":"clientConnected","parameterTypes":["com.alibaba.nacos.core.remote.Connection"] }, {"name":"clientDisConnected","parameterTypes":["com.alibaba.nacos.core.remote.Connection"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigPublishRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigPublishRequestHandler$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigQueryRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigQueryRequestHandler$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigRemoveRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.config.server.remote.ConfigRemoveRequestHandler$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.remote.RpcConfigChangeNotifier", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"registerTpsPoint","parameterTypes":[] }, {"name":"subscribeType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.remote.RpcConfigChangeNotifier$RpcPushTask" }, { "name":"com.alibaba.nacos.config.server.service.ClientIpWhiteList", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.ConfigDetailService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService"] }] }, { "name":"com.alibaba.nacos.config.server.service.ConfigOperationService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService"] }] }, { "name":"com.alibaba.nacos.config.server.service.ConfigReadinessCheckService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService"] }, {"name":"getModuleName","parameterTypes":[] }, {"name":"readiness","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.ConfigSubService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }] }, { "name":"com.alibaba.nacos.config.server.service.HistoryService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService"] }] }, { "name":"com.alibaba.nacos.config.server.service.LongPollingService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.NamespaceConfigInfoService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService"] }, {"name":"injectDetail","parameterTypes":["com.alibaba.nacos.core.namespace.model.Namespace"] }] }, { "name":"com.alibaba.nacos.config.server.service.SwitchService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.capacity.CapacityService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.capacity.GroupCapacityPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.capacity.TenantCapacityPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.dump.DumpRequest" }, { "name":"com.alibaba.nacos.config.server.service.dump.DumpService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"dump","parameterTypes":["com.alibaba.nacos.config.server.service.dump.DumpRequest"] }, {"name":"dumpAll","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.dump.EmbeddedDumpService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService","com.alibaba.nacos.core.namespace.repository.NamespacePersistService","com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoAggrPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService","com.alibaba.nacos.config.server.service.merge.MergeDatumService","com.alibaba.nacos.core.cluster.ServerMemberManager","com.alibaba.nacos.core.distributed.ProtocolManager"] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.dump.ExternalDumpService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService","com.alibaba.nacos.core.namespace.repository.NamespacePersistService","com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoAggrPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService","com.alibaba.nacos.config.server.service.merge.MergeDatumService","com.alibaba.nacos.core.cluster.ServerMemberManager"] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.dump.processor.DumpAllProcessor" }, { "name":"com.alibaba.nacos.config.server.service.merge.MergeDatumService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoAggrPersistService","com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService"] }] }, { "name":"com.alibaba.nacos.config.server.service.notify.AsyncNotifyService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }] }, { "name":"com.alibaba.nacos.config.server.service.notify.AsyncNotifyService$NotifySingleRpcTask" }, { "name":"com.alibaba.nacos.config.server.service.repository.ConfigInfoAggrPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.config.server.service.repository.ConfigInfoBetaPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.config.server.service.repository.ConfigInfoPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.config.server.service.repository.ConfigInfoTagPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.ConfigRowMapperInjector$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigDumpApplyHook", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"afterApply","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigDumpApplyHook$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoAggrPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate"] }, {"name":"addAggrConfigInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"aggrConfigInfoCount","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"batchPublishAggr","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.util.Map","java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllAggrGroup","parameterTypes":[] }, {"name":"findConfigInfoAggrByPage","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","int"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoAggrPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoBetaPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate"] }, {"name":"addConfigInfo4Beta","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"configInfoBetaCount","parameterTypes":[] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllConfigInfoBetaForDumpAll","parameterTypes":["int","int"] }, {"name":"findConfigInfo4Beta","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo4BetaState","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateBeta","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateBetaCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfo4Beta","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4Beta","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4BetaCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoBetaPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate","com.alibaba.nacos.core.distributed.id.IdGeneratorManager","com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService"] }, {"name":"addConfigInfo","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"addConfigInfoAtomic","parameterTypes":["long","java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"addConfigTagRelationAtomic","parameterTypes":["long","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"addConfigTagsRelation","parameterTypes":["long","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"batchInsertOrUpdate","parameterTypes":["java.util.List","java.lang.String","java.lang.String","java.util.Map","com.alibaba.nacos.config.server.model.SameConfigPolicy"] }, {"name":"configInfoCount","parameterTypes":[] }, {"name":"configInfoCount","parameterTypes":["java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllConfigInfo4Export","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.util.List"] }, {"name":"findAllConfigInfoFragment","parameterTypes":["long","int","boolean"] }, {"name":"findChangeConfig","parameterTypes":["java.sql.Timestamp","long","int"] }, {"name":"findConfigAdvanceInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigAllInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo","parameterTypes":["long"] }, {"name":"findConfigInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo4Page","parameterTypes":["int","int","java.lang.String","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"findConfigInfoLike4Page","parameterTypes":["int","int","java.lang.String","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"findConfigInfoState","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfosByIds","parameterTypes":["java.lang.String"] }, {"name":"findConfigMaxId","parameterTypes":[] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getGroupIdList","parameterTypes":["int","int"] }, {"name":"getTenantIdList","parameterTypes":["int","int"] }, {"name":"insertOrUpdate","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"insertOrUpdateCas","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"queryConfigInfoByNamespace","parameterTypes":["java.lang.String"] }, {"name":"removeConfigInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoAtomic","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoByIds","parameterTypes":["java.util.List","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoByIdsAtomic","parameterTypes":["java.lang.String"] }, {"name":"removeTagByIdAtomic","parameterTypes":["long"] }, {"name":"selectTagByConfig","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"updateConfigInfoAtomic","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"updateConfigInfoCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.util.Map"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoTagPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate"] }, {"name":"addConfigInfo4Tag","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"configInfoTagCount","parameterTypes":[] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllConfigInfoTagForDumpAll","parameterTypes":["int","int"] }, {"name":"findConfigInfo4Tag","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo4TagState","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfoTags","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateTag","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateTagCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoTag","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4Tag","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4TagCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedConfigInfoTagPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedHistoryConfigInfoPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"detailConfigHistory","parameterTypes":["java.lang.Long"] }, {"name":"detailPreviousConfigHistory","parameterTypes":["java.lang.Long"] }, {"name":"findConfigHistory","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","int"] }, {"name":"findConfigHistoryCountByTime","parameterTypes":["java.sql.Timestamp"] }, {"name":"findDeletedConfig","parameterTypes":["java.sql.Timestamp","long","int"] }, {"name":"insertConfigHistoryAtomic","parameterTypes":["long","com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.sql.Timestamp","java.lang.String"] }, {"name":"removeConfigHistory","parameterTypes":["java.sql.Timestamp","int"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.embedded.EmbeddedHistoryConfigInfoPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoAggrPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addAggrConfigInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"aggrConfigInfoCount","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"batchPublishAggr","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.util.Map","java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllAggrGroup","parameterTypes":[] }, {"name":"findConfigInfoAggrByPage","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","int"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoAggrPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoBetaPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addConfigInfo4Beta","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"configInfoBetaCount","parameterTypes":[] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllConfigInfoBetaForDumpAll","parameterTypes":["int","int"] }, {"name":"findConfigInfo4Beta","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo4BetaState","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateBeta","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateBetaCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfo4Beta","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4Beta","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4BetaCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoBetaPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.config.server.service.repository.HistoryConfigInfoPersistService"] }, {"name":"addConfigInfo","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"addConfigInfoAtomic","parameterTypes":["long","java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"addConfigTagRelationAtomic","parameterTypes":["long","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"addConfigTagsRelation","parameterTypes":["long","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"batchInsertOrUpdate","parameterTypes":["java.util.List","java.lang.String","java.lang.String","java.util.Map","com.alibaba.nacos.config.server.model.SameConfigPolicy"] }, {"name":"configInfoCount","parameterTypes":[] }, {"name":"configInfoCount","parameterTypes":["java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllConfigInfo4Export","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.util.List"] }, {"name":"findAllConfigInfoFragment","parameterTypes":["long","int","boolean"] }, {"name":"findChangeConfig","parameterTypes":["java.sql.Timestamp","long","int"] }, {"name":"findConfigAdvanceInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigAllInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo","parameterTypes":["long"] }, {"name":"findConfigInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo4Page","parameterTypes":["int","int","java.lang.String","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"findConfigInfoLike4Page","parameterTypes":["int","int","java.lang.String","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"findConfigInfoState","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfosByIds","parameterTypes":["java.lang.String"] }, {"name":"findConfigMaxId","parameterTypes":[] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getGroupIdList","parameterTypes":["int","int"] }, {"name":"getTenantIdList","parameterTypes":["int","int"] }, {"name":"insertOrUpdate","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"insertOrUpdateCas","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.config.server.model.ConfigInfo","java.util.Map"] }, {"name":"queryConfigInfoByNamespace","parameterTypes":["java.lang.String"] }, {"name":"removeConfigInfo","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoAtomic","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoByIds","parameterTypes":["java.util.List","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoByIdsAtomic","parameterTypes":["java.lang.String"] }, {"name":"removeTagByIdAtomic","parameterTypes":["long"] }, {"name":"selectTagByConfig","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"updateConfigInfoAtomic","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.util.Map"] }, {"name":"updateConfigInfoCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.util.Map"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoTagPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addConfigInfo4Tag","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"configInfoTagCount","parameterTypes":[] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"findAllConfigInfoTagForDumpAll","parameterTypes":["int","int"] }, {"name":"findConfigInfo4Tag","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfo4TagState","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findConfigInfoTags","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateTag","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"insertOrUpdateTagCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"removeConfigInfoTag","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4Tag","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"updateConfigInfo4TagCas","parameterTypes":["com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalConfigInfoTagPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalHistoryConfigInfoPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"detailConfigHistory","parameterTypes":["java.lang.Long"] }, {"name":"detailPreviousConfigHistory","parameterTypes":["java.lang.Long"] }, {"name":"findConfigHistory","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","int"] }, {"name":"findConfigHistoryCountByTime","parameterTypes":["java.sql.Timestamp"] }, {"name":"findDeletedConfig","parameterTypes":["java.sql.Timestamp","long","int"] }, {"name":"insertConfigHistoryAtomic","parameterTypes":["long","com.alibaba.nacos.config.server.model.ConfigInfo","java.lang.String","java.lang.String","java.sql.Timestamp","java.lang.String"] }, {"name":"removeConfigHistory","parameterTypes":["java.sql.Timestamp","int"] }] }, { "name":"com.alibaba.nacos.config.server.service.repository.extrnal.ExternalHistoryConfigInfoPersistServiceImpl$$SpringCGLIB$$0", "fields":[{"name":"CGLIB$CALLBACK_FILTER"}, {"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.config.server.service.trace.ConfigTraceService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.utils.PropertyUtil", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.config.server.utils.ZipUtils$UnZipResult" }, { "name":"com.alibaba.nacos.config.server.utils.ZipUtils$ZipItem" }, { "name":"com.alibaba.nacos.consistency.CommandOperations", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"execute","parameterTypes":["java.util.Map"] }] }, { "name":"com.alibaba.nacos.consistency.Config", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.consistency.ConsistencyProtocol", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"aGetData","parameterTypes":["com.alibaba.nacos.consistency.entity.ReadRequest"] }, {"name":"addRequestProcessors","parameterTypes":["java.util.Collection"] }, {"name":"getData","parameterTypes":["com.alibaba.nacos.consistency.entity.ReadRequest"] }, {"name":"init","parameterTypes":["com.alibaba.nacos.consistency.Config"] }, {"name":"memberChange","parameterTypes":["java.util.Set"] }, {"name":"protocolMetaData","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"write","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }, {"name":"writeAsync","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }] }, { "name":"com.alibaba.nacos.consistency.DataOperation", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.consistency.IdGenerator" }, { "name":"com.alibaba.nacos.consistency.ProtocolMetaData", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getMetaDataMap","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.consistency.RequestProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"onError","parameterTypes":["java.lang.Throwable"] }] }, { "name":"com.alibaba.nacos.consistency.SerializeFactory", "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"com.alibaba.nacos.consistency.Serializer", "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"com.alibaba.nacos.consistency.ap.APProtocol" }, { "name":"com.alibaba.nacos.consistency.ap.RequestProcessor4AP" }, { "name":"com.alibaba.nacos.consistency.cp.CPProtocol", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.consistency.cp.RequestProcessor4CP", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.consistency.entity.ReadRequest" }, { "name":"com.alibaba.nacos.consistency.entity.Response" }, { "name":"com.alibaba.nacos.consistency.entity.WriteRequest", "methods":[{"name":"getData","parameterTypes":[] }, {"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getGroup","parameterTypes":[] }, {"name":"getGroupBytes","parameterTypes":[] }, {"name":"getKey","parameterTypes":[] }, {"name":"getKeyBytes","parameterTypes":[] }, {"name":"getOperation","parameterTypes":[] }, {"name":"getOperationBytes","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"getTypeBytes","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.consistency.entity.WriteRequest$Builder", "methods":[{"name":"clearData","parameterTypes":[] }, {"name":"clearGroup","parameterTypes":[] }, {"name":"clearKey","parameterTypes":[] }, {"name":"clearOperation","parameterTypes":[] }, {"name":"clearType","parameterTypes":[] }, {"name":"getData","parameterTypes":[] }, {"name":"getGroup","parameterTypes":[] }, {"name":"getKey","parameterTypes":[] }, {"name":"getOperation","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"setData","parameterTypes":["com.google.protobuf.ByteString"] }, {"name":"setGroup","parameterTypes":["java.lang.String"] }, {"name":"setGroupBytes","parameterTypes":["com.google.protobuf.ByteString"] }, {"name":"setKey","parameterTypes":["java.lang.String"] }, {"name":"setKeyBytes","parameterTypes":["com.google.protobuf.ByteString"] }, {"name":"setOperation","parameterTypes":["java.lang.String"] }, {"name":"setOperationBytes","parameterTypes":["com.google.protobuf.ByteString"] }, {"name":"setType","parameterTypes":["java.lang.String"] }, {"name":"setTypeBytes","parameterTypes":["com.google.protobuf.ByteString"] }] }, { "name":"com.alibaba.nacos.consistency.serialize.HessianSerializer", "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"com.alibaba.nacos.consistency.serialize.JacksonSerializer" }, { "name":"com.alibaba.nacos.consistency.serialize.NacosHessianSerializerFactory", "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"com.alibaba.nacos.consistency.snapshot.LocalFileMeta", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.aot.BeanProcessorHints", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }] }, { "name":"com.alibaba.nacos.console.aot.NacosRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.config.ConsoleConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"corsFilter","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"jacksonObjectMapperCustomization","parameterTypes":[] }, {"name":"xssFilter","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.config.ConsoleModuleStateBuilder" }, { "name":"com.alibaba.nacos.console.controller.HealthController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.controller.HealthController__BeanDefinitions" }, { "name":"com.alibaba.nacos.console.controller.NamespaceController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getNamespaces","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.controller.NamespaceController__Autowiring" }, { "name":"com.alibaba.nacos.console.controller.NamespaceController__BeanDefinitions" }, { "name":"com.alibaba.nacos.console.controller.ServerStateController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getAnnouncement","parameterTypes":["java.lang.String"] }, {"name":"getConsoleUiGuide","parameterTypes":[] }, {"name":"serverState","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.controller.ServerStateController__BeanDefinitions" }, { "name":"com.alibaba.nacos.console.controller.v2.HealthControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.controller.v2.HealthControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.console.controller.v2.NamespaceControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.service.NamespaceOperationService","com.alibaba.nacos.core.namespace.repository.NamespacePersistService"] }] }, { "name":"com.alibaba.nacos.console.controller.v2.NamespaceControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.console.exception.ConsoleExceptionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.exception.NacosApiExceptionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.console.filter.XssFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor" }, { "name":"com.alibaba.nacos.core.ability.RemoteAbilityInitializer" }, { "name":"com.alibaba.nacos.core.ability.config.AbilityConfigs", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"subscribeType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.ability.config.AbilityConfigs$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.core.auth.AuthConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"authFilter","parameterTypes":["com.alibaba.nacos.auth.config.AuthConfigs","com.alibaba.nacos.core.code.ControllerMethodsCache"] }, {"name":"authFilterRegistration","parameterTypes":["com.alibaba.nacos.core.auth.AuthFilter"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.core.auth.AuthConfig$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.core.auth.AuthConfig$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.auth.AuthConfig$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.auth.AuthFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.auth.RemoteRequestAuthFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.auth.config.AuthConfigs"] }] }, { "name":"com.alibaba.nacos.core.cluster.Member", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getAbilities","parameterTypes":[] }, {"name":"getAddress","parameterTypes":[] }, {"name":"getExtendInfo","parameterTypes":[] }, {"name":"getFailAccessCnt","parameterTypes":[] }, {"name":"getIp","parameterTypes":[] }, {"name":"getPort","parameterTypes":[] }, {"name":"getState","parameterTypes":[] }, {"name":"isGrpcReportEnabled","parameterTypes":[] }, {"name":"setAbilities","parameterTypes":["com.alibaba.nacos.api.ability.ServerAbilities"] }, {"name":"setAddress","parameterTypes":["java.lang.String"] }, {"name":"setExtendInfo","parameterTypes":["java.util.Map"] }, {"name":"setFailAccessCnt","parameterTypes":["int"] }, {"name":"setGrpcReportEnabled","parameterTypes":["boolean"] }, {"name":"setIp","parameterTypes":["java.lang.String"] }, {"name":"setPort","parameterTypes":["int"] }, {"name":"setState","parameterTypes":["com.alibaba.nacos.core.cluster.NodeState"] }] }, { "name":"com.alibaba.nacos.core.cluster.MemberChangeListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"ignoreExpireEvent","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"subscribeType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.cluster.MemberLookup" }, { "name":"com.alibaba.nacos.core.cluster.MembersChangeEvent" }, { "name":"com.alibaba.nacos.core.cluster.NodeState", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.core.cluster.ServerMemberManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["jakarta.servlet.ServletContext"] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.cluster.ServerMemberManager$MemberInfoReportTask" }, { "name":"com.alibaba.nacos.core.cluster.health.AbstractModuleHealthChecker", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }] }, { "name":"com.alibaba.nacos.core.cluster.remote.MemberReportHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.core.cluster.remote.request.AbstractClusterRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getModule","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.cluster.remote.request.MemberReportRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getNode","parameterTypes":[] }, {"name":"setNode","parameterTypes":["com.alibaba.nacos.core.cluster.Member"] }] }, { "name":"com.alibaba.nacos.core.cluster.remote.response.MemberReportResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getNode","parameterTypes":[] }, {"name":"setNode","parameterTypes":["com.alibaba.nacos.core.cluster.Member"] }] }, { "name":"com.alibaba.nacos.core.code.ControllerMethodsCache", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.code.SpringApplicationRunListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.SpringApplication","java.lang.String[]"] }] }, { "name":"com.alibaba.nacos.core.code.StandaloneProfileApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.config.DistroModuleStateBuilder" }, { "name":"com.alibaba.nacos.core.config.RaftModuleStateBuilder" }, { "name":"com.alibaba.nacos.core.context.RequestContext" }, { "name":"com.alibaba.nacos.core.context.remote.HttpRequestContextConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"nacosRequestContextFilter","parameterTypes":[] }, {"name":"requestContextFilterRegistration","parameterTypes":["com.alibaba.nacos.core.context.remote.HttpRequestContextFilter"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.core.context.remote.HttpRequestContextConfig$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.core.context.remote.HttpRequestContextConfig$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.context.remote.HttpRequestContextConfig$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.context.remote.HttpRequestContextFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.control.TpsControl", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.core.control.http.HttpTpsPointRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }] }, { "name":"com.alibaba.nacos.core.control.http.NacosHttpTpsControlRegistration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"tpsFilter","parameterTypes":["com.alibaba.nacos.core.code.ControllerMethodsCache"] }, {"name":"tpsFilterRegistration","parameterTypes":["com.alibaba.nacos.core.control.http.NacosHttpTpsFilter"] }] }, { "name":"com.alibaba.nacos.core.control.http.NacosHttpTpsControlRegistration$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.core.control.http.NacosHttpTpsControlRegistration$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.control.http.NacosHttpTpsControlRegistration$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.control.http.NacosHttpTpsFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"init","parameterTypes":["jakarta.servlet.FilterConfig"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.control.remote.TpsControlRequestFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.controller.CoreOpsController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.distributed.ProtocolManager","com.alibaba.nacos.core.distributed.id.IdGeneratorManager"] }] }, { "name":"com.alibaba.nacos.core.controller.CoreOpsController__BeanDefinitions" }, { "name":"com.alibaba.nacos.core.controller.NacosClusterController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }, {"name":"leave","parameterTypes":["java.util.Collection","java.lang.Boolean"] }, {"name":"listNodes","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.core.controller.NacosClusterController__BeanDefinitions" }, { "name":"com.alibaba.nacos.core.controller.ServerLoaderController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.remote.ConnectionManager","com.alibaba.nacos.core.cluster.ServerMemberManager","com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy","com.alibaba.nacos.core.remote.core.ServerReloaderRequestHandler","com.alibaba.nacos.core.remote.core.ServerLoaderInfoRequestHandler"] }] }, { "name":"com.alibaba.nacos.core.controller.ServerLoaderController$1" }, { "name":"com.alibaba.nacos.core.controller.ServerLoaderController$2" }, { "name":"com.alibaba.nacos.core.controller.ServerLoaderController$ServerLoaderMetrics" }, { "name":"com.alibaba.nacos.core.controller.ServerLoaderController__BeanDefinitions" }, { "name":"com.alibaba.nacos.core.controller.v2.CoreOpsV2Controller", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.distributed.ProtocolManager","com.alibaba.nacos.core.distributed.id.IdGeneratorManager"] }] }, { "name":"com.alibaba.nacos.core.controller.v2.CoreOpsV2Controller__BeanDefinitions" }, { "name":"com.alibaba.nacos.core.controller.v2.NacosClusterControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.service.NacosClusterOperationService"] }] }, { "name":"com.alibaba.nacos.core.controller.v2.NacosClusterControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.core.distributed.AbstractConsistencyProtocol", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"loadLogProcessor","parameterTypes":["java.util.List"] }, {"name":"protocolMetaData","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.distributed.ConsistencyConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"strongAgreementProtocol","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }] }, { "name":"com.alibaba.nacos.core.distributed.ConsistencyConfiguration$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.core.distributed.ConsistencyConfiguration$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.distributed.ConsistencyConfiguration$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.distributed.ProtocolManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }, {"name":"destroy","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }] }, { "name":"com.alibaba.nacos.core.distributed.distro.DistroProtocol", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager","com.alibaba.nacos.core.distributed.distro.component.DistroComponentHolder","com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder"] }] }, { "name":"com.alibaba.nacos.core.distributed.distro.component.DistroComponentHolder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.distributed.distro.component.DistroDataProcessor" }, { "name":"com.alibaba.nacos.core.distributed.distro.component.DistroDataStorage" }, { "name":"com.alibaba.nacos.core.distributed.distro.component.DistroFailedTaskHandler" }, { "name":"com.alibaba.nacos.core.distributed.distro.component.DistroTransportAgent" }, { "name":"com.alibaba.nacos.core.distributed.distro.entity.DistroData", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setContent","parameterTypes":["byte[]"] }, {"name":"setDistroKey","parameterTypes":["com.alibaba.nacos.core.distributed.distro.entity.DistroKey"] }] }, { "name":"com.alibaba.nacos.core.distributed.distro.entity.DistroKey", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setResourceKey","parameterTypes":["java.lang.String"] }, {"name":"setResourceType","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.distributed.distro.component.DistroComponentHolder"] }, {"name":"destroy","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.distributed.distro.task.delay.DistroDelayTaskExecuteEngine" }, { "name":"com.alibaba.nacos.core.distributed.distro.task.execute.DistroExecuteTaskExecuteEngine" }, { "name":"com.alibaba.nacos.core.distributed.id.IdGeneratorManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.distributed.raft.JRaftProtocol", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"aGetData","parameterTypes":["com.alibaba.nacos.consistency.entity.ReadRequest"] }, {"name":"addRequestProcessors","parameterTypes":["java.util.Collection"] }, {"name":"close","parameterTypes":[] }, {"name":"execute","parameterTypes":["java.util.Map"] }, {"name":"getData","parameterTypes":["com.alibaba.nacos.consistency.entity.ReadRequest"] }, {"name":"init","parameterTypes":["com.alibaba.nacos.consistency.Config"] }, {"name":"isLeader","parameterTypes":["java.lang.String"] }, {"name":"memberChange","parameterTypes":["java.util.Set"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"write","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }, {"name":"writeAsync","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }] }, { "name":"com.alibaba.nacos.core.distributed.raft.RaftConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addMembers","parameterTypes":["java.util.Set"] }, {"name":"getData","parameterTypes":[] }, {"name":"getMembers","parameterTypes":[] }, {"name":"getSelfMember","parameterTypes":[] }, {"name":"getVal","parameterTypes":["java.lang.String"] }, {"name":"getValOfDefault","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"removeMembers","parameterTypes":["java.util.Set"] }, {"name":"setMembers","parameterTypes":["java.lang.String","java.util.Set"] }, {"name":"setVal","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.model.request.LogUpdateRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.core.model.request.LookupUpdateRequest" }, { "name":"com.alibaba.nacos.core.model.vo.IdGeneratorVO", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.core.model.vo.IdGeneratorVO$IdInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.core.monitor.GrpcServerThreadPoolMonitor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"configureTasks","parameterTypes":["org.springframework.scheduling.config.ScheduledTaskRegistrar"] }] }, { "name":"com.alibaba.nacos.core.namespace.injector.AbstractNamespaceDetailInjector", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.core.namespace.model.Namespace", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getConfigCount","parameterTypes":[] }, {"name":"getNamespace","parameterTypes":[] }, {"name":"getNamespaceDesc","parameterTypes":[] }, {"name":"getNamespaceShowName","parameterTypes":[] }, {"name":"getQuota","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.namespace.model.TenantInfo" }, { "name":"com.alibaba.nacos.core.namespace.model.form.NamespaceForm" }, { "name":"com.alibaba.nacos.core.namespace.repository.EmbeddedNamespacePersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate"] }, {"name":"findTenantByKp","parameterTypes":["java.lang.String"] }, {"name":"findTenantByKp","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"insertTenantInfoAtomic","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","long"] }, {"name":"isExistTable","parameterTypes":["java.lang.String"] }, {"name":"removeTenantInfoAtomic","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"tenantInfoCountByTenantId","parameterTypes":["java.lang.String"] }, {"name":"updateTenantNameAtomic","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.core.namespace.repository.ExternalNamespacePersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"findTenantByKp","parameterTypes":["java.lang.String"] }, {"name":"findTenantByKp","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"insertTenantInfoAtomic","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","java.lang.String","long"] }, {"name":"isExistTable","parameterTypes":["java.lang.String"] }, {"name":"removeTenantInfoAtomic","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"tenantInfoCountByTenantId","parameterTypes":["java.lang.String"] }, {"name":"updateTenantNameAtomic","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.core.namespace.repository.NamespacePersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.core.namespace.repository.NamespaceRowMapperInjector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.paramcheck.CheckConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"checkerFilter","parameterTypes":["com.alibaba.nacos.core.code.ControllerMethodsCache"] }, {"name":"checkerFilterRegistration","parameterTypes":["com.alibaba.nacos.core.paramcheck.ParamCheckerFilter"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.core.paramcheck.CheckConfiguration$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.core.paramcheck.CheckConfiguration$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.paramcheck.CheckConfiguration$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.core.paramcheck.ExtractorManager$Extractor", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.core.paramcheck.ParamCheckerFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.paramcheck.impl.BatchInstanceRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.paramcheck.impl.ConfigBatchListenRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.paramcheck.impl.ConfigRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.paramcheck.impl.InstanceRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.paramcheck.impl.PersistentInstanceRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.paramcheck.impl.ServiceListRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.paramcheck.impl.ServiceQueryRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.paramcheck.impl.SubscribeServiceRequestParamExtractor" }, { "name":"com.alibaba.nacos.core.remote.AbstractRequestFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.BaseRpcServer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getServicePort","parameterTypes":[] }, {"name":"shutdownServer","parameterTypes":[] }, {"name":"start","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.ClientConnectionEventListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getName","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"setName","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.core.remote.ClientConnectionEventListenerRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.Connection", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.core.remote.ConnectionManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.remote.ClientConnectionEventListenerRegistry"] }, {"name":"start","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.ConnectionMeta", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.core.remote.HealthCheckRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.core.remote.RequestFilters", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.RequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }, {"name":"handleRequest","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.core.remote.RequestHandlerRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }] }, { "name":"com.alibaba.nacos.core.remote.RpcPushService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.core.RpcAckCallbackInitorOrCleaner", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"clientConnected","parameterTypes":["com.alibaba.nacos.core.remote.Connection"] }, {"name":"clientDisConnected","parameterTypes":["com.alibaba.nacos.core.remote.Connection"] }] }, { "name":"com.alibaba.nacos.core.remote.core.ServerLoaderInfoRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.core.remote.core.ServerReloaderRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.core.remote.grpc.BaseGrpcServer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getConnectionType","parameterTypes":[] }, {"name":"reloadProtocolContext","parameterTypes":[] }, {"name":"reloadProtocolNegotiator","parameterTypes":[] }, {"name":"shutdownServer","parameterTypes":[] }, {"name":"startServer","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.grpc.GrpcBiStreamRequestAcceptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"requestBiStream","parameterTypes":["io.grpc.stub.StreamObserver"] }] }, { "name":"com.alibaba.nacos.core.remote.grpc.GrpcClusterServer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getRpcExecutor","parameterTypes":[] }, {"name":"rpcPortOffset","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.grpc.GrpcRequestAcceptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"request","parameterTypes":["com.alibaba.nacos.api.grpc.auto.Payload","io.grpc.stub.StreamObserver"] }] }, { "name":"com.alibaba.nacos.core.remote.grpc.GrpcSdkServer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getRpcExecutor","parameterTypes":[] }, {"name":"rpcPortOffset","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.remote.grpc.RemoteParamCheckFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.core.service.NacosClusterOperationService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager"] }] }, { "name":"com.alibaba.nacos.core.service.NamespaceOperationService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.namespace.repository.NamespacePersistService"] }] }, { "name":"com.alibaba.nacos.core.utils.ReuseHttpServletRequest" }, { "name":"com.alibaba.nacos.naming.ability.NamingAbilityInitializer" }, { "name":"com.alibaba.nacos.naming.cluster.NamingReadinessCheckService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.cluster.ServerStatusManager"] }, {"name":"getModuleName","parameterTypes":[] }, {"name":"readiness","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.cluster.ServerStatus" }, { "name":"com.alibaba.nacos.naming.cluster.ServerStatusManager", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.misc.SwitchDomain"] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.cluster.remote.request.DistroDataRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getDataOperation","parameterTypes":[] }, {"name":"getDistroData","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.cluster.remote.response.DistroDataResponse", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setDistroData","parameterTypes":["com.alibaba.nacos.core.distributed.distro.entity.DistroData"] }] }, { "name":"com.alibaba.nacos.naming.cluster.transport.JacksonSerializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"deserialize","parameterTypes":["byte[]","java.lang.Class"] }, {"name":"serialize","parameterTypes":["java.lang.Object"] }] }, { "name":"com.alibaba.nacos.naming.cluster.transport.Serializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.consistency.ConsistencyService", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.consistency.Datum" }, { "name":"com.alibaba.nacos.naming.consistency.RecordListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientComponentRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager","com.alibaba.nacos.core.distributed.distro.DistroProtocol","com.alibaba.nacos.core.distributed.distro.component.DistroComponentHolder","com.alibaba.nacos.core.distributed.distro.task.DistroTaskEngineHolder","com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate","com.alibaba.nacos.core.cluster.remote.ClusterRpcClientProxy"] }, {"name":"doRegister","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo" }, { "name":"com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.consistency.persistent.PersistentConsistencyServiceDelegateImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.distributed.ProtocolManager"] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"getErrorMsg","parameterTypes":[] }, {"name":"isAvailable","parameterTypes":[] }, {"name":"listen","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.consistency.RecordListener"] }, {"name":"put","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Record"] }, {"name":"remove","parameterTypes":["java.lang.String"] }, {"name":"unListen","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.consistency.RecordListener"] }] }, { "name":"com.alibaba.nacos.naming.consistency.persistent.impl.BasePersistentServiceProcessor" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.CatalogController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"listDetail","parameterTypes":["boolean","java.lang.String","int","int","java.lang.String","java.lang.String","java.lang.String","boolean"] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.CatalogController__Autowiring" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.CatalogController__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.ClusterController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.ClusterOperatorV2Impl"] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.ClusterController__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.HealthController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.HealthController__Autowiring" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.HealthController__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.InstanceController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.InstanceController$1" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.InstanceController__Autowiring" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.InstanceController__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.OperatorController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.misc.SwitchManager","com.alibaba.nacos.naming.cluster.ServerStatusManager","com.alibaba.nacos.naming.misc.SwitchDomain","com.alibaba.nacos.naming.core.DistroMapper","com.alibaba.nacos.naming.core.v2.client.manager.ClientManager"] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.OperatorController__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.ServiceController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.ServiceController__Autowiring" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.ServiceController__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.CatalogControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.CatalogControllerV2__Autowiring" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.CatalogControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.ClientInfoControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.manager.ClientManager","com.alibaba.nacos.core.remote.ConnectionManager","com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager"] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.ClientInfoControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.HealthControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.HealthControllerV2__Autowiring" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.HealthControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.InstanceControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.InstanceControllerV2$1" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.InstanceControllerV2__Autowiring" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.InstanceControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.OperatorControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.misc.SwitchManager","com.alibaba.nacos.naming.cluster.ServerStatusManager","com.alibaba.nacos.naming.misc.SwitchDomain","com.alibaba.nacos.naming.core.v2.client.manager.ClientManager"] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.OperatorControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.ServiceControllerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.ServiceOperatorV2Impl","com.alibaba.nacos.naming.selector.SelectorManager"] }] }, { "name":"com.alibaba.nacos.legacy.adapter.naming.v2.ServiceControllerV2__BeanDefinitions" }, { "name":"com.alibaba.nacos.naming.core.CatalogService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.CatalogServiceV2Impl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.index.ServiceStorage","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager"] }, {"name":"getServiceDetail","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"listAllInstances","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"listInstances","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"pageListService","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","int","java.lang.String","boolean"] }, {"name":"pageListServiceDetail","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","int"] }] }, { "name":"com.alibaba.nacos.naming.core.ClusterOperator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.ClusterOperatorV2Impl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService"] }, {"name":"updateClusterMetadata","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata"] }] }, { "name":"com.alibaba.nacos.naming.core.DistroMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.cluster.ServerMemberManager","com.alibaba.nacos.naming.misc.SwitchDomain"] }, {"name":"ignoreExpireEvent","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }] }, { "name":"com.alibaba.nacos.naming.core.HealthOperator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.HealthOperatorV2Impl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate","com.alibaba.nacos.naming.core.v2.service.ClientOperationServiceProxy"] }, {"name":"updateHealthStatusForPersistentInstance","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","int","boolean"] }] }, { "name":"com.alibaba.nacos.naming.core.InstanceOperator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.InstanceOperatorClientImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate","com.alibaba.nacos.naming.core.v2.service.ClientOperationServiceProxy","com.alibaba.nacos.naming.core.v2.index.ServiceStorage","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.naming.misc.SwitchDomain","com.alibaba.nacos.naming.push.UdpPushService"] }, {"name":"batchDeleteMetadata","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.InstanceOperationInfo","java.util.Map"] }, {"name":"batchUpdateMetadata","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.InstanceOperationInfo","java.util.Map"] }, {"name":"getHeartBeatInterval","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","java.lang.String"] }, {"name":"getInstance","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","java.lang.String","int"] }, {"name":"handleBeat","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","int","java.lang.String","com.alibaba.nacos.naming.healthcheck.RsInfo","com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder"] }, {"name":"listAllInstances","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"listInstance","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.naming.pojo.Subscriber","java.lang.String","boolean"] }, {"name":"patchInstance","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.naming.core.InstancePatchObject"] }, {"name":"registerInstance","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"removeInstance","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }, {"name":"updateInstance","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.api.naming.pojo.Instance"] }] }, { "name":"com.alibaba.nacos.naming.core.InstancePatchObject" }, { "name":"com.alibaba.nacos.naming.core.ServiceOperator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.ServiceOperatorV2Impl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.naming.core.v2.index.ServiceStorage"] }, {"name":"create","parameterTypes":["java.lang.String","java.lang.String","com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata"] }, {"name":"delete","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"listAllNamespace","parameterTypes":[] }, {"name":"listService","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"queryService","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"searchServiceName","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"update","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata"] }] }, { "name":"com.alibaba.nacos.naming.core.SubscribeManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.core.v2.cleaner.AbstractNamingCleaner", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"run","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.core.v2.cleaner.EmptyServiceAutoCleanerV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager","com.alibaba.nacos.naming.core.v2.index.ServiceStorage"] }, {"name":"doClean","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.core.v2.cleaner.ExpiredMetadataCleaner", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService"] }, {"name":"doClean","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.core.v2.cleaner.NamingCleaner", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.v2.client.Client" }, { "name":"com.alibaba.nacos.naming.core.v2.client.ClientAttributes", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.core.v2.client.ClientAttributesHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.core.v2.client.ClientSyncData", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.core.v2.client.ClientSyncDataHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.core.v2.client.ClientSyncDatumSnapshot", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setClientSyncDataList","parameterTypes":["java.util.List"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.client.factory.impl.ConnectionBasedClientFactory" }, { "name":"com.alibaba.nacos.naming.core.v2.client.factory.impl.EphemeralIpPortClientFactory" }, { "name":"com.alibaba.nacos.naming.core.v2.client.factory.impl.PersistentIpPortClientFactory" }, { "name":"com.alibaba.nacos.naming.core.v2.client.impl.ConnectionBasedClient" }, { "name":"com.alibaba.nacos.naming.core.v2.client.impl.IpPortBasedClient" }, { "name":"com.alibaba.nacos.naming.core.v2.client.manager.ClientManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.manager.impl.ConnectionBasedClientManager","com.alibaba.nacos.naming.core.v2.client.manager.impl.EphemeralIpPortClientManager","com.alibaba.nacos.naming.core.v2.client.manager.impl.PersistentIpPortClientManager"] }, {"name":"allClientId","parameterTypes":[] }, {"name":"clientConnected","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"clientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"clientDisconnected","parameterTypes":["java.lang.String"] }, {"name":"contains","parameterTypes":["java.lang.String"] }, {"name":"getClient","parameterTypes":["java.lang.String"] }, {"name":"isResponsibleClient","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"syncClientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"verifyClient","parameterTypes":["com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.client.manager.impl.ConnectionBasedClientManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"allClientId","parameterTypes":[] }, {"name":"clientConnected","parameterTypes":["com.alibaba.nacos.core.remote.Connection"] }, {"name":"clientConnected","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"clientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"clientDisConnected","parameterTypes":["com.alibaba.nacos.core.remote.Connection"] }, {"name":"clientDisconnected","parameterTypes":["java.lang.String"] }, {"name":"contains","parameterTypes":["java.lang.String"] }, {"name":"getClient","parameterTypes":["java.lang.String"] }, {"name":"isResponsibleClient","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"syncClientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"verifyClient","parameterTypes":["com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.client.manager.impl.EphemeralIpPortClientManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.DistroMapper","com.alibaba.nacos.naming.misc.SwitchDomain"] }, {"name":"allClientId","parameterTypes":[] }, {"name":"clientConnected","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"clientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"clientDisconnected","parameterTypes":["java.lang.String"] }, {"name":"contains","parameterTypes":["java.lang.String"] }, {"name":"getClient","parameterTypes":["java.lang.String"] }, {"name":"isResponsibleClient","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"syncClientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"verifyClient","parameterTypes":["com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.client.manager.impl.PersistentIpPortClientManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"allClientId","parameterTypes":[] }, {"name":"clientConnected","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"clientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"clientDisconnected","parameterTypes":["java.lang.String"] }, {"name":"contains","parameterTypes":["java.lang.String"] }, {"name":"getClient","parameterTypes":["java.lang.String"] }, {"name":"isResponsibleClient","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.Client"] }, {"name":"syncClientConnected","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.core.v2.client.ClientAttributes"] }, {"name":"verifyClient","parameterTypes":["com.alibaba.nacos.naming.consistency.ephemeral.distro.v2.DistroClientVerifyInfo"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.event.client.ClientEvent$ClientDisconnectEvent" }, { "name":"com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent" }, { "name":"com.alibaba.nacos.naming.core.v2.event.client.ClientOperationEvent$ClientReleaseEvent" }, { "name":"com.alibaba.nacos.naming.core.v2.event.metadata.MetadataEvent$InstanceMetadataEvent" }, { "name":"com.alibaba.nacos.naming.core.v2.event.metadata.MetadataEvent$ServiceMetadataEvent" }, { "name":"com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"subscribeTypes","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.core.v2.index.ServiceStorage", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager","com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate","com.alibaba.nacos.naming.misc.SwitchDomain","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata" }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.ExpiredMetadataInfo" }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadata", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadataHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.InstanceMetadataProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.core.distributed.ProtocolManager"] }, {"name":"group","parameterTypes":[] }, {"name":"loadSnapshotOperate","parameterTypes":[] }, {"name":"onApply","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }, {"name":"onRequest","parameterTypes":["com.alibaba.nacos.consistency.entity.ReadRequest"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.MetadataOperation" }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"subscribeTypes","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.distributed.ProtocolManager"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadata" }, { "name":"com.alibaba.nacos.naming.core.v2.metadata.ServiceMetadataProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.core.distributed.ProtocolManager","com.alibaba.nacos.naming.core.v2.index.ServiceStorage"] }, {"name":"group","parameterTypes":[] }, {"name":"loadSnapshotOperate","parameterTypes":[] }, {"name":"onApply","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }, {"name":"onRequest","parameterTypes":["com.alibaba.nacos.consistency.entity.ReadRequest"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceData", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.BatchInstanceDataHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.BatchInstancePublishInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.HealthCheckInstancePublishInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.HealthCheckInstancePublishInfoHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.Service", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.core.v2.pojo.ServiceHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.core.v2.service.ClientOperationService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getPublishInfo","parameterTypes":["com.alibaba.nacos.api.naming.pojo.Instance"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.service.ClientOperationServiceProxy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl","com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl"] }, {"name":"batchRegisterInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","java.util.List","java.lang.String"] }, {"name":"deregisterInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.api.naming.pojo.Instance","java.lang.String"] }, {"name":"registerInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.api.naming.pojo.Instance","java.lang.String"] }, {"name":"subscribeService","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.pojo.Subscriber","java.lang.String"] }, {"name":"unsubscribeService","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.pojo.Subscriber","java.lang.String"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate"] }, {"name":"batchRegisterInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","java.util.List","java.lang.String"] }, {"name":"deregisterInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.api.naming.pojo.Instance","java.lang.String"] }, {"name":"registerInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.api.naming.pojo.Instance","java.lang.String"] }, {"name":"subscribeService","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.pojo.Subscriber","java.lang.String"] }, {"name":"unsubscribeService","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.pojo.Subscriber","java.lang.String"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.manager.impl.PersistentIpPortClientManager"] }, {"name":"batchRegisterInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","java.util.List","java.lang.String"] }, {"name":"deregisterInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.api.naming.pojo.Instance","java.lang.String"] }, {"name":"group","parameterTypes":[] }, {"name":"loadSnapshotOperate","parameterTypes":[] }, {"name":"onApply","parameterTypes":["com.alibaba.nacos.consistency.entity.WriteRequest"] }, {"name":"onRequest","parameterTypes":["com.alibaba.nacos.consistency.entity.ReadRequest"] }, {"name":"registerInstance","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.api.naming.pojo.Instance","java.lang.String"] }, {"name":"subscribeService","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.pojo.Subscriber","java.lang.String"] }, {"name":"unsubscribeService","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.pojo.Subscriber","java.lang.String"] }] }, { "name":"com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl$InstanceStoreRequest", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl$InstanceStoreRequestHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.exception.ResponseExceptionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.HealthCheckStatus", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true }, { "name":"com.alibaba.nacos.naming.healthcheck.HealthCheckStatusHessianDeserializer" }, { "name":"com.alibaba.nacos.naming.healthcheck.RsInfo" }, { "name":"com.alibaba.nacos.naming.healthcheck.extend.AbstractHealthCheckProcessorExtend", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.extend.HealthCheckProcessorExtendV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2" }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.HealthStatusSynchronizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.PersistentHealthStatusSynchronizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl"] }, {"name":"instanceHealthStatusChange","parameterTypes":["boolean","com.alibaba.nacos.naming.core.v2.client.Client","com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo"] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.processor.HealthCheckCommonV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.processor.HealthCheckProcessorV2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.processor.HealthCheckProcessorV2Delegate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.healthcheck.extend.HealthCheckExtendProvider","com.alibaba.nacos.naming.healthcheck.extend.HealthCheckProcessorExtendV2"] }, {"name":"addProcessor","parameterTypes":["java.util.Collection"] }, {"name":"getType","parameterTypes":[] }, {"name":"process","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2","com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata"] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.processor.HttpHealthCheckProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.processor.HealthCheckCommonV2","com.alibaba.nacos.naming.misc.SwitchDomain"] }, {"name":"getType","parameterTypes":[] }, {"name":"process","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2","com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata"] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.processor.MysqlHealthCheckProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.processor.HealthCheckCommonV2","com.alibaba.nacos.naming.misc.SwitchDomain"] }, {"name":"getType","parameterTypes":[] }, {"name":"process","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2","com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata"] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.processor.NoneHealthCheckProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }, {"name":"process","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2","com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata"] }] }, { "name":"com.alibaba.nacos.naming.healthcheck.v2.processor.TcpHealthCheckProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.processor.HealthCheckCommonV2","com.alibaba.nacos.naming.misc.SwitchDomain"] }, {"name":"getType","parameterTypes":[] }, {"name":"process","parameterTypes":["com.alibaba.nacos.naming.healthcheck.v2.HealthCheckTaskV2","com.alibaba.nacos.naming.core.v2.pojo.Service","com.alibaba.nacos.naming.core.v2.metadata.ClusterMetadata"] }, {"name":"run","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.misc.GlobalConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.misc.NamingTraceEventInitializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"registerSubscriberForNamingEvent","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.misc.SwitchDomain", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getChecksum","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.misc.SwitchDomain$HealthParams", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.misc.SwitchDomain$HttpHealthParams", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.misc.SwitchDomain$MysqlHealthParams", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.misc.SwitchDomain$TcpHealthParams", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.misc.SwitchManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"interests","parameterTypes":["java.lang.String"] }, {"name":"matchUnlistenKey","parameterTypes":["java.lang.String"] }, {"name":"onChange","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Record"] }, {"name":"onDelete","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.naming.model.form.InstanceForm" }, { "name":"com.alibaba.nacos.naming.model.form.InstanceMetadataBatchOperationForm" }, { "name":"com.alibaba.nacos.naming.model.form.ServiceForm" }, { "name":"com.alibaba.nacos.naming.model.form.UpdateHealthForm" }, { "name":"com.alibaba.nacos.naming.model.form.UpdateSwitchForm" }, { "name":"com.alibaba.nacos.naming.model.vo.InstanceDetailInfoVo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.model.vo.InstanceMetadataBatchOperationVo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.model.vo.MetricsInfoVo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.monitor.MetricsMonitor", "allDeclaredFields":true }, { "name":"com.alibaba.nacos.naming.monitor.NamingDynamicMeterRefreshService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"refreshTopnServiceChangeCount","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.monitor.PerformanceLoggerThread", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"collectMetrics","parameterTypes":[] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.monitor.collector.NamingSubAndPubMetricsCollector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.manager.impl.ConnectionBasedClientManager","com.alibaba.nacos.naming.core.v2.client.manager.impl.EphemeralIpPortClientManager","com.alibaba.nacos.naming.core.v2.client.manager.impl.PersistentIpPortClientManager"] }] }, { "name":"com.alibaba.nacos.naming.monitor.collector.PushPendingTaskCountMetricsCollector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl"] }] }, { "name":"com.alibaba.nacos.naming.monitor.collector.ServiceEventQueueSizeMetricsCollector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.paramcheck.NamingDefaultHttpParamExtractor" }, { "name":"com.alibaba.nacos.naming.paramcheck.NamingInstanceBeatHttpParamExtractor" }, { "name":"com.alibaba.nacos.naming.paramcheck.NamingInstanceListHttpParamExtractor" }, { "name":"com.alibaba.nacos.naming.paramcheck.NamingInstanceMetadataBatchHttpParamExtractor" }, { "name":"com.alibaba.nacos.naming.pojo.ClusterInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.pojo.InstanceOperationInfo" }, { "name":"com.alibaba.nacos.naming.pojo.IpAddressInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.pojo.Record", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.pojo.ServiceDetailInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.pojo.ServiceNameView", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.naming.pojo.ServiceView" }, { "name":"com.alibaba.nacos.naming.pojo.Subscriber" }, { "name":"com.alibaba.nacos.naming.pojo.instance.BeatInfoInstanceBuilder" }, { "name":"com.alibaba.nacos.naming.push.NamingSubscriberService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.push.NamingSubscriberServiceAggregationImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.push.NamingSubscriberServiceLocalImpl","com.alibaba.nacos.core.cluster.ServerMemberManager"] }, {"name":"getFuzzySubscribers","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service"] }, {"name":"getFuzzySubscribers","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"getSubscribers","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service"] }, {"name":"getSubscribers","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.naming.push.NamingSubscriberServiceLocalImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl"] }, {"name":"getFuzzySubscribers","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service"] }, {"name":"getFuzzySubscribers","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"getSubscribers","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service"] }, {"name":"getSubscribers","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.naming.push.UdpPushService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.remote.udp.UdpConnector"] }] }, { "name":"com.alibaba.nacos.naming.push.v2.NamingSubscriberServiceV2Impl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.client.manager.ClientManagerDelegate","com.alibaba.nacos.naming.core.v2.index.ClientServiceIndexesManager","com.alibaba.nacos.naming.core.v2.index.ServiceStorage","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.naming.push.v2.executor.PushExecutorDelegate","com.alibaba.nacos.naming.misc.SwitchDomain"] }, {"name":"getFuzzySubscribers","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service"] }, {"name":"getFuzzySubscribers","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"getSubscribers","parameterTypes":["com.alibaba.nacos.naming.core.v2.pojo.Service"] }, {"name":"getSubscribers","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"subscribeTypes","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.push.v2.PushDataWrapper" }, { "name":"com.alibaba.nacos.naming.push.v2.executor.PushExecutor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.push.v2.executor.PushExecutorDelegate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.push.v2.executor.PushExecutorRpcImpl","com.alibaba.nacos.naming.push.v2.executor.PushExecutorUdpImpl"] }, {"name":"doPush","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Subscriber","com.alibaba.nacos.naming.push.v2.PushDataWrapper"] }, {"name":"doPushWithCallback","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Subscriber","com.alibaba.nacos.naming.push.v2.PushDataWrapper","com.alibaba.nacos.naming.push.v2.task.NamingPushCallback"] }] }, { "name":"com.alibaba.nacos.naming.push.v2.executor.PushExecutorRpcImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.remote.RpcPushService"] }, {"name":"doPush","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Subscriber","com.alibaba.nacos.naming.push.v2.PushDataWrapper"] }, {"name":"doPushWithCallback","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Subscriber","com.alibaba.nacos.naming.push.v2.PushDataWrapper","com.alibaba.nacos.naming.push.v2.task.NamingPushCallback"] }] }, { "name":"com.alibaba.nacos.naming.push.v2.executor.PushExecutorUdpImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.push.UdpPushService"] }, {"name":"doPush","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Subscriber","com.alibaba.nacos.naming.push.v2.PushDataWrapper"] }, {"name":"doPushWithCallback","parameterTypes":["java.lang.String","com.alibaba.nacos.naming.pojo.Subscriber","com.alibaba.nacos.naming.push.v2.PushDataWrapper","com.alibaba.nacos.naming.push.v2.task.NamingPushCallback"] }] }, { "name":"com.alibaba.nacos.naming.push.v2.task.NamingPushCallback" }, { "name":"com.alibaba.nacos.naming.remote.rpc.handler.BatchInstanceRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.naming.remote.rpc.handler.DistroDataRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.core.distributed.distro.DistroProtocol"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.naming.remote.rpc.handler.InstanceRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.naming.remote.rpc.handler.PersistentInstanceRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.naming.remote.rpc.handler.ServiceListRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.naming.remote.rpc.handler.ServiceQueryRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.index.ServiceStorage","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.naming.remote.rpc.handler.SubscribeServiceRequestHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.naming.core.v2.index.ServiceStorage","com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager","com.alibaba.nacos.naming.core.v2.service.impl.EphemeralClientOperationServiceImpl"] }, {"name":"handle","parameterTypes":["com.alibaba.nacos.api.remote.request.Request","com.alibaba.nacos.api.remote.request.RequestMeta"] }] }, { "name":"com.alibaba.nacos.naming.remote.udp.AckEntry" }, { "name":"com.alibaba.nacos.naming.remote.udp.UdpConnector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.selector.LabelSelector", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.selector.NoneSelector", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.selector.SelectorManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.web.CanDistro", "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.naming.web.ClientAttributesFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.web.DistroFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"init","parameterTypes":["jakarta.servlet.FilterConfig"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.web.DistroTagGenerator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.naming.web.DistroTagGeneratorImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getResponsibleTag","parameterTypes":["com.alibaba.nacos.core.utils.ReuseHttpServletRequest"] }] }, { "name":"com.alibaba.nacos.naming.web.NamingConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"clientAttributesFilter","parameterTypes":[] }, {"name":"clientAttributesFilterRegistration","parameterTypes":[] }, {"name":"distroFilter","parameterTypes":[] }, {"name":"distroFilterRegistration","parameterTypes":[] }, {"name":"serviceNameFilter","parameterTypes":[] }, {"name":"serviceNameFilterRegistration","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"trafficReviseFilter","parameterTypes":[] }, {"name":"trafficReviseFilterRegistration","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.web.NamingConfig$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.naming.web.NamingConfig$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.naming.web.NamingConfig$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.naming.web.ServiceNameFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.naming.web.TrafficReviseFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.configuration.DatasourceConfiguration", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.configuration.condition.ConditionDistributedEmbedStorage", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.configuration.condition.ConditionOnEmbeddedStorage", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.configuration.condition.ConditionOnExternalStorage", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.configuration.condition.ConditionStandaloneEmbedStorage", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.datasource.ExternalDataSourceProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setNum","parameterTypes":["java.lang.Integer"] }, {"name":"setPassword","parameterTypes":["java.util.List"] }, {"name":"setUrl","parameterTypes":["java.util.List"] }, {"name":"setUser","parameterTypes":["java.util.List"] }] }, { "name":"com.alibaba.nacos.persistence.model.Page", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getPageItems","parameterTypes":[] }, {"name":"getPageNumber","parameterTypes":[] }, {"name":"getPagesAvailable","parameterTypes":[] }, {"name":"getTotalCount","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.repository.PaginationHelper" }, { "name":"com.alibaba.nacos.persistence.repository.embedded.hook.EmbeddedApplyHook", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.alibaba.nacos.persistence.repository.embedded.operate.BaseDatabaseOperate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"doDataImport","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate","java.util.List"] }, {"name":"queryMany","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate","java.lang.String","java.lang.Object[]"] }, {"name":"queryMany","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate","java.lang.String","java.lang.Object[]","java.lang.Class"] }, {"name":"queryMany","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate","java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryOne","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate","java.lang.String","java.lang.Class"] }, {"name":"queryOne","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate","java.lang.String","java.lang.Object[]","java.lang.Class"] }, {"name":"queryOne","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate","java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"update","parameterTypes":["org.springframework.transaction.support.TransactionTemplate","org.springframework.jdbc.core.JdbcTemplate","java.util.List"] }, {"name":"update","parameterTypes":["org.springframework.transaction.support.TransactionTemplate","org.springframework.jdbc.core.JdbcTemplate","java.util.List","java.util.function.BiConsumer"] }] }, { "name":"com.alibaba.nacos.persistence.repository.embedded.operate.DatabaseOperate", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"blockUpdate","parameterTypes":[] }, {"name":"blockUpdate","parameterTypes":["java.util.function.BiConsumer"] }, {"name":"futureUpdate","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.persistence.repository.embedded.operate.StandaloneDatabaseOperateImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dataImport","parameterTypes":["java.io.File"] }, {"name":"init","parameterTypes":[] }, {"name":"queryMany","parameterTypes":["java.lang.String","java.lang.Object[]"] }, {"name":"queryMany","parameterTypes":["java.lang.String","java.lang.Object[]","java.lang.Class"] }, {"name":"queryMany","parameterTypes":["java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryOne","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"queryOne","parameterTypes":["java.lang.String","java.lang.Object[]","java.lang.Class"] }, {"name":"queryOne","parameterTypes":["java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"update","parameterTypes":["java.util.List"] }, {"name":"update","parameterTypes":["java.util.List","java.util.function.BiConsumer"] }] }, { "name":"com.alibaba.nacos.persistence.repository.embedded.sql.ModifyRequest" }, { "name":"com.alibaba.nacos.plugin.auth.api.IdentityContext" }, { "name":"com.alibaba.nacos.plugin.auth.api.Permission" }, { "name":"com.alibaba.nacos.plugin.auth.api.Resource" }, { "name":"com.alibaba.nacos.plugin.auth.constant.ActionTypes" }, { "name":"com.alibaba.nacos.plugin.auth.exception.AccessException" }, { "name":"com.alibaba.nacos.plugin.auth.impl.CustomAuthenticationProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"authenticate","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"supports","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.JwtAuthenticationEntryPoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"commence","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","org.springframework.security.core.AuthenticationException"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.LdapAuthConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"ldapContextSource","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.LdapAuthPluginService" }, { "name":"com.alibaba.nacos.plugin.auth.impl.LdapAuthenticationProvider" }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosAuthConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment","com.alibaba.nacos.plugin.auth.impl.token.TokenManagerDelegate","com.alibaba.nacos.auth.config.AuthConfigs","com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl","org.springframework.beans.factory.ObjectProvider","com.alibaba.nacos.core.code.ControllerMethodsCache"] }, {"name":"authenticationConfigurer","parameterTypes":[] }, {"name":"authenticationManager","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","com.alibaba.nacos.auth.config.AuthConfigs"] }, {"name":"authenticationManagerBean","parameterTypes":[] }, {"name":"defaultAuthenticationManager","parameterTypes":["com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl","com.alibaba.nacos.plugin.auth.impl.token.TokenManagerDelegate","com.alibaba.nacos.plugin.auth.impl.roles.NacosRoleServiceImpl"] }, {"name":"init","parameterTypes":[] }, {"name":"passwordEncoder","parameterTypes":[] }, {"name":"securityFilterChain","parameterTypes":["org.springframework.security.config.annotation.web.builders.HttpSecurity"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"webSecurityCustomizer","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosAuthConfig$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment","com.alibaba.nacos.plugin.auth.impl.token.TokenManagerDelegate","com.alibaba.nacos.auth.config.AuthConfigs","com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl","org.springframework.beans.factory.ObjectProvider","com.alibaba.nacos.core.code.ControllerMethodsCache"] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosAuthConfig$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosAuthConfig$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosAuthConfig$1", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.SecurityBuilder"] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosAuthManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosAuthPluginService" }, { "name":"com.alibaba.nacos.plugin.auth.impl.NacosLdapContextSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.authenticate.AbstractAuthenticationManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"authenticate","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"authenticate","parameterTypes":["java.lang.String"] }, {"name":"authenticate","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"authorize","parameterTypes":["com.alibaba.nacos.plugin.auth.api.Permission","com.alibaba.nacos.plugin.auth.impl.users.NacosUser"] }, {"name":"hasGlobalAdminRole","parameterTypes":[] }, {"name":"hasGlobalAdminRole","parameterTypes":["com.alibaba.nacos.plugin.auth.impl.users.NacosUser"] }, {"name":"hasGlobalAdminRole","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.authenticate.AuthenticationManagerDelegator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"authenticate","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"authenticate","parameterTypes":["java.lang.String"] }, {"name":"authenticate","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"authorize","parameterTypes":["com.alibaba.nacos.plugin.auth.api.Permission","com.alibaba.nacos.plugin.auth.impl.users.NacosUser"] }, {"name":"close","parameterTypes":[] }, {"name":"hasGlobalAdminRole","parameterTypes":[] }, {"name":"hasGlobalAdminRole","parameterTypes":["com.alibaba.nacos.plugin.auth.impl.users.NacosUser"] }, {"name":"hasGlobalAdminRole","parameterTypes":["java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.authenticate.DefaultAuthenticationManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.authenticate.IAuthenticationManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.configuration.ConditionOnLdapAuth", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.PermissionController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.PermissionController__Autowiring" }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.PermissionController__BeanDefinitions" }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.RoleController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.RoleController__Autowiring" }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.RoleController__BeanDefinitions" }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.UserController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"login","parameterTypes":["java.lang.String","java.lang.String","jakarta.servlet.http.HttpServletResponse","jakarta.servlet.http.HttpServletRequest"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.UserController__Autowiring" }, { "name":"com.alibaba.nacos.plugin.auth.impl.controller.UserController__BeanDefinitions" }, { "name":"com.alibaba.nacos.plugin.auth.impl.jwt.NacosJwtPayload", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getExp","parameterTypes":[] }, {"name":"getSub","parameterTypes":[] }, {"name":"setExp","parameterTypes":["long"] }, {"name":"setSub","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.AuthPaginationHelper" }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.EmbeddedPermissionPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addPermission","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"deletePermission","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findPermissionsLike4Page","parameterTypes":["java.lang.String","int","int"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getPermissions","parameterTypes":["java.lang.String","int","int"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.EmbeddedRolePersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addRole","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"deleteRole","parameterTypes":["java.lang.String"] }, {"name":"deleteRole","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"findRolesLike4Page","parameterTypes":["java.lang.String","java.lang.String","int","int"] }, {"name":"findRolesLikeRoleName","parameterTypes":["java.lang.String"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getRoles","parameterTypes":["int","int"] }, {"name":"getRolesByUserNameAndRoleName","parameterTypes":["java.lang.String","java.lang.String","int","int"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.EmbeddedUserPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"createUser","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"deleteUser","parameterTypes":["java.lang.String"] }, {"name":"findUserByUsername","parameterTypes":["java.lang.String"] }, {"name":"findUserLikeUsername","parameterTypes":["java.lang.String"] }, {"name":"findUsersLike4Page","parameterTypes":["java.lang.String","int","int"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getUsers","parameterTypes":["int","int","java.lang.String"] }, {"name":"updateUserPassword","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.ExternalPermissionPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addPermission","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"deletePermission","parameterTypes":["java.lang.String","java.lang.String","java.lang.String"] }, {"name":"findPermissionsLike4Page","parameterTypes":["java.lang.String","int","int"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getPermissions","parameterTypes":["java.lang.String","int","int"] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.ExternalRolePersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addRole","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"deleteRole","parameterTypes":["java.lang.String"] }, {"name":"deleteRole","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"findRolesLike4Page","parameterTypes":["java.lang.String","java.lang.String","int","int"] }, {"name":"findRolesLikeRoleName","parameterTypes":["java.lang.String"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getRoles","parameterTypes":["int","int"] }, {"name":"getRolesByUserNameAndRoleName","parameterTypes":["java.lang.String","java.lang.String","int","int"] }, {"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.ExternalUserPersistServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"createPaginationHelper","parameterTypes":[] }, {"name":"createUser","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"deleteUser","parameterTypes":["java.lang.String"] }, {"name":"findUserByUsername","parameterTypes":["java.lang.String"] }, {"name":"findUserLikeUsername","parameterTypes":["java.lang.String"] }, {"name":"findUsersLike4Page","parameterTypes":["java.lang.String","int","int"] }, {"name":"generateLikeArgument","parameterTypes":["java.lang.String"] }, {"name":"getUsers","parameterTypes":["int","int","java.lang.String"] }, {"name":"init","parameterTypes":[] }, {"name":"updateUserPassword","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.PermissionInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.PermissionPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.RoleInfo", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.RolePersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.User", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.alibaba.nacos.plugin.auth.impl.persistence.UserPersistService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.alibaba.nacos.plugin.auth.impl.roles.NacosRoleServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"reload","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.token.TokenManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"init","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.token.TokenManagerDelegate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"createToken","parameterTypes":["java.lang.String"] }, {"name":"createToken","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"getAuthentication","parameterTypes":["java.lang.String"] }, {"name":"getTokenTtlInSeconds","parameterTypes":["java.lang.String"] }, {"name":"getTokenValidityInSeconds","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"parseToken","parameterTypes":["java.lang.String"] }, {"name":"validateToken","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.token.impl.CachedJwtTokenManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"cleanExpiredToken","parameterTypes":[] }, {"name":"createToken","parameterTypes":["java.lang.String"] }, {"name":"createToken","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"getAuthentication","parameterTypes":["java.lang.String"] }, {"name":"getTokenTtlInSeconds","parameterTypes":["java.lang.String"] }, {"name":"getTokenValidityInSeconds","parameterTypes":[] }, {"name":"parseToken","parameterTypes":["java.lang.String"] }, {"name":"validateToken","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.token.impl.CachedJwtTokenManager$TokenEntity" }, { "name":"com.alibaba.nacos.plugin.auth.impl.token.impl.JwtTokenManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["com.alibaba.nacos.auth.config.AuthConfigs"] }, {"name":"createToken","parameterTypes":["java.lang.String"] }, {"name":"createToken","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"getAuthentication","parameterTypes":["java.lang.String"] }, {"name":"getTokenTtlInSeconds","parameterTypes":["java.lang.String"] }, {"name":"getTokenValidityInSeconds","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["com.alibaba.nacos.common.notify.Event"] }, {"name":"parseToken","parameterTypes":["java.lang.String"] }, {"name":"subscribeType","parameterTypes":[] }, {"name":"validateToken","parameterTypes":["java.lang.String"] }] }, { "name":"com.alibaba.nacos.plugin.auth.impl.users.NacosUser" }, { "name":"com.alibaba.nacos.plugin.auth.impl.users.NacosUserDetailsServiceImpl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"loadUserByUsername","parameterTypes":["java.lang.String"] }, {"name":"reload","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.plugin.config.constants.ConfigChangePointCutTypes" }, { "name":"com.alibaba.nacos.plugin.config.model.ConfigChangeRequest" }, { "name":"com.alibaba.nacos.plugin.config.model.ConfigChangeResponse" }, { "name":"com.alibaba.nacos.plugin.config.spi.ConfigChangePluginService" }, { "name":"com.alibaba.nacos.plugin.control.connection.response.ConnectionCheckResponse" }, { "name":"com.alibaba.nacos.plugin.control.tps.response.TpsCheckResponse" }, { "name":"com.alibaba.nacos.plugin.datasource.mapper.ConfigInfoMapper" }, { "name":"com.alibaba.nacos.plugin.datasource.mapper.GroupCapacityMapper" }, { "name":"com.alibaba.nacos.plugin.datasource.mapper.TenantCapacityMapper" }, { "name":"com.alibaba.nacos.prometheus.conf.PrometheusSecurityConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"prometheusWebSecurityCustomizer","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.prometheus.conf.PrometheusSecurityConfiguration$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.prometheus.conf.PrometheusSecurityConfiguration$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.prometheus.conf.PrometheusSecurityConfiguration$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.prometheus.exception.PrometheusApiExceptionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.prometheus.filter.PrometheusAuthFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"anonymousAuthenticationFilter","parameterTypes":[] }, {"name":"authorizationFilter","parameterTypes":[] }, {"name":"basicAuthenticationFilter","parameterTypes":["org.springframework.security.authentication.AuthenticationManager"] }, {"name":"exceptionTranslationFilter","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"com.alibaba.nacos.prometheus.filter.PrometheusAuthFilter$$SpringCGLIB$$0", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "fields":[{"name":"CGLIB$FACTORY_DATA"}], "methods":[{"name":"","parameterTypes":[] }, {"name":"CGLIB$SET_STATIC_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }, {"name":"CGLIB$SET_THREAD_CALLBACKS","parameterTypes":["org.springframework.cglib.proxy.Callback[]"] }] }, { "name":"com.alibaba.nacos.prometheus.filter.PrometheusAuthFilter$$SpringCGLIB$$FastClass$$0", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.prometheus.filter.PrometheusAuthFilter$$SpringCGLIB$$FastClass$$1", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.alibaba.nacos.sys.env.EnvModuleStateBuilder" }, { "name":"com.alibaba.nacos.sys.filter.NacosTypeExcludeFilter", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alibaba.nacos.sys.module.ModuleState" }, { "name":"com.alibaba.nacos.sys.utils.ApplicationUtils", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alipay.sofa.jraft.NodeDescribeSignalHandler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alipay.sofa.jraft.NodeMetricsSignalHandler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alipay.sofa.jraft.ThreadPoolMetricsSignalHandler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alipay.sofa.jraft.core.DefaultJRaftServiceFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alipay.sofa.jraft.core.NodeImpl.NodeReadWriteLock" }, { "name":"com.alipay.sofa.jraft.entity.LocalFileMetaOutter$LocalFileMeta", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.entity.LocalStorageOutter$ConfigurationPBMeta", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.entity.LocalStorageOutter$LocalSnapshotPbMeta", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.entity.LocalStorageOutter$LogPBMeta", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.entity.LocalStorageOutter$StablePBMeta", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"getVotedfor","parameterTypes":[] }, {"name":"getVotedforBytes","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"hasVotedfor","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.entity.LocalStorageOutter$StablePBMeta$Builder", "methods":[{"name":"clearTerm","parameterTypes":[] }, {"name":"clearVotedfor","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"getVotedfor","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"hasVotedfor","parameterTypes":[] }, {"name":"setTerm","parameterTypes":["long"] }, {"name":"setVotedfor","parameterTypes":["java.lang.String"] }, {"name":"setVotedforBytes","parameterTypes":["com.google.protobuf.ByteString"] }] }, { "name":"com.alipay.sofa.jraft.entity.RaftOutter$EntryMeta", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.entity.RaftOutter$SnapshotMeta", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getLastIncludedIndex","parameterTypes":[] }, {"name":"getLastIncludedTerm","parameterTypes":[] }, {"name":"getLearners","parameterTypes":["int"] }, {"name":"getLearnersCount","parameterTypes":[] }, {"name":"getLearnersList","parameterTypes":[] }, {"name":"getOldLearners","parameterTypes":["int"] }, {"name":"getOldLearnersCount","parameterTypes":[] }, {"name":"getOldLearnersList","parameterTypes":[] }, {"name":"getOldPeers","parameterTypes":["int"] }, {"name":"getOldPeersCount","parameterTypes":[] }, {"name":"getOldPeersList","parameterTypes":[] }, {"name":"getPeers","parameterTypes":["int"] }, {"name":"getPeersCount","parameterTypes":[] }, {"name":"getPeersList","parameterTypes":[] }, {"name":"hasLastIncludedIndex","parameterTypes":[] }, {"name":"hasLastIncludedTerm","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.entity.RaftOutter$SnapshotMeta$Builder", "methods":[{"name":"addLearners","parameterTypes":["java.lang.String"] }, {"name":"addOldLearners","parameterTypes":["java.lang.String"] }, {"name":"addOldPeers","parameterTypes":["java.lang.String"] }, {"name":"addPeers","parameterTypes":["java.lang.String"] }, {"name":"clearLastIncludedIndex","parameterTypes":[] }, {"name":"clearLastIncludedTerm","parameterTypes":[] }, {"name":"clearLearners","parameterTypes":[] }, {"name":"clearOldLearners","parameterTypes":[] }, {"name":"clearOldPeers","parameterTypes":[] }, {"name":"clearPeers","parameterTypes":[] }, {"name":"getLastIncludedIndex","parameterTypes":[] }, {"name":"getLastIncludedTerm","parameterTypes":[] }, {"name":"getLearners","parameterTypes":["int"] }, {"name":"getLearnersCount","parameterTypes":[] }, {"name":"getLearnersList","parameterTypes":[] }, {"name":"getOldLearners","parameterTypes":["int"] }, {"name":"getOldLearnersCount","parameterTypes":[] }, {"name":"getOldLearnersList","parameterTypes":[] }, {"name":"getOldPeers","parameterTypes":["int"] }, {"name":"getOldPeersCount","parameterTypes":[] }, {"name":"getOldPeersList","parameterTypes":[] }, {"name":"getPeers","parameterTypes":["int"] }, {"name":"getPeersCount","parameterTypes":[] }, {"name":"getPeersList","parameterTypes":[] }, {"name":"hasLastIncludedIndex","parameterTypes":[] }, {"name":"hasLastIncludedTerm","parameterTypes":[] }, {"name":"setLastIncludedIndex","parameterTypes":["long"] }, {"name":"setLastIncludedTerm","parameterTypes":["long"] }, {"name":"setLearners","parameterTypes":["int","java.lang.String"] }, {"name":"setOldLearners","parameterTypes":["int","java.lang.String"] }, {"name":"setOldPeers","parameterTypes":["int","java.lang.String"] }, {"name":"setPeers","parameterTypes":["int","java.lang.String"] }] }, { "name":"com.alipay.sofa.jraft.entity.codec.v2.LogOutter$PBLogEntry", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$AddLearnersRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$AddPeerRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$AddPeerResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$ChangePeersRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$ChangePeersResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$GetLeaderRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$GetLeaderResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getLeaderId","parameterTypes":[] }, {"name":"getLeaderIdBytes","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasLeaderId","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$GetLeaderResponse$Builder", "methods":[{"name":"clearErrorResponse","parameterTypes":[] }, {"name":"clearLeaderId","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getErrorResponseBuilder","parameterTypes":[] }, {"name":"getLeaderId","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasLeaderId","parameterTypes":[] }, {"name":"setErrorResponse","parameterTypes":["com.alipay.sofa.jraft.rpc.RpcRequests$ErrorResponse"] }, {"name":"setLeaderId","parameterTypes":["java.lang.String"] }, {"name":"setLeaderIdBytes","parameterTypes":["com.google.protobuf.ByteString"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$GetPeersRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$GetPeersResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getLearners","parameterTypes":["int"] }, {"name":"getLearnersCount","parameterTypes":[] }, {"name":"getLearnersList","parameterTypes":[] }, {"name":"getPeers","parameterTypes":["int"] }, {"name":"getPeersCount","parameterTypes":[] }, {"name":"getPeersList","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$GetPeersResponse$Builder", "methods":[{"name":"addLearners","parameterTypes":["java.lang.String"] }, {"name":"addPeers","parameterTypes":["java.lang.String"] }, {"name":"clearErrorResponse","parameterTypes":[] }, {"name":"clearLearners","parameterTypes":[] }, {"name":"clearPeers","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getErrorResponseBuilder","parameterTypes":[] }, {"name":"getLearners","parameterTypes":["int"] }, {"name":"getLearnersCount","parameterTypes":[] }, {"name":"getLearnersList","parameterTypes":[] }, {"name":"getPeers","parameterTypes":["int"] }, {"name":"getPeersCount","parameterTypes":[] }, {"name":"getPeersList","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"setErrorResponse","parameterTypes":["com.alipay.sofa.jraft.rpc.RpcRequests$ErrorResponse"] }, {"name":"setLearners","parameterTypes":["int","java.lang.String"] }, {"name":"setPeers","parameterTypes":["int","java.lang.String"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$LearnersOpResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$RemoveLearnersRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$RemovePeerRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$RemovePeerResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$ResetLearnersRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$ResetPeerRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$SnapshotRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.CliRequests$TransferLeaderRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.ProtobufMsgFactory", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequestClosure", "fields":[{"name":"state"}] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$AppendEntriesRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$AppendEntriesRequestHeader", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$AppendEntriesResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getLastLogIndex","parameterTypes":[] }, {"name":"getSuccess","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasLastLogIndex","parameterTypes":[] }, {"name":"hasSuccess","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$AppendEntriesResponse$Builder", "methods":[{"name":"clearErrorResponse","parameterTypes":[] }, {"name":"clearLastLogIndex","parameterTypes":[] }, {"name":"clearSuccess","parameterTypes":[] }, {"name":"clearTerm","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getErrorResponseBuilder","parameterTypes":[] }, {"name":"getLastLogIndex","parameterTypes":[] }, {"name":"getSuccess","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasLastLogIndex","parameterTypes":[] }, {"name":"hasSuccess","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"setErrorResponse","parameterTypes":["com.alipay.sofa.jraft.rpc.RpcRequests$ErrorResponse"] }, {"name":"setLastLogIndex","parameterTypes":["long"] }, {"name":"setSuccess","parameterTypes":["boolean"] }, {"name":"setTerm","parameterTypes":["long"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$ErrorResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"newBuilder","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$GetFileRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$GetFileResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$InstallSnapshotRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$InstallSnapshotResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$PingRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$ReadIndexRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$ReadIndexResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$RequestVoteRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$RequestVoteResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getGranted","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasGranted","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$RequestVoteResponse$Builder", "methods":[{"name":"clearErrorResponse","parameterTypes":[] }, {"name":"clearGranted","parameterTypes":[] }, {"name":"clearTerm","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getErrorResponseBuilder","parameterTypes":[] }, {"name":"getGranted","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasGranted","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"setErrorResponse","parameterTypes":["com.alipay.sofa.jraft.rpc.RpcRequests$ErrorResponse"] }, {"name":"setGranted","parameterTypes":["boolean"] }, {"name":"setTerm","parameterTypes":["long"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$TimeoutNowRequest", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$TimeoutNowResponse", "methods":[{"name":"getDefaultInstance","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getSuccess","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasSuccess","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"parseFrom","parameterTypes":["byte[]"] }] }, { "name":"com.alipay.sofa.jraft.rpc.RpcRequests$TimeoutNowResponse$Builder", "methods":[{"name":"clearErrorResponse","parameterTypes":[] }, {"name":"clearSuccess","parameterTypes":[] }, {"name":"clearTerm","parameterTypes":[] }, {"name":"getErrorResponse","parameterTypes":[] }, {"name":"getErrorResponseBuilder","parameterTypes":[] }, {"name":"getSuccess","parameterTypes":[] }, {"name":"getTerm","parameterTypes":[] }, {"name":"hasErrorResponse","parameterTypes":[] }, {"name":"hasSuccess","parameterTypes":[] }, {"name":"hasTerm","parameterTypes":[] }, {"name":"setErrorResponse","parameterTypes":["com.alipay.sofa.jraft.rpc.RpcRequests$ErrorResponse"] }, {"name":"setSuccess","parameterTypes":["boolean"] }, {"name":"setTerm","parameterTypes":["long"] }] }, { "name":"com.alipay.sofa.jraft.rpc.impl.AbstractClientService", "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"com.alipay.sofa.jraft.rpc.impl.BoltRaftRpcFactory" }, { "name":"com.alipay.sofa.jraft.rpc.impl.GrpcRaftRpcFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.alipay.sofa.jraft.util.concurrent.MpscSingleThreadExecutor", "fields":[{"name":"state"}] }, { "name":"com.alipay.sofa.jraft.util.concurrent.MpscSingleThreadExecutor$Worker", "fields":[{"name":"notifyNeeded"}] }, { "name":"com.alipay.sofa.jraft.util.timer.DefaultRaftTimerFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.caucho.burlap.io.BurlapRemoteObject" }, { "name":"com.caucho.hessian.io.BigDecimalDeserializer", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.caucho.hessian.io.ContextSerializerFactory", "queryAllDeclaredMethods":true }, { "name":"com.caucho.hessian.io.FileDeserializer", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.caucho.hessian.io.HessianRemote", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.caucho.hessian.io.HessianRemoteObject" }, { "name":"com.caucho.hessian.io.LocaleSerializer", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.caucho.hessian.io.ObjectNameDeserializer", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.caucho.hessian.io.RemoteSerializer", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.caucho.hessian.io.SqlDateDeserializer", "methods":[{"name":"","parameterTypes":["java.lang.Class"] }] }, { "name":"com.caucho.hessian.io.StringValueSerializer", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.couchbase.client.java.Bucket" }, { "name":"com.couchbase.client.java.Cluster" }, { "name":"com.datastax.oss.driver.api.core.CqlSession" }, { "name":"com.fasterxml.jackson.annotation.JacksonAnnotation", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonAnyGetter", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonAnySetter", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonAutoDetect$Value" }, { "name":"com.fasterxml.jackson.annotation.JsonAutoDetect$Visibility", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.fasterxml.jackson.annotation.JsonFormat$Feature" }, { "name":"com.fasterxml.jackson.annotation.JsonFormat$Value" }, { "name":"com.fasterxml.jackson.annotation.JsonIgnore", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonInclude", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonInclude$Include" }, { "name":"com.fasterxml.jackson.annotation.JsonInclude$Value" }, { "name":"com.fasterxml.jackson.annotation.JsonProperty", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonPropertyOrder", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonSetter$Value" }, { "name":"com.fasterxml.jackson.annotation.JsonSubTypes", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonSubTypes$Type", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonTypeInfo", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.JsonTypeInfo$As" }, { "name":"com.fasterxml.jackson.annotation.JsonTypeInfo$Id" }, { "name":"com.fasterxml.jackson.annotation.JsonUnwrapped", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.annotation.PropertyAccessor" }, { "name":"com.fasterxml.jackson.core.Base64Variant" }, { "name":"com.fasterxml.jackson.core.FormatSchema" }, { "name":"com.fasterxml.jackson.core.JsonEncoding" }, { "name":"com.fasterxml.jackson.core.JsonFactory" }, { "name":"com.fasterxml.jackson.core.JsonFactory$Feature" }, { "name":"com.fasterxml.jackson.core.JsonGenerator" }, { "name":"com.fasterxml.jackson.core.JsonGenerator$Feature" }, { "name":"com.fasterxml.jackson.core.JsonParser" }, { "name":"com.fasterxml.jackson.core.JsonParser$Feature" }, { "name":"com.fasterxml.jackson.core.JsonParser$NumberType" }, { "name":"com.fasterxml.jackson.core.JsonProcessingException" }, { "name":"com.fasterxml.jackson.core.JsonToken" }, { "name":"com.fasterxml.jackson.core.ObjectCodec", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getJsonFactory","parameterTypes":[] }, {"name":"readValues","parameterTypes":["com.fasterxml.jackson.core.JsonParser","com.fasterxml.jackson.core.type.ResolvedType"] }, {"name":"readValues","parameterTypes":["com.fasterxml.jackson.core.JsonParser","com.fasterxml.jackson.core.type.TypeReference"] }, {"name":"readValues","parameterTypes":["com.fasterxml.jackson.core.JsonParser","java.lang.Class"] }] }, { "name":"com.fasterxml.jackson.core.PrettyPrinter" }, { "name":"com.fasterxml.jackson.core.StreamReadFeature" }, { "name":"com.fasterxml.jackson.core.StreamWriteFeature" }, { "name":"com.fasterxml.jackson.core.TreeCodec", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.core.TreeNode", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"at","parameterTypes":["com.fasterxml.jackson.core.JsonPointer"] }, {"name":"at","parameterTypes":["java.lang.String"] }, {"name":"get","parameterTypes":["int"] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"path","parameterTypes":["int"] }, {"name":"path","parameterTypes":["java.lang.String"] }] }, { "name":"com.fasterxml.jackson.core.Version" }, { "name":"com.fasterxml.jackson.core.Versioned", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.fasterxml.jackson.core.exc.StreamReadException" }, { "name":"com.fasterxml.jackson.core.exc.StreamWriteException" }, { "name":"com.fasterxml.jackson.core.io.CharacterEscapes" }, { "name":"com.fasterxml.jackson.core.type.ResolvedType" }, { "name":"com.fasterxml.jackson.core.type.TypeReference" }, { "name":"com.fasterxml.jackson.databind.AnnotationIntrospector" }, { "name":"com.fasterxml.jackson.databind.BeanProperty" }, { "name":"com.fasterxml.jackson.databind.DatabindException" }, { "name":"com.fasterxml.jackson.databind.DeserializationConfig" }, { "name":"com.fasterxml.jackson.databind.DeserializationContext" }, { "name":"com.fasterxml.jackson.databind.DeserializationFeature" }, { "name":"com.fasterxml.jackson.databind.InjectableValues" }, { "name":"com.fasterxml.jackson.databind.JavaType" }, { "name":"com.fasterxml.jackson.databind.JsonDeserializer" }, { "name":"com.fasterxml.jackson.databind.JsonMappingException" }, { "name":"com.fasterxml.jackson.databind.JsonNode", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"get","parameterTypes":["int"] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"numberNode","parameterTypes":["byte"] }, {"name":"numberNode","parameterTypes":["double"] }, {"name":"numberNode","parameterTypes":["float"] }, {"name":"numberNode","parameterTypes":["int"] }, {"name":"numberNode","parameterTypes":["long"] }, {"name":"numberNode","parameterTypes":["short"] }, {"name":"path","parameterTypes":["int"] }, {"name":"path","parameterTypes":["java.lang.String"] }] }, { "name":"com.fasterxml.jackson.databind.JsonSerializable", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.fasterxml.jackson.databind.JsonSerializable$Base", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"at","parameterTypes":["com.fasterxml.jackson.core.JsonPointer"] }, {"name":"at","parameterTypes":["java.lang.String"] }, {"name":"get","parameterTypes":["int"] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"numberNode","parameterTypes":["byte"] }, {"name":"numberNode","parameterTypes":["double"] }, {"name":"numberNode","parameterTypes":["float"] }, {"name":"numberNode","parameterTypes":["int"] }, {"name":"numberNode","parameterTypes":["long"] }, {"name":"numberNode","parameterTypes":["short"] }, {"name":"path","parameterTypes":["int"] }, {"name":"path","parameterTypes":["java.lang.String"] }] }, { "name":"com.fasterxml.jackson.databind.JsonSerializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getDelegatee","parameterTypes":[] }, {"name":"isEmpty","parameterTypes":["java.lang.Object"] }, {"name":"isUnwrappingSerializer","parameterTypes":[] }, {"name":"properties","parameterTypes":[] }, {"name":"replaceDelegatee","parameterTypes":["com.fasterxml.jackson.databind.JsonSerializer"] }, {"name":"unwrappingSerializer","parameterTypes":["com.fasterxml.jackson.databind.util.NameTransformer"] }, {"name":"usesObjectId","parameterTypes":[] }, {"name":"withFilterId","parameterTypes":["java.lang.Object"] }, {"name":"withIgnoredProperties","parameterTypes":["java.util.Set"] }] }, { "name":"com.fasterxml.jackson.databind.KeyDeserializer" }, { "name":"com.fasterxml.jackson.databind.MapperFeature" }, { "name":"com.fasterxml.jackson.databind.MappingIterator" }, { "name":"com.fasterxml.jackson.databind.Module", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getDependencies","parameterTypes":[] }] }, { "name":"com.fasterxml.jackson.databind.Module$SetupContext" }, { "name":"com.fasterxml.jackson.databind.ObjectMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"createArrayNode","parameterTypes":[] }, {"name":"createObjectNode","parameterTypes":[] }, {"name":"getFactory","parameterTypes":[] }, {"name":"missingNode","parameterTypes":[] }, {"name":"nullNode","parameterTypes":[] }, {"name":"readTree","parameterTypes":["com.fasterxml.jackson.core.JsonParser"] }, {"name":"readValue","parameterTypes":["com.fasterxml.jackson.core.JsonParser","com.fasterxml.jackson.core.type.ResolvedType"] }, {"name":"readValue","parameterTypes":["com.fasterxml.jackson.core.JsonParser","com.fasterxml.jackson.core.type.TypeReference"] }, {"name":"readValue","parameterTypes":["com.fasterxml.jackson.core.JsonParser","java.lang.Class"] }, {"name":"readValues","parameterTypes":["com.fasterxml.jackson.core.JsonParser","com.fasterxml.jackson.core.type.ResolvedType"] }, {"name":"readValues","parameterTypes":["com.fasterxml.jackson.core.JsonParser","com.fasterxml.jackson.core.type.TypeReference"] }, {"name":"readValues","parameterTypes":["com.fasterxml.jackson.core.JsonParser","java.lang.Class"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"treeAsTokens","parameterTypes":["com.fasterxml.jackson.core.TreeNode"] }, {"name":"treeToValue","parameterTypes":["com.fasterxml.jackson.core.TreeNode","java.lang.Class"] }, {"name":"version","parameterTypes":[] }, {"name":"writeTree","parameterTypes":["com.fasterxml.jackson.core.JsonGenerator","com.fasterxml.jackson.core.TreeNode"] }, {"name":"writeValue","parameterTypes":["com.fasterxml.jackson.core.JsonGenerator","java.lang.Object"] }] }, { "name":"com.fasterxml.jackson.databind.ObjectMapper$DefaultTyping" }, { "name":"com.fasterxml.jackson.databind.ObjectReader" }, { "name":"com.fasterxml.jackson.databind.ObjectWriter" }, { "name":"com.fasterxml.jackson.databind.PropertyNamingStrategies", "allDeclaredFields":true }, { "name":"com.fasterxml.jackson.databind.PropertyNamingStrategy" }, { "name":"com.fasterxml.jackson.databind.SerializationConfig" }, { "name":"com.fasterxml.jackson.databind.SerializationFeature" }, { "name":"com.fasterxml.jackson.databind.SerializerProvider" }, { "name":"com.fasterxml.jackson.databind.annotation.JacksonStdImpl", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.databind.annotation.JsonSerialize", "queryAllDeclaredMethods":true }, { "name":"com.fasterxml.jackson.databind.cfg.CacheProvider" }, { "name":"com.fasterxml.jackson.databind.cfg.ConstructorDetector" }, { "name":"com.fasterxml.jackson.databind.cfg.ContextAttributes" }, { "name":"com.fasterxml.jackson.databind.cfg.DatatypeFeature" }, { "name":"com.fasterxml.jackson.databind.cfg.HandlerInstantiator" }, { "name":"com.fasterxml.jackson.databind.cfg.MutableCoercionConfig" }, { "name":"com.fasterxml.jackson.databind.cfg.MutableConfigOverride" }, { "name":"com.fasterxml.jackson.databind.deser.BeanDeserializerModifier" }, { "name":"com.fasterxml.jackson.databind.deser.DefaultDeserializationContext" }, { "name":"com.fasterxml.jackson.databind.deser.DeserializationProblemHandler" }, { "name":"com.fasterxml.jackson.databind.deser.ValueInstantiator" }, { "name":"com.fasterxml.jackson.databind.ext.Java7SupportImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.fasterxml.jackson.databind.introspect.AccessorNamingStrategy$Provider" }, { "name":"com.fasterxml.jackson.databind.introspect.ClassIntrospector" }, { "name":"com.fasterxml.jackson.databind.introspect.ClassIntrospector$MixInResolver" }, { "name":"com.fasterxml.jackson.databind.introspect.VisibilityChecker" }, { "name":"com.fasterxml.jackson.databind.json.JsonMapper$Builder" }, { "name":"com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatTypes" }, { "name":"com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitable", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper" }, { "name":"com.fasterxml.jackson.databind.jsonFormatVisitors.JsonValueFormat" }, { "name":"com.fasterxml.jackson.databind.jsonschema.JsonSchema" }, { "name":"com.fasterxml.jackson.databind.jsonschema.SchemaAware", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.fasterxml.jackson.databind.jsontype.NamedType" }, { "name":"com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator" }, { "name":"com.fasterxml.jackson.databind.jsontype.SubtypeResolver" }, { "name":"com.fasterxml.jackson.databind.jsontype.TypeResolverBuilder" }, { "name":"com.fasterxml.jackson.databind.jsontype.TypeSerializer" }, { "name":"com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver" }, { "name":"com.fasterxml.jackson.databind.module.SimpleDeserializers" }, { "name":"com.fasterxml.jackson.databind.module.SimpleKeyDeserializers" }, { "name":"com.fasterxml.jackson.databind.module.SimpleModule", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addAbstractTypeMapping","parameterTypes":["java.lang.Class","java.lang.Class"] }, {"name":"addDeserializer","parameterTypes":["java.lang.Class","com.fasterxml.jackson.databind.JsonDeserializer"] }, {"name":"addKeyDeserializer","parameterTypes":["java.lang.Class","com.fasterxml.jackson.databind.KeyDeserializer"] }, {"name":"addKeySerializer","parameterTypes":["java.lang.Class","com.fasterxml.jackson.databind.JsonSerializer"] }, {"name":"addSerializer","parameterTypes":["com.fasterxml.jackson.databind.JsonSerializer"] }, {"name":"addSerializer","parameterTypes":["java.lang.Class","com.fasterxml.jackson.databind.JsonSerializer"] }, {"name":"addValueInstantiator","parameterTypes":["java.lang.Class","com.fasterxml.jackson.databind.deser.ValueInstantiator"] }, {"name":"getModuleName","parameterTypes":[] }, {"name":"getTypeId","parameterTypes":[] }, {"name":"registerSubtypes","parameterTypes":["java.util.Collection"] }, {"name":"registerSubtypes","parameterTypes":["com.fasterxml.jackson.databind.jsontype.NamedType[]"] }, {"name":"registerSubtypes","parameterTypes":["java.lang.Class[]"] }, {"name":"setAbstractTypes","parameterTypes":["com.fasterxml.jackson.databind.module.SimpleAbstractTypeResolver"] }, {"name":"setDeserializerModifier","parameterTypes":["com.fasterxml.jackson.databind.deser.BeanDeserializerModifier"] }, {"name":"setDeserializers","parameterTypes":["com.fasterxml.jackson.databind.module.SimpleDeserializers"] }, {"name":"setKeyDeserializers","parameterTypes":["com.fasterxml.jackson.databind.module.SimpleKeyDeserializers"] }, {"name":"setKeySerializers","parameterTypes":["com.fasterxml.jackson.databind.module.SimpleSerializers"] }, {"name":"setMixInAnnotation","parameterTypes":["java.lang.Class","java.lang.Class"] }, {"name":"setSerializerModifier","parameterTypes":["com.fasterxml.jackson.databind.ser.BeanSerializerModifier"] }, {"name":"setSerializers","parameterTypes":["com.fasterxml.jackson.databind.module.SimpleSerializers"] }, {"name":"setValueInstantiators","parameterTypes":["com.fasterxml.jackson.databind.module.SimpleValueInstantiators"] }, {"name":"setupModule","parameterTypes":["com.fasterxml.jackson.databind.Module$SetupContext"] }, {"name":"version","parameterTypes":[] }] }, { "name":"com.fasterxml.jackson.databind.module.SimpleSerializers" }, { "name":"com.fasterxml.jackson.databind.module.SimpleValueInstantiators" }, { "name":"com.fasterxml.jackson.databind.node.ArrayNode" }, { "name":"com.fasterxml.jackson.databind.node.BaseJsonNode", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"get","parameterTypes":["int"] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"numberNode","parameterTypes":["byte"] }, {"name":"numberNode","parameterTypes":["double"] }, {"name":"numberNode","parameterTypes":["float"] }, {"name":"numberNode","parameterTypes":["int"] }, {"name":"numberNode","parameterTypes":["long"] }, {"name":"numberNode","parameterTypes":["short"] }, {"name":"path","parameterTypes":["int"] }, {"name":"path","parameterTypes":["java.lang.String"] }] }, { "name":"com.fasterxml.jackson.databind.node.ContainerNode", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"get","parameterTypes":["int"] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"path","parameterTypes":["int"] }, {"name":"path","parameterTypes":["java.lang.String"] }] }, { "name":"com.fasterxml.jackson.databind.node.JsonNodeCreator", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"numberNode","parameterTypes":["byte"] }, {"name":"numberNode","parameterTypes":["double"] }, {"name":"numberNode","parameterTypes":["float"] }, {"name":"numberNode","parameterTypes":["int"] }, {"name":"numberNode","parameterTypes":["long"] }, {"name":"numberNode","parameterTypes":["short"] }] }, { "name":"com.fasterxml.jackson.databind.node.JsonNodeFactory" }, { "name":"com.fasterxml.jackson.databind.node.JsonNodeType", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.fasterxml.jackson.databind.node.ObjectNode", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.fasterxml.jackson.databind.ser.BasicSerializerFactory" }, { "name":"com.fasterxml.jackson.databind.ser.BeanSerializerModifier" }, { "name":"com.fasterxml.jackson.databind.ser.DefaultSerializerProvider" }, { "name":"com.fasterxml.jackson.databind.ser.FilterProvider" }, { "name":"com.fasterxml.jackson.databind.ser.PropertyFilter" }, { "name":"com.fasterxml.jackson.databind.ser.SerializerFactory" }, { "name":"com.fasterxml.jackson.databind.ser.std.ClassSerializer", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.fasterxml.jackson.databind.ser.std.StdSerializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getSchema","parameterTypes":["com.fasterxml.jackson.databind.SerializerProvider","java.lang.reflect.Type","boolean"] }, {"name":"handledType","parameterTypes":[] }, {"name":"wrapAndThrow","parameterTypes":["com.fasterxml.jackson.databind.SerializerProvider","java.lang.Throwable","java.lang.Object","int"] }, {"name":"wrapAndThrow","parameterTypes":["com.fasterxml.jackson.databind.SerializerProvider","java.lang.Throwable","java.lang.Object","java.lang.String"] }] }, { "name":"com.fasterxml.jackson.databind.ser.std.ToStringSerializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"valueToString","parameterTypes":["java.lang.Object"] }] }, { "name":"com.fasterxml.jackson.databind.ser.std.ToStringSerializerBase", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"acceptJsonFormatVisitor","parameterTypes":["com.fasterxml.jackson.databind.jsonFormatVisitors.JsonFormatVisitorWrapper","com.fasterxml.jackson.databind.JavaType"] }, {"name":"getSchema","parameterTypes":["com.fasterxml.jackson.databind.SerializerProvider","java.lang.reflect.Type"] }, {"name":"isEmpty","parameterTypes":["com.fasterxml.jackson.databind.SerializerProvider","java.lang.Object"] }, {"name":"serialize","parameterTypes":["java.lang.Object","com.fasterxml.jackson.core.JsonGenerator","com.fasterxml.jackson.databind.SerializerProvider"] }, {"name":"serializeWithType","parameterTypes":["java.lang.Object","com.fasterxml.jackson.core.JsonGenerator","com.fasterxml.jackson.databind.SerializerProvider","com.fasterxml.jackson.databind.jsontype.TypeSerializer"] }] }, { "name":"com.fasterxml.jackson.databind.type.LogicalType" }, { "name":"com.fasterxml.jackson.databind.type.TypeFactory" }, { "name":"com.fasterxml.jackson.databind.util.NameTransformer" }, { "name":"com.fasterxml.jackson.dataformat.cbor.CBORFactory" }, { "name":"com.fasterxml.jackson.dataformat.smile.SmileFactory" }, { "name":"com.fasterxml.jackson.dataformat.xml.XmlMapper" }, { "name":"com.fasterxml.jackson.datatype.jdk8.Jdk8Module", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.fasterxml.jackson.datatype.jsr310.JavaTimeModule", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.fasterxml.jackson.module.kotlin.KotlinModule" }, { "name":"com.fasterxml.jackson.module.paramnames.ParameterNamesModule", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"hashCode","parameterTypes":[] }, {"name":"setupModule","parameterTypes":["com.fasterxml.jackson.databind.Module$SetupContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.github.benmanes.caffeine.cache.Cache" }, { "name":"com.github.benmanes.caffeine.cache.Caffeine" }, { "name":"com.google.common.util.concurrent.AbstractFuture", "fields":[{"name":"listeners"}, {"name":"value"}, {"name":"waiters"}] }, { "name":"com.google.common.util.concurrent.AbstractFuture$Waiter", "fields":[{"name":"next"}, {"name":"thread"}] }, { "name":"com.google.gson.ExclusionStrategy" }, { "name":"com.google.gson.FieldNamingPolicy" }, { "name":"com.google.gson.FieldNamingStrategy" }, { "name":"com.google.gson.Gson", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] }, { "name":"com.google.gson.GsonBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"com.google.gson.JsonElement" }, { "name":"com.google.gson.JsonIOException" }, { "name":"com.google.gson.JsonSyntaxException" }, { "name":"com.google.gson.LongSerializationPolicy" }, { "name":"com.google.gson.ToNumberStrategy" }, { "name":"com.google.gson.TypeAdapter" }, { "name":"com.google.gson.TypeAdapterFactory" }, { "name":"com.google.gson.internal.Excluder" }, { "name":"com.google.gson.reflect.TypeToken" }, { "name":"com.google.gson.stream.JsonReader" }, { "name":"com.google.gson.stream.JsonWriter" }, { "name":"com.google.protobuf.Any", "methods":[{"name":"getTypeUrl","parameterTypes":[] }, {"name":"getTypeUrlBytes","parameterTypes":[] }, {"name":"getValue","parameterTypes":[] }, {"name":"newBuilder","parameterTypes":[] }] }, { "name":"com.google.protobuf.Any$Builder", "methods":[{"name":"clearTypeUrl","parameterTypes":[] }, {"name":"clearValue","parameterTypes":[] }, {"name":"getTypeUrl","parameterTypes":[] }, {"name":"getValue","parameterTypes":[] }, {"name":"setTypeUrl","parameterTypes":["java.lang.String"] }, {"name":"setTypeUrlBytes","parameterTypes":["com.google.protobuf.ByteString"] }, {"name":"setValue","parameterTypes":["com.google.protobuf.ByteString"] }] }, { "name":"com.google.protobuf.ExtensionRegistry", "methods":[{"name":"getEmptyRegistry","parameterTypes":[] }] }, { "name":"com.hazelcast.core.Hazelcast" }, { "name":"com.hazelcast.core.HazelcastInstance" }, { "name":"com.hazelcast.spring.cache.HazelcastCache" }, { "name":"com.ibm.lang.management.OperatingSystemMXBean" }, { "name":"com.intellij.rt.execution.application.AppMainV2$Agent", "methods":[{"name":"premain","parameterTypes":["java.lang.String","java.lang.instrument.Instrumentation"] }] }, { "name":"com.lmax.disruptor.AbstractSequencer", "fields":[{"name":"gatingSequences"}] }, { "name":"com.lmax.disruptor.Value", "fields":[{"name":"value"}] }, { "name":"com.mchange.v2.c3p0.ComboPooledDataSource" }, { "name":"com.mongodb.MongoClientSettings" }, { "name":"com.mongodb.client.MongoClient" }, { "name":"com.mongodb.reactivestreams.client.MongoClient" }, { "name":"com.mysql.cj.conf.url.SingleConnectionUrl", "methods":[{"name":"","parameterTypes":["com.mysql.cj.conf.ConnectionUrlParser","java.util.Properties"] }] }, { "name":"com.mysql.cj.exceptions.CJException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, { "name":"com.mysql.cj.exceptions.UnableToConnectException", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, { "name":"com.mysql.cj.jdbc.AbandonedConnectionCleanupThread" }, { "name":"com.mysql.cj.jdbc.Driver", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.mysql.cj.log.StandardLogger", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, { "name":"com.mysql.cj.protocol.StandardSocketFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.querydsl.core.Query" }, { "name":"com.rabbitmq.client.Channel" }, { "name":"com.rabbitmq.client.ConnectionFactory" }, { "name":"com.rometools.rome.feed.WireFeed" }, { "name":"com.samskivert.mustache.Mustache" }, { "name":"com.samskivert.mustache.Template" }, { "name":"com.sendgrid.SendGrid" }, { "name":"com.sun.crypto.provider.AESCipher$General", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.ARCFOURCipher", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.ChaCha20Cipher$ChaCha20Poly1305", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.DESCipher", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.DESedeCipher", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.GaloisCounterMode$AESGCM", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.HmacCore$HmacSHA512", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.crypto.provider.PBKDF2Core$HmacSHA256", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.sun.jmx.mbeanserver.JmxMBeanServer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.NotificationListener","javax.management.NotificationFilter","java.lang.Object"] }, {"name":"addNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.ObjectName","javax.management.NotificationFilter","java.lang.Object"] }, {"name":"close","parameterTypes":[] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName"] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName","javax.management.ObjectName"] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName","javax.management.ObjectName","java.lang.Object[]","java.lang.String[]"] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName","java.lang.Object[]","java.lang.String[]"] }, {"name":"deserialize","parameterTypes":["java.lang.String","javax.management.ObjectName","byte[]"] }, {"name":"deserialize","parameterTypes":["java.lang.String","byte[]"] }, {"name":"deserialize","parameterTypes":["javax.management.ObjectName","byte[]"] }, {"name":"getAttribute","parameterTypes":["javax.management.ObjectName","java.lang.String"] }, {"name":"getAttributes","parameterTypes":["javax.management.ObjectName","java.lang.String[]"] }, {"name":"getClassLoader","parameterTypes":["javax.management.ObjectName"] }, {"name":"getClassLoaderFor","parameterTypes":["javax.management.ObjectName"] }, {"name":"getClassLoaderRepository","parameterTypes":[] }, {"name":"getDefaultDomain","parameterTypes":[] }, {"name":"getDomains","parameterTypes":[] }, {"name":"getMBeanCount","parameterTypes":[] }, {"name":"getMBeanInfo","parameterTypes":["javax.management.ObjectName"] }, {"name":"getMBeanInstantiator","parameterTypes":[] }, {"name":"getMBeanServerDelegate","parameterTypes":[] }, {"name":"getMBeanServerInterceptor","parameterTypes":[] }, {"name":"getObjectInstance","parameterTypes":["javax.management.ObjectName"] }, {"name":"instantiate","parameterTypes":["java.lang.String"] }, {"name":"instantiate","parameterTypes":["java.lang.String","javax.management.ObjectName"] }, {"name":"instantiate","parameterTypes":["java.lang.String","javax.management.ObjectName","java.lang.Object[]","java.lang.String[]"] }, {"name":"instantiate","parameterTypes":["java.lang.String","java.lang.Object[]","java.lang.String[]"] }, {"name":"interceptorsEnabled","parameterTypes":[] }, {"name":"invoke","parameterTypes":["javax.management.ObjectName","java.lang.String","java.lang.Object[]","java.lang.String[]"] }, {"name":"isInstanceOf","parameterTypes":["javax.management.ObjectName","java.lang.String"] }, {"name":"isRegistered","parameterTypes":["javax.management.ObjectName"] }, {"name":"queryMBeans","parameterTypes":["javax.management.ObjectName","javax.management.QueryExp"] }, {"name":"queryNames","parameterTypes":["javax.management.ObjectName","javax.management.QueryExp"] }, {"name":"registerMBean","parameterTypes":["java.lang.Object","javax.management.ObjectName"] }, {"name":"removeNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.NotificationListener"] }, {"name":"removeNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.NotificationListener","javax.management.NotificationFilter","java.lang.Object"] }, {"name":"removeNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.ObjectName"] }, {"name":"removeNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.ObjectName","javax.management.NotificationFilter","java.lang.Object"] }, {"name":"setAttribute","parameterTypes":["javax.management.ObjectName","javax.management.Attribute"] }, {"name":"setAttributes","parameterTypes":["javax.management.ObjectName","javax.management.AttributeList"] }, {"name":"setMBeanServerInterceptor","parameterTypes":["javax.management.MBeanServer"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"unregisterMBean","parameterTypes":["javax.management.ObjectName"] }] }, { "name":"com.sun.jmx.mbeanserver.MBeanInstantiator" }, { "name":"com.sun.jmx.mbeanserver.SunJmxMBeanServer", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"com.sun.jndi.dns.DnsContextFactory" }, { "name":"com.sun.jndi.ldap.LdapCtxFactory" }, { "name":"com.sun.jndi.ldap.ctl.PagedResultsControl" }, { "name":"com.sun.jndi.ldap.ctl.PagedResultsResponseControl" }, { "name":"com.sun.jndi.ldap.ctl.SortControl" }, { "name":"com.sun.jndi.ldap.ctl.SortResponseControl" }, { "name":"com.sun.management.GarbageCollectionNotificationInfo" }, { "name":"com.sun.management.GarbageCollectorMXBean", "queryAllPublicMethods":true }, { "name":"com.sun.management.GcInfo", "queryAllPublicMethods":true, "fields":[{"name":"builder"}, {"name":"extAttributes"}], "methods":[{"name":"getMemoryUsageBeforeGc","parameterTypes":[] }] }, { "name":"com.sun.management.HotSpotDiagnosticMXBean", "queryAllPublicMethods":true }, { "name":"com.sun.management.OperatingSystemMXBean", "queryAllPublicMethods":true, "methods":[{"name":"getCpuLoad","parameterTypes":[] }, {"name":"getProcessCpuLoad","parameterTypes":[] }] }, { "name":"com.sun.management.ThreadMXBean", "queryAllPublicMethods":true }, { "name":"com.sun.management.UnixOperatingSystemMXBean", "queryAllPublicMethods":true, "methods":[{"name":"getMaxFileDescriptorCount","parameterTypes":[] }, {"name":"getOpenFileDescriptorCount","parameterTypes":[] }] }, { "name":"com.sun.management.VMOption", "queryAllPublicMethods":true }, { "name":"com.sun.management.internal.GarbageCollectorExtImpl", "queryAllPublicConstructors":true }, { "name":"com.sun.management.internal.HotSpotDiagnostic", "queryAllPublicConstructors":true }, { "name":"com.sun.management.internal.HotSpotThreadImpl", "queryAllPublicConstructors":true }, { "name":"com.sun.management.internal.OperatingSystemImpl", "queryAllPublicConstructors":true }, { "name":"com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"com.unboundid.ldap.listener.InMemoryDirectoryServer" }, { "name":"com.wavefront.sdk.common.WavefrontSender" }, { "name":"com.wavefront.sdk.common.application.ApplicationTags" }, { "name":"com.zaxxer.hikari.HikariConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addDataSourceProperty","parameterTypes":["java.lang.String","java.lang.Object"] }, {"name":"addHealthCheckProperty","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"copyStateTo","parameterTypes":["com.zaxxer.hikari.HikariConfig"] }, {"name":"getCatalog","parameterTypes":[] }, {"name":"getConnectionInitSql","parameterTypes":[] }, {"name":"getConnectionTestQuery","parameterTypes":[] }, {"name":"getConnectionTimeout","parameterTypes":[] }, {"name":"getDataSource","parameterTypes":[] }, {"name":"getDataSourceClassName","parameterTypes":[] }, {"name":"getDataSourceJNDI","parameterTypes":[] }, {"name":"getDataSourceProperties","parameterTypes":[] }, {"name":"getDriverClassName","parameterTypes":[] }, {"name":"getExceptionOverrideClassName","parameterTypes":[] }, {"name":"getHealthCheckProperties","parameterTypes":[] }, {"name":"getHealthCheckRegistry","parameterTypes":[] }, {"name":"getIdleTimeout","parameterTypes":[] }, {"name":"getInitializationFailTimeout","parameterTypes":[] }, {"name":"getJdbcUrl","parameterTypes":[] }, {"name":"getKeepaliveTime","parameterTypes":[] }, {"name":"getLeakDetectionThreshold","parameterTypes":[] }, {"name":"getMaxLifetime","parameterTypes":[] }, {"name":"getMaximumPoolSize","parameterTypes":[] }, {"name":"getMetricRegistry","parameterTypes":[] }, {"name":"getMetricsTrackerFactory","parameterTypes":[] }, {"name":"getMinimumIdle","parameterTypes":[] }, {"name":"getPassword","parameterTypes":[] }, {"name":"getPoolName","parameterTypes":[] }, {"name":"getScheduledExecutor","parameterTypes":[] }, {"name":"getSchema","parameterTypes":[] }, {"name":"getThreadFactory","parameterTypes":[] }, {"name":"getTransactionIsolation","parameterTypes":[] }, {"name":"getUsername","parameterTypes":[] }, {"name":"getValidationTimeout","parameterTypes":[] }, {"name":"isAllowPoolSuspension","parameterTypes":[] }, {"name":"isAutoCommit","parameterTypes":[] }, {"name":"isIsolateInternalQueries","parameterTypes":[] }, {"name":"isReadOnly","parameterTypes":[] }, {"name":"isRegisterMbeans","parameterTypes":[] }, {"name":"setAllowPoolSuspension","parameterTypes":["boolean"] }, {"name":"setAutoCommit","parameterTypes":["boolean"] }, {"name":"setCatalog","parameterTypes":["java.lang.String"] }, {"name":"setConnectionInitSql","parameterTypes":["java.lang.String"] }, {"name":"setConnectionTestQuery","parameterTypes":["java.lang.String"] }, {"name":"setConnectionTimeout","parameterTypes":["long"] }, {"name":"setDataSource","parameterTypes":["javax.sql.DataSource"] }, {"name":"setDataSourceClassName","parameterTypes":["java.lang.String"] }, {"name":"setDataSourceJNDI","parameterTypes":["java.lang.String"] }, {"name":"setDataSourceProperties","parameterTypes":["java.util.Properties"] }, {"name":"setDriverClassName","parameterTypes":["java.lang.String"] }, {"name":"setExceptionOverrideClassName","parameterTypes":["java.lang.String"] }, {"name":"setHealthCheckProperties","parameterTypes":["java.util.Properties"] }, {"name":"setIdleTimeout","parameterTypes":["long"] }, {"name":"setInitializationFailTimeout","parameterTypes":["long"] }, {"name":"setIsolateInternalQueries","parameterTypes":["boolean"] }, {"name":"setJdbcUrl","parameterTypes":["java.lang.String"] }, {"name":"setKeepaliveTime","parameterTypes":["long"] }, {"name":"setLeakDetectionThreshold","parameterTypes":["long"] }, {"name":"setMaxLifetime","parameterTypes":["long"] }, {"name":"setMaximumPoolSize","parameterTypes":["int"] }, {"name":"setMinimumIdle","parameterTypes":["int"] }, {"name":"setPassword","parameterTypes":["java.lang.String"] }, {"name":"setPoolName","parameterTypes":["java.lang.String"] }, {"name":"setReadOnly","parameterTypes":["boolean"] }, {"name":"setRegisterMbeans","parameterTypes":["boolean"] }, {"name":"setScheduledExecutor","parameterTypes":["java.util.concurrent.ScheduledExecutorService"] }, {"name":"setSchema","parameterTypes":["java.lang.String"] }, {"name":"setThreadFactory","parameterTypes":["java.util.concurrent.ThreadFactory"] }, {"name":"setTransactionIsolation","parameterTypes":["java.lang.String"] }, {"name":"setUsername","parameterTypes":["java.lang.String"] }, {"name":"setValidationTimeout","parameterTypes":["long"] }, {"name":"validate","parameterTypes":[] }] }, { "name":"com.zaxxer.hikari.HikariConfigMXBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"com.zaxxer.hikari.HikariDataSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"getConnection","parameterTypes":[] }, {"name":"getConnection","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"getLogWriter","parameterTypes":[] }, {"name":"getLoginTimeout","parameterTypes":[] }, {"name":"getParentLogger","parameterTypes":[] }, {"name":"isWrapperFor","parameterTypes":["java.lang.Class"] }, {"name":"setHealthCheckRegistry","parameterTypes":["java.lang.Object"] }, {"name":"setLogWriter","parameterTypes":["java.io.PrintWriter"] }, {"name":"setLoginTimeout","parameterTypes":["int"] }, {"name":"setMetricRegistry","parameterTypes":["java.lang.Object"] }, {"name":"setMetricsTrackerFactory","parameterTypes":["com.zaxxer.hikari.metrics.MetricsTrackerFactory"] }, {"name":"toString","parameterTypes":[] }, {"name":"unwrap","parameterTypes":["java.lang.Class"] }] }, { "name":"com.zaxxer.hikari.HikariPoolMXBean" }, { "name":"com.zaxxer.hikari.metrics.MetricsTrackerFactory" }, { "name":"com.zaxxer.hikari.pool.HikariProxyConnection", "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"com.zaxxer.hikari.pool.PoolBase", "fields":[{"name":"catalog"}] }, { "name":"com.zaxxer.hikari.pool.PoolEntry", "fields":[{"name":"state"}] }, { "name":"double", "queryAllDeclaredMethods":true }, { "name":"freemarker.template.Configuration" }, { "name":"graphql.GraphQL" }, { "name":"groovy.lang.MetaClass" }, { "name":"groovy.text.TemplateEngine" }, { "name":"groovy.text.markup.MarkupTemplateEngine" }, { "name":"int", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"io.grpc.BindableService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"postConstruct","parameterTypes":[] }] }, { "name":"io.grpc.ForwardingServerCall$SimpleForwardingServerCall", "fields":[{"name":"delegate"}] }, { "name":"io.grpc.ServerInterceptor" }, { "name":"io.grpc.ServerServiceDefinition" }, { "name":"io.grpc.census.InternalCensusStatsAccessor" }, { "name":"io.grpc.census.InternalCensusTracingAccessor" }, { "name":"io.grpc.internal.DnsNameResolverProvider" }, { "name":"io.grpc.internal.JndiResourceResolverFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"io.grpc.internal.PickFirstLoadBalancerProvider" }, { "name":"io.grpc.internal.SerializingExecutor", "fields":[{"name":"runState"}] }, { "name":"io.grpc.internal.ServerCallImpl", "fields":[{"name":"stream"}] }, { "name":"io.grpc.netty.NettyChannelProvider" }, { "name":"io.grpc.netty.UdsNettyChannelProvider" }, { "name":"io.grpc.netty.shaded.io.grpc.netty.AbstractNettyHandler", "methods":[{"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.NettyChannelBuilder" }, { "name":"io.grpc.netty.shaded.io.grpc.netty.NettyClientHandler", "methods":[{"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.NettyServer$1" }, { "name":"io.grpc.netty.shaded.io.grpc.netty.NettyServerBuilder" }, { "name":"io.grpc.netty.shaded.io.grpc.netty.NettyServerHandler", "methods":[{"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.NettyServerStream", "fields":[{"name":"writeQueue"}] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$GrpcNegotiationHandler", "methods":[{"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$ProtocolNegotiationHandler", "methods":[{"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$ProxyProtocolNegotiationHandler" }, { "name":"io.grpc.netty.shaded.io.grpc.netty.ProtocolNegotiators$WaitUntilActiveHandler", "methods":[{"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.WriteBufferingAndExceptionHandler", "methods":[{"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }] }, { "name":"io.grpc.netty.shaded.io.grpc.netty.WriteQueue", "fields":[{"name":"channel"}] }, { "name":"io.grpc.netty.shaded.io.netty.bootstrap.ServerBootstrap$1" }, { "name":"io.grpc.netty.shaded.io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor", "methods":[{"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }] }, { "name":"io.grpc.netty.shaded.io.netty.buffer.AbstractByteBufAllocator", "queryAllDeclaredMethods":true }, { "name":"io.grpc.netty.shaded.io.netty.buffer.AbstractPooledDerivedByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.AbstractReferenceCountedByteBuf", "fields":[{"name":"refCnt"}] }, { "name":"io.grpc.netty.shaded.io.netty.buffer.ByteBufAllocator" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.ByteBufUtil" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.PooledByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.PooledByteBufAllocator" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.PooledSlicedByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.PooledUnsafeDirectByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.UnpooledByteBufAllocator$InstrumentedUnpooledUnsafeDirectByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.UnpooledDirectByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.UnpooledHeapByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.buffer.UnpooledUnsafeDirectByteBuf" }, { "name":"io.grpc.netty.shaded.io.netty.channel.AbstractChannelHandlerContext", "fields":[{"name":"handlerState"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.ChannelDuplexHandler", "methods":[{"name":"bind","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.ChannelInboundHandlerAdapter", "methods":[{"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.ChannelInitializer", "methods":[{"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.ChannelOutboundBuffer", "fields":[{"name":"totalPendingSize"}, {"name":"unwritable"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultChannelConfig", "fields":[{"name":"autoRead"}, {"name":"writeBufferWaterMark"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline", "fields":[{"name":"estimatorHandle"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$HeadContext", "methods":[{"name":"bind","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultChannelPipeline$TailContext", "methods":[{"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.DefaultFileRegion" }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.Epoll", "methods":[{"name":"isAvailable","parameterTypes":[] }, {"name":"unavailabilityCause","parameterTypes":[] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.EpollChannelOption", "fields":[{"name":"TCP_USER_TIMEOUT"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.EpollDomainSocketChannel" }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.EpollEventLoopGroup", "methods":[{"name":"","parameterTypes":["int","java.util.concurrent.ThreadFactory"] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.EpollServerSocketChannel", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.EpollSocketChannel", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.epoll.NativeDatagramPacketArray$NativeDatagramPacket" }, { "name":"io.grpc.netty.shaded.io.netty.channel.socket.nio.NioSocketChannel", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.DatagramSocketAddress" }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.DomainDatagramSocketAddress" }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.FileDescriptor", "fields":[{"name":"state"}] }, { "name":"io.grpc.netty.shaded.io.netty.channel.unix.PeerCredentials" }, { "name":"io.grpc.netty.shaded.io.netty.handler.codec.ByteToMessageDecoder", "methods":[{"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }] }, { "name":"io.grpc.netty.shaded.io.netty.handler.codec.http2.Http2ConnectionHandler", "methods":[{"name":"bind","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }] }, { "name":"io.grpc.netty.shaded.io.netty.handler.proxy.HttpProxyHandler" }, { "name":"io.grpc.netty.shaded.io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper", "methods":[{"name":"bind","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRegistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelUnregistered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelWritabilityChanged","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"close","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"deregister","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"disconnect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"read","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"userEventTriggered","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }] }, { "name":"io.grpc.netty.shaded.io.netty.handler.proxy.ProxyHandler", "methods":[{"name":"channelActive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelInactive","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"channelRead","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object"] }, {"name":"channelReadComplete","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"connect","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.net.SocketAddress","java.net.SocketAddress","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }, {"name":"exceptionCaught","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Throwable"] }, {"name":"flush","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext"] }, {"name":"write","parameterTypes":["io.grpc.netty.shaded.io.netty.channel.ChannelHandlerContext","java.lang.Object","io.grpc.netty.shaded.io.netty.channel.ChannelPromise"] }] }, { "name":"io.grpc.netty.shaded.io.netty.handler.ssl.PemPrivateKey" }, { "name":"io.grpc.netty.shaded.io.netty.handler.ssl.PemValue" }, { "name":"io.grpc.netty.shaded.io.netty.util.AbstractReferenceCounted", "fields":[{"name":"refCnt"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.DefaultAttributeMap", "fields":[{"name":"attributes"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.DefaultAttributeMap$DefaultAttribute", "fields":[{"name":"attributeMap"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.Recycler$DefaultHandle", "fields":[{"name":"state"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.ReferenceCountUtil", "queryAllDeclaredMethods":true }, { "name":"io.grpc.netty.shaded.io.netty.util.ResourceLeakDetector$DefaultResourceLeak", "fields":[{"name":"droppedRecords"}, {"name":"head"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.concurrent.DefaultPromise", "fields":[{"name":"result"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.concurrent.SingleThreadEventExecutor", "fields":[{"name":"state"}, {"name":"threadProperties"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.NativeLibraryUtil", "methods":[{"name":"loadLibrary","parameterTypes":["java.lang.String","boolean"] }] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields", "fields":[{"name":"producerLimit"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields", "fields":[{"name":"consumerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields", "fields":[{"name":"producerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueConsumerIndexField", "fields":[{"name":"consumerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerIndexField", "fields":[{"name":"producerIndex"}] }, { "name":"io.grpc.netty.shaded.io.netty.util.internal.shaded.org.jctools.queues.MpscArrayQueueProducerLimitField", "fields":[{"name":"producerLimit"}] }, { "name":"io.grpc.okhttp.OkHttpChannelProvider" }, { "name":"io.grpc.okhttp.OkHttpServerProvider" }, { "name":"io.grpc.override.ContextStorageOverride" }, { "name":"io.grpc.stub.ServerCalls" }, { "name":"io.grpc.stub.ServerCalls$NoopStreamObserver" }, { "name":"io.grpc.stub.ServerCalls$ServerCallStreamObserverImpl" }, { "name":"io.grpc.stub.ServerCalls$StreamingServerCallHandler" }, { "name":"io.grpc.stub.StreamObserver" }, { "name":"io.grpc.util.MutableHandlerRegistry" }, { "name":"io.grpc.util.SecretRoundRobinLoadBalancerProvider$Provider" }, { "name":"io.lettuce.core.metrics.MicrometerCommandLatencyRecorder" }, { "name":"io.micrometer.appoptics.AppOpticsMeterRegistry" }, { "name":"io.micrometer.atlas.AtlasMeterRegistry" }, { "name":"io.micrometer.common.KeyValues" }, { "name":"io.micrometer.common.lang.NonNullApi", "queryAllDeclaredMethods":true }, { "name":"io.micrometer.common.lang.NonNullFields", "queryAllDeclaredMethods":true }, { "name":"io.micrometer.common.lang.Nullable", "queryAllDeclaredMethods":true }, { "name":"io.micrometer.context.ContextSnapshot" }, { "name":"io.micrometer.core.annotation.Timed" }, { "name":"io.micrometer.core.instrument.Clock", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.Clock$1", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"monotonicTime","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"wallTime","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.Counter" }, { "name":"io.micrometer.core.instrument.DistributionSummary" }, { "name":"io.micrometer.core.instrument.FunctionCounter" }, { "name":"io.micrometer.core.instrument.FunctionTimer" }, { "name":"io.micrometer.core.instrument.Gauge" }, { "name":"io.micrometer.core.instrument.HighCardinalityTagsDetector" }, { "name":"io.micrometer.core.instrument.LongTaskTimer" }, { "name":"io.micrometer.core.instrument.Measurement" }, { "name":"io.micrometer.core.instrument.Meter" }, { "name":"io.micrometer.core.instrument.Meter$Id" }, { "name":"io.micrometer.core.instrument.Meter$Type" }, { "name":"io.micrometer.core.instrument.MeterRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"clear","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"config","parameterTypes":[] }, {"name":"counter","parameterTypes":["java.lang.String","java.lang.Iterable"] }, {"name":"counter","parameterTypes":["java.lang.String","java.lang.String[]"] }, {"name":"find","parameterTypes":["java.lang.String"] }, {"name":"forEachMeter","parameterTypes":["java.util.function.Consumer"] }, {"name":"gauge","parameterTypes":["java.lang.String","java.lang.Iterable","java.lang.Number"] }, {"name":"gauge","parameterTypes":["java.lang.String","java.lang.Iterable","java.lang.Object","java.util.function.ToDoubleFunction"] }, {"name":"gauge","parameterTypes":["java.lang.String","java.lang.Number"] }, {"name":"gauge","parameterTypes":["java.lang.String","java.lang.Object","java.util.function.ToDoubleFunction"] }, {"name":"gaugeCollectionSize","parameterTypes":["java.lang.String","java.lang.Iterable","java.util.Collection"] }, {"name":"gaugeMapSize","parameterTypes":["java.lang.String","java.lang.Iterable","java.util.Map"] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"getMeters","parameterTypes":[] }, {"name":"isClosed","parameterTypes":[] }, {"name":"more","parameterTypes":[] }, {"name":"remove","parameterTypes":["io.micrometer.core.instrument.Meter$Id"] }, {"name":"remove","parameterTypes":["io.micrometer.core.instrument.Meter"] }, {"name":"removeByPreFilterId","parameterTypes":["io.micrometer.core.instrument.Meter$Id"] }, {"name":"summary","parameterTypes":["java.lang.String","java.lang.Iterable"] }, {"name":"summary","parameterTypes":["java.lang.String","java.lang.String[]"] }, {"name":"timer","parameterTypes":["java.lang.String","java.lang.Iterable"] }, {"name":"timer","parameterTypes":["java.lang.String","java.lang.String[]"] }] }, { "name":"io.micrometer.core.instrument.MeterRegistry$Config" }, { "name":"io.micrometer.core.instrument.MeterRegistry$More" }, { "name":"io.micrometer.core.instrument.Statistic", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"io.micrometer.core.instrument.Tag" }, { "name":"io.micrometer.core.instrument.TimeGauge" }, { "name":"io.micrometer.core.instrument.Timer" }, { "name":"io.micrometer.core.instrument.binder.MeterBinder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.jetty.JettyServerThreadPoolMetrics" }, { "name":"io.micrometer.core.instrument.binder.jvm.ClassLoaderMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.jvm.ExecutorServiceMetrics" }, { "name":"io.micrometer.core.instrument.binder.jvm.JvmCompilationMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.jvm.JvmGcMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.jvm.JvmHeapPressureMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.jvm.JvmInfoMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.jvm.JvmMemoryMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.jvm.JvmThreadMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.kafka.KafkaClientMetrics" }, { "name":"io.micrometer.core.instrument.binder.logging.Log4j2Metrics" }, { "name":"io.micrometer.core.instrument.binder.logging.LogbackMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.system.FileDescriptorMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.system.ProcessorMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.system.UptimeMetrics", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.binder.tomcat.TomcatMetrics" }, { "name":"io.micrometer.core.instrument.composite.CompositeMeterRegistry" }, { "name":"io.micrometer.core.instrument.config.MeterFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"map","parameterTypes":["io.micrometer.core.instrument.Meter$Id"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.config.MeterFilter$9", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"accept","parameterTypes":["io.micrometer.core.instrument.Meter$Id"] }, {"name":"close","parameterTypes":[] }, {"name":"configure","parameterTypes":["io.micrometer.core.instrument.Meter$Id","io.micrometer.core.instrument.distribution.DistributionStatisticConfig"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.config.MeterFilterReply" }, { "name":"io.micrometer.core.instrument.config.MeterRegistryConfig", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"get","parameterTypes":["java.lang.String"] }, {"name":"requireValid","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.config.NamingConvention" }, { "name":"io.micrometer.core.instrument.config.validate.Validated" }, { "name":"io.micrometer.core.instrument.config.validate.ValidationException" }, { "name":"io.micrometer.core.instrument.distribution.DistributionStatisticConfig" }, { "name":"io.micrometer.core.instrument.distribution.HistogramSupport" }, { "name":"io.micrometer.core.instrument.distribution.TimeWindowMax", "fields":[{"name":"rotating"}] }, { "name":"io.micrometer.core.instrument.distribution.TimeWindowSum", "fields":[{"name":"rotating"}] }, { "name":"io.micrometer.core.instrument.distribution.pause.PauseDetector" }, { "name":"io.micrometer.core.instrument.observation.DefaultMeterObservationHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"onEvent","parameterTypes":["io.micrometer.observation.Observation$Event","io.micrometer.observation.Observation$Context"] }, {"name":"onStart","parameterTypes":["io.micrometer.observation.Observation$Context"] }, {"name":"onStop","parameterTypes":["io.micrometer.observation.Observation$Context"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.core.instrument.observation.MeterObservationHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"supportsContext","parameterTypes":["io.micrometer.observation.Observation$Context"] }] }, { "name":"io.micrometer.core.instrument.search.RequiredSearch" }, { "name":"io.micrometer.core.instrument.search.Search" }, { "name":"io.micrometer.datadog.DatadogMeterRegistry" }, { "name":"io.micrometer.dynatrace.DynatraceMeterRegistry" }, { "name":"io.micrometer.elastic.ElasticMeterRegistry" }, { "name":"io.micrometer.ganglia.GangliaMeterRegistry" }, { "name":"io.micrometer.graphite.GraphiteMeterRegistry" }, { "name":"io.micrometer.humio.HumioMeterRegistry" }, { "name":"io.micrometer.influx.InfluxMeterRegistry" }, { "name":"io.micrometer.jmx.JmxMeterRegistry" }, { "name":"io.micrometer.kairos.KairosMeterRegistry" }, { "name":"io.micrometer.newrelic.NewRelicMeterRegistry" }, { "name":"io.micrometer.observation.Observation" }, { "name":"io.micrometer.observation.Observation$Context" }, { "name":"io.micrometer.observation.Observation$Event" }, { "name":"io.micrometer.observation.Observation$Scope" }, { "name":"io.micrometer.observation.ObservationFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"io.micrometer.observation.ObservationHandler", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"onError","parameterTypes":["io.micrometer.observation.Observation$Context"] }, {"name":"onScopeClosed","parameterTypes":["io.micrometer.observation.Observation$Context"] }, {"name":"onScopeOpened","parameterTypes":["io.micrometer.observation.Observation$Context"] }, {"name":"onScopeReset","parameterTypes":["io.micrometer.observation.Observation$Context"] }] }, { "name":"io.micrometer.observation.ObservationPredicate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"io.micrometer.observation.ObservationRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.observation.ObservationRegistry$ObservationConfig" }, { "name":"io.micrometer.observation.SimpleObservationRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getCurrentObservation","parameterTypes":[] }, {"name":"getCurrentObservationScope","parameterTypes":[] }, {"name":"isNoop","parameterTypes":[] }, {"name":"observationConfig","parameterTypes":[] }, {"name":"setCurrentObservationScope","parameterTypes":["io.micrometer.observation.Observation$Scope"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.observation.annotation.Observed" }, { "name":"io.micrometer.observation.aop.ObservedAspect", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.micrometer.prometheus.HistogramFlavor" }, { "name":"io.micrometer.prometheus.MicrometerCollector" }, { "name":"io.micrometer.prometheus.PrometheusConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"prefix","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"validate","parameterTypes":[] }] }, { "name":"io.micrometer.prometheus.PrometheusCounter" }, { "name":"io.micrometer.prometheus.PrometheusDistributionSummary" }, { "name":"io.micrometer.prometheus.PrometheusMeterRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"io.micrometer.prometheus.PrometheusTimer" }, { "name":"io.micrometer.prometheusmetrics.PrometheusMeterRegistry" }, { "name":"io.micrometer.registry.otlp.OtlpMeterRegistry" }, { "name":"io.micrometer.signalfx.SignalFxMeterRegistry" }, { "name":"io.micrometer.stackdriver.StackdriverMeterRegistry" }, { "name":"io.micrometer.statsd.StatsdMeterRegistry" }, { "name":"io.micrometer.tracing.Tracer" }, { "name":"io.micrometer.tracing.otel.bridge.OtelTracer" }, { "name":"io.netty.buffer.PooledByteBufAllocator" }, { "name":"io.netty.util.NettyRuntime" }, { "name":"io.opentelemetry.sdk.OpenTelemetrySdk" }, { "name":"io.perfmark.impl.SecretPerfMarkImpl$PerfMarkImpl" }, { "name":"io.prometheus.client.Collector" }, { "name":"io.prometheus.client.Collector$MetricFamilySamples$Sample" }, { "name":"io.prometheus.client.Collector$Type" }, { "name":"io.prometheus.client.CollectorRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"io.prometheus.client.Predicate" }, { "name":"io.prometheus.client.exemplars.DefaultExemplarSampler" }, { "name":"io.prometheus.client.exemplars.Exemplar" }, { "name":"io.prometheus.client.exemplars.tracer.common.SpanContextSupplier" }, { "name":"io.prometheus.client.exporter.PushGateway" }, { "name":"io.r2dbc.pool.ConnectionPool" }, { "name":"io.r2dbc.proxy.ProxyConnectionFactory" }, { "name":"io.r2dbc.spi.ConnectionFactory" }, { "name":"io.reactivex.rxjava3.core.Flowable" }, { "name":"io.rsocket.RSocket" }, { "name":"io.rsocket.core.RSocketServer" }, { "name":"io.smallrye.mutiny.Multi" }, { "name":"io.undertow.Undertow" }, { "name":"io.undertow.Undertow$ListenerConfig" }, { "name":"io.undertow.protocols.ssl.UndertowAcceptingSslChannel" }, { "name":"io.undertow.websockets.jsr.Bootstrap" }, { "name":"io.vavr.control.Try" }, { "name":"jakarta.activation.MimeType" }, { "name":"jakarta.annotation.ManagedBean" }, { "name":"jakarta.annotation.PostConstruct" }, { "name":"jakarta.annotation.PreDestroy" }, { "name":"jakarta.annotation.Resource" }, { "name":"jakarta.ejb.EJB" }, { "name":"jakarta.ejb.TransactionAttribute" }, { "name":"jakarta.faces.context.FacesContext" }, { "name":"jakarta.inject.Inject" }, { "name":"jakarta.inject.Named" }, { "name":"jakarta.inject.Provider" }, { "name":"jakarta.inject.Qualifier" }, { "name":"jakarta.jms.ConnectionFactory" }, { "name":"jakarta.jms.Message" }, { "name":"jakarta.json.bind.Jsonb" }, { "name":"jakarta.persistence.EntityManager" }, { "name":"jakarta.persistence.EntityManagerFactory" }, { "name":"jakarta.persistence.PersistenceContext" }, { "name":"jakarta.servlet.AsyncContext" }, { "name":"jakarta.servlet.DispatcherType" }, { "name":"jakarta.servlet.Filter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.security.web.FilterChainProxy"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x000000f001a26bd8"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x00000188c99e8a60"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1357/0x000001e6d19f64c0"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1362/0x0000007001a1e800"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000019a3aa20e98"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000020b55a3e920"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x000001ca6da42278"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000022944a3edb8"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000023b5ea3f750"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1376/0x000002933ca3e800"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1405/0x00000070019c8830"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1407/0x000000e0019a69d8"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x0000022b3b98f428"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x00000246d69b7670"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1412/0x00000268db9b0828"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1413/0x000001cf3b99ba60"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1422/0x00000257bc9b0e98"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x00000212b89cfc90"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023bbb9e3c68"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023e5a9afa60"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x00000151709e7710"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x000002746f9ea4c0"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1443/0x000001dbfb9df710"] }, {"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1447/0x00007f160c9ef588"] }, {"name":"destroy","parameterTypes":[] }, {"name":"from","parameterTypes":["org.springframework.security.web.FilterChainProxy"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x000000f001a26bd8"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x00000188c99e8a60"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1357/0x000001e6d19f64c0"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1362/0x0000007001a1e800"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000019a3aa20e98"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000020b55a3e920"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x000001ca6da42278"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000022944a3edb8"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000023b5ea3f750"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1376/0x000002933ca3e800"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1405/0x00000070019c8830"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1407/0x000000e0019a69d8"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x0000022b3b98f428"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x00000246d69b7670"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1412/0x00000268db9b0828"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1413/0x000001cf3b99ba60"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1422/0x00000257bc9b0e98"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x00000212b89cfc90"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023bbb9e3c68"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023e5a9afa60"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x00000151709e7710"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x000002746f9ea4c0"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1443/0x000001dbfb9df710"] }, {"name":"from","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1447/0x00007f160c9ef588"] }, {"name":"init","parameterTypes":["jakarta.servlet.FilterConfig"] }, {"name":"of","parameterTypes":["org.springframework.security.web.FilterChainProxy"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x000000f001a26bd8"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x00000188c99e8a60"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1357/0x000001e6d19f64c0"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1362/0x0000007001a1e800"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000019a3aa20e98"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000020b55a3e920"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x000001ca6da42278"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000022944a3edb8"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000023b5ea3f750"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1376/0x000002933ca3e800"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1405/0x00000070019c8830"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1407/0x000000e0019a69d8"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x0000022b3b98f428"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x00000246d69b7670"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1412/0x00000268db9b0828"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1413/0x000001cf3b99ba60"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1422/0x00000257bc9b0e98"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x00000212b89cfc90"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023bbb9e3c68"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023e5a9afa60"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x00000151709e7710"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x000002746f9ea4c0"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1443/0x000001dbfb9df710"] }, {"name":"of","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1447/0x00007f160c9ef588"] }, {"name":"valueOf","parameterTypes":["org.springframework.security.web.FilterChainProxy"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x000000f001a26bd8"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1356/0x00000188c99e8a60"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1357/0x000001e6d19f64c0"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1362/0x0000007001a1e800"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000019a3aa20e98"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1370/0x0000020b55a3e920"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x000001ca6da42278"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000022944a3edb8"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1372/0x0000023b5ea3f750"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1376/0x000002933ca3e800"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1405/0x00000070019c8830"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1407/0x000000e0019a69d8"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x0000022b3b98f428"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1410/0x00000246d69b7670"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1412/0x00000268db9b0828"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1413/0x000001cf3b99ba60"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1422/0x00000257bc9b0e98"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x00000212b89cfc90"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023bbb9e3c68"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1424/0x0000023e5a9afa60"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x00000151709e7710"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1430/0x000002746f9ea4c0"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1443/0x000001dbfb9df710"] }, {"name":"valueOf","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector$$Lambda$1447/0x00007f160c9ef588"] }] }, { "name":"jakarta.servlet.FilterChain" }, { "name":"jakarta.servlet.FilterConfig" }, { "name":"jakarta.servlet.FilterRegistration$Dynamic" }, { "name":"jakarta.servlet.GenericFilter", "queryAllDeclaredMethods":true }, { "name":"jakarta.servlet.GenericServlet", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getInitParameter","parameterTypes":["java.lang.String"] }, {"name":"getInitParameterNames","parameterTypes":[] }, {"name":"getServletConfig","parameterTypes":[] }, {"name":"getServletContext","parameterTypes":[] }, {"name":"getServletInfo","parameterTypes":[] }, {"name":"log","parameterTypes":["java.lang.String"] }, {"name":"log","parameterTypes":["java.lang.String","java.lang.Throwable"] }] }, { "name":"jakarta.servlet.MultipartConfigElement", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"jakarta.servlet.Registration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"jakarta.servlet.Registration$Dynamic" }, { "name":"jakarta.servlet.RequestDispatcher" }, { "name":"jakarta.servlet.Servlet", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"jakarta.servlet.ServletConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"jakarta.servlet.ServletContext", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"jakarta.servlet.ServletException" }, { "name":"jakarta.servlet.ServletRegistration" }, { "name":"jakarta.servlet.ServletRegistration$Dynamic" }, { "name":"jakarta.servlet.ServletRequest" }, { "name":"jakarta.servlet.ServletResponse" }, { "name":"jakarta.servlet.http.HttpServlet", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"init","parameterTypes":["jakarta.servlet.ServletConfig"] }, {"name":"service","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse"] }] }, { "name":"jakarta.servlet.http.HttpServletRequest" }, { "name":"jakarta.servlet.http.HttpServletResponse" }, { "name":"jakarta.servlet.http.HttpSession" }, { "name":"jakarta.servlet.jsp.jstl.core.Config" }, { "name":"jakarta.transaction.Transaction" }, { "name":"jakarta.transaction.TransactionManager" }, { "name":"jakarta.transaction.Transactional" }, { "name":"jakarta.validation.Validation" }, { "name":"jakarta.validation.Validator" }, { "name":"jakarta.validation.executable.ExecutableValidator" }, { "name":"jakarta.websocket.server.ServerContainer" }, { "name":"jakarta.xml.bind.Binder" }, { "name":"jakarta.xml.ws.WebServiceRef" }, { "name":"java.io.ByteArrayOutputStream" }, { "name":"java.io.Closeable", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }] }, { "name":"java.io.Console", "methods":[{"name":"isTerminal","parameterTypes":[] }] }, { "name":"java.io.DataInput" }, { "name":"java.io.DataOutput" }, { "name":"java.io.File" }, { "name":"java.io.FileDescriptor" }, { "name":"java.io.FilePermission" }, { "name":"java.io.IOException" }, { "name":"java.io.InputStream" }, { "name":"java.io.ObjectInputStream" }, { "name":"java.io.ObjectOutputStream" }, { "name":"java.io.OutputStream" }, { "name":"java.io.PrintWriter" }, { "name":"java.io.Reader" }, { "name":"java.io.Serializable", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"java.io.Writer" }, { "name":"java.lang.Appendable" }, { "name":"java.lang.AutoCloseable", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }] }, { "name":"java.lang.Boolean", "fields":[{"name":"TYPE"}], "methods":[{"name":"toboolean","parameterTypes":[] }] }, { "name":"java.lang.Byte", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.CharSequence" }, { "name":"java.lang.Character", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Class", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"java.lang.ClassLoader", "methods":[{"name":"defineClass","parameterTypes":["java.lang.String","byte[]","int","int","java.security.ProtectionDomain"] }] }, { "name":"java.lang.ClassNotFoundException" }, { "name":"java.lang.CloneNotSupportedException" }, { "name":"java.lang.Cloneable", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"java.lang.Comparable", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.lang.Deprecated", "queryAllPublicMethods":true }, { "name":"java.lang.Double", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Enum", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"java.lang.Exception" }, { "name":"java.lang.Float", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.FunctionalInterface" }, { "name":"java.lang.IllegalAccessException" }, { "name":"java.lang.IllegalArgumentException" }, { "name":"java.lang.IllegalStateException" }, { "name":"java.lang.Integer", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.InterruptedException" }, { "name":"java.lang.Iterable", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"forEach","parameterTypes":["java.util.function.Consumer"] }, {"name":"iterator","parameterTypes":[] }, {"name":"spliterator","parameterTypes":[] }] }, { "name":"java.lang.Long", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.Module", "queryAllDeclaredMethods":true }, { "name":"java.lang.NoSuchFieldException" }, { "name":"java.lang.NoSuchMethodException" }, { "name":"java.lang.Number", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"java.lang.NumberFormatException" }, { "name":"java.lang.Object", "allDeclaredFields":true, "allDeclaredClasses":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"hashCode","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] }, { "name":"java.lang.OutOfMemoryError" }, { "name":"java.lang.ProcessHandle", "methods":[{"name":"current","parameterTypes":[] }, {"name":"pid","parameterTypes":[] }] }, { "name":"java.lang.Record", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"java.lang.Runnable", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"java.lang.RuntimeException" }, { "name":"java.lang.RuntimePermission" }, { "name":"java.lang.SafeVarargs" }, { "name":"java.lang.SecurityException" }, { "name":"java.lang.Short", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.StackTraceElement", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.lang.String", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "fields":[{"name":"TYPE"}, {"name":"value"}], "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.lang.StringBuilder" }, { "name":"java.lang.System", "methods":[{"name":"nanoTime","parameterTypes":[] }] }, { "name":"java.lang.Thread", "fields":[{"name":"threadLocalRandomProbe"}] }, { "name":"java.lang.Thread$Builder" }, { "name":"java.lang.Thread$State" }, { "name":"java.lang.ThreadGroup" }, { "name":"java.lang.Throwable", "fields":[{"name":"cause"}] }, { "name":"java.lang.Void", "fields":[{"name":"TYPE"}] }, { "name":"java.lang.WrongThreadException" }, { "name":"java.lang.annotation.Annotation" }, { "name":"java.lang.annotation.Documented", "queryAllDeclaredMethods":true }, { "name":"java.lang.annotation.Inherited", "queryAllDeclaredMethods":true }, { "name":"java.lang.annotation.Repeatable", "queryAllDeclaredMethods":true }, { "name":"java.lang.annotation.Retention", "queryAllDeclaredMethods":true }, { "name":"java.lang.annotation.Target", "queryAllDeclaredMethods":true }, { "name":"java.lang.constant.Constable", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.lang.invoke.SerializedLambda" }, { "name":"java.lang.invoke.TypeDescriptor", "queryAllDeclaredMethods":true }, { "name":"java.lang.invoke.TypeDescriptor$OfField", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.lang.management.BufferPoolMXBean", "queryAllPublicMethods":true }, { "name":"java.lang.management.ClassLoadingMXBean", "queryAllPublicMethods":true }, { "name":"java.lang.management.CompilationMXBean", "queryAllPublicMethods":true }, { "name":"java.lang.management.LockInfo", "queryAllPublicMethods":true }, { "name":"java.lang.management.ManagementPermission", "methods":[{"name":"","parameterTypes":["java.lang.String"] }] }, { "name":"java.lang.management.MemoryMXBean", "queryAllPublicMethods":true, "methods":[{"name":"getHeapMemoryUsage","parameterTypes":[] }] }, { "name":"java.lang.management.MemoryManagerMXBean", "queryAllPublicMethods":true }, { "name":"java.lang.management.MemoryPoolMXBean", "queryAllPublicMethods":true }, { "name":"java.lang.management.MemoryUsage", "queryAllPublicMethods":true, "methods":[{"name":"from","parameterTypes":["javax.management.openmbean.CompositeData"] }, {"name":"getCommitted","parameterTypes":[] }, {"name":"getInit","parameterTypes":[] }, {"name":"getMax","parameterTypes":[] }, {"name":"getUsed","parameterTypes":[] }] }, { "name":"java.lang.management.MonitorInfo", "queryAllPublicMethods":true }, { "name":"java.lang.management.OperatingSystemMXBean" }, { "name":"java.lang.management.PlatformLoggingMXBean", "queryAllPublicMethods":true, "methods":[{"name":"getLoggerLevel","parameterTypes":["java.lang.String"] }, {"name":"getLoggerNames","parameterTypes":[] }, {"name":"getParentLoggerName","parameterTypes":["java.lang.String"] }, {"name":"setLoggerLevel","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"java.lang.management.RuntimeMXBean", "queryAllPublicMethods":true }, { "name":"java.lang.management.ThreadInfo", "queryAllPublicMethods":true }, { "name":"java.lang.management.ThreadMXBean" }, { "name":"java.lang.reflect.AccessibleObject" }, { "name":"java.lang.reflect.AnnotatedElement", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.lang.reflect.Constructor" }, { "name":"java.lang.reflect.Field" }, { "name":"java.lang.reflect.GenericArrayType", "methods":[{"name":"getGenericComponentType","parameterTypes":[] }] }, { "name":"java.lang.reflect.GenericDeclaration", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.lang.reflect.InvocationTargetException" }, { "name":"java.lang.reflect.Method" }, { "name":"java.lang.reflect.ParameterizedType", "methods":[{"name":"getActualTypeArguments","parameterTypes":[] }, {"name":"getRawType","parameterTypes":[] }] }, { "name":"java.lang.reflect.Type", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.lang.reflect.TypeVariable", "methods":[{"name":"getBounds","parameterTypes":[] }] }, { "name":"java.lang.reflect.WildcardType", "methods":[{"name":"getLowerBounds","parameterTypes":[] }, {"name":"getUpperBounds","parameterTypes":[] }] }, { "name":"java.math.BigDecimal" }, { "name":"java.math.BigInteger" }, { "name":"java.net.DatagramPacket" }, { "name":"java.net.InetAddress" }, { "name":"java.net.InetSocketAddress", "methods":[{"name":"getHostString","parameterTypes":[] }] }, { "name":"java.net.NetPermission" }, { "name":"java.net.PortUnreachableException" }, { "name":"java.net.SocketPermission" }, { "name":"java.net.URI" }, { "name":"java.net.URL" }, { "name":"java.net.URLPermission", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"java.net.UnixDomainSocketAddress", "methods":[{"name":"of","parameterTypes":["java.lang.String"] }] }, { "name":"java.nio.Bits", "fields":[{"name":"MAX_MEMORY"}, {"name":"UNALIGNED"}] }, { "name":"java.nio.Buffer", "fields":[{"name":"address"}] }, { "name":"java.nio.ByteBuffer", "methods":[{"name":"alignedSlice","parameterTypes":["int"] }] }, { "name":"java.nio.DirectByteBuffer", "methods":[{"name":"","parameterTypes":["long","int"] }] }, { "name":"java.nio.channels.ClosedChannelException" }, { "name":"java.nio.channels.FileChannel" }, { "name":"java.nio.channels.ServerSocketChannel", "methods":[{"name":"open","parameterTypes":["java.net.ProtocolFamily"] }] }, { "name":"java.nio.channels.SocketChannel", "methods":[{"name":"open","parameterTypes":["java.net.ProtocolFamily"] }] }, { "name":"java.nio.channels.spi.SelectorProvider", "methods":[{"name":"openServerSocketChannel","parameterTypes":["java.net.ProtocolFamily"] }, {"name":"openSocketChannel","parameterTypes":["java.net.ProtocolFamily"] }] }, { "name":"java.nio.charset.Charset", "methods":[{"name":"","parameterTypes":[] }, {"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"java.rmi.MarshalledObject" }, { "name":"java.rmi.dgc.Lease" }, { "name":"java.rmi.dgc.VMID" }, { "name":"java.rmi.server.ObjID" }, { "name":"java.rmi.server.UID" }, { "name":"java.security.AlgorithmParametersSpi" }, { "name":"java.security.AllPermission" }, { "name":"java.security.KeyStoreSpi" }, { "name":"java.security.SecureRandomParameters" }, { "name":"java.security.SecurityPermission" }, { "name":"java.sql.CallableStatement" }, { "name":"java.sql.Connection", "methods":[{"name":"getMetaData","parameterTypes":[] }, {"name":"isValid","parameterTypes":["int"] }] }, { "name":"java.sql.ConnectionBuilder" }, { "name":"java.sql.Date", "methods":[{"name":"","parameterTypes":["long"] }] }, { "name":"java.sql.Driver" }, { "name":"java.sql.DriverManager" }, { "name":"java.sql.PreparedStatement" }, { "name":"java.sql.ResultSet" }, { "name":"java.sql.SQLException", "fields":[{"name":"next"}] }, { "name":"java.sql.SQLFeatureNotSupportedException" }, { "name":"java.sql.SQLWarning" }, { "name":"java.sql.ShardingKeyBuilder" }, { "name":"java.sql.Statement" }, { "name":"java.sql.Time", "methods":[{"name":"","parameterTypes":["long"] }] }, { "name":"java.sql.Timestamp", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["long"] }] }, { "name":"java.sql.Types", "allPublicFields":true }, { "name":"java.sql.Wrapper", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.text.DateFormat" }, { "name":"java.text.ListFormat" }, { "name":"java.time.Clock" }, { "name":"java.time.Duration" }, { "name":"java.time.Instant" }, { "name":"java.util.ArrayList", "fields":[{"name":"elementData"}] }, { "name":"java.util.Collection" }, { "name":"java.util.Comparator" }, { "name":"java.util.Date", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"java.util.EnumMap" }, { "name":"java.util.EnumSet" }, { "name":"java.util.Enumeration" }, { "name":"java.util.EventListener", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.util.HashMap", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.util.HashSet" }, { "name":"java.util.Hashtable" }, { "name":"java.util.Iterator" }, { "name":"java.util.LinkedHashMap", "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.util.LinkedHashMapHessianDeserializer" }, { "name":"java.util.LinkedList", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.util.LinkedListHessianDeserializer" }, { "name":"java.util.List" }, { "name":"java.util.Locale" }, { "name":"java.util.Map", "queryAllPublicMethods":true, "queryAllPublicConstructors":true, "methods":[{"name":"putIfAbsent","parameterTypes":["java.lang.Object","java.lang.Object"] }] }, { "name":"java.util.Map$Entry" }, { "name":"java.util.MapHessianDeserializer" }, { "name":"java.util.NavigableSet" }, { "name":"java.util.Optional" }, { "name":"java.util.Properties", "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.util.PropertyPermission", "methods":[{"name":"","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"java.util.Queue" }, { "name":"java.util.ServiceLoader" }, { "name":"java.util.Set" }, { "name":"java.util.Spliterator" }, { "name":"java.util.TimeZone" }, { "name":"java.util.concurrent.BlockingQueue" }, { "name":"java.util.concurrent.Callable", "methods":[{"name":"call","parameterTypes":[] }] }, { "name":"java.util.concurrent.CompletableFuture" }, { "name":"java.util.concurrent.CompletionService" }, { "name":"java.util.concurrent.ConcurrentHashMap", "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.util.concurrent.ConcurrentHashMapHessianDeserializer" }, { "name":"java.util.concurrent.ConcurrentMap" }, { "name":"java.util.concurrent.CountDownLatch" }, { "name":"java.util.concurrent.Executor", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"java.util.concurrent.ExecutorService" }, { "name":"java.util.concurrent.ForkJoinTask", "fields":[{"name":"aux"}, {"name":"status"}] }, { "name":"java.util.concurrent.Future" }, { "name":"java.util.concurrent.RejectedExecutionHandler" }, { "name":"java.util.concurrent.ScheduledExecutorService" }, { "name":"java.util.concurrent.ScheduledFuture" }, { "name":"java.util.concurrent.ScheduledThreadPoolExecutor" }, { "name":"java.util.concurrent.ThreadFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"java.util.concurrent.ThreadPoolExecutor" }, { "name":"java.util.concurrent.TimeUnit" }, { "name":"java.util.concurrent.atomic.AtomicBoolean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true, "fields":[{"name":"value"}] }, { "name":"java.util.concurrent.atomic.AtomicBooleanHessianDeserializer" }, { "name":"java.util.concurrent.atomic.AtomicInteger", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true }, { "name":"java.util.concurrent.atomic.AtomicIntegerHessianDeserializer" }, { "name":"java.util.concurrent.atomic.AtomicLong", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "unsafeAllocated":true }, { "name":"java.util.concurrent.atomic.AtomicLongHessianDeserializer" }, { "name":"java.util.concurrent.atomic.AtomicReference", "fields":[{"name":"value"}] }, { "name":"java.util.concurrent.atomic.LongAdder", "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"add","parameterTypes":["long"] }, {"name":"sum","parameterTypes":[] }] }, { "name":"java.util.concurrent.atomic.Striped64", "fields":[{"name":"base"}, {"name":"cellsBusy"}] }, { "name":"java.util.concurrent.atomic.Striped64$Cell", "fields":[{"name":"value"}] }, { "name":"java.util.function.BiConsumer" }, { "name":"java.util.function.BiFunction" }, { "name":"java.util.function.BiPredicate", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"and","parameterTypes":["java.util.function.BiPredicate"] }, {"name":"negate","parameterTypes":[] }, {"name":"or","parameterTypes":["java.util.function.BiPredicate"] }] }, { "name":"java.util.function.Consumer" }, { "name":"java.util.function.Function" }, { "name":"java.util.function.ObjIntConsumer" }, { "name":"java.util.function.Predicate" }, { "name":"java.util.function.Supplier" }, { "name":"java.util.function.ToDoubleFunction" }, { "name":"java.util.function.ToLongFunction" }, { "name":"java.util.logging.LogManager", "methods":[{"name":"getLoggingMXBean","parameterTypes":[] }] }, { "name":"java.util.logging.Logger" }, { "name":"java.util.logging.LoggingMXBean", "queryAllPublicMethods":true }, { "name":"java.util.logging.SimpleFormatter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"java.util.stream.Stream" }, { "name":"javax.annotation.ManagedBean" }, { "name":"javax.annotation.Nonnull", "queryAllDeclaredMethods":true }, { "name":"javax.annotation.Nullable", "queryAllDeclaredMethods":true }, { "name":"javax.annotation.PostConstruct", "queryAllDeclaredMethods":true }, { "name":"javax.annotation.PreDestroy", "queryAllDeclaredMethods":true }, { "name":"javax.annotation.Resource", "queryAllDeclaredMethods":true }, { "name":"javax.annotation.meta.TypeQualifier", "queryAllDeclaredMethods":true }, { "name":"javax.annotation.meta.TypeQualifierDefault", "queryAllDeclaredMethods":true }, { "name":"javax.cache.CacheManager" }, { "name":"javax.cache.Caching" }, { "name":"javax.crypto.SecretKey" }, { "name":"javax.inject.Inject" }, { "name":"javax.inject.Named" }, { "name":"javax.management.Attribute" }, { "name":"javax.management.AttributeList" }, { "name":"javax.management.AttributeNotFoundException" }, { "name":"javax.management.DynamicMBean" }, { "name":"javax.management.InstanceAlreadyExistsException" }, { "name":"javax.management.InstanceNotFoundException" }, { "name":"javax.management.IntrospectionException" }, { "name":"javax.management.InvalidAttributeValueException" }, { "name":"javax.management.JMException" }, { "name":"javax.management.ListenerNotFoundException" }, { "name":"javax.management.MBeanException" }, { "name":"javax.management.MBeanInfo", "queryAllPublicMethods":true, "methods":[{"name":"getAttributes","parameterTypes":[] }, {"name":"getConstructors","parameterTypes":[] }, {"name":"getNotifications","parameterTypes":[] }, {"name":"getOperations","parameterTypes":[] }] }, { "name":"javax.management.MBeanOperationInfo", "queryAllPublicMethods":true, "methods":[{"name":"getSignature","parameterTypes":[] }] }, { "name":"javax.management.MBeanRegistrationException" }, { "name":"javax.management.MBeanServer", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"javax.management.MBeanServerBuilder", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"javax.management.MBeanServerConnection", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"javax.management.MBeanServerDelegate" }, { "name":"javax.management.MalformedObjectNameException" }, { "name":"javax.management.NotCompliantMBeanException" }, { "name":"javax.management.Notification" }, { "name":"javax.management.NotificationEmitter" }, { "name":"javax.management.NotificationFilter" }, { "name":"javax.management.NotificationListener" }, { "name":"javax.management.ObjectInstance" }, { "name":"javax.management.ObjectName" }, { "name":"javax.management.OperationsException" }, { "name":"javax.management.QueryExp" }, { "name":"javax.management.ReflectionException" }, { "name":"javax.management.StandardEmitterMBean", "methods":[{"name":"cacheMBeanInfo","parameterTypes":["javax.management.MBeanInfo"] }, {"name":"getCachedMBeanInfo","parameterTypes":[] }, {"name":"getMBeanInfo","parameterTypes":[] }] }, { "name":"javax.management.loading.ClassLoaderRepository" }, { "name":"javax.management.modelmbean.ModelMBean" }, { "name":"javax.management.modelmbean.ModelMBeanInfo" }, { "name":"javax.management.modelmbean.ModelMBeanInfoSupport" }, { "name":"javax.management.modelmbean.ModelMBeanOperationInfo" }, { "name":"javax.management.openmbean.CompositeData" }, { "name":"javax.management.openmbean.OpenMBeanOperationInfoSupport" }, { "name":"javax.management.openmbean.TabularData" }, { "name":"javax.management.remote.rmi.RMIConnection", "queryAllPublicMethods":true, "methods":[{"name":"addNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.ObjectName","java.rmi.MarshalledObject","java.rmi.MarshalledObject","javax.security.auth.Subject"] }, {"name":"addNotificationListeners","parameterTypes":["javax.management.ObjectName[]","java.rmi.MarshalledObject[]","javax.security.auth.Subject[]"] }, {"name":"close","parameterTypes":[] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName","java.rmi.MarshalledObject","java.lang.String[]","javax.security.auth.Subject"] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName","javax.management.ObjectName","java.rmi.MarshalledObject","java.lang.String[]","javax.security.auth.Subject"] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName","javax.management.ObjectName","javax.security.auth.Subject"] }, {"name":"createMBean","parameterTypes":["java.lang.String","javax.management.ObjectName","javax.security.auth.Subject"] }, {"name":"fetchNotifications","parameterTypes":["long","int","long"] }, {"name":"getAttribute","parameterTypes":["javax.management.ObjectName","java.lang.String","javax.security.auth.Subject"] }, {"name":"getAttributes","parameterTypes":["javax.management.ObjectName","java.lang.String[]","javax.security.auth.Subject"] }, {"name":"getConnectionId","parameterTypes":[] }, {"name":"getDefaultDomain","parameterTypes":["javax.security.auth.Subject"] }, {"name":"getDomains","parameterTypes":["javax.security.auth.Subject"] }, {"name":"getMBeanCount","parameterTypes":["javax.security.auth.Subject"] }, {"name":"getMBeanInfo","parameterTypes":["javax.management.ObjectName","javax.security.auth.Subject"] }, {"name":"getObjectInstance","parameterTypes":["javax.management.ObjectName","javax.security.auth.Subject"] }, {"name":"invoke","parameterTypes":["javax.management.ObjectName","java.lang.String","java.rmi.MarshalledObject","java.lang.String[]","javax.security.auth.Subject"] }, {"name":"isInstanceOf","parameterTypes":["javax.management.ObjectName","java.lang.String","javax.security.auth.Subject"] }, {"name":"isRegistered","parameterTypes":["javax.management.ObjectName","javax.security.auth.Subject"] }, {"name":"queryMBeans","parameterTypes":["javax.management.ObjectName","java.rmi.MarshalledObject","javax.security.auth.Subject"] }, {"name":"queryNames","parameterTypes":["javax.management.ObjectName","java.rmi.MarshalledObject","javax.security.auth.Subject"] }, {"name":"removeNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.ObjectName","java.rmi.MarshalledObject","java.rmi.MarshalledObject","javax.security.auth.Subject"] }, {"name":"removeNotificationListener","parameterTypes":["javax.management.ObjectName","javax.management.ObjectName","javax.security.auth.Subject"] }, {"name":"removeNotificationListeners","parameterTypes":["javax.management.ObjectName","java.lang.Integer[]","javax.security.auth.Subject"] }, {"name":"setAttribute","parameterTypes":["javax.management.ObjectName","java.rmi.MarshalledObject","javax.security.auth.Subject"] }, {"name":"setAttributes","parameterTypes":["javax.management.ObjectName","java.rmi.MarshalledObject","javax.security.auth.Subject"] }, {"name":"unregisterMBean","parameterTypes":["javax.management.ObjectName","javax.security.auth.Subject"] }] }, { "name":"javax.management.remote.rmi.RMIConnectionImpl_Skel" }, { "name":"javax.management.remote.rmi.RMIConnectionImpl_Stub", "methods":[{"name":"","parameterTypes":["java.rmi.server.RemoteRef"] }] }, { "name":"javax.management.remote.rmi.RMIServer", "queryAllPublicMethods":true, "methods":[{"name":"getVersion","parameterTypes":[] }, {"name":"newClient","parameterTypes":["java.lang.Object"] }] }, { "name":"javax.management.remote.rmi.RMIServerImpl_Skel" }, { "name":"javax.management.remote.rmi.RMIServerImpl_Stub", "methods":[{"name":"","parameterTypes":["java.rmi.server.RemoteRef"] }] }, { "name":"javax.money.MonetaryAmount" }, { "name":"javax.naming.InitialContext" }, { "name":"javax.naming.NamingException" }, { "name":"javax.naming.directory.DirContext" }, { "name":"javax.naming.directory.InitialDirContext" }, { "name":"javax.naming.ldap.LdapContext" }, { "name":"javax.naming.ldap.LdapName" }, { "name":"javax.naming.ldap.PagedResultsControl" }, { "name":"javax.naming.ldap.PagedResultsResponseControl" }, { "name":"javax.naming.ldap.SortControl" }, { "name":"javax.naming.ldap.SortResponseControl" }, { "name":"javax.security.auth.x500.X500Principal", "fields":[{"name":"thisX500Name"}], "methods":[{"name":"","parameterTypes":["sun.security.x509.X500Name"] }] }, { "name":"javax.smartcardio.CardPermission" }, { "name":"javax.sql.CommonDataSource", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"createShardingKeyBuilder","parameterTypes":[] }] }, { "name":"javax.sql.ConnectionPoolDataSource" }, { "name":"javax.sql.DataSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"createConnectionBuilder","parameterTypes":[] }] }, { "name":"javax.sql.PooledConnection" }, { "name":"javax.sql.XAConnection" }, { "name":"javax.sql.XADataSource" }, { "name":"javax.transaction.xa.XAException" }, { "name":"javax.transaction.xa.XAResource" }, { "name":"javax.transaction.xa.Xid" }, { "name":"jdk.crac.management.CRaCMXBean" }, { "name":"jdk.internal.loader.ClassLoaders$AppClassLoader", "methods":[{"name":"clearCache","parameterTypes":[] }] }, { "name":"jdk.internal.loader.ClassLoaders$PlatformClassLoader", "methods":[{"name":"clearCache","parameterTypes":[] }] }, { "name":"jdk.internal.misc.Unsafe", "methods":[{"name":"getUnsafe","parameterTypes":[] }] }, { "name":"jdk.internal.vm.annotation.IntrinsicCandidate" }, { "name":"jdk.management.jfr.ConfigurationInfo", "queryAllPublicMethods":true }, { "name":"jdk.management.jfr.EventTypeInfo", "queryAllPublicMethods":true }, { "name":"jdk.management.jfr.FlightRecorderMXBean", "queryAllPublicMethods":true }, { "name":"jdk.management.jfr.FlightRecorderMXBeanImpl", "queryAllPublicConstructors":true, "methods":[{"name":"cacheMBeanInfo","parameterTypes":["javax.management.MBeanInfo"] }, {"name":"getCachedMBeanInfo","parameterTypes":[] }, {"name":"getMBeanInfo","parameterTypes":[] }, {"name":"getNotificationInfo","parameterTypes":[] }] }, { "name":"jdk.management.jfr.RecordingInfo", "queryAllPublicMethods":true }, { "name":"jdk.management.jfr.SettingDescriptorInfo", "queryAllPublicMethods":true }, { "name":"kotlin.Metadata" }, { "name":"kotlinx.coroutines.reactor.MonoKt" }, { "name":"kotlinx.serialization.cbor.Cbor" }, { "name":"kotlinx.serialization.json.Json" }, { "name":"kotlinx.serialization.protobuf.ProtoBuf" }, { "name":"libcore.io.Memory" }, { "name":"liquibase.change.DatabaseChange" }, { "name":"liquibase.integration.spring.SpringLiquibase" }, { "name":"okhttp3.OkHttpClient" }, { "name":"oracle.jdbc.OracleConnection" }, { "name":"oracle.jdbc.datasource.OracleDataSource" }, { "name":"oracle.ucp.jdbc.PoolDataSource" }, { "name":"oracle.ucp.jdbc.PoolDataSourceImpl" }, { "name":"org.aopalliance.aop.Advice", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.aopalliance.intercept.Interceptor", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.aopalliance.intercept.MethodInterceptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.aopalliance.intercept.MethodInvocation" }, { "name":"org.apache.catalina.Context" }, { "name":"org.apache.catalina.Engine" }, { "name":"org.apache.catalina.Host" }, { "name":"org.apache.catalina.LifecycleEvent" }, { "name":"org.apache.catalina.LifecycleListener" }, { "name":"org.apache.catalina.Manager" }, { "name":"org.apache.catalina.Valve" }, { "name":"org.apache.catalina.connector.Connector" }, { "name":"org.apache.catalina.core.ApplicationContextFacade", "queryAllDeclaredMethods":true, "methods":[{"name":"addFilter","parameterTypes":["java.lang.String","jakarta.servlet.Filter"] }, {"name":"addFilter","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"addFilter","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"addJspFile","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"addListener","parameterTypes":["java.lang.Class"] }, {"name":"addListener","parameterTypes":["java.lang.String"] }, {"name":"addListener","parameterTypes":["java.util.EventListener"] }, {"name":"addServlet","parameterTypes":["java.lang.String","jakarta.servlet.Servlet"] }, {"name":"addServlet","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"addServlet","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"createFilter","parameterTypes":["java.lang.Class"] }, {"name":"createListener","parameterTypes":["java.lang.Class"] }, {"name":"createServlet","parameterTypes":["java.lang.Class"] }, {"name":"declareRoles","parameterTypes":["java.lang.String[]"] }, {"name":"getAttribute","parameterTypes":["java.lang.String"] }, {"name":"getAttributeNames","parameterTypes":[] }, {"name":"getClassLoader","parameterTypes":[] }, {"name":"getContext","parameterTypes":["java.lang.String"] }, {"name":"getContextPath","parameterTypes":[] }, {"name":"getDefaultSessionTrackingModes","parameterTypes":[] }, {"name":"getEffectiveMajorVersion","parameterTypes":[] }, {"name":"getEffectiveMinorVersion","parameterTypes":[] }, {"name":"getEffectiveSessionTrackingModes","parameterTypes":[] }, {"name":"getFilterRegistration","parameterTypes":["java.lang.String"] }, {"name":"getFilterRegistrations","parameterTypes":[] }, {"name":"getInitParameter","parameterTypes":["java.lang.String"] }, {"name":"getInitParameterNames","parameterTypes":[] }, {"name":"getJspConfigDescriptor","parameterTypes":[] }, {"name":"getMajorVersion","parameterTypes":[] }, {"name":"getMimeType","parameterTypes":["java.lang.String"] }, {"name":"getMinorVersion","parameterTypes":[] }, {"name":"getNamedDispatcher","parameterTypes":["java.lang.String"] }, {"name":"getRealPath","parameterTypes":["java.lang.String"] }, {"name":"getRequestCharacterEncoding","parameterTypes":[] }, {"name":"getRequestDispatcher","parameterTypes":["java.lang.String"] }, {"name":"getResource","parameterTypes":["java.lang.String"] }, {"name":"getResourceAsStream","parameterTypes":["java.lang.String"] }, {"name":"getResourcePaths","parameterTypes":["java.lang.String"] }, {"name":"getResponseCharacterEncoding","parameterTypes":[] }, {"name":"getServerInfo","parameterTypes":[] }, {"name":"getServletContextName","parameterTypes":[] }, {"name":"getServletRegistration","parameterTypes":["java.lang.String"] }, {"name":"getServletRegistrations","parameterTypes":[] }, {"name":"getSessionCookieConfig","parameterTypes":[] }, {"name":"getSessionTimeout","parameterTypes":[] }, {"name":"getVirtualServerName","parameterTypes":[] }, {"name":"log","parameterTypes":["java.lang.String"] }, {"name":"log","parameterTypes":["java.lang.String","java.lang.Throwable"] }, {"name":"removeAttribute","parameterTypes":["java.lang.String"] }, {"name":"setAttribute","parameterTypes":["java.lang.String","java.lang.Object"] }, {"name":"setInitParameter","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"setRequestCharacterEncoding","parameterTypes":["java.lang.String"] }, {"name":"setResponseCharacterEncoding","parameterTypes":["java.lang.String"] }, {"name":"setSessionTimeout","parameterTypes":["int"] }, {"name":"setSessionTrackingModes","parameterTypes":["java.util.Set"] }] }, { "name":"org.apache.catalina.loader.JdbcLeakPrevention", "methods":[{"name":"","parameterTypes":[] }, {"name":"clearJdbcDriverRegistrations","parameterTypes":[] }] }, { "name":"org.apache.catalina.startup.Tomcat" }, { "name":"org.apache.catalina.util.CharsetMapper", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.commons.dbcp2.BasicDataSource" }, { "name":"org.apache.commons.logging.Log" }, { "name":"org.apache.coyote.AbstractProtocol", "methods":[{"name":"getAddress","parameterTypes":[] }, {"name":"getLocalPort","parameterTypes":[] }, {"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setPort","parameterTypes":["int"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }] }, { "name":"org.apache.coyote.ProtocolHandler" }, { "name":"org.apache.coyote.UpgradeProtocol" }, { "name":"org.apache.coyote.http11.AbstractHttp11Protocol", "methods":[{"name":"isSSLEnabled","parameterTypes":[] }] }, { "name":"org.apache.coyote.http11.Http11NioProtocol", "queryAllPublicMethods":true }, { "name":"org.apache.derby.catalog.types.DefaultInfoImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.catalog.types.IndexDescriptorImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.catalog.types.RoutineAliasInfo", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.catalog.types.StatisticsImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.catalog.types.TypeDescriptorImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.catalog.types.TypesImplInstanceGetter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.catalog.types.UserDefinedTypeIdImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.database.Database" }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b00", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b01", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b02", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b03", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b04", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b05", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b06", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac4d6f406ax0191x2b18xfbb9x0000043c86b07", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe00", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe01", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe02", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe03", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe04", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe05", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe06", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe07", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe08", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x2b1dxa15bx00000524fbe09", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f00", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f01", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f02", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f03", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f04", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f05", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f06", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac65f3c17bx0191x54d7xc03cx000004cf75f07", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac96c5c136x0191x54eaxbc3cx000004d718000", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.ac96c5c136x0191x54eaxbc3cx000004d718001", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d600", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d601", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d602", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d603", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d604", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d605", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d606", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d607", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d608", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d609", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d60a", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x1cdcx6260x000004a98d60b", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57800", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57801", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578010", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578011", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578012", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578013", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578014", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578015", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578016", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578017", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af578018", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57802", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57803", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57804", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57805", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57806", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57807", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57808", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af57809", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af5780a", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af5780b", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af5780c", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af5780d", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af5780e", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2097x24bcx000004af5780f", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23580", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23581", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23582", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23583", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23584", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23585", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23586", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23587", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23588", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc23589", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc2358a", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2302xfbfdx000004bc2358b", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2635xed21x000004e166980", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2635xed21x000004e166981", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2635xed21x000004e166982", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2635xed21x000004e166983", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2635xed21x000004e166984", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2635xed21x000004e166985", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2635xed21x000004e166986", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf200", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf201", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf202", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf203", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf204", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf205", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf206", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf207", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf208", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf209", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf20a", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x2636x8f8dx000004ccaf20b", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95800", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95801", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95802", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95803", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95804", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95805", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95806", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95807", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95808", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b95809", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b9580a", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x268ex0d47x0000049b9580b", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718000", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718001", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d7180010", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718002", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718003", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718004", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718005", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718006", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718007", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718008", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d718009", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d71800a", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d71800b", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d71800c", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d71800d", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d71800e", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x54eaxbc3cx000004d71800f", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398100", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398101", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398102", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398103", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398104", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398105", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398106", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398107", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398108", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e398109", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e39810a", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e39810b", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e39810c", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acaddac06ex0191x77e6x43eax000002e39810d", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.accddec133x0191x2097x24bcx000004af57800", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.accddec133x0191x2097x24bcx000004af57801", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acf5fb814dx0191x2097x24bcx000004af57800", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acf5fb814dx0191x2097x24bcx000004af57801", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acf5fb814dx0191x2097x24bcx000004af57802", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acf5fb814dx0191x2097x24bcx000004af57803", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acf5fb814dx0191x2097x24bcx000004af57804", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.exe.acf5fb814dx0191x2097x24bcx000004af57805", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.jdbc.AuthenticationService" }, { "name":"org.apache.derby.iapi.jdbc.DRDAServerStarter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.apache.derby.iapi.jdbc.ResourceAdapter" }, { "name":"org.apache.derby.iapi.services.cache.CacheFactory" }, { "name":"org.apache.derby.iapi.services.cache.ClassSizeCatalogImpl", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.services.compiler.JavaFactory" }, { "name":"org.apache.derby.iapi.services.daemon.DaemonFactory" }, { "name":"org.apache.derby.iapi.services.info.Version", "queryAllPublicConstructors":true }, { "name":"org.apache.derby.iapi.services.io.FormatableHashtable", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.services.io.FormatableIntHolder", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.services.io.FormatableLongHolder", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.services.jmx.ManagementService" }, { "name":"org.apache.derby.iapi.services.loader.ClassFactory" }, { "name":"org.apache.derby.iapi.services.locks.LockFactory" }, { "name":"org.apache.derby.iapi.services.monitor.Monitor", "fields":[{"name":"monitor"}] }, { "name":"org.apache.derby.iapi.services.property.PropertyFactory" }, { "name":"org.apache.derby.iapi.services.property.PropertyValidation", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.services.stream.InfoStreams" }, { "name":"org.apache.derby.iapi.services.timer.TimerFactory" }, { "name":"org.apache.derby.iapi.services.uuid.UUIDFactory" }, { "name":"org.apache.derby.iapi.sql.LanguageFactory" }, { "name":"org.apache.derby.iapi.sql.compile.OptimizerFactory" }, { "name":"org.apache.derby.iapi.sql.compile.TypeCompilerFactory" }, { "name":"org.apache.derby.iapi.sql.conn.LanguageConnectionFactory" }, { "name":"org.apache.derby.iapi.sql.dictionary.DataDictionary" }, { "name":"org.apache.derby.iapi.sql.execute.ExecAggregator" }, { "name":"org.apache.derby.iapi.sql.execute.ExecutionFactory" }, { "name":"org.apache.derby.iapi.store.access.AccessFactory" }, { "name":"org.apache.derby.iapi.store.access.conglomerate.MethodFactory" }, { "name":"org.apache.derby.iapi.store.raw.RawStoreFactory" }, { "name":"org.apache.derby.iapi.store.raw.data.DataFactory" }, { "name":"org.apache.derby.iapi.store.raw.log.LogFactory" }, { "name":"org.apache.derby.iapi.store.raw.xact.TransactionFactory" }, { "name":"org.apache.derby.iapi.types.DTSClassInfo", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.types.DataValueFactory" }, { "name":"org.apache.derby.iapi.types.DataValueFactoryImpl", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.iapi.types.Like", "queryAllPublicMethods":true }, { "name":"org.apache.derby.impl.db.BasicDatabase", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.db.SlaveDatabase" }, { "name":"org.apache.derby.impl.io.DirStorageFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.io.VFMemoryStorageFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.jdbc.SQLExceptionFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.jdbc.authentication.BasicAuthenticationServiceImpl" }, { "name":"org.apache.derby.impl.jdbc.authentication.JNDIAuthenticationService" }, { "name":"org.apache.derby.impl.jdbc.authentication.NativeAuthenticationServiceImpl" }, { "name":"org.apache.derby.impl.jdbc.authentication.NoneAuthenticationServiceImpl", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.jdbc.authentication.SpecificAuthenticationServiceImpl" }, { "name":"org.apache.derby.impl.services.bytecode.BCJava", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.cache.ConcurrentCacheFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.cache.ConcurrentCacheMBeanImpl", "queryAllPublicConstructors":true }, { "name":"org.apache.derby.impl.services.daemon.SingleThreadDaemonFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.jce.JCECipherFactoryBuilder" }, { "name":"org.apache.derby.impl.services.jmx.JMXManagementService", "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.jmx.JMXManagementService$2", "methods":[{"name":"cacheMBeanInfo","parameterTypes":["javax.management.MBeanInfo"] }, {"name":"getCachedMBeanInfo","parameterTypes":[] }, {"name":"getMBeanInfo","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.jmxnone.NoManagementService" }, { "name":"org.apache.derby.impl.services.locks.ConcurrentPool", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.monitor.BaseMonitor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.apache.derby.impl.services.monitor.FileMonitor", "allDeclaredFields":true, "allDeclaredMethods":true, "allDeclaredConstructors":true }, { "name":"org.apache.derby.impl.services.monitor.ModuleInstance", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.apache.derby.impl.services.monitor.ProtocolKey", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.apache.derby.impl.services.monitor.TopService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.apache.derby.impl.services.reflect.ReflectClassesJava2", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.stream.SingleStream", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.timer.SingletonTimerFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.uuid.BasicUUIDFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.services.uuid.BasicUUIDGetter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.GenericLanguageFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.catalog.DD_Version", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.catalog.DataDictionaryImpl", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.BooleanTypeCompiler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.CLOBTypeCompiler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.CharTypeCompiler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.CountAggregateDefinition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.MaxMinAggregateDefinition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.NumericTypeCompiler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.OptimizerFactoryImpl", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.RefTypeCompiler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.TimestampTypeCompiler", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.compile.TypeCompilerFactoryImpl", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.conn.GenericLanguageConnectionFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.execute.ConstantActionActivation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.execute.CountAggregator", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.execute.GenericExecutionFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.execute.MaxMinAggregator", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.sql.execute.RealResultSetStatisticsFactory" }, { "name":"org.apache.derby.impl.sql.execute.xplain.XPLAINFactory" }, { "name":"org.apache.derby.impl.store.access.PC_XenaVersion", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.RllRAMAccessManager", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.btree.BranchControlRow", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.btree.LeafControlRow", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.btree.index.B2I", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.btree.index.B2IFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.btree.index.B2IUndo", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.heap.HeapConglomerateFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.sort.ExternalSortFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.access.sort.UniqueWithDuplicateNullsExternalSortFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.RawStore", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.AllocPage", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.AllocPageOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.BaseDataFileFactory" }, { "name":"org.apache.derby.impl.store.raw.data.BaseDataFileFactoryJ4", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.ContainerOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.ContainerUndoOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.CopyRowsOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.DeleteOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.InitPageOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.InsertOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.InvalidatePageOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.LogicalUndoOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.PurgeOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.SetReservedSpaceOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.StoredPage", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.UpdateFieldOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.data.UpdateOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.log.CheckpointOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.log.ChecksumOperation", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.log.LogCounter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.log.LogRecord", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.log.LogToFile", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.log.ReadOnly", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.xact.BeginXact", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.xact.EndXact", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.xact.TransactionTable", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.xact.TransactionTableEntry", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.xact.XactFactory", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.raw.xact.XactId", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.impl.store.replication.master.MasterController" }, { "name":"org.apache.derby.impl.store.replication.slave.SlaveController" }, { "name":"org.apache.derby.jdbc.AutoloadedDriver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.apache.derby.jdbc.Driver42", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.jdbc.EmbeddedDriver", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.apache.derby.jdbc.InternalDriver" }, { "name":"org.apache.derby.jdbc.JDBC", "queryAllPublicConstructors":true }, { "name":"org.apache.derby.jdbc.ResourceAdapterImpl", "methods":[{"name":"","parameterTypes":[] }, {"name":"getWarnings","parameterTypes":[] }] }, { "name":"org.apache.derby.mbeans.CacheManagerMBean", "queryAllPublicMethods":true }, { "name":"org.apache.derby.mbeans.JDBCMBean", "queryAllPublicMethods":true }, { "name":"org.apache.derby.mbeans.ManagementMBean", "queryAllPublicMethods":true }, { "name":"org.apache.derby.mbeans.VersionMBean", "queryAllPublicMethods":true }, { "name":"org.apache.hc.client5.http.impl.classic.HttpClients" }, { "name":"org.apache.jasper.compiler.JspConfig" }, { "name":"org.apache.jasper.servlet.JspServlet" }, { "name":"org.apache.logging.log4j.core.LoggerContext" }, { "name":"org.apache.logging.log4j.core.config.plugins.Plugin" }, { "name":"org.apache.logging.log4j.core.impl.Log4jContextFactory" }, { "name":"org.apache.pulsar.client.api.PulsarClient" }, { "name":"org.apache.tomcat.jdbc.pool.DataSource" }, { "name":"org.apache.tomcat.jdbc.pool.DataSourceProxy" }, { "name":"org.apache.tomcat.util.net.AbstractEndpoint", "methods":[{"name":"setBindOnInit","parameterTypes":["boolean"] }] }, { "name":"org.apache.tomcat.util.net.NioEndpoint", "queryAllPublicMethods":true }, { "name":"org.apache.tomcat.websocket.server.WsFilter", "queryAllDeclaredMethods":true }, { "name":"org.apache.tomcat.websocket.server.WsSci" }, { "name":"org.aspectj.lang.JoinPoint" }, { "name":"org.aspectj.lang.ProceedingJoinPoint" }, { "name":"org.aspectj.lang.annotation.AfterThrowing", "queryAllDeclaredMethods":true }, { "name":"org.aspectj.lang.annotation.Around", "queryAllDeclaredMethods":true }, { "name":"org.aspectj.lang.annotation.Aspect", "queryAllDeclaredMethods":true }, { "name":"org.aspectj.lang.annotation.Pointcut", "queryAllDeclaredMethods":true }, { "name":"org.aspectj.weaver.Advice" }, { "name":"org.aspectj.weaver.reflect.Java15AnnotationFinder", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.aspectj.weaver.reflect.Java15GenericSignatureInformationProvider", "methods":[{"name":"","parameterTypes":["org.aspectj.weaver.World"] }] }, { "name":"org.aspectj.weaver.reflect.Java15ReflectionBasedReferenceTypeDelegate", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.aspectj.weaver.reflect.ReflectionBasedReferenceTypeDelegate", "fields":[{"name":"myClass"}] }, { "name":"org.aspectj.weaver.reflect.ReflectionVar", "fields":[{"name":"varType"}] }, { "name":"org.aspectj.weaver.reflect.ShadowMatchImpl", "fields":[{"name":"residualTest"}] }, { "name":"org.aspectj.weaver.tools.Jdk14TraceFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.brotli.dec.BrotliInputStream" }, { "name":"org.cache2k.Cache2kBuilder" }, { "name":"org.cache2k.extra.micrometer.Cache2kCacheMetrics" }, { "name":"org.cache2k.extra.spring.SpringCache2kCache" }, { "name":"org.crac.Core" }, { "name":"org.crac.Resource" }, { "name":"org.eclipse.core.runtime$FileLocator" }, { "name":"org.eclipse.core.runtime.FileLocator" }, { "name":"org.eclipse.jetty.client.HttpClient" }, { "name":"org.eclipse.jetty.ee10.webapp.WebAppContext" }, { "name":"org.eclipse.jetty.ee10.websocket.jakarta.server.config.JakartaWebSocketServletContainerInitializer" }, { "name":"org.eclipse.jetty.server.Server" }, { "name":"org.eclipse.jetty.util.Loader" }, { "name":"org.elasticsearch.client.RestClient" }, { "name":"org.elasticsearch.client.RestClientBuilder" }, { "name":"org.flywaydb.core.Flyway" }, { "name":"org.glassfish.jersey.micrometer.server.ObservationApplicationEventListener" }, { "name":"org.glassfish.jersey.server.ResourceConfig" }, { "name":"org.glassfish.jersey.server.spring.SpringComponentProvider" }, { "name":"org.glassfish.jersey.servlet.ServletContainer" }, { "name":"org.graalvm.nativeimage.ImageInfo", "methods":[{"name":"inImageCode","parameterTypes":[] }] }, { "name":"org.h2.Driver" }, { "name":"org.h2.jdbcx.JdbcDataSource" }, { "name":"org.h2.server.web.JakartaWebServlet" }, { "name":"org.infinispan.spring.embedded.provider.SpringEmbeddedCacheManager" }, { "name":"org.influxdb.InfluxDB" }, { "name":"org.jboss.logging.Logger" }, { "name":"org.jctools.queues.BaseMpscLinkedArrayQueueColdProducerFields", "fields":[{"name":"producerLimit"}] }, { "name":"org.jctools.queues.BaseMpscLinkedArrayQueueConsumerFields", "fields":[{"name":"consumerIndex"}] }, { "name":"org.jctools.queues.BaseMpscLinkedArrayQueueProducerFields", "fields":[{"name":"producerIndex"}] }, { "name":"org.jooq.DSLContext" }, { "name":"org.neo4j.driver.Driver" }, { "name":"org.postgresql.ds.PGSimpleDataSource" }, { "name":"org.quartz.Scheduler" }, { "name":"org.reactivestreams.Publisher" }, { "name":"org.robolectric.Robolectric" }, { "name":"org.rocksdb.NativeLibraryLoader", "fields":[{"name":"fallbackJniLibraryFileName"}, {"name":"jniLibraryFileName"}] }, { "name":"org.slf4j.LoggerFactory" }, { "name":"org.slf4j.bridge.SLF4JBridgeHandler" }, { "name":"org.springframework.amqp.rabbit.core.RabbitTemplate" }, { "name":"org.springframework.aop.Advisor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"isPerInstance","parameterTypes":[] }] }, { "name":"org.springframework.aop.ClassFilter" }, { "name":"org.springframework.aop.Pointcut" }, { "name":"org.springframework.aop.PointcutAdvisor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.aop.SpringProxy", "queryAllDeclaredMethods":true }, { "name":"org.springframework.aop.TargetClassAware", "queryAllDeclaredMethods":true }, { "name":"org.springframework.aop.TargetSource" }, { "name":"org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aop.aspectj.annotation.AspectJAdvisorBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aop.aspectj.annotation.AspectJAdvisorFactory" }, { "name":"org.springframework.aop.aspectj.annotation.AspectJBeanFactoryInitializationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"determineBeanType","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"setBeforeExistingAdvisors","parameterTypes":["boolean"] }] }, { "name":"org.springframework.aop.framework.Advised", "queryAllDeclaredMethods":true }, { "name":"org.springframework.aop.framework.AopInfrastructureBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.aop.framework.ProxyConfig", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"copyFrom","parameterTypes":["org.springframework.aop.framework.ProxyConfig"] }, {"name":"isExposeProxy","parameterTypes":[] }, {"name":"isFrozen","parameterTypes":[] }, {"name":"isOpaque","parameterTypes":[] }, {"name":"isOptimize","parameterTypes":[] }, {"name":"isProxyTargetClass","parameterTypes":[] }, {"name":"setExposeProxy","parameterTypes":["boolean"] }, {"name":"setFrozen","parameterTypes":["boolean"] }, {"name":"setOpaque","parameterTypes":["boolean"] }, {"name":"setOptimize","parameterTypes":["boolean"] }, {"name":"setProxyTargetClass","parameterTypes":["boolean"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.aop.framework.ProxyFactory" }, { "name":"org.springframework.aop.framework.ProxyProcessorSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getOrder","parameterTypes":[] }, {"name":"setBeanClassLoader","parameterTypes":["java.lang.ClassLoader"] }, {"name":"setOrder","parameterTypes":["int"] }, {"name":"setProxyClassLoader","parameterTypes":["java.lang.ClassLoader"] }] }, { "name":"org.springframework.aop.framework.adapter.AdvisorAdapterRegistry" }, { "name":"org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"determineBeanType","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"determineCandidateConstructors","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"getEarlyBeanReference","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"isFrozen","parameterTypes":[] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInstantiation","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"postProcessProperties","parameterTypes":["org.springframework.beans.PropertyValues","java.lang.Object","java.lang.String"] }, {"name":"predictBeanType","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"setAdvisorAdapterRegistry","parameterTypes":["org.springframework.aop.framework.adapter.AdvisorAdapterRegistry"] }, {"name":"setApplyCommonInterceptorsFirst","parameterTypes":["boolean"] }, {"name":"setCustomTargetSourceCreators","parameterTypes":["org.springframework.aop.framework.autoproxy.TargetSourceCreator[]"] }, {"name":"setFrozen","parameterTypes":["boolean"] }, {"name":"setInterceptorNames","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.springframework.aop.framework.autoproxy.AbstractBeanFactoryAwareAdvisingPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.aop.framework.autoproxy.TargetSourceCreator" }, { "name":"org.springframework.aop.scope.ScopedObject" }, { "name":"org.springframework.aop.scope.ScopedProxyBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aop.support.AbstractBeanFactoryPointcutAdvisor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getAdvice","parameterTypes":[] }, {"name":"getAdviceBeanName","parameterTypes":[] }, {"name":"setAdvice","parameterTypes":["org.aopalliance.aop.Advice"] }, {"name":"setAdviceBeanName","parameterTypes":["java.lang.String"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.aop.support.AbstractPointcutAdvisor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"hashCode","parameterTypes":[] }, {"name":"setOrder","parameterTypes":["int"] }] }, { "name":"org.springframework.aot.generate.Generated", "queryAllDeclaredMethods":true }, { "name":"org.springframework.aot.generate.GenerationContext" }, { "name":"org.springframework.aot.hint.RuntimeHintsRegistrar", "queryAllPublicMethods":true }, { "name":"org.springframework.aot.hint.annotation.Reflective", "queryAllDeclaredMethods":true }, { "name":"org.springframework.aot.hint.annotation.RegisterReflectionForBinding", "queryAllDeclaredMethods":true }, { "name":"org.springframework.aot.hint.annotation.RegisterReflectionForBindingProcessor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aot.hint.annotation.SimpleReflectiveProcessor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aot.hint.support.KotlinDetectorRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aot.hint.support.ObjectToObjectConverterRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aot.hint.support.PathMatchingResourcePatternResolverRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aot.hint.support.SpringFactoriesLoaderRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.aot.hint.support.SpringPropertiesRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.batch.core.configuration.annotation.BatchObservabilityBeanPostProcessor" }, { "name":"org.springframework.batch.core.launch.JobLauncher" }, { "name":"org.springframework.beans.BeanUtilsRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.beans.BeanWrapper" }, { "name":"org.springframework.beans.BeansException" }, { "name":"org.springframework.beans.PropertyEditorRegistry" }, { "name":"org.springframework.beans.PropertyValues" }, { "name":"org.springframework.beans.factory.Aware", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.beans.factory.BeanClassLoaderAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.BeanCreationException" }, { "name":"org.springframework.beans.factory.BeanFactory" }, { "name":"org.springframework.beans.factory.BeanFactoryAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.BeanNameAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.DisposableBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"destroy","parameterTypes":[] }] }, { "name":"org.springframework.beans.factory.FactoryBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"isSingleton","parameterTypes":[] }] }, { "name":"org.springframework.beans.factory.InitializingBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.ListableBeanFactory" }, { "name":"org.springframework.beans.factory.NoSuchBeanDefinitionException" }, { "name":"org.springframework.beans.factory.ObjectProvider" }, { "name":"org.springframework.beans.factory.SmartInitializingSingleton", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.annotation.Autowired", "queryAllDeclaredMethods":true }, { "name":"org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"determineBeanType","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"determineCandidateConstructors","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"postProcessMergedBeanDefinition","parameterTypes":["org.springframework.beans.factory.support.RootBeanDefinition","java.lang.Class","java.lang.String"] }, {"name":"postProcessProperties","parameterTypes":["org.springframework.beans.PropertyValues","java.lang.Object","java.lang.String"] }, {"name":"processAheadOfTime","parameterTypes":["org.springframework.beans.factory.support.RegisteredBean"] }, {"name":"resetBeanDefinition","parameterTypes":["java.lang.String"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addDestroyAnnotationType","parameterTypes":["java.lang.Class"] }, {"name":"addInitAnnotationType","parameterTypes":["java.lang.Class"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeDestruction","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"requiresDestruction","parameterTypes":["java.lang.Object"] }, {"name":"setDestroyAnnotationType","parameterTypes":["java.lang.Class"] }, {"name":"setInitAnnotationType","parameterTypes":["java.lang.Class"] }, {"name":"setOrder","parameterTypes":["int"] }] }, { "name":"org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata" }, { "name":"org.springframework.beans.factory.annotation.InjectionMetadata" }, { "name":"org.springframework.beans.factory.annotation.JakartaAnnotationsRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.beans.factory.annotation.Qualifier", "queryAllDeclaredMethods":true }, { "name":"org.springframework.beans.factory.annotation.Value", "queryAllDeclaredMethods":true }, { "name":"org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution" }, { "name":"org.springframework.beans.factory.aot.BeanFactoryInitializationAotProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.aot.BeanFactoryInitializationCode" }, { "name":"org.springframework.beans.factory.aot.BeanRegistrationAotContribution" }, { "name":"org.springframework.beans.factory.aot.BeanRegistrationAotProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"isBeanExcludedFromAotProcessing","parameterTypes":[] }] }, { "name":"org.springframework.beans.factory.aot.BeanRegistrationCodeFragments" }, { "name":"org.springframework.beans.factory.aot.BeanRegistrationsAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.beans.factory.config.AutowireCapableBeanFactory" }, { "name":"org.springframework.beans.factory.config.BeanDefinition" }, { "name":"org.springframework.beans.factory.config.BeanDefinitionHolder" }, { "name":"org.springframework.beans.factory.config.BeanFactoryPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.config.BeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }] }, { "name":"org.springframework.beans.factory.config.ConfigurableBeanFactory" }, { "name":"org.springframework.beans.factory.config.ConfigurableListableBeanFactory" }, { "name":"org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"postProcessAfterInstantiation","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInstantiation","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"postProcessProperties","parameterTypes":["org.springframework.beans.PropertyValues","java.lang.Object","java.lang.String"] }] }, { "name":"org.springframework.beans.factory.config.PlaceholderConfigurerSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"setBeanName","parameterTypes":["java.lang.String"] }, {"name":"setIgnoreUnresolvablePlaceholders","parameterTypes":["boolean"] }, {"name":"setNullValue","parameterTypes":["java.lang.String"] }, {"name":"setPlaceholderPrefix","parameterTypes":["java.lang.String"] }, {"name":"setPlaceholderSuffix","parameterTypes":["java.lang.String"] }, {"name":"setTrimValues","parameterTypes":["boolean"] }, {"name":"setValueSeparator","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.beans.factory.config.PropertyResourceConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getOrder","parameterTypes":[] }, {"name":"setOrder","parameterTypes":["int"] }] }, { "name":"org.springframework.beans.factory.config.SmartInstantiationAwareBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"determineCandidateConstructors","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"getEarlyBeanReference","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"predictBeanType","parameterTypes":["java.lang.Class","java.lang.String"] }] }, { "name":"org.springframework.beans.factory.generator.BeanRegistrationContributionProvider" }, { "name":"org.springframework.beans.factory.parsing.ProblemReporter" }, { "name":"org.springframework.beans.factory.parsing.SourceExtractor" }, { "name":"org.springframework.beans.factory.support.AutowireCandidateResolver" }, { "name":"org.springframework.beans.factory.support.BeanDefinitionRegistry" }, { "name":"org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.beans.factory.support.BeanNameGenerator" }, { "name":"org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"resetBeanDefinition","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.beans.factory.support.NullBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"hashCode","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.beans.factory.support.RegisteredBean" }, { "name":"org.springframework.beans.factory.support.RootBeanDefinition" }, { "name":"org.springframework.boot.ApplicationContextFactory", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.ClearCachesApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.LazyInitializationExcludeFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.SpringApplication", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.SpringApplication$SpringApplicationRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.SpringApplication.SpringApplicationRuntimeHints" }, { "name":"org.springframework.boot.SpringApplicationBannerPrinter$SpringApplicationBannerPrinterRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.SpringApplicationBannerPrinter.SpringApplicationBannerPrinterRuntimeHints" }, { "name":"org.springframework.boot.SpringApplicationRunListener", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.SpringBootConfiguration", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.SpringBootExceptionReporter", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.WebApplicationType$WebApplicationTypeRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.WebApplicationType.WebApplicationTypeRuntimeHints" }, { "name":"org.springframework.boot.actuate.audit.AuditEventRepository" }, { "name":"org.springframework.boot.actuate.audit.AuditEventsEndpoint" }, { "name":"org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.availability.AvailabilityHealthContributorAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.availability.AvailabilityProbesAutoConfiguration$ProbesCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"beansEndpoint","parameterTypes":["org.springframework.context.ConfigurableApplicationContext"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.cache.CachesEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"cachesEndpoint","parameterTypes":["java.util.Map"] }, {"name":"cachesEndpointWebExtension","parameterTypes":["org.springframework.boot.actuate.cache.CachesEndpoint"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint$ConditionsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint$ContextConditionsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint$MessageAndConditionDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpoint$MessageAndConditionsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"conditionsReportEndpoint","parameterTypes":["org.springframework.context.ConfigurableApplicationContext"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"configurationPropertiesReportEndpoint","parameterTypes":["org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointProperties","org.springframework.beans.factory.ObjectProvider"] }, {"name":"configurationPropertiesReportEndpointWebExtension","parameterTypes":["org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint","org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"endpointCachingOperationInvokerAdvisor","parameterTypes":["org.springframework.core.env.Environment"] }, {"name":"endpointOperationParameterMapper","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.condition.OnAvailableEndpointCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.expose.EndpointExposure" }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.expose.IncludeExcludeEndpointFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"match","parameterTypes":["org.springframework.boot.actuate.endpoint.ExposableEndpoint"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.jackson.JacksonEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"endpointObjectMapper","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.jmx.DefaultEndpointObjectNameFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getObjectName","parameterTypes":["org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointProperties","org.springframework.boot.autoconfigure.jmx.JmxProperties"] }, {"name":"eagerlyInitializeJmxEndpointExporter","parameterTypes":[] }, {"name":"endpointObjectNameFactory","parameterTypes":["javax.management.MBeanServer"] }, {"name":"jmxAnnotationEndpointDiscoverer","parameterTypes":["org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"jmxIncludeExcludePropertyEndpointFilter","parameterTypes":[] }, {"name":"jmxMBeanExporter","parameterTypes":["javax.management.MBeanServer","org.springframework.boot.actuate.endpoint.jmx.EndpointObjectNameFactory","org.springframework.beans.factory.ObjectProvider","org.springframework.boot.actuate.endpoint.jmx.JmxEndpointsSupplier"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getExposure","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointProperties$Exposure", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getInclude","parameterTypes":[] }, {"name":"setInclude","parameterTypes":["java.util.Set"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.MappingWebEndpointPathMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getRootPath","parameterTypes":["org.springframework.boot.actuate.endpoint.EndpointId"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"servletExposeExcludePropertyEndpointFilter","parameterTypes":["org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"servletEndpointRegistrar","parameterTypes":["org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties","org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier","org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties"] }, {"name":"controllerEndpointDiscoverer","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"controllerExposeExcludePropertyEndpointFilter","parameterTypes":[] }, {"name":"endpointMediaTypes","parameterTypes":[] }, {"name":"pathMappedEndpoints","parameterTypes":["java.util.Collection"] }, {"name":"webEndpointDiscoverer","parameterTypes":["org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper","org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"webEndpointPathMapper","parameterTypes":[] }, {"name":"webExposeExcludePropertyEndpointFilter","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration$WebEndpointServletConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"servletEndpointDiscoverer","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getExposure","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties$Discovery", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties$Exposure", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getInclude","parameterTypes":[] }, {"name":"setInclude","parameterTypes":["java.util.Set"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"controllerEndpointHandlerMapping","parameterTypes":["org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier","org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties","org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties"] }, {"name":"endpointObjectMapperWebMvcConfigurer","parameterTypes":["org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper"] }, {"name":"webEndpointServletHandlerMapping","parameterTypes":["org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier","org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier","org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier","org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes","org.springframework.boot.actuate.autoconfigure.endpoint.web.CorsEndpointProperties","org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties","org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.endpoint.web.servlet.WebMvcEndpointManagementContextConfiguration$EndpointObjectMapperWebMvcConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"configureMessageConverters","parameterTypes":["java.util.List"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"environmentEndpoint","parameterTypes":["org.springframework.core.env.Environment","org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointProperties","org.springframework.beans.factory.ObjectProvider"] }, {"name":"environmentEndpointWebExtension","parameterTypes":["org.springframework.boot.actuate.env.EnvironmentEndpoint","org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.AutoConfiguredHealthContributorRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"registerContributor","parameterTypes":["java.lang.String","java.lang.Object"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.AutoConfiguredHealthEndpointGroups", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"getNames","parameterTypes":[] }, {"name":"getPrimary","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.ConditionalOnEnabledHealthIndicator", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"pingHealthContributor","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"healthContributorRegistry","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.boot.actuate.health.HealthEndpointGroups","java.util.Map","java.util.Map"] }, {"name":"healthEndpoint","parameterTypes":["org.springframework.boot.actuate.health.HealthContributorRegistry","org.springframework.boot.actuate.health.HealthEndpointGroups","org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties"] }, {"name":"healthEndpointGroupMembershipValidator","parameterTypes":["org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties","org.springframework.boot.actuate.health.HealthContributorRegistry"] }, {"name":"healthEndpointGroups","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties"] }, {"name":"healthEndpointGroupsBeanPostProcessor","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }, {"name":"healthHttpCodeStatusMapper","parameterTypes":["org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties"] }, {"name":"healthStatusAggregator","parameterTypes":["org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointConfiguration$HealthEndpointGroupMembershipValidator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterSingletonsInstantiated","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointConfiguration$HealthEndpointGroupsBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getShowDetails","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties$Group", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties$Logging", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointReactiveWebExtensionConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"healthEndpointWebExtension","parameterTypes":["org.springframework.boot.actuate.health.HealthContributorRegistry","org.springframework.boot.actuate.health.HealthEndpointGroups","org.springframework.boot.actuate.autoconfigure.health.HealthEndpointProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"healthEndpointWebMvcHandlerMapping","parameterTypes":["org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier","org.springframework.boot.actuate.health.HealthEndpointGroups"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getRoles","parameterTypes":[] }, {"name":"getShowComponents","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }, {"name":"setRoles","parameterTypes":["java.util.Set"] }, {"name":"setShowComponents","parameterTypes":["org.springframework.boot.actuate.endpoint.Show"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.HealthProperties$Status", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.health.NoSuchHealthContributorFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.OnEnabledHealthIndicatorCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.health.ReactiveHealthEndpointConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.info.ConditionalOnEnabledInfoContributor", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.info.InfoContributorAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.info.InfoContributorFallback" }, { "name":"org.springframework.boot.actuate.autoconfigure.info.InfoContributorProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.info.InfoContributorProperties$Git", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"infoEndpoint","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.info.OnEnabledInfoContributorCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthContributorAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }, {"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"dbHealthContributor","parameterTypes":["java.util.Map","org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.jdbc.DataSourceHealthIndicatorProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointAutoConfiguration$LogFileCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.logging.LogFileWebEndpointProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"loggersEndpoint","parameterTypes":["org.springframework.boot.logging.LoggingSystem","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration$OnEnabledLoggingSystemCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.management.HeapDumpWebEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"heapDumpWebEndpoint","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dumpEndpoint","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.AutoTimeProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryConfiguration$MultipleNonPrimaryMeterRegistriesCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.JvmMetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"classLoaderMetrics","parameterTypes":[] }, {"name":"jvmCompilationMetrics","parameterTypes":[] }, {"name":"jvmGcMetrics","parameterTypes":[] }, {"name":"jvmHeapPressureMetrics","parameterTypes":[] }, {"name":"jvmInfoMetrics","parameterTypes":[] }, {"name":"jvmMemoryMetrics","parameterTypes":[] }, {"name":"jvmThreadMetrics","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.LogbackMetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"logbackMetrics","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.LogbackMetricsAutoConfiguration$LogbackLoggingCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer" }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterSingletonsInstantiated","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsAspectsAutoConfiguration$ObservationAnnotationsEnabledCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"meterRegistryCloser","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }, {"name":"meterRegistryPostProcessor","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"micrometerClock","parameterTypes":[] }, {"name":"propertiesMeterFilter","parameterTypes":["org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration$MeterRegistryCloser", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"metricsEndpoint","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$Data", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$Data$Repository", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$Distribution", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$System", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$System$Diskspace", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$Web", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$Web$Client", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties$Web$Server", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.NoOpMeterRegistryConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.PropertiesMeterFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"accept","parameterTypes":["io.micrometer.core.instrument.Meter$Id"] }, {"name":"close","parameterTypes":[] }, {"name":"configure","parameterTypes":["io.micrometer.core.instrument.Meter$Id","io.micrometer.core.instrument.distribution.DistributionStatisticConfig"] }, {"name":"map","parameterTypes":["io.micrometer.core.instrument.Meter$Id"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.ServiceLevelObjectiveBoundary", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.ServiceLevelObjectiveBoundary$ServiceLevelObjectiveBoundaryHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.ServiceLevelObjectiveBoundary.ServiceLevelObjectiveBoundaryHints" }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.SystemMetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"diskSpaceMetrics","parameterTypes":["org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties"] }, {"name":"fileDescriptorMetrics","parameterTypes":[] }, {"name":"processorMetrics","parameterTypes":[] }, {"name":"uptimeMetrics","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.ValidationFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.ConditionalOnEnabledMetricsExport", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.OnMetricsExportEnabledCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusMetricsExportAutoConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties$HistogramFlavor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties$Pushgateway", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusSimpleclientMetricsExportAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"collectorRegistry","parameterTypes":[] }, {"name":"simpleclientPrometheusConfig","parameterTypes":["org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusProperties"] }, {"name":"simpleclientPrometheusMeterRegistry","parameterTypes":["io.micrometer.prometheus.PrometheusConfig","io.prometheus.client.CollectorRegistry","io.micrometer.core.instrument.Clock","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusSimpleclientMetricsExportAutoConfiguration$PrometheusScrapeEndpointConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"prometheusEndpoint","parameterTypes":["io.prometheus.client.CollectorRegistry"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.prometheus.PrometheusSimpleclientPropertiesConfigAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"descriptions","parameterTypes":[] }, {"name":"get","parameterTypes":["java.lang.String"] }, {"name":"histogramFlavor","parameterTypes":[] }, {"name":"prefix","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"step","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.properties.PropertiesConfigAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.export.simple.SimpleMetricsExportAutoConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.integration.IntegrationMetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration$DataSourcePoolMetadataMetricsConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dataSourcePoolMetadataMeterBinder","parameterTypes":["java.util.Map","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration$DataSourcePoolMetadataMetricsConfiguration$DataSourcePoolMetadataMeterBinder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration$HikariDataSourceMetricsConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"hikariDataSourceMeterBinder","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.jdbc.DataSourcePoolMetricsAutoConfiguration$HikariDataSourceMetricsConfiguration$HikariDataSourceMeterBinder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.startup.StartupTimeMetricsListenerAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"startupTimeMetrics","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.task.TaskExecutorMetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"bindTaskExecutorsToRegistry","parameterTypes":["java.util.Map","io.micrometer.core.instrument.MeterRegistry"] }, {"name":"eagerTaskExecutorMetrics","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.metrics.web.tomcat.TomcatMetricsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"tomcatMetricsBinder","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"observationRegistry","parameterTypes":[] }, {"name":"observationRegistryPostProcessor","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"propertiesObservationFilter","parameterTypes":["org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration$MeterObservationHandlerConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration$MeterObservationHandlerConfiguration$OnlyMetricsMeterObservationHandlerConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"defaultMeterObservationHandler","parameterTypes":["io.micrometer.core.instrument.MeterRegistry","org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration$ObservedAspectConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"observedAspect","parameterTypes":["io.micrometer.observation.ObservationRegistry"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationAutoConfiguration$OnlyMetricsConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"metricsObservationHandlerGrouping","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationHandlerGrouping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties$Http", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties$Http$Client", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties$Http$Client$ClientRequests", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties$Http$Server", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties$Http$Server$ServerRequests", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties$LongTaskTimer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryConfigurer" }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.ObservationRegistryPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.PropertiesObservationFilterPredicate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"map","parameterTypes":["io.micrometer.observation.Observation$Context"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"test","parameterTypes":["java.lang.Object","java.lang.Object"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.web.client.HttpClientObservationsAutoConfiguration$MeterFilterConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"metricsHttpClientUriTagFilter","parameterTypes":["org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties","org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.web.client.RestClientObservationConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"observationRestClientCustomizer","parameterTypes":["io.micrometer.observation.ObservationRegistry","org.springframework.beans.factory.ObjectProvider","org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.web.client.RestTemplateObservationConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"observationRestTemplateCustomizer","parameterTypes":["io.micrometer.observation.ObservationRegistry","org.springframework.beans.factory.ObjectProvider","org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.web.client.WebClientObservationConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"webMvcObservationFilter","parameterTypes":["io.micrometer.observation.ObservationRegistry","org.springframework.beans.factory.ObjectProvider","org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.observation.web.servlet.WebMvcObservationAutoConfiguration$MeterFilterConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"metricsHttpServerUriTagFilter","parameterTypes":["org.springframework.boot.actuate.autoconfigure.observation.ObservationProperties","org.springframework.boot.actuate.autoconfigure.metrics.MetricsProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.sbom.SbomEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.actuate.sbom.SbomProperties"] }, {"name":"sbomEndpoint","parameterTypes":["org.springframework.core.io.ResourceLoader"] }, {"name":"sbomEndpointWebExtension","parameterTypes":["org.springframework.boot.actuate.sbom.SbomEndpoint"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"scheduledTasksEndpoint","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksObservabilityAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"observabilitySchedulingConfigurer","parameterTypes":["io.micrometer.observation.ObservationRegistry"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.scheduling.ScheduledTasksObservabilityAutoConfiguration$ObservabilitySchedulingConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"configureTasks","parameterTypes":["org.springframework.scheduling.config.ScheduledTaskRegistrar"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.security.servlet.SecurityRequestMatchersManagementContextConfiguration$MvcRequestMatcherConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"requestMatcherProvider","parameterTypes":["org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.startup.StartupEndpointAutoConfiguration$ApplicationStartupCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"diskSpaceHealthIndicator","parameterTypes":["org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorProperties"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthIndicatorProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.tracing.LogCorrelationEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.tracing.MicrometerTracingAutoConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.web.ManagementContextFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.ManagementContextType" }, { "name":"org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesAutoConfiguration" }, { "name":"org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"mappingsEndpoint","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration$ServletWebConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"filterMappingDescriptionProvider","parameterTypes":[] }, {"name":"servletMappingDescriptionProvider","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration$ServletWebConfiguration$SpringMvcConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dispatcherServletMappingDescriptionProvider","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.ConditionalOnManagementPort", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.EnableManagementContext", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$SameManagementContextConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment"] }, {"name":"afterSingletonsInstantiated","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration$SameManagementContextConfiguration$EnableSameManagementContextConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextConfigurationImportSelector", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.ManagementPortType" }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.ManagementServerProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.server.OnManagementPortCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.servlet.ManagementServletContext", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.autoconfigure.web.servlet.ServletManagementContextAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"managementServletContext","parameterTypes":["org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointProperties"] }, {"name":"servletWebChildContextFactory","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.availability.LivenessStateHealthIndicator" }, { "name":"org.springframework.boot.actuate.availability.ReadinessStateHealthIndicator" }, { "name":"org.springframework.boot.actuate.beans.BeansEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"beans","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.beans.BeansEndpoint$BeanDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getAliases","parameterTypes":[] }, {"name":"getDependencies","parameterTypes":[] }, {"name":"getResource","parameterTypes":[] }, {"name":"getScope","parameterTypes":[] }, {"name":"getType","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.beans.BeansEndpoint$BeansDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getContexts","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.beans.BeansEndpoint$ContextBeansDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getBeans","parameterTypes":[] }, {"name":"getParentId","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.cache.CachesEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.cache.CachesEndpoint$CacheDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.cache.CachesEndpoint$CacheEntryDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.cache.CachesEndpoint$CacheManagerDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.cache.CachesEndpoint$CachesDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.cache.CachesEndpointWebExtension", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.context.ShutdownEndpoint" }, { "name":"org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint$ConfigurationPropertiesBeanDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint$ConfigurationPropertiesDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint$ContextConfigurationPropertiesDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpointWebExtension", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.ApiVersion" }, { "name":"org.springframework.boot.actuate.endpoint.EndpointFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.EndpointId" }, { "name":"org.springframework.boot.actuate.endpoint.EndpointsSupplier", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.ExposableEndpoint" }, { "name":"org.springframework.boot.actuate.endpoint.Operation" }, { "name":"org.springframework.boot.actuate.endpoint.OperationResponseBody", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.OperationType" }, { "name":"org.springframework.boot.actuate.endpoint.SecurityContext" }, { "name":"org.springframework.boot.actuate.endpoint.Show" }, { "name":"org.springframework.boot.actuate.endpoint.annotation.DeleteOperation", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationMethod" }, { "name":"org.springframework.boot.actuate.endpoint.annotation.DiscoveredOperationsFactory" }, { "name":"org.springframework.boot.actuate.endpoint.annotation.Endpoint", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.annotation.EndpointConverter", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"createOperationKey","parameterTypes":["org.springframework.boot.actuate.endpoint.Operation"] }, {"name":"getEndpoints","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$EndpointBean" }, { "name":"org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$ExtensionBean" }, { "name":"org.springframework.boot.actuate.endpoint.annotation.EndpointDiscoverer$OperationKey" }, { "name":"org.springframework.boot.actuate.endpoint.annotation.EndpointExtension", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.annotation.FilteredEndpoint", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.annotation.OperationReflectiveProcessor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.annotation.ReadOperation", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.annotation.Selector", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.annotation.WriteOperation", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.invoke.OperationInvoker" }, { "name":"org.springframework.boot.actuate.endpoint.invoke.OperationInvokerAdvisor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.invoke.OperationParameter" }, { "name":"org.springframework.boot.actuate.endpoint.invoke.OperationParameters" }, { "name":"org.springframework.boot.actuate.endpoint.invoke.ParameterMappingException" }, { "name":"org.springframework.boot.actuate.endpoint.invoke.ParameterValueMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.invoke.convert.ConversionServiceParameterValueMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"mapParameterValue","parameterTypes":["org.springframework.boot.actuate.endpoint.invoke.OperationParameter","java.lang.Object"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.invoker.cache.CachingOperationInvokerAdvisor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"apply","parameterTypes":["org.springframework.boot.actuate.endpoint.EndpointId","org.springframework.boot.actuate.endpoint.OperationType","org.springframework.boot.actuate.endpoint.invoke.OperationParameters","org.springframework.boot.actuate.endpoint.invoke.OperationInvoker"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.jmx.EndpointObjectNameFactory", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.jmx.ExposableJmxEndpoint" }, { "name":"org.springframework.boot.actuate.endpoint.jmx.JmxEndpointExporter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"setBeanClassLoader","parameterTypes":["java.lang.ClassLoader"] }] }, { "name":"org.springframework.boot.actuate.endpoint.jmx.JmxEndpointsSupplier", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.jmx.JmxOperation" }, { "name":"org.springframework.boot.actuate.endpoint.jmx.annotation.JmxEndpointDiscoverer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.EndpointMediaTypes", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.ExposableServletEndpoint" }, { "name":"org.springframework.boot.actuate.endpoint.web.ExposableWebEndpoint" }, { "name":"org.springframework.boot.actuate.endpoint.web.Link", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.web.PathMappedEndpoint" }, { "name":"org.springframework.boot.actuate.endpoint.web.PathMappedEndpoints", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"iterator","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.PathMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.ServletEndpointRegistrar", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"onStartup","parameterTypes":["jakarta.servlet.ServletContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.WebEndpointResponse" }, { "name":"org.springframework.boot.actuate.endpoint.web.WebEndpointsSupplier", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.web.WebOperation" }, { "name":"org.springframework.boot.actuate.endpoint.web.WebOperationRequestPredicate" }, { "name":"org.springframework.boot.actuate.endpoint.web.WebServerNamespace" }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointDiscoverer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointDiscoverer$ControllerEndpointDiscovererRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpointsSupplier", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.EndpointWebExtension", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.ExposableControllerEndpoint" }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointDiscoverer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointDiscoverer$ServletEndpointDiscovererRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpointsSupplier", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointDiscoverer$WebEndpointDiscovererRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.annotation.WebEndpointFilter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"getEndpoints","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$AbstractWebMvcEndpointHandlerMappingRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$LinksHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$OperationHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$ServletWebOperation" }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.AdditionalHealthEndpointPathsWebMvcHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.ControllerEndpointHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping$WebMvcEndpointHandlerMappingRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping$WebMvcLinksHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpoint$EnvironmentDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpoint$EnvironmentEntryDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpoint$PropertySourceDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpoint$PropertySourceEntryDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpoint$PropertySummaryDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpoint$PropertyValueDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.env.EnvironmentEndpointWebExtension", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.AbstractHealthIndicator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"health","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.AdditionalHealthEndpointPath" }, { "name":"org.springframework.boot.actuate.health.CompositeHealth", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"getComponents","parameterTypes":[] }, {"name":"getDetails","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.ContributorRegistry", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"registerContributor","parameterTypes":["java.lang.String","java.lang.Object"] }, {"name":"unregisterContributor","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.boot.actuate.health.DefaultContributorRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"registerContributor","parameterTypes":["java.lang.String","java.lang.Object"] }] }, { "name":"org.springframework.boot.actuate.health.DefaultHealthContributorRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getContributor","parameterTypes":["java.lang.String"] }, {"name":"iterator","parameterTypes":[] }, {"name":"registerContributor","parameterTypes":["java.lang.String","java.lang.Object"] }, {"name":"unregisterContributor","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.boot.actuate.health.Health", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getDetails","parameterTypes":[] }, {"name":"getStatus","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.Health$Builder" }, { "name":"org.springframework.boot.actuate.health.HealthComponent", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.health.HealthContributor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.HealthContributorRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.HealthEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"health","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.HealthEndpointGroup" }, { "name":"org.springframework.boot.actuate.health.HealthEndpointGroups", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"get","parameterTypes":["org.springframework.boot.actuate.health.AdditionalHealthEndpointPath"] }, {"name":"getAllWithAdditionalPath","parameterTypes":["org.springframework.boot.actuate.endpoint.web.WebServerNamespace"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.HealthEndpointGroupsPostProcessor" }, { "name":"org.springframework.boot.actuate.health.HealthEndpointSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getHealth","parameterTypes":["java.lang.Object","boolean"] }] }, { "name":"org.springframework.boot.actuate.health.HealthEndpointSupport$HealthResult" }, { "name":"org.springframework.boot.actuate.health.HealthEndpointWebExtension", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.HealthEndpointWebExtensionRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.HealthIndicator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getHealth","parameterTypes":["boolean"] }] }, { "name":"org.springframework.boot.actuate.health.HttpCodeStatusMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.NamedContributors", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"getContributor","parameterTypes":["java.lang.String"] }, {"name":"stream","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.PingHealthIndicator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.SimpleHttpCodeStatusMapper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getStatusCode","parameterTypes":["org.springframework.boot.actuate.health.Status"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.SimpleStatusAggregator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getAggregateStatus","parameterTypes":["java.util.Set"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.Status", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getCode","parameterTypes":[] }, {"name":"getDescription","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.StatusAggregator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getAggregateStatus","parameterTypes":["org.springframework.boot.actuate.health.Status[]"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.health.SystemHealth", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getGroups","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.info.EnvironmentInfoContributor" }, { "name":"org.springframework.boot.actuate.info.GitInfoContributor" }, { "name":"org.springframework.boot.actuate.info.InfoContributor" }, { "name":"org.springframework.boot.actuate.info.InfoEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.info.JavaInfoContributor" }, { "name":"org.springframework.boot.actuate.info.OsInfoContributor" }, { "name":"org.springframework.boot.actuate.info.ProcessInfoContributor" }, { "name":"org.springframework.boot.actuate.jdbc.DataSourceHealthIndicator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.logging.LogFileWebEndpoint" }, { "name":"org.springframework.boot.actuate.logging.LoggersEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.logging.LoggersEndpoint$GroupLoggerLevelsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.logging.LoggersEndpoint$LoggerLevelsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.logging.LoggersEndpoint$LoggersDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.logging.LoggersEndpoint$SingleLoggerLevelsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.management.HeapDumpWebEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.management.HeapDumpWebEndpoint$HeapDumper" }, { "name":"org.springframework.boot.actuate.management.HeapDumpWebEndpoint$HeapDumperUnavailableException" }, { "name":"org.springframework.boot.actuate.management.ThreadDumpEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.management.ThreadDumpEndpoint$ThreadDumpDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.metrics.MetricsEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.metrics.MetricsEndpoint$AvailableTag", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.metrics.MetricsEndpoint$MetricDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.metrics.MetricsEndpoint$MetricNamesDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.metrics.MetricsEndpoint$Sample", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.metrics.cache.CacheMeterBinderProvider" }, { "name":"org.springframework.boot.actuate.metrics.export.prometheus.PrometheusScrapeEndpoint" }, { "name":"org.springframework.boot.actuate.metrics.export.prometheus.PrometheusSimpleclientScrapeEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.metrics.export.prometheus.TextOutputFormat" }, { "name":"org.springframework.boot.actuate.metrics.startup.StartupTimeMetricsListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"supportsEventType","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.boot.actuate.metrics.system.DiskSpaceMetricsBinder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"bindTo","parameterTypes":["io.micrometer.core.instrument.MeterRegistry"] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.metrics.web.client.ObservationRestClientCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.web.client.RestClient$Builder"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.metrics.web.client.ObservationRestTemplateCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.web.client.RestTemplate"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.metrics.web.tomcat.TomcatMetricsBinder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"destroy","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }] }, { "name":"org.springframework.boot.actuate.sbom.SbomEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.sbom.SbomEndpoint$SbomEndpointRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.sbom.SbomEndpoint$Sboms", "allDeclaredFields":true, "allRecordComponents":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.sbom.SbomEndpointWebExtension", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.sbom.SbomEndpointWebExtension$SbomType" }, { "name":"org.springframework.boot.actuate.sbom.SbomProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.sbom.SbomProperties$Sbom", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$CronTaskDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$CustomTriggerTaskDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$FixedDelayTaskDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$FixedRateTaskDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$IntervalTaskDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$RunnableDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$ScheduledTasksDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$ScheduledTasksEndpointRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.scheduling.ScheduledTasksEndpoint$TaskDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.actuate.system.DiskSpaceHealthIndicator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.exchanges.HttpExchangeRepository" }, { "name":"org.springframework.boot.actuate.web.exchanges.HttpExchangesEndpoint" }, { "name":"org.springframework.boot.actuate.web.mappings.HandlerMethodDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getClassName","parameterTypes":[] }, {"name":"getDescriptor","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.MappingDescriptionProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"describeMappings","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.boot.actuate.web.mappings.MappingsEndpoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"mappings","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.MappingsEndpoint$ApplicationMappingsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getContexts","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.MappingsEndpoint$ContextMappingsDescriptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getMappings","parameterTypes":[] }, {"name":"getParentId","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.DispatcherServletHandlerMappings" }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.DispatcherServletMappingDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getDetails","parameterTypes":[] }, {"name":"getHandler","parameterTypes":[] }, {"name":"getPredicate","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.DispatcherServletMappingDetails", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getHandlerMethod","parameterTypes":[] }, {"name":"getRequestMappingConditions","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.DispatcherServletsMappingDescriptionProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"describeMappings","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"getMappingName","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.DispatcherServletsMappingDescriptionProvider$DispatcherServletsMappingDescriptionProviderRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.FilterRegistrationMappingDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getServletNameMappings","parameterTypes":[] }, {"name":"getUrlPatternMappings","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.FiltersMappingDescriptionProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"describeMappings","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"getMappingName","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.FiltersMappingDescriptionProvider$FiltersMappingDescriptionProviderRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.RegistrationMappingDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"getClassName","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.RequestMappingConditionsDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getConsumes","parameterTypes":[] }, {"name":"getHeaders","parameterTypes":[] }, {"name":"getMethods","parameterTypes":[] }, {"name":"getParams","parameterTypes":[] }, {"name":"getPatterns","parameterTypes":[] }, {"name":"getProduces","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.RequestMappingConditionsDescription$MediaTypeExpressionDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getMediaType","parameterTypes":[] }, {"name":"isNegated","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.RequestMappingConditionsDescription$NameValueExpressionDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getName","parameterTypes":[] }, {"name":"getValue","parameterTypes":[] }, {"name":"isNegated","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.ServletRegistrationMappingDescription", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getMappings","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.ServletsMappingDescriptionProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"describeMappings","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"getMappingName","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.actuate.web.mappings.servlet.ServletsMappingDescriptionProvider$ServletsMappingDescriptionProviderRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.admin.SpringApplicationAdminMXBean", "queryAllPublicMethods":true, "methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"isReady","parameterTypes":[] }] }, { "name":"org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setEnvironment","parameterTypes":["org.springframework.core.env.Environment"] }, {"name":"supportsEventType","parameterTypes":["org.springframework.core.ResolvableType"] }, {"name":"supportsSourceType","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar$SpringApplicationAdmin", "queryAllPublicConstructors":true }, { "name":"org.springframework.boot.ansi.AnsiOutput$Enabled", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.AutoConfiguration", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationExcludeFilter", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationImportFilter", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationImportListener", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationImportSelector", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationImportSelector$AutoConfigurationGroup", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationPackage", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationPackages" }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.springframework.boot.autoconfigure.AutoConfigurationPackages$Registrar", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.AutoConfigureAfter", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.AutoConfigureBefore", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.AutoConfigureOrder", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.BackgroundPreinitializer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.EnableAutoConfiguration", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer$SharedMetadataReaderFactoryBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.SpringBootApplication", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"springApplicationAdminRegistrar","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.autoconfigure.aop.AopAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.aop.AopAutoConfiguration$AspectJAutoProxyingConfiguration$CglibAutoProxyConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.availability.ApplicationAvailabilityAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"applicationAvailability","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.batch.JobRepositoryDependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration" }, { "name":"org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration$CacheConfigurationImportSelector", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.cache.CacheCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.cache.CacheType", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnBean", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnCheckpointRestore", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnClass", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnNotWarDeployment", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnProperty", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnResource", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnThreading", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication$Type" }, { "name":"org.springframework.boot.autoconfigure.condition.OnBeanCondition", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.OnClassCondition", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.OnCloudPlatformCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.OnPropertyCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.OnResourceCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.OnThreadingCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.OnWarDeploymentCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.condition.SearchStrategy" }, { "name":"org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"defaultLifecycleProcessor","parameterTypes":["org.springframework.boot.autoconfigure.context.LifecycleProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.context.LifecycleProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration$ResourceBundleCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"propertySourcesPlaceholderConfigurer","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"persistenceExceptionTranslationPostProcessor","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.autoconfigure.data.redis.RedisUrlSyntaxFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.diagnostics.analyzer.NoSuchBeanDefinitionFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializerDatabaseInitializerDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.flyway.ResourceProviderCustomizerBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider$FreeMarkerTemplateAvailabilityRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.freemarker.FreeMarkerTemplateAvailabilityProvider.FreeMarkerTemplateAvailabilityRuntimeHints" }, { "name":"org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider$GroovyTemplateAvailabilityRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAvailabilityProvider.GroovyTemplateAvailabilityRuntimeHints" }, { "name":"org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"gson","parameterTypes":["com.google.gson.GsonBuilder"] }, {"name":"gsonBuilder","parameterTypes":["java.util.List"] }, {"name":"standardGsonBuilderCustomizer","parameterTypes":["org.springframework.boot.autoconfigure.gson.GsonProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration$StandardGsonBuilderCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["com.google.gson.GsonBuilder"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.gson.GsonProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration$JacksonAndJsonbUnavailableCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.GsonHttpMessageConvertersConfiguration$PreferGsonOrJacksonAndJsonbUnavailableCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.HttpMessageConverters", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"iterator","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"messageConverters","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$HttpMessageConvertersAutoConfigurationRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$NotReactiveWebApplicationCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"stringHttpMessageConverter","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.http.JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"mappingJackson2HttpMessageConverter","parameterTypes":["com.fasterxml.jackson.databind.ObjectMapper"] }] }, { "name":"org.springframework.boot.autoconfigure.http.JsonbHttpMessageConvertersConfiguration" }, { "name":"org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.info.ProjectInfoProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration$GitResourceAvailableCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.info.ProjectInfoProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.info.ProjectInfoProperties$Build", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.info.ProjectInfoProperties$Git", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration" }, { "name":"org.springframework.boot.autoconfigure.integration.IntegrationPropertiesEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"jsonComponentModule","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"standardJacksonObjectMapperBuilderCustomizer","parameterTypes":["org.springframework.boot.autoconfigure.jackson.JacksonProperties","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration$StandardJackson2ObjectMapperBuilderCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.http.converter.json.Jackson2ObjectMapperBuilder"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonAutoConfigurationRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonMixinConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"jsonMixinModule","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.boot.jackson.JsonMixinModuleEntries"] }, {"name":"jsonMixinModuleEntries","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"jacksonObjectMapperBuilder","parameterTypes":["org.springframework.context.ApplicationContext","java.util.List"] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$JacksonObjectMapperConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"jacksonObjectMapper","parameterTypes":["org.springframework.http.converter.json.Jackson2ObjectMapperBuilder"] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration$ParameterNamesModuleConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"parameterNamesModule","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration.JacksonAutoConfigurationRuntimeHints" }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonProperties$ConstructorDetectorStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.jackson.JacksonProperties$Datatype", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$EmbeddedDatabaseCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceAvailableCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration$PooledDataSourceConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"jdbcConnectionDetails","parameterTypes":["org.springframework.boot.autoconfigure.jdbc.DataSourceProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceBeanCreationFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceCheckpointRestoreConfiguration" }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration" }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Dbcp2" }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Generic" }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dataSource","parameterTypes":["org.springframework.boot.autoconfigure.jdbc.DataSourceProperties","org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails"] }, {"name":"jdbcConnectionDetailsHikariBeanPostProcessor","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$OracleUcp" }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Tomcat" }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceJmxConfiguration$Hikari", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["javax.sql.DataSource","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"setBeanClassLoader","parameterTypes":["java.lang.ClassLoader"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$Xa", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration$JdbcTransactionManagerConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"transactionManager","parameterTypes":["org.springframework.core.env.Environment","javax.sql.DataSource","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.HikariDriverConfigurationFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.HikariJdbcConnectionDetailsBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.JdbcClientAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"jdbcClient","parameterTypes":["org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetailsBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getOrder","parameterTypes":[] }, {"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"processDataSource","parameterTypes":["java.lang.Object","org.springframework.boot.autoconfigure.jdbc.JdbcConnectionDetails"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.JdbcProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.JdbcProperties$Template", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.JdbcTemplateConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"jdbcTemplate","parameterTypes":["javax.sql.DataSource","org.springframework.boot.autoconfigure.jdbc.JdbcProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.NamedParameterJdbcTemplateConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"namedParameterJdbcTemplate","parameterTypes":["org.springframework.jdbc.core.JdbcTemplate"] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.PropertiesJdbcConnectionDetails", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getDriverClassName","parameterTypes":[] }, {"name":"getJdbcUrl","parameterTypes":[] }, {"name":"getPassword","parameterTypes":[] }, {"name":"getUsername","parameterTypes":[] }, {"name":"getXaDataSourceClassName","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jdbc.metadata.DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"hikariPoolDataSourceMetadataProvider","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.jmx.JmxProperties"] }, {"name":"mbeanExporter","parameterTypes":["org.springframework.jmx.export.naming.ObjectNamingStrategy","org.springframework.beans.factory.BeanFactory"] }, {"name":"mbeanServer","parameterTypes":[] }, {"name":"objectNamingStrategy","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jmx.JmxProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setEnabled","parameterTypes":["boolean"] }] }, { "name":"org.springframework.boot.autoconfigure.jmx.ParentAwareNamingStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getObjectName","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.jooq.NoDslContextBeanFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration" }, { "name":"org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration" }, { "name":"org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.mustache.MustacheTemplateAvailabilityProvider", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.netty.NettyAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.netty.NettyProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.netty.NettyProperties$LeakDetection", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.quartz.SchedulerDependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryBeanCreationFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.autoconfigure.r2dbc.MissingR2dbcPoolDependencyFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.r2dbc.MultipleConnectionPoolConfigurationsFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.r2dbc.NoConnectionFactoryBeanFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.ConditionalOnDefaultWebSecurity", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.security.DefaultWebSecurityCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.SecurityDataConfiguration" }, { "name":"org.springframework.boot.autoconfigure.security.SecurityProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.SecurityProperties$Filter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.security.SecurityProperties$User", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration$RSocketEnabledOrReactiveWebApplication", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.servlet.AntPathRequestMatcherProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getRequestMatcher","parameterTypes":["java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"authenticationEventPublisher","parameterTypes":["org.springframework.context.ApplicationEventPublisher"] }] }, { "name":"org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"securityFilterChainRegistration","parameterTypes":["org.springframework.boot.autoconfigure.security.SecurityProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.servlet.SpringBootWebSecurityConfiguration$WebSecurityEnablerConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration" }, { "name":"org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.service.connection.ConnectionDetails", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.autoconfigure.session.JdbcIndexedSessionRepositoryDependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.sql.init.DataSourceInitializationConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dataSourceScriptDatabaseInitializer","parameterTypes":["javax.sql.DataSource","org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.sql.init.R2dbcInitializationConfiguration" }, { "name":"org.springframework.boot.autoconfigure.sql.init.SqlDataSourceScriptDatabaseInitializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.sql.init.SqlInitializationAutoConfiguration$SqlInitializationModeCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"setPlatform","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.boot.autoconfigure.sql.init.SqlInitializationScriptsRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.sql.init.SqlR2dbcScriptDatabaseInitializer" }, { "name":"org.springframework.boot.autoconfigure.ssl.BundleContentNotWatchableFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.ssl.FileWatcher", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.ssl.JksSslBundleProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.JksSslBundleProperties$Store", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.PemSslBundleProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.PemSslBundleProperties$Store", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.ssl.SslProperties"] }, {"name":"fileWatcher","parameterTypes":[] }, {"name":"sslBundleRegistry","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }, {"name":"sslPropertiesSslBundleRegistrar","parameterTypes":["org.springframework.boot.autoconfigure.ssl.FileWatcher"] }] }, { "name":"org.springframework.boot.autoconfigure.ssl.SslBundleProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslBundleProperties$Key", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslBundleProperties$Options", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslBundleRegistrar", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.ssl.SslProperties$Bundles", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslProperties$Bundles$Watch", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslProperties$Bundles$Watch$File", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.ssl.SslPropertiesBundleRegistrar", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"registerBundles","parameterTypes":["org.springframework.boot.ssl.SslBundleRegistry"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.ssl.SslPropertiesBundleRegistrar$Bundle" }, { "name":"org.springframework.boot.autoconfigure.task.ScheduledBeanLazyInitializationExcludeFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"isExcluded","parameterTypes":["java.lang.String","org.springframework.beans.factory.config.BeanDefinition","java.lang.Class"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutionProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutionProperties$Pool", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutionProperties$Pool$Shutdown", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutionProperties$Shutdown", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutionProperties$Simple", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations" }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$SimpleAsyncTaskExecutorBuilderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.task.TaskExecutionProperties","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"simpleAsyncTaskExecutorBuilder","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$TaskExecutorBuilderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"taskExecutorBuilder","parameterTypes":["org.springframework.boot.autoconfigure.task.TaskExecutionProperties","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$TaskExecutorConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"applicationTaskExecutor","parameterTypes":["org.springframework.boot.task.TaskExecutorBuilder","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskExecutorConfigurations$ThreadPoolTaskExecutorBuilderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"threadPoolTaskExecutorBuilder","parameterTypes":["org.springframework.boot.autoconfigure.task.TaskExecutionProperties","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"scheduledBeanLazyInitializationExcludeFilter","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations" }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$SimpleAsyncTaskSchedulerBuilderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.task.TaskSchedulingProperties","org.springframework.beans.factory.ObjectProvider"] }, {"name":"simpleAsyncTaskSchedulerBuilder","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$TaskSchedulerBuilderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"taskSchedulerBuilder","parameterTypes":["org.springframework.boot.autoconfigure.task.TaskSchedulingProperties","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$TaskSchedulerConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"taskScheduler","parameterTypes":["org.springframework.boot.task.TaskSchedulerBuilder","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingConfigurations$ThreadPoolTaskSchedulerBuilderConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"threadPoolTaskSchedulerBuilder","parameterTypes":["org.springframework.boot.autoconfigure.task.TaskSchedulingProperties","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingProperties$Pool", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingProperties$Shutdown", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.task.TaskSchedulingProperties$Simple", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.template.TemplateAvailabilityProvider", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.template.TemplateRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.thread.Threading" }, { "name":"org.springframework.boot.autoconfigure.thymeleaf.ThymeleafTemplateAvailabilityProvider", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.ExecutionListenersTransactionManagerCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.transaction.TransactionManager"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$EnableTransactionManagementConfiguration$CglibAutoProxyConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration$TransactionTemplateConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"transactionTemplate","parameterTypes":["org.springframework.transaction.PlatformTransactionManager"] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizationAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"platformTransactionManagerCustomizers","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }, {"name":"transactionExecutionListeners","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.transaction.TransactionProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.transaction.TransactionManager"] }] }, { "name":"org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration" }, { "name":"org.springframework.boot.autoconfigure.web.ConditionalOnEnabledResourceChain", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.web.ErrorProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"setIncludeMessage","parameterTypes":["org.springframework.boot.autoconfigure.web.ErrorProperties$IncludeAttribute"] }] }, { "name":"org.springframework.boot.autoconfigure.web.ErrorProperties$IncludeAttribute", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ErrorProperties$Whitelabel", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.OnEnabledResourceChainCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getError","parameterTypes":[] }, {"name":"getServlet","parameterTypes":[] }, {"name":"getTomcat","parameterTypes":[] }, {"name":"setPort","parameterTypes":["java.lang.Integer"] }] }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$ForwardHeadersStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Jetty", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Jetty$Accesslog", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Jetty$Accesslog$FORMAT", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Jetty$Threads", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Netty", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Reactive", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Reactive$Session", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Servlet", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"setContextPath","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getAccesslog","parameterTypes":[] }, {"name":"setBasedir","parameterTypes":["java.io.File"] }] }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat$Accesslog", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"setEnabled","parameterTypes":["boolean"] }, {"name":"setMaxDays","parameterTypes":["int"] }, {"name":"setPattern","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat$Mbeanregistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat$Remoteip", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat$Resource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Tomcat$Threads", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Undertow", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Undertow$Accesslog", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Undertow$Options", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.ServerProperties$Undertow$Threads", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$LocaleResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$Resources", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$Resources$Cache", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$Resources$Cache$Cachecontrol", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$Resources$Chain", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$Resources$Chain$Strategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$Resources$Chain$Strategy$Content", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebProperties$Resources$Chain$Strategy$Fixed", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.WebResourcesRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.client.AutoConfiguredRestClientSsl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"fromBundle","parameterTypes":["java.lang.String"] }, {"name":"fromBundle","parameterTypes":["org.springframework.boot.ssl.SslBundle"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.client.HttpMessageConvertersRestClientCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.web.client.RestClient$Builder"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.client.NotReactiveWebApplicationCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"httpMessageConvertersRestClientCustomizer","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }, {"name":"restClientBuilderConfigurer","parameterTypes":["org.springframework.beans.factory.ObjectProvider"] }, {"name":"restClientSsl","parameterTypes":["org.springframework.boot.ssl.SslBundles"] }] }, { "name":"org.springframework.boot.autoconfigure.web.client.RestClientBuilderConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.client.RestClientSsl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.client.RestTemplateBuilderConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"tomcatWebServerFactoryCustomizer","parameterTypes":["org.springframework.core.env.Environment","org.springframework.boot.autoconfigure.web.ServerProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.web.embedded.TomcatVirtualThreadsWebServerFactoryCustomizer" }, { "name":"org.springframework.boot.autoconfigure.web.embedded.TomcatWebServerFactoryCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.boot.web.server.WebServerFactory"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.format.DateTimeFormatters" }, { "name":"org.springframework.boot.autoconfigure.web.format.WebConversionService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration" }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ConditionalOnMissingFilterBean", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DefaultDispatcherServletCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dispatcherServlet","parameterTypes":["org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"dispatcherServletRegistration","parameterTypes":["org.springframework.web.servlet.DispatcherServlet","org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getPrefix","parameterTypes":[] }, {"name":"getRelativePath","parameterTypes":["java.lang.String"] }, {"name":"getServletUrlMapping","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"addUrlMappings","parameterTypes":["java.lang.String[]"] }, {"name":"close","parameterTypes":[] }, {"name":"getPath","parameterTypes":[] }, {"name":"setUrlMappings","parameterTypes":["java.util.Collection"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.web.ServerProperties"] }, {"name":"characterEncodingFilter","parameterTypes":[] }, {"name":"localeCharsetMappingsCustomizer","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration$LocaleCharsetMappingsCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.boot.web.server.WebServerFactory"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.JspTemplateAvailabilityProvider", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.web.servlet.MultipartProperties"] }, {"name":"multipartConfigElement","parameterTypes":[] }, {"name":"multipartResolver","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.MultipartProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"servletWebServerFactoryCustomizer","parameterTypes":["org.springframework.boot.autoconfigure.web.ServerProperties","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"tomcatServletWebServerFactoryCustomizer","parameterTypes":["org.springframework.boot.autoconfigure.web.ServerProperties"] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration$BeanPostProcessorsRegistrar", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration" }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedJetty" }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedTomcat", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"tomcatServletWebServerFactory","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryConfiguration$EmbeddedUndertow" }, { "name":"org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.boot.web.server.WebServerFactory"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.TomcatServletWebServerFactoryCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.boot.web.server.WebServerFactory"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"formContentFilter","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$EnableWebMvcConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties","org.springframework.boot.autoconfigure.web.WebProperties","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ListableBeanFactory"] }, {"name":"flashMapManager","parameterTypes":[] }, {"name":"localeResolver","parameterTypes":[] }, {"name":"mvcContentNegotiationManager","parameterTypes":[] }, {"name":"mvcConversionService","parameterTypes":[] }, {"name":"mvcValidator","parameterTypes":[] }, {"name":"setResourceLoader","parameterTypes":["org.springframework.core.io.ResourceLoader"] }, {"name":"themeResolver","parameterTypes":[] }, {"name":"welcomePageHandlerMapping","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.resource.ResourceUrlProvider"] }, {"name":"welcomePageNotAcceptableHandlerMapping","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.resource.ResourceUrlProvider"] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.web.WebProperties","org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties","org.springframework.beans.factory.ListableBeanFactory","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"addFormatters","parameterTypes":["org.springframework.format.FormatterRegistry"] }, {"name":"addResourceHandlers","parameterTypes":["org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry"] }, {"name":"configureAsyncSupport","parameterTypes":["org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer"] }, {"name":"configureContentNegotiation","parameterTypes":["org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer"] }, {"name":"configureMessageConverters","parameterTypes":["java.util.List"] }, {"name":"configurePathMatch","parameterTypes":["org.springframework.web.servlet.config.annotation.PathMatchConfigurer"] }, {"name":"defaultViewResolver","parameterTypes":[] }, {"name":"getMessageCodesResolver","parameterTypes":[] }, {"name":"requestContextFilter","parameterTypes":[] }, {"name":"setServletContext","parameterTypes":["jakarta.servlet.ServletContext"] }, {"name":"viewResolver","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration$WelcomePageHandlerMappingFactory" }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$Async", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$Contentnegotiation", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$Format", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$MatchingStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$Pathmatch", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$Problemdetails", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$Servlet", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties$View", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WelcomePage" }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WelcomePageHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.WelcomePageNotAcceptableHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.AbstractErrorController", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.DefaultErrorViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"resolveErrorView","parameterTypes":["jakarta.servlet.http.HttpServletRequest","org.springframework.http.HttpStatus","java.util.Map"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.autoconfigure.web.ServerProperties"] }, {"name":"basicErrorController","parameterTypes":["org.springframework.boot.web.servlet.error.ErrorAttributes","org.springframework.beans.factory.ObjectProvider"] }, {"name":"errorAttributes","parameterTypes":[] }, {"name":"errorPageCustomizer","parameterTypes":["org.springframework.boot.autoconfigure.web.servlet.DispatcherServletPath"] }, {"name":"preserveErrorControllerTargetClassPostProcessor","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.context.ApplicationContext","org.springframework.boot.autoconfigure.web.WebProperties"] }, {"name":"conventionErrorViewResolver","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$ErrorPageCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"registerErrorPages","parameterTypes":["org.springframework.boot.web.server.ErrorPageRegistry"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$ErrorTemplateMissingCondition", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$PreserveErrorControllerTargetClassPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$StaticView", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getContentType","parameterTypes":[] }, {"name":"render","parameterTypes":["java.util.Map","jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"beanNameViewResolver","parameterTypes":[] }, {"name":"defaultErrorView","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.web.servlet.error.ErrorViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.autoconfigure.websocket.servlet.TomcatWebSocketServletWebServerCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"customize","parameterTypes":["org.springframework.boot.web.server.WebServerFactory"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"websocketServletWebServerCustomizer","parameterTypes":[] }] }, { "name":"org.springframework.boot.availability.ApplicationAvailability", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getLivenessState","parameterTypes":[] }, {"name":"getReadinessState","parameterTypes":[] }] }, { "name":"org.springframework.boot.availability.ApplicationAvailabilityBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getLastChangeEvent","parameterTypes":["java.lang.Class"] }, {"name":"getState","parameterTypes":["java.lang.Class"] }, {"name":"getState","parameterTypes":["java.lang.Class","org.springframework.boot.availability.AvailabilityState"] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.availability.AvailabilityChangeEvent" }, { "name":"org.springframework.boot.availability.AvailabilityState" }, { "name":"org.springframework.boot.availability.LivenessState" }, { "name":"org.springframework.boot.availability.ReadinessState" }, { "name":"org.springframework.boot.builder.ParentContextCloserApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.cloud.CloudFoundryVcapEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.logging.DeferredLogFactory"] }] }, { "name":"org.springframework.boot.cloud.CloudPlatform" }, { "name":"org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.ContextIdApplicationContextInitializer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.FileEncodingApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.TypeExcludeFilter", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.AnsiOutputApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.logging.DeferredLogFactory","org.springframework.boot.ConfigurableBootstrapContext"] }] }, { "name":"org.springframework.boot.context.config.ConfigDataLoader", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.context.config.ConfigDataLocation", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"of","parameterTypes":["java.lang.String"] }, {"name":"valueOf","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.boot.context.config.ConfigDataLocationResolver", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.context.config.ConfigDataLocationRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.ConfigDataNotFoundAction", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.ConfigDataNotFoundFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.ConfigDataProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.ConfigDataProperties$Activate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.context.config.ConfigDataPropertiesRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.ConfigTreeConfigDataLoader", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.io.ResourceLoader"] }] }, { "name":"org.springframework.boot.context.config.DelegatingApplicationContextInitializer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.DelegatingApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.StandardConfigDataLoader", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.config.StandardConfigDataLocationResolver", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.logging.DeferredLogFactory","org.springframework.boot.context.properties.bind.Binder","org.springframework.core.io.ResourceLoader"] }] }, { "name":"org.springframework.boot.context.event.ApplicationReadyEvent" }, { "name":"org.springframework.boot.context.event.ApplicationStartedEvent" }, { "name":"org.springframework.boot.context.event.EventPublishingRunListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.SpringApplication","java.lang.String[]"] }] }, { "name":"org.springframework.boot.context.logging.LoggingApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.properties.BoundConfigurationProperties", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.properties.ConfigurationProperties", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.context.properties.ConfigurationPropertiesBean" }, { "name":"org.springframework.boot.context.properties.ConfigurationPropertiesBeanFactoryInitializationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.properties.ConfigurationPropertiesBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.properties.ConfigurationPropertiesBinder" }, { "name":"org.springframework.boot.context.properties.ConfigurationPropertiesBinder$ConfigurationPropertiesBinderFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getObject","parameterTypes":[] }, {"name":"getObjectType","parameterTypes":[] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.boot.context.properties.DeprecatedConfigurationProperty", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.context.properties.EnableConfigurationProperties", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.context.properties.EnableConfigurationPropertiesRegistrar", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.properties.IncompatibleConfigurationFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.properties.NestedConfigurationProperty", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.context.properties.NotConstructorBoundInjectionFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.context.properties.bind.Binder" }, { "name":"org.springframework.boot.context.properties.bind.Name", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.context.properties.bind.Nested", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.context.properties.source.ConfigurationProperty" }, { "name":"org.springframework.boot.context.properties.source.ConfigurationPropertyName" }, { "name":"org.springframework.boot.convert.DurationUnit", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.diagnostics.FailureAnalysisReporter", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.diagnostics.FailureAnalyzer", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.diagnostics.FailureAnalyzers", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.context.ConfigurableApplicationContext"] }] }, { "name":"org.springframework.boot.diagnostics.LoggingFailureAnalysisReporter", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.AotInitializerNotFoundFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.BeanCurrentlyInCreationFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.BeanDefinitionOverrideFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.BeanNotOfRequiredTypeFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.BindFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.BindValidationFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyNameFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.InvalidConfigurationPropertyValueFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.MissingParameterNamesFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.MutuallyExclusiveConfigurationPropertiesFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.NoSuchMethodFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.NoUniqueBeanDefinitionFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.PatternParseFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.PortInUseFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.UnboundConfigurationPropertyFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.diagnostics.analyzer.ValidationExceptionFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true }, { "name":"org.springframework.boot.env.EnvironmentPostProcessor", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.env.EnvironmentPostProcessorApplicationListener", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.env.PropertiesPropertySourceLoader", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.env.PropertySourceLoader", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.env.PropertySourceRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.env.RandomValuePropertySourceEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.logging.DeferredLogFactory"] }] }, { "name":"org.springframework.boot.env.SpringApplicationJsonEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.env.SystemEnvironmentPropertySourceEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.env.YamlPropertySourceLoader", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.flyway.FlywayDatabaseInitializerDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.info.BuildProperties" }, { "name":"org.springframework.boot.info.GitProperties" }, { "name":"org.springframework.boot.io.Base64ProtocolResolver", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.jackson.JsonComponent$Scope" }, { "name":"org.springframework.boot.jackson.JsonComponentModule", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.jackson.JsonComponentModule$JsonComponentBeanFactoryInitializationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.jackson.JsonComponentModule.JsonComponentBeanFactoryInitializationAotProcessor" }, { "name":"org.springframework.boot.jackson.JsonMixinModule", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.jackson.JsonMixinModuleEntries", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.jackson.JsonMixinModuleEntries$Builder" }, { "name":"org.springframework.boot.jackson.JsonMixinModuleEntriesBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.jdbc.DataSourceBuilder" }, { "name":"org.springframework.boot.jdbc.DataSourceBuilderRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.jdbc.EmbeddedDatabaseConnection" }, { "name":"org.springframework.boot.jdbc.SpringJdbcDependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.jdbc.init.DataSourceScriptDatabaseInitializerDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.jdbc.metadata.DataSourcePoolMetadata" }, { "name":"org.springframework.boot.jdbc.metadata.DataSourcePoolMetadataProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.jooq.JooqDependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.json.JacksonRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.liquibase.LiquibaseChangelogMissingFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true }, { "name":"org.springframework.boot.liquibase.LiquibaseDatabaseInitializerDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.LogLevel", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.logging.LoggerGroup" }, { "name":"org.springframework.boot.logging.LoggingSystem" }, { "name":"org.springframework.boot.logging.LoggingSystemFactory", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.logging.java.JavaLoggingSystem$Factory", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.java.JavaLoggingSystem.Factory" }, { "name":"org.springframework.boot.logging.java.JavaLoggingSystemRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.log4j2.Log4J2LoggingSystem$Factory", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.log4j2.Log4J2LoggingSystem.Factory" }, { "name":"org.springframework.boot.logging.logback.ApplicationNameConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.logback.ColorConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.logback.LogbackLoggingSystem$Factory", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.logback.LogbackLoggingSystem.Factory" }, { "name":"org.springframework.boot.logging.logback.LogbackRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.logging.logback.SpringProfileModel", "allDeclaredFields":true }, { "name":"org.springframework.boot.logging.logback.SpringPropertyModel", "allDeclaredFields":true }, { "name":"org.springframework.boot.origin.OriginProvider", "queryAllPublicMethods":true }, { "name":"org.springframework.boot.orm.jpa.JpaDatabaseInitializerDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.orm.jpa.JpaDependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.r2dbc.init.R2dbcScriptDatabaseInitializerDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.reactor.ReactorEnvironmentPostProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"initializeDatabase","parameterTypes":[] }, {"name":"setResourceLoader","parameterTypes":["org.springframework.core.io.ResourceLoader"] }] }, { "name":"org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer$ScriptLocationResolver" }, { "name":"org.springframework.boot.sql.init.AbstractScriptDatabaseInitializer$Scripts" }, { "name":"org.springframework.boot.sql.init.DatabaseInitializationMode" }, { "name":"org.springframework.boot.sql.init.DatabaseInitializationSettings" }, { "name":"org.springframework.boot.sql.init.dependency.AnnotationDependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"setEnvironment","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor$InitializerBeanNames" }, { "name":"org.springframework.boot.sql.init.dependency.DatabaseInitializerDetector", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.sql.init.dependency.DependsOnDatabaseInitializationDetector", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.ssl.DefaultSslBundleRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"addBundleUpdateHandler","parameterTypes":["java.lang.String","java.util.function.Consumer"] }, {"name":"close","parameterTypes":[] }, {"name":"getBundle","parameterTypes":["java.lang.String"] }, {"name":"registerBundle","parameterTypes":["java.lang.String","org.springframework.boot.ssl.SslBundle"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"updateBundle","parameterTypes":["java.lang.String","org.springframework.boot.ssl.SslBundle"] }] }, { "name":"org.springframework.boot.ssl.DefaultSslBundleRegistry$RegisteredSslBundle" }, { "name":"org.springframework.boot.ssl.NoSuchSslBundleException" }, { "name":"org.springframework.boot.ssl.SslBundle" }, { "name":"org.springframework.boot.ssl.SslBundleRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.ssl.SslBundles", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.task.SimpleAsyncTaskExecutorBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.task.SimpleAsyncTaskExecutorCustomizer" }, { "name":"org.springframework.boot.task.SimpleAsyncTaskSchedulerBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.task.SimpleAsyncTaskSchedulerCustomizer" }, { "name":"org.springframework.boot.task.TaskExecutorBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.task.TaskExecutorCustomizer" }, { "name":"org.springframework.boot.task.TaskSchedulerBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.task.TaskSchedulerCustomizer" }, { "name":"org.springframework.boot.task.ThreadPoolTaskExecutorBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.task.ThreadPoolTaskExecutorCustomizer" }, { "name":"org.springframework.boot.task.ThreadPoolTaskSchedulerBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.task.ThreadPoolTaskSchedulerCustomizer" }, { "name":"org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory", "methods":[{"name":"toMetadataReaderFactory","parameterTypes":[] }] }, { "name":"org.springframework.boot.validation.beanvalidation.MethodValidationExcludeFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"byAnnotation","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.boot.web.client.ClientHttpRequestFactoriesRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.client.ClientHttpRequestFactorySettings" }, { "name":"org.springframework.boot.web.client.RestClientCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.client.RestTemplateBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.client.RestTemplateCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.client.RestTemplateRequestCustomizer" }, { "name":"org.springframework.boot.web.context.MissingWebServerFactoryBeanFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.context.WebServerInitializedEvent" }, { "name":"org.springframework.boot.web.embedded.tomcat.ConfigurableTomcatWebServerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.embedded.tomcat.ConnectorStartFailureAnalyzer", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.embedded.tomcat.SslConnectorCustomizer" }, { "name":"org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer" }, { "name":"org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer" }, { "name":"org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedContext" }, { "name":"org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer" }, { "name":"org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"addConnectorCustomizers","parameterTypes":["org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer[]"] }, {"name":"addContextCustomizers","parameterTypes":["org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer[]"] }, {"name":"addEngineValves","parameterTypes":["org.apache.catalina.Valve[]"] }, {"name":"addProtocolHandlerCustomizers","parameterTypes":["org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer[]"] }, {"name":"close","parameterTypes":[] }, {"name":"getWebServer","parameterTypes":["org.springframework.boot.web.servlet.ServletContextInitializer[]"] }, {"name":"setBackgroundProcessorDelay","parameterTypes":["int"] }, {"name":"setBaseDirectory","parameterTypes":["java.io.File"] }, {"name":"setResourceLoader","parameterTypes":["org.springframework.core.io.ResourceLoader"] }, {"name":"setUriEncoding","parameterTypes":["java.nio.charset.Charset"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.embedded.tomcat.TomcatWebServer" }, { "name":"org.springframework.boot.web.embedded.undertow.UndertowWebServer$UndertowWebServerRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.embedded.undertow.UndertowWebServer.UndertowWebServerRuntimeHints" }, { "name":"org.springframework.boot.web.error.ErrorAttributeOptions" }, { "name":"org.springframework.boot.web.reactive.context.ReactiveWebServerApplicationContextFactory", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.server.AbstractConfigurableWebServerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addErrorPages","parameterTypes":["org.springframework.boot.web.server.ErrorPage[]"] }, {"name":"getAddress","parameterTypes":[] }, {"name":"getCompression","parameterTypes":[] }, {"name":"getErrorPages","parameterTypes":[] }, {"name":"getHttp2","parameterTypes":[] }, {"name":"getPort","parameterTypes":[] }, {"name":"getServerHeader","parameterTypes":[] }, {"name":"getShutdown","parameterTypes":[] }, {"name":"getSsl","parameterTypes":[] }, {"name":"getSslBundles","parameterTypes":[] }, {"name":"setAddress","parameterTypes":["java.net.InetAddress"] }, {"name":"setCompression","parameterTypes":["org.springframework.boot.web.server.Compression"] }, {"name":"setErrorPages","parameterTypes":["java.util.Set"] }, {"name":"setHttp2","parameterTypes":["org.springframework.boot.web.server.Http2"] }, {"name":"setPort","parameterTypes":["int"] }, {"name":"setServerHeader","parameterTypes":["java.lang.String"] }, {"name":"setShutdown","parameterTypes":["org.springframework.boot.web.server.Shutdown"] }, {"name":"setSsl","parameterTypes":["org.springframework.boot.web.server.Ssl"] }, {"name":"setSslBundles","parameterTypes":["org.springframework.boot.ssl.SslBundles"] }] }, { "name":"org.springframework.boot.web.server.Compression", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.ConfigurableWebServerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.Cookie", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.Cookie$SameSite", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.ErrorPage" }, { "name":"org.springframework.boot.web.server.ErrorPageRegistrar", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.ErrorPageRegistrarBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.boot.web.server.ErrorPageRegistry", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.web.server.Http2", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.MimeMappings" }, { "name":"org.springframework.boot.web.server.MimeMappings$Mapping" }, { "name":"org.springframework.boot.web.server.MimeMappings$MimeMappingsRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.server.MimeMappings.MimeMappingsRuntimeHints" }, { "name":"org.springframework.boot.web.server.Shutdown" }, { "name":"org.springframework.boot.web.server.Ssl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.Ssl$ClientAuth", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.Ssl$ServerNameSslBundle", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.WebServer" }, { "name":"org.springframework.boot.web.server.WebServerFactory", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.web.server.WebServerFactoryCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.server.WebServerFactoryCustomizerBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.boot.web.servlet.AbstractFilterRegistrationBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addServletNames","parameterTypes":["java.lang.String[]"] }, {"name":"addServletRegistrationBeans","parameterTypes":["org.springframework.boot.web.servlet.ServletRegistrationBean[]"] }, {"name":"addUrlPatterns","parameterTypes":["java.lang.String[]"] }, {"name":"determineDispatcherTypes","parameterTypes":[] }, {"name":"getFilterName","parameterTypes":[] }, {"name":"getServletNames","parameterTypes":[] }, {"name":"getServletRegistrationBeans","parameterTypes":[] }, {"name":"getUrlPatterns","parameterTypes":[] }, {"name":"isMatchAfter","parameterTypes":[] }, {"name":"setDispatcherTypes","parameterTypes":["jakarta.servlet.DispatcherType","jakarta.servlet.DispatcherType[]"] }, {"name":"setDispatcherTypes","parameterTypes":["java.util.EnumSet"] }, {"name":"setMatchAfter","parameterTypes":["boolean"] }, {"name":"setServletNames","parameterTypes":["java.util.Collection"] }, {"name":"setServletRegistrationBeans","parameterTypes":["java.util.Collection"] }, {"name":"setUrlPatterns","parameterTypes":["java.util.Collection"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getFilter","parameterTypes":[] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.web.servlet.DispatcherType", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.DynamicRegistrationBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addInitParameter","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"configure","parameterTypes":["jakarta.servlet.Registration$Dynamic"] }, {"name":"getInitParameters","parameterTypes":[] }, {"name":"isAsyncSupported","parameterTypes":[] }, {"name":"setAsyncSupported","parameterTypes":["boolean"] }, {"name":"setBeanName","parameterTypes":["java.lang.String"] }, {"name":"setIgnoreRegistrationFailure","parameterTypes":["boolean"] }, {"name":"setInitParameters","parameterTypes":["java.util.Map"] }, {"name":"setName","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.boot.web.servlet.FilterRegistrationBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getFilter","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.RegistrationBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getOrder","parameterTypes":[] }, {"name":"isEnabled","parameterTypes":[] }, {"name":"onStartup","parameterTypes":["jakarta.servlet.ServletContext"] }, {"name":"setEnabled","parameterTypes":["boolean"] }, {"name":"setOrder","parameterTypes":["int"] }] }, { "name":"org.springframework.boot.web.servlet.ServletComponentRegisteringPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"processAheadOfTime","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.boot.web.servlet.ServletComponentScan", "queryAllDeclaredMethods":true }, { "name":"org.springframework.boot.web.servlet.ServletComponentScanRegistrar", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.ServletContextInitializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.ServletRegistrationBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getMultipartConfig","parameterTypes":[] }, {"name":"getServlet","parameterTypes":[] }, {"name":"getServletName","parameterTypes":[] }, {"name":"getUrlMappings","parameterTypes":[] }, {"name":"setLoadOnStartup","parameterTypes":["int"] }, {"name":"setMultipartConfig","parameterTypes":["jakarta.servlet.MultipartConfigElement"] }, {"name":"setServlet","parameterTypes":["jakarta.servlet.Servlet"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.WebListenerRegistrar" }, { "name":"org.springframework.boot.web.servlet.WebListenerRegistry", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.web.servlet.context.ServletWebServerApplicationContextFactory", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.error.DefaultErrorAttributes", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getError","parameterTypes":["org.springframework.web.context.request.WebRequest"] }, {"name":"getErrorAttributes","parameterTypes":["org.springframework.web.context.request.WebRequest","org.springframework.boot.web.error.ErrorAttributeOptions"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"resolveException","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.Object","java.lang.Exception"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.error.ErrorAttributes", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.error.ErrorController", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getOrder","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.filter.OrderedFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.filter.OrderedFormContentFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getOrder","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter" }, { "name":"org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getOrder","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addCookieSameSiteSuppliers","parameterTypes":["org.springframework.boot.web.servlet.server.CookieSameSiteSupplier[]"] }, {"name":"addInitializers","parameterTypes":["org.springframework.boot.web.servlet.ServletContextInitializer[]"] }, {"name":"addMimeMappings","parameterTypes":["org.springframework.boot.web.server.MimeMappings"] }, {"name":"addWebListeners","parameterTypes":["java.lang.String[]"] }, {"name":"getContextPath","parameterTypes":[] }, {"name":"getCookieSameSiteSuppliers","parameterTypes":[] }, {"name":"getDisplayName","parameterTypes":[] }, {"name":"getDocumentRoot","parameterTypes":[] }, {"name":"getInitParameters","parameterTypes":[] }, {"name":"getJsp","parameterTypes":[] }, {"name":"getLocaleCharsetMappings","parameterTypes":[] }, {"name":"getMimeMappings","parameterTypes":[] }, {"name":"getSession","parameterTypes":[] }, {"name":"isRegisterDefaultServlet","parameterTypes":[] }, {"name":"setContextPath","parameterTypes":["java.lang.String"] }, {"name":"setCookieSameSiteSuppliers","parameterTypes":["java.util.List"] }, {"name":"setDisplayName","parameterTypes":["java.lang.String"] }, {"name":"setDocumentRoot","parameterTypes":["java.io.File"] }, {"name":"setInitParameters","parameterTypes":["java.util.Map"] }, {"name":"setInitializers","parameterTypes":["java.util.List"] }, {"name":"setJsp","parameterTypes":["org.springframework.boot.web.servlet.server.Jsp"] }, {"name":"setLocaleCharsetMappings","parameterTypes":["java.util.Map"] }, {"name":"setMimeMappings","parameterTypes":["org.springframework.boot.web.server.MimeMappings"] }, {"name":"setRegisterDefaultServlet","parameterTypes":["boolean"] }, {"name":"setSession","parameterTypes":["org.springframework.boot.web.servlet.server.Session"] }] }, { "name":"org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.server.CookieSameSiteSupplier" }, { "name":"org.springframework.boot.web.servlet.server.Encoding", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.boot.web.servlet.server.Jsp", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.server.ServletWebServerFactory", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.boot.web.servlet.server.Session", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.server.Session$Cookie", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.boot.web.servlet.server.Session$SessionTrackingMode", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.cache.Cache" }, { "name":"org.springframework.cache.CacheManager" }, { "name":"org.springframework.cache.caffeine.CaffeineCache" }, { "name":"org.springframework.cache.interceptor.CacheAspectSupport" }, { "name":"org.springframework.cache.jcache.JCacheCache" }, { "name":"org.springframework.cglib.proxy.Dispatcher" }, { "name":"org.springframework.cglib.proxy.MethodInterceptor" }, { "name":"org.springframework.cglib.proxy.NoOp" }, { "name":"org.springframework.context.ApplicationContext" }, { "name":"org.springframework.context.ApplicationContextAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.ApplicationContextException" }, { "name":"org.springframework.context.ApplicationContextInitializer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.ApplicationEvent" }, { "name":"org.springframework.context.ApplicationEventPublisher" }, { "name":"org.springframework.context.ApplicationEventPublisherAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.ApplicationListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"shutdown","parameterTypes":[] }, {"name":"supportsAsyncExecution","parameterTypes":[] }] }, { "name":"org.springframework.context.ApplicationStartupAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.ConfigurableApplicationContext" }, { "name":"org.springframework.context.EmbeddedValueResolverAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.EnvironmentAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.Lifecycle", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.context.LifecycleProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.MessageSource" }, { "name":"org.springframework.context.MessageSourceAware", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.context.PayloadApplicationEvent" }, { "name":"org.springframework.context.Phased", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.context.ResourceLoaderAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.SmartLifecycle", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"isAutoStartup","parameterTypes":[] }] }, { "name":"org.springframework.context.annotation.AnnotationConfigRegistry" }, { "name":"org.springframework.context.annotation.AnnotationScopeMetadataResolver", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.context.annotation.AspectJAutoProxyRegistrar", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.context.annotation.AutoProxyRegistrar", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.context.annotation.Bean", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider" }, { "name":"org.springframework.context.annotation.CommonAnnotationBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"postProcessAfterInstantiation","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInstantiation","parameterTypes":["java.lang.Class","java.lang.String"] }, {"name":"postProcessMergedBeanDefinition","parameterTypes":["org.springframework.beans.factory.support.RootBeanDefinition","java.lang.Class","java.lang.String"] }, {"name":"postProcessProperties","parameterTypes":["org.springframework.beans.PropertyValues","java.lang.Object","java.lang.String"] }, {"name":"processAheadOfTime","parameterTypes":["org.springframework.beans.factory.support.RegisteredBean"] }, {"name":"resetBeanDefinition","parameterTypes":["java.lang.String"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.context.annotation.CommonAnnotationBeanPostProcessor$LookupElement" }, { "name":"org.springframework.context.annotation.ComponentScan", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.ComponentScan$Filter", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.Conditional", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.Configuration", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.ConfigurationClassEnhancer$EnhancedConfiguration", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.context.annotation.ConfigurationClassParser$DefaultDeferredImportSelectorGroup", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.context.annotation.ConfigurationClassPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"postProcessBeanDefinitionRegistry","parameterTypes":["org.springframework.beans.factory.support.BeanDefinitionRegistry"] }, {"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"processAheadOfTime","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"processAheadOfTime","parameterTypes":["org.springframework.beans.factory.support.RegisteredBean"] }, {"name":"setApplicationStartup","parameterTypes":["org.springframework.core.metrics.ApplicationStartup"] }, {"name":"setBeanClassLoader","parameterTypes":["java.lang.ClassLoader"] }, {"name":"setEnvironment","parameterTypes":["org.springframework.core.env.Environment"] }, {"name":"setMetadataReaderFactory","parameterTypes":["org.springframework.core.type.classreading.MetadataReaderFactory"] }, {"name":"setResourceLoader","parameterTypes":["org.springframework.core.io.ResourceLoader"] }] }, { "name":"org.springframework.context.annotation.DependsOn", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.EnableAspectJAutoProxy", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.FilterType" }, { "name":"org.springframework.context.annotation.Import", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.ImportAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.annotation.ImportRuntimeHints", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.Lazy", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.Primary", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.PropertySource", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.Role", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.annotation.Scope", "queryAllDeclaredMethods":true }, { "name":"org.springframework.context.aot.KotlinReflectionBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.context.aot.ReflectiveProcessorBeanFactoryInitializationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.context.event.ApplicationContextEvent" }, { "name":"org.springframework.context.event.ContextClosedEvent" }, { "name":"org.springframework.context.event.ContextRefreshedEvent" }, { "name":"org.springframework.context.event.DefaultEventListenerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"createApplicationListener","parameterTypes":["java.lang.String","java.lang.Class","java.lang.reflect.Method"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"supportsMethod","parameterTypes":["java.lang.reflect.Method"] }] }, { "name":"org.springframework.context.event.EventListener" }, { "name":"org.springframework.context.event.EventListenerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.context.event.EventListenerMethodProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"afterSingletonsInstantiated","parameterTypes":[] }, {"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.context.event.GenericApplicationListener", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"supportsEventType","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.context.event.SmartApplicationListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getListenerId","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"supportsSourceType","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.context.i18n.LocaleContext" }, { "name":"org.springframework.context.support.ApplicationObjectSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.context.support.DefaultLifecycleProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"isRunning","parameterTypes":[] }, {"name":"onClose","parameterTypes":[] }, {"name":"onRefresh","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"start","parameterTypes":[] }, {"name":"stop","parameterTypes":[] }] }, { "name":"org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup" }, { "name":"org.springframework.context.support.MessageSourceAccessor" }, { "name":"org.springframework.context.support.PropertySourcesPlaceholderConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"setEnvironment","parameterTypes":["org.springframework.core.env.Environment"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.core.MethodParameter" }, { "name":"org.springframework.core.Ordered", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.core.ParameterNameDiscoverer" }, { "name":"org.springframework.core.PriorityOrdered", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.core.ReactiveAdapterRegistry" }, { "name":"org.springframework.core.ResolvableType" }, { "name":"org.springframework.core.annotation.AliasFor", "queryAllDeclaredMethods":true }, { "name":"org.springframework.core.annotation.MergedAnnotation" }, { "name":"org.springframework.core.annotation.MergedAnnotations$SearchStrategy" }, { "name":"org.springframework.core.annotation.Order", "queryAllDeclaredMethods":true }, { "name":"org.springframework.core.convert.ConversionService", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"convert","parameterTypes":["java.lang.Object","org.springframework.core.convert.TypeDescriptor"] }] }, { "name":"org.springframework.core.convert.TypeDescriptor" }, { "name":"org.springframework.core.convert.converter.Converter" }, { "name":"org.springframework.core.convert.converter.ConverterFactory" }, { "name":"org.springframework.core.convert.converter.ConverterRegistry", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.core.convert.converter.GenericConverter" }, { "name":"org.springframework.core.convert.support.ConfigurableConversionService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.core.convert.support.GenericConversionService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addConverter","parameterTypes":["java.lang.Class","java.lang.Class","org.springframework.core.convert.converter.Converter"] }, {"name":"addConverter","parameterTypes":["org.springframework.core.convert.converter.Converter"] }, {"name":"addConverter","parameterTypes":["org.springframework.core.convert.converter.GenericConverter"] }, {"name":"addConverterFactory","parameterTypes":["org.springframework.core.convert.converter.ConverterFactory"] }, {"name":"canBypassConvert","parameterTypes":["org.springframework.core.convert.TypeDescriptor","org.springframework.core.convert.TypeDescriptor"] }, {"name":"canConvert","parameterTypes":["java.lang.Class","java.lang.Class"] }, {"name":"canConvert","parameterTypes":["org.springframework.core.convert.TypeDescriptor","org.springframework.core.convert.TypeDescriptor"] }, {"name":"convert","parameterTypes":["java.lang.Object","java.lang.Class"] }, {"name":"convert","parameterTypes":["java.lang.Object","org.springframework.core.convert.TypeDescriptor","org.springframework.core.convert.TypeDescriptor"] }, {"name":"removeConvertible","parameterTypes":["java.lang.Class","java.lang.Class"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.core.env.ConfigurableEnvironment" }, { "name":"org.springframework.core.env.ConfigurablePropertyResolver" }, { "name":"org.springframework.core.env.EnumerablePropertySource" }, { "name":"org.springframework.core.env.Environment" }, { "name":"org.springframework.core.env.EnvironmentCapable", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.core.env.MutablePropertySources" }, { "name":"org.springframework.core.env.PropertySource" }, { "name":"org.springframework.core.env.PropertySources" }, { "name":"org.springframework.core.io.ProtocolResolver", "queryAllDeclaredConstructors":true }, { "name":"org.springframework.core.io.Resource" }, { "name":"org.springframework.core.io.ResourceLoader" }, { "name":"org.springframework.core.io.support.PropertiesLoaderSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setFileEncoding","parameterTypes":["java.lang.String"] }, {"name":"setIgnoreResourceNotFound","parameterTypes":["boolean"] }, {"name":"setLocalOverride","parameterTypes":["boolean"] }, {"name":"setLocation","parameterTypes":["org.springframework.core.io.Resource"] }, {"name":"setLocations","parameterTypes":["org.springframework.core.io.Resource[]"] }, {"name":"setProperties","parameterTypes":["java.util.Properties"] }, {"name":"setPropertiesArray","parameterTypes":["java.util.Properties[]"] }, {"name":"setPropertiesPersister","parameterTypes":["org.springframework.util.PropertiesPersister"] }] }, { "name":"org.springframework.core.metrics.ApplicationStartup" }, { "name":"org.springframework.core.task.AsyncListenableTaskExecutor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.core.task.AsyncTaskExecutor", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"execute","parameterTypes":["java.lang.Runnable","long"] }, {"name":"submitCompletable","parameterTypes":["java.lang.Runnable"] }, {"name":"submitCompletable","parameterTypes":["java.util.concurrent.Callable"] }] }, { "name":"org.springframework.core.task.SimpleAsyncTaskExecutor" }, { "name":"org.springframework.core.task.TaskDecorator" }, { "name":"org.springframework.core.task.TaskExecutor", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.core.type.AnnotationMetadata" }, { "name":"org.springframework.core.type.classreading.CachingMetadataReaderFactory", "queryAllDeclaredMethods":true, "methods":[{"name":"getMetadataReader","parameterTypes":["org.springframework.core.io.Resource"] }] }, { "name":"org.springframework.core.type.classreading.MetadataReader" }, { "name":"org.springframework.core.type.classreading.MetadataReaderFactory", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"","parameterTypes":["org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory"] }, {"name":"from","parameterTypes":["org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory"] }, {"name":"of","parameterTypes":["org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory"] }, {"name":"valueOf","parameterTypes":["org.springframework.boot.type.classreading.ConcurrentReferenceCachingMetadataReaderFactory"] }] }, { "name":"org.springframework.core.type.classreading.SimpleMetadataReaderFactory", "queryAllDeclaredMethods":true, "methods":[{"name":"getMetadataReader","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.dao.DataAccessException" }, { "name":"org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.data.cassandra.ReactiveSession" }, { "name":"org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate" }, { "name":"org.springframework.data.elasticsearch.client.elc.ReactiveElasticsearchClient" }, { "name":"org.springframework.data.elasticsearch.repository.ElasticsearchRepository" }, { "name":"org.springframework.data.jdbc.repository.config.AbstractJdbcConfiguration" }, { "name":"org.springframework.data.jpa.repository.JpaRepository" }, { "name":"org.springframework.data.ldap.repository.LdapRepository" }, { "name":"org.springframework.data.mongodb.core.MongoTemplate" }, { "name":"org.springframework.data.r2dbc.core.R2dbcEntityTemplate" }, { "name":"org.springframework.data.redis.cache.RedisCache" }, { "name":"org.springframework.data.redis.connection.RedisConnectionFactory" }, { "name":"org.springframework.data.redis.core.RedisOperations" }, { "name":"org.springframework.data.redis.repository.configuration.EnableRedisRepositories" }, { "name":"org.springframework.data.repository.Repository" }, { "name":"org.springframework.data.repository.query.Param" }, { "name":"org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter" }, { "name":"org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration" }, { "name":"org.springframework.data.web.PageableHandlerMethodArgumentResolver" }, { "name":"org.springframework.expression.EvaluationContext" }, { "name":"org.springframework.expression.ExpressionParser" }, { "name":"org.springframework.format.AnnotationFormatterFactory" }, { "name":"org.springframework.format.Formatter" }, { "name":"org.springframework.format.FormatterRegistry", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.format.Parser" }, { "name":"org.springframework.format.Printer" }, { "name":"org.springframework.format.support.DefaultFormattingConversionService", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.format.support.FormattingConversionService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"addFormatter","parameterTypes":["org.springframework.format.Formatter"] }, {"name":"addFormatterForFieldAnnotation","parameterTypes":["org.springframework.format.AnnotationFormatterFactory"] }, {"name":"addFormatterForFieldType","parameterTypes":["java.lang.Class","org.springframework.format.Formatter"] }, {"name":"addFormatterForFieldType","parameterTypes":["java.lang.Class","org.springframework.format.Printer","org.springframework.format.Parser"] }, {"name":"addParser","parameterTypes":["org.springframework.format.Parser"] }, {"name":"addPrinter","parameterTypes":["org.springframework.format.Printer"] }, {"name":"close","parameterTypes":[] }, {"name":"setEmbeddedValueResolver","parameterTypes":["org.springframework.util.StringValueResolver"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.format.support.FormattingConversionServiceRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.hateoas.EntityModel" }, { "name":"org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter" }, { "name":"org.springframework.http.CacheControl", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.ContentDisposition", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.HttpEntity", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.HttpHeaders", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.HttpInputMessage" }, { "name":"org.springframework.http.HttpMethod", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.HttpMimeTypesRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.http.HttpOutputMessage" }, { "name":"org.springframework.http.HttpRange", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.HttpStatus", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.HttpStatusCode", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.MediaType", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.http.MediaTypeEditor" }, { "name":"org.springframework.http.ProblemDetail", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.ReactiveHttpInputMessage" }, { "name":"org.springframework.http.ResponseEntity", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.client.AbstractClientHttpRequestFactoryWrapper", "allDeclaredFields":true }, { "name":"org.springframework.http.client.ClientHttpRequest" }, { "name":"org.springframework.http.client.ClientHttpRequestFactory", "queryAllPublicMethods":true }, { "name":"org.springframework.http.client.ClientHttpRequestInitializer" }, { "name":"org.springframework.http.client.ClientHttpRequestInterceptor" }, { "name":"org.springframework.http.client.HttpComponentsClientHttpRequestFactory", "queryAllDeclaredMethods":true }, { "name":"org.springframework.http.client.SimpleClientHttpRequestFactory", "queryAllDeclaredMethods":true }, { "name":"org.springframework.http.client.observation.ClientRequestObservationConvention" }, { "name":"org.springframework.http.codec.CodecConfigurer" }, { "name":"org.springframework.http.codec.CodecConfigurerRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.http.codec.multipart.DefaultPartHttpMessageReader" }, { "name":"org.springframework.http.converter.AbstractGenericHttpMessageConverter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"canWrite","parameterTypes":["java.lang.reflect.Type","java.lang.Class","org.springframework.http.MediaType"] }, {"name":"write","parameterTypes":["java.lang.Object","java.lang.reflect.Type","org.springframework.http.MediaType","org.springframework.http.HttpOutputMessage"] }] }, { "name":"org.springframework.http.converter.AbstractHttpMessageConverter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addDefaultHeaders","parameterTypes":["org.springframework.http.HttpHeaders","java.lang.Object","org.springframework.http.MediaType"] }, {"name":"canRead","parameterTypes":["java.lang.Class","org.springframework.http.MediaType"] }, {"name":"canWrite","parameterTypes":["java.lang.Class","org.springframework.http.MediaType"] }, {"name":"getContentLength","parameterTypes":["java.lang.Object","org.springframework.http.MediaType"] }, {"name":"getDefaultCharset","parameterTypes":[] }, {"name":"getSupportedMediaTypes","parameterTypes":[] }, {"name":"read","parameterTypes":["java.lang.Class","org.springframework.http.HttpInputMessage"] }, {"name":"setDefaultCharset","parameterTypes":["java.nio.charset.Charset"] }, {"name":"setSupportedMediaTypes","parameterTypes":["java.util.List"] }, {"name":"supportsRepeatableWrites","parameterTypes":["java.lang.Object"] }, {"name":"write","parameterTypes":["java.lang.Object","org.springframework.http.MediaType","org.springframework.http.HttpOutputMessage"] }, {"name":"writeInternal","parameterTypes":["java.lang.Object","org.springframework.http.HttpOutputMessage"] }] }, { "name":"org.springframework.http.converter.FormHttpMessageConverter" }, { "name":"org.springframework.http.converter.GenericHttpMessageConverter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.http.converter.HttpMessageConversionException" }, { "name":"org.springframework.http.converter.HttpMessageConverter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getSupportedMediaTypes","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.http.converter.HttpMessageNotReadableException" }, { "name":"org.springframework.http.converter.HttpMessageNotWritableException" }, { "name":"org.springframework.http.converter.StringHttpMessageConverter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"canRead","parameterTypes":["java.lang.Class","org.springframework.http.MediaType"] }, {"name":"canRead","parameterTypes":["java.lang.reflect.Type","java.lang.Class","org.springframework.http.MediaType"] }, {"name":"canWrite","parameterTypes":["java.lang.Class","org.springframework.http.MediaType"] }, {"name":"getObjectMapper","parameterTypes":[] }, {"name":"getObjectMappersForType","parameterTypes":["java.lang.Class"] }, {"name":"getSupportedMediaTypes","parameterTypes":["java.lang.Class"] }, {"name":"read","parameterTypes":["java.lang.reflect.Type","java.lang.Class","org.springframework.http.HttpInputMessage"] }, {"name":"registerObjectMappersForType","parameterTypes":["java.lang.Class","java.util.function.Consumer"] }, {"name":"setObjectMapper","parameterTypes":["com.fasterxml.jackson.databind.ObjectMapper"] }, {"name":"setPrettyPrint","parameterTypes":["boolean"] }, {"name":"setSupportedMediaTypes","parameterTypes":["java.util.List"] }] }, { "name":"org.springframework.http.converter.json.GsonHttpMessageConverter" }, { "name":"org.springframework.http.converter.json.Jackson2ObjectMapperBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.http.converter.json.JacksonModulesRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.http.converter.json.MappingJackson2HttpMessageConverter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.http.converter.json.ProblemDetailJacksonMixin", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.http.converter.json.ProblemDetailRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter" }, { "name":"org.springframework.http.server.PathContainer" }, { "name":"org.springframework.http.server.PathContainer$Options" }, { "name":"org.springframework.http.server.RequestPath" }, { "name":"org.springframework.http.server.observation.ServerRequestObservationContext" }, { "name":"org.springframework.http.server.observation.ServerRequestObservationConvention" }, { "name":"org.springframework.http.server.reactive.HttpHandler" }, { "name":"org.springframework.integration.config.EnableIntegration" }, { "name":"org.springframework.integration.graph.IntegrationGraphServer" }, { "name":"org.springframework.jdbc.SQLWarningException" }, { "name":"org.springframework.jdbc.core.BatchPreparedStatementSetter" }, { "name":"org.springframework.jdbc.core.CallableStatementCallback" }, { "name":"org.springframework.jdbc.core.CallableStatementCreator" }, { "name":"org.springframework.jdbc.core.ConnectionCallback" }, { "name":"org.springframework.jdbc.core.JdbcOperations", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.jdbc.core.JdbcTemplate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"batchUpdate","parameterTypes":["java.lang.String","java.util.Collection","int","org.springframework.jdbc.core.ParameterizedPreparedStatementSetter"] }, {"name":"batchUpdate","parameterTypes":["java.lang.String","java.util.List"] }, {"name":"batchUpdate","parameterTypes":["java.lang.String","java.util.List","int[]"] }, {"name":"batchUpdate","parameterTypes":["java.lang.String","org.springframework.jdbc.core.BatchPreparedStatementSetter"] }, {"name":"batchUpdate","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator","org.springframework.jdbc.core.BatchPreparedStatementSetter","org.springframework.jdbc.support.KeyHolder"] }, {"name":"batchUpdate","parameterTypes":["java.lang.String[]"] }, {"name":"call","parameterTypes":["org.springframework.jdbc.core.CallableStatementCreator","java.util.List"] }, {"name":"close","parameterTypes":[] }, {"name":"execute","parameterTypes":["java.lang.String"] }, {"name":"execute","parameterTypes":["java.lang.String","org.springframework.jdbc.core.CallableStatementCallback"] }, {"name":"execute","parameterTypes":["java.lang.String","org.springframework.jdbc.core.PreparedStatementCallback"] }, {"name":"execute","parameterTypes":["org.springframework.jdbc.core.CallableStatementCreator","org.springframework.jdbc.core.CallableStatementCallback"] }, {"name":"execute","parameterTypes":["org.springframework.jdbc.core.ConnectionCallback"] }, {"name":"execute","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator","org.springframework.jdbc.core.PreparedStatementCallback"] }, {"name":"execute","parameterTypes":["org.springframework.jdbc.core.StatementCallback"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.PreparedStatementSetter","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.PreparedStatementSetter","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.PreparedStatementSetter","org.springframework.jdbc.core.RowMapper"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.ResultSetExtractor","java.lang.Object[]"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowCallbackHandler","java.lang.Object[]"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowMapper"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowMapper","java.lang.Object[]"] }, {"name":"query","parameterTypes":["java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"query","parameterTypes":["java.lang.String","java.lang.Object[]","int[]","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["java.lang.String","java.lang.Object[]","int[]","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["java.lang.String","java.lang.Object[]","int[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"query","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForList","parameterTypes":["java.lang.String"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.lang.Class","java.lang.Object[]"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.lang.Object[]"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.lang.Object[]","java.lang.Class"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.lang.Object[]","int[]"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.lang.Object[]","int[]","java.lang.Class"] }, {"name":"queryForMap","parameterTypes":["java.lang.String"] }, {"name":"queryForMap","parameterTypes":["java.lang.String","java.lang.Object[]"] }, {"name":"queryForMap","parameterTypes":["java.lang.String","java.lang.Object[]","int[]"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.lang.Class"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.lang.Class","java.lang.Object[]"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowMapper","java.lang.Object[]"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.lang.Object[]","java.lang.Class"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.lang.Object[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.lang.Object[]","int[]","java.lang.Class"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.lang.Object[]","int[]","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForRowSet","parameterTypes":["java.lang.String"] }, {"name":"queryForRowSet","parameterTypes":["java.lang.String","java.lang.Object[]"] }, {"name":"queryForRowSet","parameterTypes":["java.lang.String","java.lang.Object[]","int[]"] }, {"name":"queryForStream","parameterTypes":["java.lang.String","org.springframework.jdbc.core.PreparedStatementSetter","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForStream","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForStream","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowMapper","java.lang.Object[]"] }, {"name":"queryForStream","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator","org.springframework.jdbc.core.RowMapper"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"update","parameterTypes":["java.lang.String"] }, {"name":"update","parameterTypes":["java.lang.String","org.springframework.jdbc.core.PreparedStatementSetter"] }, {"name":"update","parameterTypes":["java.lang.String","java.lang.Object[]"] }, {"name":"update","parameterTypes":["java.lang.String","java.lang.Object[]","int[]"] }, {"name":"update","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator"] }, {"name":"update","parameterTypes":["org.springframework.jdbc.core.PreparedStatementCreator","org.springframework.jdbc.support.KeyHolder"] }] }, { "name":"org.springframework.jdbc.core.ParameterizedPreparedStatementSetter" }, { "name":"org.springframework.jdbc.core.PreparedStatementCallback" }, { "name":"org.springframework.jdbc.core.PreparedStatementCreator" }, { "name":"org.springframework.jdbc.core.PreparedStatementCreatorFactory" }, { "name":"org.springframework.jdbc.core.PreparedStatementSetter" }, { "name":"org.springframework.jdbc.core.ResultSetExtractor" }, { "name":"org.springframework.jdbc.core.ResultSetSupportingSqlParameter" }, { "name":"org.springframework.jdbc.core.RowCallbackHandler" }, { "name":"org.springframework.jdbc.core.RowMapper" }, { "name":"org.springframework.jdbc.core.StatementCallback" }, { "name":"org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"batchUpdate","parameterTypes":["java.lang.String","java.util.Map[]"] }, {"name":"batchUpdate","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource[]"] }, {"name":"batchUpdate","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource[]","org.springframework.jdbc.support.KeyHolder"] }, {"name":"batchUpdate","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource[]","org.springframework.jdbc.support.KeyHolder","java.lang.String[]"] }, {"name":"close","parameterTypes":[] }, {"name":"execute","parameterTypes":["java.lang.String","java.util.Map","org.springframework.jdbc.core.PreparedStatementCallback"] }, {"name":"execute","parameterTypes":["java.lang.String","org.springframework.jdbc.core.PreparedStatementCallback"] }, {"name":"execute","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.core.PreparedStatementCallback"] }, {"name":"getJdbcOperations","parameterTypes":[] }, {"name":"query","parameterTypes":["java.lang.String","java.util.Map","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["java.lang.String","java.util.Map","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["java.lang.String","java.util.Map","org.springframework.jdbc.core.RowMapper"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.RowMapper"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.core.ResultSetExtractor"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.core.RowCallbackHandler"] }, {"name":"query","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.util.Map"] }, {"name":"queryForList","parameterTypes":["java.lang.String","java.util.Map","java.lang.Class"] }, {"name":"queryForList","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource"] }, {"name":"queryForList","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","java.lang.Class"] }, {"name":"queryForMap","parameterTypes":["java.lang.String","java.util.Map"] }, {"name":"queryForMap","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.util.Map","java.lang.Class"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","java.util.Map","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","java.lang.Class"] }, {"name":"queryForObject","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForRowSet","parameterTypes":["java.lang.String","java.util.Map"] }, {"name":"queryForRowSet","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource"] }, {"name":"queryForStream","parameterTypes":["java.lang.String","java.util.Map","org.springframework.jdbc.core.RowMapper"] }, {"name":"queryForStream","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.core.RowMapper"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"update","parameterTypes":["java.lang.String","java.util.Map"] }, {"name":"update","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource"] }, {"name":"update","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.support.KeyHolder"] }, {"name":"update","parameterTypes":["java.lang.String","org.springframework.jdbc.core.namedparam.SqlParameterSource","org.springframework.jdbc.support.KeyHolder","java.lang.String[]"] }] }, { "name":"org.springframework.jdbc.core.namedparam.ParsedSql" }, { "name":"org.springframework.jdbc.core.namedparam.SqlParameterSource" }, { "name":"org.springframework.jdbc.core.simple.DefaultJdbcClient", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"sql","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.jdbc.core.simple.JdbcClient", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.jdbc.core.simple.JdbcClient$StatementSpec" }, { "name":"org.springframework.jdbc.datasource.DataSourceTransactionManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"getDataSource","parameterTypes":[] }, {"name":"getResourceFactory","parameterTypes":[] }, {"name":"isEnforceReadOnly","parameterTypes":[] }, {"name":"setDataSource","parameterTypes":["javax.sql.DataSource"] }, {"name":"setEnforceReadOnly","parameterTypes":["boolean"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.jdbc.datasource.DelegatingDataSource" }, { "name":"org.springframework.jdbc.datasource.SimpleDriverDataSource" }, { "name":"org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactory$EmbeddedDataSourceProxy" }, { "name":"org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseFactoryRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType" }, { "name":"org.springframework.jdbc.datasource.init.DatabasePopulator" }, { "name":"org.springframework.jdbc.datasource.init.ResourceDatabasePopulator" }, { "name":"org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource" }, { "name":"org.springframework.jdbc.support.JdbcAccessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"getDataSource","parameterTypes":[] }, {"name":"getExceptionTranslator","parameterTypes":[] }, {"name":"isLazyInit","parameterTypes":[] }, {"name":"setDataSource","parameterTypes":["javax.sql.DataSource"] }, {"name":"setDatabaseProductName","parameterTypes":["java.lang.String"] }, {"name":"setExceptionTranslator","parameterTypes":["org.springframework.jdbc.support.SQLExceptionTranslator"] }, {"name":"setLazyInit","parameterTypes":["boolean"] }] }, { "name":"org.springframework.jdbc.support.JdbcTransactionManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.jdbc.support.KeyHolder" }, { "name":"org.springframework.jdbc.support.SQLExceptionTranslator" }, { "name":"org.springframework.jdbc.support.rowset.SqlRowSet" }, { "name":"org.springframework.jms.core.JmsTemplate" }, { "name":"org.springframework.jmx.export.MBeanExportException" }, { "name":"org.springframework.jmx.export.MBeanExportOperations", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.jmx.export.MBeanExporter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"addExcludedBean","parameterTypes":["java.lang.String"] }, {"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"afterSingletonsInstantiated","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"registerManagedResource","parameterTypes":["java.lang.Object"] }, {"name":"registerManagedResource","parameterTypes":["java.lang.Object","javax.management.ObjectName"] }, {"name":"setAllowEagerInit","parameterTypes":["boolean"] }, {"name":"setAssembler","parameterTypes":["org.springframework.jmx.export.assembler.MBeanInfoAssembler"] }, {"name":"setAutodetect","parameterTypes":["boolean"] }, {"name":"setAutodetectMode","parameterTypes":["int"] }, {"name":"setAutodetectModeName","parameterTypes":["java.lang.String"] }, {"name":"setBeanClassLoader","parameterTypes":["java.lang.ClassLoader"] }, {"name":"setBeans","parameterTypes":["java.util.Map"] }, {"name":"setEnsureUniqueRuntimeObjectNames","parameterTypes":["boolean"] }, {"name":"setExcludedBeans","parameterTypes":["java.lang.String[]"] }, {"name":"setExposeManagedResourceClassLoader","parameterTypes":["boolean"] }, {"name":"setListeners","parameterTypes":["org.springframework.jmx.export.MBeanExporterListener[]"] }, {"name":"setNamingStrategy","parameterTypes":["org.springframework.jmx.export.naming.ObjectNamingStrategy"] }, {"name":"setNotificationListenerMappings","parameterTypes":["java.util.Map"] }, {"name":"setNotificationListeners","parameterTypes":["org.springframework.jmx.export.NotificationListenerBean[]"] }, {"name":"unregisterManagedResource","parameterTypes":["javax.management.ObjectName"] }] }, { "name":"org.springframework.jmx.export.MBeanExporter$AutodetectCallback" }, { "name":"org.springframework.jmx.export.MBeanExporterListener" }, { "name":"org.springframework.jmx.export.NotificationListenerBean" }, { "name":"org.springframework.jmx.export.annotation.AnnotationMBeanExporter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }] }, { "name":"org.springframework.jmx.export.annotation.ManagedOperationParameters", "queryAllDeclaredMethods":true }, { "name":"org.springframework.jmx.export.assembler.MBeanInfoAssembler" }, { "name":"org.springframework.jmx.export.metadata.JmxAttributeSource" }, { "name":"org.springframework.jmx.export.naming.MetadataNamingStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"setAttributeSource","parameterTypes":["org.springframework.jmx.export.metadata.JmxAttributeSource"] }, {"name":"setDefaultDomain","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.jmx.export.naming.ObjectNamingStrategy", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.jmx.support.MBeanRegistrationSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setRegistrationPolicy","parameterTypes":["org.springframework.jmx.support.RegistrationPolicy"] }, {"name":"setServer","parameterTypes":["javax.management.MBeanServer"] }] }, { "name":"org.springframework.jmx.support.RegistrationPolicy" }, { "name":"org.springframework.kafka.core.KafkaTemplate" }, { "name":"org.springframework.kafka.core.ProducerFactory" }, { "name":"org.springframework.lang.NonNull" }, { "name":"org.springframework.lang.Nullable" }, { "name":"org.springframework.ldap.aot.hint.LdapCoreRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.ldap.core.AuthenticationSource" }, { "name":"org.springframework.ldap.core.ContextSource", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.ldap.core.DistinguishedName" }, { "name":"org.springframework.ldap.core.LdapOperations" }, { "name":"org.springframework.ldap.core.LdapTemplate" }, { "name":"org.springframework.ldap.core.support.AbstractContextSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"assembleProviderUrlString","parameterTypes":["java.lang.String[]"] }, {"name":"getAuthenticationSource","parameterTypes":[] }, {"name":"getBaseLdapName","parameterTypes":[] }, {"name":"getBaseLdapPath","parameterTypes":[] }, {"name":"getBaseLdapPathAsString","parameterTypes":[] }, {"name":"getContext","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"getContextFactory","parameterTypes":[] }, {"name":"getDirObjectFactory","parameterTypes":[] }, {"name":"getPassword","parameterTypes":[] }, {"name":"getReadOnlyContext","parameterTypes":[] }, {"name":"getReadWriteContext","parameterTypes":[] }, {"name":"getUrls","parameterTypes":[] }, {"name":"getUserDn","parameterTypes":[] }, {"name":"isAnonymousReadOnly","parameterTypes":[] }, {"name":"isPooled","parameterTypes":[] }, {"name":"setAnonymousReadOnly","parameterTypes":["boolean"] }, {"name":"setAuthenticationSource","parameterTypes":["org.springframework.ldap.core.AuthenticationSource"] }, {"name":"setAuthenticationStrategy","parameterTypes":["org.springframework.ldap.core.support.DirContextAuthenticationStrategy"] }, {"name":"setBase","parameterTypes":["java.lang.String"] }, {"name":"setBaseEnvironmentProperties","parameterTypes":["java.util.Map"] }, {"name":"setCacheEnvironmentProperties","parameterTypes":["boolean"] }, {"name":"setContextFactory","parameterTypes":["java.lang.Class"] }, {"name":"setDirObjectFactory","parameterTypes":["java.lang.Class"] }, {"name":"setPassword","parameterTypes":["java.lang.String"] }, {"name":"setPooled","parameterTypes":["boolean"] }, {"name":"setReferral","parameterTypes":["java.lang.String"] }, {"name":"setUrl","parameterTypes":["java.lang.String"] }, {"name":"setUrls","parameterTypes":["java.lang.String[]"] }, {"name":"setUserDn","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.ldap.core.support.BaseLdapPathContextSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.ldap.core.support.BaseLdapPathSource", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.ldap.core.support.DirContextAuthenticationStrategy" }, { "name":"org.springframework.ldap.core.support.LdapContextSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.mail.javamail.JavaMailSenderImpl" }, { "name":"org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler" }, { "name":"org.springframework.mock.web.MockServletContext" }, { "name":"org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" }, { "name":"org.springframework.oxm.Marshaller" }, { "name":"org.springframework.r2dbc.connection.R2dbcTransactionManager" }, { "name":"org.springframework.r2dbc.connection.init.DatabasePopulator" }, { "name":"org.springframework.scheduling.SchedulingTaskExecutor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"prefersShortLivedTasks","parameterTypes":[] }] }, { "name":"org.springframework.scheduling.TaskScheduler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"schedule","parameterTypes":["java.lang.Runnable","java.util.Date"] }, {"name":"scheduleAtFixedRate","parameterTypes":["java.lang.Runnable","long"] }, {"name":"scheduleAtFixedRate","parameterTypes":["java.lang.Runnable","java.util.Date","long"] }, {"name":"scheduleWithFixedDelay","parameterTypes":["java.lang.Runnable","long"] }, {"name":"scheduleWithFixedDelay","parameterTypes":["java.lang.Runnable","java.util.Date","long"] }] }, { "name":"org.springframework.scheduling.Trigger" }, { "name":"org.springframework.scheduling.annotation.EnableScheduling", "queryAllDeclaredMethods":true }, { "name":"org.springframework.scheduling.annotation.Scheduled", "queryAllDeclaredMethods":true }, { "name":"org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterSingletonsInstantiated","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"getScheduledTasks","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"postProcessAfterInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeDestruction","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessBeforeInitialization","parameterTypes":["java.lang.Object","java.lang.String"] }, {"name":"postProcessMergedBeanDefinition","parameterTypes":["org.springframework.beans.factory.support.RootBeanDefinition","java.lang.Class","java.lang.String"] }, {"name":"requiresDestruction","parameterTypes":["java.lang.Object"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"setBeanName","parameterTypes":["java.lang.String"] }, {"name":"setEmbeddedValueResolver","parameterTypes":["org.springframework.util.StringValueResolver"] }] }, { "name":"org.springframework.scheduling.annotation.Schedules", "queryAllDeclaredMethods":true }, { "name":"org.springframework.scheduling.annotation.SchedulingConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"scheduledAnnotationProcessor","parameterTypes":[] }] }, { "name":"org.springframework.scheduling.annotation.SchedulingConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"postConstructor","parameterTypes":[] }] }, { "name":"org.springframework.scheduling.concurrent.CustomizableThreadFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"newThread","parameterTypes":["java.lang.Runnable"] }] }, { "name":"org.springframework.scheduling.concurrent.ExecutorConfigurationSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"getPhase","parameterTypes":[] }, {"name":"initialize","parameterTypes":[] }, {"name":"initiateShutdown","parameterTypes":[] }, {"name":"isRunning","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.event.ContextClosedEvent"] }, {"name":"setAcceptTasksAfterContextClose","parameterTypes":["boolean"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setAwaitTerminationMillis","parameterTypes":["long"] }, {"name":"setAwaitTerminationSeconds","parameterTypes":["int"] }, {"name":"setBeanName","parameterTypes":["java.lang.String"] }, {"name":"setPhase","parameterTypes":["int"] }, {"name":"setRejectedExecutionHandler","parameterTypes":["java.util.concurrent.RejectedExecutionHandler"] }, {"name":"setThreadFactory","parameterTypes":["java.util.concurrent.ThreadFactory"] }, {"name":"setThreadNamePrefix","parameterTypes":["java.lang.String"] }, {"name":"setWaitForTasksToCompleteOnShutdown","parameterTypes":["boolean"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"start","parameterTypes":[] }, {"name":"stop","parameterTypes":[] }, {"name":"stop","parameterTypes":["java.lang.Runnable"] }] }, { "name":"org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler" }, { "name":"org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"execute","parameterTypes":["java.lang.Runnable"] }, {"name":"submit","parameterTypes":["java.lang.Runnable"] }, {"name":"submit","parameterTypes":["java.util.concurrent.Callable"] }, {"name":"submitListenable","parameterTypes":["java.lang.Runnable"] }, {"name":"submitListenable","parameterTypes":["java.util.concurrent.Callable"] }] }, { "name":"org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"execute","parameterTypes":["java.lang.Runnable"] }, {"name":"getClock","parameterTypes":[] }, {"name":"schedule","parameterTypes":["java.lang.Runnable","java.time.Instant"] }, {"name":"schedule","parameterTypes":["java.lang.Runnable","org.springframework.scheduling.Trigger"] }, {"name":"scheduleAtFixedRate","parameterTypes":["java.lang.Runnable","java.time.Duration"] }, {"name":"scheduleAtFixedRate","parameterTypes":["java.lang.Runnable","java.time.Instant","java.time.Duration"] }, {"name":"scheduleWithFixedDelay","parameterTypes":["java.lang.Runnable","java.time.Duration"] }, {"name":"scheduleWithFixedDelay","parameterTypes":["java.lang.Runnable","java.time.Instant","java.time.Duration"] }, {"name":"submit","parameterTypes":["java.lang.Runnable"] }, {"name":"submit","parameterTypes":["java.util.concurrent.Callable"] }, {"name":"submitListenable","parameterTypes":["java.lang.Runnable"] }, {"name":"submitListenable","parameterTypes":["java.util.concurrent.Callable"] }] }, { "name":"org.springframework.scheduling.config.ScheduledTaskHolder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.scheduling.config.ScheduledTaskRegistrar" }, { "name":"org.springframework.security.access.AccessDeniedException" }, { "name":"org.springframework.security.access.expression.AbstractSecurityExpressionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"createSecurityExpressionRoot","parameterTypes":["org.springframework.security.core.Authentication","java.lang.Object"] }] }, { "name":"org.springframework.security.access.expression.SecurityExpressionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler" }, { "name":"org.springframework.security.access.expression.method.MethodSecurityExpressionHandler" }, { "name":"org.springframework.security.aot.hint.CoreSecurityRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.authentication.AnonymousAuthenticationProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"authenticate","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"setMessageSource","parameterTypes":["org.springframework.context.MessageSource"] }, {"name":"supports","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.security.authentication.AuthenticationEventPublisher", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.authentication.AuthenticationManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.authentication.AuthenticationManagerResolver" }, { "name":"org.springframework.security.authentication.AuthenticationProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.authentication.AuthenticationTrustResolver" }, { "name":"org.springframework.security.authentication.DefaultAuthenticationEventPublisher", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"publishAuthenticationFailure","parameterTypes":["org.springframework.security.core.AuthenticationException","org.springframework.security.core.Authentication"] }, {"name":"publishAuthenticationSuccess","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"setApplicationEventPublisher","parameterTypes":["org.springframework.context.ApplicationEventPublisher"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.authentication.ProviderManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"authenticate","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"close","parameterTypes":[] }, {"name":"setMessageSource","parameterTypes":["org.springframework.context.MessageSource"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.authentication.ReactiveAuthenticationManager" }, { "name":"org.springframework.security.authentication.UsernamePasswordAuthenticationToken" }, { "name":"org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"authenticate","parameterTypes":["org.springframework.security.core.Authentication"] }, {"name":"getUserCache","parameterTypes":[] }, {"name":"isForcePrincipalAsString","parameterTypes":[] }, {"name":"isHideUserNotFoundExceptions","parameterTypes":[] }, {"name":"setAuthoritiesMapper","parameterTypes":["org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper"] }, {"name":"setForcePrincipalAsString","parameterTypes":["boolean"] }, {"name":"setHideUserNotFoundExceptions","parameterTypes":["boolean"] }, {"name":"setMessageSource","parameterTypes":["org.springframework.context.MessageSource"] }, {"name":"setPostAuthenticationChecks","parameterTypes":["org.springframework.security.core.userdetails.UserDetailsChecker"] }, {"name":"setPreAuthenticationChecks","parameterTypes":["org.springframework.security.core.userdetails.UserDetailsChecker"] }, {"name":"setUserCache","parameterTypes":["org.springframework.security.core.userdetails.UserCache"] }, {"name":"supports","parameterTypes":["java.lang.Class"] }] }, { "name":"org.springframework.security.authentication.dao.DaoAuthenticationProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureBadCredentialsEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureCredentialsExpiredEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureDisabledEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureExpiredEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureLockedEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureProviderNotFoundEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureProxyUntrustedEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.event.AuthenticationFailureServiceExceptionEvent", "methods":[{"name":"","parameterTypes":["org.springframework.security.core.Authentication","org.springframework.security.core.AuthenticationException"] }] }, { "name":"org.springframework.security.authentication.password.CompromisedPasswordChecker" }, { "name":"org.springframework.security.authorization.AuthorizationManager" }, { "name":"org.springframework.security.authorization.AuthorizationProxyFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.authorization.method.AuthorizationAdvisor", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"iterator","parameterTypes":[] }, {"name":"proxy","parameterTypes":["java.lang.Object"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory$TargetVisitor" }, { "name":"org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor" }, { "name":"org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor" }, { "name":"org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getAdvice","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"getPointcut","parameterTypes":[] }, {"name":"invoke","parameterTypes":["org.aopalliance.intercept.MethodInvocation"] }, {"name":"isPerInstance","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager" }, { "name":"org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor" }, { "name":"org.springframework.security.authorization.method.PreAuthorizeAuthorizationManager" }, { "name":"org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor" }, { "name":"org.springframework.security.config.Customizer" }, { "name":"org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"apply","parameterTypes":["org.springframework.security.config.annotation.SecurityConfigurer"] }, {"name":"apply","parameterTypes":["org.springframework.security.config.annotation.SecurityConfigurerAdapter"] }, {"name":"getConfigurer","parameterTypes":["java.lang.Class"] }, {"name":"getConfigurers","parameterTypes":["java.lang.Class"] }, {"name":"getOrBuild","parameterTypes":[] }, {"name":"getSharedObject","parameterTypes":["java.lang.Class"] }, {"name":"getSharedObjects","parameterTypes":[] }, {"name":"objectPostProcessor","parameterTypes":["org.springframework.security.config.annotation.ObjectPostProcessor"] }, {"name":"removeConfigurer","parameterTypes":["java.lang.Class"] }, {"name":"removeConfigurers","parameterTypes":["java.lang.Class"] }, {"name":"setSharedObject","parameterTypes":["java.lang.Class","java.lang.Object"] }, {"name":"with","parameterTypes":["org.springframework.security.config.annotation.SecurityConfigurerAdapter","org.springframework.security.config.Customizer"] }] }, { "name":"org.springframework.security.config.annotation.AbstractSecurityBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"build","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.ObjectPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.SecurityBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.config.annotation.SecurityConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"init","parameterTypes":["org.springframework.security.config.annotation.SecurityBuilder"] }] }, { "name":"org.springframework.security.config.annotation.SecurityConfigurerAdapter" }, { "name":"org.springframework.security.config.annotation.authentication.ProviderManagerBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"authenticationEventPublisher","parameterTypes":["org.springframework.security.authentication.AuthenticationEventPublisher"] }, {"name":"authenticationProvider","parameterTypes":["org.springframework.security.authentication.AuthenticationProvider"] }, {"name":"close","parameterTypes":[] }, {"name":"eraseCredentials","parameterTypes":["boolean"] }, {"name":"getDefaultUserDetailsService","parameterTypes":[] }, {"name":"isConfigured","parameterTypes":[] }, {"name":"ldapAuthentication","parameterTypes":[] }, {"name":"parentAuthenticationManager","parameterTypes":["org.springframework.security.authentication.AuthenticationManager"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"authenticationManagerBuilder","parameterTypes":["org.springframework.security.config.annotation.ObjectPostProcessor","org.springframework.context.ApplicationContext"] }, {"name":"enableGlobalAuthenticationAutowiredConfigurer","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"initializeAuthenticationProviderBeanManagerConfigurer","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"initializeUserDetailsBeanManagerConfigurer","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setGlobalAuthenticationConfigurers","parameterTypes":["java.util.List"] }, {"name":"setObjectPostProcessor","parameterTypes":["org.springframework.security.config.annotation.ObjectPostProcessor"] }] }, { "name":"org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$DefaultPasswordEncoderAuthenticationManagerBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"inMemoryAuthentication","parameterTypes":[] }, {"name":"jdbcAuthentication","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"userDetailsService","parameterTypes":["org.springframework.security.core.userdetails.UserDetailsService"] }] }, { "name":"org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration$EnableGlobalAuthenticationAutowiredConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.SecurityBuilder"] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.authentication.configuration.AuthenticationManagerBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.authentication.configuration.EnableGlobalAuthentication", "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.config.annotation.authentication.configuration.GlobalAuthenticationConfigurerAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"configure","parameterTypes":["org.springframework.security.config.annotation.SecurityBuilder"] }, {"name":"configure","parameterTypes":["org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder"] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.SecurityBuilder"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.authentication.configuration.InitializeAuthenticationProviderBeanManagerConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.SecurityBuilder"] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.authentication.configuration.InitializeUserDetailsBeanManagerConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.SecurityBuilder"] }, {"name":"init","parameterTypes":["org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.authentication.configurers.ldap.LdapAuthenticationProviderConfigurer" }, { "name":"org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer" }, { "name":"org.springframework.security.config.annotation.authentication.configurers.provisioning.JdbcUserDetailsManagerConfigurer" }, { "name":"org.springframework.security.config.annotation.authentication.configurers.userdetails.DaoAuthenticationConfigurer" }, { "name":"org.springframework.security.config.annotation.authentication.configurers.userdetails.UserDetailsAwareConfigurer" }, { "name":"org.springframework.security.config.annotation.configuration.AutowireBeanFactoryObjectPostProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterSingletonsInstantiated","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"postProcess","parameterTypes":["java.lang.Object"] }] }, { "name":"org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"objectPostProcessor","parameterTypes":["org.springframework.beans.factory.config.AutowireCapableBeanFactory"] }] }, { "name":"org.springframework.security.config.annotation.method.configuration.AuthorizationProxyConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"authorizationProxyFactory","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider"] }, {"name":"authorizeReturnObjectMethodInterceptor","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory"] }] }, { "name":"org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity", "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.config.annotation.method.configuration.MethodSecurityAdvisorRegistrar", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.method.configuration.MethodSecuritySelector", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.method.configuration.PrePostMethodSecurityConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"postAuthorizeAuthorizationMethodInterceptor","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.security.config.annotation.method.configuration.PrePostMethodSecurityConfiguration","org.springframework.context.ApplicationContext"] }, {"name":"postFilterAuthorizationMethodInterceptor","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.security.config.annotation.method.configuration.PrePostMethodSecurityConfiguration","org.springframework.context.ApplicationContext"] }, {"name":"preAuthorizeAuthorizationMethodInterceptor","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.security.config.annotation.method.configuration.PrePostMethodSecurityConfiguration","org.springframework.context.ApplicationContext"] }, {"name":"preFilterAuthorizationMethodInterceptor","parameterTypes":["org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.beans.factory.ObjectProvider","org.springframework.security.config.annotation.method.configuration.PrePostMethodSecurityConfiguration","org.springframework.context.ApplicationContext"] }, {"name":"setImportMetadata","parameterTypes":["org.springframework.core.type.AnnotationMetadata"] }] }, { "name":"org.springframework.security.config.annotation.method.configuration.PrePostMethodSecurityConfiguration$DeferringMethodInterceptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getAdvice","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"getPointcut","parameterTypes":[] }, {"name":"invoke","parameterTypes":["org.aopalliance.intercept.MethodInvocation"] }, {"name":"isPerInstance","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.web.HttpSecurityBuilder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.config.annotation.web.builders.HttpSecurity", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"addFilter","parameterTypes":["jakarta.servlet.Filter"] }, {"name":"addFilterAfter","parameterTypes":["jakarta.servlet.Filter","java.lang.Class"] }, {"name":"addFilterBefore","parameterTypes":["jakarta.servlet.Filter","java.lang.Class"] }, {"name":"authenticationProvider","parameterTypes":["org.springframework.security.authentication.AuthenticationProvider"] }, {"name":"close","parameterTypes":[] }, {"name":"setSharedObject","parameterTypes":["java.lang.Class","java.lang.Object"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"userDetailsService","parameterTypes":["org.springframework.security.core.userdetails.UserDetailsService"] }] }, { "name":"org.springframework.security.config.annotation.web.builders.HttpSecurity$RequestMatcherConfigurer" }, { "name":"org.springframework.security.config.annotation.web.builders.WebSecurity", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setServletContext","parameterTypes":["jakarta.servlet.ServletContext"] }] }, { "name":"org.springframework.security.config.annotation.web.builders.WebSecurity$IgnoredRequestConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configuration.EnableWebSecurity", "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"httpSecurity","parameterTypes":[] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setAuthenticationConfiguration","parameterTypes":["org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration"] }, {"name":"setContentNegotiationStrategy","parameterTypes":["org.springframework.web.accept.ContentNegotiationStrategy"] }, {"name":"setObjectPostProcessor","parameterTypes":["org.springframework.security.config.annotation.ObjectPostProcessor"] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.OAuth2ImportSelector", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.SpringWebMvcImportSelector", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"addArgumentResolvers","parameterTypes":["java.util.List"] }, {"name":"requestDataValueProcessor","parameterTypes":[] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"springSecurityHandlerMappingIntrospectorBeanDefinitionRegistryPostProcessor","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$1", "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"postProcessBeanDefinitionRegistry","parameterTypes":["org.springframework.beans.factory.support.BeanDefinitionRegistry"] }, {"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$CompositeFilterChainProxy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["java.util.List"] }, {"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"getFilterChains","parameterTypes":[] }, {"name":"getFilters","parameterTypes":["java.lang.String"] }, {"name":"setFilterChainDecorator","parameterTypes":["org.springframework.security.web.FilterChainProxy$FilterChainDecorator"] }, {"name":"setFilterChainValidator","parameterTypes":["org.springframework.security.web.FilterChainProxy$FilterChainValidator"] }, {"name":"setFirewall","parameterTypes":["org.springframework.security.web.firewall.HttpFirewall"] }, {"name":"setRequestRejectedHandler","parameterTypes":["org.springframework.security.web.firewall.RequestRejectedHandler"] }, {"name":"setSecurityContextHolderStrategy","parameterTypes":["org.springframework.security.core.context.SecurityContextHolderStrategy"] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.WebMvcSecurityConfiguration$HandlerMappingIntrospectorCacheFilterFactoryBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"getObject","parameterTypes":[] }, {"name":"getObjectType","parameterTypes":[] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"conversionServicePostProcessor","parameterTypes":[] }, {"name":"delegatingApplicationListener","parameterTypes":[] }, {"name":"privilegeEvaluator","parameterTypes":[] }, {"name":"setBeanClassLoader","parameterTypes":["java.lang.ClassLoader"] }, {"name":"setFilterChainProxySecurityConfigurer","parameterTypes":["org.springframework.security.config.annotation.ObjectPostProcessor","org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"setFilterChains","parameterTypes":["java.util.List"] }, {"name":"setImportMetadata","parameterTypes":["org.springframework.core.type.AnnotationMetadata"] }, {"name":"setWebSecurityCustomizers","parameterTypes":["java.util.List"] }, {"name":"springSecurityFilterChain","parameterTypes":[] }, {"name":"webSecurityExpressionHandler","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.annotation.web.configurers.AnonymousConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer$AuthorizationManagerRequestMatcherRegistry" }, { "name":"org.springframework.security.config.annotation.web.configurers.ChannelSecurityConfigurer$ChannelRequestMatcherRegistry" }, { "name":"org.springframework.security.config.annotation.web.configurers.CorsConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.CsrfConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.ExceptionHandlingConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer$ExpressionInterceptUrlRegistry" }, { "name":"org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.HeadersConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.JeeConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.LogoutConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.PortMapperConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.RememberMeConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.RequestCacheConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.ServletApiConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.X509Configurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2ClientConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.oauth2.client.OAuth2LoginConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.oauth2.client.OidcLogoutConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.saml2.Saml2LoginConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.saml2.Saml2LogoutConfigurer" }, { "name":"org.springframework.security.config.annotation.web.configurers.saml2.Saml2MetadataConfigurer" }, { "name":"org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity" }, { "name":"org.springframework.security.config.aot.hint.OAuth2LoginRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.config.aot.hint.WebMvcSecurityConfigurationRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.config.core.GrantedAuthorityDefaults" }, { "name":"org.springframework.security.config.crypto.RsaKeyConversionServicePostProcessor", "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"postProcessBeanFactory","parameterTypes":["org.springframework.beans.factory.config.ConfigurableListableBeanFactory"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.config.http.SessionCreationPolicy" }, { "name":"org.springframework.security.context.DelegatingApplicationListener", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.core.Authentication" }, { "name":"org.springframework.security.core.AuthenticationException" }, { "name":"org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper" }, { "name":"org.springframework.security.core.context.SecurityContextHolderStrategy" }, { "name":"org.springframework.security.core.userdetails.UserCache" }, { "name":"org.springframework.security.core.userdetails.UserDetails" }, { "name":"org.springframework.security.core.userdetails.UserDetailsChecker" }, { "name":"org.springframework.security.core.userdetails.UserDetailsPasswordService" }, { "name":"org.springframework.security.core.userdetails.UserDetailsService", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.core.userdetails.UsernameNotFoundException" }, { "name":"org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"encode","parameterTypes":["java.lang.CharSequence"] }, {"name":"matches","parameterTypes":["java.lang.CharSequence","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"upgradeEncoding","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.security.crypto.password.PasswordEncoder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.data.repository.query.SecurityEvaluationContextExtension" }, { "name":"org.springframework.security.oauth2.client.registration.ClientRegistration" }, { "name":"org.springframework.security.oauth2.client.registration.ClientRegistrationRepository" }, { "name":"org.springframework.security.oauth2.jwt.JwtDecoder" }, { "name":"org.springframework.security.oauth2.server.authorization.OAuth2Authorization" }, { "name":"org.springframework.security.oauth2.server.resource.BearerTokenError" }, { "name":"org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthenticationToken" }, { "name":"org.springframework.security.oauth2.server.resource.introspection.OpaqueTokenIntrospector" }, { "name":"org.springframework.security.oauth2.server.resource.introspection.ReactiveOpaqueTokenIntrospector" }, { "name":"org.springframework.security.rsocket.core.SecuritySocketAcceptorInterceptor" }, { "name":"org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository" }, { "name":"org.springframework.security.web.AuthenticationEntryPoint", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.web.DefaultSecurityFilterChain", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getFilters","parameterTypes":[] }, {"name":"matches","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.security.web.FilterChainProxy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"toFilter","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.security.web.FilterChainProxy$FilterChainDecorator" }, { "name":"org.springframework.security.web.FilterChainProxy$FilterChainValidator" }, { "name":"org.springframework.security.web.SecurityFilterChain", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.web.access.AccessDeniedHandler" }, { "name":"org.springframework.security.web.access.AuthorizationManagerWebInvocationPrivilegeEvaluator$HttpServletRequestTransformer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.security.web.access.ExceptionTranslationFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }, {"name":"setMessageSource","parameterTypes":["org.springframework.context.MessageSource"] }] }, { "name":"org.springframework.security.web.access.HandlerMappingIntrospectorRequestTransformer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":["org.springframework.web.servlet.handler.HandlerMappingIntrospector"] }, {"name":"transform","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }] }, { "name":"org.springframework.security.web.access.RequestMatcherDelegatingWebInvocationPrivilegeEvaluator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"isAllowed","parameterTypes":["java.lang.String","java.lang.String","java.lang.String","org.springframework.security.core.Authentication"] }, {"name":"isAllowed","parameterTypes":["java.lang.String","org.springframework.security.core.Authentication"] }, {"name":"setServletContext","parameterTypes":["jakarta.servlet.ServletContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.web.access.WebInvocationPrivilegeEvaluator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.web.access.intercept.AuthorizationFilter", "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.web.aot.hint.WebMvcSecurityRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.security.web.authentication.AnonymousAuthenticationFilter", "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.web.authentication.logout.LogoutFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }] }, { "name":"org.springframework.security.web.authentication.logout.LogoutHandler", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.security.web.authentication.logout.LogoutSuccessEventPublishingLogoutHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"logout","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","org.springframework.security.core.Authentication"] }, {"name":"setApplicationEventPublisher","parameterTypes":["org.springframework.context.ApplicationEventPublisher"] }] }, { "name":"org.springframework.security.web.authentication.session.AbstractSessionFixationProtectionStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"onAuthentication","parameterTypes":["org.springframework.security.core.Authentication","jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse"] }, {"name":"setAlwaysCreateSession","parameterTypes":["boolean"] }, {"name":"setApplicationEventPublisher","parameterTypes":["org.springframework.context.ApplicationEventPublisher"] }] }, { "name":"org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.web.authentication.session.CompositeSessionAuthenticationStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"onAuthentication","parameterTypes":["org.springframework.security.core.Authentication","jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.security.web.authentication.session.SessionAuthenticationException" }, { "name":"org.springframework.security.web.authentication.session.SessionAuthenticationStrategy", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.security.web.authentication.www.BasicAuthenticationFilter", "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer" }, { "name":"org.springframework.security.web.context.SecurityContextHolderFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }] }, { "name":"org.springframework.security.web.context.SecurityContextRepository" }, { "name":"org.springframework.security.web.csrf.CsrfFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.web.csrf.CsrfTokenRequestHandler" }, { "name":"org.springframework.security.web.firewall.FirewalledRequest" }, { "name":"org.springframework.security.web.firewall.HttpFirewall" }, { "name":"org.springframework.security.web.firewall.RequestRejectedHandler" }, { "name":"org.springframework.security.web.header.HeaderWriterFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.security.web.savedrequest.RequestCacheAwareFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }] }, { "name":"org.springframework.security.web.server.WebFilterChainProxy" }, { "name":"org.springframework.security.web.servlet.support.csrf.CsrfRequestDataValueProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getExtraHiddenFields","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"processAction","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.String","java.lang.String"] }, {"name":"processFormFieldValue","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.String","java.lang.String","java.lang.String"] }, {"name":"processUrl","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"extractUriTemplateVariables","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"hashCode","parameterTypes":[] }, {"name":"matcher","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"matches","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.security.web.servletapi.HttpServletRequestFactory" }, { "name":"org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }] }, { "name":"org.springframework.security.web.util.ThrowableAnalyzer" }, { "name":"org.springframework.security.web.util.matcher.RequestMatcher", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.security.web.util.matcher.RequestMatcher$MatchResult" }, { "name":"org.springframework.security.web.util.matcher.RequestMatcherEntry" }, { "name":"org.springframework.security.web.util.matcher.RequestVariablesExtractor", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.session.Session" }, { "name":"org.springframework.stereotype.Component", "queryAllDeclaredMethods":true }, { "name":"org.springframework.stereotype.Controller", "queryAllDeclaredMethods":true }, { "name":"org.springframework.stereotype.Indexed", "queryAllDeclaredMethods":true }, { "name":"org.springframework.stereotype.Service", "queryAllDeclaredMethods":true }, { "name":"org.springframework.transaction.ConfigurableTransactionManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"addListener","parameterTypes":["org.springframework.transaction.TransactionExecutionListener"] }] }, { "name":"org.springframework.transaction.NoTransactionException" }, { "name":"org.springframework.transaction.PlatformTransactionManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.transaction.ReactiveTransactionManager" }, { "name":"org.springframework.transaction.TransactionDefinition", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.transaction.TransactionException" }, { "name":"org.springframework.transaction.TransactionExecutionListener" }, { "name":"org.springframework.transaction.TransactionManager", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.transaction.TransactionStatus" }, { "name":"org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setImportMetadata","parameterTypes":["org.springframework.core.type.AnnotationMetadata"] }, {"name":"transactionalEventListenerFactory","parameterTypes":[] }] }, { "name":"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"hashCode","parameterTypes":[] }, {"name":"isCandidateClass","parameterTypes":["java.lang.Class"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.transaction.annotation.EnableTransactionManagement", "queryAllDeclaredMethods":true }, { "name":"org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }, {"name":"transactionAdvisor","parameterTypes":["org.springframework.transaction.interceptor.TransactionAttributeSource","org.springframework.transaction.interceptor.TransactionInterceptor"] }, {"name":"transactionAttributeSource","parameterTypes":[] }, {"name":"transactionInterceptor","parameterTypes":["org.springframework.transaction.interceptor.TransactionAttributeSource"] }] }, { "name":"org.springframework.transaction.annotation.RestrictedTransactionalEventListenerFactory", "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"createApplicationListener","parameterTypes":["java.lang.String","java.lang.Class","java.lang.reflect.Method"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.transaction.annotation.TransactionBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.transaction.annotation.TransactionManagementConfigurationSelector", "queryAllDeclaredConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.transaction.annotation.TransactionRuntimeHints", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.transaction.aspectj.AbstractTransactionAspect" }, { "name":"org.springframework.transaction.event.TransactionalEventListenerFactory", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getOrder","parameterTypes":[] }, {"name":"setOrder","parameterTypes":["int"] }, {"name":"supportsMethod","parameterTypes":["java.lang.reflect.Method"] }] }, { "name":"org.springframework.transaction.interceptor.AbstractFallbackTransactionAttributeSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getTransactionAttribute","parameterTypes":["java.lang.reflect.Method","java.lang.Class"] }, {"name":"setEmbeddedValueResolver","parameterTypes":["org.springframework.util.StringValueResolver"] }] }, { "name":"org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getPointcut","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.transaction.interceptor.TransactionAspectSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"getTransactionAttributeSource","parameterTypes":[] }, {"name":"getTransactionManager","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"setTransactionAttributeSource","parameterTypes":["org.springframework.transaction.interceptor.TransactionAttributeSource"] }, {"name":"setTransactionAttributeSources","parameterTypes":["org.springframework.transaction.interceptor.TransactionAttributeSource[]"] }, {"name":"setTransactionAttributes","parameterTypes":["java.util.Properties"] }, {"name":"setTransactionManager","parameterTypes":["org.springframework.transaction.TransactionManager"] }, {"name":"setTransactionManagerBeanName","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.transaction.interceptor.TransactionAspectSupport$CoroutinesInvocationCallback" }, { "name":"org.springframework.transaction.interceptor.TransactionAspectSupport$InvocationCallback" }, { "name":"org.springframework.transaction.interceptor.TransactionAspectSupport$ReactiveTransactionSupport" }, { "name":"org.springframework.transaction.interceptor.TransactionAspectSupport$ThrowableHolder" }, { "name":"org.springframework.transaction.interceptor.TransactionAspectSupport$TransactionInfo" }, { "name":"org.springframework.transaction.interceptor.TransactionAttribute" }, { "name":"org.springframework.transaction.interceptor.TransactionAttributeSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.transaction.interceptor.TransactionInterceptor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"invoke","parameterTypes":["org.aopalliance.intercept.MethodInvocation"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.transaction.reactive.TransactionalOperator" }, { "name":"org.springframework.transaction.support.AbstractPlatformTransactionManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"commit","parameterTypes":["org.springframework.transaction.TransactionStatus"] }, {"name":"getTransaction","parameterTypes":["org.springframework.transaction.TransactionDefinition"] }, {"name":"getTransactionExecutionListeners","parameterTypes":[] }, {"name":"rollback","parameterTypes":["org.springframework.transaction.TransactionStatus"] }, {"name":"setTransactionExecutionListeners","parameterTypes":["java.util.Collection"] }] }, { "name":"org.springframework.transaction.support.AbstractPlatformTransactionManager$SuspendedResourcesHolder" }, { "name":"org.springframework.transaction.support.DefaultTransactionDefinition", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getIsolationLevel","parameterTypes":[] }, {"name":"getName","parameterTypes":[] }, {"name":"getPropagationBehavior","parameterTypes":[] }, {"name":"getTimeout","parameterTypes":[] }, {"name":"hashCode","parameterTypes":[] }, {"name":"isReadOnly","parameterTypes":[] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.transaction.support.DefaultTransactionStatus" }, { "name":"org.springframework.transaction.support.ResourceTransactionManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.transaction.support.TransactionCallback" }, { "name":"org.springframework.transaction.support.TransactionOperations", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"executeWithoutResult","parameterTypes":["java.util.function.Consumer"] }] }, { "name":"org.springframework.transaction.support.TransactionTemplate", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"equals","parameterTypes":["java.lang.Object"] }, {"name":"execute","parameterTypes":["org.springframework.transaction.support.TransactionCallback"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.ui.ModelMap" }, { "name":"org.springframework.ui.context.ThemeSource" }, { "name":"org.springframework.util.AntPathMatcher", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"combine","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"extractPathWithinPattern","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"extractUriTemplateVariables","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"getPatternComparator","parameterTypes":["java.lang.String"] }, {"name":"isPattern","parameterTypes":["java.lang.String"] }, {"name":"match","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"matchStart","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.util.AntPathMatcher$AntPathStringMatcher" }, { "name":"org.springframework.util.CustomizableThreadCreator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"createThread","parameterTypes":["java.lang.Runnable"] }, {"name":"getThreadGroup","parameterTypes":[] }, {"name":"getThreadNamePrefix","parameterTypes":[] }, {"name":"getThreadPriority","parameterTypes":[] }, {"name":"isDaemon","parameterTypes":[] }, {"name":"setDaemon","parameterTypes":["boolean"] }, {"name":"setThreadGroup","parameterTypes":["java.lang.ThreadGroup"] }, {"name":"setThreadGroupName","parameterTypes":["java.lang.String"] }, {"name":"setThreadPriority","parameterTypes":["int"] }] }, { "name":"org.springframework.util.ErrorHandler" }, { "name":"org.springframework.util.MimeType", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.util.MultiValueMap", "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "methods":[{"name":"putIfAbsent","parameterTypes":["java.lang.Object","java.lang.Object"] }] }, { "name":"org.springframework.util.PathMatcher", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.util.PropertiesPersister" }, { "name":"org.springframework.util.StringValueResolver" }, { "name":"org.springframework.util.concurrent.ListenableFuture" }, { "name":"org.springframework.util.concurrent.ListenableFutureTask" }, { "name":"org.springframework.util.unit.DataSize" }, { "name":"org.springframework.validation.BindingResult" }, { "name":"org.springframework.validation.DefaultMessageCodesResolver$Format" }, { "name":"org.springframework.validation.Errors" }, { "name":"org.springframework.validation.MessageCodesResolver" }, { "name":"org.springframework.validation.Validator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"validateObject","parameterTypes":["java.lang.Object"] }] }, { "name":"org.springframework.validation.beanvalidation.BeanValidationBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.validation.method.MethodValidationResult" }, { "name":"org.springframework.web.HttpMediaTypeException" }, { "name":"org.springframework.web.HttpMediaTypeNotAcceptableException" }, { "name":"org.springframework.web.HttpSessionRequiredException" }, { "name":"org.springframework.web.accept.ContentNegotiationManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getAllFileExtensions","parameterTypes":[] }, {"name":"resolveFileExtensions","parameterTypes":["org.springframework.http.MediaType"] }, {"name":"resolveMediaTypes","parameterTypes":["org.springframework.web.context.request.NativeWebRequest"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.accept.ContentNegotiationStrategy", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.accept.MediaTypeFileExtensionResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.bind.MissingServletRequestParameterException" }, { "name":"org.springframework.web.bind.annotation.ControllerAdvice", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.ControllerMappingReflectiveProcessor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.web.bind.annotation.CrossOrigin" }, { "name":"org.springframework.web.bind.annotation.DeleteMapping", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.ExceptionHandler", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.ExceptionHandlerReflectiveProcessor", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.web.bind.annotation.GetMapping", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.Mapping", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.PatchMapping", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.PostMapping", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.PutMapping", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.RequestBody", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.RequestMapping", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.RequestMethod", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.web.bind.annotation.RequestParam", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.ResponseBody", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.ResponseStatus", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.annotation.RestController", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.bind.support.ConfigurableWebBindingInitializer" }, { "name":"org.springframework.web.bind.support.SessionAttributeStore" }, { "name":"org.springframework.web.bind.support.WebBindingInitializer" }, { "name":"org.springframework.web.bind.support.WebDataBinderFactory" }, { "name":"org.springframework.web.client.ResponseErrorHandler" }, { "name":"org.springframework.web.client.RestClient" }, { "name":"org.springframework.web.client.RestClient$Builder", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.client.RestClient$ResponseSpec$ErrorHandler" }, { "name":"org.springframework.web.client.RestTemplate" }, { "name":"org.springframework.web.context.ConfigurableWebApplicationContext" }, { "name":"org.springframework.web.context.ServletContextAware", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.context.WebApplicationContext" }, { "name":"org.springframework.web.context.request.NativeWebRequest" }, { "name":"org.springframework.web.context.request.RequestAttributes" }, { "name":"org.springframework.web.context.request.RequestContextListener" }, { "name":"org.springframework.web.context.request.ServletRequestAttributes" }, { "name":"org.springframework.web.context.request.WebRequest" }, { "name":"org.springframework.web.context.request.async.DeferredResult", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.web.context.request.async.DeferredResult$DeferredResultHandler", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true }, { "name":"org.springframework.web.context.request.async.WebAsyncManager" }, { "name":"org.springframework.web.context.support.GenericWebApplicationContext" }, { "name":"org.springframework.web.context.support.WebApplicationObjectSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setServletContext","parameterTypes":["jakarta.servlet.ServletContext"] }] }, { "name":"org.springframework.web.cors.CorsConfiguration" }, { "name":"org.springframework.web.cors.CorsConfigurationSource", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.cors.CorsProcessor" }, { "name":"org.springframework.web.filter.CharacterEncodingFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"getEncoding","parameterTypes":[] }, {"name":"isForceRequestEncoding","parameterTypes":[] }, {"name":"isForceResponseEncoding","parameterTypes":[] }, {"name":"setEncoding","parameterTypes":["java.lang.String"] }, {"name":"setForceEncoding","parameterTypes":["boolean"] }, {"name":"setForceRequestEncoding","parameterTypes":["boolean"] }, {"name":"setForceResponseEncoding","parameterTypes":["boolean"] }] }, { "name":"org.springframework.web.filter.CorsFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.filter.DelegatingFilterProxy", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.filter.FormContentFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setCharset","parameterTypes":["java.nio.charset.Charset"] }, {"name":"setFormConverter","parameterTypes":["org.springframework.http.converter.FormHttpMessageConverter"] }] }, { "name":"org.springframework.web.filter.GenericFilterBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"destroy","parameterTypes":[] }, {"name":"getEnvironment","parameterTypes":[] }, {"name":"getFilterConfig","parameterTypes":[] }, {"name":"init","parameterTypes":["jakarta.servlet.FilterConfig"] }, {"name":"setBeanName","parameterTypes":["java.lang.String"] }, {"name":"setEnvironment","parameterTypes":["org.springframework.core.env.Environment"] }, {"name":"setServletContext","parameterTypes":["jakarta.servlet.ServletContext"] }] }, { "name":"org.springframework.web.filter.OncePerRequestFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"doFilter","parameterTypes":["jakarta.servlet.ServletRequest","jakarta.servlet.ServletResponse","jakarta.servlet.FilterChain"] }] }, { "name":"org.springframework.web.filter.RequestContextFilter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"setThreadContextInheritable","parameterTypes":["boolean"] }] }, { "name":"org.springframework.web.filter.ServerHttpObservationFilter", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.method.ControllerAdviceBean" }, { "name":"org.springframework.web.method.HandlerMethod" }, { "name":"org.springframework.web.method.annotation.ExceptionHandlerMethodResolver", "methods":[{"name":"noMatchingExceptionHandler","parameterTypes":[] }] }, { "name":"org.springframework.web.method.annotation.InitBinderDataBinderFactory" }, { "name":"org.springframework.web.method.annotation.ModelFactory" }, { "name":"org.springframework.web.method.annotation.SessionAttributesHandler" }, { "name":"org.springframework.web.method.support.CompositeUriComponentsContributor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"contributeMethodArgument","parameterTypes":["org.springframework.core.MethodParameter","java.lang.Object","org.springframework.web.util.UriComponentsBuilder","java.util.Map","org.springframework.core.convert.ConversionService"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"supportsParameter","parameterTypes":["org.springframework.core.MethodParameter"] }] }, { "name":"org.springframework.web.method.support.InvocableHandlerMethod" }, { "name":"org.springframework.web.method.support.ModelAndViewContainer" }, { "name":"org.springframework.web.method.support.UriComponentsContributor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.multipart.MultipartException" }, { "name":"org.springframework.web.multipart.MultipartFile" }, { "name":"org.springframework.web.multipart.MultipartHttpServletRequest" }, { "name":"org.springframework.web.multipart.MultipartResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.multipart.support.StandardServletMultipartResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"cleanupMultipart","parameterTypes":["org.springframework.web.multipart.MultipartHttpServletRequest"] }, {"name":"close","parameterTypes":[] }, {"name":"isMultipart","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"resolveMultipart","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.reactive.DispatcherHandler" }, { "name":"org.springframework.web.reactive.HandlerResult" }, { "name":"org.springframework.web.reactive.config.EnableWebFlux" }, { "name":"org.springframework.web.reactive.config.WebFluxConfigurer" }, { "name":"org.springframework.web.reactive.function.client.ExchangeFilterFunction" }, { "name":"org.springframework.web.reactive.function.client.WebClient" }, { "name":"org.springframework.web.service.annotation.HttpExchange" }, { "name":"org.springframework.web.service.annotation.HttpExchangeBeanRegistrationAotProcessor", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.DispatcherServlet", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.FlashMap" }, { "name":"org.springframework.web.servlet.FlashMapManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.FrameworkServlet", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"destroy","parameterTypes":[] }, {"name":"getContextAttribute","parameterTypes":[] }, {"name":"getContextClass","parameterTypes":[] }, {"name":"getContextConfigLocation","parameterTypes":[] }, {"name":"getContextId","parameterTypes":[] }, {"name":"getNamespace","parameterTypes":[] }, {"name":"getServletContextAttributeName","parameterTypes":[] }, {"name":"isEnableLoggingRequestDetails","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.event.ContextRefreshedEvent"] }, {"name":"refresh","parameterTypes":[] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setContextAttribute","parameterTypes":["java.lang.String"] }, {"name":"setContextClass","parameterTypes":["java.lang.Class"] }, {"name":"setContextConfigLocation","parameterTypes":["java.lang.String"] }, {"name":"setContextId","parameterTypes":["java.lang.String"] }, {"name":"setContextInitializerClasses","parameterTypes":["java.lang.String"] }, {"name":"setContextInitializers","parameterTypes":["org.springframework.context.ApplicationContextInitializer[]"] }, {"name":"setDispatchOptionsRequest","parameterTypes":["boolean"] }, {"name":"setDispatchTraceRequest","parameterTypes":["boolean"] }, {"name":"setEnableLoggingRequestDetails","parameterTypes":["boolean"] }, {"name":"setNamespace","parameterTypes":["java.lang.String"] }, {"name":"setPublishContext","parameterTypes":["boolean"] }, {"name":"setPublishEvents","parameterTypes":["boolean"] }, {"name":"setThreadContextInheritable","parameterTypes":["boolean"] }] }, { "name":"org.springframework.web.servlet.HandlerAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.servlet.HandlerExceptionResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.HandlerExecutionChain" }, { "name":"org.springframework.web.servlet.HandlerInterceptor" }, { "name":"org.springframework.web.servlet.HandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.HttpServletBean", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getEnvironment","parameterTypes":[] }, {"name":"getServletName","parameterTypes":[] }, {"name":"init","parameterTypes":[] }, {"name":"setEnvironment","parameterTypes":["org.springframework.core.env.Environment"] }] }, { "name":"org.springframework.web.servlet.LocaleResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.ModelAndView" }, { "name":"org.springframework.web.servlet.RequestToViewNameTranslator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.ThemeResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.View", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.ViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer" }, { "name":"org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer" }, { "name":"org.springframework.web.servlet.config.annotation.CorsRegistry" }, { "name":"org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer" }, { "name":"org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setConfigurers","parameterTypes":["java.util.List"] }] }, { "name":"org.springframework.web.servlet.config.annotation.EnableWebMvc", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.servlet.config.annotation.InterceptorRegistry" }, { "name":"org.springframework.web.servlet.config.annotation.PathMatchConfigurer" }, { "name":"org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration" }, { "name":"org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry" }, { "name":"org.springframework.web.servlet.config.annotation.ViewControllerRegistry" }, { "name":"org.springframework.web.servlet.config.annotation.ViewResolverRegistry" }, { "name":"org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"beanNameHandlerMapping","parameterTypes":["org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.resource.ResourceUrlProvider"] }, {"name":"defaultServletHandlerMapping","parameterTypes":[] }, {"name":"handlerExceptionResolver","parameterTypes":["org.springframework.web.accept.ContentNegotiationManager"] }, {"name":"handlerFunctionAdapter","parameterTypes":[] }, {"name":"httpRequestHandlerAdapter","parameterTypes":[] }, {"name":"mvcHandlerMappingIntrospector","parameterTypes":[] }, {"name":"mvcPathMatcher","parameterTypes":[] }, {"name":"mvcPatternParser","parameterTypes":[] }, {"name":"mvcResourceUrlProvider","parameterTypes":[] }, {"name":"mvcUriComponentsContributor","parameterTypes":["org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"] }, {"name":"mvcUrlPathHelper","parameterTypes":[] }, {"name":"mvcViewResolver","parameterTypes":["org.springframework.web.accept.ContentNegotiationManager"] }, {"name":"requestMappingHandlerAdapter","parameterTypes":["org.springframework.web.accept.ContentNegotiationManager","org.springframework.format.support.FormattingConversionService","org.springframework.validation.Validator"] }, {"name":"requestMappingHandlerMapping","parameterTypes":["org.springframework.web.accept.ContentNegotiationManager","org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.resource.ResourceUrlProvider"] }, {"name":"resourceHandlerMapping","parameterTypes":["org.springframework.web.accept.ContentNegotiationManager","org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.resource.ResourceUrlProvider"] }, {"name":"routerFunctionMapping","parameterTypes":["org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.resource.ResourceUrlProvider"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setServletContext","parameterTypes":["jakarta.servlet.ServletContext"] }, {"name":"simpleControllerHandlerAdapter","parameterTypes":[] }, {"name":"viewControllerHandlerMapping","parameterTypes":["org.springframework.format.support.FormattingConversionService","org.springframework.web.servlet.resource.ResourceUrlProvider"] }, {"name":"viewNameTranslator","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport$NoOpValidator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }, {"name":"supports","parameterTypes":["java.lang.Class"] }, {"name":"validate","parameterTypes":["java.lang.Object","org.springframework.validation.Errors"] }] }, { "name":"org.springframework.web.servlet.config.annotation.WebMvcConfigurer", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"addArgumentResolvers","parameterTypes":["java.util.List"] }, {"name":"addCorsMappings","parameterTypes":["org.springframework.web.servlet.config.annotation.CorsRegistry"] }, {"name":"addFormatters","parameterTypes":["org.springframework.format.FormatterRegistry"] }, {"name":"addInterceptors","parameterTypes":["org.springframework.web.servlet.config.annotation.InterceptorRegistry"] }, {"name":"addResourceHandlers","parameterTypes":["org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry"] }, {"name":"addReturnValueHandlers","parameterTypes":["java.util.List"] }, {"name":"addViewControllers","parameterTypes":["org.springframework.web.servlet.config.annotation.ViewControllerRegistry"] }, {"name":"configureAsyncSupport","parameterTypes":["org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer"] }, {"name":"configureContentNegotiation","parameterTypes":["org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer"] }, {"name":"configureDefaultServletHandling","parameterTypes":["org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer"] }, {"name":"configureHandlerExceptionResolvers","parameterTypes":["java.util.List"] }, {"name":"configureMessageConverters","parameterTypes":["java.util.List"] }, {"name":"configurePathMatch","parameterTypes":["org.springframework.web.servlet.config.annotation.PathMatchConfigurer"] }, {"name":"configureViewResolvers","parameterTypes":["org.springframework.web.servlet.config.annotation.ViewResolverRegistry"] }, {"name":"extendHandlerExceptionResolvers","parameterTypes":["java.util.List"] }, {"name":"extendMessageConverters","parameterTypes":["java.util.List"] }, {"name":"getMessageCodesResolver","parameterTypes":[] }, {"name":"getValidator","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.function.HandlerFunction" }, { "name":"org.springframework.web.servlet.function.RouterFunction" }, { "name":"org.springframework.web.servlet.function.ServerRequest" }, { "name":"org.springframework.web.servlet.function.ServerResponse" }, { "name":"org.springframework.web.servlet.function.support.HandlerFunctionAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getLastModified","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.Object"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"handle","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.Object"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"supports","parameterTypes":["java.lang.Object"] }] }, { "name":"org.springframework.web.servlet.function.support.RouterFunctionMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"match","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.handler.AbstractDetectingUrlHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"initApplicationContext","parameterTypes":[] }, {"name":"setDetectHandlersInAncestorContexts","parameterTypes":["boolean"] }] }, { "name":"org.springframework.web.servlet.handler.AbstractHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getCorsConfigurationSource","parameterTypes":[] }, {"name":"getCorsProcessor","parameterTypes":[] }, {"name":"getDefaultHandler","parameterTypes":[] }, {"name":"getHandler","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"getPathMatcher","parameterTypes":[] }, {"name":"getPatternParser","parameterTypes":[] }, {"name":"getUrlPathHelper","parameterTypes":[] }, {"name":"setAlwaysUseFullPath","parameterTypes":["boolean"] }, {"name":"setBeanName","parameterTypes":["java.lang.String"] }, {"name":"setCorsConfigurationSource","parameterTypes":["org.springframework.web.cors.CorsConfigurationSource"] }, {"name":"setCorsConfigurations","parameterTypes":["java.util.Map"] }, {"name":"setCorsProcessor","parameterTypes":["org.springframework.web.cors.CorsProcessor"] }, {"name":"setDefaultHandler","parameterTypes":["java.lang.Object"] }, {"name":"setInterceptors","parameterTypes":["java.lang.Object[]"] }, {"name":"setOrder","parameterTypes":["int"] }, {"name":"setPathMatcher","parameterTypes":["org.springframework.util.PathMatcher"] }, {"name":"setPatternParser","parameterTypes":["org.springframework.web.util.pattern.PathPatternParser"] }, {"name":"setRemoveSemicolonContent","parameterTypes":["boolean"] }, {"name":"setUrlDecode","parameterTypes":["boolean"] }, {"name":"setUrlPathHelper","parameterTypes":["org.springframework.web.util.UrlPathHelper"] }, {"name":"usesPathPatterns","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.handler.AbstractHandlerMethodMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getDirectPaths","parameterTypes":["java.lang.Object"] }, {"name":"getHandlerMethods","parameterTypes":[] }, {"name":"getHandlerMethodsForMappingName","parameterTypes":["java.lang.String"] }, {"name":"getMappingPathPatterns","parameterTypes":["java.lang.Object"] }, {"name":"getMatchingMapping","parameterTypes":["java.lang.Object","jakarta.servlet.http.HttpServletRequest"] }, {"name":"getNamingStrategy","parameterTypes":[] }, {"name":"handleMatch","parameterTypes":["java.lang.Object","java.lang.String","jakarta.servlet.http.HttpServletRequest"] }, {"name":"initCorsConfiguration","parameterTypes":["java.lang.Object","java.lang.reflect.Method","java.lang.Object"] }, {"name":"registerHandlerMethod","parameterTypes":["java.lang.Object","java.lang.reflect.Method","java.lang.Object"] }, {"name":"registerMapping","parameterTypes":["java.lang.Object","java.lang.Object","java.lang.reflect.Method"] }, {"name":"setDetectHandlerMethodsInAncestorContexts","parameterTypes":["boolean"] }, {"name":"setHandlerMethodMappingNamingStrategy","parameterTypes":["org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy"] }, {"name":"setPatternParser","parameterTypes":["org.springframework.web.util.pattern.PathPatternParser"] }, {"name":"unregisterMapping","parameterTypes":["java.lang.Object"] }] }, { "name":"org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$EmptyHandler", "methods":[{"name":"handle","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.handler.AbstractHandlerMethodMapping$MappingRegistry" }, { "name":"org.springframework.web.servlet.handler.AbstractUrlHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getRootHandler","parameterTypes":[] }, {"name":"match","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.String"] }, {"name":"setLazyInitHandlers","parameterTypes":["boolean"] }, {"name":"setPatternParser","parameterTypes":["org.springframework.web.util.pattern.PathPatternParser"] }, {"name":"setRootHandler","parameterTypes":["java.lang.Object"] }, {"name":"setUseTrailingSlashMatch","parameterTypes":["boolean"] }, {"name":"useTrailingSlashMatch","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.handler.HandlerExceptionResolverComposite", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"resolveException","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.Object","java.lang.Exception"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.handler.HandlerMappingIntrospector", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"getCorsConfiguration","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.handler.HandlerMappingIntrospector$AttributesPreservingRequest" }, { "name":"org.springframework.web.servlet.handler.HandlerMappingIntrospector$CachedResult" }, { "name":"org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy" }, { "name":"org.springframework.web.servlet.handler.MappedInterceptor" }, { "name":"org.springframework.web.servlet.handler.MatchableHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true }, { "name":"org.springframework.web.servlet.handler.RequestMatchResult" }, { "name":"org.springframework.web.servlet.handler.SimpleUrlHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.i18n.AbstractLocaleResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"setDefaultLocale","parameterTypes":["java.util.Locale"] }] }, { "name":"org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"resolveLocale","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"setLocale","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.util.Locale"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getLastModified","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.Object"] }, {"name":"handle","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.Object"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"supports","parameterTypes":["java.lang.Object"] }] }, { "name":"org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getLastModified","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.Object"] }, {"name":"handle","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.Object"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"supports","parameterTypes":["java.lang.Object"] }] }, { "name":"org.springframework.web.servlet.mvc.condition.PathPatternsRequestCondition" }, { "name":"org.springframework.web.servlet.mvc.condition.PatternsRequestCondition" }, { "name":"org.springframework.web.servlet.mvc.condition.RequestCondition" }, { "name":"org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getLastModified","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.Object"] }, {"name":"getOrder","parameterTypes":[] }, {"name":"handle","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.Object"] }, {"name":"setOrder","parameterTypes":["int"] }, {"name":"supports","parameterTypes":["java.lang.Object"] }] }, { "name":"org.springframework.web.servlet.mvc.method.RequestMappingInfo" }, { "name":"org.springframework.web.servlet.mvc.method.RequestMappingInfo$BuilderConfiguration" }, { "name":"org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"initCorsConfiguration","parameterTypes":["java.lang.Object","java.lang.reflect.Method","java.lang.Object"] }, {"name":"registerHandlerMethod","parameterTypes":["java.lang.Object","java.lang.reflect.Method","java.lang.Object"] }, {"name":"registerMapping","parameterTypes":["java.lang.Object","java.lang.Object","java.lang.reflect.Method"] }] }, { "name":"org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping$HttpOptionsHandler", "methods":[{"name":"handle","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver" }, { "name":"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"setBeanFactory","parameterTypes":["org.springframework.beans.factory.BeanFactory"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"getBuilderConfiguration","parameterTypes":[] }, {"name":"getContentNegotiationManager","parameterTypes":[] }, {"name":"getFileExtensions","parameterTypes":[] }, {"name":"getPathPrefixes","parameterTypes":[] }, {"name":"initCorsConfiguration","parameterTypes":["java.lang.Object","java.lang.reflect.Method","java.lang.Object"] }, {"name":"match","parameterTypes":["jakarta.servlet.http.HttpServletRequest","java.lang.String"] }, {"name":"registerHandlerMethod","parameterTypes":["java.lang.Object","java.lang.reflect.Method","java.lang.Object"] }, {"name":"registerMapping","parameterTypes":["java.lang.Object","java.lang.Object","java.lang.reflect.Method"] }, {"name":"registerMapping","parameterTypes":["org.springframework.web.servlet.mvc.method.RequestMappingInfo","java.lang.Object","java.lang.reflect.Method"] }, {"name":"setContentNegotiationManager","parameterTypes":["org.springframework.web.accept.ContentNegotiationManager"] }, {"name":"setEmbeddedValueResolver","parameterTypes":["org.springframework.util.StringValueResolver"] }, {"name":"setPathPrefixes","parameterTypes":["java.util.Map"] }, {"name":"setPatternParser","parameterTypes":["org.springframework.web.util.pattern.PathPatternParser"] }, {"name":"setUseRegisteredSuffixPatternMatch","parameterTypes":["boolean"] }, {"name":"setUseSuffixPatternMatch","parameterTypes":["boolean"] }, {"name":"setUseTrailingSlashMatch","parameterTypes":["boolean"] }, {"name":"shutdown","parameterTypes":[] }, {"name":"useRegisteredSuffixPatternMatch","parameterTypes":[] }, {"name":"useSuffixPatternMatch","parameterTypes":[] }, {"name":"useTrailingSlashMatch","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping$AnnotationDescriptor" }, { "name":"org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod" }, { "name":"org.springframework.web.servlet.resource.ResourceUrlProvider", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"onApplicationEvent","parameterTypes":["org.springframework.context.ApplicationEvent"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.support.AbstractFlashMapManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getFlashMapTimeout","parameterTypes":[] }, {"name":"getUrlPathHelper","parameterTypes":[] }, {"name":"retrieveAndUpdate","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse"] }, {"name":"saveOutputFlashMap","parameterTypes":["org.springframework.web.servlet.FlashMap","jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse"] }, {"name":"setFlashMapTimeout","parameterTypes":["int"] }, {"name":"setUrlPathHelper","parameterTypes":["org.springframework.web.util.UrlPathHelper"] }] }, { "name":"org.springframework.web.servlet.support.RequestContext" }, { "name":"org.springframework.web.servlet.support.RequestDataValueProcessor", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllPublicMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.support.SessionFlashMapManager", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.support.WebContentGenerator", "allDeclaredFields":true, "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.servlet.theme.AbstractThemeResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getDefaultThemeName","parameterTypes":[] }, {"name":"setDefaultThemeName","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.web.servlet.theme.FixedThemeResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"resolveThemeName","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"setThemeName","parameterTypes":["jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse","java.lang.String"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.view.AbstractCachingViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"clearCache","parameterTypes":[] }, {"name":"getCacheFilter","parameterTypes":[] }, {"name":"getCacheLimit","parameterTypes":[] }, {"name":"isCache","parameterTypes":[] }, {"name":"isCacheUnresolved","parameterTypes":[] }, {"name":"removeFromCache","parameterTypes":["java.lang.String","java.util.Locale"] }, {"name":"resolveViewName","parameterTypes":["java.lang.String","java.util.Locale"] }, {"name":"setCache","parameterTypes":["boolean"] }, {"name":"setCacheFilter","parameterTypes":["org.springframework.web.servlet.view.AbstractCachingViewResolver$CacheFilter"] }, {"name":"setCacheLimit","parameterTypes":["int"] }, {"name":"setCacheUnresolved","parameterTypes":["boolean"] }] }, { "name":"org.springframework.web.servlet.view.AbstractCachingViewResolver$CacheFilter" }, { "name":"org.springframework.web.servlet.view.AbstractUrlBasedView", "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"checkResource","parameterTypes":["java.util.Locale"] }, {"name":"getUrl","parameterTypes":[] }, {"name":"setUrl","parameterTypes":["java.lang.String"] }, {"name":"toString","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.view.AbstractView", "queryAllDeclaredMethods":true, "methods":[{"name":"addStaticAttribute","parameterTypes":["java.lang.String","java.lang.Object"] }, {"name":"getAttributesMap","parameterTypes":[] }, {"name":"getBeanName","parameterTypes":[] }, {"name":"getContentType","parameterTypes":[] }, {"name":"getRequestContextAttribute","parameterTypes":[] }, {"name":"getStaticAttributes","parameterTypes":[] }, {"name":"isExposePathVariables","parameterTypes":[] }, {"name":"render","parameterTypes":["java.util.Map","jakarta.servlet.http.HttpServletRequest","jakarta.servlet.http.HttpServletResponse"] }, {"name":"setAttributes","parameterTypes":["java.util.Properties"] }, {"name":"setAttributesCSV","parameterTypes":["java.lang.String"] }, {"name":"setAttributesMap","parameterTypes":["java.util.Map"] }, {"name":"setBeanName","parameterTypes":["java.lang.String"] }, {"name":"setContentType","parameterTypes":["java.lang.String"] }, {"name":"setExposeContextBeansAsAttributes","parameterTypes":["boolean"] }, {"name":"setExposePathVariables","parameterTypes":["boolean"] }, {"name":"setExposedContextBeanNames","parameterTypes":["java.lang.String[]"] }, {"name":"setRequestContextAttribute","parameterTypes":["java.lang.String"] }] }, { "name":"org.springframework.web.servlet.view.BeanNameViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"resolveViewName","parameterTypes":["java.lang.String","java.util.Locale"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.view.ContentNegotiatingViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"resolveViewName","parameterTypes":["java.lang.String","java.util.Locale"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"getViewName","parameterTypes":["jakarta.servlet.http.HttpServletRequest"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.view.InternalResourceView", "queryAllDeclaredMethods":true }, { "name":"org.springframework.web.servlet.view.InternalResourceViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.servlet.view.UrlBasedViewResolver", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"getAttributesMap","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"getRedirectHosts","parameterTypes":[] }, {"name":"setAttributes","parameterTypes":["java.util.Properties"] }, {"name":"setAttributesMap","parameterTypes":["java.util.Map"] }, {"name":"setContentType","parameterTypes":["java.lang.String"] }, {"name":"setExposeContextBeansAsAttributes","parameterTypes":["boolean"] }, {"name":"setExposePathVariables","parameterTypes":["java.lang.Boolean"] }, {"name":"setExposedContextBeanNames","parameterTypes":["java.lang.String[]"] }, {"name":"setOrder","parameterTypes":["int"] }, {"name":"setPrefix","parameterTypes":["java.lang.String"] }, {"name":"setRedirectContextRelative","parameterTypes":["boolean"] }, {"name":"setRedirectHosts","parameterTypes":["java.lang.String[]"] }, {"name":"setRedirectHttp10Compatible","parameterTypes":["boolean"] }, {"name":"setRequestContextAttribute","parameterTypes":["java.lang.String"] }, {"name":"setSuffix","parameterTypes":["java.lang.String"] }, {"name":"setViewClass","parameterTypes":["java.lang.Class"] }, {"name":"setViewNames","parameterTypes":["java.lang.String[]"] }] }, { "name":"org.springframework.web.servlet.view.ViewResolverComposite", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "methods":[{"name":"afterPropertiesSet","parameterTypes":[] }, {"name":"close","parameterTypes":[] }, {"name":"getOrder","parameterTypes":[] }, {"name":"resolveViewName","parameterTypes":["java.lang.String","java.util.Locale"] }, {"name":"setApplicationContext","parameterTypes":["org.springframework.context.ApplicationContext"] }, {"name":"setServletContext","parameterTypes":["jakarta.servlet.ServletContext"] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer" }, { "name":"org.springframework.web.util.UriBuilderFactory" }, { "name":"org.springframework.web.util.UriComponentsBuilder" }, { "name":"org.springframework.web.util.UriTemplateHandler" }, { "name":"org.springframework.web.util.UrlPathHelper", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.util.WebUtilRuntimeHints", "queryAllDeclaredConstructors":true, "queryAllPublicConstructors":true, "methods":[{"name":"","parameterTypes":[] }] }, { "name":"org.springframework.web.util.pattern.PathPattern" }, { "name":"org.springframework.web.util.pattern.PathPatternParser", "allDeclaredFields":true, "queryAllDeclaredMethods":true, "queryAllDeclaredConstructors":true, "methods":[{"name":"close","parameterTypes":[] }, {"name":"shutdown","parameterTypes":[] }] }, { "name":"org.springframework.web.util.pattern.PatternParseException" }, { "name":"org.springframework.ws.transport.http.MessageDispatcherServlet" }, { "name":"org.thymeleaf.spring6.SpringTemplateEngine" }, { "name":"org.webjars.WebJarAssetLocator" }, { "name":"org.xnio.SslClientAuthMode" }, { "name":"org.yaml.snakeyaml.Yaml" }, { "name":"reactor.core.publisher.Flux" }, { "name":"reactor.core.publisher.Hooks" }, { "name":"reactor.core.publisher.Mono" }, { "name":"reactor.netty.http.server.HttpServer" }, { "name":"reactor.tools.agent.ReactorDebugAgent" }, { "name":"sun.management.ClassLoadingImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.CompilationImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.ManagementFactoryHelper$1", "queryAllPublicConstructors":true }, { "name":"sun.management.ManagementFactoryHelper$PlatformLoggingImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.MemoryImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.MemoryManagerImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.MemoryPoolImpl", "queryAllPublicConstructors":true }, { "name":"sun.management.RuntimeImpl", "queryAllPublicConstructors":true }, { "name":"sun.misc.SharedSecrets" }, { "name":"sun.misc.Signal" }, { "name":"sun.misc.Unsafe", "allDeclaredFields":true, "methods":[{"name":"arrayBaseOffset","parameterTypes":["java.lang.Class"] }, {"name":"arrayIndexScale","parameterTypes":["java.lang.Class"] }, {"name":"copyMemory","parameterTypes":["long","long","long"] }, {"name":"copyMemory","parameterTypes":["java.lang.Object","long","java.lang.Object","long","long"] }, {"name":"getAndAddLong","parameterTypes":["java.lang.Object","long","long"] }, {"name":"getAndSetObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] }, {"name":"getBoolean","parameterTypes":["java.lang.Object","long"] }, {"name":"getByte","parameterTypes":["long"] }, {"name":"getByte","parameterTypes":["java.lang.Object","long"] }, {"name":"getDouble","parameterTypes":["java.lang.Object","long"] }, {"name":"getFloat","parameterTypes":["java.lang.Object","long"] }, {"name":"getInt","parameterTypes":["long"] }, {"name":"getInt","parameterTypes":["java.lang.Object","long"] }, {"name":"getLong","parameterTypes":["long"] }, {"name":"getLong","parameterTypes":["java.lang.Object","long"] }, {"name":"getObject","parameterTypes":["java.lang.Object","long"] }, {"name":"invokeCleaner","parameterTypes":["java.nio.ByteBuffer"] }, {"name":"objectFieldOffset","parameterTypes":["java.lang.reflect.Field"] }, {"name":"putBoolean","parameterTypes":["java.lang.Object","long","boolean"] }, {"name":"putByte","parameterTypes":["long","byte"] }, {"name":"putByte","parameterTypes":["java.lang.Object","long","byte"] }, {"name":"putDouble","parameterTypes":["java.lang.Object","long","double"] }, {"name":"putFloat","parameterTypes":["java.lang.Object","long","float"] }, {"name":"putInt","parameterTypes":["long","int"] }, {"name":"putInt","parameterTypes":["java.lang.Object","long","int"] }, {"name":"putLong","parameterTypes":["long","long"] }, {"name":"putLong","parameterTypes":["java.lang.Object","long","long"] }, {"name":"putObject","parameterTypes":["java.lang.Object","long","java.lang.Object"] }, {"name":"storeFence","parameterTypes":[] }] }, { "name":"sun.nio.ch.SelectorImpl", "fields":[{"name":"publicSelectedKeys"}, {"name":"selectedKeys"}] }, { "name":"sun.reflect.ReflectionFactory", "methods":[{"name":"getReflectionFactory","parameterTypes":[] }, {"name":"newConstructorForSerialization","parameterTypes":["java.lang.Class","java.lang.reflect.Constructor"] }] }, { "name":"sun.rmi.registry.RegistryImpl_Stub" }, { "name":"sun.rmi.transport.DGCImpl_Skel", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.rmi.transport.DGCImpl_Stub", "methods":[{"name":"","parameterTypes":["java.rmi.server.RemoteRef"] }] }, { "name":"sun.security.pkcs12.PKCS12KeyStore", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.pkcs12.PKCS12KeyStore$DualFormatPKCS12", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.DRBG", "methods":[{"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] }, { "name":"sun.security.provider.JavaKeyStore$JKS", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.MD5", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.NativePRNG", "methods":[{"name":"","parameterTypes":[] }, {"name":"","parameterTypes":["java.security.SecureRandomParameters"] }] }, { "name":"sun.security.provider.SHA", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.SHA2$SHA256", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.SHA5$SHA512", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.provider.X509Factory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.rsa.RSAKeyFactory$Legacy", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.ssl.KeyManagerFactoryImpl$SunX509", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.ssl.SSLContextImpl$DefaultSSLContext", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.ssl.SSLContextImpl$TLSContext", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.ssl.TrustManagerFactoryImpl$PKIXFactory", "methods":[{"name":"","parameterTypes":[] }] }, { "name":"sun.security.x509.AuthorityInfoAccessExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.AuthorityKeyIdentifierExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.BasicConstraintsExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.CRLDistributionPointsExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.CertificatePoliciesExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.ExtendedKeyUsageExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.IssuerAlternativeNameExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.KeyUsageExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.NetscapeCertTypeExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.PrivateKeyUsageExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.SubjectAlternativeNameExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"sun.security.x509.SubjectKeyIdentifierExtension", "methods":[{"name":"","parameterTypes":["java.lang.Boolean","java.lang.Object"] }] }, { "name":"zipkin2.reporter.Encoding" } ] ================================================ FILE: console/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-console/resource-config.json ================================================ { "resources":{ "includes":[{ "pattern":".*\\.desc$" }, { "pattern":".*\\.dll$" }, { "pattern":".*\\.jnilib$" }, { "pattern":".*\\.proto$" }, { "pattern":".*\\.so$" }, { "pattern":"\\QMETA-INF/derby-schema.sql\\E" }, { "pattern":"\\QMETA-INF/hessian/deserializers\\E" }, { "pattern":"\\QMETA-INF/hessian/serializers\\E" }, { "pattern":"\\QMETA-INF/io.netty.versions.properties\\E" }, { "pattern":"\\QMETA-INF/logback/config-included.xml\\E" }, { "pattern":"\\QMETA-INF/logback/nacos.xml\\E" }, { "pattern":"\\QMETA-INF/logback/naming-included.xml\\E" }, { "pattern":"\\QMETA-INF/native/libio_grpc_netty_shaded_netty_transport_native_epoll_x86_64.so\\E" }, { "pattern":"\\QMETA-INF/services/ch.qos.logback.classic.spi.Configurator\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.api.naming.spi.generator.InstanceIdGenerator\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.api.remote.Payload\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.api.selector.Selector\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.api.selector.context.SelectorContextBuilder\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.common.ability.AbstractAbilityControlManager\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.common.paramcheck.AbstractParamChecker\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.common.pathencoder.PathEncoder\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.consistency.Serializer\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.core.ability.ServerAbilityInitializer\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.core.listener.NacosApplicationListener\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.core.paramcheck.AbstractRpcParamExtractor\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.core.remote.grpc.negotiator.ProtocolNegotiatorBuilder\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.naming.core.v2.client.factory.ClientFactory\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.naming.healthcheck.interceptor.AbstractHealthCheckInterceptor\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.naming.push.v2.hook.PushResultHook\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.plugin.auth.spi.server.AuthPluginService\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.plugin.control.configs.ControlConfigsInitializer\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.plugin.control.connection.ConnectionMetricsCollector\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.plugin.datasource.mapper.Mapper\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter\\E" }, { "pattern":"\\QMETA-INF/services/com.alibaba.nacos.sys.module.ModuleStateBuilder\\E" }, { "pattern":"\\QMETA-INF/services/com.alipay.sofa.jraft.JRaftServiceFactory\\E" }, { "pattern":"\\QMETA-INF/services/com.alipay.sofa.jraft.rpc.RaftRpcFactory\\E" }, { "pattern":"\\QMETA-INF/services/com.alipay.sofa.jraft.util.JRaftSignalHandler\\E" }, { "pattern":"\\QMETA-INF/services/com.alipay.sofa.jraft.util.timer.RaftTimerFactory\\E" }, { "pattern":"\\QMETA-INF/services/io.grpc.LoadBalancerProvider\\E" }, { "pattern":"\\QMETA-INF/services/io.grpc.ManagedChannelProvider\\E" }, { "pattern":"\\QMETA-INF/services/io.grpc.NameResolverProvider\\E" }, { "pattern":"\\QMETA-INF/services/io.grpc.ServerProvider\\E" }, { "pattern":"\\QMETA-INF/services/java.sql.Driver\\E" }, { "pattern":"\\QMETA-INF/services/org.slf4j.spi.SLF4JServiceProvider\\E" }, { "pattern":"\\QMETA-INF/spring-autoconfigure-metadata.properties\\E" }, { "pattern":"\\QMETA-INF/spring.factories\\E" }, { "pattern":"\\QMETA-INF/spring/aot.factories\\E" }, { "pattern":"\\QMETA-INF/spring/org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration.imports\\E" }, { "pattern":"\\QMETA-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports\\E" }, { "pattern":"\\Q\\E" }, { "pattern":"\\Qapplication.properties\\E" }, { "pattern":"\\Qbanner.txt\\E" }, { "pattern":"\\Qcom/alibaba/nacos/Nacos$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/Nacos.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/\\E" }, { "pattern":"\\Qcom/alibaba/nacos/api/annotation/NacosApi.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/api/grpc/auto/BiRequestStreamGrpc$BiRequestStreamImplBase.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/api/grpc/auto/RequestGrpc$RequestImplBase.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/auth/config/AuthConfigs$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/auth/config/AuthConfigs.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/controllers/OperationController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/core/SwitchAndOptions.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/memory/CmdbProvider$CmdbDumpTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/memory/CmdbProvider$CmdbEventTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/memory/CmdbProvider$CmdbLabelTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/memory/CmdbProvider.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/service/CmdbReader.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/cmdb/service/CmdbWriter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/common/notify/listener/SmartSubscriber.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/common/notify/listener/Subscriber.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/common/task/AbstractExecuteTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/common/task/NacosTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/aspect/ConfigChangeAspect.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/aspect/ConfigOpFailureAspect.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/aspect/RequestLogAspect.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/configuration/ConfigChangeConfigs$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/configuration/ConfigChangeConfigs.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/configuration/NacosConfigConfiguration$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/configuration/NacosConfigConfiguration.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/CapacityController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/ClientMetricsController$ClusterMetricsCallBack.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/ClientMetricsController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/CommunicationController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/ConfigController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/ConfigOpsController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/ConfigServletInner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/HealthController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/HistoryController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/ListenerController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/v2/ConfigControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/controller/v2/HistoryControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/exception/GlobalExceptionHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/filter/NacosWebFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/monitor/ConfigDynamicMeterRefreshService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/monitor/MemoryMonitor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/monitor/collector/ConfigSubscriberMetricsCollector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigChangeBatchListenRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigChangeClusterSyncRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigChangeListenContext.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigClusterRpcClientProxy.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigConnectionEventListener.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigPublishRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigQueryRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/ConfigRemoveRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier$RpcPushCallback.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier$RpcPushTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/remote/RpcConfigChangeNotifier.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ClientIpWhiteList.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigDetailService$SearchEvent.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigDetailService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigOperationService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigReadinessCheckService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigSubService$ClusterCheckHasListenerJob.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigSubService$ClusterJob.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigSubService$ClusterListenerByIpJob.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigSubService$ClusterListenerJob.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/ConfigSubService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/HistoryService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/LongPollingService$ClientLongPolling.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/LongPollingService$DataChangeTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/LongPollingService$StatTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/LongPollingService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/NamespaceConfigInfoService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/SwitchService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/capacity/CapacityService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistService$GroupCapacityRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/capacity/GroupCapacityPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistService$TenantCapacityRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/capacity/TenantCapacityPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/dump/DumpService$ConfigHistoryClear.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/dump/DumpService$DumpAllBetaProcessorRunner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/dump/DumpService$DumpAllProcessorRunner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/dump/DumpService$DumpAllTagProcessorRunner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/dump/DumpService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/dump/EmbeddedDumpService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/dump/ExternalDumpService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/merge/MergeDatumService$MergeAllDataWorker.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/merge/MergeDatumService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/notify/AsyncNotifyService$AsyncRpcNotifyCallBack.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/notify/AsyncNotifyService$AsyncRpcTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/notify/AsyncNotifyService$NotifySingleRpcTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/notify/AsyncNotifyService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigInfoAggrPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigInfoBetaPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigInfoPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigInfoTagPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigAdvanceInfoRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigAllInfoRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigHistoryDetailRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigHistoryRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfo4BetaRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfo4TagRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoAggrRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoBaseRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoBetaWrapperRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoChangedRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoStateWrapperRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoTagWrapperRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigInfoWrapperRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector$ConfigKeyRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/ConfigRowMapperInjector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/HistoryConfigInfoPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigDumpApplyHook.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoAggrPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoBetaPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/embedded/EmbeddedConfigInfoTagPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/embedded/EmbeddedHistoryConfigInfoPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoAggrPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoBetaPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/extrnal/ExternalConfigInfoTagPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/repository/extrnal/ExternalHistoryConfigInfoPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/config/server/service/trace/ConfigTraceService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/consistency/CommandOperations.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/consistency/Config.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/consistency/ConsistencyProtocol.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/consistency/RequestProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/consistency/cp/CPProtocol.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/consistency/cp/RequestProcessor4CP.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/aot/BeanProcessorHints.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/config/ConsoleConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/controller/HealthController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/controller/NamespaceController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/controller/ServerStateController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/controller/\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/controller/v2/HealthControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/controller/v2/NamespaceControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/exception/ConsoleExceptionHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/exception/NacosApiExceptionHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/console/filter/XssFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/ability/config/AbilityConfigs$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/ability/config/AbilityConfigs.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/auth/AuthConfig$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/auth/AuthConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/auth/AuthFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/auth/RemoteRequestAuthFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/cluster/MemberChangeListener.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/cluster/ServerMemberManager$MemberInfoReportTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/cluster/ServerMemberManager$UnhealthyMemberInfoReportTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/cluster/ServerMemberManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/cluster/health/AbstractModuleHealthChecker.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/cluster/remote/ClusterRpcClientProxy.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/cluster/remote/MemberReportHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/code/ControllerMethodsCache.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/context/remote/HttpRequestContextConfig$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/context/remote/HttpRequestContextConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/context/remote/HttpRequestContextFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/control/http/HttpTpsPointRegistry.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/control/http/NacosHttpTpsControlRegistration$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/control/http/NacosHttpTpsControlRegistration.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/control/http/NacosHttpTpsFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/control/remote/TpsControlRequestFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/controller/CoreOpsController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/controller/NacosClusterController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/controller/ServerLoaderController$ServerLoaderMetrics.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/controller/ServerLoaderController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/controller/\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/controller/v2/CoreOpsV2Controller.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/controller/v2/NacosClusterControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/AbstractConsistencyProtocol.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/ConsistencyConfiguration$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/ConsistencyConfiguration.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/ProtocolManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/distro/DistroProtocol.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/distro/component/DistroComponentHolder.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/distro/task/DistroTaskEngineHolder.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/id/IdGeneratorManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/raft/JRaftProtocol.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/distributed/raft/RaftConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/monitor/GrpcServerThreadPoolMonitor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/namespace/injector/AbstractNamespaceDetailInjector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/namespace/repository/EmbeddedNamespacePersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/namespace/repository/ExternalNamespacePersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/namespace/repository/NamespacePersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/namespace/repository/NamespaceRowMapperInjector$TenantInfoRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/namespace/repository/NamespaceRowMapperInjector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/paramcheck/CheckConfiguration$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/paramcheck/CheckConfiguration.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/paramcheck/ExtractorManager$Extractor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/paramcheck/ParamCheckerFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/AbstractRequestFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/BaseRpcServer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/ClientConnectionEventListener.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/ClientConnectionEventListenerRegistry.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/ConnectionManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/HealthCheckRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/RequestFilters.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/RequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/RequestHandlerRegistry.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/RpcPushService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/core/RpcAckCallbackInitorOrCleaner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/core/ServerLoaderInfoRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/core/ServerReloaderRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/grpc/BaseGrpcServer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/grpc/GrpcBiStreamRequestAcceptor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/grpc/GrpcClusterServer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/grpc/GrpcRequestAcceptor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/grpc/GrpcSdkServer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/remote/grpc/RemoteParamCheckFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/service/NacosClusterOperationService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/core/service/NamespaceOperationService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/cluster/NamingReadinessCheckService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/cluster/ServerStatusManager$ServerStatusUpdater.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/cluster/ServerStatusManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/cluster/transport/JacksonSerializer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/cluster/transport/Serializer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/consistency/ConsistencyService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/consistency/RecordListener.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/consistency/ephemeral/distro/v2/DistroClientComponentRegistry.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/consistency/persistent/PersistentConsistencyServiceDelegateImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/CatalogController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/ClusterController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/HealthController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/InstanceController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/OperatorController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/ServiceController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/v2/CatalogControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/v2/ClientInfoControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/v2/HealthControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/v2/InstanceControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/v2/OperatorControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/controllers/v2/ServiceControllerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/CatalogService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/CatalogServiceV2Impl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/ClusterOperator.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/ClusterOperatorV2Impl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/DistroMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/HealthOperator.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/HealthOperatorV2Impl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/InstanceOperator.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/InstanceOperatorClientImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/ServiceOperator.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/ServiceOperatorV2Impl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/SubscribeManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/cleaner/AbstractNamingCleaner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/cleaner/EmptyServiceAutoCleanerV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleaner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/cleaner/NamingCleaner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/client/manager/ClientManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/client/manager/ClientManagerDelegate.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManager$ExpiredClientCleaner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/client/manager/impl/ConnectionBasedClientManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManager$ExpiredClientCleaner.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/client/manager/impl/EphemeralIpPortClientManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/client/manager/impl/PersistentIpPortClientManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/index/ClientServiceIndexesManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/index/ServiceStorage.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/metadata/InstanceMetadataProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/metadata/NamingMetadataManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/metadata/NamingMetadataOperateService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/metadata/ServiceMetadataProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/service/ClientOperationService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/service/ClientOperationServiceProxy.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/service/impl/EphemeralClientOperationServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl$InstanceStoreRequest.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl$PersistentInstanceSnapshotOperation.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/core/v2/service/impl/PersistentClientOperationServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/exception/ResponseExceptionHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/extend/AbstractHealthCheckProcessorExtend.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/extend/HealthCheckExtendProvider.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/extend/HealthCheckProcessorExtendV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/HealthStatusSynchronizer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/PersistentHealthStatusSynchronizer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckCommonV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckProcessorV2.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/HealthCheckProcessorV2Delegate.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/HttpHealthCheckProcessor$HttpHealthCheckCallback.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/HttpHealthCheckProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/MysqlHealthCheckProcessor$MysqlCheckTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/MysqlHealthCheckProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/NoneHealthCheckProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/TcpHealthCheckProcessor$Beat.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/TcpHealthCheckProcessor$BeatKey.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/TcpHealthCheckProcessor$PostProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/TcpHealthCheckProcessor$TaskProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/TcpHealthCheckProcessor$TimeOutTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/healthcheck/v2/processor/TcpHealthCheckProcessor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/GlobalConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/NamingTraceEventInitializer.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/SwitchDomain$HealthParams.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/SwitchDomain$HttpHealthParams.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/SwitchDomain$MysqlHealthParams.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/SwitchDomain$TcpHealthParams.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/SwitchDomain.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/misc/SwitchManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/monitor/NamingDynamicMeterRefreshService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/monitor/PerformanceLoggerThread$PerformanceLogTask.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/monitor/PerformanceLoggerThread.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/monitor/collector/NamingSubAndPubMetricsCollector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/monitor/collector/PushPendingTaskCountMetricsCollector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/monitor/collector/ServiceEventQueueSizeMetricsCollector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/pojo/Record.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/NamingSubscriberService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/NamingSubscriberServiceAggregationImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/NamingSubscriberServiceLocalImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/UdpPushService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/v2/NamingSubscriberServiceV2Impl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/v2/executor/PushExecutor.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/v2/executor/PushExecutorDelegate.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/v2/executor/PushExecutorRpcImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/push/v2/executor/PushExecutorUdpImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/rpc/handler/BatchInstanceRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/rpc/handler/DistroDataRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/rpc/handler/InstanceRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/rpc/handler/PersistentInstanceRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/rpc/handler/ServiceListRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/rpc/handler/ServiceQueryRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/rpc/handler/SubscribeServiceRequestHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/udp/UdpConnector$UdpAsyncSender.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/udp/UdpConnector$UdpReceiver.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/udp/UdpConnector$UdpRetrySender.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/remote/udp/UdpConnector.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/selector/SelectorManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/ClientAttributesFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/DistroFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/DistroTagGenerator.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/DistroTagGeneratorImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/NamingConfig$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/NamingConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/ServiceNameFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/naming/web/TrafficReviseFilter.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/persistence/repository/embedded/hook/EmbeddedApplyHook.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/persistence/repository/embedded/operate/BaseDatabaseOperate.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/persistence/repository/embedded/operate/DatabaseOperate.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/persistence/repository/embedded/operate/StandaloneDatabaseOperateImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/CustomAuthenticationProvider.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/JwtAuthenticationEntryPoint.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/LdapAuthConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/NacosAuthConfig$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/NacosAuthConfig$1.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/NacosAuthConfig.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/NacosAuthManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/NacosLdapContextSource.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/authenticate/AbstractAuthenticationManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/authenticate/AuthenticationManagerDelegator.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/authenticate/DefaultAuthenticationManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/authenticate/IAuthenticationManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/controller/PermissionController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/controller/RoleController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/controller/UserController.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/controller/\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedPermissionPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedRolePersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/EmbeddedUserPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/ExternalPermissionPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImpl$RoleInfoRowMapper.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/ExternalRolePersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/ExternalUserPersistServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/PermissionPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/RolePersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/persistence/UserPersistService.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/roles/NacosRoleServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/token/TokenManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/token/TokenManagerDelegate.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/token/impl/CachedJwtTokenManager$TokenEntity.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/token/impl/CachedJwtTokenManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/token/impl/JwtTokenManager.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/plugin/auth/impl/users/NacosUserDetailsServiceImpl.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/prometheus/conf/PrometheusSecurityConfiguration$$SpringCGLIB$$0.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/prometheus/conf/PrometheusSecurityConfiguration.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/prometheus/exception/PrometheusApiExceptionHandler.class\\E" }, { "pattern":"\\Qcom/alibaba/nacos/prometheus/filter/PrometheusAuthFilter.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/core/ObjectCodec.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/core/TreeCodec.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/databind/JsonSerializer.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/databind/Module.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/databind/ObjectMapper.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/databind/module/SimpleModule.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/databind/ser/std/StdSerializer.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/databind/ser/std/ToStringSerializer.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/databind/ser/std/ToStringSerializerBase.class\\E" }, { "pattern":"\\Qcom/fasterxml/jackson/module/paramnames/ParameterNamesModule.class\\E" }, { "pattern":"\\Qcom/google/gson/Gson.class\\E" }, { "pattern":"\\Qcom/google/gson/GsonBuilder.class\\E" }, { "pattern":"\\Qcom/mysql/cj/TlsSettings.properties\\E" }, { "pattern":"\\Qcom/sun/jmx/mbeanserver/JmxMBeanServer.class\\E" }, { "pattern":"\\Qcom/zaxxer/hikari/HikariConfig.class\\E" }, { "pattern":"\\Qcom/zaxxer/hikari/HikariDataSource.class\\E" }, { "pattern":"\\Qio/grpc/BindableService.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/Clock$1.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/Clock.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/MeterRegistry.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/jvm/ClassLoaderMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/jvm/JvmCompilationMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/jvm/JvmGcMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/jvm/JvmHeapPressureMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/jvm/JvmInfoMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/jvm/JvmMemoryMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/jvm/JvmThreadMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/logging/LogbackMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/system/FileDescriptorMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/system/ProcessorMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/binder/system/UptimeMetrics.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/config/MeterFilter$9.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/config/MeterFilter.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/config/MeterRegistryConfig.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/observation/DefaultMeterObservationHandler.class\\E" }, { "pattern":"\\Qio/micrometer/core/instrument/observation/MeterObservationHandler.class\\E" }, { "pattern":"\\Qio/micrometer/observation/ObservationHandler.class\\E" }, { "pattern":"\\Qio/micrometer/observation/ObservationRegistry.class\\E" }, { "pattern":"\\Qio/micrometer/observation/SimpleObservationRegistry.class\\E" }, { "pattern":"\\Qio/micrometer/observation/annotation/Observed.class\\E" }, { "pattern":"\\Qio/micrometer/observation/aop/ObservedAspect.class\\E" }, { "pattern":"\\Qio/micrometer/prometheus/PrometheusConfig.class\\E" }, { "pattern":"\\Qio/micrometer/prometheus/PrometheusMeterRegistry.class\\E" }, { "pattern":"\\Qio/prometheus/client/CollectorRegistry.class\\E" }, { "pattern":"\\Qjakarta/servlet/Filter.class\\E" }, { "pattern":"\\Qjakarta/servlet/GenericServlet.class\\E" }, { "pattern":"\\Qjakarta/servlet/MultipartConfigElement.class\\E" }, { "pattern":"\\Qjakarta/servlet/http/HttpServlet.class\\E" }, { "pattern":"\\Qjava/lang/Iterable.class\\E" }, { "pattern":"\\Qjava/lang/Object.class\\E" }, { "pattern":"\\Qjava/util/function/BiPredicate.class\\E" }, { "pattern":"\\Qjavax/sql/CommonDataSource.class\\E" }, { "pattern":"\\Qjavax/sql/DataSource.class\\E" }, { "pattern":"\\Qlibrocksdbjni-linux64.so\\E" }, { "pattern":"\\Qlibrocksdbjni-osx-arm64.jnilib\\E" }, { "pattern":"\\Qlibrocksdbjni-win64.dll\\E" }, { "pattern":"\\Qmozilla/public-suffix-list.txt\\E" }, { "pattern":"\\Qnacos-version.txt\\E" }, { "pattern":"\\Qorg/aopalliance/intercept/MethodInterceptor.class\\E" }, { "pattern":"\\Qorg/apache/catalina/core/RestrictedFilters.properties\\E" }, { "pattern":"\\Qorg/apache/catalina/core/RestrictedListeners.properties\\E" }, { "pattern":"\\Qorg/apache/catalina/core/RestrictedServlets.properties\\E" }, { "pattern":"\\Qorg/apache/catalina/loader/JdbcLeakPrevention.class\\E" }, { "pattern":"\\Qorg/apache/catalina/util/CharsetMapperDefault.properties\\E" }, { "pattern":"\\Qorg/apache/catalina/util/ServerInfo.properties\\E" }, { "pattern":"\\Qorg/apache/derby/impl/jdbc/metadata.properties\\E" }, { "pattern":"\\Qorg/apache/derby/impl/sql/catalog/metadata_net.properties\\E" }, { "pattern":"\\Qorg/apache/derby/info/DBMS.properties\\E" }, { "pattern":"\\Qorg/apache/derby/modules.properties\\E" }, { "pattern":"\\Qorg/apache/hc/client5/version.properties\\E" }, { "pattern":"\\Qorg/aspectj/lang/annotation/Aspect.class\\E" }, { "pattern":"\\Qorg/springframework/aop/Advisor.class\\E" }, { "pattern":"\\Qorg/springframework/aop/aspectj/annotation/AnnotationAwareAspectJAutoProxyCreator.class\\E" }, { "pattern":"\\Qorg/springframework/aop/aspectj/autoproxy/AspectJAwareAdvisorAutoProxyCreator.class\\E" }, { "pattern":"\\Qorg/springframework/aop/framework/AbstractAdvisingBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/aop/framework/AopInfrastructureBean.class\\E" }, { "pattern":"\\Qorg/springframework/aop/framework/ProxyConfig.class\\E" }, { "pattern":"\\Qorg/springframework/aop/framework/ProxyProcessorSupport.class\\E" }, { "pattern":"\\Qorg/springframework/aop/framework/autoproxy/AbstractAdvisorAutoProxyCreator.class\\E" }, { "pattern":"\\Qorg/springframework/aop/framework/autoproxy/AbstractAutoProxyCreator.class\\E" }, { "pattern":"\\Qorg/springframework/aop/framework/autoproxy/AbstractBeanFactoryAwareAdvisingPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/aop/support/AbstractBeanFactoryPointcutAdvisor.class\\E" }, { "pattern":"\\Qorg/springframework/aop/support/AbstractPointcutAdvisor.class\\E" }, { "pattern":"\\Qorg/springframework/aot/hint/annotation/Reflective.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/Aware.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/BeanClassLoaderAware.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/BeanFactoryAware.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/DisposableBean.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/FactoryBean.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/InitializingBean.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/SmartInitializingSingleton.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/annotation/InitDestroyAnnotationBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/aot/BeanRegistrationAotProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/config/BeanFactoryPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/config/BeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/config/InstantiationAwareBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/config/PlaceholderConfigurerSupport.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/config/PropertyResourceConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/config/SmartInstantiationAwareBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/support/MergedBeanDefinitionPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/beans/factory/support/NullBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/LazyInitializationExcludeFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/audit/AuditAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/audit/AuditEventsEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/availability/AvailabilityHealthContributorAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/availability/AvailabilityProbesAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/beans/BeansEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/cache/CachesEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/cloudfoundry/servlet/CloudFoundryActuatorAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/condition/ConditionsReportEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/context/ShutdownEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/context/properties/ConfigurationPropertiesReportEndpointProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/EndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/condition/ConditionalOnAvailableEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/expose/IncludeExcludeEndpointFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/jackson/JacksonEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/jmx/DefaultEndpointObjectNameFactory.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/jmx/JmxEndpointProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/CorsEndpointProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/MappingWebEndpointPathMapper.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration$JerseyServletEndpointManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration$WebMvcServletEndpointManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/ServletEndpointManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration$WebEndpointServletConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/WebEndpointProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/jersey/JerseyWebEndpointManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/reactive/WebFluxEndpointManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration$EndpointObjectMapperWebMvcConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/endpoint/web/servlet/WebMvcEndpointManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/env/EnvironmentEndpointProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/AbstractCompositeHealthContributorConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthContributorRegistry.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/AutoConfiguredHealthEndpointGroups.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/CompositeHealthContributorConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/ConditionalOnEnabledHealthIndicator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthContributorAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration$AdaptedReactiveHealthContributors.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration$HealthEndpointGroupMembershipValidator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration$HealthEndpointGroupsBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointReactiveWebExtensionConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration$JerseyAdditionalHealthEndpointPathsConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration$JerseyAdditionalHealthEndpointPathsResourcesRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration$MvcAdditionalHealthEndpointPathsConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthEndpointWebExtensionConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/HealthProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/health/ReactiveHealthEndpointConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/info/InfoContributorAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/info/InfoContributorProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/info/InfoEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration$RoutingDataSourceHealthContributor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthContributorAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/jdbc/DataSourceHealthIndicatorProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/ldap/LdapHealthContributorAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointAutoConfiguration$LogFileCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/logging/LogFileWebEndpointProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/logging/LoggersEndpointAutoConfiguration$OnEnabledLoggingSystemCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/logging/LoggersEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/management/HeapDumpWebEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/management/ThreadDumpEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/CompositeMeterRegistryAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/CompositeMeterRegistryConfiguration$MultipleNonPrimaryMeterRegistriesCondition$NoMeterRegistryCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/CompositeMeterRegistryConfiguration$MultipleNonPrimaryMeterRegistriesCondition$SingleInjectableMeterRegistry.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/CompositeMeterRegistryConfiguration$MultipleNonPrimaryMeterRegistriesCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/CompositeMeterRegistryConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/JvmMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfiguration$LogbackLoggingCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/LogbackMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MeterRegistryPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration$ObservationAnnotationsEnabledCondition$ManagementObservationsEnabledCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration$ObservationAnnotationsEnabledCondition$MicrometerObservationsEnabledCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration$ObservationAnnotationsEnabledCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsAspectsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration$MeterRegistryCloser.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/MetricsProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/NoOpMeterRegistryConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/PropertiesMeterFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/SystemMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration$Cache2kCacheMeterBinderProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration$CaffeineCacheMeterBinderProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration$HazelcastCacheMeterBinderProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration$JCacheCacheMeterBinderProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration$RedisCacheMeterBinderProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMeterBinderProvidersConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/cache/CacheMetricsRegistrarConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/ConditionalOnEnabledMetricsExport.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/elastic/ElasticMetricsExportAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/influx/InfluxMetricsExportAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusSimpleclientMetricsExportAutoConfiguration$PrometheusPushGatewayConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusSimpleclientMetricsExportAutoConfiguration$PrometheusScrapeEndpointConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusSimpleclientMetricsExportAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/prometheus/PrometheusSimpleclientPropertiesConfigAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/properties/PropertiesConfigAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/export/simple/SimpleMetricsExportAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/integration/IntegrationMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration$DataSourcePoolMetadataMetricsConfiguration$DataSourcePoolMetadataMeterBinder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration$DataSourcePoolMetadataMetricsConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration$HikariDataSourceMetricsConfiguration$HikariDataSourceMeterBinder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration$HikariDataSourceMetricsConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/jdbc/DataSourcePoolMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/startup/StartupTimeMetricsListenerAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/task/TaskExecutorMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/metrics/web/tomcat/TomcatMetricsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration$MeterObservationHandlerConfiguration$OnlyMetricsMeterObservationHandlerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration$MeterObservationHandlerConfiguration$TracingAndMetricsObservationHandlerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration$MeterObservationHandlerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration$MetricsWithTracingConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration$ObservedAspectConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration$OnlyMetricsConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration$OnlyTracingConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationHandlerGrouping.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/ObservationRegistryPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/PropertiesObservationFilterPredicate.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/web/client/HttpClientObservationsAutoConfiguration$MeterFilterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/web/client/HttpClientObservationsAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/web/client/RestClientObservationConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/web/client/RestTemplateObservationConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/web/client/WebClientObservationConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfiguration$MeterFilterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/observation/web/servlet/WebMvcObservationAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/sbom/SbomEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksObservabilityAutoConfiguration$ObservabilitySchedulingConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/scheduling/ScheduledTasksObservabilityAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/security/servlet/SecurityRequestMatchersManagementContextConfiguration$JerseyRequestMatcherConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/security/servlet/SecurityRequestMatchersManagementContextConfiguration$MvcRequestMatcherConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/security/servlet/SecurityRequestMatchersManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/startup/StartupEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/system/DiskSpaceHealthContributorAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/system/DiskSpaceHealthIndicatorProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/ManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/ManagementContextFactory.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/exchanges/HttpExchangesAutoConfiguration$ReactiveHttpExchangesConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/exchanges/HttpExchangesAutoConfiguration$ServletHttpExchangesConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/exchanges/HttpExchangesAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/exchanges/HttpExchangesEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/jersey/JerseyChildManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/jersey/JerseySameManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/mappings/MappingsEndpointAutoConfiguration$ReactiveWebConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/mappings/MappingsEndpointAutoConfiguration$ServletWebConfiguration$SpringMvcConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/mappings/MappingsEndpointAutoConfiguration$ServletWebConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/mappings/MappingsEndpointAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/reactive/ReactiveManagementChildContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/ConditionalOnManagementPort.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/EnableManagementContext.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration$DifferentManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration$SameManagementContextConfiguration$EnableSameManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration$SameManagementContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/ManagementContextConfigurationImportSelector.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/server/ManagementServerProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/servlet/ManagementServletContext.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementChildContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementContextAutoConfiguration$ApplicationContextFilterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/servlet/ServletManagementContextAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/autoconfigure/web/servlet/WebMvcEndpointChildContextConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/beans/BeansEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/cache/CachesEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/cache/CachesEndpointWebExtension.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/context/properties/ConfigurationPropertiesReportEndpointWebExtension.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/annotation/EndpointDiscoverer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/invoke/ParameterValueMapper.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/invoke/convert/ConversionServiceParameterValueMapper.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/invoker/cache/CachingOperationInvokerAdvisor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/jackson/EndpointObjectMapper.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/jmx/JmxEndpointExporter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/jmx/annotation/JmxEndpointDiscoverer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/EndpointMediaTypes.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/PathMappedEndpoints.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/PathMapper.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/ServletEndpointRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/annotation/ControllerEndpointDiscoverer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/annotation/ServletEndpointDiscoverer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/annotation/WebEndpointDiscoverer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/servlet/AbstractWebMvcEndpointHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/servlet/AdditionalHealthEndpointPathsWebMvcHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/servlet/ControllerEndpointHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/endpoint/web/servlet/WebMvcEndpointHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/env/EnvironmentEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/env/EnvironmentEndpointWebExtension.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/AbstractHealthIndicator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/ContributorRegistry.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/DefaultContributorRegistry.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/DefaultHealthContributorRegistry.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HealthContributor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HealthContributorRegistry.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HealthEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HealthEndpointGroups.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HealthEndpointSupport.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HealthEndpointWebExtension.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HealthIndicator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/HttpCodeStatusMapper.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/NamedContributors.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/PingHealthIndicator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/SimpleHttpCodeStatusMapper.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/SimpleStatusAggregator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/health/StatusAggregator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/info/InfoEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/jdbc/DataSourceHealthIndicator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/logging/LoggersEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/management/HeapDumpWebEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/management/ThreadDumpEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/metrics/MetricsEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/metrics/export/prometheus/PrometheusSimpleclientScrapeEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/metrics/startup/StartupTimeMetricsListener.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/metrics/system/DiskSpaceMetricsBinder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/metrics/web/client/ObservationRestClientCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/metrics/web/client/ObservationRestTemplateCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/metrics/web/tomcat/TomcatMetricsBinder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/sbom/SbomEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/sbom/SbomEndpointWebExtension.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/sbom/SbomProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/scheduling/ScheduledTasksEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/system/DiskSpaceHealthIndicator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/web/mappings/MappingsEndpoint.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/web/mappings/servlet/DispatcherServletsMappingDescriptionProvider.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/web/mappings/servlet/FiltersMappingDescriptionProvider.class\\E" }, { "pattern":"\\Qorg/springframework/boot/actuate/web/mappings/servlet/ServletsMappingDescriptionProvider.class\\E" }, { "pattern":"\\Qorg/springframework/boot/admin/SpringApplicationAdminMXBeanRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AbstractDependsOnBeanFactoryPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfigurationImportSelector.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfigurationPackage.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfigurationPackages$BasePackages.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfigurationPackages$Registrar.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfigureAfter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfigureBefore.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/AutoConfigureOrder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/EnableAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/admin/SpringApplicationAdminJmxAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/aop/AopAutoConfiguration$AspectJAutoProxyingConfiguration$CglibAutoProxyConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/aop/AopAutoConfiguration$AspectJAutoProxyingConfiguration$JdkDynamicAutoProxyConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/aop/AopAutoConfiguration$AspectJAutoProxyingConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/aop/AopAutoConfiguration$ClassProxyingConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/aop/AopAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/availability/ApplicationAvailabilityAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/cache/CacheAutoConfiguration$CacheConfigurationImportSelector.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/cache/CacheAutoConfiguration$CacheManagerEntityManagerFactoryDependsOnPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/cache/CacheAutoConfiguration$CacheManagerValidator.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/cache/CacheAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/cache/GenericCacheConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/cache/NoOpCacheConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/cache/SimpleCacheConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnClass.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnMissingBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnMissingClass.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnNotWarDeployment.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnProperty.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnSingleCandidate.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/condition/ConditionalOnWebApplication.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/context/ConfigurationPropertiesAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/context/LifecycleAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/context/LifecycleProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/context/MessageSourceAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/context/PropertyPlaceholderAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/dao/PersistenceExceptionTranslationAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/gson/GsonAutoConfiguration$StandardGsonBuilderCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/gson/GsonAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/gson/GsonProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration$GsonHttpMessageConverterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration$JacksonAndJsonbUnavailableCondition$JacksonAvailable.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration$JacksonAndJsonbUnavailableCondition$JsonbPreferred.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration$JacksonAndJsonbUnavailableCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration$PreferGsonOrJacksonAndJsonbUnavailableCondition$GsonPreferred.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration$PreferGsonOrJacksonAndJsonbUnavailableCondition$JacksonJsonbUnavailable.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration$PreferGsonOrJacksonAndJsonbUnavailableCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/GsonHttpMessageConvertersConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/HttpMessageConverters.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/HttpMessageConvertersAutoConfiguration$HttpMessageConvertersAutoConfigurationRuntimeHints.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/HttpMessageConvertersAutoConfiguration$NotReactiveWebApplicationCondition$ReactiveWebApplication.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/HttpMessageConvertersAutoConfiguration$NotReactiveWebApplicationCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/HttpMessageConvertersAutoConfiguration$StringHttpMessageConverterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/HttpMessageConvertersAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/JacksonHttpMessageConvertersConfiguration$MappingJackson2HttpMessageConverterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/JacksonHttpMessageConvertersConfiguration$MappingJackson2XmlHttpMessageConverterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/JacksonHttpMessageConvertersConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/http/JsonbHttpMessageConvertersConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration$GitResourceAvailableCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/info/ProjectInfoAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/info/ProjectInfoProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/Jackson2ObjectMapperBuilderCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration$StandardJackson2ObjectMapperBuilderCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$Jackson2ObjectMapperBuilderCustomizerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$JacksonAutoConfigurationRuntimeHints.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$JacksonMixinConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$JacksonObjectMapperBuilderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$JacksonObjectMapperConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration$ParameterNamesModuleConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jackson/JacksonProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$EmbeddedDatabaseCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$EmbeddedDatabaseConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$PooledDataSourceAvailableCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$PooledDataSourceCondition$ExplicitType.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$PooledDataSourceCondition$PooledDataSourceAvailable.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$PooledDataSourceCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration$PooledDataSourceConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceCheckpointRestoreConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Dbcp2.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Generic.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$OracleUcp.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Tomcat.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfiguration$Hikari.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfiguration$TomcatDataSourceJmxConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceJmxConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration$JdbcTransactionManagerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/DataSourceTransactionManagerAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/HikariJdbcConnectionDetailsBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/JdbcClientAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/JdbcConnectionDetailsBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/JdbcProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/JdbcTemplateAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/JdbcTemplateConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/NamedParameterJdbcTemplateConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/PropertiesJdbcConnectionDetails.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration$CommonsDbcp2PoolDataSourceMetadataProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration$HikariPoolDataSourceMetadataProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration$OracleUcpPoolDataSourceMetadataProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration$TomcatDataSourcePoolMetadataProviderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jdbc/metadata/DataSourcePoolMetadataProvidersConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jmx/JmxProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/jmx/ParentAwareNamingStrategy.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/netty/NettyAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/netty/NettyProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/orm/jpa/EntityManagerFactoryDependsOnPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/ConditionalOnDefaultWebSecurity.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/DefaultWebSecurityCondition$Beans.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/DefaultWebSecurityCondition$Classes.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/DefaultWebSecurityCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/SecurityDataConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/SecurityProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured$MissingAlternative.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured$NameConfigured.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured$PasswordConfigured.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration$RSocketEnabledOrReactiveWebApplication$RSocketSecurityEnabledCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration$RSocketEnabledOrReactiveWebApplication$ReactiveWebApplicationCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration$RSocketEnabledOrReactiveWebApplication.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/reactive/ReactiveUserDetailsServiceAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/AntPathRequestMatcherProvider.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/RequestMatcherProvider.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/SecurityAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/SecurityFilterAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/SpringBootWebSecurityConfiguration$SecurityFilterChainConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/SpringBootWebSecurityConfiguration$WebSecurityEnablerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/SpringBootWebSecurityConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured$MissingAlternative.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured$NameConfigured.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured$PasswordConfigured.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration$MissingAlternativeOrUserPropertiesConfigured.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/security/servlet/UserDetailsServiceAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/sql/init/R2dbcInitializationConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/sql/init/SqlDataSourceScriptDatabaseInitializer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration$SqlInitializationModeCondition$ModeIsNever.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration$SqlInitializationModeCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/sql/init/SqlInitializationAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/sql/init/SqlInitializationProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/ssl/FileWatcher.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/ssl/SslAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/ssl/SslProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/ssl/SslPropertiesBundleRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/ScheduledBeanLazyInitializationExcludeFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskExecutionProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskExecutorConfigurations$SimpleAsyncTaskExecutorBuilderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskExecutorConfigurations$TaskExecutorBuilderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskExecutorConfigurations$TaskExecutorConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskExecutorConfigurations$ThreadPoolTaskExecutorBuilderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskSchedulingAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations$SimpleAsyncTaskSchedulerBuilderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations$TaskSchedulerBuilderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations$TaskSchedulerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskSchedulingConfigurations$ThreadPoolTaskSchedulerBuilderConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/task/TaskSchedulingProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/ExecutionListenersTransactionManagerCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration$AspectJTransactionManagementConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration$EnableTransactionManagementConfiguration$CglibAutoProxyConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration$EnableTransactionManagementConfiguration$JdkDynamicAutoProxyConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration$EnableTransactionManagementConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration$TransactionTemplateConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizationAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionManagerCustomizers.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/transaction/TransactionProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/ServerProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/WebProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/AutoConfiguredRestClientSsl.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/HttpMessageConvertersRestClientCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/NotReactiveWebApplicationCondition$ReactiveWebApplication.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/NotReactiveWebApplicationCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/RestClientAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/RestClientBuilderConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/RestTemplateAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/client/RestTemplateBuilderConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration$JettyWebServerFactoryCustomizerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration$NettyWebServerFactoryCustomizerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration$TomcatWebServerFactoryCustomizerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration$UndertowWebServerFactoryCustomizerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/embedded/EmbeddedWebServerFactoryCustomizerAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/embedded/TomcatWebServerFactoryCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/format/WebConversionService.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DefaultDispatcherServletCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DispatcherServletConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DispatcherServletRegistrationCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration$DispatcherServletRegistrationConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/DispatcherServletAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/DispatcherServletPath.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/DispatcherServletRegistrationBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/HttpEncodingAutoConfiguration$LocaleCharsetMappingsCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/HttpEncodingAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/MultipartAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/MultipartProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration$BeanPostProcessorsRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration$ForwardedHeaderFilterConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration$ForwardedHeaderFilterCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration$EmbeddedJetty.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration$EmbeddedTomcat.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryConfiguration$EmbeddedUndertow.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/ServletWebServerFactoryCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/TomcatServletWebServerFactoryCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$OptionalPathExtensionContentNegotiationStrategy.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$ProblemDetailsErrorHandlingConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$ResourceChainCustomizerConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$ResourceChainResourceHandlerRegistrationCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$ResourceHandlerRegistrationCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$WelcomePageHandlerMappingFactory.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WebMvcProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WelcomePageHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/WelcomePageNotAcceptableHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/AbstractErrorController.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/BasicErrorController.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/DefaultErrorViewResolver.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$DefaultErrorViewResolverConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$ErrorPageCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$ErrorTemplateMissingCondition.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$PreserveErrorControllerTargetClassPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$StaticView.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/web/servlet/error/ErrorMvcAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/websocket/servlet/TomcatWebSocketServletWebServerCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration$JettyWebSocketConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration$TomcatWebSocketConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration$UndertowWebSocketConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/autoconfigure/websocket/servlet/WebSocketServletAutoConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/boot/availability/ApplicationAvailability.class\\E" }, { "pattern":"\\Qorg/springframework/boot/availability/ApplicationAvailabilityBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/context/annotation/DeterminableImports.class\\E" }, { "pattern":"\\Qorg/springframework/boot/context/properties/BoundConfigurationProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/context/properties/ConfigurationProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/context/properties/ConfigurationPropertiesBinder$ConfigurationPropertiesBinderFactory.class\\E" }, { "pattern":"\\Qorg/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/context/properties/EnableConfigurationProperties.class\\E" }, { "pattern":"\\Qorg/springframework/boot/context/properties/EnableConfigurationPropertiesRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/boot/jackson/JsonComponentModule.class\\E" }, { "pattern":"\\Qorg/springframework/boot/jackson/JsonMixinModule.class\\E" }, { "pattern":"\\Qorg/springframework/boot/jackson/JsonMixinModuleEntries.class\\E" }, { "pattern":"\\Qorg/springframework/boot/jdbc/init/DataSourceScriptDatabaseInitializer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/jdbc/metadata/DataSourcePoolMetadataProvider.class\\E" }, { "pattern":"\\Qorg/springframework/boot/logging/logback/defaults.xml\\E" }, { "pattern":"\\Qorg/springframework/boot/sql/init/AbstractScriptDatabaseInitializer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/sql/init/dependency/DatabaseInitializationDependencyConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/ssl/DefaultSslBundleRegistry.class\\E" }, { "pattern":"\\Qorg/springframework/boot/task/SimpleAsyncTaskExecutorBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/task/SimpleAsyncTaskSchedulerBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/task/TaskExecutorBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/task/TaskSchedulerBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/task/ThreadPoolTaskExecutorBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/task/ThreadPoolTaskSchedulerBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/validation/beanvalidation/MethodValidationExcludeFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/client/RestClientCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/client/RestTemplateBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/server/AbstractConfigurableWebServerFactory.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/server/ConfigurableWebServerFactory.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/server/ErrorPageRegistrarBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/server/WebServerFactoryCustomizerBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/server/mime-mappings.properties\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/AbstractFilterRegistrationBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/DelegatingFilterProxyRegistrationBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/DynamicRegistrationBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/FilterRegistrationBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/RegistrationBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/ServletComponentRegisteringPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/ServletRegistrationBean.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/error/DefaultErrorAttributes.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/filter/OrderedCharacterEncodingFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/filter/OrderedFormContentFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/filter/OrderedRequestContextFilter.class\\E" }, { "pattern":"\\Qorg/springframework/boot/web/servlet/server/AbstractServletWebServerFactory.class\\E" }, { "pattern":"\\Qorg/springframework/context/ApplicationContextAware.class\\E" }, { "pattern":"\\Qorg/springframework/context/ApplicationListener.class\\E" }, { "pattern":"\\Qorg/springframework/context/ResourceLoaderAware.class\\E" }, { "pattern":"\\Qorg/springframework/context/SmartLifecycle.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/AdviceModeImportSelector.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/AspectJAutoProxyRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/AutoProxyRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/CommonAnnotationBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/Conditional.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/Configuration.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/ConfigurationClassPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/DeferredImportSelector.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/DependsOn.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/EnableAspectJAutoProxy.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/Import.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/ImportAware.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/ImportBeanDefinitionRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/ImportRuntimeHints.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/PropertySource.class\\E" }, { "pattern":"\\Qorg/springframework/context/annotation/Role.class\\E" }, { "pattern":"\\Qorg/springframework/context/event/DefaultEventListenerFactory.class\\E" }, { "pattern":"\\Qorg/springframework/context/event/EventListenerMethodProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/context/event/GenericApplicationListener.class\\E" }, { "pattern":"\\Qorg/springframework/context/event/SmartApplicationListener.class\\E" }, { "pattern":"\\Qorg/springframework/context/support/ApplicationObjectSupport.class\\E" }, { "pattern":"\\Qorg/springframework/context/support/DefaultLifecycleProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/context/support/PropertySourcesPlaceholderConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/core/Ordered.class\\E" }, { "pattern":"\\Qorg/springframework/core/annotation/Order.class\\E" }, { "pattern":"\\Qorg/springframework/core/convert/ConversionService.class\\E" }, { "pattern":"\\Qorg/springframework/core/convert/support/GenericConversionService.class\\E" }, { "pattern":"\\Qorg/springframework/core/io/support/PropertiesLoaderSupport.class\\E" }, { "pattern":"\\Qorg/springframework/core/task/AsyncTaskExecutor.class\\E" }, { "pattern":"\\Qorg/springframework/core/type/classreading/CachingMetadataReaderFactory.class\\E" }, { "pattern":"\\Qorg/springframework/core/type/classreading/SimpleMetadataReaderFactory.class\\E" }, { "pattern":"\\Qorg/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/format/support/DefaultFormattingConversionService.class\\E" }, { "pattern":"\\Qorg/springframework/format/support/FormattingConversionService.class\\E" }, { "pattern":"\\Qorg/springframework/http/converter/AbstractGenericHttpMessageConverter.class\\E" }, { "pattern":"\\Qorg/springframework/http/converter/AbstractHttpMessageConverter.class\\E" }, { "pattern":"\\Qorg/springframework/http/converter/HttpMessageConverter.class\\E" }, { "pattern":"\\Qorg/springframework/http/converter/StringHttpMessageConverter.class\\E" }, { "pattern":"\\Qorg/springframework/http/converter/json/AbstractJackson2HttpMessageConverter.class\\E" }, { "pattern":"\\Qorg/springframework/http/converter/json/Jackson2ObjectMapperBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/http/converter/json/MappingJackson2HttpMessageConverter.class\\E" }, { "pattern":"\\Qorg/springframework/jdbc/core/JdbcTemplate.class\\E" }, { "pattern":"\\Qorg/springframework/jdbc/core/namedparam/NamedParameterJdbcTemplate.class\\E" }, { "pattern":"\\Qorg/springframework/jdbc/core/simple/DefaultJdbcClient.class\\E" }, { "pattern":"\\Qorg/springframework/jdbc/core/simple/JdbcClient.class\\E" }, { "pattern":"\\Qorg/springframework/jdbc/datasource/DataSourceTransactionManager.class\\E" }, { "pattern":"\\Qorg/springframework/jdbc/support/JdbcAccessor.class\\E" }, { "pattern":"\\Qorg/springframework/jdbc/support/JdbcTransactionManager.class\\E" }, { "pattern":"\\Qorg/springframework/jmx/export/MBeanExporter.class\\E" }, { "pattern":"\\Qorg/springframework/jmx/export/annotation/AnnotationMBeanExporter.class\\E" }, { "pattern":"\\Qorg/springframework/jmx/export/naming/MetadataNamingStrategy.class\\E" }, { "pattern":"\\Qorg/springframework/jmx/support/MBeanRegistrationSupport.class\\E" }, { "pattern":"\\Qorg/springframework/ldap/core/support/AbstractContextSource.class\\E" }, { "pattern":"\\Qorg/springframework/ldap/core/support/LdapContextSource.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/SchedulingTaskExecutor.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/TaskScheduler.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/annotation/EnableScheduling.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/annotation/ScheduledAnnotationBeanPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/annotation/SchedulingConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/annotation/SchedulingConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/concurrent/CustomizableThreadFactory.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/concurrent/ExecutorConfigurationSupport.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.class\\E" }, { "pattern":"\\Qorg/springframework/scheduling/concurrent/ThreadPoolTaskScheduler.class\\E" }, { "pattern":"\\Qorg/springframework/security/access/expression/SecurityExpressionHandler.class\\E" }, { "pattern":"\\Qorg/springframework/security/authentication/AnonymousAuthenticationProvider.class\\E" }, { "pattern":"\\Qorg/springframework/security/authentication/AuthenticationManager.class\\E" }, { "pattern":"\\Qorg/springframework/security/authentication/AuthenticationProvider.class\\E" }, { "pattern":"\\Qorg/springframework/security/authentication/DefaultAuthenticationEventPublisher.class\\E" }, { "pattern":"\\Qorg/springframework/security/authentication/ProviderManager.class\\E" }, { "pattern":"\\Qorg/springframework/security/authentication/dao/AbstractUserDetailsAuthenticationProvider.class\\E" }, { "pattern":"\\Qorg/springframework/security/authentication/dao/DaoAuthenticationProvider.class\\E" }, { "pattern":"\\Qorg/springframework/security/authorization/method/AuthorizationAdvisorProxyFactory.class\\E" }, { "pattern":"\\Qorg/springframework/security/authorization/method/AuthorizeReturnObjectMethodInterceptor.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/AbstractConfiguredSecurityBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/AbstractSecurityBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/ObjectPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration$AuthenticationManagerDelegator.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration$DefaultPasswordEncoderAuthenticationManagerBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration$EnableGlobalAuthenticationAutowiredConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration$LazyPasswordEncoder.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/AuthenticationConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/EnableGlobalAuthentication.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/GlobalAuthenticationConfigurerAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/InitializeAuthenticationProviderBeanManagerConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/authentication/configuration/InitializeUserDetailsBeanManagerConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/configuration/AutowireBeanFactoryObjectPostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/configuration/ObjectPostProcessorConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/method/configuration/AuthorizationProxyConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/method/configuration/EnableMethodSecurity.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/method/configuration/MethodSecurityAdvisorRegistrar.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/method/configuration/MethodSecuritySelector.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration$DeferringMethodInterceptor.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/builders/HttpSecurity.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/builders/WebSecurity.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/EnableWebSecurity.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration$DefaultPasswordEncoderAuthenticationManagerBuilder.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration$LazyPasswordEncoder.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/HttpSecurityConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/OAuth2ImportSelector.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/SpringWebMvcImportSelector.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration$1.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration$CompositeFilterChainProxy.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration$HandlerMappingIntrospectorCacheFilterFactoryBean.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/WebMvcSecurityConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration$AnnotationAwareOrderComparator.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/WebSecurityConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/annotation/web/configuration/WebSecurityCustomizer.class\\E" }, { "pattern":"\\Qorg/springframework/security/config/crypto/RsaKeyConversionServicePostProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/security/context/DelegatingApplicationListener.class\\E" }, { "pattern":"\\Qorg/springframework/security/core/userdetails/UserDetailsService.class\\E" }, { "pattern":"\\Qorg/springframework/security/crypto/bcrypt/BCryptPasswordEncoder.class\\E" }, { "pattern":"\\Qorg/springframework/security/crypto/password/PasswordEncoder.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/AuthenticationEntryPoint.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/DefaultSecurityFilterChain.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/FilterChainProxy.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/SecurityFilterChain.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator$HttpServletRequestTransformer.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/access/ExceptionTranslationFilter.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/access/HandlerMappingIntrospectorRequestTransformer.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/access/RequestMatcherDelegatingWebInvocationPrivilegeEvaluator.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/access/WebInvocationPrivilegeEvaluator.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/authentication/logout/LogoutFilter.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/authentication/logout/LogoutSuccessEventPublishingLogoutHandler.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/authentication/session/AbstractSessionFixationProtectionStrategy.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/authentication/session/ChangeSessionIdAuthenticationStrategy.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/authentication/session/CompositeSessionAuthenticationStrategy.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/context/SecurityContextHolderFilter.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/csrf/CsrfFilter.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/header/HeaderWriterFilter.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/savedrequest/RequestCacheAwareFilter.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/servlet/support/csrf/CsrfRequestDataValueProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.class\\E" }, { "pattern":"\\Qorg/springframework/security/web/servletapi/SecurityContextHolderAwareRequestFilter.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/ConfigurableTransactionManager.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/TransactionDefinition.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/annotation/AbstractTransactionManagementConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/annotation/AnnotationTransactionAttributeSource.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/annotation/EnableTransactionManagement.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/annotation/ProxyTransactionManagementConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/annotation/RestrictedTransactionalEventListenerFactory.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/annotation/TransactionManagementConfigurationSelector.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/event/TransactionalEventListenerFactory.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/interceptor/AbstractFallbackTransactionAttributeSource.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/interceptor/BeanFactoryTransactionAttributeSourceAdvisor.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/interceptor/TransactionAspectSupport.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/interceptor/TransactionInterceptor.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/support/AbstractPlatformTransactionManager.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/support/DefaultTransactionDefinition.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/support/TransactionOperations.class\\E" }, { "pattern":"\\Qorg/springframework/transaction/support/TransactionTemplate.class\\E" }, { "pattern":"\\Qorg/springframework/util/AntPathMatcher.class\\E" }, { "pattern":"\\Qorg/springframework/util/CustomizableThreadCreator.class\\E" }, { "pattern":"\\Qorg/springframework/util/PathMatcher.class\\E" }, { "pattern":"\\Qorg/springframework/validation/Validator.class\\E" }, { "pattern":"\\Qorg/springframework/web/accept/ContentNegotiationManager.class\\E" }, { "pattern":"\\Qorg/springframework/web/bind/annotation/ControllerAdvice.class\\E" }, { "pattern":"\\Qorg/springframework/web/bind/annotation/Mapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/bind/annotation/PostMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/bind/annotation/RequestMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/bind/annotation/ResponseBody.class\\E" }, { "pattern":"\\Qorg/springframework/web/bind/annotation/RestController.class\\E" }, { "pattern":"\\Qorg/springframework/web/client/RestClient$Builder.class\\E" }, { "pattern":"\\Qorg/springframework/web/context/ServletContextAware.class\\E" }, { "pattern":"\\Qorg/springframework/web/context/support/WebApplicationObjectSupport.class\\E" }, { "pattern":"\\Qorg/springframework/web/filter/CharacterEncodingFilter.class\\E" }, { "pattern":"\\Qorg/springframework/web/filter/CorsFilter.class\\E" }, { "pattern":"\\Qorg/springframework/web/filter/FormContentFilter.class\\E" }, { "pattern":"\\Qorg/springframework/web/filter/GenericFilterBean.class\\E" }, { "pattern":"\\Qorg/springframework/web/filter/OncePerRequestFilter.class\\E" }, { "pattern":"\\Qorg/springframework/web/filter/RequestContextFilter.class\\E" }, { "pattern":"\\Qorg/springframework/web/method/support/CompositeUriComponentsContributor.class\\E" }, { "pattern":"\\Qorg/springframework/web/multipart/support/StandardServletMultipartResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/DispatcherServlet.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/FlashMapManager.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/FrameworkServlet.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/HandlerExceptionResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/HandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/HttpServletBean.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/LocaleResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/RequestToViewNameTranslator.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/ThemeResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/View.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/ViewResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport$NoOpValidator.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/config/annotation/WebMvcConfigurationSupport.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/config/annotation/WebMvcConfigurer.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/function/support/HandlerFunctionAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/function/support/RouterFunctionMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/AbstractDetectingUrlHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/AbstractHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/AbstractHandlerMethodMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/AbstractUrlHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/BeanNameUrlHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/HandlerExceptionResolverComposite.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/HandlerMappingIntrospector.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/MatchableHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/handler/SimpleUrlHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/i18n/AbstractLocaleResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/i18n/AcceptHeaderLocaleResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/mvc/HttpRequestHandlerAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/mvc/SimpleControllerHandlerAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/mvc/method/AbstractHandlerMethodAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/mvc/method/RequestMappingInfoHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerAdapter.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/resource/ResourceUrlProvider.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/support/AbstractFlashMapManager.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/support/RequestDataValueProcessor.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/support/SessionFlashMapManager.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/support/WebContentGenerator.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/theme/AbstractThemeResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/theme/FixedThemeResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/AbstractCachingViewResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/AbstractUrlBasedView.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/AbstractView.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/BeanNameViewResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/ContentNegotiatingViewResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/DefaultRequestToViewNameTranslator.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/InternalResourceView.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/InternalResourceViewResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/UrlBasedViewResolver.class\\E" }, { "pattern":"\\Qorg/springframework/web/servlet/view/ViewResolverComposite.class\\E" }, { "pattern":"\\Qorg/springframework/web/util/HtmlCharacterEntityReferences.properties\\E" }, { "pattern":"\\Qorg/springframework/web/util/UrlPathHelper.class\\E" }, { "pattern":"\\Qorg/springframework/web/util/pattern/PathPatternParser.class\\E" }, { "pattern":"\\Qraft.desc\\E" }, { "pattern":"\\Qstatic/\\E" }, { "pattern":"\\Qstatic/console-ui/public/css/bootstrap.css\\E" }, { "pattern":"\\Qstatic/console-ui/public/css/codemirror.css\\E" }, { "pattern":"\\Qstatic/console-ui/public/css/console1412.css\\E" }, { "pattern":"\\Qstatic/console-ui/public/css/font-awesome.css\\E" }, { "pattern":"\\Qstatic/console-ui/public/css/icon.css\\E" }, { "pattern":"\\Qstatic/console-ui/public/css/merge.css\\E" }, { "pattern":"\\Qstatic/console-ui/public/fonts/font_1533967_slipq25tezj.woff2\\E" }, { "pattern":"\\Qstatic/console-ui/public/fonts/roboto-bold.woff2\\E" }, { "pattern":"\\Qstatic/console-ui/public/fonts/roboto-medium.woff2\\E" }, { "pattern":"\\Qstatic/console-ui/public/fonts/roboto-regular.woff2\\E" }, { "pattern":"\\Qstatic/console-ui/public/img/nacos-logo.png\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/codemirror.addone.fullscreen.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/codemirror.addone.json-lint.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/codemirror.addone.lint.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/codemirror.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/codemirror.lib.clike-lint.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/codemirror.lib.json-lint.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/diff_match_patch.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/javascript.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/jquery.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/loader.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/merge.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/vs/base/worker/workerMain.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/vs/editor/editor.main.css\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/vs/editor/editor.main.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/vs/editor/editor.main.nls.zh-cn.js\\E" }, { "pattern":"\\Qstatic/console-ui/public/js/xml.js\\E" }, { "pattern":"\\Qstatic/css/main.css\\E" }, { "pattern":"\\Qstatic/img/logo-2000-390.svg\\E" }, { "pattern":"\\Qstatic/index.html\\E" }, { "pattern":"\\Qstatic/js/main.js\\E" }, { "pattern":"java.base:\\Qjava/lang/Iterable.class\\E" }, { "pattern":"java.base:\\Qjava/lang/Object.class\\E" }, { "pattern":"java.base:\\Qjava/util/function/BiPredicate.class\\E" }, { "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt67b/nfc.nrm\\E" }, { "pattern":"java.management:\\Qcom/sun/jmx/mbeanserver/JmxMBeanServer.class\\E" }, { "pattern":"java.sql:\\Qjavax/sql/CommonDataSource.class\\E" }, { "pattern":"java.sql:\\Qjavax/sql/DataSource.class\\E" }, { "pattern":"static/.*" }]}, "bundles":[{ "name":"com.mysql.cj.LocalizedErrorMessages", "locales":["und"] }, { "name":"jakarta.servlet.LocalStrings", "locales":["und"] }, { "name":"jakarta.servlet.http.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.authenticator.LocalStrings", "locales":["", "und"] }, { "name":"org.apache.catalina.authenticator.jaspic.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.connector.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.core.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.deploy.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.loader.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.mapper.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.mbeans.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.realm.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.security.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.session.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.startup.LocalStrings", "locales":["und"] }, { "name":"org.apache.catalina.util.LocalStrings", "locales":["", "und"] }, { "name":"org.apache.catalina.valves.LocalStrings", "locales":["", "und"] }, { "name":"org.apache.catalina.webresources.LocalStrings", "locales":["und"] }, { "name":"org.apache.coyote.LocalStrings", "locales":["und"] }, { "name":"org.apache.coyote.http11.LocalStrings", "locales":["und"] }, { "name":"org.apache.coyote.http11.filters.LocalStrings", "locales":["und"] }, { "name":"org.apache.derby.loc.m0", "locales":["en"] }, { "name":"org.apache.derby.loc.m13", "locales":["en"] }, { "name":"org.apache.derby.loc.m16", "locales":["en"] }, { "name":"org.apache.derby.loc.m18", "locales":["en"] }, { "name":"org.apache.derby.loc.m2", "locales":["en"] }, { "name":"org.apache.derby.loc.m22", "locales":["en"] }, { "name":"org.apache.derby.loc.m26", "locales":["en"] }, { "name":"org.apache.derby.loc.m27", "locales":["en"] }, { "name":"org.apache.derby.loc.m28", "locales":["en"] }, { "name":"org.apache.derby.loc.m29", "locales":["en"] }, { "name":"org.apache.derby.loc.m35", "locales":["en"] }, { "name":"org.apache.derby.loc.m37", "locales":["en"] }, { "name":"org.apache.derby.loc.m38", "locales":["en"] }, { "name":"org.apache.derby.loc.m42", "locales":["en"] }, { "name":"org.apache.derby.loc.m7", "locales":["en"] }, { "name":"org.apache.naming.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.buf.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.compat.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.descriptor.web.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.http.LocalStrings", "locales":["", "und"] }, { "name":"org.apache.tomcat.util.http.parser.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.modeler.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.net.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.scan.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.util.threads.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.websocket.LocalStrings", "locales":["und"] }, { "name":"org.apache.tomcat.websocket.server.LocalStrings", "locales":["und"] }] } ================================================ FILE: console/src/main/resources/META-INF/native-image/com.alibaba.nacos/nacos-console/serialization-config.json ================================================ { "types":[ { "name":"byte[]" }, { "name":"ch.qos.logback.classic.model.ConfigurationModel" }, { "name":"ch.qos.logback.classic.model.LoggerModel" }, { "name":"ch.qos.logback.classic.model.RootLoggerModel" }, { "name":"ch.qos.logback.core.model.AppenderModel" }, { "name":"ch.qos.logback.core.model.AppenderRefModel" }, { "name":"ch.qos.logback.core.model.ComponentModel" }, { "name":"ch.qos.logback.core.model.ImplicitModel" }, { "name":"ch.qos.logback.core.model.IncludeModel" }, { "name":"ch.qos.logback.core.model.Model" }, { "name":"ch.qos.logback.core.model.NamedComponentModel" }, { "name":"ch.qos.logback.core.model.NamedModel" }, { "name":"ch.qos.logback.core.model.PropertyModel" }, { "name":"ch.qos.logback.core.model.StatusListenerModel" }, { "name":"com.alibaba.nacos.naming.core.v2.service.impl.PersistentClientOperationServiceImpl$InstanceStoreRequest" }, { "name":"java.lang.Boolean" }, { "name":"java.lang.Double" }, { "name":"java.lang.Exception" }, { "name":"java.lang.Long" }, { "name":"java.lang.Number" }, { "name":"java.lang.StackTraceElement" }, { "name":"java.lang.StackTraceElement[]" }, { "name":"java.lang.String" }, { "name":"java.lang.Throwable" }, { "name":"java.rmi.MarshalledObject" }, { "name":"java.rmi.dgc.Lease" }, { "name":"java.rmi.dgc.VMID" }, { "name":"java.rmi.server.ObjID" }, { "name":"java.rmi.server.ObjID[]" }, { "name":"java.rmi.server.RemoteObject" }, { "name":"java.rmi.server.RemoteStub" }, { "name":"java.rmi.server.UID" }, { "name":"java.util.ArrayList" }, { "name":"java.util.Collections$EmptyList" }, { "name":"java.util.HashMap" }, { "name":"java.util.HashSet" }, { "name":"java.util.LinkedHashMap" }, { "name":"java.util.TreeMap" }, { "name":"javax.management.InstanceNotFoundException" }, { "name":"javax.management.JMException" }, { "name":"javax.management.ObjectInstance" }, { "name":"javax.management.ObjectName" }, { "name":"javax.management.OperationsException" }, { "name":"javax.management.openmbean.CompositeDataSupport" }, { "name":"javax.management.openmbean.CompositeType" }, { "name":"javax.management.openmbean.OpenType" }, { "name":"javax.management.openmbean.SimpleType" }, { "name":"javax.management.remote.rmi.RMIConnectionImpl_Stub" }, { "name":"javax.management.remote.rmi.RMIServerImpl_Stub" }, { "name":"org.springframework.boot.logging.logback.SpringProfileModel" }, { "name":"org.springframework.boot.logging.logback.SpringPropertyModel" } ], "lambdaCapturingTypes":[ ], "proxies":[ ] } ================================================ FILE: console/src/main/resources/META-INF/services/com.alibaba.nacos.auth.config.NacosAuthConfig ================================================ # # Copyright 1999-2025 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.console.config.NacosConsoleAuthConfig ================================================ FILE: console/src/main/resources/META-INF/services/com.alibaba.nacos.core.listener.startup.NacosStartUp ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.console.NacosConsoleStartUp ================================================ FILE: console/src/main/resources/META-INF/services/com.alibaba.nacos.core.paramcheck.AbstractHttpParamExtractor ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.console.paramcheck.ConsoleDefaultHttpParamExtractor ================================================ FILE: console/src/main/resources/META-INF/services/com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService ================================================ # # Copyright 1999-2025 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.console.handler.impl.remote.ConsoleMaintainerClientAuthPlugin ================================================ FILE: console/src/main/resources/META-INF/services/com.alibaba.nacos.sys.filter.NacosPackageExcludeFilter ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.console.config.ConsolePackageExcludeFilter ================================================ FILE: console/src/main/resources/META-INF/services/com.alibaba.nacos.sys.module.ModuleStateBuilder ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # com.alibaba.nacos.console.config.ConsoleModuleStateBuilder com.alibaba.nacos.console.config.ConsoleAuthModuleStateBuilder ================================================ FILE: console/src/main/resources/nacos-console-banner.txt ================================================ ,--. ,--.'| ,--,: : | Nacos Console ${application.version} ,`--.'`| ' : ,---. Running in ${nacos.mode} mode, ${nacos.function.mode} function modules | : : | | ' ,'\ .--.--. Port: ${nacos.console.port} : | \ | : ,--.--. ,---. / / | / / ' Pid: ${pid} | : ' '; | / \ / \. ; ,. :| : /`./ Console: http://${nacos.local.ip}:${nacos.console.port}${nacos.console.contextPath}/index.html ' ' ;. ;.--. .-. | / / '' | |: :| : ;_ | | | \ | \__\/: . .. ' / ' | .; : \ \ `. https://nacos.io ' : | ; .' ," .--.; |' ; :__| : | `----. \ | | '`--' / / ,. |' | '.'|\ \ / / /`--' / ' : | ; : .' \ : : `----' '--'. / ; |.' | , .-./\ \ / `--'---' '---' `--`---' `----' ================================================ FILE: console/src/main/resources/nacos-console.properties ================================================ # # Copyright 1999-2023 Alibaba Group Holding Ltd. # # 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. # ### nacos console port: server.port=${nacos.console.port:8080} server.servlet.contextPath=${nacos.console.contextPath:} ================================================ FILE: console/src/main/resources/static/console-ui/public/css/bootstrap.css ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ /*! normalize.css v2.1.3 | MIT License | git.io/normalize */ article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { display: block; } audio, canvas, video { display: inline-block; } audio:not([controls]) { display: none; height: 0; } [hidden], template { display: none; } html { font-family: sans-serif; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } body { margin: 0; } a { background: transparent; } a:focus { outline: thin dotted; } a:active, a:hover { outline: 0; } h1 { margin: 0.67em 0; font-size: 2em; } abbr[title] { border-bottom: 1px dotted; } b, strong { font-weight: bold; } dfn { font-style: italic; } hr { height: 0; -moz-box-sizing: content-box; box-sizing: content-box; } mark { color: #000; background: #ff0; } code, kbd, pre, samp { font-family: monospace, serif; font-size: 1em; } pre { white-space: pre-wrap; } q { quotes: "\201C" "\201D" "\2018" "\2019"; } small { font-size: 80%; } sub, sup { position: relative; font-size: 75%; line-height: 0; vertical-align: baseline; } sup { top: -0.5em; } sub { bottom: -0.25em; } img { border: 0; } svg:not(:root) { overflow: hidden; } figure { margin: 0; } fieldset { padding: 0.35em 0.625em 0.75em; margin: 0 2px; border: 1px solid #c0c0c0; } legend { padding: 0; border: 0; } button, input, select, textarea { margin: 0; font-family: inherit; font-size: 100%; } button, input { line-height: normal; } button, select { text-transform: none; } button, html input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; } button[disabled], html input[disabled] { cursor: default; } input[type="checkbox"], input[type="radio"] { padding: 0; box-sizing: border-box; } input[type="search"] { -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; -webkit-appearance: textfield; } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none; } button::-moz-focus-inner, input::-moz-focus-inner { padding: 0; border: 0; } textarea { overflow: auto; vertical-align: top; } table { border-collapse: collapse; border-spacing: 0; } @media print { * { color: #000 !important; text-shadow: none !important; background: transparent !important; box-shadow: none !important; } a, a:visited { text-decoration: underline; } a[href]:after { content: " (" attr(href) ")"; } abbr[title]:after { content: " (" attr(title) ")"; } a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } tr, img { page-break-inside: avoid; } img { max-width: 100% !important; } @page { margin: 2cm .5cm; } p, h2, h3 { orphans: 3; widows: 3; } h2, h3 { page-break-after: avoid; } select { background: #fff !important; } .navbar { display: none; } .table td, .table th { background-color: #fff !important; } .btn > .caret, .dropup > .btn > .caret { border-top-color: #000 !important; } .label { border: 1px solid #000; } .table { border-collapse: collapse !important; } .table-bordered th, .table-bordered td { border: 1px solid #ddd !important; } } *, *:before, *:after { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } html { font-size: 62.5%; -webkit-tap-highlight-color: rgba(0, 0, 0, 0); } body { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.428571429; color: #333333; background-color: #ffffff; } input, button, select, textarea { font-family: inherit; font-size: inherit; } a { color: #428bca; text-decoration: none; } a:hover, a:focus { color: #2a6496; text-decoration: underline; } a:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } img { vertical-align: middle; } .img-responsive { display: block; height: auto; max-width: 100%; } .img-rounded { border-radius: 6px; } .img-thumbnail { display: inline-block; height: auto; max-width: 100%; padding: 4px; line-height: 1.428571429; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } .img-circle { border-radius: 50%; } hr { margin-top: 20px; margin-bottom: 20px; border: 0; border-top: 1px solid #eeeeee; } .sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); border: 0; } h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-weight: 500; line-height: 1.1; color: inherit; } h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { font-weight: normal; line-height: 1; color: #999999; } h1, h2, h3 { margin-top: 20px; margin-bottom: 10px; } h1 small, h2 small, h3 small, h1 .small, h2 .small, h3 .small { font-size: 65%; } h4, h5, h6 { margin-top: 10px; margin-bottom: 10px; } h4 small, h5 small, h6 small, h4 .small, h5 .small, h6 .small { font-size: 75%; } h1, .h1 { font-size: 36px; } h2, .h2 { font-size: 30px; } h3, .h3 { font-size: 24px; } h4, .h4 { font-size: 18px; } h5, .h5 { font-size: 14px; } h6, .h6 { font-size: 12px; } p { margin: 0 0 10px; } .lead { margin-bottom: 20px; font-size: 16px; font-weight: 200; line-height: 1.4; } @media (min-width: 768px) { .lead { font-size: 21px; } } small, .small { font-size: 85%; } cite { font-style: normal; } .text-muted { color: #999999; } .text-primary { color: #428bca; } .text-primary:hover { color: #3071a9; } .text-warning { color: #8a6d3b; } .text-warning:hover { color: #66512c; } .text-danger { color: #a94442; } .text-danger:hover { color: #843534; } .text-success { color: #3c763d; } .text-success:hover { color: #2b542c; } .text-info { color: #31708f; } .text-info:hover { color: #245269; } .text-left { text-align: left; } .text-right { text-align: right; } .text-center { text-align: center; } .page-header { padding-bottom: 9px; margin: 40px 0 20px; border-bottom: 1px solid #eeeeee; } ul, ol { margin-top: 0; margin-bottom: 10px; } ul ul, ol ul, ul ol, ol ol { margin-bottom: 0; } .list-unstyled { padding-left: 0; list-style: none; } .list-inline { padding-left: 0; list-style: none; } .list-inline > li { display: inline-block; padding-right: 5px; padding-left: 5px; } .list-inline > li:first-child { padding-left: 0; } dl { margin-top: 0; margin-bottom: 20px; } dt, dd { line-height: 1.428571429; } dt { font-weight: bold; } dd { margin-left: 0; } @media (min-width: 768px) { .dl-horizontal dt { float: left; width: 160px; overflow: hidden; clear: left; text-align: right; text-overflow: ellipsis; white-space: nowrap; } .dl-horizontal dd { margin-left: 180px; } .dl-horizontal dd:before, .dl-horizontal dd:after { display: table; content: " "; } .dl-horizontal dd:after { clear: both; } .dl-horizontal dd:before, .dl-horizontal dd:after { display: table; content: " "; } .dl-horizontal dd:after { clear: both; } } abbr[title], abbr[data-original-title] { cursor: help; border-bottom: 1px dotted #999999; } .initialism { font-size: 90%; text-transform: uppercase; } blockquote { padding: 10px 20px; margin: 0 0 20px; border-left: 5px solid #eeeeee; } blockquote p { font-size: 17.5px; font-weight: 300; line-height: 1.25; } blockquote p:last-child { margin-bottom: 0; } blockquote small, blockquote .small { display: block; line-height: 1.428571429; color: #999999; } blockquote small:before, blockquote .small:before { content: '\2014 \00A0'; } blockquote.pull-right { padding-right: 15px; padding-left: 0; border-right: 5px solid #eeeeee; border-left: 0; } blockquote.pull-right p, blockquote.pull-right small, blockquote.pull-right .small { text-align: right; } blockquote.pull-right small:before, blockquote.pull-right .small:before { content: ''; } blockquote.pull-right small:after, blockquote.pull-right .small:after { content: '\00A0 \2014'; } blockquote:before, blockquote:after { content: ""; } address { margin-bottom: 20px; font-style: normal; line-height: 1.428571429; } code, kbd, pre, samp { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } code { padding: 2px 4px; font-size: 90%; color: #c7254e; white-space: nowrap; background-color: #f9f2f4; border-radius: 4px; } pre { display: block; padding: 9.5px; margin: 0 0 10px; font-size: 13px; line-height: 1.428571429; color: #333333; word-break: break-all; word-wrap: break-word; background-color: #f5f5f5; border: 1px solid #cccccc; border-radius: 4px; } pre code { padding: 0; font-size: inherit; color: inherit; white-space: pre-wrap; background-color: transparent; border-radius: 0; } .pre-scrollable { max-height: 340px; overflow-y: scroll; } .container { padding-right: 15px; padding-left: 15px; margin-right: auto; margin-left: auto; } .container:before, .container:after { display: table; content: " "; } .container:after { clear: both; } .container:before, .container:after { display: table; content: " "; } .container:after { clear: both; } @media (min-width: 768px) { .container { width: 750px; } } @media (min-width: 992px) { .container { width: 970px; } } @media (min-width: 1200px) { .container { width: 1170px; } } .row { margin-right: -15px; margin-left: -15px; } .row:before, .row:after { display: table; content: " "; } .row:after { clear: both; } .row:before, .row:after { display: table; content: " "; } .row:after { clear: both; } .col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 { position: relative; min-height: 1px; padding-right: 15px; padding-left: 15px; } .col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 { float: left; } .col-xs-12 { width: 100%; } .col-xs-11 { width: 91.66666666666666%; } .col-xs-10 { width: 83.33333333333334%; } .col-xs-9 { width: 75%; } .col-xs-8 { width: 66.66666666666666%; } .col-xs-7 { width: 58.333333333333336%; } .col-xs-6 { width: 50%; } .col-xs-5 { width: 41.66666666666667%; } .col-xs-4 { width: 33.33333333333333%; } .col-xs-3 { width: 25%; } .col-xs-2 { width: 16.666666666666664%; } .col-xs-1 { width: 8.333333333333332%; } .col-xs-pull-12 { right: 100%; } .col-xs-pull-11 { right: 91.66666666666666%; } .col-xs-pull-10 { right: 83.33333333333334%; } .col-xs-pull-9 { right: 75%; } .col-xs-pull-8 { right: 66.66666666666666%; } .col-xs-pull-7 { right: 58.333333333333336%; } .col-xs-pull-6 { right: 50%; } .col-xs-pull-5 { right: 41.66666666666667%; } .col-xs-pull-4 { right: 33.33333333333333%; } .col-xs-pull-3 { right: 25%; } .col-xs-pull-2 { right: 16.666666666666664%; } .col-xs-pull-1 { right: 8.333333333333332%; } .col-xs-pull-0 { right: 0; } .col-xs-push-12 { left: 100%; } .col-xs-push-11 { left: 91.66666666666666%; } .col-xs-push-10 { left: 83.33333333333334%; } .col-xs-push-9 { left: 75%; } .col-xs-push-8 { left: 66.66666666666666%; } .col-xs-push-7 { left: 58.333333333333336%; } .col-xs-push-6 { left: 50%; } .col-xs-push-5 { left: 41.66666666666667%; } .col-xs-push-4 { left: 33.33333333333333%; } .col-xs-push-3 { left: 25%; } .col-xs-push-2 { left: 16.666666666666664%; } .col-xs-push-1 { left: 8.333333333333332%; } .col-xs-push-0 { left: 0; } .col-xs-offset-12 { margin-left: 100%; } .col-xs-offset-11 { margin-left: 91.66666666666666%; } .col-xs-offset-10 { margin-left: 83.33333333333334%; } .col-xs-offset-9 { margin-left: 75%; } .col-xs-offset-8 { margin-left: 66.66666666666666%; } .col-xs-offset-7 { margin-left: 58.333333333333336%; } .col-xs-offset-6 { margin-left: 50%; } .col-xs-offset-5 { margin-left: 41.66666666666667%; } .col-xs-offset-4 { margin-left: 33.33333333333333%; } .col-xs-offset-3 { margin-left: 25%; } .col-xs-offset-2 { margin-left: 16.666666666666664%; } .col-xs-offset-1 { margin-left: 8.333333333333332%; } .col-xs-offset-0 { margin-left: 0; } @media (min-width: 768px) { .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 { float: left; } .col-sm-12 { width: 100%; } .col-sm-11 { width: 91.66666666666666%; } .col-sm-10 { width: 83.33333333333334%; } .col-sm-9 { width: 75%; } .col-sm-8 { width: 66.66666666666666%; } .col-sm-7 { width: 58.333333333333336%; } .col-sm-6 { width: 50%; } .col-sm-5 { width: 41.66666666666667%; } .col-sm-4 { width: 33.33333333333333%; } .col-sm-3 { width: 25%; } .col-sm-2 { width: 16.666666666666664%; } .col-sm-1 { width: 8.333333333333332%; } .col-sm-pull-12 { right: 100%; } .col-sm-pull-11 { right: 91.66666666666666%; } .col-sm-pull-10 { right: 83.33333333333334%; } .col-sm-pull-9 { right: 75%; } .col-sm-pull-8 { right: 66.66666666666666%; } .col-sm-pull-7 { right: 58.333333333333336%; } .col-sm-pull-6 { right: 50%; } .col-sm-pull-5 { right: 41.66666666666667%; } .col-sm-pull-4 { right: 33.33333333333333%; } .col-sm-pull-3 { right: 25%; } .col-sm-pull-2 { right: 16.666666666666664%; } .col-sm-pull-1 { right: 8.333333333333332%; } .col-sm-pull-0 { right: 0; } .col-sm-push-12 { left: 100%; } .col-sm-push-11 { left: 91.66666666666666%; } .col-sm-push-10 { left: 83.33333333333334%; } .col-sm-push-9 { left: 75%; } .col-sm-push-8 { left: 66.66666666666666%; } .col-sm-push-7 { left: 58.333333333333336%; } .col-sm-push-6 { left: 50%; } .col-sm-push-5 { left: 41.66666666666667%; } .col-sm-push-4 { left: 33.33333333333333%; } .col-sm-push-3 { left: 25%; } .col-sm-push-2 { left: 16.666666666666664%; } .col-sm-push-1 { left: 8.333333333333332%; } .col-sm-push-0 { left: 0; } .col-sm-offset-12 { margin-left: 100%; } .col-sm-offset-11 { margin-left: 91.66666666666666%; } .col-sm-offset-10 { margin-left: 83.33333333333334%; } .col-sm-offset-9 { margin-left: 75%; } .col-sm-offset-8 { margin-left: 66.66666666666666%; } .col-sm-offset-7 { margin-left: 58.333333333333336%; } .col-sm-offset-6 { margin-left: 50%; } .col-sm-offset-5 { margin-left: 41.66666666666667%; } .col-sm-offset-4 { margin-left: 33.33333333333333%; } .col-sm-offset-3 { margin-left: 25%; } .col-sm-offset-2 { margin-left: 16.666666666666664%; } .col-sm-offset-1 { margin-left: 8.333333333333332%; } .col-sm-offset-0 { margin-left: 0; } } @media (min-width: 992px) { .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 { float: left; } .col-md-12 { width: 100%; } .col-md-11 { width: 91.66666666666666%; } .col-md-10 { width: 83.33333333333334%; } .col-md-9 { width: 75%; } .col-md-8 { width: 66.66666666666666%; } .col-md-7 { width: 58.333333333333336%; } .col-md-6 { width: 50%; } .col-md-5 { width: 41.66666666666667%; } .col-md-4 { width: 33.33333333333333%; } .col-md-3 { width: 25%; } .col-md-2 { width: 16.666666666666664%; } .col-md-1 { width: 8.333333333333332%; } .col-md-pull-12 { right: 100%; } .col-md-pull-11 { right: 91.66666666666666%; } .col-md-pull-10 { right: 83.33333333333334%; } .col-md-pull-9 { right: 75%; } .col-md-pull-8 { right: 66.66666666666666%; } .col-md-pull-7 { right: 58.333333333333336%; } .col-md-pull-6 { right: 50%; } .col-md-pull-5 { right: 41.66666666666667%; } .col-md-pull-4 { right: 33.33333333333333%; } .col-md-pull-3 { right: 25%; } .col-md-pull-2 { right: 16.666666666666664%; } .col-md-pull-1 { right: 8.333333333333332%; } .col-md-pull-0 { right: 0; } .col-md-push-12 { left: 100%; } .col-md-push-11 { left: 91.66666666666666%; } .col-md-push-10 { left: 83.33333333333334%; } .col-md-push-9 { left: 75%; } .col-md-push-8 { left: 66.66666666666666%; } .col-md-push-7 { left: 58.333333333333336%; } .col-md-push-6 { left: 50%; } .col-md-push-5 { left: 41.66666666666667%; } .col-md-push-4 { left: 33.33333333333333%; } .col-md-push-3 { left: 25%; } .col-md-push-2 { left: 16.666666666666664%; } .col-md-push-1 { left: 8.333333333333332%; } .col-md-push-0 { left: 0; } .col-md-offset-12 { margin-left: 100%; } .col-md-offset-11 { margin-left: 91.66666666666666%; } .col-md-offset-10 { margin-left: 83.33333333333334%; } .col-md-offset-9 { margin-left: 75%; } .col-md-offset-8 { margin-left: 66.66666666666666%; } .col-md-offset-7 { margin-left: 58.333333333333336%; } .col-md-offset-6 { margin-left: 50%; } .col-md-offset-5 { margin-left: 41.66666666666667%; } .col-md-offset-4 { margin-left: 33.33333333333333%; } .col-md-offset-3 { margin-left: 25%; } .col-md-offset-2 { margin-left: 16.666666666666664%; } .col-md-offset-1 { margin-left: 8.333333333333332%; } .col-md-offset-0 { margin-left: 0; } } @media (min-width: 1200px) { .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 { float: left; } .col-lg-12 { width: 100%; } .col-lg-11 { width: 91.66666666666666%; } .col-lg-10 { width: 83.33333333333334%; } .col-lg-9 { width: 75%; } .col-lg-8 { width: 66.66666666666666%; } .col-lg-7 { width: 58.333333333333336%; } .col-lg-6 { width: 50%; } .col-lg-5 { width: 41.66666666666667%; } .col-lg-4 { width: 33.33333333333333%; } .col-lg-3 { width: 25%; } .col-lg-2 { width: 16.666666666666664%; } .col-lg-1 { width: 8.333333333333332%; } .col-lg-pull-12 { right: 100%; } .col-lg-pull-11 { right: 91.66666666666666%; } .col-lg-pull-10 { right: 83.33333333333334%; } .col-lg-pull-9 { right: 75%; } .col-lg-pull-8 { right: 66.66666666666666%; } .col-lg-pull-7 { right: 58.333333333333336%; } .col-lg-pull-6 { right: 50%; } .col-lg-pull-5 { right: 41.66666666666667%; } .col-lg-pull-4 { right: 33.33333333333333%; } .col-lg-pull-3 { right: 25%; } .col-lg-pull-2 { right: 16.666666666666664%; } .col-lg-pull-1 { right: 8.333333333333332%; } .col-lg-pull-0 { right: 0; } .col-lg-push-12 { left: 100%; } .col-lg-push-11 { left: 91.66666666666666%; } .col-lg-push-10 { left: 83.33333333333334%; } .col-lg-push-9 { left: 75%; } .col-lg-push-8 { left: 66.66666666666666%; } .col-lg-push-7 { left: 58.333333333333336%; } .col-lg-push-6 { left: 50%; } .col-lg-push-5 { left: 41.66666666666667%; } .col-lg-push-4 { left: 33.33333333333333%; } .col-lg-push-3 { left: 25%; } .col-lg-push-2 { left: 16.666666666666664%; } .col-lg-push-1 { left: 8.333333333333332%; } .col-lg-push-0 { left: 0; } .col-lg-offset-12 { margin-left: 100%; } .col-lg-offset-11 { margin-left: 91.66666666666666%; } .col-lg-offset-10 { margin-left: 83.33333333333334%; } .col-lg-offset-9 { margin-left: 75%; } .col-lg-offset-8 { margin-left: 66.66666666666666%; } .col-lg-offset-7 { margin-left: 58.333333333333336%; } .col-lg-offset-6 { margin-left: 50%; } .col-lg-offset-5 { margin-left: 41.66666666666667%; } .col-lg-offset-4 { margin-left: 33.33333333333333%; } .col-lg-offset-3 { margin-left: 25%; } .col-lg-offset-2 { margin-left: 16.666666666666664%; } .col-lg-offset-1 { margin-left: 8.333333333333332%; } .col-lg-offset-0 { margin-left: 0; } } table { max-width: 100%; background-color: transparent; } th { text-align: left; } .table { width: 100%; margin-bottom: 20px; } .table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td { padding: 8px; line-height: 1.428571429; vertical-align: top; border-top: 1px solid #dddddd; } .table > thead > tr > th { vertical-align: bottom; border-bottom: 2px solid #dddddd; } .table > caption + thead > tr:first-child > th, .table > colgroup + thead > tr:first-child > th, .table > thead:first-child > tr:first-child > th, .table > caption + thead > tr:first-child > td, .table > colgroup + thead > tr:first-child > td, .table > thead:first-child > tr:first-child > td { border-top: 0; } .table > tbody + tbody { border-top: 2px solid #dddddd; } .table .table { background-color: #ffffff; } .table-condensed > thead > tr > th, .table-condensed > tbody > tr > th, .table-condensed > tfoot > tr > th, .table-condensed > thead > tr > td, .table-condensed > tbody > tr > td, .table-condensed > tfoot > tr > td { padding: 5px; } .table-bordered { border: 1px solid #dddddd; } .table-bordered > thead > tr > th, .table-bordered > tbody > tr > th, .table-bordered > tfoot > tr > th, .table-bordered > thead > tr > td, .table-bordered > tbody > tr > td, .table-bordered > tfoot > tr > td { border: 1px solid #dddddd; } .table-bordered > thead > tr > th, .table-bordered > thead > tr > td { border-bottom-width: 2px; } .table-striped > tbody > tr:nth-child(odd) > td, .table-striped > tbody > tr:nth-child(odd) > th { background-color: #f9f9f9; } .table-hover > tbody > tr:hover > td, .table-hover > tbody > tr:hover > th { background-color: #f5f5f5; } table col[class*="col-"] { position: static; display: table-column; float: none; } table td[class*="col-"], table th[class*="col-"] { display: table-cell; float: none; } .table > thead > tr > .active, .table > tbody > tr > .active, .table > tfoot > tr > .active, .table > thead > .active > td, .table > tbody > .active > td, .table > tfoot > .active > td, .table > thead > .active > th, .table > tbody > .active > th, .table > tfoot > .active > th { background-color: #f5f5f5; } .table-hover > tbody > tr > .active:hover, .table-hover > tbody > .active:hover > td, .table-hover > tbody > .active:hover > th { background-color: #e8e8e8; } .table > thead > tr > .success, .table > tbody > tr > .success, .table > tfoot > tr > .success, .table > thead > .success > td, .table > tbody > .success > td, .table > tfoot > .success > td, .table > thead > .success > th, .table > tbody > .success > th, .table > tfoot > .success > th { background-color: #dff0d8; } .table-hover > tbody > tr > .success:hover, .table-hover > tbody > .success:hover > td, .table-hover > tbody > .success:hover > th { background-color: #d0e9c6; } .table > thead > tr > .danger, .table > tbody > tr > .danger, .table > tfoot > tr > .danger, .table > thead > .danger > td, .table > tbody > .danger > td, .table > tfoot > .danger > td, .table > thead > .danger > th, .table > tbody > .danger > th, .table > tfoot > .danger > th { background-color: #f2dede; } .table-hover > tbody > tr > .danger:hover, .table-hover > tbody > .danger:hover > td, .table-hover > tbody > .danger:hover > th { background-color: #ebcccc; } .table > thead > tr > .warning, .table > tbody > tr > .warning, .table > tfoot > tr > .warning, .table > thead > .warning > td, .table > tbody > .warning > td, .table > tfoot > .warning > td, .table > thead > .warning > th, .table > tbody > .warning > th, .table > tfoot > .warning > th { background-color: #fcf8e3; } .table-hover > tbody > tr > .warning:hover, .table-hover > tbody > .warning:hover > td, .table-hover > tbody > .warning:hover > th { background-color: #faf2cc; } @media (max-width: 767px) { .table-responsive { width: 100%; margin-bottom: 15px; overflow-x: scroll; overflow-y: hidden; border: 1px solid #dddddd; -ms-overflow-style: -ms-autohiding-scrollbar; -webkit-overflow-scrolling: touch; } .table-responsive > .table { margin-bottom: 0; } .table-responsive > .table > thead > tr > th, .table-responsive > .table > tbody > tr > th, .table-responsive > .table > tfoot > tr > th, .table-responsive > .table > thead > tr > td, .table-responsive > .table > tbody > tr > td, .table-responsive > .table > tfoot > tr > td { white-space: nowrap; } .table-responsive > .table-bordered { border: 0; } .table-responsive > .table-bordered > thead > tr > th:first-child, .table-responsive > .table-bordered > tbody > tr > th:first-child, .table-responsive > .table-bordered > tfoot > tr > th:first-child, .table-responsive > .table-bordered > thead > tr > td:first-child, .table-responsive > .table-bordered > tbody > tr > td:first-child, .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .table-responsive > .table-bordered > thead > tr > th:last-child, .table-responsive > .table-bordered > tbody > tr > th:last-child, .table-responsive > .table-bordered > tfoot > tr > th:last-child, .table-responsive > .table-bordered > thead > tr > td:last-child, .table-responsive > .table-bordered > tbody > tr > td:last-child, .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .table-responsive > .table-bordered > tbody > tr:last-child > th, .table-responsive > .table-bordered > tfoot > tr:last-child > th, .table-responsive > .table-bordered > tbody > tr:last-child > td, .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } } fieldset { padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; margin-bottom: 20px; font-size: 21px; line-height: inherit; color: #333333; border: 0; border-bottom: 1px solid #e5e5e5; } label { display: inline-block; margin-bottom: 5px; font-weight: bold; } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } input[type="radio"], input[type="checkbox"] { margin: 4px 0 0; margin-top: 1px \9; /* IE8-9 */ line-height: normal; } input[type="file"] { display: block; } select[multiple], select[size] { height: auto; } select optgroup { font-family: inherit; font-size: inherit; font-style: inherit; } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } input[type="number"]::-webkit-outer-spin-button, input[type="number"]::-webkit-inner-spin-button { height: auto; } output { display: block; padding-top: 7px; font-size: 14px; line-height: 1.428571429; color: #555555; vertical-align: middle; } .form-control { display: block; width: 100%; height: 34px; padding: 6px 12px; font-size: 14px; line-height: 1.428571429; color: #555555; vertical-align: middle; background-color: #ffffff; background-image: none; border: 1px solid #cccccc; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); -webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; } .form-control:focus { border-color: #66afe9; outline: 0; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6); } .form-control:-moz-placeholder { color: #999999; } .form-control::-moz-placeholder { color: #999999; opacity: 1; } .form-control:-ms-input-placeholder { color: #999999; } .form-control::-webkit-input-placeholder { color: #999999; } .form-control[disabled], .form-control[readonly], fieldset[disabled] .form-control { cursor: not-allowed; background-color: #eeeeee; } textarea.form-control { height: auto; } .form-group { margin-bottom: 15px; } .radio, .checkbox { display: block; min-height: 20px; padding-left: 20px; margin-top: 10px; margin-bottom: 10px; vertical-align: middle; } .radio label, .checkbox label { display: inline; margin-bottom: 0; font-weight: normal; cursor: pointer; } .radio input[type="radio"], .radio-inline input[type="radio"], .checkbox input[type="checkbox"], .checkbox-inline input[type="checkbox"] { float: left; margin-left: -20px; } .radio + .radio, .checkbox + .checkbox { margin-top: -5px; } .radio-inline, .checkbox-inline { display: inline-block; padding-left: 20px; margin-bottom: 0; font-weight: normal; vertical-align: middle; cursor: pointer; } .radio-inline + .radio-inline, .checkbox-inline + .checkbox-inline { margin-top: 0; margin-left: 10px; } input[type="radio"][disabled], input[type="checkbox"][disabled], .radio[disabled], .radio-inline[disabled], .checkbox[disabled], .checkbox-inline[disabled], fieldset[disabled] input[type="radio"], fieldset[disabled] input[type="checkbox"], fieldset[disabled] .radio, fieldset[disabled] .radio-inline, fieldset[disabled] .checkbox, fieldset[disabled] .checkbox-inline { cursor: not-allowed; } .input-sm { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-sm { height: 30px; line-height: 30px; } textarea.input-sm { height: auto; } .input-lg { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-lg { height: 46px; line-height: 46px; } textarea.input-lg { height: auto; } .has-warning .help-block, .has-warning .control-label, .has-warning .radio, .has-warning .checkbox, .has-warning .radio-inline, .has-warning .checkbox-inline { color: #8a6d3b; } .has-warning .form-control { border-color: #8a6d3b; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-warning .form-control:focus { border-color: #66512c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b; } .has-warning .input-group-addon { color: #8a6d3b; background-color: #fcf8e3; border-color: #8a6d3b; } .has-error .help-block, .has-error .control-label, .has-error .radio, .has-error .checkbox, .has-error .radio-inline, .has-error .checkbox-inline { color: #a94442; } .has-error .form-control { border-color: #a94442; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-error .form-control:focus { border-color: #843534; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483; } .has-error .input-group-addon { color: #a94442; background-color: #f2dede; border-color: #a94442; } .has-success .help-block, .has-success .control-label, .has-success .radio, .has-success .checkbox, .has-success .radio-inline, .has-success .checkbox-inline { color: #3c763d; } .has-success .form-control { border-color: #3c763d; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); } .has-success .form-control:focus { border-color: #2b542c; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168; } .has-success .input-group-addon { color: #3c763d; background-color: #dff0d8; border-color: #3c763d; } .form-control-static { margin-bottom: 0; } .help-block { display: block; margin-top: 5px; margin-bottom: 10px; color: #737373; } @media (min-width: 768px) { .form-inline .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .form-inline .form-control { display: inline-block; } .form-inline select.form-control { width: auto; } .form-inline .radio, .form-inline .checkbox { display: inline-block; padding-left: 0; margin-top: 0; margin-bottom: 0; } .form-inline .radio input[type="radio"], .form-inline .checkbox input[type="checkbox"] { float: none; margin-left: 0; } } .form-horizontal .control-label, .form-horizontal .radio, .form-horizontal .checkbox, .form-horizontal .radio-inline, .form-horizontal .checkbox-inline { padding-top: 7px; margin-top: 0; margin-bottom: 0; } .form-horizontal .radio, .form-horizontal .checkbox { min-height: 27px; } .form-horizontal .form-group { margin-right: -15px; margin-left: -15px; } .form-horizontal .form-group:before, .form-horizontal .form-group:after { display: table; content: " "; } .form-horizontal .form-group:after { clear: both; } .form-horizontal .form-group:before, .form-horizontal .form-group:after { display: table; content: " "; } .form-horizontal .form-group:after { clear: both; } .form-horizontal .form-control-static { padding-top: 7px; } @media (min-width: 768px) { .form-horizontal .control-label { text-align: right; } } .btn { display: inline-block; padding: 6px 12px; margin-bottom: 0; font-size: 14px; font-weight: normal; line-height: 1.428571429; text-align: center; white-space: nowrap; vertical-align: middle; cursor: pointer; background-image: none; border: 1px solid transparent; border-radius: 4px; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; -o-user-select: none; user-select: none; } .btn:focus { outline: thin dotted; outline: 5px auto -webkit-focus-ring-color; outline-offset: -2px; } .btn:hover, .btn:focus { color: #333333; text-decoration: none; } .btn:active, .btn.active { background-image: none; outline: 0; -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn.disabled, .btn[disabled], fieldset[disabled] .btn { pointer-events: none; cursor: not-allowed; opacity: 0.65; filter: alpha(opacity=65); -webkit-box-shadow: none; box-shadow: none; } .btn-default { color: #333333; background-color: #ffffff; border-color: #cccccc; } .btn-default:hover, .btn-default:focus, .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default { color: #333333; background-color: #ebebeb; border-color: #adadad; } .btn-default:active, .btn-default.active, .open .dropdown-toggle.btn-default { background-image: none; } .btn-default.disabled, .btn-default[disabled], fieldset[disabled] .btn-default, .btn-default.disabled:hover, .btn-default[disabled]:hover, fieldset[disabled] .btn-default:hover, .btn-default.disabled:focus, .btn-default[disabled]:focus, fieldset[disabled] .btn-default:focus, .btn-default.disabled:active, .btn-default[disabled]:active, fieldset[disabled] .btn-default:active, .btn-default.disabled.active, .btn-default[disabled].active, fieldset[disabled] .btn-default.active { background-color: #ffffff; border-color: #cccccc; } .btn-default .badge { color: #ffffff; background-color: #fff; } .btn-primary { color: #ffffff; background-color: #428bca; border-color: #357ebd; } .btn-primary:hover, .btn-primary:focus, .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary { color: #ffffff; background-color: #3276b1; border-color: #285e8e; } .btn-primary:active, .btn-primary.active, .open .dropdown-toggle.btn-primary { background-image: none; } .btn-primary.disabled, .btn-primary[disabled], fieldset[disabled] .btn-primary, .btn-primary.disabled:hover, .btn-primary[disabled]:hover, fieldset[disabled] .btn-primary:hover, .btn-primary.disabled:focus, .btn-primary[disabled]:focus, fieldset[disabled] .btn-primary:focus, .btn-primary.disabled:active, .btn-primary[disabled]:active, fieldset[disabled] .btn-primary:active, .btn-primary.disabled.active, .btn-primary[disabled].active, fieldset[disabled] .btn-primary.active { background-color: #428bca; border-color: #357ebd; } .btn-primary .badge { color: #428bca; background-color: #fff; } .btn-warning { color: #ffffff; background-color: #f0ad4e; border-color: #eea236; } .btn-warning:hover, .btn-warning:focus, .btn-warning:active, .btn-warning.active, .open .dropdown-toggle.btn-warning { color: #ffffff; background-color: #ed9c28; border-color: #d58512; } .btn-warning:active, .btn-warning.active, .open .dropdown-toggle.btn-warning { background-image: none; } .btn-warning.disabled, .btn-warning[disabled], fieldset[disabled] .btn-warning, .btn-warning.disabled:hover, .btn-warning[disabled]:hover, fieldset[disabled] .btn-warning:hover, .btn-warning.disabled:focus, .btn-warning[disabled]:focus, fieldset[disabled] .btn-warning:focus, .btn-warning.disabled:active, .btn-warning[disabled]:active, fieldset[disabled] .btn-warning:active, .btn-warning.disabled.active, .btn-warning[disabled].active, fieldset[disabled] .btn-warning.active { background-color: #f0ad4e; border-color: #eea236; } .btn-warning .badge { color: #f0ad4e; background-color: #fff; } .btn-danger { color: #ffffff; background-color: #d9534f; border-color: #d43f3a; } .btn-danger:hover, .btn-danger:focus, .btn-danger:active, .btn-danger.active, .open .dropdown-toggle.btn-danger { color: #ffffff; background-color: #d2322d; border-color: #ac2925; } .btn-danger:active, .btn-danger.active, .open .dropdown-toggle.btn-danger { background-image: none; } .btn-danger.disabled, .btn-danger[disabled], fieldset[disabled] .btn-danger, .btn-danger.disabled:hover, .btn-danger[disabled]:hover, fieldset[disabled] .btn-danger:hover, .btn-danger.disabled:focus, .btn-danger[disabled]:focus, fieldset[disabled] .btn-danger:focus, .btn-danger.disabled:active, .btn-danger[disabled]:active, fieldset[disabled] .btn-danger:active, .btn-danger.disabled.active, .btn-danger[disabled].active, fieldset[disabled] .btn-danger.active { background-color: #d9534f; border-color: #d43f3a; } .btn-danger .badge { color: #d9534f; background-color: #fff; } .btn-success { color: #ffffff; background-color: #5cb85c; border-color: #4cae4c; } .btn-success:hover, .btn-success:focus, .btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success { color: #ffffff; background-color: #47a447; border-color: #398439; } .btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success { background-image: none; } .btn-success.disabled, .btn-success[disabled], fieldset[disabled] .btn-success, .btn-success.disabled:hover, .btn-success[disabled]:hover, fieldset[disabled] .btn-success:hover, .btn-success.disabled:focus, .btn-success[disabled]:focus, fieldset[disabled] .btn-success:focus, .btn-success.disabled:active, .btn-success[disabled]:active, fieldset[disabled] .btn-success:active, .btn-success.disabled.active, .btn-success[disabled].active, fieldset[disabled] .btn-success.active { background-color: #5cb85c; border-color: #4cae4c; } .btn-success .badge { color: #5cb85c; background-color: #fff; } .btn-info { color: #ffffff; background-color: #5bc0de; border-color: #46b8da; } .btn-info:hover, .btn-info:focus, .btn-info:active, .btn-info.active, .open .dropdown-toggle.btn-info { color: #ffffff; background-color: #39b3d7; border-color: #269abc; } .btn-info:active, .btn-info.active, .open .dropdown-toggle.btn-info { background-image: none; } .btn-info.disabled, .btn-info[disabled], fieldset[disabled] .btn-info, .btn-info.disabled:hover, .btn-info[disabled]:hover, fieldset[disabled] .btn-info:hover, .btn-info.disabled:focus, .btn-info[disabled]:focus, fieldset[disabled] .btn-info:focus, .btn-info.disabled:active, .btn-info[disabled]:active, fieldset[disabled] .btn-info:active, .btn-info.disabled.active, .btn-info[disabled].active, fieldset[disabled] .btn-info.active { background-color: #5bc0de; border-color: #46b8da; } .btn-info .badge { color: #5bc0de; background-color: #fff; } .btn-link { font-weight: normal; color: #428bca; cursor: pointer; border-radius: 0; } .btn-link, .btn-link:active, .btn-link[disabled], fieldset[disabled] .btn-link { background-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { border-color: transparent; } .btn-link:hover, .btn-link:focus { color: #2a6496; text-decoration: underline; background-color: transparent; } .btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover, .btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus { color: #999999; text-decoration: none; } .btn-lg { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-sm { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-xs { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-block { display: block; width: 100%; padding-right: 0; padding-left: 0; } .btn-block + .btn-block { margin-top: 5px; } input[type="submit"].btn-block, input[type="reset"].btn-block, input[type="button"].btn-block { width: 100%; } .fade { opacity: 0; -webkit-transition: opacity 0.15s linear; transition: opacity 0.15s linear; } .fade.in { opacity: 1; } .collapse { display: none; } .collapse.in { display: block; } .collapsing { position: relative; height: 0; overflow: hidden; -webkit-transition: height 0.35s ease; transition: height 0.35s ease; } @font-face { font-family: 'Glyphicons Halflings'; src: url('../fonts/glyphicons-halflings-regular.eot'); src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons-halflingsregular') format('svg'); } .glyphicon { position: relative; top: 1px; display: inline-block; font-family: 'Glyphicons Halflings'; -webkit-font-smoothing: antialiased; font-style: normal; font-weight: normal; line-height: 1; -moz-osx-font-smoothing: grayscale; } .glyphicon:empty { width: 1em; } .glyphicon-asterisk:before { content: "\2a"; } .glyphicon-plus:before { content: "\2b"; } .glyphicon-euro:before { content: "\20ac"; } .glyphicon-minus:before { content: "\2212"; } .glyphicon-cloud:before { content: "\2601"; } .glyphicon-envelope:before { content: "\2709"; } .glyphicon-pencil:before { content: "\270f"; } .glyphicon-glass:before { content: "\e001"; } .glyphicon-music:before { content: "\e002"; } .glyphicon-search:before { content: "\e003"; } .glyphicon-heart:before { content: "\e005"; } .glyphicon-star:before { content: "\e006"; } .glyphicon-star-empty:before { content: "\e007"; } .glyphicon-user:before { content: "\e008"; } .glyphicon-film:before { content: "\e009"; } .glyphicon-th-large:before { content: "\e010"; } .glyphicon-th:before { content: "\e011"; } .glyphicon-th-list:before { content: "\e012"; } .glyphicon-ok:before { content: "\e013"; } .glyphicon-remove:before { content: "\e014"; } .glyphicon-zoom-in:before { content: "\e015"; } .glyphicon-zoom-out:before { content: "\e016"; } .glyphicon-off:before { content: "\e017"; } .glyphicon-signal:before { content: "\e018"; } .glyphicon-cog:before { content: "\e019"; } .glyphicon-trash:before { content: "\e020"; } .glyphicon-home:before { content: "\e021"; } .glyphicon-file:before { content: "\e022"; } .glyphicon-time:before { content: "\e023"; } .glyphicon-road:before { content: "\e024"; } .glyphicon-download-alt:before { content: "\e025"; } .glyphicon-download:before { content: "\e026"; } .glyphicon-upload:before { content: "\e027"; } .glyphicon-inbox:before { content: "\e028"; } .glyphicon-play-circle:before { content: "\e029"; } .glyphicon-repeat:before { content: "\e030"; } .glyphicon-refresh:before { content: "\e031"; } .glyphicon-list-alt:before { content: "\e032"; } .glyphicon-lock:before { content: "\e033"; } .glyphicon-flag:before { content: "\e034"; } .glyphicon-headphones:before { content: "\e035"; } .glyphicon-volume-off:before { content: "\e036"; } .glyphicon-volume-down:before { content: "\e037"; } .glyphicon-volume-up:before { content: "\e038"; } .glyphicon-qrcode:before { content: "\e039"; } .glyphicon-barcode:before { content: "\e040"; } .glyphicon-tag:before { content: "\e041"; } .glyphicon-tags:before { content: "\e042"; } .glyphicon-book:before { content: "\e043"; } .glyphicon-bookmark:before { content: "\e044"; } .glyphicon-print:before { content: "\e045"; } .glyphicon-camera:before { content: "\e046"; } .glyphicon-font:before { content: "\e047"; } .glyphicon-bold:before { content: "\e048"; } .glyphicon-italic:before { content: "\e049"; } .glyphicon-text-height:before { content: "\e050"; } .glyphicon-text-width:before { content: "\e051"; } .glyphicon-align-left:before { content: "\e052"; } .glyphicon-align-center:before { content: "\e053"; } .glyphicon-align-right:before { content: "\e054"; } .glyphicon-align-justify:before { content: "\e055"; } .glyphicon-list:before { content: "\e056"; } .glyphicon-indent-left:before { content: "\e057"; } .glyphicon-indent-right:before { content: "\e058"; } .glyphicon-facetime-video:before { content: "\e059"; } .glyphicon-picture:before { content: "\e060"; } .glyphicon-map-marker:before { content: "\e062"; } .glyphicon-adjust:before { content: "\e063"; } .glyphicon-tint:before { content: "\e064"; } .glyphicon-edit:before { content: "\e065"; } .glyphicon-share:before { content: "\e066"; } .glyphicon-check:before { content: "\e067"; } .glyphicon-move:before { content: "\e068"; } .glyphicon-step-backward:before { content: "\e069"; } .glyphicon-fast-backward:before { content: "\e070"; } .glyphicon-backward:before { content: "\e071"; } .glyphicon-play:before { content: "\e072"; } .glyphicon-pause:before { content: "\e073"; } .glyphicon-stop:before { content: "\e074"; } .glyphicon-forward:before { content: "\e075"; } .glyphicon-fast-forward:before { content: "\e076"; } .glyphicon-step-forward:before { content: "\e077"; } .glyphicon-eject:before { content: "\e078"; } .glyphicon-chevron-left:before { content: "\e079"; } .glyphicon-chevron-right:before { content: "\e080"; } .glyphicon-plus-sign:before { content: "\e081"; } .glyphicon-minus-sign:before { content: "\e082"; } .glyphicon-remove-sign:before { content: "\e083"; } .glyphicon-ok-sign:before { content: "\e084"; } .glyphicon-question-sign:before { content: "\e085"; } .glyphicon-info-sign:before { content: "\e086"; } .glyphicon-screenshot:before { content: "\e087"; } .glyphicon-remove-circle:before { content: "\e088"; } .glyphicon-ok-circle:before { content: "\e089"; } .glyphicon-ban-circle:before { content: "\e090"; } .glyphicon-arrow-left:before { content: "\e091"; } .glyphicon-arrow-right:before { content: "\e092"; } .glyphicon-arrow-up:before { content: "\e093"; } .glyphicon-arrow-down:before { content: "\e094"; } .glyphicon-share-alt:before { content: "\e095"; } .glyphicon-resize-full:before { content: "\e096"; } .glyphicon-resize-small:before { content: "\e097"; } .glyphicon-exclamation-sign:before { content: "\e101"; } .glyphicon-gift:before { content: "\e102"; } .glyphicon-leaf:before { content: "\e103"; } .glyphicon-fire:before { content: "\e104"; } .glyphicon-eye-open:before { content: "\e105"; } .glyphicon-eye-close:before { content: "\e106"; } .glyphicon-warning-sign:before { content: "\e107"; } .glyphicon-plane:before { content: "\e108"; } .glyphicon-calendar:before { content: "\e109"; } .glyphicon-random:before { content: "\e110"; } .glyphicon-comment:before { content: "\e111"; } .glyphicon-magnet:before { content: "\e112"; } .glyphicon-chevron-up:before { content: "\e113"; } .glyphicon-chevron-down:before { content: "\e114"; } .glyphicon-retweet:before { content: "\e115"; } .glyphicon-shopping-cart:before { content: "\e116"; } .glyphicon-folder-close:before { content: "\e117"; } .glyphicon-folder-open:before { content: "\e118"; } .glyphicon-resize-vertical:before { content: "\e119"; } .glyphicon-resize-horizontal:before { content: "\e120"; } .glyphicon-hdd:before { content: "\e121"; } .glyphicon-bullhorn:before { content: "\e122"; } .glyphicon-bell:before { content: "\e123"; } .glyphicon-certificate:before { content: "\e124"; } .glyphicon-thumbs-up:before { content: "\e125"; } .glyphicon-thumbs-down:before { content: "\e126"; } .glyphicon-hand-right:before { content: "\e127"; } .glyphicon-hand-left:before { content: "\e128"; } .glyphicon-hand-up:before { content: "\e129"; } .glyphicon-hand-down:before { content: "\e130"; } .glyphicon-circle-arrow-right:before { content: "\e131"; } .glyphicon-circle-arrow-left:before { content: "\e132"; } .glyphicon-circle-arrow-up:before { content: "\e133"; } .glyphicon-circle-arrow-down:before { content: "\e134"; } .glyphicon-globe:before { content: "\e135"; } .glyphicon-wrench:before { content: "\e136"; } .glyphicon-tasks:before { content: "\e137"; } .glyphicon-filter:before { content: "\e138"; } .glyphicon-briefcase:before { content: "\e139"; } .glyphicon-fullscreen:before { content: "\e140"; } .glyphicon-dashboard:before { content: "\e141"; } .glyphicon-paperclip:before { content: "\e142"; } .glyphicon-heart-empty:before { content: "\e143"; } .glyphicon-link:before { content: "\e144"; } .glyphicon-phone:before { content: "\e145"; } .glyphicon-pushpin:before { content: "\e146"; } .glyphicon-usd:before { content: "\e148"; } .glyphicon-gbp:before { content: "\e149"; } .glyphicon-sort:before { content: "\e150"; } .glyphicon-sort-by-alphabet:before { content: "\e151"; } .glyphicon-sort-by-alphabet-alt:before { content: "\e152"; } .glyphicon-sort-by-order:before { content: "\e153"; } .glyphicon-sort-by-order-alt:before { content: "\e154"; } .glyphicon-sort-by-attributes:before { content: "\e155"; } .glyphicon-sort-by-attributes-alt:before { content: "\e156"; } .glyphicon-unchecked:before { content: "\e157"; } .glyphicon-expand:before { content: "\e158"; } .glyphicon-collapse-down:before { content: "\e159"; } .glyphicon-collapse-up:before { content: "\e160"; } .glyphicon-log-in:before { content: "\e161"; } .glyphicon-flash:before { content: "\e162"; } .glyphicon-log-out:before { content: "\e163"; } .glyphicon-new-window:before { content: "\e164"; } .glyphicon-record:before { content: "\e165"; } .glyphicon-save:before { content: "\e166"; } .glyphicon-open:before { content: "\e167"; } .glyphicon-saved:before { content: "\e168"; } .glyphicon-import:before { content: "\e169"; } .glyphicon-export:before { content: "\e170"; } .glyphicon-send:before { content: "\e171"; } .glyphicon-floppy-disk:before { content: "\e172"; } .glyphicon-floppy-saved:before { content: "\e173"; } .glyphicon-floppy-remove:before { content: "\e174"; } .glyphicon-floppy-save:before { content: "\e175"; } .glyphicon-floppy-open:before { content: "\e176"; } .glyphicon-credit-card:before { content: "\e177"; } .glyphicon-transfer:before { content: "\e178"; } .glyphicon-cutlery:before { content: "\e179"; } .glyphicon-header:before { content: "\e180"; } .glyphicon-compressed:before { content: "\e181"; } .glyphicon-earphone:before { content: "\e182"; } .glyphicon-phone-alt:before { content: "\e183"; } .glyphicon-tower:before { content: "\e184"; } .glyphicon-stats:before { content: "\e185"; } .glyphicon-sd-video:before { content: "\e186"; } .glyphicon-hd-video:before { content: "\e187"; } .glyphicon-subtitles:before { content: "\e188"; } .glyphicon-sound-stereo:before { content: "\e189"; } .glyphicon-sound-dolby:before { content: "\e190"; } .glyphicon-sound-5-1:before { content: "\e191"; } .glyphicon-sound-6-1:before { content: "\e192"; } .glyphicon-sound-7-1:before { content: "\e193"; } .glyphicon-copyright-mark:before { content: "\e194"; } .glyphicon-registration-mark:before { content: "\e195"; } .glyphicon-cloud-download:before { content: "\e197"; } .glyphicon-cloud-upload:before { content: "\e198"; } .glyphicon-tree-conifer:before { content: "\e199"; } .glyphicon-tree-deciduous:before { content: "\e200"; } .caret { display: inline-block; width: 0; height: 0; margin-left: 2px; vertical-align: middle; border-top: 4px solid; border-right: 4px solid transparent; border-left: 4px solid transparent; } .dropdown { position: relative; } .dropdown-toggle:focus { outline: 0; } .dropdown-menu { position: absolute; top: 100%; left: 0; z-index: 1000; display: none; float: left; min-width: 160px; padding: 5px 0; margin: 2px 0 0; font-size: 14px; list-style: none; background-color: #ffffff; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.15); border-radius: 4px; -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175); background-clip: padding-box; } .dropdown-menu.pull-right { right: 0; left: auto; } .dropdown-menu .divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .dropdown-menu > li > a { display: block; padding: 3px 20px; clear: both; font-weight: normal; line-height: 1.428571429; color: #333333; white-space: nowrap; } .dropdown-menu > li > a:hover, .dropdown-menu > li > a:focus { color: #262626; text-decoration: none; background-color: #f5f5f5; } .dropdown-menu > .active > a, .dropdown-menu > .active > a:hover, .dropdown-menu > .active > a:focus { color: #ffffff; text-decoration: none; background-color: #428bca; outline: 0; } .dropdown-menu > .disabled > a, .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { color: #999999; } .dropdown-menu > .disabled > a:hover, .dropdown-menu > .disabled > a:focus { text-decoration: none; cursor: not-allowed; background-color: transparent; background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); } .open > .dropdown-menu { display: block; } .open > a { outline: 0; } .dropdown-header { display: block; padding: 3px 20px; font-size: 12px; line-height: 1.428571429; color: #999999; } .dropdown-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 990; } .pull-right > .dropdown-menu { right: 0; left: auto; } .dropup .caret, .navbar-fixed-bottom .dropdown .caret { border-top: 0; border-bottom: 4px solid; content: ""; } .dropup .dropdown-menu, .navbar-fixed-bottom .dropdown .dropdown-menu { top: auto; bottom: 100%; margin-bottom: 1px; } @media (min-width: 768px) { .navbar-right .dropdown-menu { right: 0; left: auto; } } .btn-group, .btn-group-vertical { position: relative; display: inline-block; vertical-align: middle; } .btn-group > .btn, .btn-group-vertical > .btn { position: relative; float: left; } .btn-group > .btn:hover, .btn-group-vertical > .btn:hover, .btn-group > .btn:focus, .btn-group-vertical > .btn:focus, .btn-group > .btn:active, .btn-group-vertical > .btn:active, .btn-group > .btn.active, .btn-group-vertical > .btn.active { z-index: 2; } .btn-group > .btn:focus, .btn-group-vertical > .btn:focus { outline: none; } .btn-group .btn + .btn, .btn-group .btn + .btn-group, .btn-group .btn-group + .btn, .btn-group .btn-group + .btn-group { margin-left: -1px; } .btn-toolbar:before, .btn-toolbar:after { display: table; content: " "; } .btn-toolbar:after { clear: both; } .btn-toolbar:before, .btn-toolbar:after { display: table; content: " "; } .btn-toolbar:after { clear: both; } .btn-toolbar .btn-group { float: left; } .btn-toolbar > .btn + .btn, .btn-toolbar > .btn-group + .btn, .btn-toolbar > .btn + .btn-group, .btn-toolbar > .btn-group + .btn-group { margin-left: 5px; } .btn-group > .btn:not(:first-child):not(:last-child):not(.dropdown-toggle) { border-radius: 0; } .btn-group > .btn:first-child { margin-left: 0; } .btn-group > .btn:first-child:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn:last-child:not(:first-child), .btn-group > .dropdown-toggle:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group > .btn-group { float: left; } .btn-group > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group > .btn-group:first-child > .btn:last-child, .btn-group > .btn-group:first-child > .dropdown-toggle { border-top-right-radius: 0; border-bottom-right-radius: 0; } .btn-group > .btn-group:last-child > .btn:first-child { border-bottom-left-radius: 0; border-top-left-radius: 0; } .btn-group .dropdown-toggle:active, .btn-group.open .dropdown-toggle { outline: 0; } .btn-group-xs > .btn { padding: 1px 5px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-group-sm > .btn { padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } .btn-group-lg > .btn { padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } .btn-group > .btn + .dropdown-toggle { padding-right: 8px; padding-left: 8px; } .btn-group > .btn-lg + .dropdown-toggle { padding-right: 12px; padding-left: 12px; } .btn-group.open .dropdown-toggle { -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); } .btn-group.open .dropdown-toggle.btn-link { -webkit-box-shadow: none; box-shadow: none; } .btn .caret { margin-left: 0; } .btn-lg .caret { border-width: 5px 5px 0; border-bottom-width: 0; } .dropup .btn-lg .caret { border-width: 0 5px 5px; } .btn-group-vertical > .btn, .btn-group-vertical > .btn-group, .btn-group-vertical > .btn-group > .btn { display: block; float: none; width: 100%; max-width: 100%; } .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after { display: table; content: " "; } .btn-group-vertical > .btn-group:after { clear: both; } .btn-group-vertical > .btn-group:before, .btn-group-vertical > .btn-group:after { display: table; content: " "; } .btn-group-vertical > .btn-group:after { clear: both; } .btn-group-vertical > .btn-group > .btn { float: none; } .btn-group-vertical > .btn + .btn, .btn-group-vertical > .btn + .btn-group, .btn-group-vertical > .btn-group + .btn, .btn-group-vertical > .btn-group + .btn-group { margin-top: -1px; margin-left: 0; } .btn-group-vertical > .btn:not(:first-child):not(:last-child) { border-radius: 0; } .btn-group-vertical > .btn:first-child:not(:last-child) { border-top-right-radius: 4px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn:last-child:not(:first-child) { border-top-right-radius: 0; border-bottom-left-radius: 4px; border-top-left-radius: 0; } .btn-group-vertical > .btn-group:not(:first-child):not(:last-child) > .btn { border-radius: 0; } .btn-group-vertical > .btn-group:first-child > .btn:last-child, .btn-group-vertical > .btn-group:first-child > .dropdown-toggle { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .btn-group-vertical > .btn-group:last-child > .btn:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .btn-group-justified { display: table; width: 100%; border-collapse: separate; table-layout: fixed; } .btn-group-justified > .btn, .btn-group-justified > .btn-group { display: table-cell; float: none; width: 1%; } .btn-group-justified > .btn-group .btn { width: 100%; } [data-toggle="buttons"] > .btn > input[type="radio"], [data-toggle="buttons"] > .btn > input[type="checkbox"] { display: none; } .input-group { position: relative; display: table; border-collapse: separate; } .input-group[class*="col-"] { float: none; padding-right: 0; padding-left: 0; } .input-group .form-control { width: 100%; margin-bottom: 0; } .input-group-lg > .form-control, .input-group-lg > .input-group-addon, .input-group-lg > .input-group-btn > .btn { height: 46px; padding: 10px 16px; font-size: 18px; line-height: 1.33; border-radius: 6px; } select.input-group-lg > .form-control, select.input-group-lg > .input-group-addon, select.input-group-lg > .input-group-btn > .btn { height: 46px; line-height: 46px; } textarea.input-group-lg > .form-control, textarea.input-group-lg > .input-group-addon, textarea.input-group-lg > .input-group-btn > .btn { height: auto; } .input-group-sm > .form-control, .input-group-sm > .input-group-addon, .input-group-sm > .input-group-btn > .btn { height: 30px; padding: 5px 10px; font-size: 12px; line-height: 1.5; border-radius: 3px; } select.input-group-sm > .form-control, select.input-group-sm > .input-group-addon, select.input-group-sm > .input-group-btn > .btn { height: 30px; line-height: 30px; } textarea.input-group-sm > .form-control, textarea.input-group-sm > .input-group-addon, textarea.input-group-sm > .input-group-btn > .btn { height: auto; } .input-group-addon, .input-group-btn, .input-group .form-control { display: table-cell; } .input-group-addon:not(:first-child):not(:last-child), .input-group-btn:not(:first-child):not(:last-child), .input-group .form-control:not(:first-child):not(:last-child) { border-radius: 0; } .input-group-addon, .input-group-btn { width: 1%; white-space: nowrap; vertical-align: middle; } .input-group-addon { padding: 6px 12px; font-size: 14px; font-weight: normal; line-height: 1; color: #555555; text-align: center; background-color: #eeeeee; border: 1px solid #cccccc; border-radius: 4px; } .input-group-addon.input-sm { padding: 5px 10px; font-size: 12px; border-radius: 3px; } .input-group-addon.input-lg { padding: 10px 16px; font-size: 18px; border-radius: 6px; } .input-group-addon input[type="radio"], .input-group-addon input[type="checkbox"] { margin-top: 0; } .input-group .form-control:first-child, .input-group-addon:first-child, .input-group-btn:first-child > .btn, .input-group-btn:first-child > .dropdown-toggle, .input-group-btn:last-child > .btn:not(:last-child):not(.dropdown-toggle) { border-top-right-radius: 0; border-bottom-right-radius: 0; } .input-group-addon:first-child { border-right: 0; } .input-group .form-control:last-child, .input-group-addon:last-child, .input-group-btn:last-child > .btn, .input-group-btn:last-child > .dropdown-toggle, .input-group-btn:first-child > .btn:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .input-group-addon:last-child { border-left: 0; } .input-group-btn { position: relative; white-space: nowrap; } .input-group-btn:first-child > .btn { margin-right: -1px; } .input-group-btn:last-child > .btn { margin-left: -1px; } .input-group-btn > .btn { position: relative; } .input-group-btn > .btn + .btn { margin-left: -4px; } .input-group-btn > .btn:hover, .input-group-btn > .btn:active { z-index: 2; } .nav { padding-left: 0; margin-bottom: 0; list-style: none; } .nav:before, .nav:after { display: table; content: " "; } .nav:after { clear: both; } .nav:before, .nav:after { display: table; content: " "; } .nav:after { clear: both; } .nav > li { position: relative; display: block; } .nav > li > a { position: relative; display: block; padding: 10px 15px; } .nav > li > a:hover, .nav > li > a:focus { text-decoration: none; background-color: #eeeeee; } .nav > li.disabled > a { color: #999999; } .nav > li.disabled > a:hover, .nav > li.disabled > a:focus { color: #999999; text-decoration: none; cursor: not-allowed; background-color: transparent; } .nav .open > a, .nav .open > a:hover, .nav .open > a:focus { background-color: #eeeeee; border-color: #428bca; } .nav .nav-divider { height: 1px; margin: 9px 0; overflow: hidden; background-color: #e5e5e5; } .nav > li > a > img { max-width: none; } .nav-tabs { border-bottom: 1px solid #dddddd; } .nav-tabs > li { float: left; margin-bottom: -1px; } .nav-tabs > li > a { margin-right: 2px; line-height: 1.428571429; border: 1px solid transparent; border-radius: 4px 4px 0 0; } .nav-tabs > li > a:hover { border-color: #eeeeee #eeeeee #dddddd; } .nav-tabs > li.active > a, .nav-tabs > li.active > a:hover, .nav-tabs > li.active > a:focus { color: #555555; cursor: default; background-color: #ffffff; border: 1px solid #dddddd; border-bottom-color: transparent; } .nav-tabs.nav-justified { width: 100%; border-bottom: 0; } .nav-tabs.nav-justified > li { float: none; } .nav-tabs.nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-tabs.nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-tabs.nav-justified > li { display: table-cell; width: 1%; } .nav-tabs.nav-justified > li > a { margin-bottom: 0; } } .nav-tabs.nav-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border: 1px solid #dddddd; } @media (min-width: 768px) { .nav-tabs.nav-justified > li > a { border-bottom: 1px solid #dddddd; border-radius: 4px 4px 0 0; } .nav-tabs.nav-justified > .active > a, .nav-tabs.nav-justified > .active > a:hover, .nav-tabs.nav-justified > .active > a:focus { border-bottom-color: #ffffff; } } .nav-pills > li { float: left; } .nav-pills > li > a { border-radius: 4px; } .nav-pills > li + li { margin-left: 2px; } .nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus { color: #ffffff; background-color: #428bca; } .nav-stacked > li { float: none; } .nav-stacked > li + li { margin-top: 2px; margin-left: 0; } .nav-justified { width: 100%; } .nav-justified > li { float: none; } .nav-justified > li > a { margin-bottom: 5px; text-align: center; } .nav-justified > .dropdown .dropdown-menu { top: auto; left: auto; } @media (min-width: 768px) { .nav-justified > li { display: table-cell; width: 1%; } .nav-justified > li > a { margin-bottom: 0; } } .nav-tabs-justified { border-bottom: 0; } .nav-tabs-justified > li > a { margin-right: 0; border-radius: 4px; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border: 1px solid #dddddd; } @media (min-width: 768px) { .nav-tabs-justified > li > a { border-bottom: 1px solid #dddddd; border-radius: 4px 4px 0 0; } .nav-tabs-justified > .active > a, .nav-tabs-justified > .active > a:hover, .nav-tabs-justified > .active > a:focus { border-bottom-color: #ffffff; } } .tab-content > .tab-pane { display: none; } .tab-content > .active { display: block; } .nav-tabs .dropdown-menu { margin-top: -1px; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar { position: relative; min-height: 50px; margin-bottom: 20px; border: 1px solid transparent; } .navbar:before, .navbar:after { display: table; content: " "; } .navbar:after { clear: both; } .navbar:before, .navbar:after { display: table; content: " "; } .navbar:after { clear: both; } @media (min-width: 768px) { .navbar { border-radius: 4px; } } .navbar-header:before, .navbar-header:after { display: table; content: " "; } .navbar-header:after { clear: both; } .navbar-header:before, .navbar-header:after { display: table; content: " "; } .navbar-header:after { clear: both; } @media (min-width: 768px) { .navbar-header { float: left; } } .navbar-collapse { max-height: 340px; padding-right: 15px; padding-left: 15px; overflow-x: visible; border-top: 1px solid transparent; box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1); -webkit-overflow-scrolling: touch; } .navbar-collapse:before, .navbar-collapse:after { display: table; content: " "; } .navbar-collapse:after { clear: both; } .navbar-collapse:before, .navbar-collapse:after { display: table; content: " "; } .navbar-collapse:after { clear: both; } .navbar-collapse.in { overflow-y: auto; } @media (min-width: 768px) { .navbar-collapse { width: auto; border-top: 0; box-shadow: none; } .navbar-collapse.collapse { display: block !important; height: auto !important; padding-bottom: 0; overflow: visible !important; } .navbar-collapse.in { overflow-y: visible; } .navbar-fixed-top .navbar-collapse, .navbar-static-top .navbar-collapse, .navbar-fixed-bottom .navbar-collapse { padding-right: 0; padding-left: 0; } } .container > .navbar-header, .container > .navbar-collapse { margin-right: -15px; margin-left: -15px; } @media (min-width: 768px) { .container > .navbar-header, .container > .navbar-collapse { margin-right: 0; margin-left: 0; } } .navbar-static-top { z-index: 1000; border-width: 0 0 1px; } @media (min-width: 768px) { .navbar-static-top { border-radius: 0; } } .navbar-fixed-top, .navbar-fixed-bottom { position: fixed; right: 0; left: 0; z-index: 1030; } @media (min-width: 768px) { .navbar-fixed-top, .navbar-fixed-bottom { border-radius: 0; } } .navbar-fixed-top { top: 0; border-width: 0 0 1px; } .navbar-fixed-bottom { bottom: 0; margin-bottom: 0; border-width: 1px 0 0; } .navbar-brand { float: left; padding: 15px 15px; font-size: 18px; line-height: 20px; } .navbar-brand:hover, .navbar-brand:focus { text-decoration: none; } @media (min-width: 768px) { .navbar > .container .navbar-brand { margin-left: -15px; } } .navbar-toggle { position: relative; float: right; padding: 9px 10px; margin-top: 8px; margin-right: 15px; margin-bottom: 8px; background-color: transparent; background-image: none; border: 1px solid transparent; border-radius: 4px; } .navbar-toggle .icon-bar { display: block; width: 22px; height: 2px; border-radius: 1px; } .navbar-toggle .icon-bar + .icon-bar { margin-top: 4px; } @media (min-width: 768px) { .navbar-toggle { display: none; } } .navbar-nav { margin: 7.5px -15px; } .navbar-nav > li > a { padding-top: 10px; padding-bottom: 10px; line-height: 20px; } @media (max-width: 767px) { .navbar-nav .open .dropdown-menu { position: static; float: none; width: auto; margin-top: 0; background-color: transparent; border: 0; box-shadow: none; } .navbar-nav .open .dropdown-menu > li > a, .navbar-nav .open .dropdown-menu .dropdown-header { padding: 5px 15px 5px 25px; } .navbar-nav .open .dropdown-menu > li > a { line-height: 20px; } .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-nav .open .dropdown-menu > li > a:focus { background-image: none; } } @media (min-width: 768px) { .navbar-nav { float: left; margin: 0; } .navbar-nav > li { float: left; } .navbar-nav > li > a { padding-top: 15px; padding-bottom: 15px; } .navbar-nav.navbar-right:last-child { margin-right: -15px; } } @media (min-width: 768px) { .navbar-left { float: left !important; } .navbar-right { float: right !important; } } .navbar-form { padding: 10px 15px; margin-top: 8px; margin-right: -15px; margin-bottom: 8px; margin-left: -15px; border-top: 1px solid transparent; border-bottom: 1px solid transparent; -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1), 0 1px 0 rgba(255, 255, 255, 0.1); } @media (min-width: 768px) { .navbar-form .form-group { display: inline-block; margin-bottom: 0; vertical-align: middle; } .navbar-form .form-control { display: inline-block; } .navbar-form select.form-control { width: auto; } .navbar-form .radio, .navbar-form .checkbox { display: inline-block; padding-left: 0; margin-top: 0; margin-bottom: 0; } .navbar-form .radio input[type="radio"], .navbar-form .checkbox input[type="checkbox"] { float: none; margin-left: 0; } } @media (max-width: 767px) { .navbar-form .form-group { margin-bottom: 5px; } } @media (min-width: 768px) { .navbar-form { width: auto; padding-top: 0; padding-bottom: 0; margin-right: 0; margin-left: 0; border: 0; -webkit-box-shadow: none; box-shadow: none; } .navbar-form.navbar-right:last-child { margin-right: -15px; } } .navbar-nav > li > .dropdown-menu { margin-top: 0; border-top-right-radius: 0; border-top-left-radius: 0; } .navbar-fixed-bottom .navbar-nav > li > .dropdown-menu { border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .navbar-nav.pull-right > li > .dropdown-menu, .navbar-nav > li > .dropdown-menu.pull-right { right: 0; left: auto; } .navbar-btn { margin-top: 8px; margin-bottom: 8px; } .navbar-btn.btn-sm { margin-top: 10px; margin-bottom: 10px; } .navbar-btn.btn-xs { margin-top: 14px; margin-bottom: 14px; } .navbar-text { margin-top: 15px; margin-bottom: 15px; } @media (min-width: 768px) { .navbar-text { float: left; margin-right: 15px; margin-left: 15px; } .navbar-text.navbar-right:last-child { margin-right: 0; } } .navbar-default { background-color: #f8f8f8; border-color: #e7e7e7; } .navbar-default .navbar-brand { color: #777777; } .navbar-default .navbar-brand:hover, .navbar-default .navbar-brand:focus { color: #5e5e5e; background-color: transparent; } .navbar-default .navbar-text { color: #777777; } .navbar-default .navbar-nav > li > a { color: #777777; } .navbar-default .navbar-nav > li > a:hover, .navbar-default .navbar-nav > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav > .active > a, .navbar-default .navbar-nav > .active > a:hover, .navbar-default .navbar-nav > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav > .disabled > a, .navbar-default .navbar-nav > .disabled > a:hover, .navbar-default .navbar-nav > .disabled > a:focus { color: #cccccc; background-color: transparent; } .navbar-default .navbar-toggle { border-color: #dddddd; } .navbar-default .navbar-toggle:hover, .navbar-default .navbar-toggle:focus { background-color: #dddddd; } .navbar-default .navbar-toggle .icon-bar { background-color: #cccccc; } .navbar-default .navbar-collapse, .navbar-default .navbar-form { border-color: #e7e7e7; } .navbar-default .navbar-nav > .open > a, .navbar-default .navbar-nav > .open > a:hover, .navbar-default .navbar-nav > .open > a:focus { color: #555555; background-color: #e7e7e7; } @media (max-width: 767px) { .navbar-default .navbar-nav .open .dropdown-menu > li > a { color: #777777; } .navbar-default .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > li > a:focus { color: #333333; background-color: transparent; } .navbar-default .navbar-nav .open .dropdown-menu > .active > a, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .active > a:focus { color: #555555; background-color: #e7e7e7; } .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-default .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #cccccc; background-color: transparent; } } .navbar-default .navbar-link { color: #777777; } .navbar-default .navbar-link:hover { color: #333333; } .navbar-inverse { background-color: #222222; border-color: #080808; } .navbar-inverse .navbar-brand { color: #999999; } .navbar-inverse .navbar-brand:hover, .navbar-inverse .navbar-brand:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-text { color: #999999; } .navbar-inverse .navbar-nav > li > a { color: #999999; } .navbar-inverse .navbar-nav > li > a:hover, .navbar-inverse .navbar-nav > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav > .active > a, .navbar-inverse .navbar-nav > .active > a:hover, .navbar-inverse .navbar-nav > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav > .disabled > a, .navbar-inverse .navbar-nav > .disabled > a:hover, .navbar-inverse .navbar-nav > .disabled > a:focus { color: #444444; background-color: transparent; } .navbar-inverse .navbar-toggle { border-color: #333333; } .navbar-inverse .navbar-toggle:hover, .navbar-inverse .navbar-toggle:focus { background-color: #333333; } .navbar-inverse .navbar-toggle .icon-bar { background-color: #ffffff; } .navbar-inverse .navbar-collapse, .navbar-inverse .navbar-form { border-color: #101010; } .navbar-inverse .navbar-nav > .open > a, .navbar-inverse .navbar-nav > .open > a:hover, .navbar-inverse .navbar-nav > .open > a:focus { color: #ffffff; background-color: #080808; } @media (max-width: 767px) { .navbar-inverse .navbar-nav .open .dropdown-menu > .dropdown-header { border-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu .divider { background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a { color: #999999; } .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > li > a:focus { color: #ffffff; background-color: transparent; } .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .active > a:focus { color: #ffffff; background-color: #080808; } .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:hover, .navbar-inverse .navbar-nav .open .dropdown-menu > .disabled > a:focus { color: #444444; background-color: transparent; } } .navbar-inverse .navbar-link { color: #999999; } .navbar-inverse .navbar-link:hover { color: #ffffff; } .breadcrumb { padding: 8px 15px; margin-bottom: 20px; list-style: none; background-color: #f5f5f5; border-radius: 4px; } .breadcrumb > li { display: inline-block; } .breadcrumb > li + li:before { padding: 0 5px; color: #cccccc; content: "/\00a0"; } .breadcrumb > .active { color: #999999; } .pagination { display: inline-block; padding-left: 0; margin: 20px 0; border-radius: 4px; } .pagination > li { display: inline; } .pagination > li > a, .pagination > li > span { position: relative; float: left; padding: 6px 12px; margin-left: -1px; line-height: 1.428571429; text-decoration: none; background-color: #ffffff; border: 1px solid #dddddd; } .pagination > li:first-child > a, .pagination > li:first-child > span { margin-left: 0; border-bottom-left-radius: 4px; border-top-left-radius: 4px; } .pagination > li:last-child > a, .pagination > li:last-child > span { border-top-right-radius: 4px; border-bottom-right-radius: 4px; } .pagination > li > a:hover, .pagination > li > span:hover, .pagination > li > a:focus, .pagination > li > span:focus { background-color: #eeeeee; } .pagination > .active > a, .pagination > .active > span, .pagination > .active > a:hover, .pagination > .active > span:hover, .pagination > .active > a:focus, .pagination > .active > span:focus { z-index: 2; color: #ffffff; cursor: default; background-color: #428bca; border-color: #428bca; } .pagination > .disabled > span, .pagination > .disabled > span:hover, .pagination > .disabled > span:focus, .pagination > .disabled > a, .pagination > .disabled > a:hover, .pagination > .disabled > a:focus { color: #999999; cursor: not-allowed; background-color: #ffffff; border-color: #dddddd; } .pagination-lg > li > a, .pagination-lg > li > span { padding: 10px 16px; font-size: 18px; } .pagination-lg > li:first-child > a, .pagination-lg > li:first-child > span { border-bottom-left-radius: 6px; border-top-left-radius: 6px; } .pagination-lg > li:last-child > a, .pagination-lg > li:last-child > span { border-top-right-radius: 6px; border-bottom-right-radius: 6px; } .pagination-sm > li > a, .pagination-sm > li > span { padding: 5px 10px; font-size: 12px; } .pagination-sm > li:first-child > a, .pagination-sm > li:first-child > span { border-bottom-left-radius: 3px; border-top-left-radius: 3px; } .pagination-sm > li:last-child > a, .pagination-sm > li:last-child > span { border-top-right-radius: 3px; border-bottom-right-radius: 3px; } .pager { padding-left: 0; margin: 20px 0; text-align: center; list-style: none; } .pager:before, .pager:after { display: table; content: " "; } .pager:after { clear: both; } .pager:before, .pager:after { display: table; content: " "; } .pager:after { clear: both; } .pager li { display: inline; } .pager li > a, .pager li > span { display: inline-block; padding: 5px 14px; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 15px; } .pager li > a:hover, .pager li > a:focus { text-decoration: none; background-color: #eeeeee; } .pager .next > a, .pager .next > span { float: right; } .pager .previous > a, .pager .previous > span { float: left; } .pager .disabled > a, .pager .disabled > a:hover, .pager .disabled > a:focus, .pager .disabled > span { color: #999999; cursor: not-allowed; background-color: #ffffff; } .label { display: inline; padding: .2em .6em .3em; font-size: 75%; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; border-radius: .25em; } .label[href]:hover, .label[href]:focus { color: #ffffff; text-decoration: none; cursor: pointer; } .label:empty { display: none; } .btn .label { position: relative; top: -1px; } .label-default { background-color: #999999; } .label-default[href]:hover, .label-default[href]:focus { background-color: #808080; } .label-primary { background-color: #428bca; } .label-primary[href]:hover, .label-primary[href]:focus { background-color: #3071a9; } .label-success { background-color: #5cb85c; } .label-success[href]:hover, .label-success[href]:focus { background-color: #449d44; } .label-info { background-color: #5bc0de; } .label-info[href]:hover, .label-info[href]:focus { background-color: #31b0d5; } .label-warning { background-color: #f0ad4e; } .label-warning[href]:hover, .label-warning[href]:focus { background-color: #ec971f; } .label-danger { background-color: #d9534f; } .label-danger[href]:hover, .label-danger[href]:focus { background-color: #c9302c; } .badge { display: inline-block; min-width: 10px; padding: 3px 7px; font-size: 12px; font-weight: bold; line-height: 1; color: #ffffff; text-align: center; white-space: nowrap; vertical-align: baseline; background-color: #999999; border-radius: 10px; } .badge:empty { display: none; } .btn .badge { position: relative; top: -1px; } a.badge:hover, a.badge:focus { color: #ffffff; text-decoration: none; cursor: pointer; } a.list-group-item.active > .badge, .nav-pills > .active > a > .badge { color: #428bca; background-color: #ffffff; } .nav-pills > li > a > .badge { margin-left: 3px; } .jumbotron { padding: 30px; margin-bottom: 30px; font-size: 21px; font-weight: 200; line-height: 2.1428571435; color: inherit; background-color: #eeeeee; } .jumbotron h1, .jumbotron .h1 { line-height: 1; color: inherit; } .jumbotron p { line-height: 1.4; } .container .jumbotron { border-radius: 6px; } .jumbotron .container { max-width: 100%; } @media screen and (min-width: 768px) { .jumbotron { padding-top: 48px; padding-bottom: 48px; } .container .jumbotron { padding-right: 60px; padding-left: 60px; } .jumbotron h1, .jumbotron .h1 { font-size: 63px; } } .thumbnail { display: block; padding: 4px; margin-bottom: 20px; line-height: 1.428571429; background-color: #ffffff; border: 1px solid #dddddd; border-radius: 4px; -webkit-transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out; } .thumbnail > img, .thumbnail a > img { display: block; height: auto; max-width: 100%; margin-right: auto; margin-left: auto; } a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active { border-color: #428bca; } .thumbnail .caption { padding: 9px; color: #333333; } .alert { padding: 15px; margin-bottom: 20px; border: 1px solid transparent; border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert .alert-link { font-weight: bold; } .alert > p, .alert > ul { margin-bottom: 0; } .alert > p + p { margin-top: 5px; } .alert-dismissable { padding-right: 35px; } .alert-dismissable .close { position: relative; top: -2px; right: -21px; color: inherit; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #2b542c; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #245269; } .alert-warning { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .alert-warning hr { border-top-color: #f7e1b5; } .alert-warning .alert-link { color: #66512c; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .alert-danger hr { border-top-color: #e4b9c0; } .alert-danger .alert-link { color: #843534; } @-webkit-keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } @keyframes progress-bar-stripes { from { background-position: 40px 0; } to { background-position: 0 0; } } .progress { height: 20px; margin-bottom: 20px; overflow: hidden; background-color: #f5f5f5; border-radius: 4px; -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); } .progress-bar { float: left; width: 0; height: 100%; font-size: 12px; line-height: 20px; color: #ffffff; text-align: center; background-color: #428bca; -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); -webkit-transition: width 0.6s ease; transition: width 0.6s ease; } .progress-striped .progress-bar { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-size: 40px 40px; } .progress.active .progress-bar { -webkit-animation: progress-bar-stripes 2s linear infinite; animation: progress-bar-stripes 2s linear infinite; } .progress-bar-success { background-color: #5cb85c; } .progress-striped .progress-bar-success { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-info { background-color: #5bc0de; } .progress-striped .progress-bar-info { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-warning { background-color: #f0ad4e; } .progress-striped .progress-bar-warning { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .progress-bar-danger { background-color: #d9534f; } .progress-striped .progress-bar-danger { background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); background-image: linear-gradient(45deg, rgba(255, 255, 255, 0.15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, 0.15) 50%, rgba(255, 255, 255, 0.15) 75%, transparent 75%, transparent); } .media, .media-body { overflow: hidden; zoom: 1; } .media, .media .media { margin-top: 15px; } .media:first-child { margin-top: 0; } .media-object { display: block; } .media-heading { margin: 0 0 5px; } .media > .pull-left { margin-right: 10px; } .media > .pull-right { margin-left: 10px; } .media-list { padding-left: 0; list-style: none; } .list-group { padding-left: 0; margin-bottom: 20px; } .list-group-item { position: relative; display: block; padding: 10px 15px; margin-bottom: -1px; background-color: #ffffff; border: 1px solid #dddddd; } .list-group-item:first-child { border-top-right-radius: 4px; border-top-left-radius: 4px; } .list-group-item:last-child { margin-bottom: 0; border-bottom-right-radius: 4px; border-bottom-left-radius: 4px; } .list-group-item > .badge { float: right; } .list-group-item > .badge + .badge { margin-right: 5px; } a.list-group-item { color: #555555; } a.list-group-item .list-group-item-heading { color: #333333; } a.list-group-item:hover, a.list-group-item:focus { text-decoration: none; background-color: #f5f5f5; } a.list-group-item.active, a.list-group-item.active:hover, a.list-group-item.active:focus { z-index: 2; color: #ffffff; background-color: #428bca; border-color: #428bca; } a.list-group-item.active .list-group-item-heading, a.list-group-item.active:hover .list-group-item-heading, a.list-group-item.active:focus .list-group-item-heading { color: inherit; } a.list-group-item.active .list-group-item-text, a.list-group-item.active:hover .list-group-item-text, a.list-group-item.active:focus .list-group-item-text { color: #e1edf7; } .list-group-item-heading { margin-top: 0; margin-bottom: 5px; } .list-group-item-text { margin-bottom: 0; line-height: 1.3; } .panel { margin-bottom: 20px; background-color: #ffffff; border: 1px solid transparent; border-radius: 4px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); } .panel-body { padding: 15px; } .panel-body:before, .panel-body:after { display: table; content: " "; } .panel-body:after { clear: both; } .panel-body:before, .panel-body:after { display: table; content: " "; } .panel-body:after { clear: both; } .panel > .list-group { margin-bottom: 0; } .panel > .list-group .list-group-item { border-width: 1px 0; } .panel > .list-group .list-group-item:first-child { border-top-right-radius: 0; border-top-left-radius: 0; } .panel > .list-group .list-group-item:last-child { border-bottom: 0; } .panel-heading + .list-group .list-group-item:first-child { border-top-width: 0; } .panel > .table, .panel > .table-responsive > .table { margin-bottom: 0; } .panel > .panel-body + .table, .panel > .panel-body + .table-responsive { border-top: 1px solid #dddddd; } .panel > .table > tbody:first-child th, .panel > .table > tbody:first-child td { border-top: 0; } .panel > .table-bordered, .panel > .table-responsive > .table-bordered { border: 0; } .panel > .table-bordered > thead > tr > th:first-child, .panel > .table-responsive > .table-bordered > thead > tr > th:first-child, .panel > .table-bordered > tbody > tr > th:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:first-child, .panel > .table-bordered > tfoot > tr > th:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:first-child, .panel > .table-bordered > thead > tr > td:first-child, .panel > .table-responsive > .table-bordered > thead > tr > td:first-child, .panel > .table-bordered > tbody > tr > td:first-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:first-child, .panel > .table-bordered > tfoot > tr > td:first-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:first-child { border-left: 0; } .panel > .table-bordered > thead > tr > th:last-child, .panel > .table-responsive > .table-bordered > thead > tr > th:last-child, .panel > .table-bordered > tbody > tr > th:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > th:last-child, .panel > .table-bordered > tfoot > tr > th:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > th:last-child, .panel > .table-bordered > thead > tr > td:last-child, .panel > .table-responsive > .table-bordered > thead > tr > td:last-child, .panel > .table-bordered > tbody > tr > td:last-child, .panel > .table-responsive > .table-bordered > tbody > tr > td:last-child, .panel > .table-bordered > tfoot > tr > td:last-child, .panel > .table-responsive > .table-bordered > tfoot > tr > td:last-child { border-right: 0; } .panel > .table-bordered > thead > tr:last-child > th, .panel > .table-responsive > .table-bordered > thead > tr:last-child > th, .panel > .table-bordered > tbody > tr:last-child > th, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > th, .panel > .table-bordered > tfoot > tr:last-child > th, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > th, .panel > .table-bordered > thead > tr:last-child > td, .panel > .table-responsive > .table-bordered > thead > tr:last-child > td, .panel > .table-bordered > tbody > tr:last-child > td, .panel > .table-responsive > .table-bordered > tbody > tr:last-child > td, .panel > .table-bordered > tfoot > tr:last-child > td, .panel > .table-responsive > .table-bordered > tfoot > tr:last-child > td { border-bottom: 0; } .panel > .table-responsive { margin-bottom: 0; border: 0; } .panel-heading { padding: 10px 15px; border-bottom: 1px solid transparent; border-top-right-radius: 3px; border-top-left-radius: 3px; } .panel-heading > .dropdown .dropdown-toggle { color: inherit; } .panel-title { margin-top: 0; margin-bottom: 0; font-size: 16px; color: inherit; } .panel-title > a { color: inherit; } .panel-footer { padding: 10px 15px; background-color: #f5f5f5; border-top: 1px solid #dddddd; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; } .panel-group .panel { margin-bottom: 0; overflow: hidden; border-radius: 4px; } .panel-group .panel + .panel { margin-top: 5px; } .panel-group .panel-heading { border-bottom: 0; } .panel-group .panel-heading + .panel-collapse .panel-body { border-top: 1px solid #dddddd; } .panel-group .panel-footer { border-top: 0; } .panel-group .panel-footer + .panel-collapse .panel-body { border-bottom: 1px solid #dddddd; } .panel-default { border-color: #dddddd; } .panel-default > .panel-heading { color: #333333; background-color: #f5f5f5; border-color: #dddddd; } .panel-default > .panel-heading + .panel-collapse .panel-body { border-top-color: #dddddd; } .panel-default > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #dddddd; } .panel-primary { border-color: #428bca; } .panel-primary > .panel-heading { color: #ffffff; background-color: #428bca; border-color: #428bca; } .panel-primary > .panel-heading + .panel-collapse .panel-body { border-top-color: #428bca; } .panel-primary > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #428bca; } .panel-success { border-color: #d6e9c6; } .panel-success > .panel-heading { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .panel-success > .panel-heading + .panel-collapse .panel-body { border-top-color: #d6e9c6; } .panel-success > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #d6e9c6; } .panel-warning { border-color: #faebcc; } .panel-warning > .panel-heading { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .panel-warning > .panel-heading + .panel-collapse .panel-body { border-top-color: #faebcc; } .panel-warning > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #faebcc; } .panel-danger { border-color: #ebccd1; } .panel-danger > .panel-heading { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .panel-danger > .panel-heading + .panel-collapse .panel-body { border-top-color: #ebccd1; } .panel-danger > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #ebccd1; } .panel-info { border-color: #bce8f1; } .panel-info > .panel-heading { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .panel-info > .panel-heading + .panel-collapse .panel-body { border-top-color: #bce8f1; } .panel-info > .panel-footer + .panel-collapse .panel-body { border-bottom-color: #bce8f1; } .well { min-height: 20px; padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px; -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.05); } .well blockquote { border-color: #ddd; border-color: rgba(0, 0, 0, 0.15); } .well-lg { padding: 24px; border-radius: 6px; } .well-sm { padding: 9px; border-radius: 3px; } .close { float: right; font-size: 21px; font-weight: bold; line-height: 1; color: #000000; text-shadow: 0 1px 0 #ffffff; opacity: 0.2; filter: alpha(opacity=20); } .close:hover, .close:focus { color: #000000; text-decoration: none; cursor: pointer; opacity: 0.5; filter: alpha(opacity=50); } button.close { padding: 0; cursor: pointer; background: transparent; border: 0; -webkit-appearance: none; } .modal-open { overflow: hidden; } .modal { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1040; display: none; overflow: auto; overflow-y: scroll; } .modal.fade .modal-dialog { -webkit-transform: translate(0, -25%); -ms-transform: translate(0, -25%); transform: translate(0, -25%); -webkit-transition: -webkit-transform 0.3s ease-out; -moz-transition: -moz-transform 0.3s ease-out; -o-transition: -o-transform 0.3s ease-out; transition: transform 0.3s ease-out; } .modal.in .modal-dialog { -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); } .modal-dialog { position: relative; z-index: 1050; width: auto; margin: 10px; } .modal-content { position: relative; background-color: #ffffff; border: 1px solid #999999; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 6px; outline: none; -webkit-box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); box-shadow: 0 3px 9px rgba(0, 0, 0, 0.5); background-clip: padding-box; } .modal-backdrop { position: fixed; top: 0; right: 0; bottom: 0; left: 0; z-index: 1030; background-color: #000000; } .modal-backdrop.fade { opacity: 0; filter: alpha(opacity=0); } .modal-backdrop.in { opacity: 0.5; filter: alpha(opacity=50); } .modal-header { min-height: 16.428571429px; padding: 15px; border-bottom: 1px solid #e5e5e5; } .modal-header .close { margin-top: -2px; } .modal-title { margin: 0; line-height: 1.428571429; } .modal-body { position: relative; padding: 20px; } .modal-footer { padding: 19px 20px 20px; margin-top: 15px; text-align: right; border-top: 1px solid #e5e5e5; } .modal-footer:before, .modal-footer:after { display: table; content: " "; } .modal-footer:after { clear: both; } .modal-footer:before, .modal-footer:after { display: table; content: " "; } .modal-footer:after { clear: both; } .modal-footer .btn + .btn { margin-bottom: 0; margin-left: 5px; } .modal-footer .btn-group .btn + .btn { margin-left: -1px; } .modal-footer .btn-block + .btn-block { margin-left: 0; } @media screen and (min-width: 768px) { .modal-dialog { width: 600px; margin: 30px auto; } .modal-content { -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5); } } .tooltip { position: absolute; z-index: 1030; display: block; font-size: 12px; line-height: 1.4; opacity: 0; filter: alpha(opacity=0); visibility: visible; } .tooltip.in { opacity: 0.9; filter: alpha(opacity=90); } .tooltip.top { padding: 5px 0; margin-top: -3px; } .tooltip.right { padding: 0 5px; margin-left: 3px; } .tooltip.bottom { padding: 5px 0; margin-top: 3px; } .tooltip.left { padding: 0 5px; margin-left: -3px; } .tooltip-inner { max-width: 200px; padding: 3px 8px; color: #ffffff; text-align: center; text-decoration: none; background-color: #000000; border-radius: 4px; } .tooltip-arrow { position: absolute; width: 0; height: 0; border-color: transparent; border-style: solid; } .tooltip.top .tooltip-arrow { bottom: 0; left: 50%; margin-left: -5px; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.top-left .tooltip-arrow { bottom: 0; left: 5px; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.top-right .tooltip-arrow { right: 5px; bottom: 0; border-top-color: #000000; border-width: 5px 5px 0; } .tooltip.right .tooltip-arrow { top: 50%; left: 0; margin-top: -5px; border-right-color: #000000; border-width: 5px 5px 5px 0; } .tooltip.left .tooltip-arrow { top: 50%; right: 0; margin-top: -5px; border-left-color: #000000; border-width: 5px 0 5px 5px; } .tooltip.bottom .tooltip-arrow { top: 0; left: 50%; margin-left: -5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .tooltip.bottom-left .tooltip-arrow { top: 0; left: 5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .tooltip.bottom-right .tooltip-arrow { top: 0; right: 5px; border-bottom-color: #000000; border-width: 0 5px 5px; } .popover { position: absolute; top: 0; left: 0; z-index: 1010; display: none; max-width: 276px; padding: 1px; text-align: left; white-space: normal; background-color: #ffffff; border: 1px solid #cccccc; border: 1px solid rgba(0, 0, 0, 0.2); border-radius: 6px; -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2); background-clip: padding-box; } .popover.top { margin-top: -10px; } .popover.right { margin-left: 10px; } .popover.bottom { margin-top: 10px; } .popover.left { margin-left: -10px; } .popover-title { padding: 8px 14px; margin: 0; font-size: 14px; font-weight: normal; line-height: 18px; background-color: #f7f7f7; border-bottom: 1px solid #ebebeb; border-radius: 5px 5px 0 0; } .popover-content { padding: 9px 14px; } .popover .arrow, .popover .arrow:after { position: absolute; display: block; width: 0; height: 0; border-color: transparent; border-style: solid; } .popover .arrow { border-width: 11px; } .popover .arrow:after { border-width: 10px; content: ""; } .popover.top .arrow { bottom: -11px; left: 50%; margin-left: -11px; border-top-color: #999999; border-top-color: rgba(0, 0, 0, 0.25); border-bottom-width: 0; } .popover.top .arrow:after { bottom: 1px; margin-left: -10px; border-top-color: #ffffff; border-bottom-width: 0; content: " "; } .popover.right .arrow { top: 50%; left: -11px; margin-top: -11px; border-right-color: #999999; border-right-color: rgba(0, 0, 0, 0.25); border-left-width: 0; } .popover.right .arrow:after { bottom: -10px; left: 1px; border-right-color: #ffffff; border-left-width: 0; content: " "; } .popover.bottom .arrow { top: -11px; left: 50%; margin-left: -11px; border-bottom-color: #999999; border-bottom-color: rgba(0, 0, 0, 0.25); border-top-width: 0; } .popover.bottom .arrow:after { top: 1px; margin-left: -10px; border-bottom-color: #ffffff; border-top-width: 0; content: " "; } .popover.left .arrow { top: 50%; right: -11px; margin-top: -11px; border-left-color: #999999; border-left-color: rgba(0, 0, 0, 0.25); border-right-width: 0; } .popover.left .arrow:after { right: 1px; bottom: -10px; border-left-color: #ffffff; border-right-width: 0; content: " "; } .carousel { position: relative; } .carousel-inner { position: relative; width: 100%; overflow: hidden; } .carousel-inner > .item { position: relative; display: none; -webkit-transition: 0.6s ease-in-out left; transition: 0.6s ease-in-out left; } .carousel-inner > .item > img, .carousel-inner > .item > a > img { display: block; height: auto; max-width: 100%; line-height: 1; } .carousel-inner > .active, .carousel-inner > .next, .carousel-inner > .prev { display: block; } .carousel-inner > .active { left: 0; } .carousel-inner > .next, .carousel-inner > .prev { position: absolute; top: 0; width: 100%; } .carousel-inner > .next { left: 100%; } .carousel-inner > .prev { left: -100%; } .carousel-inner > .next.left, .carousel-inner > .prev.right { left: 0; } .carousel-inner > .active.left { left: -100%; } .carousel-inner > .active.right { left: 100%; } .carousel-control { position: absolute; top: 0; bottom: 0; left: 0; width: 15%; font-size: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); opacity: 0.5; filter: alpha(opacity=50); } .carousel-control.left { background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.5) 0), color-stop(rgba(0, 0, 0, 0.0001) 100%)); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.5) 0, rgba(0, 0, 0, 0.0001) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1); } .carousel-control.right { right: 0; left: auto; background-image: -webkit-linear-gradient(left, color-stop(rgba(0, 0, 0, 0.0001) 0), color-stop(rgba(0, 0, 0, 0.5) 100%)); background-image: linear-gradient(to right, rgba(0, 0, 0, 0.0001) 0, rgba(0, 0, 0, 0.5) 100%); background-repeat: repeat-x; filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1); } .carousel-control:hover, .carousel-control:focus { color: #ffffff; text-decoration: none; outline: none; opacity: 0.9; filter: alpha(opacity=90); } .carousel-control .icon-prev, .carousel-control .icon-next, .carousel-control .glyphicon-chevron-left, .carousel-control .glyphicon-chevron-right { position: absolute; top: 50%; z-index: 5; display: inline-block; } .carousel-control .icon-prev, .carousel-control .glyphicon-chevron-left { left: 50%; } .carousel-control .icon-next, .carousel-control .glyphicon-chevron-right { right: 50%; } .carousel-control .icon-prev, .carousel-control .icon-next { width: 20px; height: 20px; margin-top: -10px; margin-left: -10px; font-family: serif; } .carousel-control .icon-prev:before { content: '\2039'; } .carousel-control .icon-next:before { content: '\203a'; } .carousel-indicators { position: absolute; bottom: 10px; left: 50%; z-index: 15; width: 60%; padding-left: 0; margin-left: -30%; text-align: center; list-style: none; } .carousel-indicators li { display: inline-block; width: 10px; height: 10px; margin: 1px; text-indent: -999px; cursor: pointer; background-color: #000 \9; background-color: rgba(0, 0, 0, 0); border: 1px solid #ffffff; border-radius: 10px; } .carousel-indicators .active { width: 12px; height: 12px; margin: 0; background-color: #ffffff; } .carousel-caption { position: absolute; right: 15%; bottom: 20px; left: 15%; z-index: 10; padding-top: 20px; padding-bottom: 20px; color: #ffffff; text-align: center; text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); } .carousel-caption .btn { text-shadow: none; } @media screen and (min-width: 768px) { .carousel-control .glyphicons-chevron-left, .carousel-control .glyphicons-chevron-right, .carousel-control .icon-prev, .carousel-control .icon-next { width: 30px; height: 30px; margin-top: -15px; margin-left: -15px; font-size: 30px; } .carousel-caption { right: 20%; left: 20%; padding-bottom: 30px; } .carousel-indicators { bottom: 20px; } } .clearfix:before, .clearfix:after { display: table; content: " "; } .clearfix:after { clear: both; } .center-block { display: block; margin-right: auto; margin-left: auto; } .pull-right { float: right !important; } .pull-left { float: left !important; } .hide { display: none !important; } .show { display: block !important; } .invisible { visibility: hidden; } .text-hide { font: 0/0 a; color: transparent; text-shadow: none; background-color: transparent; border: 0; } .hidden { display: none !important; visibility: hidden !important; } .affix { position: fixed; } @-ms-viewport { width: device-width; } .visible-xs, tr.visible-xs, th.visible-xs, td.visible-xs { display: none !important; } @media (max-width: 767px) { .visible-xs { display: block !important; } table.visible-xs { display: table; } tr.visible-xs { display: table-row !important; } th.visible-xs, td.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-xs.visible-sm { display: block !important; } table.visible-xs.visible-sm { display: table; } tr.visible-xs.visible-sm { display: table-row !important; } th.visible-xs.visible-sm, td.visible-xs.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-xs.visible-md { display: block !important; } table.visible-xs.visible-md { display: table; } tr.visible-xs.visible-md { display: table-row !important; } th.visible-xs.visible-md, td.visible-xs.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-xs.visible-lg { display: block !important; } table.visible-xs.visible-lg { display: table; } tr.visible-xs.visible-lg { display: table-row !important; } th.visible-xs.visible-lg, td.visible-xs.visible-lg { display: table-cell !important; } } .visible-sm, tr.visible-sm, th.visible-sm, td.visible-sm { display: none !important; } @media (max-width: 767px) { .visible-sm.visible-xs { display: block !important; } table.visible-sm.visible-xs { display: table; } tr.visible-sm.visible-xs { display: table-row !important; } th.visible-sm.visible-xs, td.visible-sm.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-sm { display: block !important; } table.visible-sm { display: table; } tr.visible-sm { display: table-row !important; } th.visible-sm, td.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-sm.visible-md { display: block !important; } table.visible-sm.visible-md { display: table; } tr.visible-sm.visible-md { display: table-row !important; } th.visible-sm.visible-md, td.visible-sm.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-sm.visible-lg { display: block !important; } table.visible-sm.visible-lg { display: table; } tr.visible-sm.visible-lg { display: table-row !important; } th.visible-sm.visible-lg, td.visible-sm.visible-lg { display: table-cell !important; } } .visible-md, tr.visible-md, th.visible-md, td.visible-md { display: none !important; } @media (max-width: 767px) { .visible-md.visible-xs { display: block !important; } table.visible-md.visible-xs { display: table; } tr.visible-md.visible-xs { display: table-row !important; } th.visible-md.visible-xs, td.visible-md.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-md.visible-sm { display: block !important; } table.visible-md.visible-sm { display: table; } tr.visible-md.visible-sm { display: table-row !important; } th.visible-md.visible-sm, td.visible-md.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-md { display: block !important; } table.visible-md { display: table; } tr.visible-md { display: table-row !important; } th.visible-md, td.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-md.visible-lg { display: block !important; } table.visible-md.visible-lg { display: table; } tr.visible-md.visible-lg { display: table-row !important; } th.visible-md.visible-lg, td.visible-md.visible-lg { display: table-cell !important; } } .visible-lg, tr.visible-lg, th.visible-lg, td.visible-lg { display: none !important; } @media (max-width: 767px) { .visible-lg.visible-xs { display: block !important; } table.visible-lg.visible-xs { display: table; } tr.visible-lg.visible-xs { display: table-row !important; } th.visible-lg.visible-xs, td.visible-lg.visible-xs { display: table-cell !important; } } @media (min-width: 768px) and (max-width: 991px) { .visible-lg.visible-sm { display: block !important; } table.visible-lg.visible-sm { display: table; } tr.visible-lg.visible-sm { display: table-row !important; } th.visible-lg.visible-sm, td.visible-lg.visible-sm { display: table-cell !important; } } @media (min-width: 992px) and (max-width: 1199px) { .visible-lg.visible-md { display: block !important; } table.visible-lg.visible-md { display: table; } tr.visible-lg.visible-md { display: table-row !important; } th.visible-lg.visible-md, td.visible-lg.visible-md { display: table-cell !important; } } @media (min-width: 1200px) { .visible-lg { display: block !important; } table.visible-lg { display: table; } tr.visible-lg { display: table-row !important; } th.visible-lg, td.visible-lg { display: table-cell !important; } } .hidden-xs { display: block !important; } table.hidden-xs { display: table; } tr.hidden-xs { display: table-row !important; } th.hidden-xs, td.hidden-xs { display: table-cell !important; } @media (max-width: 767px) { .hidden-xs, tr.hidden-xs, th.hidden-xs, td.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-xs.hidden-sm, tr.hidden-xs.hidden-sm, th.hidden-xs.hidden-sm, td.hidden-xs.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-xs.hidden-md, tr.hidden-xs.hidden-md, th.hidden-xs.hidden-md, td.hidden-xs.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-xs.hidden-lg, tr.hidden-xs.hidden-lg, th.hidden-xs.hidden-lg, td.hidden-xs.hidden-lg { display: none !important; } } .hidden-sm { display: block !important; } table.hidden-sm { display: table; } tr.hidden-sm { display: table-row !important; } th.hidden-sm, td.hidden-sm { display: table-cell !important; } @media (max-width: 767px) { .hidden-sm.hidden-xs, tr.hidden-sm.hidden-xs, th.hidden-sm.hidden-xs, td.hidden-sm.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-sm, tr.hidden-sm, th.hidden-sm, td.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-sm.hidden-md, tr.hidden-sm.hidden-md, th.hidden-sm.hidden-md, td.hidden-sm.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-sm.hidden-lg, tr.hidden-sm.hidden-lg, th.hidden-sm.hidden-lg, td.hidden-sm.hidden-lg { display: none !important; } } .hidden-md { display: block !important; } table.hidden-md { display: table; } tr.hidden-md { display: table-row !important; } th.hidden-md, td.hidden-md { display: table-cell !important; } @media (max-width: 767px) { .hidden-md.hidden-xs, tr.hidden-md.hidden-xs, th.hidden-md.hidden-xs, td.hidden-md.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-md.hidden-sm, tr.hidden-md.hidden-sm, th.hidden-md.hidden-sm, td.hidden-md.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-md, tr.hidden-md, th.hidden-md, td.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-md.hidden-lg, tr.hidden-md.hidden-lg, th.hidden-md.hidden-lg, td.hidden-md.hidden-lg { display: none !important; } } .hidden-lg { display: block !important; } table.hidden-lg { display: table; } tr.hidden-lg { display: table-row !important; } th.hidden-lg, td.hidden-lg { display: table-cell !important; } @media (max-width: 767px) { .hidden-lg.hidden-xs, tr.hidden-lg.hidden-xs, th.hidden-lg.hidden-xs, td.hidden-lg.hidden-xs { display: none !important; } } @media (min-width: 768px) and (max-width: 991px) { .hidden-lg.hidden-sm, tr.hidden-lg.hidden-sm, th.hidden-lg.hidden-sm, td.hidden-lg.hidden-sm { display: none !important; } } @media (min-width: 992px) and (max-width: 1199px) { .hidden-lg.hidden-md, tr.hidden-lg.hidden-md, th.hidden-lg.hidden-md, td.hidden-lg.hidden-md { display: none !important; } } @media (min-width: 1200px) { .hidden-lg, tr.hidden-lg, th.hidden-lg, td.hidden-lg { display: none !important; } } .visible-print, tr.visible-print, th.visible-print, td.visible-print { display: none !important; } @media print { .visible-print { display: block !important; } table.visible-print { display: table; } tr.visible-print { display: table-row !important; } th.visible-print, td.visible-print { display: table-cell !important; } .hidden-print, tr.hidden-print, th.hidden-print, td.hidden-print { display: none !important; } } ================================================ FILE: console/src/main/resources/static/console-ui/public/css/codemirror.css ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ /* BASICS */ .CodeMirror { /* Set height, width, borders, and global font properties here */ font-family: monospace; height: 300px; color: black; } /* PADDING */ .CodeMirror-lines { padding: 4px 0; /* Vertical padding around content */ } .CodeMirror pre { padding: 0 4px; /* Horizontal padding of content */ } .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { background-color: white; /* The little square between H and V scrollbars */ } /* GUTTER */ .CodeMirror-gutters { border-right: 1px solid #ddd; background-color: #f7f7f7; white-space: nowrap; } .CodeMirror-linenumbers {} .CodeMirror-linenumber { padding: 0 3px 0 5px; min-width: 20px; text-align: right; color: #999; white-space: nowrap; } .CodeMirror-guttermarker { color: black; } .CodeMirror-guttermarker-subtle { color: #999; } /* CURSOR */ .CodeMirror-cursor { border-left: 1px solid black; border-right: none; width: 0; } /* Shown when moving in bi-directional text */ .CodeMirror div.CodeMirror-secondarycursor { border-left: 1px solid silver; } .cm-fat-cursor .CodeMirror-cursor { width: auto; border: 0 !important; background: #7e7; } .cm-fat-cursor div.CodeMirror-cursors { z-index: 1; } .cm-animate-fat-cursor { width: auto; border: 0; -webkit-animation: blink 1.06s steps(1) infinite; -moz-animation: blink 1.06s steps(1) infinite; animation: blink 1.06s steps(1) infinite; background-color: #7e7; } @-moz-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @-webkit-keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } @keyframes blink { 0% {} 50% { background-color: transparent; } 100% {} } /* Can style cursor different in overwrite (non-insert) mode */ .CodeMirror-overwrite .CodeMirror-cursor {} .cm-tab { display: inline-block; text-decoration: inherit; } .CodeMirror-rulers { position: absolute; left: 0; right: 0; top: -50px; bottom: -20px; overflow: hidden; } .CodeMirror-ruler { border-left: 1px solid #ccc; top: 0; bottom: 0; position: absolute; } /* DEFAULT THEME */ .cm-s-default .cm-header {color: blue;} .cm-s-default .cm-quote {color: #090;} .cm-negative {color: #d44;} .cm-positive {color: #292;} .cm-header, .cm-strong {font-weight: bold;} .cm-em {font-style: italic;} .cm-link {text-decoration: underline;} .cm-strikethrough {text-decoration: line-through;} .cm-s-default .cm-keyword {color: #708;} .cm-s-default .cm-atom {color: #219;} .cm-s-default .cm-number {color: #164;} .cm-s-default .cm-def {color: #00f;} .cm-s-default .cm-variable, .cm-s-default .cm-punctuation, .cm-s-default .cm-property, .cm-s-default .cm-operator {} .cm-s-default .cm-variable-2 {color: #05a;} .cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;} .cm-s-default .cm-comment {color: #a50;} .cm-s-default .cm-string {color: #a11;} .cm-s-default .cm-string-2 {color: #f50;} .cm-s-default .cm-meta {color: #555;} .cm-s-default .cm-qualifier {color: #555;} .cm-s-default .cm-builtin {color: #30a;} .cm-s-default .cm-bracket {color: #997;} .cm-s-default .cm-tag {color: #170;} .cm-s-default .cm-attribute {color: #00c;} .cm-s-default .cm-hr {color: #999;} .cm-s-default .cm-link {color: #00c;} .cm-s-default .cm-error {color: #f00;} .cm-invalidchar {color: #f00;} .CodeMirror-composing { border-bottom: 2px solid; } /* Default styles for common addons */ div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} .CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); } .CodeMirror-activeline-background {background: #e8f2ff;} /* STOP */ /* The rest of this file contains styles related to the mechanics of the editor. You probably shouldn't touch them. */ .CodeMirror { position: relative; overflow: hidden; background: white; } .CodeMirror-scroll { overflow: scroll !important; /* Things will break if this is overridden */ /* 30px is the magic margin used to hide the element's real scrollbars */ /* See overflow: hidden in .CodeMirror */ margin-bottom: -30px; margin-right: -30px; padding-bottom: 30px; height: 100%; outline: none; /* Prevent dragging from highlighting the element */ position: relative; } .CodeMirror-sizer { position: relative; border-right: 30px solid transparent; } /* The fake, visible scrollbars. Used to force redraw during scrolling before actual scrolling happens, thus preventing shaking and flickering artifacts. */ .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { position: absolute; z-index: 6; display: none; } .CodeMirror-vscrollbar { right: 0; top: 0; overflow-x: hidden; overflow-y: scroll; } .CodeMirror-hscrollbar { bottom: 0; left: 0; overflow-y: hidden; overflow-x: scroll; } .CodeMirror-scrollbar-filler { right: 0; bottom: 0; } .CodeMirror-gutter-filler { left: 0; bottom: 0; } .CodeMirror-gutters { position: absolute; left: 0; top: 0; min-height: 100%; z-index: 3; } .CodeMirror-gutter { white-space: normal; height: 100%; display: inline-block; vertical-align: top; margin-bottom: -30px; } .CodeMirror-gutter-wrapper { position: absolute; z-index: 4; background: none !important; border: none !important; } .CodeMirror-gutter-background { position: absolute; top: 0; bottom: 0; z-index: 4; } .CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4; } .CodeMirror-gutter-wrapper ::selection { background-color: transparent } .CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent } .CodeMirror-lines { cursor: text; min-height: 1px; /* prevents collapsing before first draw */ } .CodeMirror pre { /* Reset some styles that the rest of the page might have set */ -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; border-width: 0; background: transparent; font-family: inherit; font-size: inherit; margin: 0; white-space: pre; word-wrap: normal; line-height: inherit; color: inherit; z-index: 2; position: relative; overflow: visible; -webkit-tap-highlight-color: transparent; -webkit-font-variant-ligatures: contextual; font-variant-ligatures: contextual; } .CodeMirror-wrap pre { word-wrap: break-word; white-space: pre-wrap; word-break: normal; } .CodeMirror-linebackground { position: absolute; left: 0; right: 0; top: 0; bottom: 0; z-index: 0; } .CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto; } .CodeMirror-widget {} .CodeMirror-rtl pre { direction: rtl; } .CodeMirror-code { outline: none; } /* Force content-box sizing for the elements where we expect it */ .CodeMirror-scroll, .CodeMirror-sizer, .CodeMirror-gutter, .CodeMirror-gutters, .CodeMirror-linenumber { -moz-box-sizing: content-box; box-sizing: content-box; } .CodeMirror-measure { position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden; } .CodeMirror-cursor { position: absolute; pointer-events: none; } .CodeMirror-measure pre { position: static; } div.CodeMirror-cursors { visibility: hidden; position: relative; z-index: 3; } div.CodeMirror-dragcursors { visibility: visible; } .CodeMirror-focused div.CodeMirror-cursors { visibility: visible; } .CodeMirror-selected { background: #d9d9d9; } .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } .CodeMirror-crosshair { cursor: crosshair; } .CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; } .CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; } .cm-searching { background: #ffa; background: rgba(255, 255, 0, .4); } /* Used to force a border model for a node */ .cm-force-border { padding-right: .1px; } @media print { /* Hide the cursor when printing */ .CodeMirror div.CodeMirror-cursors { visibility: hidden; } } /* See issue #2901 */ .cm-tab-wrap-hack:after { content: ''; } /* Help users use markselection to safely style text background */ span.CodeMirror-selectedtext { background: none; } ================================================ FILE: console/src/main/resources/static/console-ui/public/css/console1412.css ================================================ @charset "UTF-8";.viewFramework-topbar{position:fixed;width:100%;height:50px;background:#09C;z-index:101}.viewFramework-body{position:absolute;width:100%;top:50px;bottom:0px;background-color:#000;z-index:100}.viewFramework-body .console-global-notice .console-global-notice-nav{top:14px}.viewFramework-body .console-global-notice .console-global-notice-list{margin:0;height:40px}.viewFramework-body .console-global-notice .console-global-notice-list .console-global-notice-item{padding:11px 12px;border:none}.viewFramework-body .console-global-notice .console-global-notice-list .console-global-notice-item .console-global-notice-nomore{top:10px}.viewFramework-body.viewFramework-topbar-hide{top:0px}.viewFramework-body.viewFramework-topbar-hide .viewFramework-sidebar{top:0px}.viewFramework-sidebar{width:0px;display:none;position:fixed;top:50px;bottom:0px;background-color:#293038;z-index:102;overflow-x:hidden}.viewFramework-sidebar .sidebar-content{width:200px;height:100%;overflow:auto;overflow-x:hidden}.viewFramework-sidebar .sidebar-trans{-o-transition:all 0.12s ease,0.12s ease;-ms-transition:all 0.12s ease,0.12s ease;-moz-transition:all 0.12s ease,0.12s ease;-webkit-transition:all 0.12s ease,0.12s ease}.viewFramework-sidebar .sidebar-fold{height:30px;width:180px;background:#394555;color:#aeb9c2;text-align:center;line-height:30px !important;font-size:12px;user-select:none;cursor:pointer;-webkit-user-select:none;-moz-user-select:none}.viewFramework-sidebar .sidebar-fold:hover{background:#37424f}.viewFramework-sidebar .sidebar-nav{width:180px}.viewFramework-sidebar .sidebar-nav ul{margin:0px;padding:0px;list-style:none;overflow:hidden}.viewFramework-sidebar .sidebar-nav li a{display:block;width:100%;height:40px;line-height:40px;overflow:hidden}.viewFramework-sidebar .sidebar-nav li a:hover{background:#37424f}.viewFramework-sidebar .sidebar-nav li a:hover .nav-icon,.viewFramework-sidebar .sidebar-nav li a:hover .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav .nav-item{position:relative}.viewFramework-sidebar .sidebar-nav .nav-comment{background:#2d3945;color:#cccccc;height:26px;margin-left:8px;line-height:26px;padding:0 7px;vertical-align:middle;position:relative;display:none}.viewFramework-sidebar .sidebar-nav .nav-comment .icon-arrow-left{position:absolute;left:-14px;line-height:26px;font-size:24px;color:#2d3945}.viewFramework-sidebar .sidebar-nav .nav-tooltip-comment{color:#ccc}.viewFramework-sidebar .sidebar-nav .sidebar-title{height:40px;background:#22282e;color:#fff;line-height:40px;position:relative;cursor:pointer;-webkit-user-select:none;-moz-user-select:none}.viewFramework-sidebar .sidebar-nav .sidebar-title:hover{background:#414d5c}.viewFramework-sidebar .sidebar-nav .sidebar-title-icon{display:inline-block;margin:0 8px 0 20px;vertical-align:middle;transition:transform 0.12s;-o-transition:-o-transform 0.12s;-ms-transition:-ms-transform 0.12s;-moz-transition:-moz-transform 0.12s;-webkit-transition:-webkit-transform 0.12s}.viewFramework-sidebar .sidebar-manage{vertical-align:middle;position:absolute;height:40px;width:40px;right:0}.viewFramework-sidebar .sidebar-manage a{display:block;width:100%;height:100%;text-align:center;line-height:40px;font-size:14px;color:#a0abb3;text-decoration:none}.viewFramework-sidebar .sidebar-nav-fold ul{height:0 !important;overflow:hidden}.viewFramework-sidebar .sidebar-nav-fold .sidebar-title-icon{transform:rotate(-90deg);-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg)}.viewFramework-sidebar .sidebar-nav-fold .sidebar-title{background:#37424f;border-bottom:1px solid #414d5c}.viewFramework-sidebar .sidebar-nav .nav-item:hover .nav-comment{display:inline-block}.viewFramework-sidebar .entrance-nav .nav-comment{margin-left:10px}.viewFramework-sidebar .sidebar-nav .nav-icon{width:50px;text-align:center;font-size:16px;float:left;color:#aeb9c2}.viewFramework-sidebar .sidebar-nav .nav-title{float:left;overflow:hidden;color:#fff;white-space:nowrap;text-overflow:ellipsis;display:block;width:130px}.viewFramework-sidebar .entrance-nav .nav-title{width:auto}.viewFramework-sidebar .sidebar-nav li.consolehome .nav-tooltip{top:15px;line-height:40px}.viewFramework-sidebar .sidebar-nav li.consolehome a{height:70px;line-height:70px;background:#293038}.viewFramework-sidebar .sidebar-nav li.consolehome a .nav-icon{font-size:20px}.viewFramework-sidebar .sidebar-nav li.consolehome.active a{background:#293038}.viewFramework-sidebar .sidebar-nav li.active a{background:#0099cc}.viewFramework-sidebar .sidebar-nav li.active a .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav li.active a .nav-icon{color:#fff}.viewFramework-sidebar .sidebar-nav .manage-nav{height:30px;overflow:hidden}.viewFramework-sidebar .sidebar-nav .manage-nav:hover .nav-icon{color:#fff}.viewFramework-sidebar .sidebar-nav .manage-nav a{display:block;height:100%}.viewFramework-sidebar .sidebar-nav .manage-nav .nav-icon{height:100%;line-height:30px;font-size:16px}.viewFramework-sidebar .sidebar-nav .manage-nav .nav-title{margin-top:14px;background:#293038;height:1px;width:120px}.viewFramework-sidebar .sidebar-nav .more-nav{display:block;width:100%;height:40px;line-height:40px;position:relative}.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch{background:#09c}.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch .nav-icon,.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch:hover{background:#09c}.viewFramework-sidebar .sidebar-nav .more-nav.open .icon-up{display:none}.viewFramework-sidebar .sidebar-nav .more-nav.open .icon-down{display:inline-block}.viewFramework-sidebar .sidebar-nav .more-nav .icon-up{display:inline-block}.viewFramework-sidebar .sidebar-nav .more-nav .icon-down{display:none}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch{display:block;width:100%;height:40px;line-height:40px}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover{background:#425160}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover .nav-icon,.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover .nav-title{color:#fff}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container{background:#425160;position:absolute;bottom:40px;top:auto;border:none;border-radius:0 0;box-shadow:none;margin:0;width:100%}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container a{color:#fff;text-decoration:none}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item{display:block;height:40px;line-height:40px}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item:hover{background:#3a4856}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item:hover .more-nav-item-icon{color:#aeb9c2}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item-icon{width:50px;display:inline-block;vertical-align:text-top;text-align:center;color:#546478}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item.active{background:#2d3945}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item.active .more-nav-item-icon{color:#0099cc}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-close{height:20px;background:#09c;text-align:right;line-height:20px;cursor:pointer}.viewFramework-sidebar .sidebar-nav .more-nav .more-nav-close .icon-down{text-align:left;width:32px;display:inline-block;color:#80cce6;vertical-align:middle}.viewFramework-sidebar-mini .viewFramework-sidebar,.viewFramework-sidebar.sidebar-mini{width:50px;display:block}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-content,.viewFramework-sidebar.sidebar-mini .sidebar-content{width:70px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-fold,.viewFramework-sidebar.sidebar-mini .sidebar-fold{width:50px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav,.viewFramework-sidebar.sidebar-mini .sidebar-nav{width:50px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .nav-item a:hover+.nav-tooltip,.viewFramework-sidebar.sidebar-mini .sidebar-nav .nav-item a:hover+.nav-tooltip{display:block}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .nav-title,.viewFramework-sidebar.sidebar-mini .sidebar-nav .nav-title{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-switch:hover,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-switch:hover{background:#425160 !important}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav.open .more-nav-switch,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav.open .more-nav-switch{background:#425160 !important}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-container{bottom:0px;left:50px;width:180px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-container .more-nav-item{display:block;height:40px;line-height:40px}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-container .more-nav-item-icon,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-container .more-nav-item-icon{width:50px;padding-left:0}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav .more-nav .more-nav-close,.viewFramework-sidebar.sidebar-mini .sidebar-nav .more-nav .more-nav-close{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-nav li.consolehome a :hover,.viewFramework-sidebar.sidebar-mini .sidebar-nav li.consolehome a :hover{background:#425160}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-title .sidebar-title-text,.viewFramework-sidebar.sidebar-mini .sidebar-title .sidebar-title-text{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-title-inner:hover+.nav-tooltip,.viewFramework-sidebar.sidebar-mini .sidebar-title-inner:hover+.nav-tooltip{display:block}.viewFramework-sidebar-mini .viewFramework-sidebar .sidebar-manage,.viewFramework-sidebar.sidebar-mini .sidebar-manage{display:none}.viewFramework-sidebar-mini .viewFramework-sidebar .entrance-nav .nav-item:hover .nav-comment,.viewFramework-sidebar.sidebar-mini .entrance-nav .nav-item:hover .nav-comment{display:none}.viewFramework-sidebar-full .viewFramework-sidebar,.viewFramework-sidebar.sidebar-full{width:180px;display:block}.viewFramework-sidebar-full .viewFramework-sidebar .sidebar-nav .nav-icon,.viewFramework-sidebar.sidebar-full .sidebar-nav .nav-icon{width:50px}.viewFramework-sidebar-mini .viewFramework-product{left:50px}.viewFramework-sidebar-full .viewFramework-product{left:180px}.viewFramework-sidebar-dialog .modal-dialog{width:730px}.viewFramework-sidebar-dialog .modal-dialog .modal-title{user-select:none;-webkit-user-select:none}.viewFramework-sidebar-manage .sidebar-item-list{padding:4px 0 0 0;height:auto}.viewFramework-sidebar-manage .sidebar-item-list-picked .sidebar-item{border:1px solid #37a9d5}.viewFramework-sidebar-manage .sidebar-item-list-picked .sidebar-item .sidebar-item-opt-icon{display:block}.viewFramework-sidebar-manage .sidebar-item-list-picked .sidebar-item .sidebar-item-icon{color:#516176}.viewFramework-sidebar-manage .sidebar-config-title{padding-left:6px;user-select:none;-webkit-user-select:none}.viewFramework-sidebar-manage .sidebar-item-wrap{padding:6px;width:33.3%;float:left;user-select:none;-webkit-user-select:none}.viewFramework-sidebar-manage .sidebar-item-wrap.on-drag-hover .sidebar-item{border:1px dashed #ddd}.viewFramework-sidebar-manage .sidebar-item{height:32px;padding:4px;line-height:24px;background:#fff;border:1px solid #d3dce3;position:relative;cursor:pointer;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;-o-transition:all 0.1s, 0.1s;-ms-transition:all 0.1s, 0.1s;-moz-transition:all 0.1s, 0.1s;-webkit-transition:all 0.1s, 0.1s}.viewFramework-sidebar-manage .sidebar-item:hover{border:1px solid #37a9d5}.viewFramework-sidebar-manage .sidebar-item:hover .sidebar-item-opt-icon{display:block}.viewFramework-sidebar-manage .sidebar-item .sidebar-item-icon{color:#aeb9c2;font-size:14px;margin:0 2px;position:relative;top:1px}.viewFramework-sidebar-manage .sidebar-item .sidebar-item-opt-icon{display:none;position:absolute;height:30px;width:30px;right:0;top:0;line-height:30px;text-align:center;border-left:1px solid #37a9d5;color:#37a9d5;font-size:14px}.viewFramework-sidebar-manage .sidebar-config-gap{border:1px dashed #e8ecf0;margin:16px 5px;user-select:none;-webkit-user-select:none}.aliyun-console-sidebar-tooltip{position:absolute;z-index:1030;display:block;font-size:12px;line-height:1.4;opacity:0;filter:alpha(opacity=0);visibility:visible}.aliyun-console-sidebar-tooltip .tooltip-inner{max-width:200px;padding:12px 8px;color:#ffffff;text-align:center;text-decoration:none;border-radius:0 0;background-color:#425160}.aliyun-console-sidebar-tooltip .tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.aliyun-console-sidebar-tooltip.in{opacity:0.9;filter:alpha(opacity=90)}.aliyun-console-sidebar-tooltip.right{padding:0 5px;margin-left:3px}.aliyun-console-sidebar-tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-right-color:#425160;border-width:5px 5px 5px 0}.viewFramework-product{width:auto;position:absolute;top:0px;left:0px;bottom:0px;right:0px;overflow:hidden;background:#FFF}.viewFramework-product-navbar{width:0px;float:left;background-color:#EAEDF1;position:absolute;top:0px;bottom:0px;z-index:2;overflow:hidden;-o-transition:all 0.2s ease;-ms-transition:all 0.2s ease;-moz-transition:all 0.2s ease;-webkit-transition:all 0.2s ease}.viewFramework-product-navbar .product-nav-stage{width:180px;overflow:hidden;position:absolute;top:0px;bottom:0px;right:0px}.viewFramework-product-navbar .product-nav-stage .product-nav-scene{width:180px;position:absolute;top:0px;bottom:0px;-webkit-transition:position,.2s,linear;-moz-transition:position,.2s,linear}.viewFramework-product-navbar .product-nav-stage .product-nav-main-scene{left:0px}.viewFramework-product-navbar .product-nav-stage .product-nav-sub-scene{left:180px}.viewFramework-product-navbar .product-nav-stage-main .product-nav-main-scene{left:0px}.viewFramework-product-navbar .product-nav-stage-main .product-nav-sub-scene{left:180px}.viewFramework-product-navbar .product-nav-stage-sub .product-nav-main-scene{left:-180px}.viewFramework-product-navbar .product-nav-stage-sub .product-nav-sub-scene{left:0px}.viewFramework-product-navbar .product-nav-scene .product-nav-title{width:180px;height:70px;line-height:70px;background:#D9DEE4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.viewFramework-product-navbar .product-nav-main-scene .product-nav-title{font-weight:bold;text-indent:20px}.viewFramework-product-navbar .product-nav-sub-scene .product-nav-title{text-align:center}.viewFramework-product-navbar .product-nav-sub-scene .product-nav-title a{font-size:20px;color:#546478;text-decoration:none}.viewFramework-product-navbar .product-nav-sub-scene .product-nav-title a:hover{color:#09C}.viewFramework-product-navbar .product-nav-list{position:absolute;top:70px;left:0px;right:0px;bottom:0px;overflow-y:auto;overflow-x:hidden}.viewFramework-product-navbar .product-nav-list .nav-icon{width:30px;height:40px;float:left;text-align:center;font-size:16px;color:#333}.viewFramework-product-navbar .product-nav-list .nav-title{width:138px;float:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.viewFramework-product-navbar .product-nav-list .nav-extend{height:40px;line-height:40px;float:right;margin-top:-40px}.viewFramework-product-navbar .product-nav-list ul{list-style:none;padding:0px;margin:0px}.viewFramework-product-navbar .product-nav-list li a{width:180px;height:40px;line-height:40px;display:block;color:#333}.viewFramework-product-navbar .product-nav-list ul ul li a{color:#666}.viewFramework-product-navbar .product-nav-list ul ul li a .nav-title{text-indent:8px}.viewFramework-product-navbar .product-nav-list li a:hover{background-color:#F4F6F8}.viewFramework-product-navbar .product-nav-list li.active a{background-color:#FFF}.viewFramework-product-navbar-collapse{position:absolute;left:0;top:50%;width:20px;height:50px;z-index:3;-o-transition:all 0.2s ease;-ms-transition:all 0.2s ease;-moz-transition:all 0.2s ease;-webkit-transition:all 0.2s ease}.viewFramework-product-navbar-collapse:hover .product-navbar-collapse{left:0}.viewFramework-product-navbar-collapse:hover .product-navbar-collapse-bg{border-bottom:8px solid transparent;border-left:20px solid #D9DEE4;border-top:8px solid transparent}.viewFramework-product-navbar-collapse .product-navbar-collapse-inner{top:-50%;position:relative;overflow:hidden}.viewFramework-product-navbar-collapse .product-navbar-collapse{height:50px;position:relative;left:-7px;text-align:center;cursor:pointer;-o-transition:all 0.1s ease,0.1s ease;-ms-transition:all 0.1s ease,0.1s ease;-moz-transition:all 0.1s ease,0.1s ease;-webkit-transition:all 0.1s ease,0.1s ease}.viewFramework-product-navbar-collapse .product-navbar-collapse>span{font-size:15px;line-height:50px;vertical-align:text-top}.viewFramework-product-navbar-collapse .product-navbar-collapse-bg{width:0;height:50px;position:absolute;top:0;left:0;border-bottom:9px solid transparent;border-left:13px solid #D9DEE4;border-top:9px solid transparent;-o-transition:all 0.1s ease,0.1s ease;-ms-transition:all 0.1s ease,0.1s ease;-moz-transition:all 0.1s ease,0.1s ease;-webkit-transition:all 0.1s ease,0.1s ease}.viewFramework-product-navbar-collapse .icon-collapse-left{display:none}.viewFramework-product-navbar-collapse .icon-collapse-right{display:inline}.viewFramework-product-body{position:absolute;width:auto;top:0px;bottom:0px;left:0px;right:0px;overflow:hidden;overflow-y:auto;-o-transition:all 0.2s ease;-ms-transition:all 0.2s ease;-moz-transition:all 0.2s ease;-webkit-transition:all 0.2s ease}.viewFramework-product-col-1 .viewFramework-product-navbar-bg,.viewFramework-product-col-1 .viewFramework-product-navbar{width:180px}.viewFramework-product-col-1 .viewFramework-product-body{left:180px}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse{left:160px}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .product-navbar-collapse{right:-7px;left:auto}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .product-navbar-collapse>span{color:#546478}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .product-navbar-collapse-bg{right:0;left:auto;border-bottom:9px solid transparent;border-left:none;border-right:13px solid #f7f7f7;border-top:9px solid transparent}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .icon-collapse-left{display:inline}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse .icon-collapse-right{display:none}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse:hover .product-navbar-collapse{right:0;left:auto}.viewFramework-product-col-1 .viewFramework-product-navbar-collapse:hover .product-navbar-collapse-bg{border-bottom:8px solid transparent;border-left:none;border-right:20px solid #f7f7f7;border-top:8px solid transparent}.viewFramework-product-col-2 .viewFramework-product-navbar-bg,.viewFramework-product-col-2 .viewFramework-product-navbar{width:360px}.viewFramework-product-col-2 .viewFramework-product-body{left:360px}.viewFramework-animate{-webkit-animation-duration:0.1s;animation-duration:0.1s;-webkit-animation-fill-mode:both;animation-fill-mode:both}.viewFramework-fadeIn{-webkit-animation-name:viewFrameworkFadeIn;animation-name:viewFrameworkFadeIn}@-webkit-keyframes viewFrameworkFadeIn{0%{opacity:0}100%{opacity:1}}@keyframes viewFrameworkFadeIn{0%{opacity:0}100%{opacity:1}}.text-muted{color:#999 !important}.text-muted:hover{color:#999 !important}.text-info{color:#69C !important}.text-info:hover{color:#69C !important}.text-primary{color:#09C !important}.text-primary:hover{color:#09C !important}.text-success{color:#090 !important}.text-success:hover{color:#090 !important}.text-warning{color:#F90 !important}.text-warning:hover{color:#F90 !important}.text-danger{color:#F00 !important}.text-danger:hover{color:#F00 !important}.text-explode{color:#CCC !important;font-weight:normal !important;margin:0px 4px !important}span.label{font-weight:normal}.text-size-14{font-size:14px !important}.text-size-16{font-size:16px !important}.text-size-24{font-size:24px !important}.text-size-32{font-size:32px !important}.text-size-48{font-size:48px !important}.text-size-64{font-size:64px !important}.btn{font-size:12px;border-radius:0px;padding:8px 16px;height:32px;line-height:14px}.btn-default{color:#333;border:1px solid #ddd;background-color:#f7f7f7}.btn-default:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-default:focus{color:#333;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn-default:hover{color:#333;border:1px solid #ddd;background-color:#fff}.btn-primary{color:#fff;border:1px solid #09c;background-color:#09c}.btn-primary:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-primary:focus{color:#fff;border:1px solid #09c;background-color:#09c;outline:none}.btn-primary:hover{color:#fff;border:1px solid #28b5d6;background-color:#28b5d6}.btn-success{color:#fff;border:1px solid #57a235;background-color:#4db118}.btn-success:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-success:focus{color:#fff;border:1px solid #57a235;background-color:#4db118;outline:none}.btn-success:hover{color:#fff;border:1px solid #57bc20;background-color:#57bc20}.btn-warning{color:#333;border:1px solid #ddd;background-color:#f7f7f7}.btn-warning:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-warning:focus{color:#333;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn-warning:hover{color:#fff;border:1px solid #ffa200;background-color:#ffa200}.btn-danger{color:#333;color:#333;border:1px solid #ddd;background-color:#f7f7f7}.btn-danger:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn-danger:focus{color:#333;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn-danger:hover{color:#fff;border:1px solid #f25721;background-color:#f25721}.btn-link{color:#06C;text-shadow:none;border:none}.btn-link:hover{color:#039}.btn-lg{font-size:14px;padding:12px 20px;height:40px;line-height:16px}.btn-sm{font-size:12px;padding:4px 12px;height:24px;line-height:14px}.btn-xs{font-size:12px;padding:2px 8px;height:20px;line-height:14px}.btn.disabled,.btn[disabled]{text-shadow:none;filter:none;opacity:1;color:#bbb;border:1px solid #ddd;background-color:#f7f7f7}.btn.disabled:active,.btn[disabled]:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn.disabled:focus,.btn[disabled]:focus{color:#bbb;border:1px solid #ddd;background-color:#f7f7f7;outline:none}.btn.disabled:hover,.btn[disabled]:hover{color:#bbb;border:1px solid #ddd;background-color:#f7f7f7}.btn.btn-link.disabled,.btn.btn-link[disabled]{border:none;background:transparent none}.btn.btn-primary.disabled,.btn.btn-primary[disabled]{color:#EEE;text-shadow:none;filter:none;opacity:1;color:#fff;border:1px solid #ccc;background-color:#ccc}.btn.btn-primary.disabled:active,.btn.btn-primary[disabled]:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,0.125);box-shadow:inset 0 1px 2px rgba(0,0,0,0.125)}.btn.btn-primary.disabled:focus,.btn.btn-primary[disabled]:focus{color:#fff;border:1px solid #ccc;background-color:#ccc;outline:none}.btn.btn-primary.disabled:hover,.btn.btn-primary[disabled]:hover{color:#fff;border:1px solid #ccc;background-color:#ccc}.btn-default-active,.btn-default-active:hover,.btn-default-active:focus{border:1px solid #485260;background-color:#525d6d;background:-webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #525d6d), color-stop(100%, #525d6d));background:-webkit-linear-gradient(top, #525d6d,#525d6d);background:-moz-linear-gradient(top, #525d6d,#525d6d);background:-o-linear-gradient(top, #525d6d,#525d6d);background:linear-gradient(top, #525d6d,#525d6d);color:#FFFFFF;box-shadow:inset 0px 1px 2px rgba(0,0,0,0.3)}.btn-toinstlist{border:1px solid #BBB;color:#666;text-shadow:none;vertical-align:middle;margin-top:7px}.btn-toinstlist .icon-toinstlist{width:12px;height:12px;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;background:url(images/toinstlist.png) center 1px no-repeat}.console-sub-title+.table{margin-top:0px}.table-header{border:1px solid #e1e6eb;width:100%;z-index:1;margin-bottom:-1px;padding:8px;line-height:32px;display:table}.table-header+.table{margin-top:0px}.table{background:#FFF;font-size:12px;border-top:1px solid #e1e6eb;margin-top:8px;border:1px solid #e1e6eb}.table thead tr th{padding:8px 8px;font-weight:normal;color:#999;border-bottom:1px solid #e1e6eb;background-color:#F5F6FA}.table thead tr th a.dropdown-toggle{color:#999}.table thead tr th .dropdown.open a{color:#333}.table tbody tr td{padding:12px 8px;border-top:0px;border-bottom:1px solid #e1e6eb;vertical-align:middle}.table tbody tr td p{margin-bottom:0px}.table tfoot tr td{padding:12px 8px;border-bottom:1px solid #e1e6eb;vertical-align:middle}.table .text-right .dropdown-menu{text-align:left;left:auto;right:0px}.table-hover tbody tr:hover td{background-color:#F9F9FA}.pagination{margin:0px;vertical-align:middle;border-radius:0px}.pagination li a,.pagination li span{height:32px;line-height:20px;color:#333;cursor:pointer;border-color:#CCC}.pagination li a:hover{color:#FFF;background-color:#28B5D6;border-color:#28B5D6}.pagination li span{color:#999}.pagination li span:hover{background:none}.pagination li:first-child>a,.pagination li:first-child>span{border-radius:0px}.pagination li:last-child>a,.pagination li:last-child>span{border-radius:0px}.pagination li.active a,.pagination li.active span{background-color:#09C;border:1px solid #09C}.pagination li.active a:hover,.pagination li.active span:hover{background-color:#09C;border:1px solid #09C}.pagination-info{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;padding:0px 16px;color:#888}.form-group{margin-top:8px;margin-bottom:16px}.help-block{margin:4px 0px}.form-control{height:32px;border-radius:0px;padding:6px;-webkit-transition:none;transition:none;font-size:12px}.form-control:focus{-webkit-box-shadow:none;box-shadow:none}.form-control[size],.form-control[cols],.form-control.autosize{width:auto}.form-control.inline{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline}select{color:#555555;vertical-align:middle;background-color:#ffffff;background-image:none;border:1px solid #cccccc}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline{color:#090}.has-success .form-control,input.ng-valid.ng-dirty,textarea.ng-valid.ng-dirty{border-color:#090}.has-success .form-control:focus,input.ng-valid.ng-dirty:focus,textarea.ng-valid.ng-dirty:focus{border-color:#2A0;-webkit-box-shadow:none;box-shadow:none}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline{color:#F90}.has-warning .form-control{border-color:#F90}.has-warning .form-control:focus{border-color:#FA0;-webkit-box-shadow:none;box-shadow:none}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline{color:#E40}.has-error .form-control,input.ng-invalid.ng-dirty,textarea.ng-invalid.ng-dirty{border-color:#E40}.has-error .form-control:focus,input.ng-invalid.ng-dirty:focus,textarea.ng-invalid.ng-dirty:focus{border-color:#F30;-webkit-box-shadow:none;box-shadow:none}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{color:#999}label.control-label{font-weight:normal;font-size:12px;color:#666}.form-inline .form-group{margin:4px 8px 4px 0px}.form-inline .form-control{width:auto}.form-inline .input-group-btn{width:auto}select.input-lg,.input-lg{height:40px}select.input-sm,.input-sm{height:24px}.console-onoff{vertical-align:middle;width:50px;height:20px;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;background-image:url("images/on-off.png");background-image:-webkit-image-set(url("images/on-off.png") 1x, url("images/on-off@2x.png") 2x);background-image:-moz-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-o-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-ms-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-repeat:no-repeat;background-position:0px 0px;cursor:pointer}.console-onoff .onoff-handle{display:block;width:50px;height:20px;-webkit-transition:background-position 0.2s ease;-moz-transition:background-position 0.2s ease;-o-transition:background-position 0.2s ease;transition:background-position 0.2s ease;background-image:url("images/on-off.png");background-image:-webkit-image-set(url("images/on-off.png") 1x, url("images/on-off@2x.png") 2x);background-image:-moz-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-o-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-image:-ms-image-set(url("images/onoff.png") 1x, url("images/onoff@2x.png") 2x);background-repeat:no-repeat;background-position:0px 0px}.console-onoff .onoff-loading{display:block;width:50px;height:20px;-webkit-transition:background-position 0.2s ease;-moz-transition:background-position 0.2s ease;-o-transition:background-position 0.2s ease;transition:background-position 0.2s ease;background-image:url("images/on-off-loading.gif");background-image:-webkit-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-image:-moz-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-image:-o-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-image:-ms-image-set(url("images/on-off-loading.gif") 1x, url("images/on-off-loading@2x.gif") 2x);background-repeat:no-repeat;background-position:0px 0px}.console-onoff-on{background-position:0px -40px}.console-onoff-on .onoff-handle{background-position:0px 0px}.console-onoff-on .onoff-loading{background-position:32px 4px}.console-onoff-off{background-position:0px -60px}.console-onoff-off .onoff-handle{background-position:-28px 0px}.console-onoff-off .onoff-loading{background-position:4px 4px}.console-onoff[disabled="disabled"]{cursor:not-allowed;background-position:0px -80px}.console-onoff[disabled="disabled"] .onoff-loading{display:none}.console-onoff-on[disabled="disabled"] .onoff-handle{background-position:0px -20px}.console-onoff-off[disabled="disabled"] .onoff-handle{background-position:-28px -20px}.console-number-spinner{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;vertical-align:middle}.console-number-spinner .form-control{width:auto;float:left;text-indent:-16px}.console-number-spinner .console-number-spinner-action{width:14px;height:30px;float:left;margin-left:-16px;border-left:1px solid #E3E3E3;margin-top:1px}.console-number-spinner .console-number-spinner-action button{width:14px;height:15px;overflow:hidden;line-height:16px;font-size:12px;border:0px;background-color:transparent;padding:0px;margin:0px;display:block;color:#999;text-align:center;outline:0px}.console-number-spinner .console-number-spinner-action button:hover{color:#06C}.console-number-spinner .console-number-spinner-action button[disabled]{color:#999}.console-number-spinner .console-number-spinner-action .console-number-spinner-down{border-top:1px solid #E3E3E3}.console-timepicker{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;vertical-align:middle}.console-datepicker{padding:8px}.console-datepicker thead .h6 th{padding-top:8px}.console-datepicker tbody tr:first-child td{padding-top:8px}.console-datepicker tbody .btn{border:0px !important}.console-datepicker tbody .btn:hover{background:#F3F3F3}.console-datepicker tbody .btn-default{background:transparent}.console-datepicker tbody .active,.console-datepicker tbody .active:hover,.console-datepicker tbody .active span{background:#3C0;color:#FFF}.console-datepicker tbody .btn[disabled="disabled"] .btn[disabled="disabled"] span{color:#CCC}.console-datepicker em{font-size:12px;color:#ACD}.aliyun-console-topbar{position:relative;z-index:100;clear:both;height:50px;background:#09C;font-size:12px;min-width:990px}.aliyun-console-topbar a{text-decoration:none}.aliyun-console-topbar a:focus{outline:none}.aliyun-console-topbar .accessibility-ast{position:absolute;top:-10000px;left:-10000px;width:100px}.aliyun-console-topbar .accessibility-ast:focus{position:absolute;top:0;left:310px}.aliyun-console-topbar .icon-arrow-down{display:inline-block;width:18px;text-align:center;vertical-align:middle;transition:transform 0.2s, vertical-align 0.2s;-o-transition:transform 0.2s, vertical-align 0.2s;-ms-transition:transform 0.2s, vertical-align 0.2s;-moz-transition:transform 0.2s, vertical-align 0.2s;-webkit-transition:transform 0.2s, vertical-align 0.2s}.aliyun-console-topbar .dropdown .dropdown-menu{z-index:1;font-size:12px;border-radius:0;-webkit-box-shadow:0 1px 3px rgba(0,0,0,0.1);-moz-box-shadow:0 1px 3px rgba(0,0,0,0.1);box-shadow:0 1px 3px rgba(0,0,0,0.1)}.aliyun-console-topbar .dropdown .dropdown-menu a{padding:0}.aliyun-console-topbar .dropdown.open .icon-arrow-down{vertical-align:text-top;transform:rotate(180deg);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg)}.aliyun-console-topbar .topbar-wrap,.aliyun-console-topbar .topbar-logo,.aliyun-console-topbar .topbar-home,.aliyun-console-topbar .topbar-home-link,.aliyun-console-topbar .topbar-nav,.aliyun-console-topbar .topbar-info{height:100%}.aliyun-console-topbar .topbar-left{float:left}.aliyun-console-topbar .topbar-right{float:right}.aliyun-console-topbar .topbar-clearfix:before,.aliyun-console-topbar .topbar-clearfix:after{display:table;content:" "}.aliyun-console-topbar .topbar-clearfix:after{clear:both}.aliyun-console-topbar .topbar-head{background:#008fbf;height:50px;position:relative;z-index:3}.aliyun-console-topbar .topbar-nav{position:relative;z-index:2;background:#09C}.aliyun-console-topbar .topbar-logo,.aliyun-console-topbar .topbar-home{display:block;width:50px;background:#0099cc;font-size:28px;color:#FFF;text-align:center;line-height:50px}.aliyun-console-topbar .topbar-logo span,.aliyun-console-topbar .topbar-home span{line-height:50px}.aliyun-console-topbar .topbar-logo{background:#0087b4}.aliyun-console-topbar .topbar-home{margin-right:1px;font-size:20px}.aliyun-console-topbar .topbar-home:hover{background:#008fbf}.aliyun-console-topbar .topbar-home-link{padding:0 20px;margin-right:1px;background:#09c}.aliyun-console-topbar .topbar-home{-o-transition:all 0.15s, 0.15s;-ms-transition:all 0.15s, 0.15s;-moz-transition:all 0.15s, 0.15s;-webkit-transition:all 0.15s, 0.15s}.aliyun-console-topbar .topbar-btn{color:#fff;font-size:14px;line-height:50px}.aliyun-console-topbar .topbar-btn:hover,.aliyun-console-topbar .topbar-btn.topbar-btn-dark{background:#008fbf}.aliyun-console-topbar .topbar-nav.open .topbar-nav-btn{background:#fff;color:#333}.aliyun-console-topbar .topbar-nav-btn{padding:0 20px;display:inline-block;height:50px}.aliyun-console-topbar .topbar-nav-list{border:none;padding:10px;margin-top:0;white-space:nowrap}.aliyun-console-topbar .topbar-nav-list .topbar-nav-col{display:inline-block;vertical-align:top;padding:0 10px}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item .topbar-nav-item-title{margin:3px 0px;color:#999;font-weight:600}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item .topbar-nav-gap{border-top:1px solid #f2f2f2;width:100%;margin:6px 0 10px}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul{padding:0;margin:8px 0 0 0;list-style:none}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li{min-width:160px;height:28px;line-height:28px;margin-bottom:2px}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li a{display:block;height:100%;padding:0 10px;text-decoration:none;color:#333}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li a:hover{background-color:#f2f2f2}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li a .topbar-nav-item-icon{padding-right:2px;font-size:16px;vertical-align:text-bottom;display:inline-block}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li.topbar-unservice a{color:#999}.aliyun-console-topbar .topbar-nav-list .topbar-nav-item ul li.topbar-unservice a .topbar-nav-item-icon{color:#999}.aliyun-console-topbar .topbar-info{background:#008fbf;position:absolute;z-index:1;top:0;right:0}.aliyun-console-topbar .topbar-info .topbar-btn{padding:0 10px;height:50px;display:block;z-index:2;background:#09c}.aliyun-console-topbar .topbar-info .topbar-btn:hover,.aliyun-console-topbar .topbar-info .topbar-btn.topbar-btn-dark{background:#008fbf}.aliyun-console-topbar .topbar-info .topbar-btn.open{position:relative}.aliyun-console-topbar .topbar-info .topbar-btn-search{padding:0;margin-left:1px}.aliyun-console-topbar .topbar-info .topbar-info-gap{color:#fff}.aliyun-console-topbar .topbar-info .dropdown .dropdown-menu{width:100%;min-width:0;margin:0;border:none}.aliyun-console-topbar .topbar-info .dropdown.open .topbar-btn{color:#333;background:#fff;border-bottom:1px solid #eaedf1;position:relative}.aliyun-console-topbar .topbar-info .topbar-info-btn{height:40px;border-bottom:1px solid #eaedf1}.aliyun-console-topbar .topbar-info .topbar-info-btn a{line-height:39px;padding-left:10px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu{width:310px;left:auto;right:0}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity{height:80px;padding:8px 16px;position:relative}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity .user-identity-item{height:32px;line-height:32px;display:block}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity .user-identity-colon{padding:0 5px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity-sign{padding:2px 6px;background:#7ecef4;color:#fff;border-radius:1px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-identity-sign-wrap{position:absolute;top:14px;right:30px}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-btn-link{display:inline-block;color:#06C}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-btn-link:hover{background:none;text-decoration:underline}.aliyun-console-topbar .topbar-info .topbar-user-large .dropdown-menu .user-btn-link.user-btn-link-signout{float:right;padding:0 16px}.aliyun-console-topbar .topbar-info-item{display:inline-block;margin-left:1px}.aliyun-console-topbar .topbar-notice{position:relative;font-size:12px;margin-left:1px;padding:0 12px 0 8px !important}.aliyun-console-topbar .topbar-notice .topbar-notice-panel{display:none}.aliyun-console-topbar .topbar-notice.open .topbar-notice-panel{display:block}.aliyun-console-topbar .topbar-notice .topbar-notice-panel{position:absolute;top:48px;left:-185px;width:440px;border-radius:2px;z-index:15;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.175);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.175);box-shadow:0 1px 2px rgba(0,0,0,0.175)}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-arrow{background:url(images/notice-arrow.png) 0 0 no-repeat;width:11px;height:6px;position:absolute;top:-6px;left:220px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-head{height:50px;background-color:#eaedf1;padding:0 15px;line-height:50px;color:#333;font-size:14px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body{height:300px;overflow-y:auto;background:#fff;font-size:12px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul{list-style:none;margin:0;padding:0}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li{height:60px;line-height:20px;border-bottom:1px solid #eaedf1}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li a{display:block;height:100%;padding:10px 10px;background:#fff;color:#333}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li a .topbar-notice-link{display:block;max-width:300px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;color:#06c}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li a:hover{background:#f9f9f9}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li.topbar-notice-readed a{color:#666}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body ul li.topbar-notice-readed a .topbar-notice-time{color:#999}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-body .topbar-notice-empty{text-align:center;color:#666;margin-top:80px}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-foot{height:50px;line-height:50px;background:#fff;text-align:center}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-class{padding:8px 0;float:right}.aliyun-console-topbar .topbar-notice .topbar-notice-panel .topbar-notice-class .topbar-notice-class-name{display:block;height:24px;line-height:24px;width:66px;background:#eaedf1;text-align:center;border-radius:3px}.aliyun-console-topbar .topbar-btn-notice{width:auto;display:block;height:50px}.aliyun-console-topbar .topbar-btn-notice .topbar-btn-notice-icon{font-size:24px;line-height:50px;vertical-align:text-bottom;color:#fff}.aliyun-console-topbar .topbar-btn-notice .topbar-btn-notice-num{font-size:12px;color:#fff;background:#ff9900;border-radius:5px;padding:2px 5px;display:inline-block;margin-top:15px;line-height:16px;vertical-align:top;text-align:center}.aliyun-console-topbar .topbar-btn-notice .topbar-btn-notice-num.topbar-btn-notice-num-zero{color:#00ace9;background-color:#0087b4}.aliyun-console-topbar .topbar-btn-notice .topbar-nav-item-short{padding-left:2px}.aliyun-console-topbar .topbar-qrcode{position:relative;margin-left:1px}.aliyun-console-topbar .topbar-qrcode:hover .topbar-qrcode-panel{display:block}.aliyun-console-topbar .topbar-qrcode .topbar-qrcode-panel{top:50px;left:0;position:absolute;width:130px;padding:12px 8px;background:#fff;border:1px solid #eaedf1;box-shadow:0 1px 3px rgba(0,0,0,0.1);display:none}.aliyun-console-topbar .topbar-qrcode .topbar-qrcode-image{width:100px;margin:0 auto}.aliyun-console-topbar .topbar-qrcode .topbar-qrcode-title{text-align:center;padding-top:10px}.aliyun-console-topbar .topbar-new-icon{position:relative;top:-4px;padding-left:2px}.aliyun-console-topbar-search{position:relative;z-index:1}.aliyun-console-topbar-search:hover{background:#008fbf}.aliyun-console-topbar-search:hover .topbar-search-ask{background:#008fbf}.aliyun-console-topbar-search .topbar-search-ask{width:200px;height:50px;border:0;background:#09c;line-height:26px;padding:12px 30px 12px 10px;display:block;color:#fff;-webkit-border-radius:1px 1px;-moz-border-radius:1px / 1px;border-radius:1px / 1px;-o-transition:all 0.15s, 0.15s;-ms-transition:all 0.15s, 0.15s;-moz-transition:all 0.15s, 0.15s;-webkit-transition:all 0.15s, 0.15s}.aliyun-console-topbar-search .topbar-search-ask:focus{outline:none}.aliyun-console-topbar-search .topbar-search-ask-shade{color:#00ace9}.aliyun-console-topbar-search .topbar-search-mark{font-size:16px;line-height:50px;position:absolute;height:50px;width:40px;color:#fff;text-decoration:none;display:block;text-align:center;top:0;right:0}.aliyun-console-topbar-search .topbar-search-mark .icon-search,.aliyun-console-topbar-search .topbar-search-mark .icon-enter{line-height:50px}.aliyun-console-topbar-search.topbar-search-active{background:#008fbf}.aliyun-console-topbar-search.topbar-search-active .topbar-search-ask{background:#008fbf}.aliyun-console-topbar-search.topbar-search-active .topbar-search-ask-shade{color:#fff}.aliyun-console-topbar-search-v1_3_21{position:relative}.aliyun-console-topbar-search-v1_3_21.topbar-search-dropdown-open .topbar-btn{background:#008fbf}.aliyun-console-topbar-search-v1_3_21 .icon-search{font-size:16px;padding-right:4px;position:relative;top:2px}.aliyun-console-topbar-search-v1_3_21 .topbar-search-dropdown{height:38px;position:absolute;bottom:-38px;right:-1px;border:2px solid #008fbf;background:#fff}.aliyun-console-topbar-search-v1_3_21 .topbar-search-dropdown input{display:block;height:34px;padding:4px 6px;margin-right:30px;width:250px;border-width:0;outline:0;line-height:34px;color:#546478;font-size:12px}.aliyun-console-topbar-search-v1_3_21 .topbar-search-dropdown .topbar-search-mark{position:absolute;right:0;top:0;height:34px;width:34px;display:block;line-height:34px;text-align:center;color:#546478}.aliyun-console-topbar-help{position:fixed;top:0;right:0;bottom:0}.aliyun-console-topbar-help .topbar-help-inner{width:486px;overflow:hidden;background:#fff;border-left:1px solid #e1e6eb;position:absolute;right:-486px;-webkit-box-shadow:0 0px 10px rgba(0,0,0,0.1);-moz-box-shadow:0 0px 10px rgba(0,0,0,0.1);box-shadow:0 0px 10px rgba(0,0,0,0.1);-o-transition:all 0.2s ease, 0.2s ease;-ms-transition:all 0.2s ease, 0.2s ease;-moz-transition:all 0.2s ease, 0.2s ease;-webkit-transition:all 0.2s ease, 0.2s ease;z-index:1;top:50px;bottom:0}.aliyun-console-topbar-help .topbar-help-inner.topbar-help-show{right:0px}.aliyun-console-topbar-help .topbar-help-head{height:68px;padding-left:20px;line-height:68px;border-bottom:1px solid #e1e6eb;position:relative;color:#333}.aliyun-console-topbar-help .topbar-help-body{position:absolute;top:68px;bottom:0;background:#fff}.aliyun-console-topbar-help .topbar-help-iframe{height:100%}.aliyun-console-topbar-help .topbar-help-close{font-size:18px;float:right;height:68px;width:68px;line-height:68px !important;text-align:center;color:#546478;cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.aliyun-console-topbar-help .topbar-help-close:hover{color:#000}.console-topbar-new{position:relative;z-index:100;clear:both;height:40px;background:#34383c;font-size:12px;min-width:1000px}.console-topbar-new .console-topbar-btn{width:50px;height:40px;display:inline-block;vertical-align:middle;margin-right:1px;background-color:#2a2e31;text-decoration:none;text-align:center;color:#fff;line-height:40px;-o-transition:all 0.3s;-ms-transition:all 0.3s;-moz-transition:all 0.3s;-webkit-transition:all 0.3s}.console-topbar-new .console-topbar-btn .console-topbar-btn-text{font-size:14px;text-align:center;white-space:nowrap;display:none}.console-topbar-new .console-topbar-btn .console-topbar-btn-icon{font-size:22px;display:inline;line-height:40px;color:#fff}.console-topbar-new .console-topbar-btn .caret{-o-transition:-o-transform 0.3s;-ms-transition:-ms-transform 0.3s;-moz-transition:-moz-transform 0.3s;-webkit-transition:-webkit-transform 0.3s;transition:transform 0.3s}.console-topbar-new .console-topbar-btn:hover{width:auto}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-inverse,.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-inverse-white{background-color:#585e65;color:#fff}.console-topbar-new .console-topbar-btn:hover .console-topbar-btn-text{display:inline}.console-topbar-new .console-topbar-btn:hover .console-topbar-btn-icon{display:none;vertical-align:text-bottom}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-home{width:106px}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-nav{width:120px}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-ak{width:104px}.console-topbar-new .console-topbar-btn:hover.console-topbar-btn-workorder{width:94px}.console-topbar-new .console-topbar-btn.console-topbar-btn-last{margin-right:0}.console-topbar-new .console-topbar-btn.console-topbar-logo-icon{-o-transition:none;-ms-transition:none;-moz-transition:none;-webkit-transition:none;color:#2a2e31}.console-topbar-new .console-topbar-btn.console-topbar-logo-icon img{width:22px;height:22px;display:block;margin:9px 14px}.console-topbar-new .console-topbar-btn.console-topbar-nav-link{font-size:12px;width:auto;padding:0 15px}.console-topbar-new .console-topbar-btn.console-topbar-nav-link .console-topbar-nav-link-icon{width:16px;height:19px;vertical-align:middle;position:relative;display:inline-block;margin-right:4px;overflow:hidden;font-size:16px}.console-topbar-new .console-topbar-btn.console-topbar-btn-user{width:auto}.console-topbar-new .console-topbar-btn.console-topbar-btn-user .console-topbar-btn-text{display:inline;padding:0 15px}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice{width:auto;padding:0 10px}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-btn-notice-icon{font-size:24px;line-height:40px;vertical-align:text-bottom}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-btn-notice-num{font-size:12px;color:#fff;background:#ff9900;border-radius:5px;padding:2px 1px;width:20px;display:inline-block;margin-top:10px;line-height:16px;vertical-align:top}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-btn-notice-num.console-topbar-btn-notice-num-zero{color:#999;background-color:#34383c}.console-topbar-new .console-topbar-btn.console-topbar-btn-notice .console-topbar-nav-item-short{padding-left:2px}.console-topbar-new .console-topbar-btn.console-topbar-btn-ak{overflow:hidden}.console-topbar-new .console-topbar-btn.console-topbar-btn-nav{overflow:hidden;position:relative;z-index:2}.console-topbar-new .console-topbar-nav .console-topbar-nav-list{border:1px solid #ddd;border-top:none;padding:10px;margin-top:0;margin-left:-1px}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-col{float:left;padding:0 10px}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item .console-topbar-nav-item-title{margin:3px 0px;color:#999;font-weight:600}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item .console-topbar-nav-gap{border-top:1px solid #f2f2f2;width:100%;margin:10px 0}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul{padding:0;margin:10px 0 0 0;list-style:none}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li{width:170px;height:30px;line-height:30px;margin-bottom:2px}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a{display:block;height:100%;padding-left:10px;text-decoration:none;color:#333}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a:hover{background-color:#f2f2f2}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon{padding-right:2px;font-size:16px;vertical-align:text-bottom}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ecs{color:#007eff}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-slb{color:#f27741}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-rds{color:#20f8b8}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-oss{color:#ade675}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-cdn{color:#bff3fe}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ots{color:#15d4f0}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ocs{color:#40ff8f}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-odps{color:#ffba00}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ace{color:#c8341c}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-yundun{color:#298edb}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-yunjiankong{color:#86f2af}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-sls{color:#075ac0}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-oas{color:#79df71}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ess{color:#0cf}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-mqs{color:#fff400}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-vpc{color:#6cf}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-opensearch{color:#5bc8e8}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-lightcloud{color:#6bbd52}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-pts{color:#009dff}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ons{color:#6b3100}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-dpc{color:#289de9}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-ads{color:#71ceec}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-mts{color:#f93}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li a .console-topbar-nav-item-icon.icon-drds{color:#6f9}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li.console-topbar-unservice a{color:#999}.console-topbar-new .console-topbar-nav .console-topbar-nav-list .console-topbar-nav-item ul li.console-topbar-unservice a .console-topbar-nav-item-icon{color:#999}.console-topbar-new .dropdown .dropdown-menu{z-index:1;border-radius:0;-webkit-box-shadow:0 2px 4px rgba(0,0,0,0.175);-moz-box-shadow:0 2px 4px rgba(0,0,0,0.175);box-shadow:0 2px 4px rgba(0,0,0,0.175)}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-inverse{background-color:#585e65;color:#fff}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-inverse-white{background-color:#fff;color:#333}.console-topbar-new .dropdown.open .console-topbar-btn .console-topbar-btn-text{display:inline}.console-topbar-new .dropdown.open .console-topbar-btn .console-topbar-btn-icon{display:none;vertical-align:text-bottom}.console-topbar-new .dropdown.open .console-topbar-btn:hover.console-topbar-btn-inverse-white{background-color:#fff !important;color:#333 !important}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-nav{width:120px}.console-topbar-new .dropdown.open .console-topbar-btn.console-topbar-btn-workorder{width:94px}.console-topbar-new .dropdown.open .console-topbar-btn .caret{transform:rotate(180deg);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg)}.console-topbar-new .console-topbar-user .dropdown-menu,.console-topbar-new .console-topbar-dropdown .dropdown-menu{margin-top:-1px}.console-topbar-new .console-topbar-user .dropdown-menu>li a,.console-topbar-new .console-topbar-dropdown .dropdown-menu>li a{padding:6px 20px}.console-topbar-new .console-topbar-workorder .dropdown-menu{min-width:96px}.console-topbar-new .console-topbar-workorder .dropdown-menu>li a{padding:6px 24px 6px 16px}.console-topbar-new .console-topbar-notice{position:relative}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel{display:none}.console-topbar-new .console-topbar-notice.open .console-topbar-notice-panel{display:block}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel{position:absolute;top:38px;left:-170px;width:390px;border:1px solid #bbb;border-radius:2px;z-index:15;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.175);-moz-box-shadow:0 1px 2px rgba(0,0,0,0.175);box-shadow:0 1px 2px rgba(0,0,0,0.175)}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-arrow{background:url(images/notice-arrow.png) 0 0 no-repeat;width:11px;height:6px;position:absolute;top:-6px;left:196px}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-head{height:40px;border-bottom:1px solid #ccc;background-color:#f2f2f2;padding:0 15px;line-height:40px;color:#333;font-size:14px}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body{height:240px;overflow-y:auto;background:#fff}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul{list-style:none;margin:0 5px;padding:0}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li{height:40px;line-height:40px;border-bottom:1px solid #ececec}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a{display:block;height:100%;padding:0 10px;background:#fff}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a span{display:block}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a .console-topbar-notice-link{float:left;max-width:272px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a .console-topbar-notice-time{float:right;color:#333}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li a:hover{background:#f9f9f9}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li.console-topbar-notice-readed a{color:#666}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body ul li.console-topbar-notice-readed a .console-topbar-notice-time{color:#999}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-body .console-topbar-notice-empty{text-align:center;color:#666;margin-top:80px}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-foot{height:48px;line-height:48px;background:#fff}.console-topbar-new .console-topbar-notice .console-topbar-notice-panel .console-topbar-notice-foot .console-topbar-notice-more{padding-right:15px}.console-topbar-new .console-topbar-locale{float:left}.console-topbar-new .console-topbar-locale .dropdown-menu{right:0;left:auto;margin-top:-1px;width:50px;min-width:60px}.console-topbar-new .console-topbar-locale .console-topbar-btn.console-topbar-btn-locale{width:60px;background:none}.console-topbar-new .console-topbar-locale .console-topbar-btn.console-topbar-btn-locale .console-topbar-btn-text{display:block}.console-topbar-new.console-topbar-new-en .console-topbar-btn:hover.console-topbar-btn-home{width:116px}.console-topbar-new.console-topbar-new-en .console-topbar-btn:hover.console-topbar-btn-nav{width:170px}.console-topbar-new.console-topbar-new-en .console-topbar-btn:hover.console-topbar-btn-workorder{width:146px}.console-topbar-new.console-topbar-new-en .console-topbar-nav .console-topbar-nav-item ul li{width:auto !important}.console-topbar-new.console-topbar-new-en .console-topbar-nav .console-topbar-nav-item ul li a{padding:0 10px}.console-topbar-new.console-topbar-new-en .dropdown.open .console-topbar-btn.console-topbar-btn-nav{width:170px}.console-topbar-new.console-topbar-new-en .dropdown.open .console-topbar-btn.console-topbar-btn-workorder{width:146px}.console-navbar{font-size:12px;color:#666666;word-wrap:break-word;height:56px;border:none;border-bottom:1px solid #dddddd;box-shadow:0px 0px 4px rgba(0,0,0,0.1);position:relative;box-sizing:content-box;margin-bottom:0px;background-color:#fff;border-radius:0 !important;z-index:2}.console-navbar *{box-sizing:content-box}.console-navbar a{color:#333}.console-navbar .console-navbar-title{float:left;line-height:56px;padding:0 40px 0 14px;font-size:18px;color:#999999}.console-navbar .console-navbar-title .console-navbar-subtitle{margin-right:5px}.console-navbar .nav li{float:left;display:inline;margin:0 20px;height:56px;font-size:14px}.console-navbar .nav li a{padding:0 2px;float:left;height:55px;color:#333333;line-height:56px;text-decoration:none}.console-navbar .nav li a:hover,.console-navbar .nav li a:focus{background-color:#fff}.console-navbar .nav li.active{height:55px}.console-navbar .nav li.active a{color:#ff4902;border-bottom:2px solid #ff4902}.console-navbar .console-navbar-a-default{cursor:default}.console-navbar .console-navbar-links-example{margin-top:15px;padding:0 15px 0;line-height:24px;border-left:1px solid #eeeeee}.console-navbar .console-navbar-links-example a{color:#b3b3b3}.console-title{padding:16px 0px;min-height:70px}.console-title .nav-pills{display:inline-block;vertical-align:bottom}.console-title .nav-pills li a,.console-title .nav-pills li a:focus,.console-title .nav-pills li button,.console-title .nav-pills li button:focus{padding:6px 6px}.console-title h1,.console-title h2,.console-title h3,.console-title h4,.console-title h5,.console-title h6{display:inline-block;text-indent:8px;border-left:2px solid #88B7E0;margin-top:0px;margin-bottom:0px;margin-right:8px}.console-title h1{margin-top:0px;margin-bottom:0px}.console-title h2{margin-top:2px;margin-bottom:2px}.console-title h3{margin-top:4px;margin-bottom:4px}.console-title h4{margin-top:6px;margin-bottom:6px}.console-title h5{margin-top:8px;margin-bottom:8px}.console-title-border{border-bottom:1px solid #DDD}.console-sub-title{position:relative;padding-left:16px;margin-bottom:-1px;display:table;width:100%;z-index:1;height:40px;border:1px solid #E1E6EB;border-left:3px solid #778;background-color:#F4F5F9}.console-sub-title h5{color:#666;font-size:14px}.console-box-border{border:1px solid #E1E6EB}.margin-left,.margin-left-1{margin-left:8px !important}.margin-left-2{margin-left:16px !important}.margin-left-3{margin-left:24px !important}.margin-left-4{margin-left:32px !important}.margin-right,.margin-right-1{margin-right:8px !important}.margin-right-2{margin-right:16px !important}.margin-right-3{margin-right:24px !important}.margin-right-4{margin-right:32px !important}.margin-top,.margin-top-1{margin-top:8px !important}.margin-top-2{margin-top:16px !important}.margin-top-3{margin-top:24px !important}.margin-top-4{margin-top:32px !important}.row-padding-1{padding-top:8px;padding-bottom:8px}.row-padding,.row-padding-2{padding-top:16px;padding-bottom:16px}.row-padding-3{padding-top:24px;padding-bottom:24px}.row-padding-4{padding-top:32px;padding-bottom:32px}.row-margin-1{margin-top:8px;margin-bottom:8px}.row-margin,.row-margin-2{margin-top:16px;margin-bottom:16px}.row-margin-3{margin-top:24px;margin-bottom:24px}.row-margin-4{margin-top:32px;margin-bottom:32px}.col-padding-1{padding-left:8px;padding-right:8px}.col-padding,.col-padding-2{padding-left:16px;padding-right:16px}.col-padding-3{padding-left:24px;padding-right:24px}.col-padding-4{padding-left:32px;padding-right:32px}.col-margin-1{margin-left:8px;margin-right:8px}.col-margin,.col-margin-2{margin-left:16px;margin-right:16px}.col-margin-3{margin-left:24px;margin-right:24px}.col-margin-4{margin-left:32px;margin-right:32px}.inline-block{display:inline-block !important;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline}.partition{display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;padding:0px 4px}.no-data{padding:24px 0px;text-align:center;color:#666}@font-face{font-family:'aliyun-console-font';src:url("fonts/aliyun-console-font.eot?t91au5");src:url("fonts/aliyun-console-font.eot?t91au5#iefix") format("embedded-opentype"),url("fonts/aliyun-console-font.ttf?t91au5") format("truetype"),url("fonts/aliyun-console-font.woff?t91au5") format("woff"),url("fonts/aliyun-console-font.svg?t91au5#aliyun-console-font") format("svg");font-weight:normal;font-style:normal}[class^="icon-"],[class*=" icon-"]{font-family:'aliyun-console-font' !important;speak:none;font-style:normal;font-weight:normal;font-variant:normal;text-transform:none;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.icon-logo2:before{content:"\e63b"}.icon-logo1:before{content:"\e63a"}.icon-logo-new:before{content:"\e97f"}.icon-dms-2:before{content:"\e92d"}.icon-dms-3:before{content:"\e92e"}.icon-dms:before{content:"\e92f"}.icon-gpdb-2:before{content:"\e983"}.icon-gpdb-3:before{content:"\e984"}.icon-gpdb:before{content:"\e985"}.icon-schedulerx-2:before{content:"\e986"}.icon-schedulerx-3:before{content:"\e987"}.icon-schedulerx:before{content:"\e988"}.icon-txc-2:before{content:"\e989"}.icon-txc-3:before{content:"\e98a"}.icon-txc:before{content:"\e98b"}.icon-csb-2:before{content:"\e909"}.icon-csb-3:before{content:"\e90a"}.icon-csb:before{content:"\e90b"}.icon-mobsec-2:before{content:"\e96d"}.icon-mobsec-3:before{content:"\e96e"}.icon-mobsec:before{content:"\e96f"}.icon-mss-2:before{content:"\e970"}.icon-mss-3:before{content:"\e971"}.icon-mss:before{content:"\e972"}.icon-sos-2:before{content:"\e973"}.icon-sos-3:before{content:"\e974"}.icon-sos:before{content:"\e975"}.icon-sppc-2:before{content:"\e976"}.icon-sppc-3:before{content:"\e977"}.icon-sppc:before{content:"\e978"}.icon-webfirewall-2:before{content:"\e979"}.icon-webfirewall-3:before{content:"\e97a"}.icon-webfirewall:before{content:"\e97b"}.icon-xianzhi-2:before{content:"\e97c"}.icon-xianzhi-3:before{content:"\e97d"}.icon-xianzhi:before{content:"\e97e"}.icon-livevideo-2:before{content:"\e964"}.icon-livevideo-3:before{content:"\e965"}.icon-livevideo:before{content:"\e966"}.icon-slm-2:before{content:"\e967"}.icon-slm-3:before{content:"\e968"}.icon-slm:before{content:"\e969"}.icon-vod-2:before{content:"\e96a"}.icon-vod-3:before{content:"\e96b"}.icon-vod:before{content:"\e96c"}.icon-kms-2:before{content:"\e95e"}.icon-kms-3:before{content:"\e95f"}.icon-kms:before{content:"\e960"}.icon-nas-2:before{content:"\e961"}.icon-nas-3:before{content:"\e962"}.icon-nas:before{content:"\e963"}.icon-apigateway-2:before{content:"\e94f"}.icon-apigateway-3:before{content:"\e950"}.icon-apigateway:before{content:"\e951"}.icon-oceanbase-2:before{content:"\e952"}.icon-oceanbase-3:before{content:"\e953"}.icon-oceanbase:before{content:"\e954"}.icon-petadata-2:before{content:"\e955"}.icon-petadata-3:before{content:"\e956"}.icon-petadata:before{content:"\e957"}.icon-ecsm-2:before{content:"\e958"}.icon-ecsm-3:before{content:"\e959"}.icon-ecsm:before{content:"\e95a"}.icon-yundunzhengshu-2:before{content:"\e95b"}.icon-yundunzhengshu-3:before{content:"\e95c"}.icon-yundunzhengshu:before{content:"\e95d"}.icon-cdi-2:before{content:"\e93a"}.icon-cdi-3:before{content:"\e93b"}.icon-cdi:before{content:"\e93c"}.icon-disk-2:before{content:"\e93d"}.icon-disk-3:before{content:"\e93e"}.icon-disk:before{content:"\e93f"}.icon-dsi-2:before{content:"\e940"}.icon-dsi-3:before{content:"\e941"}.icon-dsi:before{content:"\e942"}.icon-hpc-2:before{content:"\e943"}.icon-hpc-3:before{content:"\e944"}.icon-hpc:before{content:"\e945"}.icon-httpdns-2:before{content:"\e946"}.icon-httpdns-3:before{content:"\e947"}.icon-httpdns:before{content:"\e948"}.icon-iot-2:before{content:"\e949"}.icon-iot-3:before{content:"\e94a"}.icon-iot2:before{content:"\e94b"}.icon-vipaegis-2:before{content:"\e94c"}.icon-vipaegis-3:before{content:"\e94d"}.icon-vipaegis:before{content:"\e94e"}.icon-cs-2:before{content:"\e92a"}.icon-cs-3:before{content:"\e92b"}.icon-cs:before{content:"\e92c"}.icon-ewh-2:before{content:"\e930"}.icon-ewh-3:before{content:"\e931"}.icon-ewh:before{content:"\e932"}.icon-expressconnect-2:before{content:"\e933"}.icon-expressconnect-3:before{content:"\e934"}.icon-expressconnect:before{content:"\e935"}.icon-hsm-2:before{content:"\e936"}.icon-hsm-3:before{content:"\e937"}.icon-hsm:before{content:"\e938"}.icon-kuaizhaolian:before{content:"\e939"}.icon-mongodb-2:before{content:"\e927"}.icon-mongodb-3:before{content:"\e928"}.icon-mongodb:before{content:"\e929"}.icon-actiontrail-2:before{content:"\e90f"}.icon-actiontrail-3:before{content:"\e910"}.icon-actiontrail:before{content:"\e911"}.icon-ats-2:before{content:"\e912"}.icon-ats-3:before{content:"\e913"}.icon-ats:before{content:"\e914"}.icon-cli-2:before{content:"\e915"}.icon-cli-3:before{content:"\e916"}.icon-cli:before{content:"\e917"}.icon-directmail-2:before{content:"\e918"}.icon-directmail-3:before{content:"\e919"}.icon-directmail:before{content:"\e91a"}.icon-eclipse-2:before{content:"\e91b"}.icon-eclipse-3:before{content:"\e91c"}.icon-eclipse:before{content:"\e91d"}.icon-havip-2:before{content:"\e91e"}.icon-havip-3:before{content:"\e91f"}.icon-havip:before{content:"\e920"}.icon-ros-2:before{content:"\e921"}.icon-ros-3:before{content:"\e922"}.icon-ros:before{content:"\e923"}.icon-visualstudio-2:before{content:"\e924"}.icon-visualstudio-3:before{content:"\e925"}.icon-visualstudio:before{content:"\e926"}.icon-emr-2:before{content:"\e90c"}.icon-emr-3:before{content:"\e90d"}.icon-emr:before{content:"\e90e"}.icon-antifraud-3:before{content:"\e903"}.icon-antifraud:before{content:"\e904"}.icon-antifraud-2:before{content:"\e905"}.icon-ddosbasic:before{content:"\e906"}.icon-ddosbasic-3:before{content:"\e907"}.icon-ddosbasic-2:before{content:"\e908"}.icon-aegis:before{content:"\e900"}.icon-aegis-3:before{content:"\e901"}.icon-aegis-2:before{content:"\e902"}.icon-amr-2:before{content:"\e71c"}.icon-amr-3:before{content:"\e71d"}.icon-amr:before{content:"\e71e"}.icon-eip-2:before{content:"\e71f"}.icon-eip-3:before{content:"\e720"}.icon-eip:before{content:"\e721"}.icon-expense-i18n:before{content:"\e71b"}.icon-aps-2:before{content:"\e715"}.icon-aps-3:before{content:"\e716"}.icon-aps:before{content:"\e717"}.icon-batchcompute-2:before{content:"\e718"}.icon-batchcompute-3:before{content:"\e719"}.icon-batchcompute:before{content:"\e71a"}.icon-sas-2:before{content:"\e70c"}.icon-sas-3:before{content:"\e70d"}.icon-sas:before{content:"\e70e"}.icon-scan-2:before{content:"\e70f"}.icon-scan-3:before{content:"\e710"}.icon-scan:before{content:"\e711"}.icon-waf-2:before{content:"\e712"}.icon-waf-3:before{content:"\e713"}.icon-waf:before{content:"\e714"}.icon-mns-2:before{content:"\e709"}.icon-mns-3:before{content:"\e70a"}.icon-mns:before{content:"\e70b"}.icon-qrcode:before{content:"\e708"}.icon-unfold:before{content:"\e707"}.icon-fold:before{content:"\e706"}.icon-form:before{content:"\e6fd"}.icon-accelerate:before{content:"\e6fe"}.icon-feedback:before{content:"\e702"}.icon-vdc-2:before{content:"\e703"}.icon-vdc-3:before{content:"\e704"}.icon-vdc:before{content:"\e705"}.icon-new:before{content:"\e6fc"}.icon-collapse-right:before{content:"\e6fb"}.icon-collapse-left:before{content:"\e6fa"}.icon-aec:before{content:"\e6f3"}.icon-aic:before{content:"\e6f4"}.icon-mobile-2:before{content:"\e6f5"}.icon-amc:before{content:"\e6f6"}.icon-arc:before{content:"\e6f7"}.icon-game:before{content:"\e6f8"}.icon-iot:before{content:"\e6f9"}.icon-right:before{content:"\e6f2"}.icon-afc:before{content:"\e6f0"}.icon-specs:before{content:"\e6f1"}.icon-pen-2:before{content:"\e6c8"}.icon-key:before{content:"\e635"}.icon-bsn:before{content:"\e6ea"}.icon-mac-2:before{content:"\e6eb"}.icon-mac-3:before{content:"\e6ec"}.icon-mac:before{content:"\e6ed"}.icon-fenxiao:before{content:"\e6ee"}.icon-account-2:before{content:"\e6ef"}.icon-qiyeyouxiang-2:before{content:"\e6be"}.icon-qiyeyouxiang-3:before{content:"\e6bf"}.icon-qiyeyouxiang:before{content:"\e6c0"}.icon-yuming-2:before{content:"\e6d3"}.icon-yuming-3:before{content:"\e6df"}.icon-yuming:before{content:"\e6e0"}.icon-yumingyuwangzhan-2:before{content:"\e6e1"}.icon-yumingyuwangzhan-3:before{content:"\e6e2"}.icon-yumingyuwangzhan:before{content:"\e6e3"}.icon-yunjiexi-2:before{content:"\e6e4"}.icon-yunjiexi-3:before{content:"\e6e5"}.icon-yunjiexi:before{content:"\e6e6"}.icon-yunxunizhuji-2:before{content:"\e6e7"}.icon-yunxunizhuji-3:before{content:"\e6e8"}.icon-yunxunizhuji:before{content:"\e6e9"}.icon-api-3:before{content:"\e6d2"}.icon-api-2:before{content:"\e6d4"}.icon-api:before{content:"\e6d5"}.icon-dpa-2:before{content:"\e6d6"}.icon-dpa-3:before{content:"\e6d7"}.icon-dpa:before{content:"\e6d8"}.icon-lvwang-2:before{content:"\e6d9"}.icon-lvwang-3:before{content:"\e6da"}.icon-lvwang:before{content:"\e6db"}.icon-mas-2:before{content:"\e6dc"}.icon-mas-3:before{content:"\e6dd"}.icon-mas:before{content:"\e6de"}.icon-dts-2:before{content:"\e6cf"}.icon-dts-3:before{content:"\e6d0"}.icon-dts:before{content:"\e6d1"}.icon-android:before{content:"\e6c9"}.icon-cps-2:before{content:"\e6ca"}.icon-cps-3:before{content:"\e6cb"}.icon-cps:before{content:"\e6cc"}.icon-ios:before{content:"\e6cd"}.icon-vitality:before{content:"\e6ce"}.icon-dfs-2:before{content:"\e6bb"}.icon-dfs-3:before{content:"\e6bc"}.icon-dfs:before{content:"\e6bd"}.icon-edas-2:before{content:"\e6c1"}.icon-edas-3:before{content:"\e6c2"}.icon-edas:before{content:"\e6c3"}.icon-enter:before{content:"\e6c4"}.icon-usableCenter-2:before{content:"\e6c5"}.icon-usableCenter-3:before{content:"\e6c6"}.icon-usableCenter:before{content:"\e6c7"}.icon-ace-2:before{content:"\e600"}.icon-ace:before{content:"\e601"}.icon-add-1:before{content:"\e602"}.icon-add-2:before{content:"\e603"}.icon-add:before{content:"\e604"}.icon-ads-2:before{content:"\e605"}.icon-ads:before{content:"\e606"}.icon-amplify:before{content:"\e607"}.icon-arrow-down:before{content:"\e608"}.icon-arrow-left:before{content:"\e609"}.icon-arrow-right:before{content:"\e60a"}.icon-arrow-up:before{content:"\e60b"}.icon-backup:before{content:"\e60c"}.icon-bell:before{content:"\e60d"}.icon-buy:before{content:"\e60e"}.icon-calendar:before{content:"\e60f"}.icon-cdn-2:before{content:"\e610"}.icon-cdn:before{content:"\e611"}.icon-cdp:before{content:"\e612"}.icon-clock:before{content:"\e613"}.icon-cloudisk:before{content:"\e614"}.icon-cloudisk2:before{content:"\e615"}.icon-db-g:before{content:"\e616"}.icon-db-r:before{content:"\e617"}.icon-db-sign:before{content:"\e618"}.icon-db-t:before{content:"\e619"}.icon-db:before{content:"\e61a"}.icon-ddos-2:before{content:"\e61b"}.icon-ddos:before{content:"\e61c"}.icon-detail-2:before{content:"\e61d"}.icon-detail:before{content:"\e61e"}.icon-disk-image:before{content:"\e61f"}.icon-down:before{content:"\e620"}.icon-dpc-2:before{content:"\e621"}.icon-dpc:before{content:"\e622"}.icon-drds-2:before{content:"\e623"}.icon-drds:before{content:"\e624"}.icon-ecs-2:before{content:"\e625"}.icon-ecs:before{content:"\e626"}.icon-ess-2:before{content:"\e627"}.icon-ess:before{content:"\e628"}.icon-exec-snapshot-policy:before{content:"\e629"}.icon-goback:before{content:"\e62a"}.icon-graphs:before{content:"\e62b"}.icon-help-1:before{content:"\e62c"}.icon-help-2:before{content:"\e62d"}.icon-help:before{content:"\e62e"}.icon-home:before{content:"\e62f"}.icon-info-1:before{content:"\e630"}.icon-info-2:before{content:"\e631"}.icon-info:before{content:"\e632"}.icon-invite:before{content:"\e633"}.icon-jiankong-2:before{content:"\e634"}.icon-lightcloud-2:before{content:"\e636"}.icon-lightcloud:before{content:"\e637"}.icon-log:before{content:"\e638"}.icon-logo:before{content:"\e639"}.icon-menu:before{content:"\e63c"}.icon-mqs-2:before{content:"\e63d"}.icon-mqs:before{content:"\e63e"}.icon-mts:before{content:"\e63f"}.icon-narrow:before{content:"\e640"}.icon-no-1:before{content:"\e641"}.icon-no-2:before{content:"\e642"}.icon-no:before{content:"\e643"}.icon-oas-2:before{content:"\e644"}.icon-oas:before{content:"\e645"}.icon-ocs-2:before{content:"\e646"}.icon-ocs:before{content:"\e647"}.icon-odps-2:before{content:"\e648"}.icon-odps:before{content:"\e649"}.icon-ons-2:before{content:"\e64a"}.icon-ons:before{content:"\e64b"}.icon-opensearch-2:before{content:"\e64c"}.icon-opensearch:before{content:"\e64d"}.icon-oss-2:before{content:"\e64e"}.icon-oss:before{content:"\e64f"}.icon-ots-2:before{content:"\e650"}.icon-ots:before{content:"\e651"}.icon-pen:before{content:"\e652"}.icon-performance:before{content:"\e653"}.icon-pts-2:before{content:"\e654"}.icon-pts:before{content:"\e655"}.icon-ram-2:before{content:"\e656"}.icon-ram:before{content:"\e657"}.icon-rds-2:before{content:"\e658"}.icon-rds:before{content:"\e659"}.icon-regional:before{content:"\e65a"}.icon-remove-1:before{content:"\e65b"}.icon-remove-2:before{content:"\e65c"}.icon-remove:before{content:"\e65d"}.icon-renew-mgt:before{content:"\e65e"}.icon-safe-lock:before{content:"\e65f"}.icon-safetycontrol:before{content:"\e660"}.icon-search:before{content:"\e661"}.icon-setup:before{content:"\e662"}.icon-shift-in:before{content:"\e663"}.icon-slb-2:before{content:"\e664"}.icon-slb:before{content:"\e665"}.icon-sls-2:before{content:"\e666"}.icon-sls:before{content:"\e667"}.icon-snapshot:before{content:"\e668"}.icon-text-free:before{content:"\e669"}.icon-threshold:before{content:"\e66a"}.icon-tree:before{content:"\e66b"}.icon-unlock:before{content:"\e66c"}.icon-up:before{content:"\e66d"}.icon-updown:before{content:"\e66e"}.icon-viewtable:before{content:"\e66f"}.icon-vpc-2:before{content:"\e670"}.icon-vpc:before{content:"\e671"}.icon-warning-1:before{content:"\e672"}.icon-warning-2:before{content:"\e673"}.icon-warning:before{content:"\e674"}.icon-weekly:before{content:"\e675"}.icon-yes-1:before{content:"\e676"}.icon-yes-2:before{content:"\e677"}.icon-yes:before{content:"\e678"}.icon-yundun-2:before{content:"\e679"}.icon-yundun:before{content:"\e67a"}.icon-yunjiankong:before{content:"\e67b"}.icon-annex:before{content:"\e67c"}.icon-renew:before{content:"\e67d"}.icon-renew-2:before{content:"\e67e"}.icon-plus-border:before{content:"\e67f"}.icon-wo-domain:before{content:"\e680"}.icon-wo-email:before{content:"\e681"}.icon-wo-host:before{content:"\e682"}.icon-wo-sitebuild:before{content:"\e683"}.icon-wo-salepre:before{content:"\e684"}.icon-wo-beian:before{content:"\e685"}.icon-wo-account:before{content:"\e686"}.icon-wo-finance:before{content:"\e687"}.icon-square:before{content:"\e688"}.icon-left:before{content:"\e689"}.icon-upload:before{content:"\e68a"}.icon-list-open:before{content:"\e68b"}.icon-pause:before{content:"\e68c"}.icon-list-close:before{content:"\e68d"}.icon-circle:before{content:"\e68e"}.icon-refresh:before{content:"\e68f"}.icon-return:before{content:"\e690"}.icon-undo:before{content:"\e691"}.icon-alipay:before{content:"\e692"}.icon-auto-renew:before{content:"\e693"}.icon-mobile:before{content:"\e694"}.icon-account:before{content:"\e695"}.icon-services:before{content:"\e696"}.icon-expense:before{content:"\e697"}.icon-redisa-2:before{content:"\e698"}.icon-redisa:before{content:"\e699"}.icon-ddos-3:before{content:"\e69a"}.icon-redisa-3:before{content:"\e69b"}.icon-toolsimage-2:before{content:"\e69c"}.icon-cdp-2:before{content:"\e69d"}.icon-mts-2:before{content:"\e69e"}.icon-toolsimage:before{content:"\e69f"}.icon-toolsimage-3:before{content:"\e6a0"}.icon-ons-3:before{content:"\e6a1"}.icon-ram-3:before{content:"\e6a2"}.icon-yundun-3:before{content:"\e6a3"}.icon-pts-3:before{content:"\e6a4"}.icon-mts-3:before{content:"\e6a5"}.icon-mqs-3:before{content:"\e6a6"}.icon-drds-3:before{content:"\e6a7"}.icon-cdp-3:before{content:"\e6a8"}.icon-dpc-3:before{content:"\e6a9"}.icon-ads-3:before{content:"\e6aa"}.icon-jiankong-3:before{content:"\e6ab"}.icon-vpc-3:before{content:"\e6ac"}.icon-slb-3:before{content:"\e6ad"}.icon-rds-3:before{content:"\e6ae"}.icon-ots-3:before{content:"\e6af"}.icon-oss-3:before{content:"\e6b0"}.icon-ess-3:before{content:"\e6b1"}.icon-opensearch-3:before{content:"\e6b2"}.icon-odps-3:before{content:"\e6b3"}.icon-ocs-3:before{content:"\e6b4"}.icon-oas-3:before{content:"\e6b5"}.icon-lightcloud-3:before{content:"\e6b6"}.icon-cdn-3:before{content:"\e6b7"}.icon-ace-3:before{content:"\e6b8"}.icon-sls-3:before{content:"\e6b9"}.icon-ecs-3:before{content:"\e6ba"}.modal-content{border-radius:0px;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);-webkit-box-shadow:0px 5px 10px rgba(0,0,0,0.5);-moz-box-shadow:0px 5px 10px rgba(0,0,0,0.5);box-shadow:0px 5px 10px rgba(0,0,0,0.5)}.modal-footer{margin-top:0px}.modal-title{font-size:14px}.modal-header .close{font-size:28px;margin-top:-8px;font-weight:normal}.modal-backdrop{background-color:#FFF}.console-message-dialog .modal-body .lead{font-size:16px}.console-message-dialog .modal-body p{margin-top:6px}.nav-tabs>li>a,.nav-tabs.nav-justified>li>a{border-radius:0px 0px 0px 0px}.nav-tabs{border-color:#ddd}.nav-tabs>li{margin-left:-1px;border-top:1px solid #ddd;border-left:1px solid #ddd;border-right:1px solid #ddd;z-index:1}.nav-tabs>li>a,.nav-tabs>li>a:focus{color:#666;border-left:0px;border-right:0px;margin-right:0px;padding:10px 16px;background:#FBFAF8;border-bottom:0px}.nav-tabs>li.active{border-top:0px;border-left:1px solid #ddd;border-right:1px solid #ddd;z-index:3}.nav-tabs>li.active>a,.nav-tabs>li.active>a:hover,.nav-tabs>li.active>a:focus{border-top:2px solid #00a2ca;border-left:0px;border-right:0px;border-bottom:1px solid #FFF;color:#333}.nav-tabs>li>a:hover{background-color:#FFF;color:#09C}.nav-tabs .open>a,.nav-tabs .open>a:hover,.nav-tabs .open>a:focus{color:#000;background-color:#FAFAFA;border-color:#EEE}.nav-tabs.nav-justified>li:first-child{border-left:1px solid #ddd}.nav-tabs.nav-justified>li{border-top:1px solid #ddd;border-left:0px solid #ddd;border-right:1px solid #ddd;z-index:1}.nav-tabs.nav-justified>li>a{border-left:0px;border-right:0px;margin-right:0px;background-color:#fbfaf8;border-bottom:1px solid #ddd}.nav-tabs.nav-justified>li>a:hover{color:#09C;background-color:#FFF}.nav-tabs.nav-justified>li.active{border-top:0px;z-index:3}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:hover,.nav-tabs.nav-justified>.active>a:focus{border-top:2px solid #00a2ca;border-left:0px;border-right:0px;border-bottom:1px solid #FFF;color:#333;background-color:#FFF}.nav-pills li a,.nav-pills li a:focus,.nav-pills li button,.nav-pills li button:focus{padding:6px 12px;border-radius:0px;border:1px solid #D9DEE4;background-color:#D9DEE4;color:#666;line-height:20px;height:32px;margin-left:2px}.nav-pills li a:hover,.nav-pills li a:focus:hover,.nav-pills li button:hover,.nav-pills li button:focus:hover{border:1px solid #D9DEE4;background-color:#DCE2E7;color:#444}.nav-pills li.active a,.nav-pills li.active a:hover,.nav-pills li.active a:focus,.nav-pills li.active button,.nav-pills li.active button:hover,.nav-pills li.active button:focus{border:1px solid #546478;background-color:#546478;color:#FFFFFF}.c-texttrimmer-pen{position:absolute;width:18px;height:18px;font-size:12px;padding:2px;text-align:center;margin-left:6px}.c-texttrimmer-box{position:absolute;padding:16px;background:#fff;z-index:1000;border:1px solid #999;border:1px solid rgba(0,0,0,0.3);-webkit-border-radius:0px;-moz-border-radius:0px;-ms-border-radius:0px;-o-border-radius:0px;border-radius:0px;-webkit-box-shadow:1px 1px 8px rgba(0,0,0,0.5);-moz-box-shadow:1px 1px 8px rgba(0,0,0,0.5);box-shadow:1px 1px 8px rgba(0,0,0,0.5)}.c-texttrimmer-box:focus{outline:none}.c-texttrimmer-box p{margin:0 0 10px}.c-texttrimmer-box p.c-texttrimmer-tip{color:red}.c-texttrimmer-box .c-texttrimmer-btnbox a{margin-right:8px}.modal,.modal-open{overflow:auto;overflow-y:auto}.console-helper{position:absolute;height:100%;width:400px;right:0px;top:32px;z-index:1000;border:1px solid #eee;background:#fff;border-left:1px solid #dddddd;box-shadow:0px 0px 4px rgba(0,0,0,0.2);position:fixed}.console-helper-animation{-webkit-transition:all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);transition:all 0.3s cubic-bezier(0.25, 0.5, 0.5, 0.9);-webkit-transform:translateX(0);transform:translateX(0)}.console-helper-folded{right:-400px}.console-helper-folded .console-helper-head .console-helper-button{margin-left:-44px}.console-helper-head{height:56px;background:#f5f5f5;border-bottom:1px solid #dddddd}.console-helper-head .console-helper-button{float:left;background:url(images/helper-icon.png) center center no-repeat;height:32px;width:32px;margin:12px;cursor:pointer;opacity:0.6}.console-helper-head .console-helper-button:hover{opacity:1}.console-helper-head .console-helper-title{float:left;font-size:14px;line-height:32px;height:32px;margin:12px 0;color:#333}.console-helper-body .console-helper-nav{border-bottom:1px solid #dddddd;margin:0 20px;list-style:none;overflow:hidden;zoom:1;padding:0}.console-helper-body .console-helper-nav li{float:left;padding:12px}.console-helper-body .console-helper-nav li a{color:#666}.console-helper-body .console-helper-nav li a:hover{color:#000}.console-helper-body .console-helper-nav li.active{border-bottom:2px solid #999}.console-helper-body .console-helper-panel-list .console-helper-panel{margin:20px}.console-helper-body .console-helper-panel-list .console-helper-panel .console-helper-xiaoyun .console-helper-xiaoyun-search{height:32px}.console-helper-body .console-helper-panel-list .console-helper-panel .console-helper-xiaoyun .console-helper-xiaoyun-recommend ul{list-style:none;margin:0;padding:0}.console-helper-foot{background:#f5f5f5;position:absolute;width:100%;bottom:0;left:0;border-top:1px solid #eee}.console-helper-foot .console-helper-service{overflow:hidden;zoom:1;height:32px;margin:12px;list-style:none}.console-helper-foot .console-helper-service li{width:48%;float:left}.console-helper-foot .console-helper-service li p{margin:0;color:#666}.console-helper-foot .console-helper-service li p a{color:#666}.console-helper-foot .console-helper-service li p a:hover{text-decoration:underline}.growl{z-index:9999999;top:50px;width:260px}.alert-success{color:#090;background-color:#F2FFEA;border-color:#C7DDB9}.alert-success .alert-link{color:#063;font-weight:normal}.alert-info{color:#555;background-color:#F9F9F9;border-color:#DDD}.alert-info .alert-link{color:#06C;font-weight:normal}.alert-warning{color:#f68300;background-color:#FCF8E2;border-color:#FBECCB}.alert-warning .alert-link{color:#c50;font-weight:normal}.alert-danger{color:#ee2117;background-color:#FFF6F2;border-color:#F1ACAC}.alert-danger .alert-link{color:#b00;font-weight:normal}.alert{padding:6px 12px;line-height:18px;margin-bottom:6px;border-radius:0px}.alert .close{margin-top:-5px}.alert ul{padding-left:16px}.product-icons-32,.product-icons-48,.product-icons-64{background-repeat:no-repeat;display:-moz-inline-stack;display:inline-block;vertical-align:middle;*vertical-align:auto;zoom:1;*display:inline;background-image:url(aliyun-logo/product.icons.png);background-image:-webkit-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x);background-image:-moz-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x);background-image:-ms-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x);background-image:-os-image-set(url(aliyun-logo/product.icons.png) 1x, url(aliyun-logo/product.icons@2x.png) 2x)}.product-icons-32{width:32px;height:32px}.product-icons-48{width:48px;height:48px}.product-icons-64{width:64px;height:64px}.product-icons-32.product-icons-ace-grey{background-position:-448px -1088px !important}.product-icons-32.product-icons-ace{background-position:-800px -1024px !important}.product-icons-48.product-icons-ace-grey{background-position:-192px -832px !important}.product-icons-48.product-icons-ace{background-position:-720px -880px !important}.product-icons-64.product-icons-ace-grey{background-position:-576px -128px !important}.product-icons-64.product-icons-ace{background-position:0px -64px !important}.product-icons-32.product-icons-actiontrail-grey{background-position:-416px -1088px !important}.product-icons-32.product-icons-actiontrail{background-position:-384px -1088px !important}.product-icons-48.product-icons-actiontrail-grey{background-position:-976px -288px !important}.product-icons-48.product-icons-actiontrail{background-position:-432px -976px !important}.product-icons-64.product-icons-actiontrail-grey{background-position:-128px 0px !important}.product-icons-64.product-icons-actiontrail{background-position:-128px -64px !important}.product-icons-32.product-icons-ads-grey{background-position:-352px -1088px !important}.product-icons-32.product-icons-ads{background-position:-256px -1088px !important}.product-icons-48.product-icons-ads-grey{background-position:-768px -880px !important}.product-icons-48.product-icons-ads{background-position:-928px -384px !important}.product-icons-64.product-icons-ads-grey{background-position:-64px -128px !important}.product-icons-64.product-icons-ads{background-position:-128px -128px !important}.product-icons-32.product-icons-aegis-grey{background-position:-224px -1088px !important}.product-icons-32.product-icons-aegis{background-position:-192px -1088px !important}.product-icons-48.product-icons-aegis-grey{background-position:-688px -768px !important}.product-icons-48.product-icons-aegis{background-position:-832px -96px !important}.product-icons-64.product-icons-aegis-grey{background-position:-192px -64px !important}.product-icons-64.product-icons-aegis{background-position:-192px -128px !important}.product-icons-32.product-icons-antifraud-grey{background-position:-160px -1088px !important}.product-icons-32.product-icons-antifraud{background-position:-64px -1088px !important}.product-icons-48.product-icons-antifraud-grey{background-position:-880px -480px !important}.product-icons-48.product-icons-antifraud{background-position:-880px -720px !important}.product-icons-64.product-icons-antifraud-grey{background-position:-64px -192px !important}.product-icons-64.product-icons-antifraud{background-position:-128px -192px !important}.product-icons-32.product-icons-api-grey{background-position:-32px -1088px !important}.product-icons-32.product-icons-api{background-position:0px -1088px !important}.product-icons-48.product-icons-api-grey{background-position:-96px -928px !important}.product-icons-48.product-icons-api{background-position:-192px -928px !important}.product-icons-64.product-icons-api-grey{background-position:-256px 0px !important}.product-icons-64.product-icons-api{background-position:-256px -64px !important}.product-icons-32.product-icons-apigateway-grey{background-position:-1104px -1056px !important}.product-icons-32.product-icons-apigateway{background-position:-1104px -960px !important}.product-icons-48.product-icons-apigateway-grey{background-position:-480px -976px !important}.product-icons-48.product-icons-apigateway{background-position:-576px -976px !important}.product-icons-64.product-icons-apigateway-grey{background-position:-256px -192px !important}.product-icons-64.product-icons-apigateway{background-position:0px -256px !important}.product-icons-32.product-icons-aps-grey{background-position:-1104px -928px !important}.product-icons-32.product-icons-aps{background-position:-1104px -896px !important}.product-icons-48.product-icons-aps-grey{background-position:-832px -432px !important}.product-icons-48.product-icons-aps{background-position:-832px -528px !important}.product-icons-64.product-icons-aps-grey{background-position:-128px -256px !important}.product-icons-64.product-icons-aps{background-position:-192px -256px !important}.product-icons-32.product-icons-ats-grey{background-position:-1104px -864px !important}.product-icons-32.product-icons-ats{background-position:-1104px -768px !important}.product-icons-48.product-icons-ats-grey{background-position:-768px -832px !important}.product-icons-48.product-icons-ats{background-position:-880px 0px !important}.product-icons-64.product-icons-ats-grey{background-position:-320px 0px !important}.product-icons-64.product-icons-ats{background-position:-320px -64px !important}.product-icons-32.product-icons-batchcompute-grey{background-position:-1104px -736px !important}.product-icons-32.product-icons-batchcompute{background-position:-1104px -704px !important}.product-icons-48.product-icons-batchcompute-grey{background-position:-192px -880px !important}.product-icons-48.product-icons-batchcompute{background-position:-288px -880px !important}.product-icons-64.product-icons-batchcompute-grey{background-position:-320px -192px !important}.product-icons-64.product-icons-batchcompute{background-position:-320px -256px !important}.product-icons-32.product-icons-cas-grey{background-position:-1104px -672px !important}.product-icons-32.product-icons-cas{background-position:-1104px -576px !important}.product-icons-48.product-icons-cas-grey{background-position:-928px -432px !important}.product-icons-48.product-icons-cas{background-position:-928px -480px !important}.product-icons-64.product-icons-cas-grey{background-position:-64px -320px !important}.product-icons-64.product-icons-cas{background-position:-128px -320px !important}.product-icons-32.product-icons-cdi-grey{background-position:-1104px -544px !important}.product-icons-32.product-icons-cdi{background-position:-1104px -512px !important}.product-icons-48.product-icons-cdi-grey{background-position:-672px -928px !important}.product-icons-48.product-icons-cdi{background-position:-720px -928px !important}.product-icons-64.product-icons-cdi-grey{background-position:-256px -320px !important}.product-icons-64.product-icons-cdi{background-position:-320px -320px !important}.product-icons-32.product-icons-cdn-grey{background-position:-1104px -480px !important}.product-icons-32.product-icons-cdn{background-position:-1104px -384px !important}.product-icons-48.product-icons-cdn-grey{background-position:-976px -864px !important}.product-icons-48.product-icons-cdn{background-position:-976px -912px !important}.product-icons-64.product-icons-cdn-grey{background-position:-384px -64px !important}.product-icons-64.product-icons-cdn{background-position:-384px -128px !important}.product-icons-32.product-icons-cdp-grey{background-position:-1104px -352px !important}.product-icons-32.product-icons-cdp{background-position:-1104px -320px !important}.product-icons-48.product-icons-cdp-grey{background-position:-1024px -48px !important}.product-icons-48.product-icons-cdp{background-position:-1024px -96px !important}.product-icons-64.product-icons-cdp-grey{background-position:-384px -256px !important}.product-icons-64.product-icons-cdp{background-position:-384px -320px !important}.product-icons-32.product-icons-cli-grey{background-position:-1104px -288px !important}.product-icons-32.product-icons-cli{background-position:-1104px -192px !important}.product-icons-48.product-icons-cli-grey{background-position:-832px -144px !important}.product-icons-48.product-icons-cli{background-position:-832px -192px !important}.product-icons-64.product-icons-cli-grey{background-position:-64px -384px !important}.product-icons-64.product-icons-cli{background-position:-128px -384px !important}.product-icons-32.product-icons-containerservice-grey{background-position:-1104px -160px !important}.product-icons-32.product-icons-containerservice{background-position:-1104px -128px !important}.product-icons-48.product-icons-containerservice-grey{background-position:-832px -720px !important}.product-icons-48.product-icons-containerservice{background-position:-832px -768px !important}.product-icons-64.product-icons-containerservice-grey{background-position:-256px -384px !important}.product-icons-64.product-icons-containerservice{background-position:-320px -384px !important}.product-icons-32.product-icons-cps-grey{background-position:-1104px -96px !important}.product-icons-32.product-icons-cps{background-position:-1104px 0px !important}.product-icons-48.product-icons-cps-grey{background-position:-480px -832px !important}.product-icons-48.product-icons-cps{background-position:-528px -832px !important}.product-icons-64.product-icons-cps-grey{background-position:-448px 0px !important}.product-icons-64.product-icons-cps{background-position:-448px -64px !important}.product-icons-32.product-icons-csb-grey{background-position:-1056px -1056px !important}.product-icons-32.product-icons-csb{background-position:-1024px -1056px !important}.product-icons-48.product-icons-csb-grey{background-position:-880px -192px !important}.product-icons-48.product-icons-csb{background-position:-880px -240px !important}.product-icons-64.product-icons-csb-grey{background-position:-448px -192px !important}.product-icons-64.product-icons-csb{background-position:-448px -256px !important}.product-icons-32.product-icons-ddos-grey{background-position:-992px -1056px !important}.product-icons-32.product-icons-ddos{background-position:-896px -1056px !important}.product-icons-48.product-icons-ddos-grey{background-position:-880px -768px !important}.product-icons-48.product-icons-ddos{background-position:-880px -816px !important}.product-icons-64.product-icons-ddos-grey{background-position:-448px -384px !important}.product-icons-64.product-icons-ddos{background-position:0px -448px !important}.product-icons-32.product-icons-ddosbasic-grey{background-position:-864px -1056px !important}.product-icons-32.product-icons-ddosbasic{background-position:-832px -1056px !important}.product-icons-48.product-icons-ddosbasic-grey{background-position:-480px -880px !important}.product-icons-48.product-icons-ddosbasic{background-position:-528px -880px !important}.product-icons-64.product-icons-ddosbasic-grey{background-position:-128px -448px !important}.product-icons-64.product-icons-ddosbasic{background-position:-192px -448px !important}.product-icons-32.product-icons-dfs-grey{background-position:-800px -1056px !important}.product-icons-32.product-icons-dfs{background-position:-704px -1056px !important}.product-icons-48.product-icons-dfs-grey{background-position:-928px -144px !important}.product-icons-48.product-icons-dfs{background-position:-928px -192px !important}.product-icons-64.product-icons-dfs-grey{background-position:-320px -448px !important}.product-icons-64.product-icons-dfs{background-position:-384px -448px !important}.product-icons-32.product-icons-directmail-grey{background-position:-672px -1056px !important}.product-icons-32.product-icons-directmail{background-position:-640px -1056px !important}.product-icons-48.product-icons-directmail-grey{background-position:-928px -672px !important}.product-icons-48.product-icons-directmail{background-position:-928px -720px !important}.product-icons-64.product-icons-directmail-grey{background-position:-512px 0px !important}.product-icons-64.product-icons-directmail{background-position:-512px -64px !important}.product-icons-32.product-icons-disk-grey{background-position:-608px -1056px !important}.product-icons-32.product-icons-disk{background-position:-512px -1056px !important}.product-icons-48.product-icons-disk-grey{background-position:-336px -928px !important}.product-icons-48.product-icons-disk{background-position:-384px -928px !important}.product-icons-64.product-icons-disk-grey{background-position:-512px -192px !important}.product-icons-64.product-icons-disk{background-position:-512px -256px !important}.product-icons-32.product-icons-dms-grey{background-position:-480px -1056px !important}.product-icons-32.product-icons-dms{background-position:-448px -1056px !important}.product-icons-48.product-icons-dms-grey{background-position:-912px -928px !important}.product-icons-48.product-icons-dms{background-position:-976px 0px !important}.product-icons-64.product-icons-dms-grey{background-position:-512px -384px !important}.product-icons-64.product-icons-dms{background-position:-512px -448px !important}.product-icons-32.product-icons-dpc-grey{background-position:-416px -1056px !important}.product-icons-32.product-icons-dpc{background-position:-320px -1056px !important}.product-icons-48.product-icons-dpc-grey{background-position:-976px -528px !important}.product-icons-48.product-icons-dpc{background-position:-976px -576px !important}.product-icons-64.product-icons-dpc-grey{background-position:-64px -512px !important}.product-icons-64.product-icons-dpc{background-position:-128px -512px !important}.product-icons-32.product-icons-drds-grey{background-position:-288px -1056px !important}.product-icons-32.product-icons-drds{background-position:-256px -1056px !important}.product-icons-48.product-icons-drds-grey{background-position:-144px -976px !important}.product-icons-48.product-icons-drds{background-position:-192px -976px !important}.product-icons-64.product-icons-drds-grey{background-position:-256px -512px !important}.product-icons-64.product-icons-drds{background-position:-320px -512px !important}.product-icons-32.product-icons-dsi-grey{background-position:-224px -1056px !important}.product-icons-32.product-icons-dsi{background-position:-128px -1056px !important}.product-icons-48.product-icons-dsi-grey{background-position:-720px -976px !important}.product-icons-48.product-icons-dsi{background-position:-768px -976px !important}.product-icons-64.product-icons-dsi-grey{background-position:-448px -512px !important}.product-icons-64.product-icons-dsi{background-position:-512px -512px !important}.product-icons-32.product-icons-dts-grey{background-position:-96px -1056px !important}.product-icons-32.product-icons-dts{background-position:-64px -1056px !important}.product-icons-48.product-icons-dts-grey{background-position:-1024px -288px !important}.product-icons-48.product-icons-dts{background-position:-1024px -336px !important}.product-icons-64.product-icons-dts-grey{background-position:-576px -64px !important}.product-icons-64.product-icons-dts{background-position:0px 0px !important}.product-icons-32.product-icons-eclipse-grey{background-position:-32px -1056px !important}.product-icons-32.product-icons-eclipse{background-position:-1072px -992px !important}.product-icons-48.product-icons-eclipse-grey{background-position:-832px 0px !important}.product-icons-48.product-icons-eclipse{background-position:-832px -48px !important}.product-icons-64.product-icons-eclipse-grey{background-position:-576px -256px !important}.product-icons-64.product-icons-eclipse{background-position:-576px -320px !important}.product-icons-32.product-icons-ecs-grey{background-position:-1072px -960px !important}.product-icons-32.product-icons-ecs{background-position:-1072px -928px !important}.product-icons-48.product-icons-ecs-grey{background-position:-832px -288px !important}.product-icons-48.product-icons-ecs{background-position:-832px -336px !important}.product-icons-64.product-icons-ecs-grey{background-position:-576px -448px !important}.product-icons-64.product-icons-ecs{background-position:-576px -512px !important}.product-icons-32.product-icons-edas-grey{background-position:-1072px -896px !important}.product-icons-32.product-icons-edas{background-position:-1072px -800px !important}.product-icons-48.product-icons-edas-grey{background-position:-832px -576px !important}.product-icons-48.product-icons-edas{background-position:-832px -624px !important}.product-icons-64.product-icons-edas-grey{background-position:-64px -576px !important}.product-icons-64.product-icons-edas{background-position:-128px -576px !important}.product-icons-32.product-icons-elp-grey{background-position:-1072px -768px !important}.product-icons-32.product-icons-elp{background-position:-1072px -736px !important}.product-icons-48.product-icons-elp-grey{background-position:-48px -832px !important}.product-icons-48.product-icons-elp{background-position:-96px -832px !important}.product-icons-64.product-icons-elp-grey{background-position:-256px -576px !important}.product-icons-64.product-icons-elp{background-position:-320px -576px !important}.product-icons-32.product-icons-emapreduce-grey{background-position:-1072px -704px !important}.product-icons-32.product-icons-emapreduce{background-position:-1072px -608px !important}.product-icons-48.product-icons-emapreduce-grey{background-position:-336px -832px !important}.product-icons-48.product-icons-emapreduce{background-position:-384px -832px !important}.product-icons-64.product-icons-emapreduce-grey{background-position:-448px -576px !important}.product-icons-64.product-icons-emapreduce{background-position:-512px -576px !important}.product-icons-32.product-icons-esn-grey{background-position:-1072px -576px !important}.product-icons-32.product-icons-esn{background-position:-1072px -544px !important}.product-icons-48.product-icons-esn-grey{background-position:-624px -832px !important}.product-icons-48.product-icons-esn{background-position:-672px -832px !important}.product-icons-64.product-icons-esn-grey{background-position:-640px 0px !important}.product-icons-64.product-icons-esn{background-position:-640px -64px !important}.product-icons-32.product-icons-ess-grey{background-position:-1072px -512px !important}.product-icons-32.product-icons-ess{background-position:-1072px -416px !important}.product-icons-48.product-icons-ess-grey{background-position:-880px -48px !important}.product-icons-48.product-icons-ess{background-position:-880px -96px !important}.product-icons-64.product-icons-ess-grey{background-position:-640px -192px !important}.product-icons-64.product-icons-ess{background-position:-640px -256px !important}.product-icons-32.product-icons-expressconnect-grey{background-position:-1072px -384px !important}.product-icons-32.product-icons-expressconnect{background-position:-1072px -352px !important}.product-icons-48.product-icons-expressconnect-grey{background-position:-880px -336px !important}.product-icons-48.product-icons-expressconnect{background-position:-880px -384px !important}.product-icons-64.product-icons-expressconnect-grey{background-position:-640px -384px !important}.product-icons-64.product-icons-expressconnect{background-position:-640px -448px !important}.product-icons-32.product-icons-havip-grey{background-position:-1072px -320px !important}.product-icons-32.product-icons-havip{background-position:-1072px -224px !important}.product-icons-48.product-icons-havip-grey{background-position:-880px -624px !important}.product-icons-48.product-icons-havip{background-position:-880px -672px !important}.product-icons-64.product-icons-havip-grey{background-position:-640px -576px !important}.product-icons-64.product-icons-havip{background-position:0px -640px !important}.product-icons-32.product-icons-hpc-grey{background-position:-1072px -192px !important}.product-icons-32.product-icons-hpc{background-position:-1072px -160px !important}.product-icons-48.product-icons-hpc-grey{background-position:-48px -880px !important}.product-icons-48.product-icons-hpc{background-position:-96px -880px !important}.product-icons-64.product-icons-hpc-grey{background-position:-128px -640px !important}.product-icons-64.product-icons-hpc{background-position:-192px -640px !important}.product-icons-32.product-icons-hsm-grey{background-position:-1072px -128px !important}.product-icons-32.product-icons-hsm{background-position:-1072px -32px !important}.product-icons-48.product-icons-hsm-grey{background-position:-336px -880px !important}.product-icons-48.product-icons-hsm{background-position:-384px -880px !important}.product-icons-64.product-icons-hsm-grey{background-position:-320px -640px !important}.product-icons-64.product-icons-hsm{background-position:-384px -640px !important}.product-icons-32.product-icons-iot-grey{background-position:-1072px 0px !important}.product-icons-32.product-icons-iot{background-position:-1024px -1024px !important}.product-icons-48.product-icons-iot-grey{background-position:-624px -880px !important}.product-icons-48.product-icons-iot{background-position:-672px -880px !important}.product-icons-64.product-icons-iot-grey{background-position:-512px -640px !important}.product-icons-64.product-icons-iot{background-position:-576px -640px !important}.product-icons-32.product-icons-jiankong-grey{background-position:-992px -1024px !important}.product-icons-32.product-icons-jiankong{background-position:-896px -1024px !important}.product-icons-48.product-icons-jiankong-grey{background-position:-928px 0px !important}.product-icons-48.product-icons-jiankong{background-position:-928px -48px !important}.product-icons-64.product-icons-jiankong-grey{background-position:-704px 0px !important}.product-icons-64.product-icons-jiankong{background-position:-704px -64px !important}.product-icons-32.product-icons-keyongxingzhongxin-grey{background-position:-864px -1024px !important}.product-icons-32.product-icons-keyongxingzhongxin{background-position:-832px -1024px !important}.product-icons-48.product-icons-keyongxingzhongxin-grey{background-position:-928px -288px !important}.product-icons-48.product-icons-keyongxingzhongxin{background-position:-144px -832px !important}.product-icons-64.product-icons-keyongxingzhongxin-grey{background-position:-704px -192px !important}.product-icons-64.product-icons-keyongxingzhongxin{background-position:-704px -256px !important}.product-icons-32.product-icons-kms-grey{background-position:-704px -1024px !important}.product-icons-32.product-icons-kms{background-position:-672px -1024px !important}.product-icons-48.product-icons-kms-grey{background-position:-928px -576px !important}.product-icons-48.product-icons-kms{background-position:-928px -624px !important}.product-icons-64.product-icons-kms-grey{background-position:-704px -448px !important}.product-icons-64.product-icons-kms{background-position:-704px -512px !important}.product-icons-32.product-icons-kvstore-grey{background-position:-640px -1024px !important}.product-icons-32.product-icons-kvstore{background-position:-608px -1024px !important}.product-icons-48.product-icons-kvstore-grey{background-position:-928px -864px !important}.product-icons-48.product-icons-kvstore{background-position:0px -928px !important}.product-icons-64.product-icons-kvstore-grey{background-position:-704px -576px !important}.product-icons-64.product-icons-kvstore{background-position:-704px -640px !important}.product-icons-32.product-icons-livevideo-grey{background-position:-512px -1024px !important}.product-icons-32.product-icons-livevideo{background-position:-480px -1024px !important}.product-icons-48.product-icons-livevideo-grey{background-position:-240px -928px !important}.product-icons-48.product-icons-livevideo{background-position:-288px -928px !important}.product-icons-64.product-icons-livevideo-grey{background-position:-128px -704px !important}.product-icons-64.product-icons-livevideo{background-position:-192px -704px !important}.product-icons-32.product-icons-lvwang-grey{background-position:-448px -1024px !important}.product-icons-32.product-icons-lvwang{background-position:-416px -1024px !important}.product-icons-48.product-icons-lvwang-grey{background-position:-528px -928px !important}.product-icons-48.product-icons-lvwang{background-position:-576px -928px !important}.product-icons-64.product-icons-lvwang-grey{background-position:-256px -704px !important}.product-icons-64.product-icons-lvwang{background-position:-320px -704px !important}.product-icons-32.product-icons-mac-grey{background-position:-320px -1024px !important}.product-icons-32.product-icons-mac{background-position:-288px -1024px !important}.product-icons-48.product-icons-mac-grey{background-position:-816px -928px !important}.product-icons-48.product-icons-mac{background-position:-864px -928px !important}.product-icons-64.product-icons-mac-grey{background-position:-512px -704px !important}.product-icons-64.product-icons-mac{background-position:-576px -704px !important}.product-icons-32.product-icons-man-grey{background-position:-256px -1024px !important}.product-icons-32.product-icons-man{background-position:-224px -1024px !important}.product-icons-48.product-icons-man-grey{background-position:-976px -144px !important}.product-icons-48.product-icons-man{background-position:-976px -192px !important}.product-icons-64.product-icons-man-grey{background-position:-640px -704px !important}.product-icons-64.product-icons-man{background-position:-704px -704px !important}.product-icons-32.product-icons-mns-grey{background-position:-128px -1024px !important}.product-icons-32.product-icons-mns{background-position:-96px -1024px !important}.product-icons-48.product-icons-mns-grey{background-position:-976px -432px !important}.product-icons-48.product-icons-mns{background-position:-976px -480px !important}.product-icons-64.product-icons-mns-grey{background-position:-768px -128px !important}.product-icons-64.product-icons-mns{background-position:-768px -192px !important}.product-icons-32.product-icons-mongodb-grey{background-position:-64px -1024px !important}.product-icons-32.product-icons-mongodb{background-position:-32px -1024px !important}.product-icons-48.product-icons-mongodb-grey{background-position:-976px -720px !important}.product-icons-48.product-icons-mongodb{background-position:-976px -768px !important}.product-icons-64.product-icons-mongodb-grey{background-position:-768px -256px !important}.product-icons-64.product-icons-mongodb{background-position:-768px -320px !important}.product-icons-32.product-icons-mqs-grey{background-position:-1024px -960px !important}.product-icons-32.product-icons-mqs{background-position:-1024px -928px !important}.product-icons-48.product-icons-mqs-grey{background-position:-48px -976px !important}.product-icons-48.product-icons-mqs{background-position:-96px -976px !important}.product-icons-64.product-icons-mqs-grey{background-position:-768px -512px !important}.product-icons-64.product-icons-mqs{background-position:-768px -576px !important}.product-icons-32.product-icons-mss-grey{background-position:-1024px -896px !important}.product-icons-32.product-icons-mss{background-position:-1024px -864px !important}.product-icons-48.product-icons-mss-grey{background-position:-336px -976px !important}.product-icons-48.product-icons-mss{background-position:-384px -976px !important}.product-icons-64.product-icons-mss-grey{background-position:-768px -640px !important}.product-icons-64.product-icons-mss{background-position:-768px -704px !important}.product-icons-32.product-icons-mts-grey{background-position:-1024px -768px !important}.product-icons-32.product-icons-mts{background-position:-1024px -736px !important}.product-icons-48.product-icons-mts-grey{background-position:-624px -976px !important}.product-icons-48.product-icons-mts{background-position:-672px -976px !important}.product-icons-64.product-icons-mts-grey{background-position:-128px -768px !important}.product-icons-64.product-icons-mts{background-position:-192px -768px !important}.product-icons-32.product-icons-nas-grey{background-position:-1024px -704px !important}.product-icons-32.product-icons-nas{background-position:-1024px -672px !important}.product-icons-48.product-icons-nas-grey{background-position:-912px -976px !important}.product-icons-48.product-icons-nas{background-position:-960px -976px !important}.product-icons-64.product-icons-nas-grey{background-position:-256px -768px !important}.product-icons-64.product-icons-nas{background-position:-320px -768px !important}.product-icons-32.product-icons-oas-grey{background-position:-1024px -576px !important}.product-icons-32.product-icons-oas{background-position:-1024px -544px !important}.product-icons-48.product-icons-oas-grey{background-position:-1024px -192px !important}.product-icons-48.product-icons-oas{background-position:-1024px -240px !important}.product-icons-64.product-icons-oas-grey{background-position:-512px -768px !important}.product-icons-64.product-icons-oas{background-position:-576px -768px !important}.product-icons-32.product-icons-oceanbase-grey{background-position:0px -1056px !important}.product-icons-32.product-icons-oceanbase{background-position:-1024px -512px !important}.product-icons-48.product-icons-oceanbase-grey{background-position:-1024px -432px !important}.product-icons-48.product-icons-oceanbase{background-position:-1024px -384px !important}.product-icons-64.product-icons-oceanbase-grey{background-position:-448px -768px !important}.product-icons-64.product-icons-oceanbase{background-position:-384px -768px !important}.product-icons-32.product-icons-ocs-grey{background-position:-1024px -608px !important}.product-icons-32.product-icons-ocs{background-position:-1024px -640px !important}.product-icons-48.product-icons-ocs-grey{background-position:-864px -976px !important}.product-icons-48.product-icons-ocs{background-position:-816px -976px !important}.product-icons-64.product-icons-ocs-grey{background-position:-64px -768px !important}.product-icons-64.product-icons-ocs{background-position:0px -768px !important}.product-icons-32.product-icons-odps-grey{background-position:-1024px -800px !important}.product-icons-32.product-icons-odps{background-position:-1024px -832px !important}.product-icons-48.product-icons-odps-grey{background-position:-288px -976px !important}.product-icons-48.product-icons-odps{background-position:-240px -976px !important}.product-icons-64.product-icons-odps-grey{background-position:-768px -448px !important}.product-icons-64.product-icons-odps{background-position:-768px -384px !important}.product-icons-32.product-icons-ons-grey{background-position:-1024px -992px !important}.product-icons-32.product-icons-ons{background-position:0px -1024px !important}.product-icons-48.product-icons-ons-grey{background-position:-976px -672px !important}.product-icons-48.product-icons-ons{background-position:-976px -624px !important}.product-icons-64.product-icons-ons-grey{background-position:-768px -64px !important}.product-icons-64.product-icons-ons{background-position:-768px 0px !important}.product-icons-32.product-icons-opensearch-grey{background-position:-160px -1024px !important}.product-icons-32.product-icons-opensearch{background-position:-192px -1024px !important}.product-icons-48.product-icons-opensearch-grey{background-position:-976px -96px !important}.product-icons-48.product-icons-opensearch{background-position:-976px -48px !important}.product-icons-64.product-icons-opensearch-grey{background-position:-448px -704px !important}.product-icons-64.product-icons-opensearch{background-position:-384px -704px !important}.product-icons-32.product-icons-oss-grey{background-position:-352px -1024px !important}.product-icons-32.product-icons-oss{background-position:-384px -1024px !important}.product-icons-48.product-icons-oss-grey{background-position:-480px -928px !important}.product-icons-48.product-icons-oss{background-position:-432px -928px !important}.product-icons-64.product-icons-oss-grey{background-position:-64px -704px !important}.product-icons-64.product-icons-oss{background-position:0px -704px !important}.product-icons-32.product-icons-ots-grey{background-position:-544px -1024px !important}.product-icons-32.product-icons-ots{background-position:-576px -1024px !important}.product-icons-48.product-icons-ots-grey{background-position:-928px -816px !important}.product-icons-48.product-icons-ots{background-position:-928px -768px !important}.product-icons-64.product-icons-ots-grey{background-position:-704px -384px !important}.product-icons-64.product-icons-ots{background-position:-704px -320px !important}.product-icons-32.product-icons-petadata-grey{background-position:-736px -1024px !important}.product-icons-32.product-icons-petadata{background-position:-768px -1024px !important}.product-icons-48.product-icons-petadata-grey{background-position:-928px -336px !important}.product-icons-48.product-icons-petadata{background-position:-928px -240px !important}.product-icons-64.product-icons-petadata-grey{background-position:-704px -128px !important}.product-icons-64.product-icons-petadata{background-position:-640px -640px !important}.product-icons-32.product-icons-pts-grey{background-position:-928px -1024px !important}.product-icons-32.product-icons-pts{background-position:-960px -1024px !important}.product-icons-48.product-icons-pts-grey{background-position:-816px -880px !important}.product-icons-48.product-icons-pts{background-position:-576px -880px !important}.product-icons-64.product-icons-pts-grey{background-position:-448px -640px !important}.product-icons-64.product-icons-pts{background-position:-256px -640px !important}.product-icons-32.product-icons-ram-grey{background-position:-1072px -64px !important}.product-icons-32.product-icons-ram{background-position:-1072px -96px !important}.product-icons-48.product-icons-ram-grey{background-position:-240px -880px !important}.product-icons-48.product-icons-ram{background-position:0px -880px !important}.product-icons-64.product-icons-ram-grey{background-position:-64px -640px !important}.product-icons-64.product-icons-ram{background-position:-640px -512px !important}.product-icons-32.product-icons-rds-grey{background-position:-1072px -256px !important}.product-icons-32.product-icons-rds{background-position:-1072px -288px !important}.product-icons-48.product-icons-rds-grey{background-position:-880px -528px !important}.product-icons-48.product-icons-rds{background-position:-880px -288px !important}.product-icons-64.product-icons-rds-grey{background-position:-640px -320px !important}.product-icons-64.product-icons-rds{background-position:-640px -128px !important}.product-icons-32.product-icons-ros-grey{background-position:-1072px -448px !important}.product-icons-32.product-icons-ros{background-position:-1072px -480px !important}.product-icons-48.product-icons-ros-grey{background-position:-816px -832px !important}.product-icons-48.product-icons-ros{background-position:-576px -832px !important}.product-icons-64.product-icons-ros-grey{background-position:-576px -576px !important}.product-icons-64.product-icons-ros{background-position:-384px -576px !important}.product-icons-32.product-icons-sas-grey{background-position:-1072px -640px !important}.product-icons-32.product-icons-sas{background-position:-1072px -672px !important}.product-icons-48.product-icons-sas-grey{background-position:-240px -832px !important}.product-icons-48.product-icons-sas{background-position:0px -832px !important}.product-icons-64.product-icons-sas-grey{background-position:-192px -576px !important}.product-icons-64.product-icons-sas{background-position:0px -576px !important}.product-icons-32.product-icons-scan-grey{background-position:-1072px -832px !important}.product-icons-32.product-icons-scan{background-position:-1072px -864px !important}.product-icons-48.product-icons-scan-grey{background-position:-832px -480px !important}.product-icons-48.product-icons-scan{background-position:-832px -240px !important}.product-icons-64.product-icons-scan-grey{background-position:-576px -384px !important}.product-icons-64.product-icons-scan{background-position:-576px -192px !important}.product-icons-32.product-icons-slb-grey{background-position:-1072px -1024px !important}.product-icons-32.product-icons-slb{background-position:-1024px -480px !important}.product-icons-48.product-icons-slb-grey{background-position:-736px -768px !important}.product-icons-48.product-icons-slb{background-position:-1024px -144px !important}.product-icons-64.product-icons-slb-grey{background-position:-576px 0px !important}.product-icons-64.product-icons-slb{background-position:-384px -512px !important}.product-icons-32.product-icons-slm-grey{background-position:-160px -1056px !important}.product-icons-32.product-icons-slm{background-position:-192px -1056px !important}.product-icons-48.product-icons-slm-grey{background-position:-528px -976px !important}.product-icons-48.product-icons-slm{background-position:0px -976px !important}.product-icons-64.product-icons-slm-grey{background-position:-192px -512px !important}.product-icons-64.product-icons-slm{background-position:0px -512px !important}.product-icons-32.product-icons-sls-grey{background-position:-352px -1056px !important}.product-icons-32.product-icons-sls{background-position:-384px -1056px !important}.product-icons-48.product-icons-sls-grey{background-position:-976px -336px !important}.product-icons-48.product-icons-sls{background-position:-768px -928px !important}.product-icons-64.product-icons-sls-grey{background-position:-512px -320px !important}.product-icons-64.product-icons-sls{background-position:-512px -128px !important}.product-icons-32.product-icons-sos-grey{background-position:-544px -1056px !important}.product-icons-32.product-icons-sos{background-position:-576px -1056px !important}.product-icons-48.product-icons-sos-grey{background-position:-144px -928px !important}.product-icons-48.product-icons-sos{background-position:-928px -528px !important}.product-icons-64.product-icons-sos-grey{background-position:-448px -448px !important}.product-icons-64.product-icons-sos{background-position:-256px -448px !important}.product-icons-32.product-icons-toolsimage-grey{background-position:-736px -1056px !important}.product-icons-32.product-icons-toolsimage{background-position:-768px -1056px !important}.product-icons-48.product-icons-toolsimage-grey{background-position:-864px -880px !important}.product-icons-48.product-icons-toolsimage{background-position:-432px -880px !important}.product-icons-64.product-icons-toolsimage-grey{background-position:-64px -448px !important}.product-icons-64.product-icons-toolsimage{background-position:-448px -320px !important}.product-icons-32.product-icons-vipaegis-grey{background-position:-928px -1056px !important}.product-icons-32.product-icons-vipaegis{background-position:-960px -1056px !important}.product-icons-48.product-icons-vipaegis-grey{background-position:-880px -576px !important}.product-icons-48.product-icons-vipaegis{background-position:-880px -144px !important}.product-icons-64.product-icons-vipaegis-grey{background-position:-448px -128px !important}.product-icons-64.product-icons-vipaegis{background-position:-384px -384px !important}.product-icons-32.product-icons-visualstudio-grey{background-position:-1104px -32px !important}.product-icons-32.product-icons-visualstudio{background-position:-1104px -64px !important}.product-icons-48.product-icons-visualstudio-grey{background-position:-288px -832px !important}.product-icons-48.product-icons-visualstudio{background-position:-832px -672px !important}.product-icons-64.product-icons-visualstudio-grey{background-position:-192px -384px !important}.product-icons-64.product-icons-visualstudio{background-position:0px -384px !important}.product-icons-32.product-icons-vod-grey{background-position:-1104px -224px !important}.product-icons-32.product-icons-vod{background-position:-1104px -256px !important}.product-icons-48.product-icons-vod-grey{background-position:-784px -768px !important}.product-icons-48.product-icons-vod{background-position:-1024px 0px !important}.product-icons-64.product-icons-vod-grey{background-position:-384px -192px !important}.product-icons-64.product-icons-vod{background-position:-384px 0px !important}.product-icons-32.product-icons-vpc-grey{background-position:-1104px -416px !important}.product-icons-32.product-icons-vpc{background-position:-1104px -448px !important}.product-icons-48.product-icons-vpc-grey{background-position:-976px -384px !important}.product-icons-48.product-icons-vpc{background-position:-624px -928px !important}.product-icons-64.product-icons-vpc-grey{background-position:-192px -320px !important}.product-icons-64.product-icons-vpc{background-position:0px -320px !important}.product-icons-32.product-icons-waf-grey{background-position:-1104px -608px !important}.product-icons-32.product-icons-waf{background-position:-1104px -640px !important}.product-icons-48.product-icons-waf-grey{background-position:-928px -96px !important}.product-icons-48.product-icons-waf{background-position:-144px -880px !important}.product-icons-64.product-icons-waf-grey{background-position:-320px -128px !important}.product-icons-64.product-icons-waf{background-position:-256px -256px !important}.product-icons-32.product-icons-xianzhi-grey{background-position:-1104px -800px !important}.product-icons-32.product-icons-xianzhi{background-position:-1104px -832px !important}.product-icons-48.product-icons-xianzhi-grey{background-position:-432px -832px !important}.product-icons-48.product-icons-xianzhi{background-position:-832px -384px !important}.product-icons-64.product-icons-xianzhi-grey{background-position:-64px -256px !important}.product-icons-64.product-icons-xianzhi{background-position:-256px -128px !important}.product-icons-32.product-icons-ysf-grey{background-position:-1104px -992px !important}.product-icons-32.product-icons-ysf{background-position:-1104px -1024px !important}.product-icons-48.product-icons-ysf-grey{background-position:-976px -816px !important}.product-icons-48.product-icons-ysf{background-position:-48px -928px !important}.product-icons-64.product-icons-ysf-grey{background-position:-192px -192px !important}.product-icons-64.product-icons-ysf{background-position:0px -192px !important}.product-icons-32.product-icons-yundun-grey{background-position:-96px -1088px !important}.product-icons-32.product-icons-yundun{background-position:-128px -1088px !important}.product-icons-48.product-icons-yundun-grey{background-position:-720px -832px !important}.product-icons-48.product-icons-yundun{background-position:-640px -768px !important}.product-icons-64.product-icons-yundun-grey{background-position:-192px 0px !important}.product-icons-64.product-icons-yundun{background-position:0px -128px !important}.product-icons-32.product-icons-yunjiankong-grey{background-position:-288px -1088px !important}.product-icons-32.product-icons-yunjiankong{background-position:-320px -1088px !important}.product-icons-48.product-icons-yunjiankong-grey{background-position:-880px -432px !important}.product-icons-48.product-icons-yunjiankong{background-position:-976px -240px !important}.product-icons-64.product-icons-yunjiankong-grey{background-position:-64px -64px !important}.product-icons-64.product-icons-yunjiankong{background-position:-64px 0px !important}.console-search{box-sizing:border-box;float:left;margin-right:1px;position:relative;z-index:11}.console-search *{box-sizing:border-box}.console-search .console-search-ask{position:relative}.console-search .console-search-ask .console-search-ask-input{width:200px;height:40px;background:#2a2e31;border:0;padding:12px 30px 12px 10px;display:inline-block;color:#999;-webkit-border-radius:1px 1px;-moz-border-radius:1px / 1px;border-radius:1px / 1px;-o-transition:all 0.3s, 0.3s;-ms-transition:all 0.3s, 0.3s;-moz-transition:all 0.3s, 0.3s;-webkit-transition:all 0.3s, 0.3s}.console-search .console-search-ask .console-search-ask-input:focus{outline:none}.console-search .console-search-ask .console-search-mark{font-size:16px;line-height:30px;position:absolute;height:100%;width:40px;color:#eee;padding:5px;text-decoration:none;display:block}.console-search .console-search-ask .console-search-questionmark{right:0;top:0}.console-search .console-search-ask-active .console-search-ask-input{width:320px;height:40px;background:#f2f2f2;border:0;color:#000}.console-search .console-search-ask-active .console-search-questionmark{color:#0099cc}.console-search .console-search-answer{width:402px;margin-top:2px;left:-1px;border:1px solid #d4d4d4;border-top:none;background:#fff;position:absolute;-webkit-border-radius:2px 2px;-moz-border-radius:2px / 2px;border-radius:2px / 2px;text-shadow:1px}.console-search .console-search-answer .console-search-answer-head{background:#f8f8f8;border-bottom:1px solid #eee;height:42px}.console-search .console-search-answer .console-search-answer-head ul{list-style:none;margin:0;padding:0 24px}.console-search .console-search-answer .console-search-answer-head ul li{float:left;margin-right:14px;height:42px;line-height:42px}.console-search .console-search-answer .console-search-answer-head ul li a{display:block;width:100%;height:100%;color:#666;text-decoration:none}.console-search .console-search-answer .console-search-answer-head ul li a:hover{color:#ff6500;border-bottom:2px solid #ff6500}.console-search .console-search-answer .console-search-answer-head ul li.console-search-tab-active a{color:#ff6500;border-bottom:2px solid #ff6500}.console-search .console-search-answer .console-search-answer-body{padding:0 24px}.console-search .console-search-answer .console-search-answer-body .console-search-answer-list .console-search-answer-item{height:40px;line-height:40px;border-bottom:1px solid #eee}.console-search .console-search-answer .console-search-answer-body .console-search-answer-list .console-search-answer-item a{color:#00a2ca}.console-search .console-search-answer .console-search-answer-body .console-search-answer-more{height:40px;line-height:40px;text-align:right}.console-search .console-search-answer .console-search-answer-body .console-search-answer-more a{color:#00a2ca}.selector{width:100%;height:140px;border:1px solid #999;background-color:#FFF;overflow-x:hidden;overflow-y:auto}.selector .selector-list{list-style:none;margin:0px;padding:0px}.selector .selector-list .selector-item{height:32px;line-height:32px;overflow:hidden;border-bottom:1px solid #DDD;text-overflow:ellipsis;white-space:nowrap;text-indent:8px}.selector .selector-list .selector-item:hover{color:#06C;background-color:#FAFCFF;cursor:pointer}.selector .selector-list .selector-item.active{background-color:#37C;color:#FFF}.selector .selector-list .selector-item.disabled{color:#AAA;cursor:not-allowed;background-color:#FAFAFA}.selector .selector-msg{text-align:center;color:#999;height:32px;line-height:32px}.selector.selector-status-error .selector-msg{cursor:pointer}.selector.selector-status-hasmore .selector-msg{cursor:pointer}.list-selector .selector-box{width:45%;float:left}.list-selector .selector-box .inner-wrap{border:1px solid #bbb;height:200px;overflow:hidden}.list-selector .selector-box .inner-wrap .inner-head{border:1px solid #eee;margin:6px;position:relative}.list-selector .selector-box .inner-wrap .inner-head input{border:0;width:90%}.list-selector .selector-box .inner-wrap .inner-head input:focus{outline:0}.list-selector .selector-box .inner-wrap .inner-head .search{width:20px;height:20px;line-height:20px;padding:0 6px;cursor:pointer;position:absolute;right:0;top:0}.list-selector .selector-box .inner-wrap .inner-body{height:160px;overflow-y:auto;overflow-x:hidden;border:0}.list-selector .selector-box .inner-wrap .inner-body2{height:200px;overflow-y:auto;overflow-x:hidden;border:0}.list-selector .selector-mid{width:10%;text-align:center;float:left}.list-selector .selector-mid .mid-box{margin:10px auto;height:40px;width:40px;font-weight:bold;border:1px solid #bbb;background:#f7f7f7;display:block;cursor:pointer}.list-selector .selector-mid .mid-margin{margin-top:80px;margin-bottom:10px}.aliyun-console-table-search-list{min-width:100px}.console-global-notice{position:relative;margin-top:-1px;z-index:1}.console-global-notice .console-global-notice-nav{position:absolute;top:13px;left:25px;z-index:2}.console-global-notice .console-global-notice-nav span{width:12px;height:12px;display:block;float:left;background:#e8e8e8;border-radius:12px;margin-right:3px;cursor:pointer}.console-global-notice .console-global-notice-nav span.active{background:#999999}.console-global-notice .console-global-notice-list{height:50px;position:relative}.console-global-notice .console-global-notice-list .console-global-notice-item{position:absolute;width:100%;top:0;left:0;z-index:1;padding:10px 12px;border-radius:2px;margin-bottom:0px;text-align:left}.console-global-notice .console-global-notice-list .console-global-notice-item .console-global-notice-nomore{position:absolute;top:8px;right:12px}.console-global-notice .console-global-notice-list .console-global-notice-item .console-global-notice-content{padding-right:80px;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.console-clip-copy{background:rgba(0,0,0,0.75);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#bf000000,endColorstr=#bf000000);position:absolute;color:#fff;line-height:24px;height:24px;overflow:hidden;padding:0px 12px 0px 30px}.console-clip-copy .rectangle1,.console-clip-copy .rectangle2{position:absolute;top:8px;left:13px;border:1px solid #fff;width:10px;height:12px;background:#404040;z-index:2}.console-clip-copy .rectangle2{left:15px;z-index:1;top:5px}.console-clip-copyed{padding-left:12px}.console-clip-copyed .rectangle1,.console-clip-copyed .rectangle2{display:none}.console-aside-wrap{position:fixed;z-index:105}.console-aside-wrap .console-aside{position:absolute;display:none}.console-aside-wrap .console-aside.console-aside-transform{-o-transition:all 0.3s, 0.3s;-ms-transition:all 0.3s, 0.3s;-moz-transition:all 0.3s, 0.3s;-webkit-transition:all 0.3s, 0.3s}.console-aside-wrap.top{top:0;width:100%}.console-aside-wrap.top .console-aside{top:0;width:100%}.console-aside-wrap.right{right:0;height:100%;top:0}.console-aside-wrap.right .console-aside{right:0;height:100%}.console-aside-wrap.left{left:0;height:100%;top:0}.console-aside-wrap.left .console-aside{left:0;height:100%}.console-aside-wrap.bottom{bottom:0;width:100%}.console-aside-wrap.bottom .console-aside{bottom:0;width:100%}.table-default-viewer{width:100%;background-color:#FFF}.table-default-viewer td{padding:11px 20px;border:1px solid #eeeeee}.table-default-viewer.off{display:none}.table-viewer-topbar-content{padding:0px;margin:0px;margin-right:8px}.table-viewer-header{margin-top:10px;margin-bottom:-1px;height:40px;background:#F5f6FA;line-height:38px;border:1px solid #e1e6eb;position:relative;border-left:4px solid #6d7781}.table-viewer-header .table-viewer-topbar-title{font-size:14px;color:#333333;display:inline-block;margin-left:16px}.table-viewer-header .table-viewer-topbar-content{margin-right:60px}.table-viewer-header .toggle-drop-down-icon{-webkit-user-select:none;-moz-user-select:none;user-select:none;-o-user-select:none;-ms-user-select:none;position:absolute;width:40px;height:39px;right:0;top:0;border-left:1px solid #e1e6eb}.table-viewer-header .table-viewer-dropdown{display:inline-block;margin:10px;font-size:20px}.simple-chart{height:100%;border:1px solid #ccd6e0;position:relative;-webkit-box-shadow:0px 0px 3px rgba(0,0,0,0.1);-moz-box-shadow:0px 0px 3px rgba(0,0,0,0.1);box-shadow:0px 0px 3px rgba(0,0,0,0.1)}.simple-chart .simple-chart-head{height:40px;line-height:40px;font-size:14px;padding-left:10px;background:#f8f9fb}.simple-chart .simple-chart-head-title{float:left}.simple-chart .simple-chart-operations{float:right}.simple-chart .simple-chart-operations .simple-chart-btn{display:inline-block;border-left:1px solid #e1e6eb;width:40px;text-align:center;cursor:pointer}.simple-chart .simple-chart-operations .simple-chart-btn span{font-size:14px;font-weight:bold;vertical-align:text-bottom}.simple-chart .simple-chart-body{border-top:1px solid #ccd6e0;padding:0px 2px 2px 0px}.simple-chart .simple-chart-body .highcharts-container{float:left}.simple-chart .simple-chart-body-inner{height:100%}.simple-chart .simple-chart-annulus-center{position:absolute;text-align:center}.simple-chart .simple-chart-annulus-center .simple-chart-annulus-number{color:#0099cc;font-size:32px}.simple-chart.simple-chart-nowrap{border:none;-webkit-box-shadow:0px 0px 0px;-moz-box-shadow:0px 0px 0px;box-shadow:0px 0px 0px}.simple-chart.simple-chart-nowrap .simple-chart-head{display:none}.simple-chart.simple-chart-nowrap .simple-chart-body{border:none;height:100% !important}.console-middle-page{margin-top:80px}.console-middle-page .console-middle-page-icon{text-align:right}.console-middle-page .console-middle-page-title{font-size:20px;margin:0;line-height:48px}.console-middle-page .console-middle-page-text{font-size:12px;color:#666}.console-middle-page .console-middle-page-link{border-top:1px solid #EEE;margin-top:16px;padding-top:16px}.console-middle-page .console-middle-page-link>a{padding-right:14px}.console-rank-select{height:32px;padding:8px 0px;overflow:hidden}.console-rank-select .rank-item{width:20px;height:16px;line-height:16px;overflow:hidden;display:block;float:left;font-size:16px;color:#CCC;cursor:pointer;zoom:1}.console-rank-select .rank-active{color:#09C}.console-rank-select .rank-hover{color:#3CF}.simple-loading{position:relative}.simple-loading .simple-loading-inner{margin-left:auto;margin-right:auto}.simple-loading-1:before,.simple-loading-1:after,.simple-loading-1{border-radius:50%;width:14px;height:14px;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation:simple-loading-1 1.8s infinite ease-in-out;animation:simple-loading-1 1.8s infinite ease-in-out}.simple-loading-1{font-size:10px;position:relative;text-indent:-9999em;-webkit-animation-delay:0.16s;animation-delay:0.16s}.simple-loading-1:before{left:-30px}.simple-loading-1:after{left:30px;-webkit-animation-delay:0.32s;animation-delay:0.32s}.simple-loading-1:before,.simple-loading-1:after{content:'';position:absolute;top:0}@-webkit-keyframes simple-loading-1{0%,80%,100%{box-shadow:0 2.5em 0 -1.3em #ddd}40%{box-shadow:0 2.5em 0 0 #ddd}}@keyframes simple-loading-1{0%,80%,100%{box-shadow:0 2.5em 0 -1.3em #ddd}40%{box-shadow:0 2.5em 0 0 #ddd}}.feedback-container{position:fixed;right:0px;bottom:100px;font-family:微软雅黑, 'Microsoft Yahei', 'Hiragino Sans GB', tahoma, arial, 宋体;font-size:12px;font-stretch:normal;font-style:normal;font-variant:normal;font-weight:normal;z-index:100}.feedback-container:hover .feedback-trigger .feedback-trigger-text,.feedback-container.active .feedback-trigger .feedback-trigger-text{width:56px;padding:0 0px 0 4px}.feedback-container h1,.feedback-container h2,.feedback-container textarea,.feedback-container input{margin:0;padding:0;border:0}.feedback-container .feedback{position:absolute;width:396px;background:rgba(0,162,202,0.5);padding:3px;right:81px;bottom:0}.feedback-container .feedback .feedback-panel{width:390px;background:#fff;padding:20px}.feedback-container .feedback .feedback-title{border-bottom:1px solid #eee;padding-bottom:8px;margin-bottom:15px}.feedback-container .feedback .feedback-title h1{color:#000;font-size:16px;display:inline-block}.feedback-container .feedback .feedback-title h1 i{cursor:pointer;margin-top:6px;float:right}.feedback-container .feedback .feedback-content{position:relative;margin-bottom:15px}.feedback-container .feedback .feedback-content h2{font-size:14px;margin-bottom:5px}.feedback-container .feedback .feedback-content .must{position:absolute;left:-10px;top:3px;color:red}.feedback-container .feedback textarea,.feedback-container .feedback input{font:12px/1.5 "\5FAE\8F6F\96C5\9ED1","Microsoft Yahei","Hiragino Sans GB",tahoma,arial,"\5B8B\4F53"}.feedback-container .feedback .feedback-content textarea{resize:none;height:106px;width:100%;padding:9px 10px;margin:6px 1px 1px 0;border:solid 1px #e8e8e8;border-radius:4px;line-height:16px;color:#333;font-size:12px;outline:0}.feedback-container .feedback .feedback-content .feedback-content-count{color:#666;margin-top:5px}.feedback-container .feedback .feedback-contact{margin-bottom:25px;position:relative}.feedback-container .feedback .feedback-contact h2{font-size:14px;margin-bottom:5px}.feedback-container .feedback .feedback-contact input{height:36px;margin-bottom:20px;resize:none;width:100%;padding:9px 10px;margin:6px 1px 1px 0;border:solid 1px #e8e8e8;line-height:16px;color:#333;font-size:12px;outline:0;background:#fff;border-radius:4px;text-decoration:none;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;user-select:none}.feedback-container .feedback .feedback-contact input:focus{background:#e9fbfe;border:solid 1px #e8e8e8}.feedback-container .feedback .feedback-contact .inputError{position:absolute;bottom:-22px;left:2px;color:red}.feedback-container .feedback .submit-btn{padding:0;height:24px;line-height:24px;font-size:12px;display:inline-block;min-width:78px;background:#00a2ca;color:#fff;text-align:center;outline:none;border-radius:0;text-decoration:none;cursor:pointer}.feedback-container .feedback .submit-btn:hover{background:#33b5d4;border-color:#33b5d4;text-decoration:none}.feedback-container .feedback .feedback-footer{text-align:center;padding:5px 0}.feedback-container .feedback .submit-btn.disabled{background:#efefef;border-color:#efefef;color:#ccc;cursor:default}.feedback .thanks{font-size:16px;margin-left:15px;color:#000;position:relative;top:-9px}.feedback .feedback-close{display:inline-block;float:right;cursor:pointer;font-size:18px}.feedback .feedback-check{font-size:30px;color:#65ce00}.feedback-container .feedback-trigger{display:inline-block;background-color:#3d5061;font-size:12px;color:#fff;border-radius:4px;cursor:pointer;padding:4px 4px 1px 1px}.feedback-container .feedback-trigger .feedback-trigger-text{padding:0;display:inline-block;height:16px;overflow:hidden;width:0;-moz-transition:all 0.3s ease-in;-webkit-transition:all 0.3s ease-in;-o-transition:all 0.3s ease-in;transition:all 0.3s ease-in}.feedback-container .feedback-trigger .feedback-trigger-icon{padding:0;display:inline-block;font-size:15px}.console-guide-dialog .modal-dialog{width:840px}.console-guide-dialog .modal-body{margin-bottom:15px}.console-guide-dialog .carousel-control{display:none}.console-guide-dialog .carousel-indicators{bottom:-40px !important}.console-guide-dialog .carousel-indicators li{background:#e8e8e8;width:16px;height:16px;border-radius:12px;margin:0 0 0 2px}.console-guide-dialog .carousel-indicators li.active{background:#09c;width:16px;height:16px;border-radius:12px;margin:0 0 0 2px}.console-guide-dialog .console-guide-dialog-link{position:absolute;z-index:100;bottom:20px;right:20px}.console-tag-select{position:absolute;width:320px}.console-tag-select ul{list-style:none;box-shadow:none !important;display:block;margin:0;padding:0}.console-tag-select .console-tag-dropdown{position:absolute;top:100%;left:0;z-index:1000;margin-top:2px;width:326px}.console-tag-select .console-tag-dropdown .dropdown-menu{position:static;border:1px solid #e1e6eb;width:160px}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-key-item-block{padding:7px 16px;display:block}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-key-item-empty{color:#999;font-style:italic}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-key-item-title .console-tag-key-item-block{background:#F5F6FA;border-bottom:1px solid #eee}.console-tag-select .console-tag-dropdown .dropdown-menu li a{border-bottom:1px solid #eee;white-space:pre-line;position:relative}.console-tag-select .console-tag-dropdown .dropdown-menu li a:hover,.console-tag-select .console-tag-dropdown .dropdown-menu li a:focus{background-color:#F9F9FA}.console-tag-select .console-tag-dropdown .dropdown-menu li:last-child a{border-bottom:none}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:hover,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:focus{text-decoration:none;outline:0;-webkit-transition:backgroud 0.2s ease, 0.2s ease}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a .console-tag-selected-icon,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:hover .console-tag-selected-icon,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-active a:focus .console-tag-selected-icon{display:block}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active{border-left:2px solid #09c;margin-left:-1px}.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active a,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active a:hover,.console-tag-select .console-tag-dropdown .dropdown-menu li.tag-key-active a:focus{padding-left:15px}.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-value-item a.tag-active,.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-value-item a.tag-active:hover,.console-tag-select .console-tag-dropdown .dropdown-menu .console-tag-value-item a.tag-active:focus{background-color:#F9F9FA}.console-tag-select .console-tag-dropdown .console-tag-value-dropdown{margin-left:-1px}.console-tag-select .console-tag-pagepick{padding:0 5px}.console-tag-select .console-tag-pagepick a{display:inline-block !important;border-bottom:none !important;-webkit-user-select:none}.console-tag-select .console-tag-selected-icon{display:none;float:right;font-size:14px;position:absolute;right:8px;top:8px}.console-tag-select .console-tag-label-wrap{padding-left:2px}.console-tag-select .console-tag-label{padding:5px 20px 5px 5px;background:#f1f1f1;position:relative;margin-left:2px}.console-tag-select .console-tag-label .console-tag-label-remove{position:absolute;top:0;right:0;width:20px;height:27px;line-height:27px;text-align:center;cursor:pointer}.console-tag-select-view .console-tag-label{padding:5px 20px 5px 5px;background:#f1f1f1;position:relative;margin-left:2px}.console-tag-select-view .console-tag-label .console-tag-label-remove{position:absolute;top:0;right:0;width:20px;height:27px;line-height:27px;text-align:center;cursor:pointer;color:#666}.console-tag-select-view .console-tag-label .console-tag-label-colon{padding:0 1px}.console-tag-edit .tag-panel{min-height:120px;border:2px dashed #ddd;padding:8px}.console-tag-edit .tag-panel .console-tag-select-view .console-tag-label{margin:4px}.console-tag-edit .tag-edit-tip{color:#999;font-style:italic;margin-top:8px}.console-tag-edit-form{display:inline-block}.console-tag-edit-form input.form-control{width:80px}.console-tag-edit-form.form-inline .form-group{margin:0 8px 0 0px}.console-tag-loading-overlay{position:absolute;height:100%;width:100%;top:0;left:0;background:#fff;opacity:0.5;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50)}.console-tag-loading-block{position:absolute;top:50%;left:50%}.console-searchbar-textinput{min-width:180px}.console-shuttle .title{border-left:1px solid #e1e6eb;border-right:1px solid #e1e6eb;border-top:1px solid #e1e6eb;background-color:#F5F6FA;padding-left:5px;line-height:30px}.console-shuttle .search{position:relative}.console-shuttle .search .icon-search{position:absolute;right:5px;top:10px;font-size:12px}.console-shuttle .search input{width:100%;height:32px}.console-shuttle .selector{border:1px solid #e1e6eb;height:240px}.console-shuttle .left-selector{height:220px}.console-shuttle .right-selector{height:240px}.console-shuttle .search-hidden{height:240px}.console-shuttle .btn{display:block;margin:12px 45%}.console-shuttle .console-selector{width:40%}.console-shuttle .console-selector-result{width:40%}.console-shuttle .selector-group-options{vertical-align:middle;width:20%;text-align:center;margin-top:100px}.console-shuttle .selector-list .line-head{line-height:12px;margin-bottom:8px;color:#000}.console-shuttle .selector-list .line-bottom{line-height:12px;color:#999}.console-shuttle .selector-list .line-column-left{display:inline-block}.console-shuttle .selector-list .line-column-right{display:inline-block;float:right;padding:5px 0;color:#000}.console-shuttle .selector-list .line-yellow-text{color:#ff6600}.console-shuttle .selector-list .selector-item{height:auto;line-height:normal;padding:10px;text-indent:0}.console-shuttle .selector-list .selector-item:hover{color:auto;background:#f9f9f9}.console-shuttle .selector-list .selector-item.active{color:#fff;background:#0099cb}.console-shuttle .selector-list .selector-item.active .line-head{color:#fff}.console-shuttle .selector-list .selector-item.active .line-bottom{color:#fff}.console-shuttle .selector-list .selector-item.active .line-yellow-text{color:#fff}.console-shuttle .selector-list .selector-item.active .line-column-right{color:#fff}body{font-size:12px}body,h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:"Helvetica Neue", "Luxi Sans", "DejaVu Sans", Tahoma, "Hiragino Sans GB", STHeiti, "Microsoft YaHei"}a{color:#06C;cursor:pointer}a:hover{color:#039}label{font-size:100%}.nowrap{white-space:nowrap}.breakall{word-break:break-all;word-wrap:break-word}input::-ms-clear{display:none}input[type="radio"],input[type="checkbox"]{margin-top:2px;margin-top:1px \9}.console-container{padding:0 15px}.console-sidebar{border-right:1px solid #DDD}.console-sidebar .nav{margin-right:4px}.console-sidebar .nav li{border-bottom:1px solid #DDD;padding:4px 0px;position:relative}.console-sidebar .nav li a{color:#333;padding:6px 16px;border-left:2px solid #FFF}.console-sidebar .nav li a:hover{background-color:#FFF;border-left:2px solid #F90}.console-sidebar .nav li a span[class^="icon-"]{position:absolute;left:0px;top:8px;color:#999;font-size:14px}.console-sidebar .nav li.active a{color:#fff;border-left:2px solid #F90;background-color:#313844}.console-sidebar .nav .nav{margin-right:0px}.console-sidebar .nav .nav li{border-bottom:0px}.console-sidebar .nav .nav li a{text-indent:12px;background:#FFF;border-left-color:#FFF;color:#333}.console-sidebar .nav .nav li a:hover{background-color:#FFF;border-left:2px solid #F90}.console-sidebar .nav .nav li.active a{color:#fff;border-left:2px solid #F90;background-color:#313844}.console-instance-head{padding:3px 0px;border-bottom:1px solid #DDD}.console-instance-head h3.instance-id{display:inline-block;margin-right:8px;vertical-align:middle}.console-instance-head .pull-right{padding:16px 0px 10px}.dropdown-menu{font-size:12px;border-radius:0px;padding:0px;box-shadow:2px 2px 8px rgba(0,0,0,0.2)}.dropdown-menu li a{padding:7px 16px}.dropdown-menu .divider{margin:0px 0px}.console-chart{width:100%}.tooltip{word-break:break-all}.popover .popover-inner{padding:8px}.popover .popover-inner .popover-content{padding:0px}.console-not-service{margin-top:80px}.console-not-service .console-not-service-icon{text-align:right;padding-top:8px}.console-not-service .console-not-service-title{font-size:20px}.console-not-service .console-not-service-text{font-size:12px;color:#666}.console-not-service .console-not-service-link{border-top:1px solid #EEE;margin-top:16px;padding-top:16px}.console-step{height:24px;position:relative;margin-left:0px;margin-right:0px}.console-step .step{font-size:14px;height:24px;line-height:24px;color:#FFF;background:#cacaca;z-index:1;text-align:center}.console-step .step:before{content:'';display:block;position:absolute;left:-12px;z-index:8;top:0px;border-top:12px solid #cacaca;border-left:12px solid transparent !important;border-bottom:12px solid #cacaca}.console-step .step:after{content:'';display:block;width:16px;height:24px;position:absolute;right:0px;z-index:9;top:0px;border-top:12px solid transparent !important;border-left:12px solid #cacaca;border-bottom:12px solid transparent !important;background-color:#FFF}.console-step .step-first:before{display:none}.console-step .step-end:after{display:none}.console-step .step-pass{background-color:#99dcf3}.console-step .step-pass:after{border-color:#99dcf3}.console-step .step-pass:before{border-color:#99dcf3}.console-step .step-active{background-color:#00a0c7}.console-step .step-active:after{border-color:#00a0c7}.console-step .step-active:before{border-color:#00a0c7} ================================================ FILE: console/src/main/resources/static/console-ui/public/css/font-awesome.css ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ /*! * Font Awesome 4.5.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ /* FONT PATH * -------------------------- */ @font-face { font-family: 'FontAwesome'; src: url('../fonts/fontawesome-webfont.eot?v=4.5.0'); src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.5.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.5.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.5.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.5.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.5.0#fontawesomeregular') format('svg'); font-weight: normal; font-style: normal; } .fa { display: inline-block; font: normal normal normal 14px/1 FontAwesome; font-size: inherit; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } /* makes the font 33% larger relative to the icon container */ .fa-lg { font-size: 1.33333333em; line-height: 0.75em; vertical-align: -15%; } .fa-2x { font-size: 2em; } .fa-3x { font-size: 3em; } .fa-4x { font-size: 4em; } .fa-5x { font-size: 5em; } .fa-fw { width: 1.28571429em; text-align: center; } .fa-ul { padding-left: 0; margin-left: 2.14285714em; list-style-type: none; } .fa-ul > li { position: relative; } .fa-li { position: absolute; left: -2.14285714em; width: 2.14285714em; top: 0.14285714em; text-align: center; } .fa-li.fa-lg { left: -1.85714286em; } .fa-border { padding: .2em .25em .15em; border: solid 0.08em #eeeeee; border-radius: .1em; } .fa-pull-left { float: left; } .fa-pull-right { float: right; } .fa.fa-pull-left { margin-right: .3em; } .fa.fa-pull-right { margin-left: .3em; } /* Deprecated as of 4.4.0 */ .pull-right { float: right; } .pull-left { float: left; } .fa.pull-left { margin-right: .3em; } .fa.pull-right { margin-left: .3em; } .fa-spin { -webkit-animation: fa-spin 2s infinite linear; animation: fa-spin 2s infinite linear; } .fa-pulse { -webkit-animation: fa-spin 1s infinite steps(8); animation: fa-spin 1s infinite steps(8); } @-webkit-keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes fa-spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg); } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } .fa-rotate-90 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); -webkit-transform: rotate(90deg); -ms-transform: rotate(90deg); transform: rotate(90deg); } .fa-rotate-180 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -webkit-transform: rotate(180deg); -ms-transform: rotate(180deg); transform: rotate(180deg); } .fa-rotate-270 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); -webkit-transform: rotate(270deg); -ms-transform: rotate(270deg); transform: rotate(270deg); } .fa-flip-horizontal { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); -webkit-transform: scale(-1, 1); -ms-transform: scale(-1, 1); transform: scale(-1, 1); } .fa-flip-vertical { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); -webkit-transform: scale(1, -1); -ms-transform: scale(1, -1); transform: scale(1, -1); } :root .fa-rotate-90, :root .fa-rotate-180, :root .fa-rotate-270, :root .fa-flip-horizontal, :root .fa-flip-vertical { filter: none; } .fa-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle; } .fa-stack-1x, .fa-stack-2x { position: absolute; left: 0; width: 100%; text-align: center; } .fa-stack-1x { line-height: inherit; } .fa-stack-2x { font-size: 2em; } .fa-inverse { color: #ffffff; } /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen readers do not read off random characters that represent icons */ .fa-glass:before { content: "\f000"; } .fa-music:before { content: "\f001"; } .fa-search:before { content: "\f002"; } .fa-envelope-o:before { content: "\f003"; } .fa-heart:before { content: "\f004"; } .fa-star:before { content: "\f005"; } .fa-star-o:before { content: "\f006"; } .fa-user:before { content: "\f007"; } .fa-film:before { content: "\f008"; } .fa-th-large:before { content: "\f009"; } .fa-th:before { content: "\f00a"; } .fa-th-list:before { content: "\f00b"; } .fa-check:before { content: "\f00c"; } .fa-remove:before, .fa-close:before, .fa-times:before { content: "\f00d"; } .fa-search-plus:before { content: "\f00e"; } .fa-search-minus:before { content: "\f010"; } .fa-power-off:before { content: "\f011"; } .fa-signal:before { content: "\f012"; } .fa-gear:before, .fa-cog:before { content: "\f013"; } .fa-trash-o:before { content: "\f014"; } .fa-home:before { content: "\f015"; } .fa-file-o:before { content: "\f016"; } .fa-clock-o:before { content: "\f017"; } .fa-road:before { content: "\f018"; } .fa-download:before { content: "\f019"; } .fa-arrow-circle-o-down:before { content: "\f01a"; } .fa-arrow-circle-o-up:before { content: "\f01b"; } .fa-inbox:before { content: "\f01c"; } .fa-play-circle-o:before { content: "\f01d"; } .fa-rotate-right:before, .fa-repeat:before { content: "\f01e"; } .fa-refresh:before { content: "\f021"; } .fa-list-alt:before { content: "\f022"; } .fa-lock:before { content: "\f023"; } .fa-flag:before { content: "\f024"; } .fa-headphones:before { content: "\f025"; } .fa-volume-off:before { content: "\f026"; } .fa-volume-down:before { content: "\f027"; } .fa-volume-up:before { content: "\f028"; } .fa-qrcode:before { content: "\f029"; } .fa-barcode:before { content: "\f02a"; } .fa-tag:before { content: "\f02b"; } .fa-tags:before { content: "\f02c"; } .fa-book:before { content: "\f02d"; } .fa-bookmark:before { content: "\f02e"; } .fa-print:before { content: "\f02f"; } .fa-camera:before { content: "\f030"; } .fa-font:before { content: "\f031"; } .fa-bold:before { content: "\f032"; } .fa-italic:before { content: "\f033"; } .fa-text-height:before { content: "\f034"; } .fa-text-width:before { content: "\f035"; } .fa-align-left:before { content: "\f036"; } .fa-align-center:before { content: "\f037"; } .fa-align-right:before { content: "\f038"; } .fa-align-justify:before { content: "\f039"; } .fa-list:before { content: "\f03a"; } .fa-dedent:before, .fa-outdent:before { content: "\f03b"; } .fa-indent:before { content: "\f03c"; } .fa-video-camera:before { content: "\f03d"; } .fa-photo:before, .fa-image:before, .fa-picture-o:before { content: "\f03e"; } .fa-pencil:before { content: "\f040"; } .fa-map-marker:before { content: "\f041"; } .fa-adjust:before { content: "\f042"; } .fa-tint:before { content: "\f043"; } .fa-edit:before, .fa-pencil-square-o:before { content: "\f044"; } .fa-share-square-o:before { content: "\f045"; } .fa-check-square-o:before { content: "\f046"; } .fa-arrows:before { content: "\f047"; } .fa-step-backward:before { content: "\f048"; } .fa-fast-backward:before { content: "\f049"; } .fa-backward:before { content: "\f04a"; } .fa-play:before { content: "\f04b"; } .fa-pause:before { content: "\f04c"; } .fa-stop:before { content: "\f04d"; } .fa-forward:before { content: "\f04e"; } .fa-fast-forward:before { content: "\f050"; } .fa-step-forward:before { content: "\f051"; } .fa-eject:before { content: "\f052"; } .fa-chevron-left:before { content: "\f053"; } .fa-chevron-right:before { content: "\f054"; } .fa-plus-circle:before { content: "\f055"; } .fa-minus-circle:before { content: "\f056"; } .fa-times-circle:before { content: "\f057"; } .fa-check-circle:before { content: "\f058"; } .fa-question-circle:before { content: "\f059"; } .fa-info-circle:before { content: "\f05a"; } .fa-crosshairs:before { content: "\f05b"; } .fa-times-circle-o:before { content: "\f05c"; } .fa-check-circle-o:before { content: "\f05d"; } .fa-ban:before { content: "\f05e"; } .fa-arrow-left:before { content: "\f060"; } .fa-arrow-right:before { content: "\f061"; } .fa-arrow-up:before { content: "\f062"; } .fa-arrow-down:before { content: "\f063"; } .fa-mail-forward:before, .fa-share:before { content: "\f064"; } .fa-expand:before { content: "\f065"; } .fa-compress:before { content: "\f066"; } .fa-plus:before { content: "\f067"; } .fa-minus:before { content: "\f068"; } .fa-asterisk:before { content: "\f069"; } .fa-exclamation-circle:before { content: "\f06a"; } .fa-gift:before { content: "\f06b"; } .fa-leaf:before { content: "\f06c"; } .fa-fire:before { content: "\f06d"; } .fa-eye:before { content: "\f06e"; } .fa-eye-slash:before { content: "\f070"; } .fa-warning:before, .fa-exclamation-triangle:before { content: "\f071"; } .fa-plane:before { content: "\f072"; } .fa-calendar:before { content: "\f073"; } .fa-random:before { content: "\f074"; } .fa-comment:before { content: "\f075"; } .fa-magnet:before { content: "\f076"; } .fa-chevron-up:before { content: "\f077"; } .fa-chevron-down:before { content: "\f078"; } .fa-retweet:before { content: "\f079"; } .fa-shopping-cart:before { content: "\f07a"; } .fa-folder:before { content: "\f07b"; } .fa-folder-open:before { content: "\f07c"; } .fa-arrows-v:before { content: "\f07d"; } .fa-arrows-h:before { content: "\f07e"; } .fa-bar-chart-o:before, .fa-bar-chart:before { content: "\f080"; } .fa-twitter-square:before { content: "\f081"; } .fa-facebook-square:before { content: "\f082"; } .fa-camera-retro:before { content: "\f083"; } .fa-key:before { content: "\f084"; } .fa-gears:before, .fa-cogs:before { content: "\f085"; } .fa-comments:before { content: "\f086"; } .fa-thumbs-o-up:before { content: "\f087"; } .fa-thumbs-o-down:before { content: "\f088"; } .fa-star-half:before { content: "\f089"; } .fa-heart-o:before { content: "\f08a"; } .fa-sign-out:before { content: "\f08b"; } .fa-linkedin-square:before { content: "\f08c"; } .fa-thumb-tack:before { content: "\f08d"; } .fa-external-link:before { content: "\f08e"; } .fa-sign-in:before { content: "\f090"; } .fa-trophy:before { content: "\f091"; } .fa-github-square:before { content: "\f092"; } .fa-upload:before { content: "\f093"; } .fa-lemon-o:before { content: "\f094"; } .fa-phone:before { content: "\f095"; } .fa-square-o:before { content: "\f096"; } .fa-bookmark-o:before { content: "\f097"; } .fa-phone-square:before { content: "\f098"; } .fa-twitter:before { content: "\f099"; } .fa-facebook-f:before, .fa-facebook:before { content: "\f09a"; } .fa-github:before { content: "\f09b"; } .fa-unlock:before { content: "\f09c"; } .fa-credit-card:before { content: "\f09d"; } .fa-feed:before, .fa-rss:before { content: "\f09e"; } .fa-hdd-o:before { content: "\f0a0"; } .fa-bullhorn:before { content: "\f0a1"; } .fa-bell:before { content: "\f0f3"; } .fa-certificate:before { content: "\f0a3"; } .fa-hand-o-right:before { content: "\f0a4"; } .fa-hand-o-left:before { content: "\f0a5"; } .fa-hand-o-up:before { content: "\f0a6"; } .fa-hand-o-down:before { content: "\f0a7"; } .fa-arrow-circle-left:before { content: "\f0a8"; } .fa-arrow-circle-right:before { content: "\f0a9"; } .fa-arrow-circle-up:before { content: "\f0aa"; } .fa-arrow-circle-down:before { content: "\f0ab"; } .fa-globe:before { content: "\f0ac"; } .fa-wrench:before { content: "\f0ad"; } .fa-tasks:before { content: "\f0ae"; } .fa-filter:before { content: "\f0b0"; } .fa-briefcase:before { content: "\f0b1"; } .fa-arrows-alt:before { content: "\f0b2"; } .fa-group:before, .fa-users:before { content: "\f0c0"; } .fa-chain:before, .fa-link:before { content: "\f0c1"; } .fa-cloud:before { content: "\f0c2"; } .fa-flask:before { content: "\f0c3"; } .fa-cut:before, .fa-scissors:before { content: "\f0c4"; } .fa-copy:before, .fa-files-o:before { content: "\f0c5"; } .fa-paperclip:before { content: "\f0c6"; } .fa-save:before, .fa-floppy-o:before { content: "\f0c7"; } .fa-square:before { content: "\f0c8"; } .fa-navicon:before, .fa-reorder:before, .fa-bars:before { content: "\f0c9"; } .fa-list-ul:before { content: "\f0ca"; } .fa-list-ol:before { content: "\f0cb"; } .fa-strikethrough:before { content: "\f0cc"; } .fa-underline:before { content: "\f0cd"; } .fa-table:before { content: "\f0ce"; } .fa-magic:before { content: "\f0d0"; } .fa-truck:before { content: "\f0d1"; } .fa-pinterest:before { content: "\f0d2"; } .fa-pinterest-square:before { content: "\f0d3"; } .fa-google-plus-square:before { content: "\f0d4"; } .fa-google-plus:before { content: "\f0d5"; } .fa-money:before { content: "\f0d6"; } .fa-caret-down:before { content: "\f0d7"; } .fa-caret-up:before { content: "\f0d8"; } .fa-caret-left:before { content: "\f0d9"; } .fa-caret-right:before { content: "\f0da"; } .fa-columns:before { content: "\f0db"; } .fa-unsorted:before, .fa-sort:before { content: "\f0dc"; } .fa-sort-down:before, .fa-sort-desc:before { content: "\f0dd"; } .fa-sort-up:before, .fa-sort-asc:before { content: "\f0de"; } .fa-envelope:before { content: "\f0e0"; } .fa-linkedin:before { content: "\f0e1"; } .fa-rotate-left:before, .fa-undo:before { content: "\f0e2"; } .fa-legal:before, .fa-gavel:before { content: "\f0e3"; } .fa-dashboard:before, .fa-tachometer:before { content: "\f0e4"; } .fa-comment-o:before { content: "\f0e5"; } .fa-comments-o:before { content: "\f0e6"; } .fa-flash:before, .fa-bolt:before { content: "\f0e7"; } .fa-sitemap:before { content: "\f0e8"; } .fa-umbrella:before { content: "\f0e9"; } .fa-paste:before, .fa-clipboard:before { content: "\f0ea"; } .fa-lightbulb-o:before { content: "\f0eb"; } .fa-exchange:before { content: "\f0ec"; } .fa-cloud-download:before { content: "\f0ed"; } .fa-cloud-upload:before { content: "\f0ee"; } .fa-user-md:before { content: "\f0f0"; } .fa-stethoscope:before { content: "\f0f1"; } .fa-suitcase:before { content: "\f0f2"; } .fa-bell-o:before { content: "\f0a2"; } .fa-coffee:before { content: "\f0f4"; } .fa-cutlery:before { content: "\f0f5"; } .fa-file-text-o:before { content: "\f0f6"; } .fa-building-o:before { content: "\f0f7"; } .fa-hospital-o:before { content: "\f0f8"; } .fa-ambulance:before { content: "\f0f9"; } .fa-medkit:before { content: "\f0fa"; } .fa-fighter-jet:before { content: "\f0fb"; } .fa-beer:before { content: "\f0fc"; } .fa-h-square:before { content: "\f0fd"; } .fa-plus-square:before { content: "\f0fe"; } .fa-angle-double-left:before { content: "\f100"; } .fa-angle-double-right:before { content: "\f101"; } .fa-angle-double-up:before { content: "\f102"; } .fa-angle-double-down:before { content: "\f103"; } .fa-angle-left:before { content: "\f104"; } .fa-angle-right:before { content: "\f105"; } .fa-angle-up:before { content: "\f106"; } .fa-angle-down:before { content: "\f107"; } .fa-desktop:before { content: "\f108"; } .fa-laptop:before { content: "\f109"; } .fa-tablet:before { content: "\f10a"; } .fa-mobile-phone:before, .fa-mobile:before { content: "\f10b"; } .fa-circle-o:before { content: "\f10c"; } .fa-quote-left:before { content: "\f10d"; } .fa-quote-right:before { content: "\f10e"; } .fa-spinner:before { content: "\f110"; } .fa-circle:before { content: "\f111"; } .fa-mail-reply:before, .fa-reply:before { content: "\f112"; } .fa-github-alt:before { content: "\f113"; } .fa-folder-o:before { content: "\f114"; } .fa-folder-open-o:before { content: "\f115"; } .fa-smile-o:before { content: "\f118"; } .fa-frown-o:before { content: "\f119"; } .fa-meh-o:before { content: "\f11a"; } .fa-gamepad:before { content: "\f11b"; } .fa-keyboard-o:before { content: "\f11c"; } .fa-flag-o:before { content: "\f11d"; } .fa-flag-checkered:before { content: "\f11e"; } .fa-terminal:before { content: "\f120"; } .fa-code:before { content: "\f121"; } .fa-mail-reply-all:before, .fa-reply-all:before { content: "\f122"; } .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { content: "\f123"; } .fa-location-arrow:before { content: "\f124"; } .fa-crop:before { content: "\f125"; } .fa-code-fork:before { content: "\f126"; } .fa-unlink:before, .fa-chain-broken:before { content: "\f127"; } .fa-question:before { content: "\f128"; } .fa-info:before { content: "\f129"; } .fa-exclamation:before { content: "\f12a"; } .fa-superscript:before { content: "\f12b"; } .fa-subscript:before { content: "\f12c"; } .fa-eraser:before { content: "\f12d"; } .fa-puzzle-piece:before { content: "\f12e"; } .fa-microphone:before { content: "\f130"; } .fa-microphone-slash:before { content: "\f131"; } .fa-shield:before { content: "\f132"; } .fa-calendar-o:before { content: "\f133"; } .fa-fire-extinguisher:before { content: "\f134"; } .fa-rocket:before { content: "\f135"; } .fa-maxcdn:before { content: "\f136"; } .fa-chevron-circle-left:before { content: "\f137"; } .fa-chevron-circle-right:before { content: "\f138"; } .fa-chevron-circle-up:before { content: "\f139"; } .fa-chevron-circle-down:before { content: "\f13a"; } .fa-html5:before { content: "\f13b"; } .fa-css3:before { content: "\f13c"; } .fa-anchor:before { content: "\f13d"; } .fa-unlock-alt:before { content: "\f13e"; } .fa-bullseye:before { content: "\f140"; } .fa-ellipsis-h:before { content: "\f141"; } .fa-ellipsis-v:before { content: "\f142"; } .fa-rss-square:before { content: "\f143"; } .fa-play-circle:before { content: "\f144"; } .fa-ticket:before { content: "\f145"; } .fa-minus-square:before { content: "\f146"; } .fa-minus-square-o:before { content: "\f147"; } .fa-level-up:before { content: "\f148"; } .fa-level-down:before { content: "\f149"; } .fa-check-square:before { content: "\f14a"; } .fa-pencil-square:before { content: "\f14b"; } .fa-external-link-square:before { content: "\f14c"; } .fa-share-square:before { content: "\f14d"; } .fa-compass:before { content: "\f14e"; } .fa-toggle-down:before, .fa-caret-square-o-down:before { content: "\f150"; } .fa-toggle-up:before, .fa-caret-square-o-up:before { content: "\f151"; } .fa-toggle-right:before, .fa-caret-square-o-right:before { content: "\f152"; } .fa-euro:before, .fa-eur:before { content: "\f153"; } .fa-gbp:before { content: "\f154"; } .fa-dollar:before, .fa-usd:before { content: "\f155"; } .fa-rupee:before, .fa-inr:before { content: "\f156"; } .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { content: "\f157"; } .fa-ruble:before, .fa-rouble:before, .fa-rub:before { content: "\f158"; } .fa-won:before, .fa-krw:before { content: "\f159"; } .fa-bitcoin:before, .fa-btc:before { content: "\f15a"; } .fa-file:before { content: "\f15b"; } .fa-file-text:before { content: "\f15c"; } .fa-sort-alpha-asc:before { content: "\f15d"; } .fa-sort-alpha-desc:before { content: "\f15e"; } .fa-sort-amount-asc:before { content: "\f160"; } .fa-sort-amount-desc:before { content: "\f161"; } .fa-sort-numeric-asc:before { content: "\f162"; } .fa-sort-numeric-desc:before { content: "\f163"; } .fa-thumbs-up:before { content: "\f164"; } .fa-thumbs-down:before { content: "\f165"; } .fa-youtube-square:before { content: "\f166"; } .fa-youtube:before { content: "\f167"; } .fa-xing:before { content: "\f168"; } .fa-xing-square:before { content: "\f169"; } .fa-youtube-play:before { content: "\f16a"; } .fa-dropbox:before { content: "\f16b"; } .fa-stack-overflow:before { content: "\f16c"; } .fa-instagram:before { content: "\f16d"; } .fa-flickr:before { content: "\f16e"; } .fa-adn:before { content: "\f170"; } .fa-bitbucket:before { content: "\f171"; } .fa-bitbucket-square:before { content: "\f172"; } .fa-tumblr:before { content: "\f173"; } .fa-tumblr-square:before { content: "\f174"; } .fa-long-arrow-down:before { content: "\f175"; } .fa-long-arrow-up:before { content: "\f176"; } .fa-long-arrow-left:before { content: "\f177"; } .fa-long-arrow-right:before { content: "\f178"; } .fa-apple:before { content: "\f179"; } .fa-windows:before { content: "\f17a"; } .fa-android:before { content: "\f17b"; } .fa-linux:before { content: "\f17c"; } .fa-dribbble:before { content: "\f17d"; } .fa-skype:before { content: "\f17e"; } .fa-foursquare:before { content: "\f180"; } .fa-trello:before { content: "\f181"; } .fa-female:before { content: "\f182"; } .fa-male:before { content: "\f183"; } .fa-gittip:before, .fa-gratipay:before { content: "\f184"; } .fa-sun-o:before { content: "\f185"; } .fa-moon-o:before { content: "\f186"; } .fa-archive:before { content: "\f187"; } .fa-bug:before { content: "\f188"; } .fa-vk:before { content: "\f189"; } .fa-weibo:before { content: "\f18a"; } .fa-renren:before { content: "\f18b"; } .fa-pagelines:before { content: "\f18c"; } .fa-stack-exchange:before { content: "\f18d"; } .fa-arrow-circle-o-right:before { content: "\f18e"; } .fa-arrow-circle-o-left:before { content: "\f190"; } .fa-toggle-left:before, .fa-caret-square-o-left:before { content: "\f191"; } .fa-dot-circle-o:before { content: "\f192"; } .fa-wheelchair:before { content: "\f193"; } .fa-vimeo-square:before { content: "\f194"; } .fa-turkish-lira:before, .fa-try:before { content: "\f195"; } .fa-plus-square-o:before { content: "\f196"; } .fa-space-shuttle:before { content: "\f197"; } .fa-slack:before { content: "\f198"; } .fa-envelope-square:before { content: "\f199"; } .fa-wordpress:before { content: "\f19a"; } .fa-openid:before { content: "\f19b"; } .fa-institution:before, .fa-bank:before, .fa-university:before { content: "\f19c"; } .fa-mortar-board:before, .fa-graduation-cap:before { content: "\f19d"; } .fa-yahoo:before { content: "\f19e"; } .fa-google:before { content: "\f1a0"; } .fa-reddit:before { content: "\f1a1"; } .fa-reddit-square:before { content: "\f1a2"; } .fa-stumbleupon-circle:before { content: "\f1a3"; } .fa-stumbleupon:before { content: "\f1a4"; } .fa-delicious:before { content: "\f1a5"; } .fa-digg:before { content: "\f1a6"; } .fa-pied-piper:before { content: "\f1a7"; } .fa-pied-piper-alt:before { content: "\f1a8"; } .fa-drupal:before { content: "\f1a9"; } .fa-joomla:before { content: "\f1aa"; } .fa-language:before { content: "\f1ab"; } .fa-fax:before { content: "\f1ac"; } .fa-building:before { content: "\f1ad"; } .fa-child:before { content: "\f1ae"; } .fa-paw:before { content: "\f1b0"; } .fa-spoon:before { content: "\f1b1"; } .fa-cube:before { content: "\f1b2"; } .fa-cubes:before { content: "\f1b3"; } .fa-behance:before { content: "\f1b4"; } .fa-behance-square:before { content: "\f1b5"; } .fa-steam:before { content: "\f1b6"; } .fa-steam-square:before { content: "\f1b7"; } .fa-recycle:before { content: "\f1b8"; } .fa-automobile:before, .fa-car:before { content: "\f1b9"; } .fa-cab:before, .fa-taxi:before { content: "\f1ba"; } .fa-tree:before { content: "\f1bb"; } .fa-spotify:before { content: "\f1bc"; } .fa-deviantart:before { content: "\f1bd"; } .fa-soundcloud:before { content: "\f1be"; } .fa-database:before { content: "\f1c0"; } .fa-file-pdf-o:before { content: "\f1c1"; } .fa-file-word-o:before { content: "\f1c2"; } .fa-file-excel-o:before { content: "\f1c3"; } .fa-file-powerpoint-o:before { content: "\f1c4"; } .fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { content: "\f1c5"; } .fa-file-zip-o:before, .fa-file-archive-o:before { content: "\f1c6"; } .fa-file-sound-o:before, .fa-file-audio-o:before { content: "\f1c7"; } .fa-file-movie-o:before, .fa-file-video-o:before { content: "\f1c8"; } .fa-file-code-o:before { content: "\f1c9"; } .fa-vine:before { content: "\f1ca"; } .fa-codepen:before { content: "\f1cb"; } .fa-jsfiddle:before { content: "\f1cc"; } .fa-life-bouy:before, .fa-life-buoy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { content: "\f1cd"; } .fa-circle-o-notch:before { content: "\f1ce"; } .fa-ra:before, .fa-rebel:before { content: "\f1d0"; } .fa-ge:before, .fa-empire:before { content: "\f1d1"; } .fa-git-square:before { content: "\f1d2"; } .fa-git:before { content: "\f1d3"; } .fa-y-combinator-square:before, .fa-yc-square:before, .fa-hacker-news:before { content: "\f1d4"; } .fa-tencent-weibo:before { content: "\f1d5"; } .fa-qq:before { content: "\f1d6"; } .fa-wechat:before, .fa-weixin:before { content: "\f1d7"; } .fa-send:before, .fa-paper-plane:before { content: "\f1d8"; } .fa-send-o:before, .fa-paper-plane-o:before { content: "\f1d9"; } .fa-history:before { content: "\f1da"; } .fa-circle-thin:before { content: "\f1db"; } .fa-header:before { content: "\f1dc"; } .fa-paragraph:before { content: "\f1dd"; } .fa-sliders:before { content: "\f1de"; } .fa-share-alt:before { content: "\f1e0"; } .fa-share-alt-square:before { content: "\f1e1"; } .fa-bomb:before { content: "\f1e2"; } .fa-soccer-ball-o:before, .fa-futbol-o:before { content: "\f1e3"; } .fa-tty:before { content: "\f1e4"; } .fa-binoculars:before { content: "\f1e5"; } .fa-plug:before { content: "\f1e6"; } .fa-slideshare:before { content: "\f1e7"; } .fa-twitch:before { content: "\f1e8"; } .fa-yelp:before { content: "\f1e9"; } .fa-newspaper-o:before { content: "\f1ea"; } .fa-wifi:before { content: "\f1eb"; } .fa-calculator:before { content: "\f1ec"; } .fa-paypal:before { content: "\f1ed"; } .fa-google-wallet:before { content: "\f1ee"; } .fa-cc-visa:before { content: "\f1f0"; } .fa-cc-mastercard:before { content: "\f1f1"; } .fa-cc-discover:before { content: "\f1f2"; } .fa-cc-amex:before { content: "\f1f3"; } .fa-cc-paypal:before { content: "\f1f4"; } .fa-cc-stripe:before { content: "\f1f5"; } .fa-bell-slash:before { content: "\f1f6"; } .fa-bell-slash-o:before { content: "\f1f7"; } .fa-trash:before { content: "\f1f8"; } .fa-copyright:before { content: "\f1f9"; } .fa-at:before { content: "\f1fa"; } .fa-eyedropper:before { content: "\f1fb"; } .fa-paint-brush:before { content: "\f1fc"; } .fa-birthday-cake:before { content: "\f1fd"; } .fa-area-chart:before { content: "\f1fe"; } .fa-pie-chart:before { content: "\f200"; } .fa-line-chart:before { content: "\f201"; } .fa-lastfm:before { content: "\f202"; } .fa-lastfm-square:before { content: "\f203"; } .fa-toggle-off:before { content: "\f204"; } .fa-toggle-on:before { content: "\f205"; } .fa-bicycle:before { content: "\f206"; } .fa-bus:before { content: "\f207"; } .fa-ioxhost:before { content: "\f208"; } .fa-angellist:before { content: "\f209"; } .fa-cc:before { content: "\f20a"; } .fa-shekel:before, .fa-sheqel:before, .fa-ils:before { content: "\f20b"; } .fa-meanpath:before { content: "\f20c"; } .fa-buysellads:before { content: "\f20d"; } .fa-connectdevelop:before { content: "\f20e"; } .fa-dashcube:before { content: "\f210"; } .fa-forumbee:before { content: "\f211"; } .fa-leanpub:before { content: "\f212"; } .fa-sellsy:before { content: "\f213"; } .fa-shirtsinbulk:before { content: "\f214"; } .fa-simplybuilt:before { content: "\f215"; } .fa-skyatlas:before { content: "\f216"; } .fa-cart-plus:before { content: "\f217"; } .fa-cart-arrow-down:before { content: "\f218"; } .fa-diamond:before { content: "\f219"; } .fa-ship:before { content: "\f21a"; } .fa-user-secret:before { content: "\f21b"; } .fa-motorcycle:before { content: "\f21c"; } .fa-street-view:before { content: "\f21d"; } .fa-heartbeat:before { content: "\f21e"; } .fa-venus:before { content: "\f221"; } .fa-mars:before { content: "\f222"; } .fa-mercury:before { content: "\f223"; } .fa-intersex:before, .fa-transgender:before { content: "\f224"; } .fa-transgender-alt:before { content: "\f225"; } .fa-venus-double:before { content: "\f226"; } .fa-mars-double:before { content: "\f227"; } .fa-venus-mars:before { content: "\f228"; } .fa-mars-stroke:before { content: "\f229"; } .fa-mars-stroke-v:before { content: "\f22a"; } .fa-mars-stroke-h:before { content: "\f22b"; } .fa-neuter:before { content: "\f22c"; } .fa-genderless:before { content: "\f22d"; } .fa-facebook-official:before { content: "\f230"; } .fa-pinterest-p:before { content: "\f231"; } .fa-whatsapp:before { content: "\f232"; } .fa-server:before { content: "\f233"; } .fa-user-plus:before { content: "\f234"; } .fa-user-times:before { content: "\f235"; } .fa-hotel:before, .fa-bed:before { content: "\f236"; } .fa-viacoin:before { content: "\f237"; } .fa-train:before { content: "\f238"; } .fa-subway:before { content: "\f239"; } .fa-medium:before { content: "\f23a"; } .fa-yc:before, .fa-y-combinator:before { content: "\f23b"; } .fa-optin-monster:before { content: "\f23c"; } .fa-opencart:before { content: "\f23d"; } .fa-expeditedssl:before { content: "\f23e"; } .fa-battery-4:before, .fa-battery-full:before { content: "\f240"; } .fa-battery-3:before, .fa-battery-three-quarters:before { content: "\f241"; } .fa-battery-2:before, .fa-battery-half:before { content: "\f242"; } .fa-battery-1:before, .fa-battery-quarter:before { content: "\f243"; } .fa-battery-0:before, .fa-battery-empty:before { content: "\f244"; } .fa-mouse-pointer:before { content: "\f245"; } .fa-i-cursor:before { content: "\f246"; } .fa-object-group:before { content: "\f247"; } .fa-object-ungroup:before { content: "\f248"; } .fa-sticky-note:before { content: "\f249"; } .fa-sticky-note-o:before { content: "\f24a"; } .fa-cc-jcb:before { content: "\f24b"; } .fa-cc-diners-club:before { content: "\f24c"; } .fa-clone:before { content: "\f24d"; } .fa-balance-scale:before { content: "\f24e"; } .fa-hourglass-o:before { content: "\f250"; } .fa-hourglass-1:before, .fa-hourglass-start:before { content: "\f251"; } .fa-hourglass-2:before, .fa-hourglass-half:before { content: "\f252"; } .fa-hourglass-3:before, .fa-hourglass-end:before { content: "\f253"; } .fa-hourglass:before { content: "\f254"; } .fa-hand-grab-o:before, .fa-hand-rock-o:before { content: "\f255"; } .fa-hand-stop-o:before, .fa-hand-paper-o:before { content: "\f256"; } .fa-hand-scissors-o:before { content: "\f257"; } .fa-hand-lizard-o:before { content: "\f258"; } .fa-hand-spock-o:before { content: "\f259"; } .fa-hand-pointer-o:before { content: "\f25a"; } .fa-hand-peace-o:before { content: "\f25b"; } .fa-trademark:before { content: "\f25c"; } .fa-registered:before { content: "\f25d"; } .fa-creative-commons:before { content: "\f25e"; } .fa-gg:before { content: "\f260"; } .fa-gg-circle:before { content: "\f261"; } .fa-tripadvisor:before { content: "\f262"; } .fa-odnoklassniki:before { content: "\f263"; } .fa-odnoklassniki-square:before { content: "\f264"; } .fa-get-pocket:before { content: "\f265"; } .fa-wikipedia-w:before { content: "\f266"; } .fa-safari:before { content: "\f267"; } .fa-chrome:before { content: "\f268"; } .fa-firefox:before { content: "\f269"; } .fa-opera:before { content: "\f26a"; } .fa-internet-explorer:before { content: "\f26b"; } .fa-tv:before, .fa-television:before { content: "\f26c"; } .fa-contao:before { content: "\f26d"; } .fa-500px:before { content: "\f26e"; } .fa-amazon:before { content: "\f270"; } .fa-calendar-plus-o:before { content: "\f271"; } .fa-calendar-minus-o:before { content: "\f272"; } .fa-calendar-times-o:before { content: "\f273"; } .fa-calendar-check-o:before { content: "\f274"; } .fa-industry:before { content: "\f275"; } .fa-map-pin:before { content: "\f276"; } .fa-map-signs:before { content: "\f277"; } .fa-map-o:before { content: "\f278"; } .fa-map:before { content: "\f279"; } .fa-commenting:before { content: "\f27a"; } .fa-commenting-o:before { content: "\f27b"; } .fa-houzz:before { content: "\f27c"; } .fa-vimeo:before { content: "\f27d"; } .fa-black-tie:before { content: "\f27e"; } .fa-fonticons:before { content: "\f280"; } .fa-reddit-alien:before { content: "\f281"; } .fa-edge:before { content: "\f282"; } .fa-credit-card-alt:before { content: "\f283"; } .fa-codiepie:before { content: "\f284"; } .fa-modx:before { content: "\f285"; } .fa-fort-awesome:before { content: "\f286"; } .fa-usb:before { content: "\f287"; } .fa-product-hunt:before { content: "\f288"; } .fa-mixcloud:before { content: "\f289"; } .fa-scribd:before { content: "\f28a"; } .fa-pause-circle:before { content: "\f28b"; } .fa-pause-circle-o:before { content: "\f28c"; } .fa-stop-circle:before { content: "\f28d"; } .fa-stop-circle-o:before { content: "\f28e"; } .fa-shopping-bag:before { content: "\f290"; } .fa-shopping-basket:before { content: "\f291"; } .fa-hashtag:before { content: "\f292"; } .fa-bluetooth:before { content: "\f293"; } .fa-bluetooth-b:before { content: "\f294"; } .fa-percent:before { content: "\f295"; } ================================================ FILE: console/src/main/resources/static/console-ui/public/css/icon.css ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ @font-face { /*无边框*/ font-family: "iconfont-1"; src: url('icon1/iconfont.eot?t=1458627591'); /* IE9*/ src: url('icon1/iconfont.eot?t=1458627591#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('icon1/iconfont.woff?t=1458627591') format('woff'), /* chrome, firefox */ url('icon1/iconfont.ttf?t=1458627591') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('icon1/iconfont.svg?t=1458627591#iconfont') format('svg'); /* iOS 4.1- */ } @font-face { /*有边框*/ font-family: "iconfont-2"; src: url('icon/iconfont.eot'); /* IE9*/ src: url('icon/iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('icon/iconfont.woff') format('woff'), /* chrome, firefox */ url('icon/iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('icon/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */ } .iconfont { /* 有边框 */ font-family: "iconfont" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -webkit-text-stroke-width: 0.2px; -moz-osx-font-smoothing: grayscale; } .iconfont-1 { /*无边框*/ font-family: "iconfont-1" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -webkit-text-stroke-width: 0.2px; -moz-osx-font-smoothing: grayscale; } .iconfont-2 { /*有边框*/ font-family: "iconfont-2" !important; font-size: 16px; font-style: normal; -webkit-font-smoothing: antialiased; -webkit-text-stroke-width: 0.2px; -moz-osx-font-smoothing: grayscale; } .logo { } .panel-logo { padding-right: 2px; font-size: 18px; display: inline-block; color: #333; } .icon-lg { font-size: 80px !important; } .icon-size-md { font-size: 40px !important; vertical-align: middle; } .icon-size-lg { font-size: 80px !important; vertical-align: middle; } .icon-hsf:before { content: "\e62f" !important; } .icon-rocketmq:before { content: "\e632" !important; } .icon-notify:before { content: "\e61e" !important; } .icon-tddl:before { content: "\e61e" !important; } .icon-pandora:before { content: "\e622" !important; } .icon-ailtomcat:before { content: "\e628" !important; } .icon-configserver:before { content: "\e61e" !important; } .icon-diamondserver:before { content: "\e62a" !important; } .icon-vipserver:before { content: "\e625" !important; } .icon-eagleeye:before { content: "\e62c" !important; } .icon-tengine:before { content: "\e635" !important; } .icon-tair:before { content: "\e634" !important; } .icon-hbase:before { content: "\e62d" !important; } .icon-jstorm:before { content: "\e627" !important; } .icon-histore:before { content: "\e62e" !important; } .icon-jingwei:before { content: "\e61e" !important; } .icon-txc:before { content: "\e636" !important; } .icon-edas:before { content: "\e620" !important; } .icon-csb:before { content: "\e61e" !important; } .icon-ons:before { content: "\e630" !important; } .icon-drds:before { content: "\e61f" !important; } .icon-duct:before { content: "\e62b" !important; } .icon-amazon:before { content: "\e61e" !important; } .icon-autoload:before { content: "\e61e" !important; } .icon-switch:before { content: "\e633" !important; } .icon-sentinel:before { content: "\e623" !important; } .icon-preplan:before { content: "\e631" !important; } .icon-moses:before { content: "\e61e" !important; } .icon-zeus:before { content: "\e61e" !important; } .icon-athena:before { content: "\e61e" !important; } .icon-bcp:before { content: "\e61e" !important; } .icon-lark:before { content: "\e61e" !important; } .icon-nest:before { content: "\e61e" !important; } .icon-monkeyking:before { content: "\e61e" !important; } .icon-tab:before { content: "\e61e" !important; } .icon-oceanus:before { content: "\e61e" !important; } .icon-eos :before { content: "\e61e" !important; } .icon-sonar:before { content: "\e61e" !important; } .icon-ai:before { content: "\e605" !important; } .icon-hotcode:before { content: "\e621" !important; } .icon-taokeeper:before { content: "\e624" !important; } .icon-mdl:before { content: "\e61e" !important; } .icon-mw:before { content: "\e61e" !important; } .icon-default:before { content: "\e607" !important; } .icon-alitomcat:before { content: "\e607" !important; } ================================================ FILE: console/src/main/resources/static/console-ui/public/css/merge.css ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ .CodeMirror-merge { position: relative; border: 1px solid #ddd; white-space: pre; } .CodeMirror-merge, .CodeMirror-merge .CodeMirror { height: 350px; } .CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; } .CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; } .CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; } .CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; } .CodeMirror-merge-pane { display: inline-block; white-space: normal; vertical-align: top; } .CodeMirror-merge-pane-rightmost { position: absolute; right: 0px; z-index: 1; } .CodeMirror-merge-gap { z-index: 2; display: inline-block; height: 100%; -moz-box-sizing: border-box; box-sizing: border-box; overflow: hidden; border-left: 1px solid #ddd; border-right: 1px solid #ddd; position: relative; background: #f8f8f8; } .CodeMirror-merge-scrolllock-wrap { position: absolute; bottom: 0; left: 50%; } .CodeMirror-merge-scrolllock { position: relative; left: -50%; cursor: pointer; color: #555; line-height: 1; } .CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right { position: absolute; left: 0; top: 0; right: 0; bottom: 0; line-height: 1; } .CodeMirror-merge-copy { position: absolute; cursor: pointer; color: #44c; z-index: 3; } .CodeMirror-merge-copy-reverse { position: absolute; cursor: pointer; color: #44c; } .CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; } .CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; } .CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==); background-position: bottom left; background-repeat: repeat-x; } .CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==); background-position: bottom left; background-repeat: repeat-x; } .CodeMirror-merge-r-chunk { background: #ffffe0; } .CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; } .CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; } .CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; } .CodeMirror-merge-l-chunk { background: #eef; } .CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; } .CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; } .CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; } .CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; } .CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; } .CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; } .CodeMirror-merge-collapsed-widget:before { content: "(...)"; } .CodeMirror-merge-collapsed-widget { cursor: pointer; color: #88b; background: #eef; border: 1px solid #ddf; font-size: 90%; padding: 0 3px; border-radius: 4px; } .CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; } ================================================ FILE: console/src/main/resources/static/console-ui/public/js/codemirror.addone.fullscreen.js ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.defineOption("fullScreen", false, function(cm, val, old) { if (old == CodeMirror.Init) old = false; if (!old == !val) return; if (val) setFullscreen(cm); else setNormal(cm); }); function setFullscreen(cm) { var wrap = cm.getWrapperElement(); cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset, width: wrap.style.width, height: wrap.style.height}; wrap.style.width = ""; wrap.style.height = "auto"; wrap.className += " CodeMirror-fullscreen"; document.documentElement.style.overflow = "hidden"; cm.refresh(); } function setNormal(cm) { var wrap = cm.getWrapperElement(); wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, ""); document.documentElement.style.overflow = ""; var info = cm.state.fullScreenRestore; wrap.style.width = info.width; wrap.style.height = info.height; window.scrollTo(info.scrollLeft, info.scrollTop); cm.refresh(); } }); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/codemirror.addone.json-lint.js ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ // Depends on jsonlint.js from https://github.com/zaach/jsonlint // declare global: jsonlint (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; CodeMirror.registerHelper("lint", "json", function(text) { var found = []; jsonlint.parseError = function(str, hash) { var loc = hash.loc; found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column), to: CodeMirror.Pos(loc.last_line - 1, loc.last_column), message: str}); }; try { jsonlint.parse(text); } catch(e) {} return found; }); }); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/codemirror.addone.lint.js ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; var GUTTER_ID = "CodeMirror-lint-markers"; function showTooltip(e, content) { var tt = document.createElement("div"); tt.className = "CodeMirror-lint-tooltip"; tt.appendChild(content.cloneNode(true)); document.body.appendChild(tt); function position(e) { if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position); tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px"; tt.style.left = (e.clientX + 5) + "px"; } CodeMirror.on(document, "mousemove", position); position(e); if (tt.style.opacity != null) tt.style.opacity = 1; return tt; } function rm(elt) { if (elt.parentNode) elt.parentNode.removeChild(elt); } function hideTooltip(tt) { if (!tt.parentNode) return; if (tt.style.opacity == null) rm(tt); tt.style.opacity = 0; setTimeout(function() { rm(tt); }, 600); } function showTooltipFor(e, content, node) { var tooltip = showTooltip(e, content); function hide() { CodeMirror.off(node, "mouseout", hide); if (tooltip) { hideTooltip(tooltip); tooltip = null; } } var poll = setInterval(function() { if (tooltip) for (var n = node;; n = n.parentNode) { if (n && n.nodeType == 11) n = n.host; if (n == document.body) return; if (!n) { hide(); break; } } if (!tooltip) return clearInterval(poll); }, 400); CodeMirror.on(node, "mouseout", hide); } function LintState(cm, options, hasGutter) { this.marked = []; this.options = options; this.timeout = null; this.hasGutter = hasGutter; this.onMouseOver = function(e) { onMouseOver(cm, e); }; this.waitingFor = 0 } function parseOptions(_cm, options) { if (options instanceof Function) return {getAnnotations: options}; if (!options || options === true) options = {}; return options; } function clearMarks(cm) { var state = cm.state.lint; if (state.hasGutter) cm.clearGutter(GUTTER_ID); for (var i = 0; i < state.marked.length; ++i) state.marked[i].clear(); state.marked.length = 0; } function makeMarker(labels, severity, multiple, tooltips) { var marker = document.createElement("div"), inner = marker; marker.className = "CodeMirror-lint-marker-" + severity; if (multiple) { inner = marker.appendChild(document.createElement("div")); inner.className = "CodeMirror-lint-marker-multiple"; } if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) { showTooltipFor(e, labels, inner); }); return marker; } function getMaxSeverity(a, b) { if (a == "error") return a; else return b; } function groupByLine(annotations) { var lines = []; for (var i = 0; i < annotations.length; ++i) { var ann = annotations[i], line = ann.from.line; (lines[line] || (lines[line] = [])).push(ann); } return lines; } function annotationTooltip(ann) { var severity = ann.severity; if (!severity) severity = "error"; var tip = document.createElement("div"); tip.className = "CodeMirror-lint-message-" + severity; tip.appendChild(document.createTextNode(ann.message)); return tip; } function lintAsync(cm, getAnnotations, passOptions) { var state = cm.state.lint var id = ++state.waitingFor function abort() { id = -1 cm.off("change", abort) } cm.on("change", abort) getAnnotations(cm.getValue(), function(annotations, arg2) { cm.off("change", abort) if (state.waitingFor != id) return if (arg2 && annotations instanceof CodeMirror) annotations = arg2 updateLinting(cm, annotations) }, passOptions, cm); } function startLinting(cm) { var state = cm.state.lint, options = state.options; var passOptions = options.options || options; // Support deprecated passing of `options` property in options var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint"); if (!getAnnotations) return; if (options.async || getAnnotations.async) { lintAsync(cm, getAnnotations, passOptions) } else { updateLinting(cm, getAnnotations(cm.getValue(), passOptions, cm)); } } function updateLinting(cm, annotationsNotSorted) { clearMarks(cm); var state = cm.state.lint, options = state.options; var annotations = groupByLine(annotationsNotSorted); for (var line = 0; line < annotations.length; ++line) { var anns = annotations[line]; if (!anns) continue; var maxSeverity = null; var tipLabel = state.hasGutter && document.createDocumentFragment(); for (var i = 0; i < anns.length; ++i) { var ann = anns[i]; var severity = ann.severity; if (!severity) severity = "error"; maxSeverity = getMaxSeverity(maxSeverity, severity); if (options.formatAnnotation) ann = options.formatAnnotation(ann); if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann)); if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, { className: "CodeMirror-lint-mark-" + severity, __annotation: ann })); } if (state.hasGutter) cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1, state.options.tooltips)); } if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm); } function onChange(cm) { var state = cm.state.lint; if (!state) return; clearTimeout(state.timeout); state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500); } function popupTooltips(annotations, e) { var target = e.target || e.srcElement; var tooltip = document.createDocumentFragment(); for (var i = 0; i < annotations.length; i++) { var ann = annotations[i]; tooltip.appendChild(annotationTooltip(ann)); } showTooltipFor(e, tooltip, target); } function onMouseOver(cm, e) { var target = e.target || e.srcElement; if (!/\bCodeMirror-lint-mark-/.test(target.className)) return; var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2; var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client")); var annotations = []; for (var i = 0; i < spans.length; ++i) { annotations.push(spans[i].__annotation); } if (annotations.length) popupTooltips(annotations, e); } CodeMirror.defineOption("lint", false, function(cm, val, old) { if (old && old != CodeMirror.Init) { clearMarks(cm); if (cm.state.lint.options.lintOnChange !== false) cm.off("change", onChange); CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver); clearTimeout(cm.state.lint.timeout); delete cm.state.lint; } if (val) { var gutters = cm.getOption("gutters"), hasLintGutter = false; for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true; var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter); if (state.options.lintOnChange !== false) cm.on("change", onChange); if (state.options.tooltips != false) CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver); startLinting(cm); } }); CodeMirror.defineExtension("performLint", function() { if (this.state.lint) startLinting(this); }); }); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/codemirror.js ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ // This is CodeMirror (http://codemirror.net), a code editor // implemented in JavaScript on top of the browser's DOM. // // You can find some technical background for some of the code below // at http://marijnhaverbeke.nl/blog/#cm-internals . (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global.CodeMirror = factory()); }(this, (function () { 'use strict'; // Kludges for bugs and behavior differences that can't be feature // detected are enabled based on userAgent etc sniffing. var userAgent = navigator.userAgent; var platform = navigator.platform; var gecko = /gecko\/\d/i.test(userAgent); var ie_upto10 = /MSIE \d/.test(userAgent); var ie_11up = /Trident\/(?:[7-9]|\d{2,})\..*rv:(\d+)/.exec(userAgent); var edge = /Edge\/(\d+)/.exec(userAgent); var ie = ie_upto10 || ie_11up || edge; var ie_version = ie && (ie_upto10 ? document.documentMode || 6 : +(edge || ie_11up)[1]); var webkit = !edge && /WebKit\//.test(userAgent); var qtwebkit = webkit && /Qt\/\d+\.\d+/.test(userAgent); var chrome = !edge && /Chrome\//.test(userAgent); var presto = /Opera\//.test(userAgent); var safari = /Apple Computer/.test(navigator.vendor); var mac_geMountainLion = /Mac OS X 1\d\D([8-9]|\d\d)\D/.test(userAgent); var phantom = /PhantomJS/.test(userAgent); var ios = !edge && /AppleWebKit/.test(userAgent) && /Mobile\/\w+/.test(userAgent); var android = /Android/.test(userAgent); // This is woefully incomplete. Suggestions for alternative methods welcome. var mobile = ios || android || /webOS|BlackBerry|Opera Mini|Opera Mobi|IEMobile/i.test(userAgent); var mac = ios || /Mac/.test(platform); var chromeOS = /\bCrOS\b/.test(userAgent); var windows = /win/i.test(platform); var presto_version = presto && userAgent.match(/Version\/(\d*\.\d*)/); if (presto_version) { presto_version = Number(presto_version[1]); } if (presto_version && presto_version >= 15) { presto = false; webkit = true; } // Some browsers use the wrong event properties to signal cmd/ctrl on OS X var flipCtrlCmd = mac && (qtwebkit || presto && (presto_version == null || presto_version < 12.11)); var captureRightClick = gecko || (ie && ie_version >= 9); function classTest(cls) { return new RegExp("(^|\\s)" + cls + "(?:$|\\s)\\s*") } var rmClass = function(node, cls) { var current = node.className; var match = classTest(cls).exec(current); if (match) { var after = current.slice(match.index + match[0].length); node.className = current.slice(0, match.index) + (after ? match[1] + after : ""); } }; function removeChildren(e) { for (var count = e.childNodes.length; count > 0; --count) { e.removeChild(e.firstChild); } return e } function removeChildrenAndAdd(parent, e) { return removeChildren(parent).appendChild(e) } function elt(tag, content, className, style) { var e = document.createElement(tag); if (className) { e.className = className; } if (style) { e.style.cssText = style; } if (typeof content == "string") { e.appendChild(document.createTextNode(content)); } else if (content) { for (var i = 0; i < content.length; ++i) { e.appendChild(content[i]); } } return e } // wrapper for elt, which removes the elt from the accessibility tree function eltP(tag, content, className, style) { var e = elt(tag, content, className, style); e.setAttribute("role", "presentation"); return e } var range; if (document.createRange) { range = function(node, start, end, endNode) { var r = document.createRange(); r.setEnd(endNode || node, end); r.setStart(node, start); return r }; } else { range = function(node, start, end) { var r = document.body.createTextRange(); try { r.moveToElementText(node.parentNode); } catch(e) { return r } r.collapse(true); r.moveEnd("character", end); r.moveStart("character", start); return r }; } function contains(parent, child) { if (child.nodeType == 3) // Android browser always returns false when child is a textnode { child = child.parentNode; } if (parent.contains) { return parent.contains(child) } do { if (child.nodeType == 11) { child = child.host; } if (child == parent) { return true } } while (child = child.parentNode) } function activeElt() { // IE and Edge may throw an "Unspecified Error" when accessing document.activeElement. // IE < 10 will throw when accessed while the page is loading or in an iframe. // IE > 9 and Edge will throw when accessed in an iframe if document.body is unavailable. var activeElement; try { activeElement = document.activeElement; } catch(e) { activeElement = document.body || null; } while (activeElement && activeElement.shadowRoot && activeElement.shadowRoot.activeElement) { activeElement = activeElement.shadowRoot.activeElement; } return activeElement } function addClass(node, cls) { var current = node.className; if (!classTest(cls).test(current)) { node.className += (current ? " " : "") + cls; } } function joinClasses(a, b) { var as = a.split(" "); for (var i = 0; i < as.length; i++) { if (as[i] && !classTest(as[i]).test(b)) { b += " " + as[i]; } } return b } var selectInput = function(node) { node.select(); }; if (ios) // Mobile Safari apparently has a bug where select() is broken. { selectInput = function(node) { node.selectionStart = 0; node.selectionEnd = node.value.length; }; } else if (ie) // Suppress mysterious IE10 errors { selectInput = function(node) { try { node.select(); } catch(_e) {} }; } function bind(f) { var args = Array.prototype.slice.call(arguments, 1); return function(){return f.apply(null, args)} } function copyObj(obj, target, overwrite) { if (!target) { target = {}; } for (var prop in obj) { if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop))) { target[prop] = obj[prop]; } } return target } // Counts the column offset in a string, taking tabs into account. // Used mostly to find indentation. function countColumn(string, end, tabSize, startIndex, startValue) { if (end == null) { end = string.search(/[^\s\u00a0]/); if (end == -1) { end = string.length; } } for (var i = startIndex || 0, n = startValue || 0;;) { var nextTab = string.indexOf("\t", i); if (nextTab < 0 || nextTab >= end) { return n + (end - i) } n += nextTab - i; n += tabSize - (n % tabSize); i = nextTab + 1; } } var Delayed = function() {this.id = null;}; Delayed.prototype.set = function (ms, f) { clearTimeout(this.id); this.id = setTimeout(f, ms); }; function indexOf(array, elt) { for (var i = 0; i < array.length; ++i) { if (array[i] == elt) { return i } } return -1 } // Number of pixels added to scroller and sizer to hide scrollbar var scrollerGap = 30; // Returned or thrown by various protocols to signal 'I'm not // handling this'. var Pass = {toString: function(){return "CodeMirror.Pass"}}; // Reused option objects for setSelection & friends var sel_dontScroll = {scroll: false}; var sel_mouse = {origin: "*mouse"}; var sel_move = {origin: "+move"}; // The inverse of countColumn -- find the offset that corresponds to // a particular column. function findColumn(string, goal, tabSize) { for (var pos = 0, col = 0;;) { var nextTab = string.indexOf("\t", pos); if (nextTab == -1) { nextTab = string.length; } var skipped = nextTab - pos; if (nextTab == string.length || col + skipped >= goal) { return pos + Math.min(skipped, goal - col) } col += nextTab - pos; col += tabSize - (col % tabSize); pos = nextTab + 1; if (col >= goal) { return pos } } } var spaceStrs = [""]; function spaceStr(n) { while (spaceStrs.length <= n) { spaceStrs.push(lst(spaceStrs) + " "); } return spaceStrs[n] } function lst(arr) { return arr[arr.length-1] } function map(array, f) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = f(array[i], i); } return out } function insertSorted(array, value, score) { var pos = 0, priority = score(value); while (pos < array.length && score(array[pos]) <= priority) { pos++; } array.splice(pos, 0, value); } function nothing() {} function createObj(base, props) { var inst; if (Object.create) { inst = Object.create(base); } else { nothing.prototype = base; inst = new nothing(); } if (props) { copyObj(props, inst); } return inst } var nonASCIISingleCaseWordChar = /[\u00df\u0587\u0590-\u05f4\u0600-\u06ff\u3040-\u309f\u30a0-\u30ff\u3400-\u4db5\u4e00-\u9fcc\uac00-\ud7af]/; function isWordCharBasic(ch) { return /\w/.test(ch) || ch > "\x80" && (ch.toUpperCase() != ch.toLowerCase() || nonASCIISingleCaseWordChar.test(ch)) } function isWordChar(ch, helper) { if (!helper) { return isWordCharBasic(ch) } if (helper.source.indexOf("\\w") > -1 && isWordCharBasic(ch)) { return true } return helper.test(ch) } function isEmpty(obj) { for (var n in obj) { if (obj.hasOwnProperty(n) && obj[n]) { return false } } return true } // Extending unicode characters. A series of a non-extending char + // any number of extending chars is treated as a single unit as far // as editing and measuring is concerned. This is not fully correct, // since some scripts/fonts/browsers also treat other configurations // of code points as a group. var extendingChars = /[\u0300-\u036f\u0483-\u0489\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u064b-\u065e\u0670\u06d6-\u06dc\u06de-\u06e4\u06e7\u06e8\u06ea-\u06ed\u0711\u0730-\u074a\u07a6-\u07b0\u07eb-\u07f3\u0816-\u0819\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0900-\u0902\u093c\u0941-\u0948\u094d\u0951-\u0955\u0962\u0963\u0981\u09bc\u09be\u09c1-\u09c4\u09cd\u09d7\u09e2\u09e3\u0a01\u0a02\u0a3c\u0a41\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a70\u0a71\u0a75\u0a81\u0a82\u0abc\u0ac1-\u0ac5\u0ac7\u0ac8\u0acd\u0ae2\u0ae3\u0b01\u0b3c\u0b3e\u0b3f\u0b41-\u0b44\u0b4d\u0b56\u0b57\u0b62\u0b63\u0b82\u0bbe\u0bc0\u0bcd\u0bd7\u0c3e-\u0c40\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62\u0c63\u0cbc\u0cbf\u0cc2\u0cc6\u0ccc\u0ccd\u0cd5\u0cd6\u0ce2\u0ce3\u0d3e\u0d41-\u0d44\u0d4d\u0d57\u0d62\u0d63\u0dca\u0dcf\u0dd2-\u0dd4\u0dd6\u0ddf\u0e31\u0e34-\u0e3a\u0e47-\u0e4e\u0eb1\u0eb4-\u0eb9\u0ebb\u0ebc\u0ec8-\u0ecd\u0f18\u0f19\u0f35\u0f37\u0f39\u0f71-\u0f7e\u0f80-\u0f84\u0f86\u0f87\u0f90-\u0f97\u0f99-\u0fbc\u0fc6\u102d-\u1030\u1032-\u1037\u1039\u103a\u103d\u103e\u1058\u1059\u105e-\u1060\u1071-\u1074\u1082\u1085\u1086\u108d\u109d\u135f\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17b7-\u17bd\u17c6\u17c9-\u17d3\u17dd\u180b-\u180d\u18a9\u1920-\u1922\u1927\u1928\u1932\u1939-\u193b\u1a17\u1a18\u1a56\u1a58-\u1a5e\u1a60\u1a62\u1a65-\u1a6c\u1a73-\u1a7c\u1a7f\u1b00-\u1b03\u1b34\u1b36-\u1b3a\u1b3c\u1b42\u1b6b-\u1b73\u1b80\u1b81\u1ba2-\u1ba5\u1ba8\u1ba9\u1c2c-\u1c33\u1c36\u1c37\u1cd0-\u1cd2\u1cd4-\u1ce0\u1ce2-\u1ce8\u1ced\u1dc0-\u1de6\u1dfd-\u1dff\u200c\u200d\u20d0-\u20f0\u2cef-\u2cf1\u2de0-\u2dff\u302a-\u302f\u3099\u309a\ua66f-\ua672\ua67c\ua67d\ua6f0\ua6f1\ua802\ua806\ua80b\ua825\ua826\ua8c4\ua8e0-\ua8f1\ua926-\ua92d\ua947-\ua951\ua980-\ua982\ua9b3\ua9b6-\ua9b9\ua9bc\uaa29-\uaa2e\uaa31\uaa32\uaa35\uaa36\uaa43\uaa4c\uaab0\uaab2-\uaab4\uaab7\uaab8\uaabe\uaabf\uaac1\uabe5\uabe8\uabed\udc00-\udfff\ufb1e\ufe00-\ufe0f\ufe20-\ufe26\uff9e\uff9f]/; function isExtendingChar(ch) { return ch.charCodeAt(0) >= 768 && extendingChars.test(ch) } // Returns a number from the range [`0`; `str.length`] unless `pos` is outside that range. function skipExtendingChars(str, pos, dir) { while ((dir < 0 ? pos > 0 : pos < str.length) && isExtendingChar(str.charAt(pos))) { pos += dir; } return pos } // Returns the value from the range [`from`; `to`] that satisfies // `pred` and is closest to `from`. Assumes that at least `to` satisfies `pred`. function findFirst(pred, from, to) { for (;;) { if (Math.abs(from - to) <= 1) { return pred(from) ? from : to } var mid = Math.floor((from + to) / 2); if (pred(mid)) { to = mid; } else { from = mid; } } } // The display handles the DOM integration, both for input reading // and content drawing. It holds references to DOM nodes and // display-related state. function Display(place, doc, input) { var d = this; this.input = input; // Covers bottom-right square when both scrollbars are present. d.scrollbarFiller = elt("div", null, "CodeMirror-scrollbar-filler"); d.scrollbarFiller.setAttribute("cm-not-content", "true"); // Covers bottom of gutter when coverGutterNextToScrollbar is on // and h scrollbar is present. d.gutterFiller = elt("div", null, "CodeMirror-gutter-filler"); d.gutterFiller.setAttribute("cm-not-content", "true"); // Will contain the actual code, positioned to cover the viewport. d.lineDiv = eltP("div", null, "CodeMirror-code"); // Elements are added to these to represent selection and cursors. d.selectionDiv = elt("div", null, null, "position: relative; z-index: 1"); d.cursorDiv = elt("div", null, "CodeMirror-cursors"); // A visibility: hidden element used to find the size of things. d.measure = elt("div", null, "CodeMirror-measure"); // When lines outside of the viewport are measured, they are drawn in this. d.lineMeasure = elt("div", null, "CodeMirror-measure"); // Wraps everything that needs to exist inside the vertically-padded coordinate system d.lineSpace = eltP("div", [d.measure, d.lineMeasure, d.selectionDiv, d.cursorDiv, d.lineDiv], null, "position: relative; outline: none"); var lines = eltP("div", [d.lineSpace], "CodeMirror-lines"); // Moved around its parent to cover visible view. d.mover = elt("div", [lines], null, "position: relative"); // Set to the height of the document, allowing scrolling. d.sizer = elt("div", [d.mover], "CodeMirror-sizer"); d.sizerWidth = null; // Behavior of elts with overflow: auto and padding is // inconsistent across browsers. This is used to ensure the // scrollable area is big enough. d.heightForcer = elt("div", null, null, "position: absolute; height: " + scrollerGap + "px; width: 1px;"); // Will contain the gutters, if any. d.gutters = elt("div", null, "CodeMirror-gutters"); d.lineGutter = null; // Actual scrollable element. d.scroller = elt("div", [d.sizer, d.heightForcer, d.gutters], "CodeMirror-scroll"); d.scroller.setAttribute("tabIndex", "-1"); // The element in which the editor lives. d.wrapper = elt("div", [d.scrollbarFiller, d.gutterFiller, d.scroller], "CodeMirror"); // Work around IE7 z-index bug (not perfect, hence IE7 not really being supported) if (ie && ie_version < 8) { d.gutters.style.zIndex = -1; d.scroller.style.paddingRight = 0; } if (!webkit && !(gecko && mobile)) { d.scroller.draggable = true; } if (place) { if (place.appendChild) { place.appendChild(d.wrapper); } else { place(d.wrapper); } } // Current rendered range (may be bigger than the view window). d.viewFrom = d.viewTo = doc.first; d.reportedViewFrom = d.reportedViewTo = doc.first; // Information about the rendered lines. d.view = []; d.renderedView = null; // Holds info about a single rendered line when it was rendered // for measurement, while not in view. d.externalMeasured = null; // Empty space (in pixels) above the view d.viewOffset = 0; d.lastWrapHeight = d.lastWrapWidth = 0; d.updateLineNumbers = null; d.nativeBarWidth = d.barHeight = d.barWidth = 0; d.scrollbarsClipped = false; // Used to only resize the line number gutter when necessary (when // the amount of lines crosses a boundary that makes its width change) d.lineNumWidth = d.lineNumInnerWidth = d.lineNumChars = null; // Set to true when a non-horizontal-scrolling line widget is // added. As an optimization, line widget aligning is skipped when // this is false. d.alignWidgets = false; d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; // Tracks the maximum line length so that the horizontal scrollbar // can be kept static when scrolling. d.maxLine = null; d.maxLineLength = 0; d.maxLineChanged = false; // Used for measuring wheel scrolling granularity d.wheelDX = d.wheelDY = d.wheelStartX = d.wheelStartY = null; // True when shift is held down. d.shift = false; // Used to track whether anything happened since the context menu // was opened. d.selForContextMenu = null; d.activeTouch = null; input.init(d); } // Find the line object corresponding to the given line number. function getLine(doc, n) { n -= doc.first; if (n < 0 || n >= doc.size) { throw new Error("There is no line " + (n + doc.first) + " in the document.") } var chunk = doc; while (!chunk.lines) { for (var i = 0;; ++i) { var child = chunk.children[i], sz = child.chunkSize(); if (n < sz) { chunk = child; break } n -= sz; } } return chunk.lines[n] } // Get the part of a document between two positions, as an array of // strings. function getBetween(doc, start, end) { var out = [], n = start.line; doc.iter(start.line, end.line + 1, function (line) { var text = line.text; if (n == end.line) { text = text.slice(0, end.ch); } if (n == start.line) { text = text.slice(start.ch); } out.push(text); ++n; }); return out } // Get the lines between from and to, as array of strings. function getLines(doc, from, to) { var out = []; doc.iter(from, to, function (line) { out.push(line.text); }); // iter aborts when callback returns truthy value return out } // Update the height of a line, propagating the height change // upwards to parent nodes. function updateLineHeight(line, height) { var diff = height - line.height; if (diff) { for (var n = line; n; n = n.parent) { n.height += diff; } } } // Given a line object, find its line number by walking up through // its parent links. function lineNo(line) { if (line.parent == null) { return null } var cur = line.parent, no = indexOf(cur.lines, line); for (var chunk = cur.parent; chunk; cur = chunk, chunk = chunk.parent) { for (var i = 0;; ++i) { if (chunk.children[i] == cur) { break } no += chunk.children[i].chunkSize(); } } return no + cur.first } // Find the line at the given vertical position, using the height // information in the document tree. function lineAtHeight(chunk, h) { var n = chunk.first; outer: do { for (var i$1 = 0; i$1 < chunk.children.length; ++i$1) { var child = chunk.children[i$1], ch = child.height; if (h < ch) { chunk = child; continue outer } h -= ch; n += child.chunkSize(); } return n } while (!chunk.lines) var i = 0; for (; i < chunk.lines.length; ++i) { var line = chunk.lines[i], lh = line.height; if (h < lh) { break } h -= lh; } return n + i } function isLine(doc, l) {return l >= doc.first && l < doc.first + doc.size} function lineNumberFor(options, i) { return String(options.lineNumberFormatter(i + options.firstLineNumber)) } // A Pos instance represents a position within the text. function Pos(line, ch, sticky) { if ( sticky === void 0 ) sticky = null; if (!(this instanceof Pos)) { return new Pos(line, ch, sticky) } this.line = line; this.ch = ch; this.sticky = sticky; } // Compare two positions, return 0 if they are the same, a negative // number when a is less, and a positive number otherwise. function cmp(a, b) { return a.line - b.line || a.ch - b.ch } function equalCursorPos(a, b) { return a.sticky == b.sticky && cmp(a, b) == 0 } function copyPos(x) {return Pos(x.line, x.ch)} function maxPos(a, b) { return cmp(a, b) < 0 ? b : a } function minPos(a, b) { return cmp(a, b) < 0 ? a : b } // Most of the external API clips given positions to make sure they // actually exist within the document. function clipLine(doc, n) {return Math.max(doc.first, Math.min(n, doc.first + doc.size - 1))} function clipPos(doc, pos) { if (pos.line < doc.first) { return Pos(doc.first, 0) } var last = doc.first + doc.size - 1; if (pos.line > last) { return Pos(last, getLine(doc, last).text.length) } return clipToLen(pos, getLine(doc, pos.line).text.length) } function clipToLen(pos, linelen) { var ch = pos.ch; if (ch == null || ch > linelen) { return Pos(pos.line, linelen) } else if (ch < 0) { return Pos(pos.line, 0) } else { return pos } } function clipPosArray(doc, array) { var out = []; for (var i = 0; i < array.length; i++) { out[i] = clipPos(doc, array[i]); } return out } // Optimize some code when these features are not used. var sawReadOnlySpans = false; var sawCollapsedSpans = false; function seeReadOnlySpans() { sawReadOnlySpans = true; } function seeCollapsedSpans() { sawCollapsedSpans = true; } // TEXTMARKER SPANS function MarkedSpan(marker, from, to) { this.marker = marker; this.from = from; this.to = to; } // Search an array of spans for a span matching the given marker. function getMarkedSpanFor(spans, marker) { if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.marker == marker) { return span } } } } // Remove a span from an array, returning undefined if no spans are // left (we don't store arrays for lines without spans). function removeMarkedSpan(spans, span) { var r; for (var i = 0; i < spans.length; ++i) { if (spans[i] != span) { (r || (r = [])).push(spans[i]); } } return r } // Add a span to a line. function addMarkedSpan(line, span) { line.markedSpans = line.markedSpans ? line.markedSpans.concat([span]) : [span]; span.marker.attachLine(line); } // Used for the algorithm that adjusts markers for a change in the // document. These functions cut an array of spans at a given // character position, returning an array of remaining chunks (or // undefined if nothing remains). function markedSpansBefore(old, startCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= startCh : span.from < startCh); if (startsBefore || span.from == startCh && marker.type == "bookmark" && (!isInsert || !span.marker.insertLeft)) { var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= startCh : span.to > startCh);(nw || (nw = [])).push(new MarkedSpan(marker, span.from, endsAfter ? null : span.to)); } } } return nw } function markedSpansAfter(old, endCh, isInsert) { var nw; if (old) { for (var i = 0; i < old.length; ++i) { var span = old[i], marker = span.marker; var endsAfter = span.to == null || (marker.inclusiveRight ? span.to >= endCh : span.to > endCh); if (endsAfter || span.from == endCh && marker.type == "bookmark" && (!isInsert || span.marker.insertLeft)) { var startsBefore = span.from == null || (marker.inclusiveLeft ? span.from <= endCh : span.from < endCh);(nw || (nw = [])).push(new MarkedSpan(marker, startsBefore ? null : span.from - endCh, span.to == null ? null : span.to - endCh)); } } } return nw } // Given a change object, compute the new set of marker spans that // cover the line in which the change took place. Removes spans // entirely within the change, reconnects spans belonging to the // same marker that appear on both sides of the change, and cuts off // spans partially within the change. Returns an array of span // arrays with one element for each line in (after) the change. function stretchSpansOverChange(doc, change) { if (change.full) { return null } var oldFirst = isLine(doc, change.from.line) && getLine(doc, change.from.line).markedSpans; var oldLast = isLine(doc, change.to.line) && getLine(doc, change.to.line).markedSpans; if (!oldFirst && !oldLast) { return null } var startCh = change.from.ch, endCh = change.to.ch, isInsert = cmp(change.from, change.to) == 0; // Get the spans that 'stick out' on both sides var first = markedSpansBefore(oldFirst, startCh, isInsert); var last = markedSpansAfter(oldLast, endCh, isInsert); // Next, merge those two ends var sameLine = change.text.length == 1, offset = lst(change.text).length + (sameLine ? startCh : 0); if (first) { // Fix up .to properties of first for (var i = 0; i < first.length; ++i) { var span = first[i]; if (span.to == null) { var found = getMarkedSpanFor(last, span.marker); if (!found) { span.to = startCh; } else if (sameLine) { span.to = found.to == null ? null : found.to + offset; } } } } if (last) { // Fix up .from in last (or move them into first in case of sameLine) for (var i$1 = 0; i$1 < last.length; ++i$1) { var span$1 = last[i$1]; if (span$1.to != null) { span$1.to += offset; } if (span$1.from == null) { var found$1 = getMarkedSpanFor(first, span$1.marker); if (!found$1) { span$1.from = offset; if (sameLine) { (first || (first = [])).push(span$1); } } } else { span$1.from += offset; if (sameLine) { (first || (first = [])).push(span$1); } } } } // Make sure we didn't create any zero-length spans if (first) { first = clearEmptySpans(first); } if (last && last != first) { last = clearEmptySpans(last); } var newMarkers = [first]; if (!sameLine) { // Fill gap with whole-line-spans var gap = change.text.length - 2, gapMarkers; if (gap > 0 && first) { for (var i$2 = 0; i$2 < first.length; ++i$2) { if (first[i$2].to == null) { (gapMarkers || (gapMarkers = [])).push(new MarkedSpan(first[i$2].marker, null, null)); } } } for (var i$3 = 0; i$3 < gap; ++i$3) { newMarkers.push(gapMarkers); } newMarkers.push(last); } return newMarkers } // Remove spans that are empty and don't have a clearWhenEmpty // option of false. function clearEmptySpans(spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if (span.from != null && span.from == span.to && span.marker.clearWhenEmpty !== false) { spans.splice(i--, 1); } } if (!spans.length) { return null } return spans } // Used to 'clip' out readOnly ranges when making a change. function removeReadOnlyRanges(doc, from, to) { var markers = null; doc.iter(from.line, to.line + 1, function (line) { if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var mark = line.markedSpans[i].marker; if (mark.readOnly && (!markers || indexOf(markers, mark) == -1)) { (markers || (markers = [])).push(mark); } } } }); if (!markers) { return null } var parts = [{from: from, to: to}]; for (var i = 0; i < markers.length; ++i) { var mk = markers[i], m = mk.find(0); for (var j = 0; j < parts.length; ++j) { var p = parts[j]; if (cmp(p.to, m.from) < 0 || cmp(p.from, m.to) > 0) { continue } var newParts = [j, 1], dfrom = cmp(p.from, m.from), dto = cmp(p.to, m.to); if (dfrom < 0 || !mk.inclusiveLeft && !dfrom) { newParts.push({from: p.from, to: m.from}); } if (dto > 0 || !mk.inclusiveRight && !dto) { newParts.push({from: m.to, to: p.to}); } parts.splice.apply(parts, newParts); j += newParts.length - 3; } } return parts } // Connect or disconnect spans from a line. function detachMarkedSpans(line) { var spans = line.markedSpans; if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.detachLine(line); } line.markedSpans = null; } function attachMarkedSpans(line, spans) { if (!spans) { return } for (var i = 0; i < spans.length; ++i) { spans[i].marker.attachLine(line); } line.markedSpans = spans; } // Helpers used when computing which overlapping collapsed span // counts as the larger one. function extraLeft(marker) { return marker.inclusiveLeft ? -1 : 0 } function extraRight(marker) { return marker.inclusiveRight ? 1 : 0 } // Returns a number indicating which of two overlapping collapsed // spans is larger (and thus includes the other). Falls back to // comparing ids when the spans cover exactly the same range. function compareCollapsedMarkers(a, b) { var lenDiff = a.lines.length - b.lines.length; if (lenDiff != 0) { return lenDiff } var aPos = a.find(), bPos = b.find(); var fromCmp = cmp(aPos.from, bPos.from) || extraLeft(a) - extraLeft(b); if (fromCmp) { return -fromCmp } var toCmp = cmp(aPos.to, bPos.to) || extraRight(a) - extraRight(b); if (toCmp) { return toCmp } return b.id - a.id } // Find out whether a line ends or starts in a collapsed span. If // so, return the marker for that span. function collapsedSpanAtSide(line, start) { var sps = sawCollapsedSpans && line.markedSpans, found; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (sp.marker.collapsed && (start ? sp.from : sp.to) == null && (!found || compareCollapsedMarkers(found, sp.marker) < 0)) { found = sp.marker; } } } return found } function collapsedSpanAtStart(line) { return collapsedSpanAtSide(line, true) } function collapsedSpanAtEnd(line) { return collapsedSpanAtSide(line, false) } // Test whether there exists a collapsed span that partially // overlaps (covers the start or end, but not both) of a new span. // Such overlap is not allowed. function conflictingCollapsedRange(doc, lineNo$$1, from, to, marker) { var line = getLine(doc, lineNo$$1); var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { var sp = sps[i]; if (!sp.marker.collapsed) { continue } var found = sp.marker.find(0); var fromCmp = cmp(found.from, from) || extraLeft(sp.marker) - extraLeft(marker); var toCmp = cmp(found.to, to) || extraRight(sp.marker) - extraRight(marker); if (fromCmp >= 0 && toCmp <= 0 || fromCmp <= 0 && toCmp >= 0) { continue } if (fromCmp <= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.to, from) >= 0 : cmp(found.to, from) > 0) || fromCmp >= 0 && (sp.marker.inclusiveRight && marker.inclusiveLeft ? cmp(found.from, to) <= 0 : cmp(found.from, to) < 0)) { return true } } } } // A visual line is a line as drawn on the screen. Folding, for // example, can cause multiple logical lines to appear on the same // visual line. This finds the start of the visual line that the // given line is part of (usually that is the line itself). function visualLine(line) { var merged; while (merged = collapsedSpanAtStart(line)) { line = merged.find(-1, true).line; } return line } function visualLineEnd(line) { var merged; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return line } // Returns an array of logical lines that continue the visual line // started by the argument, or undefined if there are no such lines. function visualLineContinued(line) { var merged, lines; while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line ;(lines || (lines = [])).push(line); } return lines } // Get the line number of the start of the visual line that the // given line number is part of. function visualLineNo(doc, lineN) { var line = getLine(doc, lineN), vis = visualLine(line); if (line == vis) { return lineN } return lineNo(vis) } // Get the line number of the start of the next visual line after // the given line. function visualLineEndNo(doc, lineN) { if (lineN > doc.lastLine()) { return lineN } var line = getLine(doc, lineN), merged; if (!lineIsHidden(doc, line)) { return lineN } while (merged = collapsedSpanAtEnd(line)) { line = merged.find(1, true).line; } return lineNo(line) + 1 } // Compute whether a line is hidden. Lines count as hidden when they // are part of a visual line that starts with another line, or when // they are entirely covered by collapsed, non-widget span. function lineIsHidden(doc, line) { var sps = sawCollapsedSpans && line.markedSpans; if (sps) { for (var sp = (void 0), i = 0; i < sps.length; ++i) { sp = sps[i]; if (!sp.marker.collapsed) { continue } if (sp.from == null) { return true } if (sp.marker.widgetNode) { continue } if (sp.from == 0 && sp.marker.inclusiveLeft && lineIsHiddenInner(doc, line, sp)) { return true } } } } function lineIsHiddenInner(doc, line, span) { if (span.to == null) { var end = span.marker.find(1, true); return lineIsHiddenInner(doc, end.line, getMarkedSpanFor(end.line.markedSpans, span.marker)) } if (span.marker.inclusiveRight && span.to == line.text.length) { return true } for (var sp = (void 0), i = 0; i < line.markedSpans.length; ++i) { sp = line.markedSpans[i]; if (sp.marker.collapsed && !sp.marker.widgetNode && sp.from == span.to && (sp.to == null || sp.to != span.from) && (sp.marker.inclusiveLeft || span.marker.inclusiveRight) && lineIsHiddenInner(doc, line, sp)) { return true } } } // Find the height above the given line. function heightAtLine(lineObj) { lineObj = visualLine(lineObj); var h = 0, chunk = lineObj.parent; for (var i = 0; i < chunk.lines.length; ++i) { var line = chunk.lines[i]; if (line == lineObj) { break } else { h += line.height; } } for (var p = chunk.parent; p; chunk = p, p = chunk.parent) { for (var i$1 = 0; i$1 < p.children.length; ++i$1) { var cur = p.children[i$1]; if (cur == chunk) { break } else { h += cur.height; } } } return h } // Compute the character length of a line, taking into account // collapsed ranges (see markText) that might hide parts, and join // other lines onto it. function lineLength(line) { if (line.height == 0) { return 0 } var len = line.text.length, merged, cur = line; while (merged = collapsedSpanAtStart(cur)) { var found = merged.find(0, true); cur = found.from.line; len += found.from.ch - found.to.ch; } cur = line; while (merged = collapsedSpanAtEnd(cur)) { var found$1 = merged.find(0, true); len -= cur.text.length - found$1.from.ch; cur = found$1.to.line; len += cur.text.length - found$1.to.ch; } return len } // Find the longest line in the document. function findMaxLine(cm) { var d = cm.display, doc = cm.doc; d.maxLine = getLine(doc, doc.first); d.maxLineLength = lineLength(d.maxLine); d.maxLineChanged = true; doc.iter(function (line) { var len = lineLength(line); if (len > d.maxLineLength) { d.maxLineLength = len; d.maxLine = line; } }); } // BIDI HELPERS function iterateBidiSections(order, from, to, f) { if (!order) { return f(from, to, "ltr") } var found = false; for (var i = 0; i < order.length; ++i) { var part = order[i]; if (part.from < to && part.to > from || from == to && part.to == from) { f(Math.max(part.from, from), Math.min(part.to, to), part.level == 1 ? "rtl" : "ltr"); found = true; } } if (!found) { f(from, to, "ltr"); } } var bidiOther = null; function getBidiPartAt(order, ch, sticky) { var found; bidiOther = null; for (var i = 0; i < order.length; ++i) { var cur = order[i]; if (cur.from < ch && cur.to > ch) { return i } if (cur.to == ch) { if (cur.from != cur.to && sticky == "before") { found = i; } else { bidiOther = i; } } if (cur.from == ch) { if (cur.from != cur.to && sticky != "before") { found = i; } else { bidiOther = i; } } } return found != null ? found : bidiOther } // Bidirectional ordering algorithm // See http://unicode.org/reports/tr9/tr9-13.html for the algorithm // that this (partially) implements. // One-char codes used for character types: // L (L): Left-to-Right // R (R): Right-to-Left // r (AL): Right-to-Left Arabic // 1 (EN): European Number // + (ES): European Number Separator // % (ET): European Number Terminator // n (AN): Arabic Number // , (CS): Common Number Separator // m (NSM): Non-Spacing Mark // b (BN): Boundary Neutral // s (B): Paragraph Separator // t (S): Segment Separator // w (WS): Whitespace // N (ON): Other Neutrals // Returns null if characters are ordered as they appear // (left-to-right), or an array of sections ({from, to, level} // objects) in the order in which they occur visually. var bidiOrdering = (function() { // Character types for codepoints 0 to 0xff var lowTypes = "bbbbbbbbbtstwsbbbbbbbbbbbbbbssstwNN%%%NNNNNN,N,N1111111111NNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNNNLLLLLLLLLLLLLLLLLLLLLLLLLLNNNNbbbbbbsbbbbbbbbbbbbbbbbbbbbbbbbbb,N%%%%NNNNLNNNNN%%11NLNNN1LNNNNNLLLLLLLLLLLLLLLLLLLLLLLNLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLN"; // Character types for codepoints 0x600 to 0x6f9 var arabicTypes = "nnnnnnNNr%%r,rNNmmmmmmmmmmmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmmmmmmmmmmmmmmmnnnnnnnnnn%nnrrrmrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrmmmmmmmnNmmmmmmrrmmNmmmmrr1111111111"; function charType(code) { if (code <= 0xf7) { return lowTypes.charAt(code) } else if (0x590 <= code && code <= 0x5f4) { return "R" } else if (0x600 <= code && code <= 0x6f9) { return arabicTypes.charAt(code - 0x600) } else if (0x6ee <= code && code <= 0x8ac) { return "r" } else if (0x2000 <= code && code <= 0x200b) { return "w" } else if (code == 0x200c) { return "b" } else { return "L" } } var bidiRE = /[\u0590-\u05f4\u0600-\u06ff\u0700-\u08ac]/; var isNeutral = /[stwN]/, isStrong = /[LRr]/, countsAsLeft = /[Lb1n]/, countsAsNum = /[1n]/; function BidiSpan(level, from, to) { this.level = level; this.from = from; this.to = to; } return function(str, direction) { var outerType = direction == "ltr" ? "L" : "R"; if (str.length == 0 || direction == "ltr" && !bidiRE.test(str)) { return false } var len = str.length, types = []; for (var i = 0; i < len; ++i) { types.push(charType(str.charCodeAt(i))); } // W1. Examine each non-spacing mark (NSM) in the level run, and // change the type of the NSM to the type of the previous // character. If the NSM is at the start of the level run, it will // get the type of sor. for (var i$1 = 0, prev = outerType; i$1 < len; ++i$1) { var type = types[i$1]; if (type == "m") { types[i$1] = prev; } else { prev = type; } } // W2. Search backwards from each instance of a European number // until the first strong type (R, L, AL, or sor) is found. If an // AL is found, change the type of the European number to Arabic // number. // W3. Change all ALs to R. for (var i$2 = 0, cur = outerType; i$2 < len; ++i$2) { var type$1 = types[i$2]; if (type$1 == "1" && cur == "r") { types[i$2] = "n"; } else if (isStrong.test(type$1)) { cur = type$1; if (type$1 == "r") { types[i$2] = "R"; } } } // W4. A single European separator between two European numbers // changes to a European number. A single common separator between // two numbers of the same type changes to that type. for (var i$3 = 1, prev$1 = types[0]; i$3 < len - 1; ++i$3) { var type$2 = types[i$3]; if (type$2 == "+" && prev$1 == "1" && types[i$3+1] == "1") { types[i$3] = "1"; } else if (type$2 == "," && prev$1 == types[i$3+1] && (prev$1 == "1" || prev$1 == "n")) { types[i$3] = prev$1; } prev$1 = type$2; } // W5. A sequence of European terminators adjacent to European // numbers changes to all European numbers. // W6. Otherwise, separators and terminators change to Other // Neutral. for (var i$4 = 0; i$4 < len; ++i$4) { var type$3 = types[i$4]; if (type$3 == ",") { types[i$4] = "N"; } else if (type$3 == "%") { var end = (void 0); for (end = i$4 + 1; end < len && types[end] == "%"; ++end) {} var replace = (i$4 && types[i$4-1] == "!") || (end < len && types[end] == "1") ? "1" : "N"; for (var j = i$4; j < end; ++j) { types[j] = replace; } i$4 = end - 1; } } // W7. Search backwards from each instance of a European number // until the first strong type (R, L, or sor) is found. If an L is // found, then change the type of the European number to L. for (var i$5 = 0, cur$1 = outerType; i$5 < len; ++i$5) { var type$4 = types[i$5]; if (cur$1 == "L" && type$4 == "1") { types[i$5] = "L"; } else if (isStrong.test(type$4)) { cur$1 = type$4; } } // N1. A sequence of neutrals takes the direction of the // surrounding strong text if the text on both sides has the same // direction. European and Arabic numbers act as if they were R in // terms of their influence on neutrals. Start-of-level-run (sor) // and end-of-level-run (eor) are used at level run boundaries. // N2. Any remaining neutrals take the embedding direction. for (var i$6 = 0; i$6 < len; ++i$6) { if (isNeutral.test(types[i$6])) { var end$1 = (void 0); for (end$1 = i$6 + 1; end$1 < len && isNeutral.test(types[end$1]); ++end$1) {} var before = (i$6 ? types[i$6-1] : outerType) == "L"; var after = (end$1 < len ? types[end$1] : outerType) == "L"; var replace$1 = before == after ? (before ? "L" : "R") : outerType; for (var j$1 = i$6; j$1 < end$1; ++j$1) { types[j$1] = replace$1; } i$6 = end$1 - 1; } } // Here we depart from the documented algorithm, in order to avoid // building up an actual levels array. Since there are only three // levels (0, 1, 2) in an implementation that doesn't take // explicit embedding into account, we can build up the order on // the fly, without following the level-based algorithm. var order = [], m; for (var i$7 = 0; i$7 < len;) { if (countsAsLeft.test(types[i$7])) { var start = i$7; for (++i$7; i$7 < len && countsAsLeft.test(types[i$7]); ++i$7) {} order.push(new BidiSpan(0, start, i$7)); } else { var pos = i$7, at = order.length; for (++i$7; i$7 < len && types[i$7] != "L"; ++i$7) {} for (var j$2 = pos; j$2 < i$7;) { if (countsAsNum.test(types[j$2])) { if (pos < j$2) { order.splice(at, 0, new BidiSpan(1, pos, j$2)); } var nstart = j$2; for (++j$2; j$2 < i$7 && countsAsNum.test(types[j$2]); ++j$2) {} order.splice(at, 0, new BidiSpan(2, nstart, j$2)); pos = j$2; } else { ++j$2; } } if (pos < i$7) { order.splice(at, 0, new BidiSpan(1, pos, i$7)); } } } if (order[0].level == 1 && (m = str.match(/^\s+/))) { order[0].from = m[0].length; order.unshift(new BidiSpan(0, 0, m[0].length)); } if (lst(order).level == 1 && (m = str.match(/\s+$/))) { lst(order).to -= m[0].length; order.push(new BidiSpan(0, len - m[0].length, len)); } return direction == "rtl" ? order.reverse() : order } })(); // Get the bidi ordering for the given line (and cache it). Returns // false for lines that are fully left-to-right, and an array of // BidiSpan objects otherwise. function getOrder(line, direction) { var order = line.order; if (order == null) { order = line.order = bidiOrdering(line.text, direction); } return order } function moveCharLogically(line, ch, dir) { var target = skipExtendingChars(line.text, ch + dir, dir); return target < 0 || target > line.text.length ? null : target } function moveLogically(line, start, dir) { var ch = moveCharLogically(line, start.ch, dir); return ch == null ? null : new Pos(start.line, ch, dir < 0 ? "after" : "before") } function endOfLine(visually, cm, lineObj, lineNo, dir) { if (visually) { var order = getOrder(lineObj, cm.doc.direction); if (order) { var part = dir < 0 ? lst(order) : order[0]; var moveInStorageOrder = (dir < 0) == (part.level == 1); var sticky = moveInStorageOrder ? "after" : "before"; var ch; // With a wrapped rtl chunk (possibly spanning multiple bidi parts), // it could be that the last bidi part is not on the last visual line, // since visual lines contain content order-consecutive chunks. // Thus, in rtl, we are looking for the first (content-order) character // in the rtl chunk that is on the last line (that is, the same line // as the last (content-order) character). if (part.level > 0) { var prep = prepareMeasureForLine(cm, lineObj); ch = dir < 0 ? lineObj.text.length - 1 : 0; var targetTop = measureCharPrepared(cm, prep, ch).top; ch = findFirst(function (ch) { return measureCharPrepared(cm, prep, ch).top == targetTop; }, (dir < 0) == (part.level == 1) ? part.from : part.to - 1, ch); if (sticky == "before") { ch = moveCharLogically(lineObj, ch, 1); } } else { ch = dir < 0 ? part.to : part.from; } return new Pos(lineNo, ch, sticky) } } return new Pos(lineNo, dir < 0 ? lineObj.text.length : 0, dir < 0 ? "before" : "after") } function moveVisually(cm, line, start, dir) { var bidi = getOrder(line, cm.doc.direction); if (!bidi) { return moveLogically(line, start, dir) } if (start.ch >= line.text.length) { start.ch = line.text.length; start.sticky = "before"; } else if (start.ch <= 0) { start.ch = 0; start.sticky = "after"; } var partPos = getBidiPartAt(bidi, start.ch, start.sticky), part = bidi[partPos]; if (cm.doc.direction == "ltr" && part.level % 2 == 0 && (dir > 0 ? part.to > start.ch : part.from < start.ch)) { // Case 1: We move within an ltr part in an ltr editor. Even with wrapped lines, // nothing interesting happens. return moveLogically(line, start, dir) } var mv = function (pos, dir) { return moveCharLogically(line, pos instanceof Pos ? pos.ch : pos, dir); }; var prep; var getWrappedLineExtent = function (ch) { if (!cm.options.lineWrapping) { return {begin: 0, end: line.text.length} } prep = prep || prepareMeasureForLine(cm, line); return wrappedLineExtentChar(cm, line, prep, ch) }; var wrappedLineExtent = getWrappedLineExtent(start.sticky == "before" ? mv(start, -1) : start.ch); if (cm.doc.direction == "rtl" || part.level == 1) { var moveInStorageOrder = (part.level == 1) == (dir < 0); var ch = mv(start, moveInStorageOrder ? 1 : -1); if (ch != null && (!moveInStorageOrder ? ch >= part.from && ch >= wrappedLineExtent.begin : ch <= part.to && ch <= wrappedLineExtent.end)) { // Case 2: We move within an rtl part or in an rtl editor on the same visual line var sticky = moveInStorageOrder ? "before" : "after"; return new Pos(start.line, ch, sticky) } } // Case 3: Could not move within this bidi part in this visual line, so leave // the current bidi part var searchInVisualLine = function (partPos, dir, wrappedLineExtent) { var getRes = function (ch, moveInStorageOrder) { return moveInStorageOrder ? new Pos(start.line, mv(ch, 1), "before") : new Pos(start.line, ch, "after"); }; for (; partPos >= 0 && partPos < bidi.length; partPos += dir) { var part = bidi[partPos]; var moveInStorageOrder = (dir > 0) == (part.level != 1); var ch = moveInStorageOrder ? wrappedLineExtent.begin : mv(wrappedLineExtent.end, -1); if (part.from <= ch && ch < part.to) { return getRes(ch, moveInStorageOrder) } ch = moveInStorageOrder ? part.from : mv(part.to, -1); if (wrappedLineExtent.begin <= ch && ch < wrappedLineExtent.end) { return getRes(ch, moveInStorageOrder) } } }; // Case 3a: Look for other bidi parts on the same visual line var res = searchInVisualLine(partPos + dir, dir, wrappedLineExtent); if (res) { return res } // Case 3b: Look for other bidi parts on the next visual line var nextCh = dir > 0 ? wrappedLineExtent.end : mv(wrappedLineExtent.begin, -1); if (nextCh != null && !(dir > 0 && nextCh == line.text.length)) { res = searchInVisualLine(dir > 0 ? 0 : bidi.length - 1, dir, getWrappedLineExtent(nextCh)); if (res) { return res } } // Case 4: Nowhere to move return null } // EVENT HANDLING // Lightweight event framework. on/off also work on DOM nodes, // registering native DOM handlers. var noHandlers = []; var on = function(emitter, type, f) { if (emitter.addEventListener) { emitter.addEventListener(type, f, false); } else if (emitter.attachEvent) { emitter.attachEvent("on" + type, f); } else { var map$$1 = emitter._handlers || (emitter._handlers = {}); map$$1[type] = (map$$1[type] || noHandlers).concat(f); } }; function getHandlers(emitter, type) { return emitter._handlers && emitter._handlers[type] || noHandlers } function off(emitter, type, f) { if (emitter.removeEventListener) { emitter.removeEventListener(type, f, false); } else if (emitter.detachEvent) { emitter.detachEvent("on" + type, f); } else { var map$$1 = emitter._handlers, arr = map$$1 && map$$1[type]; if (arr) { var index = indexOf(arr, f); if (index > -1) { map$$1[type] = arr.slice(0, index).concat(arr.slice(index + 1)); } } } } function signal(emitter, type /*, values...*/) { var handlers = getHandlers(emitter, type); if (!handlers.length) { return } var args = Array.prototype.slice.call(arguments, 2); for (var i = 0; i < handlers.length; ++i) { handlers[i].apply(null, args); } } // The DOM events that CodeMirror handles can be overridden by // registering a (non-DOM) handler on the editor for the event name, // and preventDefault-ing the event in that handler. function signalDOMEvent(cm, e, override) { if (typeof e == "string") { e = {type: e, preventDefault: function() { this.defaultPrevented = true; }}; } signal(cm, override || e.type, cm, e); return e_defaultPrevented(e) || e.codemirrorIgnore } function signalCursorActivity(cm) { var arr = cm._handlers && cm._handlers.cursorActivity; if (!arr) { return } var set = cm.curOp.cursorActivityHandlers || (cm.curOp.cursorActivityHandlers = []); for (var i = 0; i < arr.length; ++i) { if (indexOf(set, arr[i]) == -1) { set.push(arr[i]); } } } function hasHandler(emitter, type) { return getHandlers(emitter, type).length > 0 } // Add on and off methods to a constructor's prototype, to make // registering events on such objects more convenient. function eventMixin(ctor) { ctor.prototype.on = function(type, f) {on(this, type, f);}; ctor.prototype.off = function(type, f) {off(this, type, f);}; } // Due to the fact that we still support jurassic IE versions, some // compatibility wrappers are needed. function e_preventDefault(e) { if (e.preventDefault) { e.preventDefault(); } else { e.returnValue = false; } } function e_stopPropagation(e) { if (e.stopPropagation) { e.stopPropagation(); } else { e.cancelBubble = true; } } function e_defaultPrevented(e) { return e.defaultPrevented != null ? e.defaultPrevented : e.returnValue == false } function e_stop(e) {e_preventDefault(e); e_stopPropagation(e);} function e_target(e) {return e.target || e.srcElement} function e_button(e) { var b = e.which; if (b == null) { if (e.button & 1) { b = 1; } else if (e.button & 2) { b = 3; } else if (e.button & 4) { b = 2; } } if (mac && e.ctrlKey && b == 1) { b = 3; } return b } // Detect drag-and-drop var dragAndDrop = function() { // There is *some* kind of drag-and-drop support in IE6-8, but I // couldn't get it to work yet. if (ie && ie_version < 9) { return false } var div = elt('div'); return "draggable" in div || "dragDrop" in div }(); var zwspSupported; function zeroWidthElement(measure) { if (zwspSupported == null) { var test = elt("span", "\u200b"); removeChildrenAndAdd(measure, elt("span", [test, document.createTextNode("x")])); if (measure.firstChild.offsetHeight != 0) { zwspSupported = test.offsetWidth <= 1 && test.offsetHeight > 2 && !(ie && ie_version < 8); } } var node = zwspSupported ? elt("span", "\u200b") : elt("span", "\u00a0", null, "display: inline-block; width: 1px; margin-right: -1px"); node.setAttribute("cm-text", ""); return node } // Feature-detect IE's crummy client rect reporting for bidi text var badBidiRects; function hasBadBidiRects(measure) { if (badBidiRects != null) { return badBidiRects } var txt = removeChildrenAndAdd(measure, document.createTextNode("A\u062eA")); var r0 = range(txt, 0, 1).getBoundingClientRect(); var r1 = range(txt, 1, 2).getBoundingClientRect(); removeChildren(measure); if (!r0 || r0.left == r0.right) { return false } // Safari returns null in some cases (#2780) return badBidiRects = (r1.right - r0.right < 3) } // See if "".split is the broken IE version, if so, provide an // alternative way to split lines. var splitLinesAuto = "\n\nb".split(/\n/).length != 3 ? function (string) { var pos = 0, result = [], l = string.length; while (pos <= l) { var nl = string.indexOf("\n", pos); if (nl == -1) { nl = string.length; } var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl); var rt = line.indexOf("\r"); if (rt != -1) { result.push(line.slice(0, rt)); pos += rt + 1; } else { result.push(line); pos = nl + 1; } } return result } : function (string) { return string.split(/\r\n?|\n/); }; var hasSelection = window.getSelection ? function (te) { try { return te.selectionStart != te.selectionEnd } catch(e) { return false } } : function (te) { var range$$1; try {range$$1 = te.ownerDocument.selection.createRange();} catch(e) {} if (!range$$1 || range$$1.parentElement() != te) { return false } return range$$1.compareEndPoints("StartToEnd", range$$1) != 0 }; var hasCopyEvent = (function () { var e = elt("div"); if ("oncopy" in e) { return true } e.setAttribute("oncopy", "return;"); return typeof e.oncopy == "function" })(); var badZoomedRects = null; function hasBadZoomedRects(measure) { if (badZoomedRects != null) { return badZoomedRects } var node = removeChildrenAndAdd(measure, elt("span", "x")); var normal = node.getBoundingClientRect(); var fromRange = range(node, 0, 1).getBoundingClientRect(); return badZoomedRects = Math.abs(normal.left - fromRange.left) > 1 } // Known modes, by name and by MIME var modes = {}; var mimeModes = {}; // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) function defineMode(name, mode) { if (arguments.length > 2) { mode.dependencies = Array.prototype.slice.call(arguments, 2); } modes[name] = mode; } function defineMIME(mime, spec) { mimeModes[mime] = spec; } // Given a MIME type, a {name, ...options} config object, or a name // string, return a mode config object. function resolveMode(spec) { if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) { spec = mimeModes[spec]; } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) { var found = mimeModes[spec.name]; if (typeof found == "string") { found = {name: found}; } spec = createObj(found, spec); spec.name = found.name; } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+xml$/.test(spec)) { return resolveMode("application/xml") } else if (typeof spec == "string" && /^[\w\-]+\/[\w\-]+\+json$/.test(spec)) { return resolveMode("application/json") } if (typeof spec == "string") { return {name: spec} } else { return spec || {name: "null"} } } // Given a mode spec (anything that resolveMode accepts), find and // initialize an actual mode object. function getMode(options, spec) { spec = resolveMode(spec); var mfactory = modes[spec.name]; if (!mfactory) { return getMode(options, "text/plain") } var modeObj = mfactory(options, spec); if (modeExtensions.hasOwnProperty(spec.name)) { var exts = modeExtensions[spec.name]; for (var prop in exts) { if (!exts.hasOwnProperty(prop)) { continue } if (modeObj.hasOwnProperty(prop)) { modeObj["_" + prop] = modeObj[prop]; } modeObj[prop] = exts[prop]; } } modeObj.name = spec.name; if (spec.helperType) { modeObj.helperType = spec.helperType; } if (spec.modeProps) { for (var prop$1 in spec.modeProps) { modeObj[prop$1] = spec.modeProps[prop$1]; } } return modeObj } // This can be used to attach properties to mode objects from // outside the actual mode definition. var modeExtensions = {}; function extendMode(mode, properties) { var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {}); copyObj(properties, exts); } function copyState(mode, state) { if (state === true) { return state } if (mode.copyState) { return mode.copyState(state) } var nstate = {}; for (var n in state) { var val = state[n]; if (val instanceof Array) { val = val.concat([]); } nstate[n] = val; } return nstate } // Given a mode and a state (for that mode), find the inner mode and // state at the position that the state refers to. function innerMode(mode, state) { var info; while (mode.innerMode) { info = mode.innerMode(state); if (!info || info.mode == mode) { break } state = info.state; mode = info.mode; } return info || {mode: mode, state: state} } function startState(mode, a1, a2) { return mode.startState ? mode.startState(a1, a2) : true } // STRING STREAM // Fed to the mode parsers, provides helper functions to make // parsers more succinct. var StringStream = function(string, tabSize, lineOracle) { this.pos = this.start = 0; this.string = string; this.tabSize = tabSize || 8; this.lastColumnPos = this.lastColumnValue = 0; this.lineStart = 0; this.lineOracle = lineOracle; }; StringStream.prototype.eol = function () {return this.pos >= this.string.length}; StringStream.prototype.sol = function () {return this.pos == this.lineStart}; StringStream.prototype.peek = function () {return this.string.charAt(this.pos) || undefined}; StringStream.prototype.next = function () { if (this.pos < this.string.length) { return this.string.charAt(this.pos++) } }; StringStream.prototype.eat = function (match) { var ch = this.string.charAt(this.pos); var ok; if (typeof match == "string") { ok = ch == match; } else { ok = ch && (match.test ? match.test(ch) : match(ch)); } if (ok) {++this.pos; return ch} }; StringStream.prototype.eatWhile = function (match) { var start = this.pos; while (this.eat(match)){} return this.pos > start }; StringStream.prototype.eatSpace = function () { var this$1 = this; var start = this.pos; while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) { ++this$1.pos; } return this.pos > start }; StringStream.prototype.skipToEnd = function () {this.pos = this.string.length;}; StringStream.prototype.skipTo = function (ch) { var found = this.string.indexOf(ch, this.pos); if (found > -1) {this.pos = found; return true} }; StringStream.prototype.backUp = function (n) {this.pos -= n;}; StringStream.prototype.column = function () { if (this.lastColumnPos < this.start) { this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue); this.lastColumnPos = this.start; } return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.indentation = function () { return countColumn(this.string, null, this.tabSize) - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0) }; StringStream.prototype.match = function (pattern, consume, caseInsensitive) { if (typeof pattern == "string") { var cased = function (str) { return caseInsensitive ? str.toLowerCase() : str; }; var substr = this.string.substr(this.pos, pattern.length); if (cased(substr) == cased(pattern)) { if (consume !== false) { this.pos += pattern.length; } return true } } else { var match = this.string.slice(this.pos).match(pattern); if (match && match.index > 0) { return null } if (match && consume !== false) { this.pos += match[0].length; } return match } }; StringStream.prototype.current = function (){return this.string.slice(this.start, this.pos)}; StringStream.prototype.hideFirstChars = function (n, inner) { this.lineStart += n; try { return inner() } finally { this.lineStart -= n; } }; StringStream.prototype.lookAhead = function (n) { var oracle = this.lineOracle; return oracle && oracle.lookAhead(n) }; var SavedContext = function(state, lookAhead) { this.state = state; this.lookAhead = lookAhead; }; var Context = function(doc, state, line, lookAhead) { this.state = state; this.doc = doc; this.line = line; this.maxLookAhead = lookAhead || 0; }; Context.prototype.lookAhead = function (n) { var line = this.doc.getLine(this.line + n); if (line != null && n > this.maxLookAhead) { this.maxLookAhead = n; } return line }; Context.prototype.nextLine = function () { this.line++; if (this.maxLookAhead > 0) { this.maxLookAhead--; } }; Context.fromSaved = function (doc, saved, line) { if (saved instanceof SavedContext) { return new Context(doc, copyState(doc.mode, saved.state), line, saved.lookAhead) } else { return new Context(doc, copyState(doc.mode, saved), line) } }; Context.prototype.save = function (copy) { var state = copy !== false ? copyState(this.doc.mode, this.state) : this.state; return this.maxLookAhead > 0 ? new SavedContext(state, this.maxLookAhead) : state }; // Compute a style array (an array starting with a mode generation // -- for invalidation -- followed by pairs of end positions and // style strings), which is used to highlight the tokens on the // line. function highlightLine(cm, line, context, forceToEnd) { // A styles array always starts with a number identifying the // mode/overlays that it is based on (for easy invalidation). var st = [cm.state.modeGen], lineClasses = {}; // Compute the base array of styles runMode(cm, line.text, cm.doc.mode, context, function (end, style) { return st.push(end, style); }, lineClasses, forceToEnd); var state = context.state; // Run overlays, adjust style array. var loop = function ( o ) { var overlay = cm.state.overlays[o], i = 1, at = 0; context.state = true; runMode(cm, line.text, overlay.mode, context, function (end, style) { var start = i; // Ensure there's a token end at the current position, and that i points at it while (at < end) { var i_end = st[i]; if (i_end > end) { st.splice(i, 1, end, st[i+1], i_end); } i += 2; at = Math.min(end, i_end); } if (!style) { return } if (overlay.opaque) { st.splice(start, i - start, end, "overlay " + style); i = start + 2; } else { for (; start < i; start += 2) { var cur = st[start+1]; st[start+1] = (cur ? cur + " " : "") + "overlay " + style; } } }, lineClasses); }; for (var o = 0; o < cm.state.overlays.length; ++o) loop( o ); context.state = state; return {styles: st, classes: lineClasses.bgClass || lineClasses.textClass ? lineClasses : null} } function getLineStyles(cm, line, updateFrontier) { if (!line.styles || line.styles[0] != cm.state.modeGen) { var context = getContextBefore(cm, lineNo(line)); var resetState = line.text.length > cm.options.maxHighlightLength && copyState(cm.doc.mode, context.state); var result = highlightLine(cm, line, context); if (resetState) { context.state = resetState; } line.stateAfter = context.save(!resetState); line.styles = result.styles; if (result.classes) { line.styleClasses = result.classes; } else if (line.styleClasses) { line.styleClasses = null; } if (updateFrontier === cm.doc.highlightFrontier) { cm.doc.modeFrontier = Math.max(cm.doc.modeFrontier, ++cm.doc.highlightFrontier); } } return line.styles } function getContextBefore(cm, n, precise) { var doc = cm.doc, display = cm.display; if (!doc.mode.startState) { return new Context(doc, true, n) } var start = findStartLine(cm, n, precise); var saved = start > doc.first && getLine(doc, start - 1).stateAfter; var context = saved ? Context.fromSaved(doc, saved, start) : new Context(doc, startState(doc.mode), start); doc.iter(start, n, function (line) { processLine(cm, line.text, context); var pos = context.line; line.stateAfter = pos == n - 1 || pos % 5 == 0 || pos >= display.viewFrom && pos < display.viewTo ? context.save() : null; context.nextLine(); }); if (precise) { doc.modeFrontier = context.line; } return context } // Lightweight form of highlight -- proceed over this line and // update state, but don't save a style array. Used for lines that // aren't currently visible. function processLine(cm, text, context, startAt) { var mode = cm.doc.mode; var stream = new StringStream(text, cm.options.tabSize, context); stream.start = stream.pos = startAt || 0; if (text == "") { callBlankLine(mode, context.state); } while (!stream.eol()) { readToken(mode, stream, context.state); stream.start = stream.pos; } } function callBlankLine(mode, state) { if (mode.blankLine) { return mode.blankLine(state) } if (!mode.innerMode) { return } var inner = innerMode(mode, state); if (inner.mode.blankLine) { return inner.mode.blankLine(inner.state) } } function readToken(mode, stream, state, inner) { for (var i = 0; i < 10; i++) { if (inner) { inner[0] = innerMode(mode, state).mode; } var style = mode.token(stream, state); if (stream.pos > stream.start) { return style } } throw new Error("Mode " + mode.name + " failed to advance stream.") } var Token = function(stream, type, state) { this.start = stream.start; this.end = stream.pos; this.string = stream.current(); this.type = type || null; this.state = state; }; // Utility for getTokenAt and getLineTokens function takeToken(cm, pos, precise, asArray) { var doc = cm.doc, mode = doc.mode, style; pos = clipPos(doc, pos); var line = getLine(doc, pos.line), context = getContextBefore(cm, pos.line, precise); var stream = new StringStream(line.text, cm.options.tabSize, context), tokens; if (asArray) { tokens = []; } while ((asArray || stream.pos < pos.ch) && !stream.eol()) { stream.start = stream.pos; style = readToken(mode, stream, context.state); if (asArray) { tokens.push(new Token(stream, style, copyState(doc.mode, context.state))); } } return asArray ? tokens : new Token(stream, style, context.state) } function extractLineClasses(type, output) { if (type) { for (;;) { var lineClass = type.match(/(?:^|\s+)line-(background-)?(\S+)/); if (!lineClass) { break } type = type.slice(0, lineClass.index) + type.slice(lineClass.index + lineClass[0].length); var prop = lineClass[1] ? "bgClass" : "textClass"; if (output[prop] == null) { output[prop] = lineClass[2]; } else if (!(new RegExp("(?:^|\s)" + lineClass[2] + "(?:$|\s)")).test(output[prop])) { output[prop] += " " + lineClass[2]; } } } return type } // Run the given mode's parser over a line, calling f for each token. function runMode(cm, text, mode, context, f, lineClasses, forceToEnd) { var flattenSpans = mode.flattenSpans; if (flattenSpans == null) { flattenSpans = cm.options.flattenSpans; } var curStart = 0, curStyle = null; var stream = new StringStream(text, cm.options.tabSize, context), style; var inner = cm.options.addModeClass && [null]; if (text == "") { extractLineClasses(callBlankLine(mode, context.state), lineClasses); } while (!stream.eol()) { if (stream.pos > cm.options.maxHighlightLength) { flattenSpans = false; if (forceToEnd) { processLine(cm, text, context, stream.pos); } stream.pos = text.length; style = null; } else { style = extractLineClasses(readToken(mode, stream, context.state, inner), lineClasses); } if (inner) { var mName = inner[0].name; if (mName) { style = "m-" + (style ? mName + " " + style : mName); } } if (!flattenSpans || curStyle != style) { while (curStart < stream.start) { curStart = Math.min(stream.start, curStart + 5000); f(curStart, curStyle); } curStyle = style; } stream.start = stream.pos; } while (curStart < stream.pos) { // Webkit seems to refuse to render text nodes longer than 57444 // characters, and returns inaccurate measurements in nodes // starting around 5000 chars. var pos = Math.min(stream.pos, curStart + 5000); f(pos, curStyle); curStart = pos; } } // Finds the line to start with when starting a parse. Tries to // find a line with a stateAfter, so that it can start with a // valid state. If that fails, it returns the line with the // smallest indentation, which tends to need the least context to // parse correctly. function findStartLine(cm, n, precise) { var minindent, minline, doc = cm.doc; var lim = precise ? -1 : n - (cm.doc.mode.innerMode ? 1000 : 100); for (var search = n; search > lim; --search) { if (search <= doc.first) { return doc.first } var line = getLine(doc, search - 1), after = line.stateAfter; if (after && (!precise || search + (after instanceof SavedContext ? after.lookAhead : 0) <= doc.modeFrontier)) { return search } var indented = countColumn(line.text, null, cm.options.tabSize); if (minline == null || minindent > indented) { minline = search - 1; minindent = indented; } } return minline } function retreatFrontier(doc, n) { doc.modeFrontier = Math.min(doc.modeFrontier, n); if (doc.highlightFrontier < n - 10) { return } var start = doc.first; for (var line = n - 1; line > start; line--) { var saved = getLine(doc, line).stateAfter; // change is on 3 // state on line 1 looked ahead 2 -- so saw 3 // test 1 + 2 < 3 should cover this if (saved && (!(saved instanceof SavedContext) || line + saved.lookAhead < n)) { start = line + 1; break } } doc.highlightFrontier = Math.min(doc.highlightFrontier, start); } // LINE DATA STRUCTURE // Line objects. These hold state related to a line, including // highlighting info (the styles array). var Line = function(text, markedSpans, estimateHeight) { this.text = text; attachMarkedSpans(this, markedSpans); this.height = estimateHeight ? estimateHeight(this) : 1; }; Line.prototype.lineNo = function () { return lineNo(this) }; eventMixin(Line); // Change the content (text, markers) of a line. Automatically // invalidates cached information and tries to re-estimate the // line's height. function updateLine(line, text, markedSpans, estimateHeight) { line.text = text; if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } if (line.order != null) { line.order = null; } detachMarkedSpans(line); attachMarkedSpans(line, markedSpans); var estHeight = estimateHeight ? estimateHeight(line) : 1; if (estHeight != line.height) { updateLineHeight(line, estHeight); } } // Detach a line from the document tree and its markers. function cleanUpLine(line) { line.parent = null; detachMarkedSpans(line); } // Convert a style as returned by a mode (either null, or a string // containing one or more styles) to a CSS style. This is cached, // and also looks for line-wide styles. var styleToClassCache = {}; var styleToClassCacheWithMode = {}; function interpretTokenStyle(style, options) { if (!style || /^\s*$/.test(style)) { return null } var cache = options.addModeClass ? styleToClassCacheWithMode : styleToClassCache; return cache[style] || (cache[style] = style.replace(/\S+/g, "cm-$&")) } // Render the DOM representation of the text of a line. Also builds // up a 'line map', which points at the DOM nodes that represent // specific stretches of text, and is used by the measuring code. // The returned object contains the DOM node, this map, and // information about line-wide styles that were set by the mode. function buildLineContent(cm, lineView) { // The padding-right forces the element to have a 'border', which // is needed on Webkit to be able to get line-level bounding // rectangles for it (in measureChar). var content = eltP("span", null, null, webkit ? "padding-right: .1px" : null); var builder = {pre: eltP("pre", [content], "CodeMirror-line"), content: content, col: 0, pos: 0, cm: cm, trailingSpace: false, splitSpaces: (ie || webkit) && cm.getOption("lineWrapping")}; lineView.measure = {}; // Iterate over the logical lines that make up this visual line. for (var i = 0; i <= (lineView.rest ? lineView.rest.length : 0); i++) { var line = i ? lineView.rest[i - 1] : lineView.line, order = (void 0); builder.pos = 0; builder.addToken = buildToken; // Optionally wire in some hacks into the token-rendering // algorithm, to deal with browser quirks. if (hasBadBidiRects(cm.display.measure) && (order = getOrder(line, cm.doc.direction))) { builder.addToken = buildTokenBadBidi(builder.addToken, order); } builder.map = []; var allowFrontierUpdate = lineView != cm.display.externalMeasured && lineNo(line); insertLineContent(line, builder, getLineStyles(cm, line, allowFrontierUpdate)); if (line.styleClasses) { if (line.styleClasses.bgClass) { builder.bgClass = joinClasses(line.styleClasses.bgClass, builder.bgClass || ""); } if (line.styleClasses.textClass) { builder.textClass = joinClasses(line.styleClasses.textClass, builder.textClass || ""); } } // Ensure at least a single node is present, for measuring. if (builder.map.length == 0) { builder.map.push(0, 0, builder.content.appendChild(zeroWidthElement(cm.display.measure))); } // Store the map and a cache object for the current logical line if (i == 0) { lineView.measure.map = builder.map; lineView.measure.cache = {}; } else { (lineView.measure.maps || (lineView.measure.maps = [])).push(builder.map) ;(lineView.measure.caches || (lineView.measure.caches = [])).push({}); } } // See issue #2901 if (webkit) { var last = builder.content.lastChild; if (/\bcm-tab\b/.test(last.className) || (last.querySelector && last.querySelector(".cm-tab"))) { builder.content.className = "cm-tab-wrap-hack"; } } signal(cm, "renderLine", cm, lineView.line, builder.pre); if (builder.pre.className) { builder.textClass = joinClasses(builder.pre.className, builder.textClass || ""); } return builder } function defaultSpecialCharPlaceholder(ch) { var token = elt("span", "\u2022", "cm-invalidchar"); token.title = "\\u" + ch.charCodeAt(0).toString(16); token.setAttribute("aria-label", token.title); return token } // Build up the DOM representation for a single token, and add it to // the line map. Takes care to render special characters separately. function buildToken(builder, text, style, startStyle, endStyle, title, css) { if (!text) { return } var displayText = builder.splitSpaces ? splitSpaces(text, builder.trailingSpace) : text; var special = builder.cm.state.specialChars, mustWrap = false; var content; if (!special.test(text)) { builder.col += text.length; content = document.createTextNode(displayText); builder.map.push(builder.pos, builder.pos + text.length, content); if (ie && ie_version < 9) { mustWrap = true; } builder.pos += text.length; } else { content = document.createDocumentFragment(); var pos = 0; while (true) { special.lastIndex = pos; var m = special.exec(text); var skipped = m ? m.index - pos : text.length - pos; if (skipped) { var txt = document.createTextNode(displayText.slice(pos, pos + skipped)); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt])); } else { content.appendChild(txt); } builder.map.push(builder.pos, builder.pos + skipped, txt); builder.col += skipped; builder.pos += skipped; } if (!m) { break } pos += skipped + 1; var txt$1 = (void 0); if (m[0] == "\t") { var tabSize = builder.cm.options.tabSize, tabWidth = tabSize - builder.col % tabSize; txt$1 = content.appendChild(elt("span", spaceStr(tabWidth), "cm-tab")); txt$1.setAttribute("role", "presentation"); txt$1.setAttribute("cm-text", "\t"); builder.col += tabWidth; } else if (m[0] == "\r" || m[0] == "\n") { txt$1 = content.appendChild(elt("span", m[0] == "\r" ? "\u240d" : "\u2424", "cm-invalidchar")); txt$1.setAttribute("cm-text", m[0]); builder.col += 1; } else { txt$1 = builder.cm.options.specialCharPlaceholder(m[0]); txt$1.setAttribute("cm-text", m[0]); if (ie && ie_version < 9) { content.appendChild(elt("span", [txt$1])); } else { content.appendChild(txt$1); } builder.col += 1; } builder.map.push(builder.pos, builder.pos + 1, txt$1); builder.pos++; } } builder.trailingSpace = displayText.charCodeAt(text.length - 1) == 32; if (style || startStyle || endStyle || mustWrap || css) { var fullStyle = style || ""; if (startStyle) { fullStyle += startStyle; } if (endStyle) { fullStyle += endStyle; } var token = elt("span", [content], fullStyle, css); if (title) { token.title = title; } return builder.content.appendChild(token) } builder.content.appendChild(content); } function splitSpaces(text, trailingBefore) { if (text.length > 1 && !/ /.test(text)) { return text } var spaceBefore = trailingBefore, result = ""; for (var i = 0; i < text.length; i++) { var ch = text.charAt(i); if (ch == " " && spaceBefore && (i == text.length - 1 || text.charCodeAt(i + 1) == 32)) { ch = "\u00a0"; } result += ch; spaceBefore = ch == " "; } return result } // Work around nonsense dimensions being reported for stretches of // right-to-left text. function buildTokenBadBidi(inner, order) { return function (builder, text, style, startStyle, endStyle, title, css) { style = style ? style + " cm-force-border" : "cm-force-border"; var start = builder.pos, end = start + text.length; for (;;) { // Find the part that overlaps with the start of this text var part = (void 0); for (var i = 0; i < order.length; i++) { part = order[i]; if (part.to > start && part.from <= start) { break } } if (part.to >= end) { return inner(builder, text, style, startStyle, endStyle, title, css) } inner(builder, text.slice(0, part.to - start), style, startStyle, null, title, css); startStyle = null; text = text.slice(part.to - start); start = part.to; } } } function buildCollapsedSpan(builder, size, marker, ignoreWidget) { var widget = !ignoreWidget && marker.widgetNode; if (widget) { builder.map.push(builder.pos, builder.pos + size, widget); } if (!ignoreWidget && builder.cm.display.input.needsContentAttribute) { if (!widget) { widget = builder.content.appendChild(document.createElement("span")); } widget.setAttribute("cm-marker", marker.id); } if (widget) { builder.cm.display.input.setUneditable(widget); builder.content.appendChild(widget); } builder.pos += size; builder.trailingSpace = false; } // Outputs a number of spans to make up a line, taking highlighting // and marked text into account. function insertLineContent(line, builder, styles) { var spans = line.markedSpans, allText = line.text, at = 0; if (!spans) { for (var i$1 = 1; i$1 < styles.length; i$1+=2) { builder.addToken(builder, allText.slice(at, at = styles[i$1]), interpretTokenStyle(styles[i$1+1], builder.cm.options)); } return } var len = allText.length, pos = 0, i = 1, text = "", style, css; var nextChange = 0, spanStyle, spanEndStyle, spanStartStyle, title, collapsed; for (;;) { if (nextChange == pos) { // Update current marker set spanStyle = spanEndStyle = spanStartStyle = title = css = ""; collapsed = null; nextChange = Infinity; var foundBookmarks = [], endStyles = (void 0); for (var j = 0; j < spans.length; ++j) { var sp = spans[j], m = sp.marker; if (m.type == "bookmark" && sp.from == pos && m.widgetNode) { foundBookmarks.push(m); } else if (sp.from <= pos && (sp.to == null || sp.to > pos || m.collapsed && sp.to == pos && sp.from == pos)) { if (sp.to != null && sp.to != pos && nextChange > sp.to) { nextChange = sp.to; spanEndStyle = ""; } if (m.className) { spanStyle += " " + m.className; } if (m.css) { css = (css ? css + ";" : "") + m.css; } if (m.startStyle && sp.from == pos) { spanStartStyle += " " + m.startStyle; } if (m.endStyle && sp.to == nextChange) { (endStyles || (endStyles = [])).push(m.endStyle, sp.to); } if (m.title && !title) { title = m.title; } if (m.collapsed && (!collapsed || compareCollapsedMarkers(collapsed.marker, m) < 0)) { collapsed = sp; } } else if (sp.from > pos && nextChange > sp.from) { nextChange = sp.from; } } if (endStyles) { for (var j$1 = 0; j$1 < endStyles.length; j$1 += 2) { if (endStyles[j$1 + 1] == nextChange) { spanEndStyle += " " + endStyles[j$1]; } } } if (!collapsed || collapsed.from == pos) { for (var j$2 = 0; j$2 < foundBookmarks.length; ++j$2) { buildCollapsedSpan(builder, 0, foundBookmarks[j$2]); } } if (collapsed && (collapsed.from || 0) == pos) { buildCollapsedSpan(builder, (collapsed.to == null ? len + 1 : collapsed.to) - pos, collapsed.marker, collapsed.from == null); if (collapsed.to == null) { return } if (collapsed.to == pos) { collapsed = false; } } } if (pos >= len) { break } var upto = Math.min(len, nextChange); while (true) { if (text) { var end = pos + text.length; if (!collapsed) { var tokenText = end > upto ? text.slice(0, upto - pos) : text; builder.addToken(builder, tokenText, style ? style + spanStyle : spanStyle, spanStartStyle, pos + tokenText.length == nextChange ? spanEndStyle : "", title, css); } if (end >= upto) {text = text.slice(upto - pos); pos = upto; break} pos = end; spanStartStyle = ""; } text = allText.slice(at, at = styles[i++]); style = interpretTokenStyle(styles[i++], builder.cm.options); } } } // These objects are used to represent the visible (currently drawn) // part of the document. A LineView may correspond to multiple // logical lines, if those are connected by collapsed ranges. function LineView(doc, line, lineN) { // The starting line this.line = line; // Continuing lines, if any this.rest = visualLineContinued(line); // Number of logical lines in this visual line this.size = this.rest ? lineNo(lst(this.rest)) - lineN + 1 : 1; this.node = this.text = null; this.hidden = lineIsHidden(doc, line); } // Create a range of LineView objects for the given lines. function buildViewArray(cm, from, to) { var array = [], nextPos; for (var pos = from; pos < to; pos = nextPos) { var view = new LineView(cm.doc, getLine(cm.doc, pos), pos); nextPos = pos + view.size; array.push(view); } return array } var operationGroup = null; function pushOperation(op) { if (operationGroup) { operationGroup.ops.push(op); } else { op.ownsGroup = operationGroup = { ops: [op], delayedCallbacks: [] }; } } function fireCallbacksForOps(group) { // Calls delayed callbacks and cursorActivity handlers until no // new ones appear var callbacks = group.delayedCallbacks, i = 0; do { for (; i < callbacks.length; i++) { callbacks[i].call(null); } for (var j = 0; j < group.ops.length; j++) { var op = group.ops[j]; if (op.cursorActivityHandlers) { while (op.cursorActivityCalled < op.cursorActivityHandlers.length) { op.cursorActivityHandlers[op.cursorActivityCalled++].call(null, op.cm); } } } } while (i < callbacks.length) } function finishOperation(op, endCb) { var group = op.ownsGroup; if (!group) { return } try { fireCallbacksForOps(group); } finally { operationGroup = null; endCb(group); } } var orphanDelayedCallbacks = null; // Often, we want to signal events at a point where we are in the // middle of some work, but don't want the handler to start calling // other methods on the editor, which might be in an inconsistent // state or simply not expect any other events to happen. // signalLater looks whether there are any handlers, and schedules // them to be executed when the last operation ends, or, if no // operation is active, when a timeout fires. function signalLater(emitter, type /*, values...*/) { var arr = getHandlers(emitter, type); if (!arr.length) { return } var args = Array.prototype.slice.call(arguments, 2), list; if (operationGroup) { list = operationGroup.delayedCallbacks; } else if (orphanDelayedCallbacks) { list = orphanDelayedCallbacks; } else { list = orphanDelayedCallbacks = []; setTimeout(fireOrphanDelayed, 0); } var loop = function ( i ) { list.push(function () { return arr[i].apply(null, args); }); }; for (var i = 0; i < arr.length; ++i) loop( i ); } function fireOrphanDelayed() { var delayed = orphanDelayedCallbacks; orphanDelayedCallbacks = null; for (var i = 0; i < delayed.length; ++i) { delayed[i](); } } // When an aspect of a line changes, a string is added to // lineView.changes. This updates the relevant part of the line's // DOM structure. function updateLineForChanges(cm, lineView, lineN, dims) { for (var j = 0; j < lineView.changes.length; j++) { var type = lineView.changes[j]; if (type == "text") { updateLineText(cm, lineView); } else if (type == "gutter") { updateLineGutter(cm, lineView, lineN, dims); } else if (type == "class") { updateLineClasses(cm, lineView); } else if (type == "widget") { updateLineWidgets(cm, lineView, dims); } } lineView.changes = null; } // Lines with gutter elements, widgets or a background class need to // be wrapped, and have the extra elements added to the wrapper div function ensureLineWrapped(lineView) { if (lineView.node == lineView.text) { lineView.node = elt("div", null, null, "position: relative"); if (lineView.text.parentNode) { lineView.text.parentNode.replaceChild(lineView.node, lineView.text); } lineView.node.appendChild(lineView.text); if (ie && ie_version < 8) { lineView.node.style.zIndex = 2; } } return lineView.node } function updateLineBackground(cm, lineView) { var cls = lineView.bgClass ? lineView.bgClass + " " + (lineView.line.bgClass || "") : lineView.line.bgClass; if (cls) { cls += " CodeMirror-linebackground"; } if (lineView.background) { if (cls) { lineView.background.className = cls; } else { lineView.background.parentNode.removeChild(lineView.background); lineView.background = null; } } else if (cls) { var wrap = ensureLineWrapped(lineView); lineView.background = wrap.insertBefore(elt("div", null, cls), wrap.firstChild); cm.display.input.setUneditable(lineView.background); } } // Wrapper around buildLineContent which will reuse the structure // in display.externalMeasured when possible. function getLineContent(cm, lineView) { var ext = cm.display.externalMeasured; if (ext && ext.line == lineView.line) { cm.display.externalMeasured = null; lineView.measure = ext.measure; return ext.built } return buildLineContent(cm, lineView) } // Redraw the line's text. Interacts with the background and text // classes because the mode may output tokens that influence these // classes. function updateLineText(cm, lineView) { var cls = lineView.text.className; var built = getLineContent(cm, lineView); if (lineView.text == lineView.node) { lineView.node = built.pre; } lineView.text.parentNode.replaceChild(built.pre, lineView.text); lineView.text = built.pre; if (built.bgClass != lineView.bgClass || built.textClass != lineView.textClass) { lineView.bgClass = built.bgClass; lineView.textClass = built.textClass; updateLineClasses(cm, lineView); } else if (cls) { lineView.text.className = cls; } } function updateLineClasses(cm, lineView) { updateLineBackground(cm, lineView); if (lineView.line.wrapClass) { ensureLineWrapped(lineView).className = lineView.line.wrapClass; } else if (lineView.node != lineView.text) { lineView.node.className = ""; } var textClass = lineView.textClass ? lineView.textClass + " " + (lineView.line.textClass || "") : lineView.line.textClass; lineView.text.className = textClass || ""; } function updateLineGutter(cm, lineView, lineN, dims) { if (lineView.gutter) { lineView.node.removeChild(lineView.gutter); lineView.gutter = null; } if (lineView.gutterBackground) { lineView.node.removeChild(lineView.gutterBackground); lineView.gutterBackground = null; } if (lineView.line.gutterClass) { var wrap = ensureLineWrapped(lineView); lineView.gutterBackground = elt("div", null, "CodeMirror-gutter-background " + lineView.line.gutterClass, ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px; width: " + (dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(lineView.gutterBackground); wrap.insertBefore(lineView.gutterBackground, lineView.text); } var markers = lineView.line.gutterMarkers; if (cm.options.lineNumbers || markers) { var wrap$1 = ensureLineWrapped(lineView); var gutterWrap = lineView.gutter = elt("div", null, "CodeMirror-gutter-wrapper", ("left: " + (cm.options.fixedGutter ? dims.fixedPos : -dims.gutterTotalWidth) + "px")); cm.display.input.setUneditable(gutterWrap); wrap$1.insertBefore(gutterWrap, lineView.text); if (lineView.line.gutterClass) { gutterWrap.className += " " + lineView.line.gutterClass; } if (cm.options.lineNumbers && (!markers || !markers["CodeMirror-linenumbers"])) { lineView.lineNumber = gutterWrap.appendChild( elt("div", lineNumberFor(cm.options, lineN), "CodeMirror-linenumber CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft["CodeMirror-linenumbers"]) + "px; width: " + (cm.display.lineNumInnerWidth) + "px"))); } if (markers) { for (var k = 0; k < cm.options.gutters.length; ++k) { var id = cm.options.gutters[k], found = markers.hasOwnProperty(id) && markers[id]; if (found) { gutterWrap.appendChild(elt("div", [found], "CodeMirror-gutter-elt", ("left: " + (dims.gutterLeft[id]) + "px; width: " + (dims.gutterWidth[id]) + "px"))); } } } } } function updateLineWidgets(cm, lineView, dims) { if (lineView.alignable) { lineView.alignable = null; } for (var node = lineView.node.firstChild, next = (void 0); node; node = next) { next = node.nextSibling; if (node.className == "CodeMirror-linewidget") { lineView.node.removeChild(node); } } insertLineWidgets(cm, lineView, dims); } // Build a line's DOM representation from scratch function buildLineElement(cm, lineView, lineN, dims) { var built = getLineContent(cm, lineView); lineView.text = lineView.node = built.pre; if (built.bgClass) { lineView.bgClass = built.bgClass; } if (built.textClass) { lineView.textClass = built.textClass; } updateLineClasses(cm, lineView); updateLineGutter(cm, lineView, lineN, dims); insertLineWidgets(cm, lineView, dims); return lineView.node } // A lineView may contain multiple logical lines (when merged by // collapsed spans). The widgets for all of them need to be drawn. function insertLineWidgets(cm, lineView, dims) { insertLineWidgetsFor(cm, lineView.line, lineView, dims, true); if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { insertLineWidgetsFor(cm, lineView.rest[i], lineView, dims, false); } } } function insertLineWidgetsFor(cm, line, lineView, dims, allowAbove) { if (!line.widgets) { return } var wrap = ensureLineWrapped(lineView); for (var i = 0, ws = line.widgets; i < ws.length; ++i) { var widget = ws[i], node = elt("div", [widget.node], "CodeMirror-linewidget"); if (!widget.handleMouseEvents) { node.setAttribute("cm-ignore-events", "true"); } positionLineWidget(widget, node, lineView, dims); cm.display.input.setUneditable(node); if (allowAbove && widget.above) { wrap.insertBefore(node, lineView.gutter || lineView.text); } else { wrap.appendChild(node); } signalLater(widget, "redraw"); } } function positionLineWidget(widget, node, lineView, dims) { if (widget.noHScroll) { (lineView.alignable || (lineView.alignable = [])).push(node); var width = dims.wrapperWidth; node.style.left = dims.fixedPos + "px"; if (!widget.coverGutter) { width -= dims.gutterTotalWidth; node.style.paddingLeft = dims.gutterTotalWidth + "px"; } node.style.width = width + "px"; } if (widget.coverGutter) { node.style.zIndex = 5; node.style.position = "relative"; if (!widget.noHScroll) { node.style.marginLeft = -dims.gutterTotalWidth + "px"; } } } function widgetHeight(widget) { if (widget.height != null) { return widget.height } var cm = widget.doc.cm; if (!cm) { return 0 } if (!contains(document.body, widget.node)) { var parentStyle = "position: relative;"; if (widget.coverGutter) { parentStyle += "margin-left: -" + cm.display.gutters.offsetWidth + "px;"; } if (widget.noHScroll) { parentStyle += "width: " + cm.display.wrapper.clientWidth + "px;"; } removeChildrenAndAdd(cm.display.measure, elt("div", [widget.node], null, parentStyle)); } return widget.height = widget.node.parentNode.offsetHeight } // Return true when the given mouse event happened in a widget function eventInWidget(display, e) { for (var n = e_target(e); n != display.wrapper; n = n.parentNode) { if (!n || (n.nodeType == 1 && n.getAttribute("cm-ignore-events") == "true") || (n.parentNode == display.sizer && n != display.mover)) { return true } } } // POSITION MEASUREMENT function paddingTop(display) {return display.lineSpace.offsetTop} function paddingVert(display) {return display.mover.offsetHeight - display.lineSpace.offsetHeight} function paddingH(display) { if (display.cachedPaddingH) { return display.cachedPaddingH } var e = removeChildrenAndAdd(display.measure, elt("pre", "x")); var style = window.getComputedStyle ? window.getComputedStyle(e) : e.currentStyle; var data = {left: parseInt(style.paddingLeft), right: parseInt(style.paddingRight)}; if (!isNaN(data.left) && !isNaN(data.right)) { display.cachedPaddingH = data; } return data } function scrollGap(cm) { return scrollerGap - cm.display.nativeBarWidth } function displayWidth(cm) { return cm.display.scroller.clientWidth - scrollGap(cm) - cm.display.barWidth } function displayHeight(cm) { return cm.display.scroller.clientHeight - scrollGap(cm) - cm.display.barHeight } // Ensure the lineView.wrapping.heights array is populated. This is // an array of bottom offsets for the lines that make up a drawn // line. When lineWrapping is on, there might be more than one // height. function ensureLineHeights(cm, lineView, rect) { var wrapping = cm.options.lineWrapping; var curWidth = wrapping && displayWidth(cm); if (!lineView.measure.heights || wrapping && lineView.measure.width != curWidth) { var heights = lineView.measure.heights = []; if (wrapping) { lineView.measure.width = curWidth; var rects = lineView.text.firstChild.getClientRects(); for (var i = 0; i < rects.length - 1; i++) { var cur = rects[i], next = rects[i + 1]; if (Math.abs(cur.bottom - next.bottom) > 2) { heights.push((cur.bottom + next.top) / 2 - rect.top); } } } heights.push(rect.bottom - rect.top); } } // Find a line map (mapping character offsets to text nodes) and a // measurement cache for the given line number. (A line view might // contain multiple lines when collapsed ranges are present.) function mapFromLineView(lineView, line, lineN) { if (lineView.line == line) { return {map: lineView.measure.map, cache: lineView.measure.cache} } for (var i = 0; i < lineView.rest.length; i++) { if (lineView.rest[i] == line) { return {map: lineView.measure.maps[i], cache: lineView.measure.caches[i]} } } for (var i$1 = 0; i$1 < lineView.rest.length; i$1++) { if (lineNo(lineView.rest[i$1]) > lineN) { return {map: lineView.measure.maps[i$1], cache: lineView.measure.caches[i$1], before: true} } } } // Render a line into the hidden node display.externalMeasured. Used // when measurement is needed for a line that's not in the viewport. function updateExternalMeasurement(cm, line) { line = visualLine(line); var lineN = lineNo(line); var view = cm.display.externalMeasured = new LineView(cm.doc, line, lineN); view.lineN = lineN; var built = view.built = buildLineContent(cm, view); view.text = built.pre; removeChildrenAndAdd(cm.display.lineMeasure, built.pre); return view } // Get a {top, bottom, left, right} box (in line-local coordinates) // for a given character. function measureChar(cm, line, ch, bias) { return measureCharPrepared(cm, prepareMeasureForLine(cm, line), ch, bias) } // Find a line view that corresponds to the given line number. function findViewForLine(cm, lineN) { if (lineN >= cm.display.viewFrom && lineN < cm.display.viewTo) { return cm.display.view[findViewIndex(cm, lineN)] } var ext = cm.display.externalMeasured; if (ext && lineN >= ext.lineN && lineN < ext.lineN + ext.size) { return ext } } // Measurement can be split in two steps, the set-up work that // applies to the whole line, and the measurement of the actual // character. Functions like coordsChar, that need to do a lot of // measurements in a row, can thus ensure that the set-up work is // only done once. function prepareMeasureForLine(cm, line) { var lineN = lineNo(line); var view = findViewForLine(cm, lineN); if (view && !view.text) { view = null; } else if (view && view.changes) { updateLineForChanges(cm, view, lineN, getDimensions(cm)); cm.curOp.forceUpdate = true; } if (!view) { view = updateExternalMeasurement(cm, line); } var info = mapFromLineView(view, line, lineN); return { line: line, view: view, rect: null, map: info.map, cache: info.cache, before: info.before, hasHeights: false } } // Given a prepared measurement object, measures the position of an // actual character (or fetches it from the cache). function measureCharPrepared(cm, prepared, ch, bias, varHeight) { if (prepared.before) { ch = -1; } var key = ch + (bias || ""), found; if (prepared.cache.hasOwnProperty(key)) { found = prepared.cache[key]; } else { if (!prepared.rect) { prepared.rect = prepared.view.text.getBoundingClientRect(); } if (!prepared.hasHeights) { ensureLineHeights(cm, prepared.view, prepared.rect); prepared.hasHeights = true; } found = measureCharInner(cm, prepared, ch, bias); if (!found.bogus) { prepared.cache[key] = found; } } return {left: found.left, right: found.right, top: varHeight ? found.rtop : found.top, bottom: varHeight ? found.rbottom : found.bottom} } var nullRect = {left: 0, right: 0, top: 0, bottom: 0}; function nodeAndOffsetInLineMap(map$$1, ch, bias) { var node, start, end, collapse, mStart, mEnd; // First, search the line map for the text node corresponding to, // or closest to, the target character. for (var i = 0; i < map$$1.length; i += 3) { mStart = map$$1[i]; mEnd = map$$1[i + 1]; if (ch < mStart) { start = 0; end = 1; collapse = "left"; } else if (ch < mEnd) { start = ch - mStart; end = start + 1; } else if (i == map$$1.length - 3 || ch == mEnd && map$$1[i + 3] > ch) { end = mEnd - mStart; start = end - 1; if (ch >= mEnd) { collapse = "right"; } } if (start != null) { node = map$$1[i + 2]; if (mStart == mEnd && bias == (node.insertLeft ? "left" : "right")) { collapse = bias; } if (bias == "left" && start == 0) { while (i && map$$1[i - 2] == map$$1[i - 3] && map$$1[i - 1].insertLeft) { node = map$$1[(i -= 3) + 2]; collapse = "left"; } } if (bias == "right" && start == mEnd - mStart) { while (i < map$$1.length - 3 && map$$1[i + 3] == map$$1[i + 4] && !map$$1[i + 5].insertLeft) { node = map$$1[(i += 3) + 2]; collapse = "right"; } } break } } return {node: node, start: start, end: end, collapse: collapse, coverStart: mStart, coverEnd: mEnd} } function getUsefulRect(rects, bias) { var rect = nullRect; if (bias == "left") { for (var i = 0; i < rects.length; i++) { if ((rect = rects[i]).left != rect.right) { break } } } else { for (var i$1 = rects.length - 1; i$1 >= 0; i$1--) { if ((rect = rects[i$1]).left != rect.right) { break } } } return rect } function measureCharInner(cm, prepared, ch, bias) { var place = nodeAndOffsetInLineMap(prepared.map, ch, bias); var node = place.node, start = place.start, end = place.end, collapse = place.collapse; var rect; if (node.nodeType == 3) { // If it is a text node, use a range to retrieve the coordinates. for (var i$1 = 0; i$1 < 4; i$1++) { // Retry a maximum of 4 times when nonsense rectangles are returned while (start && isExtendingChar(prepared.line.text.charAt(place.coverStart + start))) { --start; } while (place.coverStart + end < place.coverEnd && isExtendingChar(prepared.line.text.charAt(place.coverStart + end))) { ++end; } if (ie && ie_version < 9 && start == 0 && end == place.coverEnd - place.coverStart) { rect = node.parentNode.getBoundingClientRect(); } else { rect = getUsefulRect(range(node, start, end).getClientRects(), bias); } if (rect.left || rect.right || start == 0) { break } end = start; start = start - 1; collapse = "right"; } if (ie && ie_version < 11) { rect = maybeUpdateRectForZooming(cm.display.measure, rect); } } else { // If it is a widget, simply get the box for the whole widget. if (start > 0) { collapse = bias = "right"; } var rects; if (cm.options.lineWrapping && (rects = node.getClientRects()).length > 1) { rect = rects[bias == "right" ? rects.length - 1 : 0]; } else { rect = node.getBoundingClientRect(); } } if (ie && ie_version < 9 && !start && (!rect || !rect.left && !rect.right)) { var rSpan = node.parentNode.getClientRects()[0]; if (rSpan) { rect = {left: rSpan.left, right: rSpan.left + charWidth(cm.display), top: rSpan.top, bottom: rSpan.bottom}; } else { rect = nullRect; } } var rtop = rect.top - prepared.rect.top, rbot = rect.bottom - prepared.rect.top; var mid = (rtop + rbot) / 2; var heights = prepared.view.measure.heights; var i = 0; for (; i < heights.length - 1; i++) { if (mid < heights[i]) { break } } var top = i ? heights[i - 1] : 0, bot = heights[i]; var result = {left: (collapse == "right" ? rect.right : rect.left) - prepared.rect.left, right: (collapse == "left" ? rect.left : rect.right) - prepared.rect.left, top: top, bottom: bot}; if (!rect.left && !rect.right) { result.bogus = true; } if (!cm.options.singleCursorHeightPerLine) { result.rtop = rtop; result.rbottom = rbot; } return result } // Work around problem with bounding client rects on ranges being // returned incorrectly when zoomed on IE10 and below. function maybeUpdateRectForZooming(measure, rect) { if (!window.screen || screen.logicalXDPI == null || screen.logicalXDPI == screen.deviceXDPI || !hasBadZoomedRects(measure)) { return rect } var scaleX = screen.logicalXDPI / screen.deviceXDPI; var scaleY = screen.logicalYDPI / screen.deviceYDPI; return {left: rect.left * scaleX, right: rect.right * scaleX, top: rect.top * scaleY, bottom: rect.bottom * scaleY} } function clearLineMeasurementCacheFor(lineView) { if (lineView.measure) { lineView.measure.cache = {}; lineView.measure.heights = null; if (lineView.rest) { for (var i = 0; i < lineView.rest.length; i++) { lineView.measure.caches[i] = {}; } } } } function clearLineMeasurementCache(cm) { cm.display.externalMeasure = null; removeChildren(cm.display.lineMeasure); for (var i = 0; i < cm.display.view.length; i++) { clearLineMeasurementCacheFor(cm.display.view[i]); } } function clearCaches(cm) { clearLineMeasurementCache(cm); cm.display.cachedCharWidth = cm.display.cachedTextHeight = cm.display.cachedPaddingH = null; if (!cm.options.lineWrapping) { cm.display.maxLineChanged = true; } cm.display.lineNumChars = null; } function pageScrollX() { // Work around https://bugs.chromium.org/p/chromium/issues/detail?id=489206 // which causes page_Offset and bounding client rects to use // different reference viewports and invalidate our calculations. if (chrome && android) { return -(document.body.getBoundingClientRect().left - parseInt(getComputedStyle(document.body).marginLeft)) } return window.pageXOffset || (document.documentElement || document.body).scrollLeft } function pageScrollY() { if (chrome && android) { return -(document.body.getBoundingClientRect().top - parseInt(getComputedStyle(document.body).marginTop)) } return window.pageYOffset || (document.documentElement || document.body).scrollTop } // Converts a {top, bottom, left, right} box from line-local // coordinates into another coordinate system. Context may be one of // "line", "div" (display.lineDiv), "local"./null (editor), "window", // or "page". function intoCoordSystem(cm, lineObj, rect, context, includeWidgets) { if (!includeWidgets && lineObj.widgets) { for (var i = 0; i < lineObj.widgets.length; ++i) { if (lineObj.widgets[i].above) { var size = widgetHeight(lineObj.widgets[i]); rect.top += size; rect.bottom += size; } } } if (context == "line") { return rect } if (!context) { context = "local"; } var yOff = heightAtLine(lineObj); if (context == "local") { yOff += paddingTop(cm.display); } else { yOff -= cm.display.viewOffset; } if (context == "page" || context == "window") { var lOff = cm.display.lineSpace.getBoundingClientRect(); yOff += lOff.top + (context == "window" ? 0 : pageScrollY()); var xOff = lOff.left + (context == "window" ? 0 : pageScrollX()); rect.left += xOff; rect.right += xOff; } rect.top += yOff; rect.bottom += yOff; return rect } // Coverts a box from "div" coords to another coordinate system. // Context may be "window", "page", "div", or "local"./null. function fromCoordSystem(cm, coords, context) { if (context == "div") { return coords } var left = coords.left, top = coords.top; // First move into "page" coordinate system if (context == "page") { left -= pageScrollX(); top -= pageScrollY(); } else if (context == "local" || !context) { var localBox = cm.display.sizer.getBoundingClientRect(); left += localBox.left; top += localBox.top; } var lineSpaceBox = cm.display.lineSpace.getBoundingClientRect(); return {left: left - lineSpaceBox.left, top: top - lineSpaceBox.top} } function charCoords(cm, pos, context, lineObj, bias) { if (!lineObj) { lineObj = getLine(cm.doc, pos.line); } return intoCoordSystem(cm, lineObj, measureChar(cm, lineObj, pos.ch, bias), context) } // Returns a box for a given cursor position, which may have an // 'other' property containing the position of the secondary cursor // on a bidi boundary. // A cursor Pos(line, char, "before") is on the same visual line as `char - 1` // and after `char - 1` in writing order of `char - 1` // A cursor Pos(line, char, "after") is on the same visual line as `char` // and before `char` in writing order of `char` // Examples (upper-case letters are RTL, lower-case are LTR): // Pos(0, 1, ...) // before after // ab a|b a|b // aB a|B aB| // Ab |Ab A|b // AB B|A B|A // Every position after the last character on a line is considered to stick // to the last character on the line. function cursorCoords(cm, pos, context, lineObj, preparedMeasure, varHeight) { lineObj = lineObj || getLine(cm.doc, pos.line); if (!preparedMeasure) { preparedMeasure = prepareMeasureForLine(cm, lineObj); } function get(ch, right) { var m = measureCharPrepared(cm, preparedMeasure, ch, right ? "right" : "left", varHeight); if (right) { m.left = m.right; } else { m.right = m.left; } return intoCoordSystem(cm, lineObj, m, context) } var order = getOrder(lineObj, cm.doc.direction), ch = pos.ch, sticky = pos.sticky; if (ch >= lineObj.text.length) { ch = lineObj.text.length; sticky = "before"; } else if (ch <= 0) { ch = 0; sticky = "after"; } if (!order) { return get(sticky == "before" ? ch - 1 : ch, sticky == "before") } function getBidi(ch, partPos, invert) { var part = order[partPos], right = (part.level % 2) != 0; return get(invert ? ch - 1 : ch, right != invert) } var partPos = getBidiPartAt(order, ch, sticky); var other = bidiOther; var val = getBidi(ch, partPos, sticky == "before"); if (other != null) { val.other = getBidi(ch, other, sticky != "before"); } return val } // Used to cheaply estimate the coordinates for a position. Used for // intermediate scroll updates. function estimateCoords(cm, pos) { var left = 0; pos = clipPos(cm.doc, pos); if (!cm.options.lineWrapping) { left = charWidth(cm.display) * pos.ch; } var lineObj = getLine(cm.doc, pos.line); var top = heightAtLine(lineObj) + paddingTop(cm.display); return {left: left, right: left, top: top, bottom: top + lineObj.height} } // Positions returned by coordsChar contain some extra information. // xRel is the relative x position of the input coordinates compared // to the found position (so xRel > 0 means the coordinates are to // the right of the character position, for example). When outside // is true, that means the coordinates lie outside the line's // vertical range. function PosWithInfo(line, ch, sticky, outside, xRel) { var pos = Pos(line, ch, sticky); pos.xRel = xRel; if (outside) { pos.outside = true; } return pos } // Compute the character position closest to the given coordinates. // Input must be lineSpace-local ("div" coordinate system). function coordsChar(cm, x, y) { var doc = cm.doc; y += cm.display.viewOffset; if (y < 0) { return PosWithInfo(doc.first, 0, null, true, -1) } var lineN = lineAtHeight(doc, y), last = doc.first + doc.size - 1; if (lineN > last) { return PosWithInfo(doc.first + doc.size - 1, getLine(doc, last).text.length, null, true, 1) } if (x < 0) { x = 0; } var lineObj = getLine(doc, lineN); for (;;) { var found = coordsCharInner(cm, lineObj, lineN, x, y); var merged = collapsedSpanAtEnd(lineObj); var mergedPos = merged && merged.find(0, true); if (merged && (found.ch > mergedPos.from.ch || found.ch == mergedPos.from.ch && found.xRel > 0)) { lineN = lineNo(lineObj = mergedPos.to.line); } else { return found } } } function wrappedLineExtent(cm, lineObj, preparedMeasure, y) { var measure = function (ch) { return intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); }; var end = lineObj.text.length; var begin = findFirst(function (ch) { return measure(ch - 1).bottom <= y; }, end, 0); end = findFirst(function (ch) { return measure(ch).top > y; }, begin, end); return {begin: begin, end: end} } function wrappedLineExtentChar(cm, lineObj, preparedMeasure, target) { var targetTop = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, target), "line").top; return wrappedLineExtent(cm, lineObj, preparedMeasure, targetTop) } function coordsCharInner(cm, lineObj, lineNo$$1, x, y) { y -= heightAtLine(lineObj); var begin = 0, end = lineObj.text.length; var preparedMeasure = prepareMeasureForLine(cm, lineObj); var pos; var order = getOrder(lineObj, cm.doc.direction); if (order) { if (cm.options.lineWrapping) { var assign; ((assign = wrappedLineExtent(cm, lineObj, preparedMeasure, y), begin = assign.begin, end = assign.end, assign)); } pos = new Pos(lineNo$$1, Math.floor(begin + (end - begin) / 2)); var beginLeft = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left; var dir = beginLeft < x ? 1 : -1; var prevDiff, diff = beginLeft - x, prevPos; var steps = Math.ceil((end - begin) / 4); outer: do { prevDiff = diff; prevPos = pos; var i = 0; for (; i < steps; ++i) { var prevPos$1 = pos; pos = moveVisually(cm, lineObj, pos, dir); if (pos == null || pos.ch < begin || end <= (pos.sticky == "before" ? pos.ch - 1 : pos.ch)) { pos = prevPos$1; break outer } } diff = cursorCoords(cm, pos, "line", lineObj, preparedMeasure).left - x; if (steps > 1) { var diff_change_per_step = Math.abs(diff - prevDiff) / steps; steps = Math.min(steps, Math.ceil(Math.abs(diff) / diff_change_per_step)); dir = diff < 0 ? 1 : -1; } } while (diff != 0 && (steps > 1 || ((dir < 0) != (diff < 0) && (Math.abs(diff) <= Math.abs(prevDiff))))) if (Math.abs(diff) > Math.abs(prevDiff)) { if ((diff < 0) == (prevDiff < 0)) { throw new Error("Broke out of infinite loop in coordsCharInner") } pos = prevPos; } } else { var ch = findFirst(function (ch) { var box = intoCoordSystem(cm, lineObj, measureCharPrepared(cm, preparedMeasure, ch), "line"); if (box.top > y) { // For the cursor stickiness end = Math.min(ch, end); return true } else if (box.bottom <= y) { return false } else if (box.left > x) { return true } else if (box.right < x) { return false } else { return (x - box.left < box.right - x) } }, begin, end); ch = skipExtendingChars(lineObj.text, ch, 1); pos = new Pos(lineNo$$1, ch, ch == end ? "before" : "after"); } var coords = cursorCoords(cm, pos, "line", lineObj, preparedMeasure); if (y < coords.top || coords.bottom < y) { pos.outside = true; } pos.xRel = x < coords.left ? -1 : (x > coords.right ? 1 : 0); return pos } var measureText; // Compute the default text height. function textHeight(display) { if (display.cachedTextHeight != null) { return display.cachedTextHeight } if (measureText == null) { measureText = elt("pre"); // Measure a bunch of lines, for browsers that compute // fractional heights. for (var i = 0; i < 49; ++i) { measureText.appendChild(document.createTextNode("x")); measureText.appendChild(elt("br")); } measureText.appendChild(document.createTextNode("x")); } removeChildrenAndAdd(display.measure, measureText); var height = measureText.offsetHeight / 50; if (height > 3) { display.cachedTextHeight = height; } removeChildren(display.measure); return height || 1 } // Compute the default character width. function charWidth(display) { if (display.cachedCharWidth != null) { return display.cachedCharWidth } var anchor = elt("span", "xxxxxxxxxx"); var pre = elt("pre", [anchor]); removeChildrenAndAdd(display.measure, pre); var rect = anchor.getBoundingClientRect(), width = (rect.right - rect.left) / 10; if (width > 2) { display.cachedCharWidth = width; } return width || 10 } // Do a bulk-read of the DOM positions and sizes needed to draw the // view, so that we don't interleave reading and writing to the DOM. function getDimensions(cm) { var d = cm.display, left = {}, width = {}; var gutterLeft = d.gutters.clientLeft; for (var n = d.gutters.firstChild, i = 0; n; n = n.nextSibling, ++i) { left[cm.options.gutters[i]] = n.offsetLeft + n.clientLeft + gutterLeft; width[cm.options.gutters[i]] = n.clientWidth; } return {fixedPos: compensateForHScroll(d), gutterTotalWidth: d.gutters.offsetWidth, gutterLeft: left, gutterWidth: width, wrapperWidth: d.wrapper.clientWidth} } // Computes display.scroller.scrollLeft + display.gutters.offsetWidth, // but using getBoundingClientRect to get a sub-pixel-accurate // result. function compensateForHScroll(display) { return display.scroller.getBoundingClientRect().left - display.sizer.getBoundingClientRect().left } // Returns a function that estimates the height of a line, to use as // first approximation until the line becomes visible (and is thus // properly measurable). function estimateHeight(cm) { var th = textHeight(cm.display), wrapping = cm.options.lineWrapping; var perLine = wrapping && Math.max(5, cm.display.scroller.clientWidth / charWidth(cm.display) - 3); return function (line) { if (lineIsHidden(cm.doc, line)) { return 0 } var widgetsHeight = 0; if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].height) { widgetsHeight += line.widgets[i].height; } } } if (wrapping) { return widgetsHeight + (Math.ceil(line.text.length / perLine) || 1) * th } else { return widgetsHeight + th } } } function estimateLineHeights(cm) { var doc = cm.doc, est = estimateHeight(cm); doc.iter(function (line) { var estHeight = est(line); if (estHeight != line.height) { updateLineHeight(line, estHeight); } }); } // Given a mouse event, find the corresponding position. If liberal // is false, it checks whether a gutter or scrollbar was clicked, // and returns null if it was. forRect is used by rectangular // selections, and tries to estimate a character position even for // coordinates beyond the right of the text. function posFromMouse(cm, e, liberal, forRect) { var display = cm.display; if (!liberal && e_target(e).getAttribute("cm-not-content") == "true") { return null } var x, y, space = display.lineSpace.getBoundingClientRect(); // Fails unpredictably on IE[67] when mouse is dragged around quickly. try { x = e.clientX - space.left; y = e.clientY - space.top; } catch (e) { return null } var coords = coordsChar(cm, x, y), line; if (forRect && coords.xRel == 1 && (line = getLine(cm.doc, coords.line).text).length == coords.ch) { var colDiff = countColumn(line, line.length, cm.options.tabSize) - line.length; coords = Pos(coords.line, Math.max(0, Math.round((x - paddingH(cm.display).left) / charWidth(cm.display)) - colDiff)); } return coords } // Find the view element corresponding to a given line. Return null // when the line isn't visible. function findViewIndex(cm, n) { if (n >= cm.display.viewTo) { return null } n -= cm.display.viewFrom; if (n < 0) { return null } var view = cm.display.view; for (var i = 0; i < view.length; i++) { n -= view[i].size; if (n < 0) { return i } } } function updateSelection(cm) { cm.display.input.showSelection(cm.display.input.prepareSelection()); } function prepareSelection(cm, primary) { var doc = cm.doc, result = {}; var curFragment = result.cursors = document.createDocumentFragment(); var selFragment = result.selection = document.createDocumentFragment(); for (var i = 0; i < doc.sel.ranges.length; i++) { if (primary === false && i == doc.sel.primIndex) { continue } var range$$1 = doc.sel.ranges[i]; if (range$$1.from().line >= cm.display.viewTo || range$$1.to().line < cm.display.viewFrom) { continue } var collapsed = range$$1.empty(); if (collapsed || cm.options.showCursorWhenSelecting) { drawSelectionCursor(cm, range$$1.head, curFragment); } if (!collapsed) { drawSelectionRange(cm, range$$1, selFragment); } } return result } // Draws a cursor for the given range function drawSelectionCursor(cm, head, output) { var pos = cursorCoords(cm, head, "div", null, null, !cm.options.singleCursorHeightPerLine); var cursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor")); cursor.style.left = pos.left + "px"; cursor.style.top = pos.top + "px"; cursor.style.height = Math.max(0, pos.bottom - pos.top) * cm.options.cursorHeight + "px"; if (pos.other) { // Secondary cursor, shown when on a 'jump' in bi-directional text var otherCursor = output.appendChild(elt("div", "\u00a0", "CodeMirror-cursor CodeMirror-secondarycursor")); otherCursor.style.display = ""; otherCursor.style.left = pos.other.left + "px"; otherCursor.style.top = pos.other.top + "px"; otherCursor.style.height = (pos.other.bottom - pos.other.top) * .85 + "px"; } } // Draws the given range as a highlighted selection function drawSelectionRange(cm, range$$1, output) { var display = cm.display, doc = cm.doc; var fragment = document.createDocumentFragment(); var padding = paddingH(cm.display), leftSide = padding.left; var rightSide = Math.max(display.sizerWidth, displayWidth(cm) - display.sizer.offsetLeft) - padding.right; function add(left, top, width, bottom) { if (top < 0) { top = 0; } top = Math.round(top); bottom = Math.round(bottom); fragment.appendChild(elt("div", null, "CodeMirror-selected", ("position: absolute; left: " + left + "px;\n top: " + top + "px; width: " + (width == null ? rightSide - left : width) + "px;\n height: " + (bottom - top) + "px"))); } function drawForLine(line, fromArg, toArg) { var lineObj = getLine(doc, line); var lineLen = lineObj.text.length; var start, end; function coords(ch, bias) { return charCoords(cm, Pos(line, ch), "div", lineObj, bias) } iterateBidiSections(getOrder(lineObj, doc.direction), fromArg || 0, toArg == null ? lineLen : toArg, function (from, to, dir) { var leftPos = coords(from, "left"), rightPos, left, right; if (from == to) { rightPos = leftPos; left = right = leftPos.left; } else { rightPos = coords(to - 1, "right"); if (dir == "rtl") { var tmp = leftPos; leftPos = rightPos; rightPos = tmp; } left = leftPos.left; right = rightPos.right; } if (fromArg == null && from == 0) { left = leftSide; } if (rightPos.top - leftPos.top > 3) { // Different lines, draw top part add(left, leftPos.top, null, leftPos.bottom); left = leftSide; if (leftPos.bottom < rightPos.top) { add(left, leftPos.bottom, null, rightPos.top); } } if (toArg == null && to == lineLen) { right = rightSide; } if (!start || leftPos.top < start.top || leftPos.top == start.top && leftPos.left < start.left) { start = leftPos; } if (!end || rightPos.bottom > end.bottom || rightPos.bottom == end.bottom && rightPos.right > end.right) { end = rightPos; } if (left < leftSide + 1) { left = leftSide; } add(left, rightPos.top, right - left, rightPos.bottom); }); return {start: start, end: end} } var sFrom = range$$1.from(), sTo = range$$1.to(); if (sFrom.line == sTo.line) { drawForLine(sFrom.line, sFrom.ch, sTo.ch); } else { var fromLine = getLine(doc, sFrom.line), toLine = getLine(doc, sTo.line); var singleVLine = visualLine(fromLine) == visualLine(toLine); var leftEnd = drawForLine(sFrom.line, sFrom.ch, singleVLine ? fromLine.text.length + 1 : null).end; var rightStart = drawForLine(sTo.line, singleVLine ? 0 : null, sTo.ch).start; if (singleVLine) { if (leftEnd.top < rightStart.top - 2) { add(leftEnd.right, leftEnd.top, null, leftEnd.bottom); add(leftSide, rightStart.top, rightStart.left, rightStart.bottom); } else { add(leftEnd.right, leftEnd.top, rightStart.left - leftEnd.right, leftEnd.bottom); } } if (leftEnd.bottom < rightStart.top) { add(leftSide, leftEnd.bottom, null, rightStart.top); } } output.appendChild(fragment); } // Cursor-blinking function restartBlink(cm) { if (!cm.state.focused) { return } var display = cm.display; clearInterval(display.blinker); var on = true; display.cursorDiv.style.visibility = ""; if (cm.options.cursorBlinkRate > 0) { display.blinker = setInterval(function () { return display.cursorDiv.style.visibility = (on = !on) ? "" : "hidden"; }, cm.options.cursorBlinkRate); } else if (cm.options.cursorBlinkRate < 0) { display.cursorDiv.style.visibility = "hidden"; } } function ensureFocus(cm) { if (!cm.state.focused) { cm.display.input.focus(); onFocus(cm); } } function delayBlurEvent(cm) { cm.state.delayingBlurEvent = true; setTimeout(function () { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; onBlur(cm); } }, 100); } function onFocus(cm, e) { if (cm.state.delayingBlurEvent) { cm.state.delayingBlurEvent = false; } if (cm.options.readOnly == "nocursor") { return } if (!cm.state.focused) { signal(cm, "focus", cm, e); cm.state.focused = true; addClass(cm.display.wrapper, "CodeMirror-focused"); // This test prevents this from firing when a context // menu is closed (since the input reset would kill the // select-all detection hack) if (!cm.curOp && cm.display.selForContextMenu != cm.doc.sel) { cm.display.input.reset(); if (webkit) { setTimeout(function () { return cm.display.input.reset(true); }, 20); } // Issue #1730 } cm.display.input.receivedFocus(); } restartBlink(cm); } function onBlur(cm, e) { if (cm.state.delayingBlurEvent) { return } if (cm.state.focused) { signal(cm, "blur", cm, e); cm.state.focused = false; rmClass(cm.display.wrapper, "CodeMirror-focused"); } clearInterval(cm.display.blinker); setTimeout(function () { if (!cm.state.focused) { cm.display.shift = false; } }, 150); } // Read the actual heights of the rendered lines, and update their // stored heights to match. function updateHeightsInViewport(cm) { var display = cm.display; var prevBottom = display.lineDiv.offsetTop; for (var i = 0; i < display.view.length; i++) { var cur = display.view[i], height = (void 0); if (cur.hidden) { continue } if (ie && ie_version < 8) { var bot = cur.node.offsetTop + cur.node.offsetHeight; height = bot - prevBottom; prevBottom = bot; } else { var box = cur.node.getBoundingClientRect(); height = box.bottom - box.top; } var diff = cur.line.height - height; if (height < 2) { height = textHeight(display); } if (diff > .005 || diff < -.005) { updateLineHeight(cur.line, height); updateWidgetHeight(cur.line); if (cur.rest) { for (var j = 0; j < cur.rest.length; j++) { updateWidgetHeight(cur.rest[j]); } } } } } // Read and store the height of line widgets associated with the // given line. function updateWidgetHeight(line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; ++i) { line.widgets[i].height = line.widgets[i].node.parentNode.offsetHeight; } } } // Compute the lines that are visible in a given viewport (defaults // the the current scroll position). viewport may contain top, // height, and ensure (see op.scrollToPos) properties. function visibleLines(display, doc, viewport) { var top = viewport && viewport.top != null ? Math.max(0, viewport.top) : display.scroller.scrollTop; top = Math.floor(top - paddingTop(display)); var bottom = viewport && viewport.bottom != null ? viewport.bottom : top + display.wrapper.clientHeight; var from = lineAtHeight(doc, top), to = lineAtHeight(doc, bottom); // Ensure is a {from: {line, ch}, to: {line, ch}} object, and // forces those lines into the viewport (if possible). if (viewport && viewport.ensure) { var ensureFrom = viewport.ensure.from.line, ensureTo = viewport.ensure.to.line; if (ensureFrom < from) { from = ensureFrom; to = lineAtHeight(doc, heightAtLine(getLine(doc, ensureFrom)) + display.wrapper.clientHeight); } else if (Math.min(ensureTo, doc.lastLine()) >= to) { from = lineAtHeight(doc, heightAtLine(getLine(doc, ensureTo)) - display.wrapper.clientHeight); to = ensureTo; } } return {from: from, to: Math.max(to, from + 1)} } // Re-align line numbers and gutter marks to compensate for // horizontal scrolling. function alignHorizontally(cm) { var display = cm.display, view = display.view; if (!display.alignWidgets && (!display.gutters.firstChild || !cm.options.fixedGutter)) { return } var comp = compensateForHScroll(display) - display.scroller.scrollLeft + cm.doc.scrollLeft; var gutterW = display.gutters.offsetWidth, left = comp + "px"; for (var i = 0; i < view.length; i++) { if (!view[i].hidden) { if (cm.options.fixedGutter) { if (view[i].gutter) { view[i].gutter.style.left = left; } if (view[i].gutterBackground) { view[i].gutterBackground.style.left = left; } } var align = view[i].alignable; if (align) { for (var j = 0; j < align.length; j++) { align[j].style.left = left; } } } } if (cm.options.fixedGutter) { display.gutters.style.left = (comp + gutterW) + "px"; } } // Used to ensure that the line number gutter is still the right // size for the current document size. Returns true when an update // is needed. function maybeUpdateLineNumberWidth(cm) { if (!cm.options.lineNumbers) { return false } var doc = cm.doc, last = lineNumberFor(cm.options, doc.first + doc.size - 1), display = cm.display; if (last.length != display.lineNumChars) { var test = display.measure.appendChild(elt("div", [elt("div", last)], "CodeMirror-linenumber CodeMirror-gutter-elt")); var innerW = test.firstChild.offsetWidth, padding = test.offsetWidth - innerW; display.lineGutter.style.width = ""; display.lineNumInnerWidth = Math.max(innerW, display.lineGutter.offsetWidth - padding) + 1; display.lineNumWidth = display.lineNumInnerWidth + padding; display.lineNumChars = display.lineNumInnerWidth ? last.length : -1; display.lineGutter.style.width = display.lineNumWidth + "px"; updateGutterSpace(cm); return true } return false } // SCROLLING THINGS INTO VIEW // If an editor sits on the top or bottom of the window, partially // scrolled out of view, this ensures that the cursor is visible. function maybeScrollWindow(cm, rect) { if (signalDOMEvent(cm, "scrollCursorIntoView")) { return } var display = cm.display, box = display.sizer.getBoundingClientRect(), doScroll = null; if (rect.top + box.top < 0) { doScroll = true; } else if (rect.bottom + box.top > (window.innerHeight || document.documentElement.clientHeight)) { doScroll = false; } if (doScroll != null && !phantom) { var scrollNode = elt("div", "\u200b", null, ("position: absolute;\n top: " + (rect.top - display.viewOffset - paddingTop(cm.display)) + "px;\n height: " + (rect.bottom - rect.top + scrollGap(cm) + display.barHeight) + "px;\n left: " + (rect.left) + "px; width: " + (Math.max(2, rect.right - rect.left)) + "px;")); cm.display.lineSpace.appendChild(scrollNode); scrollNode.scrollIntoView(doScroll); cm.display.lineSpace.removeChild(scrollNode); } } // Scroll a given position into view (immediately), verifying that // it actually became visible (as line heights are accurately // measured, the position of something may 'drift' during drawing). function scrollPosIntoView(cm, pos, end, margin) { if (margin == null) { margin = 0; } var rect; if (!cm.options.lineWrapping && pos == end) { // Set pos and end to the cursor positions around the character pos sticks to // If pos.sticky == "before", that is around pos.ch - 1, otherwise around pos.ch // If pos == Pos(_, 0, "before"), pos and end are unchanged pos = pos.ch ? Pos(pos.line, pos.sticky == "before" ? pos.ch - 1 : pos.ch, "after") : pos; end = pos.sticky == "before" ? Pos(pos.line, pos.ch + 1, "before") : pos; } for (var limit = 0; limit < 5; limit++) { var changed = false; var coords = cursorCoords(cm, pos); var endCoords = !end || end == pos ? coords : cursorCoords(cm, end); rect = {left: Math.min(coords.left, endCoords.left), top: Math.min(coords.top, endCoords.top) - margin, right: Math.max(coords.left, endCoords.left), bottom: Math.max(coords.bottom, endCoords.bottom) + margin}; var scrollPos = calculateScrollPos(cm, rect); var startTop = cm.doc.scrollTop, startLeft = cm.doc.scrollLeft; if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); if (Math.abs(cm.doc.scrollTop - startTop) > 1) { changed = true; } } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); if (Math.abs(cm.doc.scrollLeft - startLeft) > 1) { changed = true; } } if (!changed) { break } } return rect } // Scroll a given set of coordinates into view (immediately). function scrollIntoView(cm, rect) { var scrollPos = calculateScrollPos(cm, rect); if (scrollPos.scrollTop != null) { updateScrollTop(cm, scrollPos.scrollTop); } if (scrollPos.scrollLeft != null) { setScrollLeft(cm, scrollPos.scrollLeft); } } // Calculate a new scroll position needed to scroll the given // rectangle into view. Returns an object with scrollTop and // scrollLeft properties. When these are undefined, the // vertical/horizontal position does not need to be adjusted. function calculateScrollPos(cm, rect) { var display = cm.display, snapMargin = textHeight(cm.display); if (rect.top < 0) { rect.top = 0; } var screentop = cm.curOp && cm.curOp.scrollTop != null ? cm.curOp.scrollTop : display.scroller.scrollTop; var screen = displayHeight(cm), result = {}; if (rect.bottom - rect.top > screen) { rect.bottom = rect.top + screen; } var docBottom = cm.doc.height + paddingVert(display); var atTop = rect.top < snapMargin, atBottom = rect.bottom > docBottom - snapMargin; if (rect.top < screentop) { result.scrollTop = atTop ? 0 : rect.top; } else if (rect.bottom > screentop + screen) { var newTop = Math.min(rect.top, (atBottom ? docBottom : rect.bottom) - screen); if (newTop != screentop) { result.scrollTop = newTop; } } var screenleft = cm.curOp && cm.curOp.scrollLeft != null ? cm.curOp.scrollLeft : display.scroller.scrollLeft; var screenw = displayWidth(cm) - (cm.options.fixedGutter ? display.gutters.offsetWidth : 0); var tooWide = rect.right - rect.left > screenw; if (tooWide) { rect.right = rect.left + screenw; } if (rect.left < 10) { result.scrollLeft = 0; } else if (rect.left < screenleft) { result.scrollLeft = Math.max(0, rect.left - (tooWide ? 0 : 10)); } else if (rect.right > screenw + screenleft - 3) { result.scrollLeft = rect.right + (tooWide ? 0 : 10) - screenw; } return result } // Store a relative adjustment to the scroll position in the current // operation (to be applied when the operation finishes). function addToScrollTop(cm, top) { if (top == null) { return } resolveScrollToPos(cm); cm.curOp.scrollTop = (cm.curOp.scrollTop == null ? cm.doc.scrollTop : cm.curOp.scrollTop) + top; } // Make sure that at the end of the operation the current cursor is // shown. function ensureCursorVisible(cm) { resolveScrollToPos(cm); var cur = cm.getCursor(); cm.curOp.scrollToPos = {from: cur, to: cur, margin: cm.options.cursorScrollMargin}; } function scrollToCoords(cm, x, y) { if (x != null || y != null) { resolveScrollToPos(cm); } if (x != null) { cm.curOp.scrollLeft = x; } if (y != null) { cm.curOp.scrollTop = y; } } function scrollToRange(cm, range$$1) { resolveScrollToPos(cm); cm.curOp.scrollToPos = range$$1; } // When an operation has its scrollToPos property set, and another // scroll action is applied before the end of the operation, this // 'simulates' scrolling that position into view in a cheap way, so // that the effect of intermediate scroll commands is not ignored. function resolveScrollToPos(cm) { var range$$1 = cm.curOp.scrollToPos; if (range$$1) { cm.curOp.scrollToPos = null; var from = estimateCoords(cm, range$$1.from), to = estimateCoords(cm, range$$1.to); scrollToCoordsRange(cm, from, to, range$$1.margin); } } function scrollToCoordsRange(cm, from, to, margin) { var sPos = calculateScrollPos(cm, { left: Math.min(from.left, to.left), top: Math.min(from.top, to.top) - margin, right: Math.max(from.right, to.right), bottom: Math.max(from.bottom, to.bottom) + margin }); scrollToCoords(cm, sPos.scrollLeft, sPos.scrollTop); } // Sync the scrollable area and scrollbars, ensure the viewport // covers the visible area. function updateScrollTop(cm, val) { if (Math.abs(cm.doc.scrollTop - val) < 2) { return } if (!gecko) { updateDisplaySimple(cm, {top: val}); } setScrollTop(cm, val, true); if (gecko) { updateDisplaySimple(cm); } startWorker(cm, 100); } function setScrollTop(cm, val, forceScroll) { val = Math.min(cm.display.scroller.scrollHeight - cm.display.scroller.clientHeight, val); if (cm.display.scroller.scrollTop == val && !forceScroll) { return } cm.doc.scrollTop = val; cm.display.scrollbars.setScrollTop(val); if (cm.display.scroller.scrollTop != val) { cm.display.scroller.scrollTop = val; } } // Sync scroller and scrollbar, ensure the gutter elements are // aligned. function setScrollLeft(cm, val, isScroller, forceScroll) { val = Math.min(val, cm.display.scroller.scrollWidth - cm.display.scroller.clientWidth); if ((isScroller ? val == cm.doc.scrollLeft : Math.abs(cm.doc.scrollLeft - val) < 2) && !forceScroll) { return } cm.doc.scrollLeft = val; alignHorizontally(cm); if (cm.display.scroller.scrollLeft != val) { cm.display.scroller.scrollLeft = val; } cm.display.scrollbars.setScrollLeft(val); } // SCROLLBARS // Prepare DOM reads needed to update the scrollbars. Done in one // shot to minimize update/measure roundtrips. function measureForScrollbars(cm) { var d = cm.display, gutterW = d.gutters.offsetWidth; var docH = Math.round(cm.doc.height + paddingVert(cm.display)); return { clientHeight: d.scroller.clientHeight, viewHeight: d.wrapper.clientHeight, scrollWidth: d.scroller.scrollWidth, clientWidth: d.scroller.clientWidth, viewWidth: d.wrapper.clientWidth, barLeft: cm.options.fixedGutter ? gutterW : 0, docHeight: docH, scrollHeight: docH + scrollGap(cm) + d.barHeight, nativeBarWidth: d.nativeBarWidth, gutterWidth: gutterW } } var NativeScrollbars = function(place, scroll, cm) { this.cm = cm; var vert = this.vert = elt("div", [elt("div", null, null, "min-width: 1px")], "CodeMirror-vscrollbar"); var horiz = this.horiz = elt("div", [elt("div", null, null, "height: 100%; min-height: 1px")], "CodeMirror-hscrollbar"); place(vert); place(horiz); on(vert, "scroll", function () { if (vert.clientHeight) { scroll(vert.scrollTop, "vertical"); } }); on(horiz, "scroll", function () { if (horiz.clientWidth) { scroll(horiz.scrollLeft, "horizontal"); } }); this.checkedZeroWidth = false; // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8). if (ie && ie_version < 8) { this.horiz.style.minHeight = this.vert.style.minWidth = "18px"; } }; NativeScrollbars.prototype.update = function (measure) { var needsH = measure.scrollWidth > measure.clientWidth + 1; var needsV = measure.scrollHeight > measure.clientHeight + 1; var sWidth = measure.nativeBarWidth; if (needsV) { this.vert.style.display = "block"; this.vert.style.bottom = needsH ? sWidth + "px" : "0"; var totalHeight = measure.viewHeight - (needsH ? sWidth : 0); // A bug in IE8 can cause this value to be negative, so guard it. this.vert.firstChild.style.height = Math.max(0, measure.scrollHeight - measure.clientHeight + totalHeight) + "px"; } else { this.vert.style.display = ""; this.vert.firstChild.style.height = "0"; } if (needsH) { this.horiz.style.display = "block"; this.horiz.style.right = needsV ? sWidth + "px" : "0"; this.horiz.style.left = measure.barLeft + "px"; var totalWidth = measure.viewWidth - measure.barLeft - (needsV ? sWidth : 0); this.horiz.firstChild.style.width = Math.max(0, measure.scrollWidth - measure.clientWidth + totalWidth) + "px"; } else { this.horiz.style.display = ""; this.horiz.firstChild.style.width = "0"; } if (!this.checkedZeroWidth && measure.clientHeight > 0) { if (sWidth == 0) { this.zeroWidthHack(); } this.checkedZeroWidth = true; } return {right: needsV ? sWidth : 0, bottom: needsH ? sWidth : 0} }; NativeScrollbars.prototype.setScrollLeft = function (pos) { if (this.horiz.scrollLeft != pos) { this.horiz.scrollLeft = pos; } if (this.disableHoriz) { this.enableZeroWidthBar(this.horiz, this.disableHoriz, "horiz"); } }; NativeScrollbars.prototype.setScrollTop = function (pos) { if (this.vert.scrollTop != pos) { this.vert.scrollTop = pos; } if (this.disableVert) { this.enableZeroWidthBar(this.vert, this.disableVert, "vert"); } }; NativeScrollbars.prototype.zeroWidthHack = function () { var w = mac && !mac_geMountainLion ? "12px" : "18px"; this.horiz.style.height = this.vert.style.width = w; this.horiz.style.pointerEvents = this.vert.style.pointerEvents = "none"; this.disableHoriz = new Delayed; this.disableVert = new Delayed; }; NativeScrollbars.prototype.enableZeroWidthBar = function (bar, delay, type) { bar.style.pointerEvents = "auto"; function maybeDisable() { // To find out whether the scrollbar is still visible, we // check whether the element under the pixel in the bottom // right corner of the scrollbar box is the scrollbar box // itself (when the bar is still visible) or its filler child // (when the bar is hidden). If it is still visible, we keep // it enabled, if it's hidden, we disable pointer events. var box = bar.getBoundingClientRect(); var elt$$1 = type == "vert" ? document.elementFromPoint(box.right - 1, (box.top + box.bottom) / 2) : document.elementFromPoint((box.right + box.left) / 2, box.bottom - 1); if (elt$$1 != bar) { bar.style.pointerEvents = "none"; } else { delay.set(1000, maybeDisable); } } delay.set(1000, maybeDisable); }; NativeScrollbars.prototype.clear = function () { var parent = this.horiz.parentNode; parent.removeChild(this.horiz); parent.removeChild(this.vert); }; var NullScrollbars = function () {}; NullScrollbars.prototype.update = function () { return {bottom: 0, right: 0} }; NullScrollbars.prototype.setScrollLeft = function () {}; NullScrollbars.prototype.setScrollTop = function () {}; NullScrollbars.prototype.clear = function () {}; function updateScrollbars(cm, measure) { if (!measure) { measure = measureForScrollbars(cm); } var startWidth = cm.display.barWidth, startHeight = cm.display.barHeight; updateScrollbarsInner(cm, measure); for (var i = 0; i < 4 && startWidth != cm.display.barWidth || startHeight != cm.display.barHeight; i++) { if (startWidth != cm.display.barWidth && cm.options.lineWrapping) { updateHeightsInViewport(cm); } updateScrollbarsInner(cm, measureForScrollbars(cm)); startWidth = cm.display.barWidth; startHeight = cm.display.barHeight; } } // Re-synchronize the fake scrollbars with the actual size of the // content. function updateScrollbarsInner(cm, measure) { var d = cm.display; var sizes = d.scrollbars.update(measure); d.sizer.style.paddingRight = (d.barWidth = sizes.right) + "px"; d.sizer.style.paddingBottom = (d.barHeight = sizes.bottom) + "px"; d.heightForcer.style.borderBottom = sizes.bottom + "px solid transparent"; if (sizes.right && sizes.bottom) { d.scrollbarFiller.style.display = "block"; d.scrollbarFiller.style.height = sizes.bottom + "px"; d.scrollbarFiller.style.width = sizes.right + "px"; } else { d.scrollbarFiller.style.display = ""; } if (sizes.bottom && cm.options.coverGutterNextToScrollbar && cm.options.fixedGutter) { d.gutterFiller.style.display = "block"; d.gutterFiller.style.height = sizes.bottom + "px"; d.gutterFiller.style.width = measure.gutterWidth + "px"; } else { d.gutterFiller.style.display = ""; } } var scrollbarModel = {"native": NativeScrollbars, "null": NullScrollbars}; function initScrollbars(cm) { if (cm.display.scrollbars) { cm.display.scrollbars.clear(); if (cm.display.scrollbars.addClass) { rmClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } cm.display.scrollbars = new scrollbarModel[cm.options.scrollbarStyle](function (node) { cm.display.wrapper.insertBefore(node, cm.display.scrollbarFiller); // Prevent clicks in the scrollbars from killing focus on(node, "mousedown", function () { if (cm.state.focused) { setTimeout(function () { return cm.display.input.focus(); }, 0); } }); node.setAttribute("cm-not-content", "true"); }, function (pos, axis) { if (axis == "horizontal") { setScrollLeft(cm, pos); } else { updateScrollTop(cm, pos); } }, cm); if (cm.display.scrollbars.addClass) { addClass(cm.display.wrapper, cm.display.scrollbars.addClass); } } // Operations are used to wrap a series of changes to the editor // state in such a way that each change won't have to update the // cursor and display (which would be awkward, slow, and // error-prone). Instead, display updates are batched and then all // combined and executed at once. var nextOpId = 0; // Start a new operation. function startOperation(cm) { cm.curOp = { cm: cm, viewChanged: false, // Flag that indicates that lines might need to be redrawn startHeight: cm.doc.height, // Used to detect need to update scrollbar forceUpdate: false, // Used to force a redraw updateInput: null, // Whether to reset the input textarea typing: false, // Whether this reset should be careful to leave existing text (for compositing) changeObjs: null, // Accumulated changes, for firing change events cursorActivityHandlers: null, // Set of handlers to fire cursorActivity on cursorActivityCalled: 0, // Tracks which cursorActivity handlers have been called already selectionChanged: false, // Whether the selection needs to be redrawn updateMaxLine: false, // Set when the widest line needs to be determined anew scrollLeft: null, scrollTop: null, // Intermediate scroll position, not pushed to DOM yet scrollToPos: null, // Used to scroll to a specific position focus: false, id: ++nextOpId // Unique ID }; pushOperation(cm.curOp); } // Finish an operation, updating the display and signalling delayed events function endOperation(cm) { var op = cm.curOp; finishOperation(op, function (group) { for (var i = 0; i < group.ops.length; i++) { group.ops[i].cm.curOp = null; } endOperations(group); }); } // The DOM updates done when an operation finishes are batched so // that the minimum number of relayouts are required. function endOperations(group) { var ops = group.ops; for (var i = 0; i < ops.length; i++) // Read DOM { endOperation_R1(ops[i]); } for (var i$1 = 0; i$1 < ops.length; i$1++) // Write DOM (maybe) { endOperation_W1(ops[i$1]); } for (var i$2 = 0; i$2 < ops.length; i$2++) // Read DOM { endOperation_R2(ops[i$2]); } for (var i$3 = 0; i$3 < ops.length; i$3++) // Write DOM (maybe) { endOperation_W2(ops[i$3]); } for (var i$4 = 0; i$4 < ops.length; i$4++) // Read DOM { endOperation_finish(ops[i$4]); } } function endOperation_R1(op) { var cm = op.cm, display = cm.display; maybeClipScrollbars(cm); if (op.updateMaxLine) { findMaxLine(cm); } op.mustUpdate = op.viewChanged || op.forceUpdate || op.scrollTop != null || op.scrollToPos && (op.scrollToPos.from.line < display.viewFrom || op.scrollToPos.to.line >= display.viewTo) || display.maxLineChanged && cm.options.lineWrapping; op.update = op.mustUpdate && new DisplayUpdate(cm, op.mustUpdate && {top: op.scrollTop, ensure: op.scrollToPos}, op.forceUpdate); } function endOperation_W1(op) { op.updatedDisplay = op.mustUpdate && updateDisplayIfNeeded(op.cm, op.update); } function endOperation_R2(op) { var cm = op.cm, display = cm.display; if (op.updatedDisplay) { updateHeightsInViewport(cm); } op.barMeasure = measureForScrollbars(cm); // If the max line changed since it was last measured, measure it, // and ensure the document's width matches it. // updateDisplay_W2 will use these properties to do the actual resizing if (display.maxLineChanged && !cm.options.lineWrapping) { op.adjustWidthTo = measureChar(cm, display.maxLine, display.maxLine.text.length).left + 3; cm.display.sizerWidth = op.adjustWidthTo; op.barMeasure.scrollWidth = Math.max(display.scroller.clientWidth, display.sizer.offsetLeft + op.adjustWidthTo + scrollGap(cm) + cm.display.barWidth); op.maxScrollLeft = Math.max(0, display.sizer.offsetLeft + op.adjustWidthTo - displayWidth(cm)); } if (op.updatedDisplay || op.selectionChanged) { op.preparedSelection = display.input.prepareSelection(op.focus); } } function endOperation_W2(op) { var cm = op.cm; if (op.adjustWidthTo != null) { cm.display.sizer.style.minWidth = op.adjustWidthTo + "px"; if (op.maxScrollLeft < cm.doc.scrollLeft) { setScrollLeft(cm, Math.min(cm.display.scroller.scrollLeft, op.maxScrollLeft), true); } cm.display.maxLineChanged = false; } var takeFocus = op.focus && op.focus == activeElt() && (!document.hasFocus || document.hasFocus()); if (op.preparedSelection) { cm.display.input.showSelection(op.preparedSelection, takeFocus); } if (op.updatedDisplay || op.startHeight != cm.doc.height) { updateScrollbars(cm, op.barMeasure); } if (op.updatedDisplay) { setDocumentHeight(cm, op.barMeasure); } if (op.selectionChanged) { restartBlink(cm); } if (cm.state.focused && op.updateInput) { cm.display.input.reset(op.typing); } if (takeFocus) { ensureFocus(op.cm); } } function endOperation_finish(op) { var cm = op.cm, display = cm.display, doc = cm.doc; if (op.updatedDisplay) { postUpdateDisplay(cm, op.update); } // Abort mouse wheel delta measurement, when scrolling explicitly if (display.wheelStartX != null && (op.scrollTop != null || op.scrollLeft != null || op.scrollToPos)) { display.wheelStartX = display.wheelStartY = null; } // Propagate the scroll position to the actual DOM scroller if (op.scrollTop != null) { setScrollTop(cm, op.scrollTop, op.forceScroll); } if (op.scrollLeft != null) { setScrollLeft(cm, op.scrollLeft, true, true); } // If we need to scroll a specific position into view, do so. if (op.scrollToPos) { var rect = scrollPosIntoView(cm, clipPos(doc, op.scrollToPos.from), clipPos(doc, op.scrollToPos.to), op.scrollToPos.margin); maybeScrollWindow(cm, rect); } // Fire events for markers that are hidden/unidden by editing or // undoing var hidden = op.maybeHiddenMarkers, unhidden = op.maybeUnhiddenMarkers; if (hidden) { for (var i = 0; i < hidden.length; ++i) { if (!hidden[i].lines.length) { signal(hidden[i], "hide"); } } } if (unhidden) { for (var i$1 = 0; i$1 < unhidden.length; ++i$1) { if (unhidden[i$1].lines.length) { signal(unhidden[i$1], "unhide"); } } } if (display.wrapper.offsetHeight) { doc.scrollTop = cm.display.scroller.scrollTop; } // Fire change events, and delayed event handlers if (op.changeObjs) { signal(cm, "changes", cm, op.changeObjs); } if (op.update) { op.update.finish(); } } // Run the given function in an operation function runInOp(cm, f) { if (cm.curOp) { return f() } startOperation(cm); try { return f() } finally { endOperation(cm); } } // Wraps a function in an operation. Returns the wrapped function. function operation(cm, f) { return function() { if (cm.curOp) { return f.apply(cm, arguments) } startOperation(cm); try { return f.apply(cm, arguments) } finally { endOperation(cm); } } } // Used to add methods to editor and doc instances, wrapping them in // operations. function methodOp(f) { return function() { if (this.curOp) { return f.apply(this, arguments) } startOperation(this); try { return f.apply(this, arguments) } finally { endOperation(this); } } } function docMethodOp(f) { return function() { var cm = this.cm; if (!cm || cm.curOp) { return f.apply(this, arguments) } startOperation(cm); try { return f.apply(this, arguments) } finally { endOperation(cm); } } } // Updates the display.view data structure for a given change to the // document. From and to are in pre-change coordinates. Lendiff is // the amount of lines added or subtracted by the change. This is // used for changes that span multiple lines, or change the way // lines are divided into visual lines. regLineChange (below) // registers single-line changes. function regChange(cm, from, to, lendiff) { if (from == null) { from = cm.doc.first; } if (to == null) { to = cm.doc.first + cm.doc.size; } if (!lendiff) { lendiff = 0; } var display = cm.display; if (lendiff && to < display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers > from)) { display.updateLineNumbers = from; } cm.curOp.viewChanged = true; if (from >= display.viewTo) { // Change after if (sawCollapsedSpans && visualLineNo(cm.doc, from) < display.viewTo) { resetView(cm); } } else if (to <= display.viewFrom) { // Change before if (sawCollapsedSpans && visualLineEndNo(cm.doc, to + lendiff) > display.viewFrom) { resetView(cm); } else { display.viewFrom += lendiff; display.viewTo += lendiff; } } else if (from <= display.viewFrom && to >= display.viewTo) { // Full overlap resetView(cm); } else if (from <= display.viewFrom) { // Top overlap var cut = viewCuttingPoint(cm, to, to + lendiff, 1); if (cut) { display.view = display.view.slice(cut.index); display.viewFrom = cut.lineN; display.viewTo += lendiff; } else { resetView(cm); } } else if (to >= display.viewTo) { // Bottom overlap var cut$1 = viewCuttingPoint(cm, from, from, -1); if (cut$1) { display.view = display.view.slice(0, cut$1.index); display.viewTo = cut$1.lineN; } else { resetView(cm); } } else { // Gap in the middle var cutTop = viewCuttingPoint(cm, from, from, -1); var cutBot = viewCuttingPoint(cm, to, to + lendiff, 1); if (cutTop && cutBot) { display.view = display.view.slice(0, cutTop.index) .concat(buildViewArray(cm, cutTop.lineN, cutBot.lineN)) .concat(display.view.slice(cutBot.index)); display.viewTo += lendiff; } else { resetView(cm); } } var ext = display.externalMeasured; if (ext) { if (to < ext.lineN) { ext.lineN += lendiff; } else if (from < ext.lineN + ext.size) { display.externalMeasured = null; } } } // Register a change to a single line. Type must be one of "text", // "gutter", "class", "widget" function regLineChange(cm, line, type) { cm.curOp.viewChanged = true; var display = cm.display, ext = cm.display.externalMeasured; if (ext && line >= ext.lineN && line < ext.lineN + ext.size) { display.externalMeasured = null; } if (line < display.viewFrom || line >= display.viewTo) { return } var lineView = display.view[findViewIndex(cm, line)]; if (lineView.node == null) { return } var arr = lineView.changes || (lineView.changes = []); if (indexOf(arr, type) == -1) { arr.push(type); } } // Clear the view. function resetView(cm) { cm.display.viewFrom = cm.display.viewTo = cm.doc.first; cm.display.view = []; cm.display.viewOffset = 0; } function viewCuttingPoint(cm, oldN, newN, dir) { var index = findViewIndex(cm, oldN), diff, view = cm.display.view; if (!sawCollapsedSpans || newN == cm.doc.first + cm.doc.size) { return {index: index, lineN: newN} } var n = cm.display.viewFrom; for (var i = 0; i < index; i++) { n += view[i].size; } if (n != oldN) { if (dir > 0) { if (index == view.length - 1) { return null } diff = (n + view[index].size) - oldN; index++; } else { diff = n - oldN; } oldN += diff; newN += diff; } while (visualLineNo(cm.doc, newN) != newN) { if (index == (dir < 0 ? 0 : view.length - 1)) { return null } newN += dir * view[index - (dir < 0 ? 1 : 0)].size; index += dir; } return {index: index, lineN: newN} } // Force the view to cover a given range, adding empty view element // or clipping off existing ones as needed. function adjustView(cm, from, to) { var display = cm.display, view = display.view; if (view.length == 0 || from >= display.viewTo || to <= display.viewFrom) { display.view = buildViewArray(cm, from, to); display.viewFrom = from; } else { if (display.viewFrom > from) { display.view = buildViewArray(cm, from, display.viewFrom).concat(display.view); } else if (display.viewFrom < from) { display.view = display.view.slice(findViewIndex(cm, from)); } display.viewFrom = from; if (display.viewTo < to) { display.view = display.view.concat(buildViewArray(cm, display.viewTo, to)); } else if (display.viewTo > to) { display.view = display.view.slice(0, findViewIndex(cm, to)); } } display.viewTo = to; } // Count the number of lines in the view whose DOM representation is // out of date (or nonexistent). function countDirtyView(cm) { var view = cm.display.view, dirty = 0; for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (!lineView.hidden && (!lineView.node || lineView.changes)) { ++dirty; } } return dirty } // HIGHLIGHT WORKER function startWorker(cm, time) { if (cm.doc.highlightFrontier < cm.display.viewTo) { cm.state.highlight.set(time, bind(highlightWorker, cm)); } } function highlightWorker(cm) { var doc = cm.doc; if (doc.highlightFrontier >= cm.display.viewTo) { return } var end = +new Date + cm.options.workTime; var context = getContextBefore(cm, doc.highlightFrontier); var changedLines = []; doc.iter(context.line, Math.min(doc.first + doc.size, cm.display.viewTo + 500), function (line) { if (context.line >= cm.display.viewFrom) { // Visible var oldStyles = line.styles; var resetState = line.text.length > cm.options.maxHighlightLength ? copyState(doc.mode, context.state) : null; var highlighted = highlightLine(cm, line, context, true); if (resetState) { context.state = resetState; } line.styles = highlighted.styles; var oldCls = line.styleClasses, newCls = highlighted.classes; if (newCls) { line.styleClasses = newCls; } else if (oldCls) { line.styleClasses = null; } var ischange = !oldStyles || oldStyles.length != line.styles.length || oldCls != newCls && (!oldCls || !newCls || oldCls.bgClass != newCls.bgClass || oldCls.textClass != newCls.textClass); for (var i = 0; !ischange && i < oldStyles.length; ++i) { ischange = oldStyles[i] != line.styles[i]; } if (ischange) { changedLines.push(context.line); } line.stateAfter = context.save(); context.nextLine(); } else { if (line.text.length <= cm.options.maxHighlightLength) { processLine(cm, line.text, context); } line.stateAfter = context.line % 5 == 0 ? context.save() : null; context.nextLine(); } if (+new Date > end) { startWorker(cm, cm.options.workDelay); return true } }); doc.highlightFrontier = context.line; doc.modeFrontier = Math.max(doc.modeFrontier, context.line); if (changedLines.length) { runInOp(cm, function () { for (var i = 0; i < changedLines.length; i++) { regLineChange(cm, changedLines[i], "text"); } }); } } // DISPLAY DRAWING var DisplayUpdate = function(cm, viewport, force) { var display = cm.display; this.viewport = viewport; // Store some values that we'll need later (but don't want to force a relayout for) this.visible = visibleLines(display, cm.doc, viewport); this.editorIsHidden = !display.wrapper.offsetWidth; this.wrapperHeight = display.wrapper.clientHeight; this.wrapperWidth = display.wrapper.clientWidth; this.oldDisplayWidth = displayWidth(cm); this.force = force; this.dims = getDimensions(cm); this.events = []; }; DisplayUpdate.prototype.signal = function (emitter, type) { if (hasHandler(emitter, type)) { this.events.push(arguments); } }; DisplayUpdate.prototype.finish = function () { var this$1 = this; for (var i = 0; i < this.events.length; i++) { signal.apply(null, this$1.events[i]); } }; function maybeClipScrollbars(cm) { var display = cm.display; if (!display.scrollbarsClipped && display.scroller.offsetWidth) { display.nativeBarWidth = display.scroller.offsetWidth - display.scroller.clientWidth; display.heightForcer.style.height = scrollGap(cm) + "px"; display.sizer.style.marginBottom = -display.nativeBarWidth + "px"; display.sizer.style.borderRightWidth = scrollGap(cm) + "px"; display.scrollbarsClipped = true; } } function selectionSnapshot(cm) { if (cm.hasFocus()) { return null } var active = activeElt(); if (!active || !contains(cm.display.lineDiv, active)) { return null } var result = {activeElt: active}; if (window.getSelection) { var sel = window.getSelection(); if (sel.anchorNode && sel.extend && contains(cm.display.lineDiv, sel.anchorNode)) { result.anchorNode = sel.anchorNode; result.anchorOffset = sel.anchorOffset; result.focusNode = sel.focusNode; result.focusOffset = sel.focusOffset; } } return result } function restoreSelection(snapshot) { if (!snapshot || !snapshot.activeElt || snapshot.activeElt == activeElt()) { return } snapshot.activeElt.focus(); if (snapshot.anchorNode && contains(document.body, snapshot.anchorNode) && contains(document.body, snapshot.focusNode)) { var sel = window.getSelection(), range$$1 = document.createRange(); range$$1.setEnd(snapshot.anchorNode, snapshot.anchorOffset); range$$1.collapse(false); sel.removeAllRanges(); sel.addRange(range$$1); sel.extend(snapshot.focusNode, snapshot.focusOffset); } } // Does the actual updating of the line display. Bails out // (returning false) when there is nothing to be done and forced is // false. function updateDisplayIfNeeded(cm, update) { var display = cm.display, doc = cm.doc; if (update.editorIsHidden) { resetView(cm); return false } // Bail out if the visible area is already rendered and nothing changed. if (!update.force && update.visible.from >= display.viewFrom && update.visible.to <= display.viewTo && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo) && display.renderedView == display.view && countDirtyView(cm) == 0) { return false } if (maybeUpdateLineNumberWidth(cm)) { resetView(cm); update.dims = getDimensions(cm); } // Compute a suitable new viewport (from & to) var end = doc.first + doc.size; var from = Math.max(update.visible.from - cm.options.viewportMargin, doc.first); var to = Math.min(end, update.visible.to + cm.options.viewportMargin); if (display.viewFrom < from && from - display.viewFrom < 20) { from = Math.max(doc.first, display.viewFrom); } if (display.viewTo > to && display.viewTo - to < 20) { to = Math.min(end, display.viewTo); } if (sawCollapsedSpans) { from = visualLineNo(cm.doc, from); to = visualLineEndNo(cm.doc, to); } var different = from != display.viewFrom || to != display.viewTo || display.lastWrapHeight != update.wrapperHeight || display.lastWrapWidth != update.wrapperWidth; adjustView(cm, from, to); display.viewOffset = heightAtLine(getLine(cm.doc, display.viewFrom)); // Position the mover div to align with the current scroll position cm.display.mover.style.top = display.viewOffset + "px"; var toUpdate = countDirtyView(cm); if (!different && toUpdate == 0 && !update.force && display.renderedView == display.view && (display.updateLineNumbers == null || display.updateLineNumbers >= display.viewTo)) { return false } // For big changes, we hide the enclosing element during the // update, since that speeds up the operations on most browsers. var selSnapshot = selectionSnapshot(cm); if (toUpdate > 4) { display.lineDiv.style.display = "none"; } patchDisplay(cm, display.updateLineNumbers, update.dims); if (toUpdate > 4) { display.lineDiv.style.display = ""; } display.renderedView = display.view; // There might have been a widget with a focused element that got // hidden or updated, if so re-focus it. restoreSelection(selSnapshot); // Prevent selection and cursors from interfering with the scroll // width and height. removeChildren(display.cursorDiv); removeChildren(display.selectionDiv); display.gutters.style.height = display.sizer.style.minHeight = 0; if (different) { display.lastWrapHeight = update.wrapperHeight; display.lastWrapWidth = update.wrapperWidth; startWorker(cm, 400); } display.updateLineNumbers = null; return true } function postUpdateDisplay(cm, update) { var viewport = update.viewport; for (var first = true;; first = false) { if (!first || !cm.options.lineWrapping || update.oldDisplayWidth == displayWidth(cm)) { // Clip forced viewport to actual scrollable area. if (viewport && viewport.top != null) { viewport = {top: Math.min(cm.doc.height + paddingVert(cm.display) - displayHeight(cm), viewport.top)}; } // Updated line heights might result in the drawn area not // actually covering the viewport. Keep looping until it does. update.visible = visibleLines(cm.display, cm.doc, viewport); if (update.visible.from >= cm.display.viewFrom && update.visible.to <= cm.display.viewTo) { break } } if (!updateDisplayIfNeeded(cm, update)) { break } updateHeightsInViewport(cm); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.force = false; } update.signal(cm, "update", cm); if (cm.display.viewFrom != cm.display.reportedViewFrom || cm.display.viewTo != cm.display.reportedViewTo) { update.signal(cm, "viewportChange", cm, cm.display.viewFrom, cm.display.viewTo); cm.display.reportedViewFrom = cm.display.viewFrom; cm.display.reportedViewTo = cm.display.viewTo; } } function updateDisplaySimple(cm, viewport) { var update = new DisplayUpdate(cm, viewport); if (updateDisplayIfNeeded(cm, update)) { updateHeightsInViewport(cm); postUpdateDisplay(cm, update); var barMeasure = measureForScrollbars(cm); updateSelection(cm); updateScrollbars(cm, barMeasure); setDocumentHeight(cm, barMeasure); update.finish(); } } // Sync the actual display DOM structure with display.view, removing // nodes for lines that are no longer in view, and creating the ones // that are not there yet, and updating the ones that are out of // date. function patchDisplay(cm, updateNumbersFrom, dims) { var display = cm.display, lineNumbers = cm.options.lineNumbers; var container = display.lineDiv, cur = container.firstChild; function rm(node) { var next = node.nextSibling; // Works around a throw-scroll bug in OS X Webkit if (webkit && mac && cm.display.currentWheelTarget == node) { node.style.display = "none"; } else { node.parentNode.removeChild(node); } return next } var view = display.view, lineN = display.viewFrom; // Loop over the elements in the view, syncing cur (the DOM nodes // in display.lineDiv) with the view as we go. for (var i = 0; i < view.length; i++) { var lineView = view[i]; if (lineView.hidden) { } else if (!lineView.node || lineView.node.parentNode != container) { // Not drawn yet var node = buildLineElement(cm, lineView, lineN, dims); container.insertBefore(node, cur); } else { // Already drawn while (cur != lineView.node) { cur = rm(cur); } var updateNumber = lineNumbers && updateNumbersFrom != null && updateNumbersFrom <= lineN && lineView.lineNumber; if (lineView.changes) { if (indexOf(lineView.changes, "gutter") > -1) { updateNumber = false; } updateLineForChanges(cm, lineView, lineN, dims); } if (updateNumber) { removeChildren(lineView.lineNumber); lineView.lineNumber.appendChild(document.createTextNode(lineNumberFor(cm.options, lineN))); } cur = lineView.node.nextSibling; } lineN += lineView.size; } while (cur) { cur = rm(cur); } } function updateGutterSpace(cm) { var width = cm.display.gutters.offsetWidth; cm.display.sizer.style.marginLeft = width + "px"; } function setDocumentHeight(cm, measure) { cm.display.sizer.style.minHeight = measure.docHeight + "px"; cm.display.heightForcer.style.top = measure.docHeight + "px"; cm.display.gutters.style.height = (measure.docHeight + cm.display.barHeight + scrollGap(cm)) + "px"; } // Rebuild the gutter elements, ensure the margin to the left of the // code matches their width. function updateGutters(cm) { var gutters = cm.display.gutters, specs = cm.options.gutters; removeChildren(gutters); var i = 0; for (; i < specs.length; ++i) { var gutterClass = specs[i]; var gElt = gutters.appendChild(elt("div", null, "CodeMirror-gutter " + gutterClass)); if (gutterClass == "CodeMirror-linenumbers") { cm.display.lineGutter = gElt; gElt.style.width = (cm.display.lineNumWidth || 1) + "px"; } } gutters.style.display = i ? "" : "none"; updateGutterSpace(cm); } // Make sure the gutters options contains the element // "CodeMirror-linenumbers" when the lineNumbers option is true. function setGuttersForLineNumbers(options) { var found = indexOf(options.gutters, "CodeMirror-linenumbers"); if (found == -1 && options.lineNumbers) { options.gutters = options.gutters.concat(["CodeMirror-linenumbers"]); } else if (found > -1 && !options.lineNumbers) { options.gutters = options.gutters.slice(0); options.gutters.splice(found, 1); } } // Since the delta values reported on mouse wheel events are // unstandardized between browsers and even browser versions, and // generally horribly unpredictable, this code starts by measuring // the scroll effect that the first few mouse wheel events have, // and, from that, detects the way it can convert deltas to pixel // offsets afterwards. // // The reason we want to know the amount a wheel event will scroll // is that it gives us a chance to update the display before the // actual scrolling happens, reducing flickering. var wheelSamples = 0; var wheelPixelsPerUnit = null; // Fill in a browser-detected starting value on browsers where we // know one. These don't have to be accurate -- the result of them // being wrong would just be a slight flicker on the first wheel // scroll (if it is large enough). if (ie) { wheelPixelsPerUnit = -.53; } else if (gecko) { wheelPixelsPerUnit = 15; } else if (chrome) { wheelPixelsPerUnit = -.7; } else if (safari) { wheelPixelsPerUnit = -1/3; } function wheelEventDelta(e) { var dx = e.wheelDeltaX, dy = e.wheelDeltaY; if (dx == null && e.detail && e.axis == e.HORIZONTAL_AXIS) { dx = e.detail; } if (dy == null && e.detail && e.axis == e.VERTICAL_AXIS) { dy = e.detail; } else if (dy == null) { dy = e.wheelDelta; } return {x: dx, y: dy} } function wheelEventPixels(e) { var delta = wheelEventDelta(e); delta.x *= wheelPixelsPerUnit; delta.y *= wheelPixelsPerUnit; return delta } function onScrollWheel(cm, e) { var delta = wheelEventDelta(e), dx = delta.x, dy = delta.y; var display = cm.display, scroll = display.scroller; // Quit if there's nothing to scroll here var canScrollX = scroll.scrollWidth > scroll.clientWidth; var canScrollY = scroll.scrollHeight > scroll.clientHeight; if (!(dx && canScrollX || dy && canScrollY)) { return } // Webkit browsers on OS X abort momentum scrolls when the target // of the scroll event is removed from the scrollable element. // This hack (see related code in patchDisplay) makes sure the // element is kept around. if (dy && mac && webkit) { outer: for (var cur = e.target, view = display.view; cur != scroll; cur = cur.parentNode) { for (var i = 0; i < view.length; i++) { if (view[i].node == cur) { cm.display.currentWheelTarget = cur; break outer } } } } // On some browsers, horizontal scrolling will cause redraws to // happen before the gutter has been realigned, causing it to // wriggle around in a most unseemly way. When we have an // estimated pixels/delta value, we just handle horizontal // scrolling entirely here. It'll be slightly off from native, but // better than glitching out. if (dx && !gecko && !presto && wheelPixelsPerUnit != null) { if (dy && canScrollY) { updateScrollTop(cm, Math.max(0, scroll.scrollTop + dy * wheelPixelsPerUnit)); } setScrollLeft(cm, Math.max(0, scroll.scrollLeft + dx * wheelPixelsPerUnit)); // Only prevent default scrolling if vertical scrolling is // actually possible. Otherwise, it causes vertical scroll // jitter on OSX trackpads when deltaX is small and deltaY // is large (issue #3579) if (!dy || (dy && canScrollY)) { e_preventDefault(e); } display.wheelStartX = null; // Abort measurement, if in progress return } // 'Project' the visible viewport to cover the area that is being // scrolled into view (if we know enough to estimate it). if (dy && wheelPixelsPerUnit != null) { var pixels = dy * wheelPixelsPerUnit; var top = cm.doc.scrollTop, bot = top + display.wrapper.clientHeight; if (pixels < 0) { top = Math.max(0, top + pixels - 50); } else { bot = Math.min(cm.doc.height, bot + pixels + 50); } updateDisplaySimple(cm, {top: top, bottom: bot}); } if (wheelSamples < 20) { if (display.wheelStartX == null) { display.wheelStartX = scroll.scrollLeft; display.wheelStartY = scroll.scrollTop; display.wheelDX = dx; display.wheelDY = dy; setTimeout(function () { if (display.wheelStartX == null) { return } var movedX = scroll.scrollLeft - display.wheelStartX; var movedY = scroll.scrollTop - display.wheelStartY; var sample = (movedY && display.wheelDY && movedY / display.wheelDY) || (movedX && display.wheelDX && movedX / display.wheelDX); display.wheelStartX = display.wheelStartY = null; if (!sample) { return } wheelPixelsPerUnit = (wheelPixelsPerUnit * wheelSamples + sample) / (wheelSamples + 1); ++wheelSamples; }, 200); } else { display.wheelDX += dx; display.wheelDY += dy; } } } // Selection objects are immutable. A new one is created every time // the selection changes. A selection is one or more non-overlapping // (and non-touching) ranges, sorted, and an integer that indicates // which one is the primary selection (the one that's scrolled into // view, that getCursor returns, etc). var Selection = function(ranges, primIndex) { this.ranges = ranges; this.primIndex = primIndex; }; Selection.prototype.primary = function () { return this.ranges[this.primIndex] }; Selection.prototype.equals = function (other) { var this$1 = this; if (other == this) { return true } if (other.primIndex != this.primIndex || other.ranges.length != this.ranges.length) { return false } for (var i = 0; i < this.ranges.length; i++) { var here = this$1.ranges[i], there = other.ranges[i]; if (!equalCursorPos(here.anchor, there.anchor) || !equalCursorPos(here.head, there.head)) { return false } } return true }; Selection.prototype.deepCopy = function () { var this$1 = this; var out = []; for (var i = 0; i < this.ranges.length; i++) { out[i] = new Range(copyPos(this$1.ranges[i].anchor), copyPos(this$1.ranges[i].head)); } return new Selection(out, this.primIndex) }; Selection.prototype.somethingSelected = function () { var this$1 = this; for (var i = 0; i < this.ranges.length; i++) { if (!this$1.ranges[i].empty()) { return true } } return false }; Selection.prototype.contains = function (pos, end) { var this$1 = this; if (!end) { end = pos; } for (var i = 0; i < this.ranges.length; i++) { var range = this$1.ranges[i]; if (cmp(end, range.from()) >= 0 && cmp(pos, range.to()) <= 0) { return i } } return -1 }; var Range = function(anchor, head) { this.anchor = anchor; this.head = head; }; Range.prototype.from = function () { return minPos(this.anchor, this.head) }; Range.prototype.to = function () { return maxPos(this.anchor, this.head) }; Range.prototype.empty = function () { return this.head.line == this.anchor.line && this.head.ch == this.anchor.ch }; // Take an unsorted, potentially overlapping set of ranges, and // build a selection out of it. 'Consumes' ranges array (modifying // it). function normalizeSelection(ranges, primIndex) { var prim = ranges[primIndex]; ranges.sort(function (a, b) { return cmp(a.from(), b.from()); }); primIndex = indexOf(ranges, prim); for (var i = 1; i < ranges.length; i++) { var cur = ranges[i], prev = ranges[i - 1]; if (cmp(prev.to(), cur.from()) >= 0) { var from = minPos(prev.from(), cur.from()), to = maxPos(prev.to(), cur.to()); var inv = prev.empty() ? cur.from() == cur.head : prev.from() == prev.head; if (i <= primIndex) { --primIndex; } ranges.splice(--i, 2, new Range(inv ? to : from, inv ? from : to)); } } return new Selection(ranges, primIndex) } function simpleSelection(anchor, head) { return new Selection([new Range(anchor, head || anchor)], 0) } // Compute the position of the end of a change (its 'to' property // refers to the pre-change end). function changeEnd(change) { if (!change.text) { return change.to } return Pos(change.from.line + change.text.length - 1, lst(change.text).length + (change.text.length == 1 ? change.from.ch : 0)) } // Adjust a position to refer to the post-change position of the // same text, or the end of the change if the change covers it. function adjustForChange(pos, change) { if (cmp(pos, change.from) < 0) { return pos } if (cmp(pos, change.to) <= 0) { return changeEnd(change) } var line = pos.line + change.text.length - (change.to.line - change.from.line) - 1, ch = pos.ch; if (pos.line == change.to.line) { ch += changeEnd(change).ch - change.to.ch; } return Pos(line, ch) } function computeSelAfterChange(doc, change) { var out = []; for (var i = 0; i < doc.sel.ranges.length; i++) { var range = doc.sel.ranges[i]; out.push(new Range(adjustForChange(range.anchor, change), adjustForChange(range.head, change))); } return normalizeSelection(out, doc.sel.primIndex) } function offsetPos(pos, old, nw) { if (pos.line == old.line) { return Pos(nw.line, pos.ch - old.ch + nw.ch) } else { return Pos(nw.line + (pos.line - old.line), pos.ch) } } // Used by replaceSelections to allow moving the selection to the // start or around the replaced test. Hint may be "start" or "around". function computeReplacedSel(doc, changes, hint) { var out = []; var oldPrev = Pos(doc.first, 0), newPrev = oldPrev; for (var i = 0; i < changes.length; i++) { var change = changes[i]; var from = offsetPos(change.from, oldPrev, newPrev); var to = offsetPos(changeEnd(change), oldPrev, newPrev); oldPrev = change.to; newPrev = to; if (hint == "around") { var range = doc.sel.ranges[i], inv = cmp(range.head, range.anchor) < 0; out[i] = new Range(inv ? to : from, inv ? from : to); } else { out[i] = new Range(from, from); } } return new Selection(out, doc.sel.primIndex) } // Used to get the editor into a consistent state again when options change. function loadMode(cm) { cm.doc.mode = getMode(cm.options, cm.doc.modeOption); resetModeState(cm); } function resetModeState(cm) { cm.doc.iter(function (line) { if (line.stateAfter) { line.stateAfter = null; } if (line.styles) { line.styles = null; } }); cm.doc.modeFrontier = cm.doc.highlightFrontier = cm.doc.first; startWorker(cm, 100); cm.state.modeGen++; if (cm.curOp) { regChange(cm); } } // DOCUMENT DATA STRUCTURE // By default, updates that start and end at the beginning of a line // are treated specially, in order to make the association of line // widgets and marker elements with the text behave more intuitive. function isWholeLineUpdate(doc, change) { return change.from.ch == 0 && change.to.ch == 0 && lst(change.text) == "" && (!doc.cm || doc.cm.options.wholeLineUpdateBefore) } // Perform a change on the document data structure. function updateDoc(doc, change, markedSpans, estimateHeight$$1) { function spansFor(n) {return markedSpans ? markedSpans[n] : null} function update(line, text, spans) { updateLine(line, text, spans, estimateHeight$$1); signalLater(line, "change", line, change); } function linesFor(start, end) { var result = []; for (var i = start; i < end; ++i) { result.push(new Line(text[i], spansFor(i), estimateHeight$$1)); } return result } var from = change.from, to = change.to, text = change.text; var firstLine = getLine(doc, from.line), lastLine = getLine(doc, to.line); var lastText = lst(text), lastSpans = spansFor(text.length - 1), nlines = to.line - from.line; // Adjust the line structure if (change.full) { doc.insert(0, linesFor(0, text.length)); doc.remove(text.length, doc.size - text.length); } else if (isWholeLineUpdate(doc, change)) { // This is a whole-line replace. Treated specially to make // sure line objects move the way they are supposed to. var added = linesFor(0, text.length - 1); update(lastLine, lastLine.text, lastSpans); if (nlines) { doc.remove(from.line, nlines); } if (added.length) { doc.insert(from.line, added); } } else if (firstLine == lastLine) { if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + lastText + firstLine.text.slice(to.ch), lastSpans); } else { var added$1 = linesFor(1, text.length - 1); added$1.push(new Line(lastText + firstLine.text.slice(to.ch), lastSpans, estimateHeight$$1)); update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); doc.insert(from.line + 1, added$1); } } else if (text.length == 1) { update(firstLine, firstLine.text.slice(0, from.ch) + text[0] + lastLine.text.slice(to.ch), spansFor(0)); doc.remove(from.line + 1, nlines); } else { update(firstLine, firstLine.text.slice(0, from.ch) + text[0], spansFor(0)); update(lastLine, lastText + lastLine.text.slice(to.ch), lastSpans); var added$2 = linesFor(1, text.length - 1); if (nlines > 1) { doc.remove(from.line + 1, nlines - 1); } doc.insert(from.line + 1, added$2); } signalLater(doc, "change", doc, change); } // Call f for all linked documents. function linkedDocs(doc, f, sharedHistOnly) { function propagate(doc, skip, sharedHist) { if (doc.linked) { for (var i = 0; i < doc.linked.length; ++i) { var rel = doc.linked[i]; if (rel.doc == skip) { continue } var shared = sharedHist && rel.sharedHist; if (sharedHistOnly && !shared) { continue } f(rel.doc, shared); propagate(rel.doc, doc, shared); } } } propagate(doc, null, true); } // Attach a document to an editor. function attachDoc(cm, doc) { if (doc.cm) { throw new Error("This document is already in use.") } cm.doc = doc; doc.cm = cm; estimateLineHeights(cm); loadMode(cm); setDirectionClass(cm); if (!cm.options.lineWrapping) { findMaxLine(cm); } cm.options.mode = doc.modeOption; regChange(cm); } function setDirectionClass(cm) { (cm.doc.direction == "rtl" ? addClass : rmClass)(cm.display.lineDiv, "CodeMirror-rtl"); } function directionChanged(cm) { runInOp(cm, function () { setDirectionClass(cm); regChange(cm); }); } function History(startGen) { // Arrays of change events and selections. Doing something adds an // event to done and clears undo. Undoing moves events from done // to undone, redoing moves them in the other direction. this.done = []; this.undone = []; this.undoDepth = Infinity; // Used to track when changes can be merged into a single undo // event this.lastModTime = this.lastSelTime = 0; this.lastOp = this.lastSelOp = null; this.lastOrigin = this.lastSelOrigin = null; // Used by the isClean() method this.generation = this.maxGeneration = startGen || 1; } // Create a history change event from an updateDoc-style change // object. function historyChangeFromChange(doc, change) { var histChange = {from: copyPos(change.from), to: changeEnd(change), text: getBetween(doc, change.from, change.to)}; attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); linkedDocs(doc, function (doc) { return attachLocalSpans(doc, histChange, change.from.line, change.to.line + 1); }, true); return histChange } // Pop all selection events off the end of a history array. Stop at // a change event. function clearSelectionEvents(array) { while (array.length) { var last = lst(array); if (last.ranges) { array.pop(); } else { break } } } // Find the top change event in the history. Pop off selection // events that are in the way. function lastChangeEvent(hist, force) { if (force) { clearSelectionEvents(hist.done); return lst(hist.done) } else if (hist.done.length && !lst(hist.done).ranges) { return lst(hist.done) } else if (hist.done.length > 1 && !hist.done[hist.done.length - 2].ranges) { hist.done.pop(); return lst(hist.done) } } // Register a change in the history. Merges changes that are within // a single operation, or are close together with an origin that // allows merging (starting with "+") into a single event. function addChangeToHistory(doc, change, selAfter, opId) { var hist = doc.history; hist.undone.length = 0; var time = +new Date, cur; var last; if ((hist.lastOp == opId || hist.lastOrigin == change.origin && change.origin && ((change.origin.charAt(0) == "+" && doc.cm && hist.lastModTime > time - doc.cm.options.historyEventDelay) || change.origin.charAt(0) == "*")) && (cur = lastChangeEvent(hist, hist.lastOp == opId))) { // Merge this change into the last event last = lst(cur.changes); if (cmp(change.from, change.to) == 0 && cmp(change.from, last.to) == 0) { // Optimized case for simple insertion -- don't want to add // new changesets for every character typed last.to = changeEnd(change); } else { // Add new sub-event cur.changes.push(historyChangeFromChange(doc, change)); } } else { // Can not be merged, start a new event. var before = lst(hist.done); if (!before || !before.ranges) { pushSelectionToHistory(doc.sel, hist.done); } cur = {changes: [historyChangeFromChange(doc, change)], generation: hist.generation}; hist.done.push(cur); while (hist.done.length > hist.undoDepth) { hist.done.shift(); if (!hist.done[0].ranges) { hist.done.shift(); } } } hist.done.push(selAfter); hist.generation = ++hist.maxGeneration; hist.lastModTime = hist.lastSelTime = time; hist.lastOp = hist.lastSelOp = opId; hist.lastOrigin = hist.lastSelOrigin = change.origin; if (!last) { signal(doc, "historyAdded"); } } function selectionEventCanBeMerged(doc, origin, prev, sel) { var ch = origin.charAt(0); return ch == "*" || ch == "+" && prev.ranges.length == sel.ranges.length && prev.somethingSelected() == sel.somethingSelected() && new Date - doc.history.lastSelTime <= (doc.cm ? doc.cm.options.historyEventDelay : 500) } // Called whenever the selection changes, sets the new selection as // the pending selection in the history, and pushes the old pending // selection into the 'done' array when it was significantly // different (in number of selected ranges, emptiness, or time). function addSelectionToHistory(doc, sel, opId, options) { var hist = doc.history, origin = options && options.origin; // A new event is started when the previous origin does not match // the current, or the origins don't allow matching. Origins // starting with * are always merged, those starting with + are // merged when similar and close together in time. if (opId == hist.lastSelOp || (origin && hist.lastSelOrigin == origin && (hist.lastModTime == hist.lastSelTime && hist.lastOrigin == origin || selectionEventCanBeMerged(doc, origin, lst(hist.done), sel)))) { hist.done[hist.done.length - 1] = sel; } else { pushSelectionToHistory(sel, hist.done); } hist.lastSelTime = +new Date; hist.lastSelOrigin = origin; hist.lastSelOp = opId; if (options && options.clearRedo !== false) { clearSelectionEvents(hist.undone); } } function pushSelectionToHistory(sel, dest) { var top = lst(dest); if (!(top && top.ranges && top.equals(sel))) { dest.push(sel); } } // Used to store marked span information in the history. function attachLocalSpans(doc, change, from, to) { var existing = change["spans_" + doc.id], n = 0; doc.iter(Math.max(doc.first, from), Math.min(doc.first + doc.size, to), function (line) { if (line.markedSpans) { (existing || (existing = change["spans_" + doc.id] = {}))[n] = line.markedSpans; } ++n; }); } // When un/re-doing restores text containing marked spans, those // that have been explicitly cleared should not be restored. function removeClearedSpans(spans) { if (!spans) { return null } var out; for (var i = 0; i < spans.length; ++i) { if (spans[i].marker.explicitlyCleared) { if (!out) { out = spans.slice(0, i); } } else if (out) { out.push(spans[i]); } } return !out ? spans : out.length ? out : null } // Retrieve and filter the old marked spans stored in a change event. function getOldSpans(doc, change) { var found = change["spans_" + doc.id]; if (!found) { return null } var nw = []; for (var i = 0; i < change.text.length; ++i) { nw.push(removeClearedSpans(found[i])); } return nw } // Used for un/re-doing changes from the history. Combines the // result of computing the existing spans with the set of spans that // existed in the history (so that deleting around a span and then // undoing brings back the span). function mergeOldSpans(doc, change) { var old = getOldSpans(doc, change); var stretched = stretchSpansOverChange(doc, change); if (!old) { return stretched } if (!stretched) { return old } for (var i = 0; i < old.length; ++i) { var oldCur = old[i], stretchCur = stretched[i]; if (oldCur && stretchCur) { spans: for (var j = 0; j < stretchCur.length; ++j) { var span = stretchCur[j]; for (var k = 0; k < oldCur.length; ++k) { if (oldCur[k].marker == span.marker) { continue spans } } oldCur.push(span); } } else if (stretchCur) { old[i] = stretchCur; } } return old } // Used both to provide a JSON-safe object in .getHistory, and, when // detaching a document, to split the history in two function copyHistoryArray(events, newGroup, instantiateSel) { var copy = []; for (var i = 0; i < events.length; ++i) { var event = events[i]; if (event.ranges) { copy.push(instantiateSel ? Selection.prototype.deepCopy.call(event) : event); continue } var changes = event.changes, newChanges = []; copy.push({changes: newChanges}); for (var j = 0; j < changes.length; ++j) { var change = changes[j], m = (void 0); newChanges.push({from: change.from, to: change.to, text: change.text}); if (newGroup) { for (var prop in change) { if (m = prop.match(/^spans_(\d+)$/)) { if (indexOf(newGroup, Number(m[1])) > -1) { lst(newChanges)[prop] = change[prop]; delete change[prop]; } } } } } } return copy } // The 'scroll' parameter given to many of these indicated whether // the new cursor position should be scrolled into view after // modifying the selection. // If shift is held or the extend flag is set, extends a range to // include a given position (and optionally a second position). // Otherwise, simply returns the range between the given positions. // Used for cursor motion and such. function extendRange(range, head, other, extend) { if (extend) { var anchor = range.anchor; if (other) { var posBefore = cmp(head, anchor) < 0; if (posBefore != (cmp(other, anchor) < 0)) { anchor = head; head = other; } else if (posBefore != (cmp(head, other) < 0)) { head = other; } } return new Range(anchor, head) } else { return new Range(other || head, head) } } // Extend the primary selection range, discard the rest. function extendSelection(doc, head, other, options, extend) { if (extend == null) { extend = doc.cm && (doc.cm.display.shift || doc.extend); } setSelection(doc, new Selection([extendRange(doc.sel.primary(), head, other, extend)], 0), options); } // Extend all selections (pos is an array of selections with length // equal the number of selections) function extendSelections(doc, heads, options) { var out = []; var extend = doc.cm && (doc.cm.display.shift || doc.extend); for (var i = 0; i < doc.sel.ranges.length; i++) { out[i] = extendRange(doc.sel.ranges[i], heads[i], null, extend); } var newSel = normalizeSelection(out, doc.sel.primIndex); setSelection(doc, newSel, options); } // Updates a single range in the selection. function replaceOneSelection(doc, i, range, options) { var ranges = doc.sel.ranges.slice(0); ranges[i] = range; setSelection(doc, normalizeSelection(ranges, doc.sel.primIndex), options); } // Reset the selection to a single range. function setSimpleSelection(doc, anchor, head, options) { setSelection(doc, simpleSelection(anchor, head), options); } // Give beforeSelectionChange handlers a change to influence a // selection update. function filterSelectionChange(doc, sel, options) { var obj = { ranges: sel.ranges, update: function(ranges) { var this$1 = this; this.ranges = []; for (var i = 0; i < ranges.length; i++) { this$1.ranges[i] = new Range(clipPos(doc, ranges[i].anchor), clipPos(doc, ranges[i].head)); } }, origin: options && options.origin }; signal(doc, "beforeSelectionChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeSelectionChange", doc.cm, obj); } if (obj.ranges != sel.ranges) { return normalizeSelection(obj.ranges, obj.ranges.length - 1) } else { return sel } } function setSelectionReplaceHistory(doc, sel, options) { var done = doc.history.done, last = lst(done); if (last && last.ranges) { done[done.length - 1] = sel; setSelectionNoUndo(doc, sel, options); } else { setSelection(doc, sel, options); } } // Set a new selection. function setSelection(doc, sel, options) { setSelectionNoUndo(doc, sel, options); addSelectionToHistory(doc, doc.sel, doc.cm ? doc.cm.curOp.id : NaN, options); } function setSelectionNoUndo(doc, sel, options) { if (hasHandler(doc, "beforeSelectionChange") || doc.cm && hasHandler(doc.cm, "beforeSelectionChange")) { sel = filterSelectionChange(doc, sel, options); } var bias = options && options.bias || (cmp(sel.primary().head, doc.sel.primary().head) < 0 ? -1 : 1); setSelectionInner(doc, skipAtomicInSelection(doc, sel, bias, true)); if (!(options && options.scroll === false) && doc.cm) { ensureCursorVisible(doc.cm); } } function setSelectionInner(doc, sel) { if (sel.equals(doc.sel)) { return } doc.sel = sel; if (doc.cm) { doc.cm.curOp.updateInput = doc.cm.curOp.selectionChanged = true; signalCursorActivity(doc.cm); } signalLater(doc, "cursorActivity", doc); } // Verify that the selection does not partially select any atomic // marked ranges. function reCheckSelection(doc) { setSelectionInner(doc, skipAtomicInSelection(doc, doc.sel, null, false)); } // Return a selection that does not partially select any atomic // ranges. function skipAtomicInSelection(doc, sel, bias, mayClear) { var out; for (var i = 0; i < sel.ranges.length; i++) { var range = sel.ranges[i]; var old = sel.ranges.length == doc.sel.ranges.length && doc.sel.ranges[i]; var newAnchor = skipAtomic(doc, range.anchor, old && old.anchor, bias, mayClear); var newHead = skipAtomic(doc, range.head, old && old.head, bias, mayClear); if (out || newAnchor != range.anchor || newHead != range.head) { if (!out) { out = sel.ranges.slice(0, i); } out[i] = new Range(newAnchor, newHead); } } return out ? normalizeSelection(out, sel.primIndex) : sel } function skipAtomicInner(doc, pos, oldPos, dir, mayClear) { var line = getLine(doc, pos.line); if (line.markedSpans) { for (var i = 0; i < line.markedSpans.length; ++i) { var sp = line.markedSpans[i], m = sp.marker; if ((sp.from == null || (m.inclusiveLeft ? sp.from <= pos.ch : sp.from < pos.ch)) && (sp.to == null || (m.inclusiveRight ? sp.to >= pos.ch : sp.to > pos.ch))) { if (mayClear) { signal(m, "beforeCursorEnter"); if (m.explicitlyCleared) { if (!line.markedSpans) { break } else {--i; continue} } } if (!m.atomic) { continue } if (oldPos) { var near = m.find(dir < 0 ? 1 : -1), diff = (void 0); if (dir < 0 ? m.inclusiveRight : m.inclusiveLeft) { near = movePos(doc, near, -dir, near && near.line == pos.line ? line : null); } if (near && near.line == pos.line && (diff = cmp(near, oldPos)) && (dir < 0 ? diff < 0 : diff > 0)) { return skipAtomicInner(doc, near, pos, dir, mayClear) } } var far = m.find(dir < 0 ? -1 : 1); if (dir < 0 ? m.inclusiveLeft : m.inclusiveRight) { far = movePos(doc, far, dir, far.line == pos.line ? line : null); } return far ? skipAtomicInner(doc, far, pos, dir, mayClear) : null } } } return pos } // Ensure a given position is not inside an atomic range. function skipAtomic(doc, pos, oldPos, bias, mayClear) { var dir = bias || 1; var found = skipAtomicInner(doc, pos, oldPos, dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, dir, true)) || skipAtomicInner(doc, pos, oldPos, -dir, mayClear) || (!mayClear && skipAtomicInner(doc, pos, oldPos, -dir, true)); if (!found) { doc.cantEdit = true; return Pos(doc.first, 0) } return found } function movePos(doc, pos, dir, line) { if (dir < 0 && pos.ch == 0) { if (pos.line > doc.first) { return clipPos(doc, Pos(pos.line - 1)) } else { return null } } else if (dir > 0 && pos.ch == (line || getLine(doc, pos.line)).text.length) { if (pos.line < doc.first + doc.size - 1) { return Pos(pos.line + 1, 0) } else { return null } } else { return new Pos(pos.line, pos.ch + dir) } } function selectAll(cm) { cm.setSelection(Pos(cm.firstLine(), 0), Pos(cm.lastLine()), sel_dontScroll); } // UPDATING // Allow "beforeChange" event handlers to influence a change function filterChange(doc, change, update) { var obj = { canceled: false, from: change.from, to: change.to, text: change.text, origin: change.origin, cancel: function () { return obj.canceled = true; } }; if (update) { obj.update = function (from, to, text, origin) { if (from) { obj.from = clipPos(doc, from); } if (to) { obj.to = clipPos(doc, to); } if (text) { obj.text = text; } if (origin !== undefined) { obj.origin = origin; } }; } signal(doc, "beforeChange", doc, obj); if (doc.cm) { signal(doc.cm, "beforeChange", doc.cm, obj); } if (obj.canceled) { return null } return {from: obj.from, to: obj.to, text: obj.text, origin: obj.origin} } // Apply a change to a document, and add it to the document's // history, and propagating it to all linked documents. function makeChange(doc, change, ignoreReadOnly) { if (doc.cm) { if (!doc.cm.curOp) { return operation(doc.cm, makeChange)(doc, change, ignoreReadOnly) } if (doc.cm.state.suppressEdits) { return } } if (hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange")) { change = filterChange(doc, change, true); if (!change) { return } } // Possibly split or suppress the update based on the presence // of read-only spans in its range. var split = sawReadOnlySpans && !ignoreReadOnly && removeReadOnlyRanges(doc, change.from, change.to); if (split) { for (var i = split.length - 1; i >= 0; --i) { makeChangeInner(doc, {from: split[i].from, to: split[i].to, text: i ? [""] : change.text}); } } else { makeChangeInner(doc, change); } } function makeChangeInner(doc, change) { if (change.text.length == 1 && change.text[0] == "" && cmp(change.from, change.to) == 0) { return } var selAfter = computeSelAfterChange(doc, change); addChangeToHistory(doc, change, selAfter, doc.cm ? doc.cm.curOp.id : NaN); makeChangeSingleDoc(doc, change, selAfter, stretchSpansOverChange(doc, change)); var rebased = []; linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, stretchSpansOverChange(doc, change)); }); } // Revert a change stored in a document's history. function makeChangeFromHistory(doc, type, allowSelectionOnly) { if (doc.cm && doc.cm.state.suppressEdits && !allowSelectionOnly) { return } var hist = doc.history, event, selAfter = doc.sel; var source = type == "undo" ? hist.done : hist.undone, dest = type == "undo" ? hist.undone : hist.done; // Verify that there is a useable event (so that ctrl-z won't // needlessly clear selection events) var i = 0; for (; i < source.length; i++) { event = source[i]; if (allowSelectionOnly ? event.ranges && !event.equals(doc.sel) : !event.ranges) { break } } if (i == source.length) { return } hist.lastOrigin = hist.lastSelOrigin = null; for (;;) { event = source.pop(); if (event.ranges) { pushSelectionToHistory(event, dest); if (allowSelectionOnly && !event.equals(doc.sel)) { setSelection(doc, event, {clearRedo: false}); return } selAfter = event; } else { break } } // Build up a reverse change object to add to the opposite history // stack (redo when undoing, and vice versa). var antiChanges = []; pushSelectionToHistory(selAfter, dest); dest.push({changes: antiChanges, generation: hist.generation}); hist.generation = event.generation || ++hist.maxGeneration; var filter = hasHandler(doc, "beforeChange") || doc.cm && hasHandler(doc.cm, "beforeChange"); var loop = function ( i ) { var change = event.changes[i]; change.origin = type; if (filter && !filterChange(doc, change, false)) { source.length = 0; return {} } antiChanges.push(historyChangeFromChange(doc, change)); var after = i ? computeSelAfterChange(doc, change) : lst(source); makeChangeSingleDoc(doc, change, after, mergeOldSpans(doc, change)); if (!i && doc.cm) { doc.cm.scrollIntoView({from: change.from, to: changeEnd(change)}); } var rebased = []; // Propagate to the linked documents linkedDocs(doc, function (doc, sharedHist) { if (!sharedHist && indexOf(rebased, doc.history) == -1) { rebaseHist(doc.history, change); rebased.push(doc.history); } makeChangeSingleDoc(doc, change, null, mergeOldSpans(doc, change)); }); }; for (var i$1 = event.changes.length - 1; i$1 >= 0; --i$1) { var returned = loop( i$1 ); if ( returned ) return returned.v; } } // Sub-views need their line numbers shifted when text is added // above or below them in the parent document. function shiftDoc(doc, distance) { if (distance == 0) { return } doc.first += distance; doc.sel = new Selection(map(doc.sel.ranges, function (range) { return new Range( Pos(range.anchor.line + distance, range.anchor.ch), Pos(range.head.line + distance, range.head.ch) ); }), doc.sel.primIndex); if (doc.cm) { regChange(doc.cm, doc.first, doc.first - distance, distance); for (var d = doc.cm.display, l = d.viewFrom; l < d.viewTo; l++) { regLineChange(doc.cm, l, "gutter"); } } } // More lower-level change function, handling only a single document // (not linked ones). function makeChangeSingleDoc(doc, change, selAfter, spans) { if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, makeChangeSingleDoc)(doc, change, selAfter, spans) } if (change.to.line < doc.first) { shiftDoc(doc, change.text.length - 1 - (change.to.line - change.from.line)); return } if (change.from.line > doc.lastLine()) { return } // Clip the change to the size of this doc if (change.from.line < doc.first) { var shift = change.text.length - 1 - (doc.first - change.from.line); shiftDoc(doc, shift); change = {from: Pos(doc.first, 0), to: Pos(change.to.line + shift, change.to.ch), text: [lst(change.text)], origin: change.origin}; } var last = doc.lastLine(); if (change.to.line > last) { change = {from: change.from, to: Pos(last, getLine(doc, last).text.length), text: [change.text[0]], origin: change.origin}; } change.removed = getBetween(doc, change.from, change.to); if (!selAfter) { selAfter = computeSelAfterChange(doc, change); } if (doc.cm) { makeChangeSingleDocInEditor(doc.cm, change, spans); } else { updateDoc(doc, change, spans); } setSelectionNoUndo(doc, selAfter, sel_dontScroll); } // Handle the interaction of a change to a document with the editor // that this document is part of. function makeChangeSingleDocInEditor(cm, change, spans) { var doc = cm.doc, display = cm.display, from = change.from, to = change.to; var recomputeMaxLength = false, checkWidthStart = from.line; if (!cm.options.lineWrapping) { checkWidthStart = lineNo(visualLine(getLine(doc, from.line))); doc.iter(checkWidthStart, to.line + 1, function (line) { if (line == display.maxLine) { recomputeMaxLength = true; return true } }); } if (doc.sel.contains(change.from, change.to) > -1) { signalCursorActivity(cm); } updateDoc(doc, change, spans, estimateHeight(cm)); if (!cm.options.lineWrapping) { doc.iter(checkWidthStart, from.line + change.text.length, function (line) { var len = lineLength(line); if (len > display.maxLineLength) { display.maxLine = line; display.maxLineLength = len; display.maxLineChanged = true; recomputeMaxLength = false; } }); if (recomputeMaxLength) { cm.curOp.updateMaxLine = true; } } retreatFrontier(doc, from.line); startWorker(cm, 400); var lendiff = change.text.length - (to.line - from.line) - 1; // Remember that these lines changed, for updating the display if (change.full) { regChange(cm); } else if (from.line == to.line && change.text.length == 1 && !isWholeLineUpdate(cm.doc, change)) { regLineChange(cm, from.line, "text"); } else { regChange(cm, from.line, to.line + 1, lendiff); } var changesHandler = hasHandler(cm, "changes"), changeHandler = hasHandler(cm, "change"); if (changeHandler || changesHandler) { var obj = { from: from, to: to, text: change.text, removed: change.removed, origin: change.origin }; if (changeHandler) { signalLater(cm, "change", cm, obj); } if (changesHandler) { (cm.curOp.changeObjs || (cm.curOp.changeObjs = [])).push(obj); } } cm.display.selForContextMenu = null; } function replaceRange(doc, code, from, to, origin) { if (!to) { to = from; } if (cmp(to, from) < 0) { var tmp = to; to = from; from = tmp; } if (typeof code == "string") { code = doc.splitLines(code); } makeChange(doc, {from: from, to: to, text: code, origin: origin}); } // Rebasing/resetting history to deal with externally-sourced changes function rebaseHistSelSingle(pos, from, to, diff) { if (to < pos.line) { pos.line += diff; } else if (from < pos.line) { pos.line = from; pos.ch = 0; } } // Tries to rebase an array of history events given a change in the // document. If the change touches the same lines as the event, the // event, and everything 'behind' it, is discarded. If the change is // before the event, the event's positions are updated. Uses a // copy-on-write scheme for the positions, to avoid having to // reallocate them all on every rebase, but also avoid problems with // shared position objects being unsafely updated. function rebaseHistArray(array, from, to, diff) { for (var i = 0; i < array.length; ++i) { var sub = array[i], ok = true; if (sub.ranges) { if (!sub.copied) { sub = array[i] = sub.deepCopy(); sub.copied = true; } for (var j = 0; j < sub.ranges.length; j++) { rebaseHistSelSingle(sub.ranges[j].anchor, from, to, diff); rebaseHistSelSingle(sub.ranges[j].head, from, to, diff); } continue } for (var j$1 = 0; j$1 < sub.changes.length; ++j$1) { var cur = sub.changes[j$1]; if (to < cur.from.line) { cur.from = Pos(cur.from.line + diff, cur.from.ch); cur.to = Pos(cur.to.line + diff, cur.to.ch); } else if (from <= cur.to.line) { ok = false; break } } if (!ok) { array.splice(0, i + 1); i = 0; } } } function rebaseHist(hist, change) { var from = change.from.line, to = change.to.line, diff = change.text.length - (to - from) - 1; rebaseHistArray(hist.done, from, to, diff); rebaseHistArray(hist.undone, from, to, diff); } // Utility for applying a change to a line by handle or number, // returning the number and optionally registering the line as // changed. function changeLine(doc, handle, changeType, op) { var no = handle, line = handle; if (typeof handle == "number") { line = getLine(doc, clipLine(doc, handle)); } else { no = lineNo(handle); } if (no == null) { return null } if (op(line, no) && doc.cm) { regLineChange(doc.cm, no, changeType); } return line } // The document is represented as a BTree consisting of leaves, with // chunk of lines in them, and branches, with up to ten leaves or // other branch nodes below them. The top node is always a branch // node, and is the document object itself (meaning it has // additional methods and properties). // // All nodes have parent links. The tree is used both to go from // line numbers to line objects, and to go from objects to numbers. // It also indexes by height, and is used to convert between height // and line object, and to find the total height of the document. // // See also http://marijnhaverbeke.nl/blog/codemirror-line-tree.html function LeafChunk(lines) { var this$1 = this; this.lines = lines; this.parent = null; var height = 0; for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; height += lines[i].height; } this.height = height; } LeafChunk.prototype = { chunkSize: function chunkSize() { return this.lines.length }, // Remove the n lines at offset 'at'. removeInner: function removeInner(at, n) { var this$1 = this; for (var i = at, e = at + n; i < e; ++i) { var line = this$1.lines[i]; this$1.height -= line.height; cleanUpLine(line); signalLater(line, "delete"); } this.lines.splice(at, n); }, // Helper used to collapse a small branch into a single leaf. collapse: function collapse(lines) { lines.push.apply(lines, this.lines); }, // Insert the given array of lines at offset 'at', count them as // having the given height. insertInner: function insertInner(at, lines, height) { var this$1 = this; this.height += height; this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at)); for (var i = 0; i < lines.length; ++i) { lines[i].parent = this$1; } }, // Used to iterate over a part of the tree. iterN: function iterN(at, n, op) { var this$1 = this; for (var e = at + n; at < e; ++at) { if (op(this$1.lines[at])) { return true } } } }; function BranchChunk(children) { var this$1 = this; this.children = children; var size = 0, height = 0; for (var i = 0; i < children.length; ++i) { var ch = children[i]; size += ch.chunkSize(); height += ch.height; ch.parent = this$1; } this.size = size; this.height = height; this.parent = null; } BranchChunk.prototype = { chunkSize: function chunkSize() { return this.size }, removeInner: function removeInner(at, n) { var this$1 = this; this.size -= n; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at < sz) { var rm = Math.min(n, sz - at), oldHeight = child.height; child.removeInner(at, rm); this$1.height -= oldHeight - child.height; if (sz == rm) { this$1.children.splice(i--, 1); child.parent = null; } if ((n -= rm) == 0) { break } at = 0; } else { at -= sz; } } // If the result is smaller than 25 lines, ensure that it is a // single leaf node. if (this.size - n < 25 && (this.children.length > 1 || !(this.children[0] instanceof LeafChunk))) { var lines = []; this.collapse(lines); this.children = [new LeafChunk(lines)]; this.children[0].parent = this; } }, collapse: function collapse(lines) { var this$1 = this; for (var i = 0; i < this.children.length; ++i) { this$1.children[i].collapse(lines); } }, insertInner: function insertInner(at, lines, height) { var this$1 = this; this.size += lines.length; this.height += height; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at <= sz) { child.insertInner(at, lines, height); if (child.lines && child.lines.length > 50) { // To avoid memory thrashing when child.lines is huge (e.g. first view of a large file), it's never spliced. // Instead, small slices are taken. They're taken in order because sequential memory accesses are fastest. var remaining = child.lines.length % 25 + 25; for (var pos = remaining; pos < child.lines.length;) { var leaf = new LeafChunk(child.lines.slice(pos, pos += 25)); child.height -= leaf.height; this$1.children.splice(++i, 0, leaf); leaf.parent = this$1; } child.lines = child.lines.slice(0, remaining); this$1.maybeSpill(); } break } at -= sz; } }, // When a node has grown, check whether it should be split. maybeSpill: function maybeSpill() { if (this.children.length <= 10) { return } var me = this; do { var spilled = me.children.splice(me.children.length - 5, 5); var sibling = new BranchChunk(spilled); if (!me.parent) { // Become the parent node var copy = new BranchChunk(me.children); copy.parent = me; me.children = [copy, sibling]; me = copy; } else { me.size -= sibling.size; me.height -= sibling.height; var myIndex = indexOf(me.parent.children, me); me.parent.children.splice(myIndex + 1, 0, sibling); } sibling.parent = me.parent; } while (me.children.length > 10) me.parent.maybeSpill(); }, iterN: function iterN(at, n, op) { var this$1 = this; for (var i = 0; i < this.children.length; ++i) { var child = this$1.children[i], sz = child.chunkSize(); if (at < sz) { var used = Math.min(n, sz - at); if (child.iterN(at, used, op)) { return true } if ((n -= used) == 0) { break } at = 0; } else { at -= sz; } } } }; // Line widgets are block elements displayed above or below a line. var LineWidget = function(doc, node, options) { var this$1 = this; if (options) { for (var opt in options) { if (options.hasOwnProperty(opt)) { this$1[opt] = options[opt]; } } } this.doc = doc; this.node = node; }; LineWidget.prototype.clear = function () { var this$1 = this; var cm = this.doc.cm, ws = this.line.widgets, line = this.line, no = lineNo(line); if (no == null || !ws) { return } for (var i = 0; i < ws.length; ++i) { if (ws[i] == this$1) { ws.splice(i--, 1); } } if (!ws.length) { line.widgets = null; } var height = widgetHeight(this); updateLineHeight(line, Math.max(0, line.height - height)); if (cm) { runInOp(cm, function () { adjustScrollWhenAboveVisible(cm, line, -height); regLineChange(cm, no, "widget"); }); signalLater(cm, "lineWidgetCleared", cm, this, no); } }; LineWidget.prototype.changed = function () { var this$1 = this; var oldH = this.height, cm = this.doc.cm, line = this.line; this.height = null; var diff = widgetHeight(this) - oldH; if (!diff) { return } updateLineHeight(line, line.height + diff); if (cm) { runInOp(cm, function () { cm.curOp.forceUpdate = true; adjustScrollWhenAboveVisible(cm, line, diff); signalLater(cm, "lineWidgetChanged", cm, this$1, lineNo(line)); }); } }; eventMixin(LineWidget); function adjustScrollWhenAboveVisible(cm, line, diff) { if (heightAtLine(line) < ((cm.curOp && cm.curOp.scrollTop) || cm.doc.scrollTop)) { addToScrollTop(cm, diff); } } function addLineWidget(doc, handle, node, options) { var widget = new LineWidget(doc, node, options); var cm = doc.cm; if (cm && widget.noHScroll) { cm.display.alignWidgets = true; } changeLine(doc, handle, "widget", function (line) { var widgets = line.widgets || (line.widgets = []); if (widget.insertAt == null) { widgets.push(widget); } else { widgets.splice(Math.min(widgets.length - 1, Math.max(0, widget.insertAt)), 0, widget); } widget.line = line; if (cm && !lineIsHidden(doc, line)) { var aboveVisible = heightAtLine(line) < doc.scrollTop; updateLineHeight(line, line.height + widgetHeight(widget)); if (aboveVisible) { addToScrollTop(cm, widget.height); } cm.curOp.forceUpdate = true; } return true }); signalLater(cm, "lineWidgetAdded", cm, widget, typeof handle == "number" ? handle : lineNo(handle)); return widget } // TEXTMARKERS // Created with markText and setBookmark methods. A TextMarker is a // handle that can be used to clear or find a marked position in the // document. Line objects hold arrays (markedSpans) containing // {from, to, marker} object pointing to such marker objects, and // indicating that such a marker is present on that line. Multiple // lines may point to the same marker when it spans across lines. // The spans will have null for their from/to properties when the // marker continues beyond the start/end of the line. Markers have // links back to the lines they currently touch. // Collapsed markers have unique ids, in order to be able to order // them, which is needed for uniquely determining an outer marker // when they overlap (they may nest, but not partially overlap). var nextMarkerId = 0; var TextMarker = function(doc, type) { this.lines = []; this.type = type; this.doc = doc; this.id = ++nextMarkerId; }; // Clear the marker. TextMarker.prototype.clear = function () { var this$1 = this; if (this.explicitlyCleared) { return } var cm = this.doc.cm, withOp = cm && !cm.curOp; if (withOp) { startOperation(cm); } if (hasHandler(this, "clear")) { var found = this.find(); if (found) { signalLater(this, "clear", found.from, found.to); } } var min = null, max = null; for (var i = 0; i < this.lines.length; ++i) { var line = this$1.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this$1); if (cm && !this$1.collapsed) { regLineChange(cm, lineNo(line), "text"); } else if (cm) { if (span.to != null) { max = lineNo(line); } if (span.from != null) { min = lineNo(line); } } line.markedSpans = removeMarkedSpan(line.markedSpans, span); if (span.from == null && this$1.collapsed && !lineIsHidden(this$1.doc, line) && cm) { updateLineHeight(line, textHeight(cm.display)); } } if (cm && this.collapsed && !cm.options.lineWrapping) { for (var i$1 = 0; i$1 < this.lines.length; ++i$1) { var visual = visualLine(this$1.lines[i$1]), len = lineLength(visual); if (len > cm.display.maxLineLength) { cm.display.maxLine = visual; cm.display.maxLineLength = len; cm.display.maxLineChanged = true; } } } if (min != null && cm && this.collapsed) { regChange(cm, min, max + 1); } this.lines.length = 0; this.explicitlyCleared = true; if (this.atomic && this.doc.cantEdit) { this.doc.cantEdit = false; if (cm) { reCheckSelection(cm.doc); } } if (cm) { signalLater(cm, "markerCleared", cm, this, min, max); } if (withOp) { endOperation(cm); } if (this.parent) { this.parent.clear(); } }; // Find the position of the marker in the document. Returns a {from, // to} object by default. Side can be passed to get a specific side // -- 0 (both), -1 (left), or 1 (right). When lineObj is true, the // Pos objects returned contain a line object, rather than a line // number (used to prevent looking up the same line twice). TextMarker.prototype.find = function (side, lineObj) { var this$1 = this; if (side == null && this.type == "bookmark") { side = 1; } var from, to; for (var i = 0; i < this.lines.length; ++i) { var line = this$1.lines[i]; var span = getMarkedSpanFor(line.markedSpans, this$1); if (span.from != null) { from = Pos(lineObj ? line : lineNo(line), span.from); if (side == -1) { return from } } if (span.to != null) { to = Pos(lineObj ? line : lineNo(line), span.to); if (side == 1) { return to } } } return from && {from: from, to: to} }; // Signals that the marker's widget changed, and surrounding layout // should be recomputed. TextMarker.prototype.changed = function () { var this$1 = this; var pos = this.find(-1, true), widget = this, cm = this.doc.cm; if (!pos || !cm) { return } runInOp(cm, function () { var line = pos.line, lineN = lineNo(pos.line); var view = findViewForLine(cm, lineN); if (view) { clearLineMeasurementCacheFor(view); cm.curOp.selectionChanged = cm.curOp.forceUpdate = true; } cm.curOp.updateMaxLine = true; if (!lineIsHidden(widget.doc, line) && widget.height != null) { var oldHeight = widget.height; widget.height = null; var dHeight = widgetHeight(widget) - oldHeight; if (dHeight) { updateLineHeight(line, line.height + dHeight); } } signalLater(cm, "markerChanged", cm, this$1); }); }; TextMarker.prototype.attachLine = function (line) { if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp; if (!op.maybeHiddenMarkers || indexOf(op.maybeHiddenMarkers, this) == -1) { (op.maybeUnhiddenMarkers || (op.maybeUnhiddenMarkers = [])).push(this); } } this.lines.push(line); }; TextMarker.prototype.detachLine = function (line) { this.lines.splice(indexOf(this.lines, line), 1); if (!this.lines.length && this.doc.cm) { var op = this.doc.cm.curOp;(op.maybeHiddenMarkers || (op.maybeHiddenMarkers = [])).push(this); } }; eventMixin(TextMarker); // Create a marker, wire it up to the right lines, and function markText(doc, from, to, options, type) { // Shared markers (across linked documents) are handled separately // (markTextShared will call out to this again, once per // document). if (options && options.shared) { return markTextShared(doc, from, to, options, type) } // Ensure we are in an operation. if (doc.cm && !doc.cm.curOp) { return operation(doc.cm, markText)(doc, from, to, options, type) } var marker = new TextMarker(doc, type), diff = cmp(from, to); if (options) { copyObj(options, marker, false); } // Don't connect empty markers unless clearWhenEmpty is false if (diff > 0 || diff == 0 && marker.clearWhenEmpty !== false) { return marker } if (marker.replacedWith) { // Showing up as a widget implies collapsed (widget replaces text) marker.collapsed = true; marker.widgetNode = eltP("span", [marker.replacedWith], "CodeMirror-widget"); if (!options.handleMouseEvents) { marker.widgetNode.setAttribute("cm-ignore-events", "true"); } if (options.insertLeft) { marker.widgetNode.insertLeft = true; } } if (marker.collapsed) { if (conflictingCollapsedRange(doc, from.line, from, to, marker) || from.line != to.line && conflictingCollapsedRange(doc, to.line, from, to, marker)) { throw new Error("Inserting collapsed marker partially overlapping an existing one") } seeCollapsedSpans(); } if (marker.addToHistory) { addChangeToHistory(doc, {from: from, to: to, origin: "markText"}, doc.sel, NaN); } var curLine = from.line, cm = doc.cm, updateMaxLine; doc.iter(curLine, to.line + 1, function (line) { if (cm && marker.collapsed && !cm.options.lineWrapping && visualLine(line) == cm.display.maxLine) { updateMaxLine = true; } if (marker.collapsed && curLine != from.line) { updateLineHeight(line, 0); } addMarkedSpan(line, new MarkedSpan(marker, curLine == from.line ? from.ch : null, curLine == to.line ? to.ch : null)); ++curLine; }); // lineIsHidden depends on the presence of the spans, so needs a second pass if (marker.collapsed) { doc.iter(from.line, to.line + 1, function (line) { if (lineIsHidden(doc, line)) { updateLineHeight(line, 0); } }); } if (marker.clearOnEnter) { on(marker, "beforeCursorEnter", function () { return marker.clear(); }); } if (marker.readOnly) { seeReadOnlySpans(); if (doc.history.done.length || doc.history.undone.length) { doc.clearHistory(); } } if (marker.collapsed) { marker.id = ++nextMarkerId; marker.atomic = true; } if (cm) { // Sync editor state if (updateMaxLine) { cm.curOp.updateMaxLine = true; } if (marker.collapsed) { regChange(cm, from.line, to.line + 1); } else if (marker.className || marker.title || marker.startStyle || marker.endStyle || marker.css) { for (var i = from.line; i <= to.line; i++) { regLineChange(cm, i, "text"); } } if (marker.atomic) { reCheckSelection(cm.doc); } signalLater(cm, "markerAdded", cm, marker); } return marker } // SHARED TEXTMARKERS // A shared marker spans multiple linked documents. It is // implemented as a meta-marker-object controlling multiple normal // markers. var SharedTextMarker = function(markers, primary) { var this$1 = this; this.markers = markers; this.primary = primary; for (var i = 0; i < markers.length; ++i) { markers[i].parent = this$1; } }; SharedTextMarker.prototype.clear = function () { var this$1 = this; if (this.explicitlyCleared) { return } this.explicitlyCleared = true; for (var i = 0; i < this.markers.length; ++i) { this$1.markers[i].clear(); } signalLater(this, "clear"); }; SharedTextMarker.prototype.find = function (side, lineObj) { return this.primary.find(side, lineObj) }; eventMixin(SharedTextMarker); function markTextShared(doc, from, to, options, type) { options = copyObj(options); options.shared = false; var markers = [markText(doc, from, to, options, type)], primary = markers[0]; var widget = options.widgetNode; linkedDocs(doc, function (doc) { if (widget) { options.widgetNode = widget.cloneNode(true); } markers.push(markText(doc, clipPos(doc, from), clipPos(doc, to), options, type)); for (var i = 0; i < doc.linked.length; ++i) { if (doc.linked[i].isParent) { return } } primary = lst(markers); }); return new SharedTextMarker(markers, primary) } function findSharedMarkers(doc) { return doc.findMarks(Pos(doc.first, 0), doc.clipPos(Pos(doc.lastLine())), function (m) { return m.parent; }) } function copySharedMarkers(doc, markers) { for (var i = 0; i < markers.length; i++) { var marker = markers[i], pos = marker.find(); var mFrom = doc.clipPos(pos.from), mTo = doc.clipPos(pos.to); if (cmp(mFrom, mTo)) { var subMark = markText(doc, mFrom, mTo, marker.primary, marker.primary.type); marker.markers.push(subMark); subMark.parent = marker; } } } function detachSharedMarkers(markers) { var loop = function ( i ) { var marker = markers[i], linked = [marker.primary.doc]; linkedDocs(marker.primary.doc, function (d) { return linked.push(d); }); for (var j = 0; j < marker.markers.length; j++) { var subMarker = marker.markers[j]; if (indexOf(linked, subMarker.doc) == -1) { subMarker.parent = null; marker.markers.splice(j--, 1); } } }; for (var i = 0; i < markers.length; i++) loop( i ); } var nextDocId = 0; var Doc = function(text, mode, firstLine, lineSep, direction) { if (!(this instanceof Doc)) { return new Doc(text, mode, firstLine, lineSep, direction) } if (firstLine == null) { firstLine = 0; } BranchChunk.call(this, [new LeafChunk([new Line("", null)])]); this.first = firstLine; this.scrollTop = this.scrollLeft = 0; this.cantEdit = false; this.cleanGeneration = 1; this.modeFrontier = this.highlightFrontier = firstLine; var start = Pos(firstLine, 0); this.sel = simpleSelection(start); this.history = new History(null); this.id = ++nextDocId; this.modeOption = mode; this.lineSep = lineSep; this.direction = (direction == "rtl") ? "rtl" : "ltr"; this.extend = false; if (typeof text == "string") { text = this.splitLines(text); } updateDoc(this, {from: start, to: start, text: text}); setSelection(this, simpleSelection(start), sel_dontScroll); }; Doc.prototype = createObj(BranchChunk.prototype, { constructor: Doc, // Iterate over the document. Supports two forms -- with only one // argument, it calls that for each line in the document. With // three, it iterates over the range given by the first two (with // the second being non-inclusive). iter: function(from, to, op) { if (op) { this.iterN(from - this.first, to - from, op); } else { this.iterN(this.first, this.first + this.size, from); } }, // Non-public interface for adding and removing lines. insert: function(at, lines) { var height = 0; for (var i = 0; i < lines.length; ++i) { height += lines[i].height; } this.insertInner(at - this.first, lines, height); }, remove: function(at, n) { this.removeInner(at - this.first, n); }, // From here, the methods are part of the public interface. Most // are also available from CodeMirror (editor) instances. getValue: function(lineSep) { var lines = getLines(this, this.first, this.first + this.size); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, setValue: docMethodOp(function(code) { var top = Pos(this.first, 0), last = this.first + this.size - 1; makeChange(this, {from: top, to: Pos(last, getLine(this, last).text.length), text: this.splitLines(code), origin: "setValue", full: true}, true); if (this.cm) { scrollToCoords(this.cm, 0, 0); } setSelection(this, simpleSelection(top), sel_dontScroll); }), replaceRange: function(code, from, to, origin) { from = clipPos(this, from); to = to ? clipPos(this, to) : from; replaceRange(this, code, from, to, origin); }, getRange: function(from, to, lineSep) { var lines = getBetween(this, clipPos(this, from), clipPos(this, to)); if (lineSep === false) { return lines } return lines.join(lineSep || this.lineSeparator()) }, getLine: function(line) {var l = this.getLineHandle(line); return l && l.text}, getLineHandle: function(line) {if (isLine(this, line)) { return getLine(this, line) }}, getLineNumber: function(line) {return lineNo(line)}, getLineHandleVisualStart: function(line) { if (typeof line == "number") { line = getLine(this, line); } return visualLine(line) }, lineCount: function() {return this.size}, firstLine: function() {return this.first}, lastLine: function() {return this.first + this.size - 1}, clipPos: function(pos) {return clipPos(this, pos)}, getCursor: function(start) { var range$$1 = this.sel.primary(), pos; if (start == null || start == "head") { pos = range$$1.head; } else if (start == "anchor") { pos = range$$1.anchor; } else if (start == "end" || start == "to" || start === false) { pos = range$$1.to(); } else { pos = range$$1.from(); } return pos }, listSelections: function() { return this.sel.ranges }, somethingSelected: function() {return this.sel.somethingSelected()}, setCursor: docMethodOp(function(line, ch, options) { setSimpleSelection(this, clipPos(this, typeof line == "number" ? Pos(line, ch || 0) : line), null, options); }), setSelection: docMethodOp(function(anchor, head, options) { setSimpleSelection(this, clipPos(this, anchor), clipPos(this, head || anchor), options); }), extendSelection: docMethodOp(function(head, other, options) { extendSelection(this, clipPos(this, head), other && clipPos(this, other), options); }), extendSelections: docMethodOp(function(heads, options) { extendSelections(this, clipPosArray(this, heads), options); }), extendSelectionsBy: docMethodOp(function(f, options) { var heads = map(this.sel.ranges, f); extendSelections(this, clipPosArray(this, heads), options); }), setSelections: docMethodOp(function(ranges, primary, options) { var this$1 = this; if (!ranges.length) { return } var out = []; for (var i = 0; i < ranges.length; i++) { out[i] = new Range(clipPos(this$1, ranges[i].anchor), clipPos(this$1, ranges[i].head)); } if (primary == null) { primary = Math.min(ranges.length - 1, this.sel.primIndex); } setSelection(this, normalizeSelection(out, primary), options); }), addSelection: docMethodOp(function(anchor, head, options) { var ranges = this.sel.ranges.slice(0); ranges.push(new Range(clipPos(this, anchor), clipPos(this, head || anchor))); setSelection(this, normalizeSelection(ranges, ranges.length - 1), options); }), getSelection: function(lineSep) { var this$1 = this; var ranges = this.sel.ranges, lines; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); lines = lines ? lines.concat(sel) : sel; } if (lineSep === false) { return lines } else { return lines.join(lineSep || this.lineSeparator()) } }, getSelections: function(lineSep) { var this$1 = this; var parts = [], ranges = this.sel.ranges; for (var i = 0; i < ranges.length; i++) { var sel = getBetween(this$1, ranges[i].from(), ranges[i].to()); if (lineSep !== false) { sel = sel.join(lineSep || this$1.lineSeparator()); } parts[i] = sel; } return parts }, replaceSelection: function(code, collapse, origin) { var dup = []; for (var i = 0; i < this.sel.ranges.length; i++) { dup[i] = code; } this.replaceSelections(dup, collapse, origin || "+input"); }, replaceSelections: docMethodOp(function(code, collapse, origin) { var this$1 = this; var changes = [], sel = this.sel; for (var i = 0; i < sel.ranges.length; i++) { var range$$1 = sel.ranges[i]; changes[i] = {from: range$$1.from(), to: range$$1.to(), text: this$1.splitLines(code[i]), origin: origin}; } var newSel = collapse && collapse != "end" && computeReplacedSel(this, changes, collapse); for (var i$1 = changes.length - 1; i$1 >= 0; i$1--) { makeChange(this$1, changes[i$1]); } if (newSel) { setSelectionReplaceHistory(this, newSel); } else if (this.cm) { ensureCursorVisible(this.cm); } }), undo: docMethodOp(function() {makeChangeFromHistory(this, "undo");}), redo: docMethodOp(function() {makeChangeFromHistory(this, "redo");}), undoSelection: docMethodOp(function() {makeChangeFromHistory(this, "undo", true);}), redoSelection: docMethodOp(function() {makeChangeFromHistory(this, "redo", true);}), setExtending: function(val) {this.extend = val;}, getExtending: function() {return this.extend}, historySize: function() { var hist = this.history, done = 0, undone = 0; for (var i = 0; i < hist.done.length; i++) { if (!hist.done[i].ranges) { ++done; } } for (var i$1 = 0; i$1 < hist.undone.length; i$1++) { if (!hist.undone[i$1].ranges) { ++undone; } } return {undo: done, redo: undone} }, clearHistory: function() {this.history = new History(this.history.maxGeneration);}, markClean: function() { this.cleanGeneration = this.changeGeneration(true); }, changeGeneration: function(forceSplit) { if (forceSplit) { this.history.lastOp = this.history.lastSelOp = this.history.lastOrigin = null; } return this.history.generation }, isClean: function (gen) { return this.history.generation == (gen || this.cleanGeneration) }, getHistory: function() { return {done: copyHistoryArray(this.history.done), undone: copyHistoryArray(this.history.undone)} }, setHistory: function(histData) { var hist = this.history = new History(this.history.maxGeneration); hist.done = copyHistoryArray(histData.done.slice(0), null, true); hist.undone = copyHistoryArray(histData.undone.slice(0), null, true); }, setGutterMarker: docMethodOp(function(line, gutterID, value) { return changeLine(this, line, "gutter", function (line) { var markers = line.gutterMarkers || (line.gutterMarkers = {}); markers[gutterID] = value; if (!value && isEmpty(markers)) { line.gutterMarkers = null; } return true }) }), clearGutter: docMethodOp(function(gutterID) { var this$1 = this; this.iter(function (line) { if (line.gutterMarkers && line.gutterMarkers[gutterID]) { changeLine(this$1, line, "gutter", function () { line.gutterMarkers[gutterID] = null; if (isEmpty(line.gutterMarkers)) { line.gutterMarkers = null; } return true }); } }); }), lineInfo: function(line) { var n; if (typeof line == "number") { if (!isLine(this, line)) { return null } n = line; line = getLine(this, line); if (!line) { return null } } else { n = lineNo(line); if (n == null) { return null } } return {line: n, handle: line, text: line.text, gutterMarkers: line.gutterMarkers, textClass: line.textClass, bgClass: line.bgClass, wrapClass: line.wrapClass, widgets: line.widgets} }, addLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; if (!line[prop]) { line[prop] = cls; } else if (classTest(cls).test(line[prop])) { return false } else { line[prop] += " " + cls; } return true }) }), removeLineClass: docMethodOp(function(handle, where, cls) { return changeLine(this, handle, where == "gutter" ? "gutter" : "class", function (line) { var prop = where == "text" ? "textClass" : where == "background" ? "bgClass" : where == "gutter" ? "gutterClass" : "wrapClass"; var cur = line[prop]; if (!cur) { return false } else if (cls == null) { line[prop] = null; } else { var found = cur.match(classTest(cls)); if (!found) { return false } var end = found.index + found[0].length; line[prop] = cur.slice(0, found.index) + (!found.index || end == cur.length ? "" : " ") + cur.slice(end) || null; } return true }) }), addLineWidget: docMethodOp(function(handle, node, options) { return addLineWidget(this, handle, node, options) }), removeLineWidget: function(widget) { widget.clear(); }, markText: function(from, to, options) { return markText(this, clipPos(this, from), clipPos(this, to), options, options && options.type || "range") }, setBookmark: function(pos, options) { var realOpts = {replacedWith: options && (options.nodeType == null ? options.widget : options), insertLeft: options && options.insertLeft, clearWhenEmpty: false, shared: options && options.shared, handleMouseEvents: options && options.handleMouseEvents}; pos = clipPos(this, pos); return markText(this, pos, pos, realOpts, "bookmark") }, findMarksAt: function(pos) { pos = clipPos(this, pos); var markers = [], spans = getLine(this, pos.line).markedSpans; if (spans) { for (var i = 0; i < spans.length; ++i) { var span = spans[i]; if ((span.from == null || span.from <= pos.ch) && (span.to == null || span.to >= pos.ch)) { markers.push(span.marker.parent || span.marker); } } } return markers }, findMarks: function(from, to, filter) { from = clipPos(this, from); to = clipPos(this, to); var found = [], lineNo$$1 = from.line; this.iter(from.line, to.line + 1, function (line) { var spans = line.markedSpans; if (spans) { for (var i = 0; i < spans.length; i++) { var span = spans[i]; if (!(span.to != null && lineNo$$1 == from.line && from.ch >= span.to || span.from == null && lineNo$$1 != from.line || span.from != null && lineNo$$1 == to.line && span.from >= to.ch) && (!filter || filter(span.marker))) { found.push(span.marker.parent || span.marker); } } } ++lineNo$$1; }); return found }, getAllMarks: function() { var markers = []; this.iter(function (line) { var sps = line.markedSpans; if (sps) { for (var i = 0; i < sps.length; ++i) { if (sps[i].from != null) { markers.push(sps[i].marker); } } } }); return markers }, posFromIndex: function(off) { var ch, lineNo$$1 = this.first, sepSize = this.lineSeparator().length; this.iter(function (line) { var sz = line.text.length + sepSize; if (sz > off) { ch = off; return true } off -= sz; ++lineNo$$1; }); return clipPos(this, Pos(lineNo$$1, ch)) }, indexFromPos: function (coords) { coords = clipPos(this, coords); var index = coords.ch; if (coords.line < this.first || coords.ch < 0) { return 0 } var sepSize = this.lineSeparator().length; this.iter(this.first, coords.line, function (line) { // iter aborts when callback returns a truthy value index += line.text.length + sepSize; }); return index }, copy: function(copyHistory) { var doc = new Doc(getLines(this, this.first, this.first + this.size), this.modeOption, this.first, this.lineSep, this.direction); doc.scrollTop = this.scrollTop; doc.scrollLeft = this.scrollLeft; doc.sel = this.sel; doc.extend = false; if (copyHistory) { doc.history.undoDepth = this.history.undoDepth; doc.setHistory(this.getHistory()); } return doc }, linkedDoc: function(options) { if (!options) { options = {}; } var from = this.first, to = this.first + this.size; if (options.from != null && options.from > from) { from = options.from; } if (options.to != null && options.to < to) { to = options.to; } var copy = new Doc(getLines(this, from, to), options.mode || this.modeOption, from, this.lineSep, this.direction); if (options.sharedHist) { copy.history = this.history ; }(this.linked || (this.linked = [])).push({doc: copy, sharedHist: options.sharedHist}); copy.linked = [{doc: this, isParent: true, sharedHist: options.sharedHist}]; copySharedMarkers(copy, findSharedMarkers(this)); return copy }, unlinkDoc: function(other) { var this$1 = this; if (other instanceof CodeMirror$1) { other = other.doc; } if (this.linked) { for (var i = 0; i < this.linked.length; ++i) { var link = this$1.linked[i]; if (link.doc != other) { continue } this$1.linked.splice(i, 1); other.unlinkDoc(this$1); detachSharedMarkers(findSharedMarkers(this$1)); break } } // If the histories were shared, split them again if (other.history == this.history) { var splitIds = [other.id]; linkedDocs(other, function (doc) { return splitIds.push(doc.id); }, true); other.history = new History(null); other.history.done = copyHistoryArray(this.history.done, splitIds); other.history.undone = copyHistoryArray(this.history.undone, splitIds); } }, iterLinkedDocs: function(f) {linkedDocs(this, f);}, getMode: function() {return this.mode}, getEditor: function() {return this.cm}, splitLines: function(str) { if (this.lineSep) { return str.split(this.lineSep) } return splitLinesAuto(str) }, lineSeparator: function() { return this.lineSep || "\n" }, setDirection: docMethodOp(function (dir) { if (dir != "rtl") { dir = "ltr"; } if (dir == this.direction) { return } this.direction = dir; this.iter(function (line) { return line.order = null; }); if (this.cm) { directionChanged(this.cm); } }) }); // Public alias. Doc.prototype.eachLine = Doc.prototype.iter; // Kludge to work around strange IE behavior where it'll sometimes // re-fire a series of drag-related events right after the drop (#1551) var lastDrop = 0; function onDrop(e) { var cm = this; clearDragCursor(cm); if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); if (ie) { lastDrop = +new Date; } var pos = posFromMouse(cm, e, true), files = e.dataTransfer.files; if (!pos || cm.isReadOnly()) { return } // Might be a file drop, in which case we simply extract the text // and insert it. if (files && files.length && window.FileReader && window.File) { var n = files.length, text = Array(n), read = 0; var loadFile = function (file, i) { if (cm.options.allowDropFileTypes && indexOf(cm.options.allowDropFileTypes, file.type) == -1) { return } var reader = new FileReader; reader.onload = operation(cm, function () { var content = reader.result; if (/[\x00-\x08\x0e-\x1f]{2}/.test(content)) { content = ""; } text[i] = content; if (++read == n) { pos = clipPos(cm.doc, pos); var change = {from: pos, to: pos, text: cm.doc.splitLines(text.join(cm.doc.lineSeparator())), origin: "paste"}; makeChange(cm.doc, change); setSelectionReplaceHistory(cm.doc, simpleSelection(pos, changeEnd(change))); } }); reader.readAsText(file); }; for (var i = 0; i < n; ++i) { loadFile(files[i], i); } } else { // Normal drop // Don't do a replace if the drop happened inside of the selected text. if (cm.state.draggingText && cm.doc.sel.contains(pos) > -1) { cm.state.draggingText(e); // Ensure the editor is re-focused setTimeout(function () { return cm.display.input.focus(); }, 20); return } try { var text$1 = e.dataTransfer.getData("Text"); if (text$1) { var selected; if (cm.state.draggingText && !cm.state.draggingText.copy) { selected = cm.listSelections(); } setSelectionNoUndo(cm.doc, simpleSelection(pos, pos)); if (selected) { for (var i$1 = 0; i$1 < selected.length; ++i$1) { replaceRange(cm.doc, "", selected[i$1].anchor, selected[i$1].head, "drag"); } } cm.replaceSelection(text$1, "around", "paste"); cm.display.input.focus(); } } catch(e){} } } function onDragStart(cm, e) { if (ie && (!cm.state.draggingText || +new Date - lastDrop < 100)) { e_stop(e); return } if (signalDOMEvent(cm, e) || eventInWidget(cm.display, e)) { return } e.dataTransfer.setData("Text", cm.getSelection()); e.dataTransfer.effectAllowed = "copyMove"; // Use dummy image instead of default browsers image. // Recent Safari (~6.0.2) have a tendency to segfault when this happens, so we don't do it there. if (e.dataTransfer.setDragImage && !safari) { var img = elt("img", null, null, "position: fixed; left: 0; top: 0;"); img.src = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="; if (presto) { img.width = img.height = 1; cm.display.wrapper.appendChild(img); // Force a relayout, or Opera won't use our image for some obscure reason img._top = img.offsetTop; } e.dataTransfer.setDragImage(img, 0, 0); if (presto) { img.parentNode.removeChild(img); } } } function onDragOver(cm, e) { var pos = posFromMouse(cm, e); if (!pos) { return } var frag = document.createDocumentFragment(); drawSelectionCursor(cm, pos, frag); if (!cm.display.dragCursor) { cm.display.dragCursor = elt("div", null, "CodeMirror-cursors CodeMirror-dragcursors"); cm.display.lineSpace.insertBefore(cm.display.dragCursor, cm.display.cursorDiv); } removeChildrenAndAdd(cm.display.dragCursor, frag); } function clearDragCursor(cm) { if (cm.display.dragCursor) { cm.display.lineSpace.removeChild(cm.display.dragCursor); cm.display.dragCursor = null; } } // These must be handled carefully, because naively registering a // handler for each editor will cause the editors to never be // garbage collected. function forEachCodeMirror(f) { if (!document.getElementsByClassName) { return } var byClass = document.getElementsByClassName("CodeMirror"); for (var i = 0; i < byClass.length; i++) { var cm = byClass[i].CodeMirror; if (cm) { f(cm); } } } var globalsRegistered = false; function ensureGlobalHandlers() { if (globalsRegistered) { return } registerGlobalHandlers(); globalsRegistered = true; } function registerGlobalHandlers() { // When the window resizes, we need to refresh active editors. var resizeTimer; on(window, "resize", function () { if (resizeTimer == null) { resizeTimer = setTimeout(function () { resizeTimer = null; forEachCodeMirror(onResize); }, 100); } }); // When the window loses focus, we want to show the editor as blurred on(window, "blur", function () { return forEachCodeMirror(onBlur); }); } // Called when the window resizes function onResize(cm) { var d = cm.display; if (d.lastWrapHeight == d.wrapper.clientHeight && d.lastWrapWidth == d.wrapper.clientWidth) { return } // Might be a text scaling operation, clear size caches. d.cachedCharWidth = d.cachedTextHeight = d.cachedPaddingH = null; d.scrollbarsClipped = false; cm.setSize(); } var keyNames = { 3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt", 19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End", 36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert", 46: "Delete", 59: ";", 61: "=", 91: "Mod", 92: "Mod", 93: "Mod", 106: "*", 107: "=", 109: "-", 110: ".", 111: "/", 127: "Delete", 173: "-", 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63232: "Up", 63233: "Down", 63234: "Left", 63235: "Right", 63272: "Delete", 63273: "Home", 63275: "End", 63276: "PageUp", 63277: "PageDown", 63302: "Insert" }; // Number keys for (var i = 0; i < 10; i++) { keyNames[i + 48] = keyNames[i + 96] = String(i); } // Alphabetic keys for (var i$1 = 65; i$1 <= 90; i$1++) { keyNames[i$1] = String.fromCharCode(i$1); } // Function keys for (var i$2 = 1; i$2 <= 12; i$2++) { keyNames[i$2 + 111] = keyNames[i$2 + 63235] = "F" + i$2; } var keyMap = {}; keyMap.basic = { "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown", "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown", "Delete": "delCharAfter", "Backspace": "delCharBefore", "Shift-Backspace": "delCharBefore", "Tab": "defaultTab", "Shift-Tab": "indentAuto", "Enter": "newlineAndIndent", "Insert": "toggleOverwrite", "Esc": "singleSelection" }; // Note that the save and find-related commands aren't defined by // default. User code or addons can define them. Unknown commands // are simply ignored. keyMap.pcDefault = { "Ctrl-A": "selectAll", "Ctrl-D": "deleteLine", "Ctrl-Z": "undo", "Shift-Ctrl-Z": "redo", "Ctrl-Y": "redo", "Ctrl-Home": "goDocStart", "Ctrl-End": "goDocEnd", "Ctrl-Up": "goLineUp", "Ctrl-Down": "goLineDown", "Ctrl-Left": "goGroupLeft", "Ctrl-Right": "goGroupRight", "Alt-Left": "goLineStart", "Alt-Right": "goLineEnd", "Ctrl-Backspace": "delGroupBefore", "Ctrl-Delete": "delGroupAfter", "Ctrl-S": "save", "Ctrl-F": "find", "Ctrl-G": "findNext", "Shift-Ctrl-G": "findPrev", "Shift-Ctrl-F": "replace", "Shift-Ctrl-R": "replaceAll", "Ctrl-[": "indentLess", "Ctrl-]": "indentMore", "Ctrl-U": "undoSelection", "Shift-Ctrl-U": "redoSelection", "Alt-U": "redoSelection", fallthrough: "basic" }; // Very basic readline/emacs-style bindings, which are standard on Mac. keyMap.emacsy = { "Ctrl-F": "goCharRight", "Ctrl-B": "goCharLeft", "Ctrl-P": "goLineUp", "Ctrl-N": "goLineDown", "Alt-F": "goWordRight", "Alt-B": "goWordLeft", "Ctrl-A": "goLineStart", "Ctrl-E": "goLineEnd", "Ctrl-V": "goPageDown", "Shift-Ctrl-V": "goPageUp", "Ctrl-D": "delCharAfter", "Ctrl-H": "delCharBefore", "Alt-D": "delWordAfter", "Alt-Backspace": "delWordBefore", "Ctrl-K": "killLine", "Ctrl-T": "transposeChars", "Ctrl-O": "openLine" }; keyMap.macDefault = { "Cmd-A": "selectAll", "Cmd-D": "deleteLine", "Cmd-Z": "undo", "Shift-Cmd-Z": "redo", "Cmd-Y": "redo", "Cmd-Home": "goDocStart", "Cmd-Up": "goDocStart", "Cmd-End": "goDocEnd", "Cmd-Down": "goDocEnd", "Alt-Left": "goGroupLeft", "Alt-Right": "goGroupRight", "Cmd-Left": "goLineLeft", "Cmd-Right": "goLineRight", "Alt-Backspace": "delGroupBefore", "Ctrl-Alt-Backspace": "delGroupAfter", "Alt-Delete": "delGroupAfter", "Cmd-S": "save", "Cmd-F": "find", "Cmd-G": "findNext", "Shift-Cmd-G": "findPrev", "Cmd-Alt-F": "replace", "Shift-Cmd-Alt-F": "replaceAll", "Cmd-[": "indentLess", "Cmd-]": "indentMore", "Cmd-Backspace": "delWrappedLineLeft", "Cmd-Delete": "delWrappedLineRight", "Cmd-U": "undoSelection", "Shift-Cmd-U": "redoSelection", "Ctrl-Up": "goDocStart", "Ctrl-Down": "goDocEnd", fallthrough: ["basic", "emacsy"] }; keyMap["default"] = mac ? keyMap.macDefault : keyMap.pcDefault; // KEYMAP DISPATCH function normalizeKeyName(name) { var parts = name.split(/-(?!$)/); name = parts[parts.length - 1]; var alt, ctrl, shift, cmd; for (var i = 0; i < parts.length - 1; i++) { var mod = parts[i]; if (/^(cmd|meta|m)$/i.test(mod)) { cmd = true; } else if (/^a(lt)?$/i.test(mod)) { alt = true; } else if (/^(c|ctrl|control)$/i.test(mod)) { ctrl = true; } else if (/^s(hift)?$/i.test(mod)) { shift = true; } else { throw new Error("Unrecognized modifier name: " + mod) } } if (alt) { name = "Alt-" + name; } if (ctrl) { name = "Ctrl-" + name; } if (cmd) { name = "Cmd-" + name; } if (shift) { name = "Shift-" + name; } return name } // This is a kludge to keep keymaps mostly working as raw objects // (backwards compatibility) while at the same time support features // like normalization and multi-stroke key bindings. It compiles a // new normalized keymap, and then updates the old object to reflect // this. function normalizeKeyMap(keymap) { var copy = {}; for (var keyname in keymap) { if (keymap.hasOwnProperty(keyname)) { var value = keymap[keyname]; if (/^(name|fallthrough|(de|at)tach)$/.test(keyname)) { continue } if (value == "...") { delete keymap[keyname]; continue } var keys = map(keyname.split(" "), normalizeKeyName); for (var i = 0; i < keys.length; i++) { var val = (void 0), name = (void 0); if (i == keys.length - 1) { name = keys.join(" "); val = value; } else { name = keys.slice(0, i + 1).join(" "); val = "..."; } var prev = copy[name]; if (!prev) { copy[name] = val; } else if (prev != val) { throw new Error("Inconsistent bindings for " + name) } } delete keymap[keyname]; } } for (var prop in copy) { keymap[prop] = copy[prop]; } return keymap } function lookupKey(key, map$$1, handle, context) { map$$1 = getKeyMap(map$$1); var found = map$$1.call ? map$$1.call(key, context) : map$$1[key]; if (found === false) { return "nothing" } if (found === "...") { return "multi" } if (found != null && handle(found)) { return "handled" } if (map$$1.fallthrough) { if (Object.prototype.toString.call(map$$1.fallthrough) != "[object Array]") { return lookupKey(key, map$$1.fallthrough, handle, context) } for (var i = 0; i < map$$1.fallthrough.length; i++) { var result = lookupKey(key, map$$1.fallthrough[i], handle, context); if (result) { return result } } } } // Modifier key presses don't count as 'real' key presses for the // purpose of keymap fallthrough. function isModifierKey(value) { var name = typeof value == "string" ? value : keyNames[value.keyCode]; return name == "Ctrl" || name == "Alt" || name == "Shift" || name == "Mod" } function addModifierNames(name, event, noShift) { var base = name; if (event.altKey && base != "Alt") { name = "Alt-" + name; } if ((flipCtrlCmd ? event.metaKey : event.ctrlKey) && base != "Ctrl") { name = "Ctrl-" + name; } if ((flipCtrlCmd ? event.ctrlKey : event.metaKey) && base != "Cmd") { name = "Cmd-" + name; } if (!noShift && event.shiftKey && base != "Shift") { name = "Shift-" + name; } return name } // Look up the name of a key as indicated by an event object. function keyName(event, noShift) { if (presto && event.keyCode == 34 && event["char"]) { return false } var name = keyNames[event.keyCode]; if (name == null || event.altGraphKey) { return false } return addModifierNames(name, event, noShift) } function getKeyMap(val) { return typeof val == "string" ? keyMap[val] : val } // Helper for deleting text near the selection(s), used to implement // backspace, delete, and similar functionality. function deleteNearSelection(cm, compute) { var ranges = cm.doc.sel.ranges, kill = []; // Build up a set of ranges to kill first, merging overlapping // ranges. for (var i = 0; i < ranges.length; i++) { var toKill = compute(ranges[i]); while (kill.length && cmp(toKill.from, lst(kill).to) <= 0) { var replaced = kill.pop(); if (cmp(replaced.from, toKill.from) < 0) { toKill.from = replaced.from; break } } kill.push(toKill); } // Next, remove those actual ranges. runInOp(cm, function () { for (var i = kill.length - 1; i >= 0; i--) { replaceRange(cm.doc, "", kill[i].from, kill[i].to, "+delete"); } ensureCursorVisible(cm); }); } // Commands are parameter-less actions that can be performed on an // editor, mostly used for keybindings. var commands = { selectAll: selectAll, singleSelection: function (cm) { return cm.setSelection(cm.getCursor("anchor"), cm.getCursor("head"), sel_dontScroll); }, killLine: function (cm) { return deleteNearSelection(cm, function (range) { if (range.empty()) { var len = getLine(cm.doc, range.head.line).text.length; if (range.head.ch == len && range.head.line < cm.lastLine()) { return {from: range.head, to: Pos(range.head.line + 1, 0)} } else { return {from: range.head, to: Pos(range.head.line, len)} } } else { return {from: range.from(), to: range.to()} } }); }, deleteLine: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: clipPos(cm.doc, Pos(range.to().line + 1, 0)) }); }); }, delLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { return ({ from: Pos(range.from().line, 0), to: range.from() }); }); }, delWrappedLineLeft: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var leftPos = cm.coordsChar({left: 0, top: top}, "div"); return {from: leftPos, to: range.from()} }); }, delWrappedLineRight: function (cm) { return deleteNearSelection(cm, function (range) { var top = cm.charCoords(range.head, "div").top + 5; var rightPos = cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div"); return {from: range.from(), to: rightPos } }); }, undo: function (cm) { return cm.undo(); }, redo: function (cm) { return cm.redo(); }, undoSelection: function (cm) { return cm.undoSelection(); }, redoSelection: function (cm) { return cm.redoSelection(); }, goDocStart: function (cm) { return cm.extendSelection(Pos(cm.firstLine(), 0)); }, goDocEnd: function (cm) { return cm.extendSelection(Pos(cm.lastLine())); }, goLineStart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStart(cm, range.head.line); }, {origin: "+move", bias: 1} ); }, goLineStartSmart: function (cm) { return cm.extendSelectionsBy(function (range) { return lineStartSmart(cm, range.head); }, {origin: "+move", bias: 1} ); }, goLineEnd: function (cm) { return cm.extendSelectionsBy(function (range) { return lineEnd(cm, range.head.line); }, {origin: "+move", bias: -1} ); }, goLineRight: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.charCoords(range.head, "div").top + 5; return cm.coordsChar({left: cm.display.lineDiv.offsetWidth + 100, top: top}, "div") }, sel_move); }, goLineLeft: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.charCoords(range.head, "div").top + 5; return cm.coordsChar({left: 0, top: top}, "div") }, sel_move); }, goLineLeftSmart: function (cm) { return cm.extendSelectionsBy(function (range) { var top = cm.charCoords(range.head, "div").top + 5; var pos = cm.coordsChar({left: 0, top: top}, "div"); if (pos.ch < cm.getLine(pos.line).search(/\S/)) { return lineStartSmart(cm, range.head) } return pos }, sel_move); }, goLineUp: function (cm) { return cm.moveV(-1, "line"); }, goLineDown: function (cm) { return cm.moveV(1, "line"); }, goPageUp: function (cm) { return cm.moveV(-1, "page"); }, goPageDown: function (cm) { return cm.moveV(1, "page"); }, goCharLeft: function (cm) { return cm.moveH(-1, "char"); }, goCharRight: function (cm) { return cm.moveH(1, "char"); }, goColumnLeft: function (cm) { return cm.moveH(-1, "column"); }, goColumnRight: function (cm) { return cm.moveH(1, "column"); }, goWordLeft: function (cm) { return cm.moveH(-1, "word"); }, goGroupRight: function (cm) { return cm.moveH(1, "group"); }, goGroupLeft: function (cm) { return cm.moveH(-1, "group"); }, goWordRight: function (cm) { return cm.moveH(1, "word"); }, delCharBefore: function (cm) { return cm.deleteH(-1, "char"); }, delCharAfter: function (cm) { return cm.deleteH(1, "char"); }, delWordBefore: function (cm) { return cm.deleteH(-1, "word"); }, delWordAfter: function (cm) { return cm.deleteH(1, "word"); }, delGroupBefore: function (cm) { return cm.deleteH(-1, "group"); }, delGroupAfter: function (cm) { return cm.deleteH(1, "group"); }, indentAuto: function (cm) { return cm.indentSelection("smart"); }, indentMore: function (cm) { return cm.indentSelection("add"); }, indentLess: function (cm) { return cm.indentSelection("subtract"); }, insertTab: function (cm) { return cm.replaceSelection("\t"); }, insertSoftTab: function (cm) { var spaces = [], ranges = cm.listSelections(), tabSize = cm.options.tabSize; for (var i = 0; i < ranges.length; i++) { var pos = ranges[i].from(); var col = countColumn(cm.getLine(pos.line), pos.ch, tabSize); spaces.push(spaceStr(tabSize - col % tabSize)); } cm.replaceSelections(spaces); }, defaultTab: function (cm) { if (cm.somethingSelected()) { cm.indentSelection("add"); } else { cm.execCommand("insertTab"); } }, // Swap the two chars left and right of each selection's head. // Move cursor behind the two swapped characters afterwards. // // Doesn't consider line feeds a character. // Doesn't scan more than one line above to find a character. // Doesn't do anything on an empty line. // Doesn't do anything with non-empty selections. transposeChars: function (cm) { return runInOp(cm, function () { var ranges = cm.listSelections(), newSel = []; for (var i = 0; i < ranges.length; i++) { if (!ranges[i].empty()) { continue } var cur = ranges[i].head, line = getLine(cm.doc, cur.line).text; if (line) { if (cur.ch == line.length) { cur = new Pos(cur.line, cur.ch - 1); } if (cur.ch > 0) { cur = new Pos(cur.line, cur.ch + 1); cm.replaceRange(line.charAt(cur.ch - 1) + line.charAt(cur.ch - 2), Pos(cur.line, cur.ch - 2), cur, "+transpose"); } else if (cur.line > cm.doc.first) { var prev = getLine(cm.doc, cur.line - 1).text; if (prev) { cur = new Pos(cur.line, 1); cm.replaceRange(line.charAt(0) + cm.doc.lineSeparator() + prev.charAt(prev.length - 1), Pos(cur.line - 1, prev.length - 1), cur, "+transpose"); } } } newSel.push(new Range(cur, cur)); } cm.setSelections(newSel); }); }, newlineAndIndent: function (cm) { return runInOp(cm, function () { var sels = cm.listSelections(); for (var i = sels.length - 1; i >= 0; i--) { cm.replaceRange(cm.doc.lineSeparator(), sels[i].anchor, sels[i].head, "+input"); } sels = cm.listSelections(); for (var i$1 = 0; i$1 < sels.length; i$1++) { cm.indentLine(sels[i$1].from().line, null, true); } ensureCursorVisible(cm); }); }, openLine: function (cm) { return cm.replaceSelection("\n", "start"); }, toggleOverwrite: function (cm) { return cm.toggleOverwrite(); } }; function lineStart(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLine(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, visual, lineN, 1) } function lineEnd(cm, lineN) { var line = getLine(cm.doc, lineN); var visual = visualLineEnd(line); if (visual != line) { lineN = lineNo(visual); } return endOfLine(true, cm, line, lineN, -1) } function lineStartSmart(cm, pos) { var start = lineStart(cm, pos.line); var line = getLine(cm.doc, start.line); var order = getOrder(line, cm.doc.direction); if (!order || order[0].level == 0) { var firstNonWS = Math.max(0, line.text.search(/\S/)); var inWS = pos.line == start.line && pos.ch <= firstNonWS && pos.ch; return Pos(start.line, inWS ? 0 : firstNonWS, start.sticky) } return start } // Run a handler that was bound to a key. function doHandleBinding(cm, bound, dropShift) { if (typeof bound == "string") { bound = commands[bound]; if (!bound) { return false } } // Ensure previous input has been read, so that the handler sees a // consistent view of the document cm.display.input.ensurePolled(); var prevShift = cm.display.shift, done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } if (dropShift) { cm.display.shift = false; } done = bound(cm) != Pass; } finally { cm.display.shift = prevShift; cm.state.suppressEdits = false; } return done } function lookupKeyForEditor(cm, name, handle) { for (var i = 0; i < cm.state.keyMaps.length; i++) { var result = lookupKey(name, cm.state.keyMaps[i], handle, cm); if (result) { return result } } return (cm.options.extraKeys && lookupKey(name, cm.options.extraKeys, handle, cm)) || lookupKey(name, cm.options.keyMap, handle, cm) } // Note that, despite the name, this function is also used to check // for bound mouse clicks. var stopSeq = new Delayed; function dispatchKey(cm, name, e, handle) { var seq = cm.state.keySeq; if (seq) { if (isModifierKey(name)) { return "handled" } stopSeq.set(50, function () { if (cm.state.keySeq == seq) { cm.state.keySeq = null; cm.display.input.reset(); } }); name = seq + " " + name; } var result = lookupKeyForEditor(cm, name, handle); if (result == "multi") { cm.state.keySeq = name; } if (result == "handled") { signalLater(cm, "keyHandled", cm, name, e); } if (result == "handled" || result == "multi") { e_preventDefault(e); restartBlink(cm); } if (seq && !result && /\'$/.test(name)) { e_preventDefault(e); return true } return !!result } // Handle a key from the keydown event. function handleKeyBinding(cm, e) { var name = keyName(e, true); if (!name) { return false } if (e.shiftKey && !cm.state.keySeq) { // First try to resolve full name (including 'Shift-'). Failing // that, see if there is a cursor-motion command (starting with // 'go') bound to the keyname without 'Shift-'. return dispatchKey(cm, "Shift-" + name, e, function (b) { return doHandleBinding(cm, b, true); }) || dispatchKey(cm, name, e, function (b) { if (typeof b == "string" ? /^go[A-Z]/.test(b) : b.motion) { return doHandleBinding(cm, b) } }) } else { return dispatchKey(cm, name, e, function (b) { return doHandleBinding(cm, b); }) } } // Handle a key from the keypress event function handleCharBinding(cm, e, ch) { return dispatchKey(cm, "'" + ch + "'", e, function (b) { return doHandleBinding(cm, b, true); }) } var lastStoppedKey = null; function onKeyDown(e) { var cm = this; cm.curOp.focus = activeElt(); if (signalDOMEvent(cm, e)) { return } // IE does strange things with escape. if (ie && ie_version < 11 && e.keyCode == 27) { e.returnValue = false; } var code = e.keyCode; cm.display.shift = code == 16 || e.shiftKey; var handled = handleKeyBinding(cm, e); if (presto) { lastStoppedKey = handled ? code : null; // Opera has no cut event... we try to at least catch the key combo if (!handled && code == 88 && !hasCopyEvent && (mac ? e.metaKey : e.ctrlKey)) { cm.replaceSelection("", null, "cut"); } } // Turn mouse into crosshair when Alt is held on Mac. if (code == 18 && !/\bCodeMirror-crosshair\b/.test(cm.display.lineDiv.className)) { showCrossHair(cm); } } function showCrossHair(cm) { var lineDiv = cm.display.lineDiv; addClass(lineDiv, "CodeMirror-crosshair"); function up(e) { if (e.keyCode == 18 || !e.altKey) { rmClass(lineDiv, "CodeMirror-crosshair"); off(document, "keyup", up); off(document, "mouseover", up); } } on(document, "keyup", up); on(document, "mouseover", up); } function onKeyUp(e) { if (e.keyCode == 16) { this.doc.sel.shift = false; } signalDOMEvent(this, e); } function onKeyPress(e) { var cm = this; if (eventInWidget(cm.display, e) || signalDOMEvent(cm, e) || e.ctrlKey && !e.altKey || mac && e.metaKey) { return } var keyCode = e.keyCode, charCode = e.charCode; if (presto && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return} if ((presto && (!e.which || e.which < 10)) && handleKeyBinding(cm, e)) { return } var ch = String.fromCharCode(charCode == null ? keyCode : charCode); // Some browsers fire keypress events for backspace if (ch == "\x08") { return } if (handleCharBinding(cm, e, ch)) { return } cm.display.input.onKeyPress(e); } var DOUBLECLICK_DELAY = 400; var PastClick = function(time, pos, button) { this.time = time; this.pos = pos; this.button = button; }; PastClick.prototype.compare = function (time, pos, button) { return this.time + DOUBLECLICK_DELAY > time && cmp(pos, this.pos) == 0 && button == this.button }; var lastClick; var lastDoubleClick; function clickRepeat(pos, button) { var now = +new Date; if (lastDoubleClick && lastDoubleClick.compare(now, pos, button)) { lastClick = lastDoubleClick = null; return "triple" } else if (lastClick && lastClick.compare(now, pos, button)) { lastDoubleClick = new PastClick(now, pos, button); lastClick = null; return "double" } else { lastClick = new PastClick(now, pos, button); lastDoubleClick = null; return "single" } } // A mouse down can be a single click, double click, triple click, // start of selection drag, start of text drag, new cursor // (ctrl-click), rectangle drag (alt-drag), or xwin // middle-click-paste. Or it might be a click on something we should // not interfere with, such as a scrollbar or widget. function onMouseDown(e) { var cm = this, display = cm.display; if (signalDOMEvent(cm, e) || display.activeTouch && display.input.supportsTouch()) { return } display.input.ensurePolled(); display.shift = e.shiftKey; if (eventInWidget(display, e)) { if (!webkit) { // Briefly turn off draggability, to allow widgets to do // normal dragging things. display.scroller.draggable = false; setTimeout(function () { return display.scroller.draggable = true; }, 100); } return } if (clickInGutter(cm, e)) { return } var pos = posFromMouse(cm, e), button = e_button(e), repeat = pos ? clickRepeat(pos, button) : "single"; window.focus(); // #3261: make sure, that we're not starting a second selection if (button == 1 && cm.state.selectingText) { cm.state.selectingText(e); } if (pos && handleMappedButton(cm, button, pos, repeat, e)) { return } if (button == 1) { if (pos) { leftButtonDown(cm, pos, repeat, e); } else if (e_target(e) == display.scroller) { e_preventDefault(e); } } else if (button == 2) { if (pos) { extendSelection(cm.doc, pos); } setTimeout(function () { return display.input.focus(); }, 20); } else if (button == 3) { if (captureRightClick) { onContextMenu(cm, e); } else { delayBlurEvent(cm); } } } function handleMappedButton(cm, button, pos, repeat, event) { var name = "Click"; if (repeat == "double") { name = "Double" + name; } else if (repeat == "triple") { name = "Triple" + name; } name = (button == 1 ? "Left" : button == 2 ? "Middle" : "Right") + name; return dispatchKey(cm, addModifierNames(name, event), event, function (bound) { if (typeof bound == "string") { bound = commands[bound]; } if (!bound) { return false } var done = false; try { if (cm.isReadOnly()) { cm.state.suppressEdits = true; } done = bound(cm, pos) != Pass; } finally { cm.state.suppressEdits = false; } return done }) } function configureMouse(cm, repeat, event) { var option = cm.getOption("configureMouse"); var value = option ? option(cm, repeat, event) : {}; if (value.unit == null) { var rect = chromeOS ? event.shiftKey && event.metaKey : event.altKey; value.unit = rect ? "rectangle" : repeat == "single" ? "char" : repeat == "double" ? "word" : "line"; } if (value.extend == null || cm.doc.extend) { value.extend = cm.doc.extend || event.shiftKey; } if (value.addNew == null) { value.addNew = mac ? event.metaKey : event.ctrlKey; } if (value.moveOnDrag == null) { value.moveOnDrag = !(mac ? event.altKey : event.ctrlKey); } return value } function leftButtonDown(cm, pos, repeat, event) { if (ie) { setTimeout(bind(ensureFocus, cm), 0); } else { cm.curOp.focus = activeElt(); } var behavior = configureMouse(cm, repeat, event); var sel = cm.doc.sel, contained; if (cm.options.dragDrop && dragAndDrop && !cm.isReadOnly() && repeat == "single" && (contained = sel.contains(pos)) > -1 && (cmp((contained = sel.ranges[contained]).from(), pos) < 0 || pos.xRel > 0) && (cmp(contained.to(), pos) > 0 || pos.xRel < 0)) { leftButtonStartDrag(cm, event, pos, behavior); } else { leftButtonSelect(cm, event, pos, behavior); } } // Start a text drag. When it ends, see if any dragging actually // happen, and treat as a click if it didn't. function leftButtonStartDrag(cm, event, pos, behavior) { var display = cm.display, moved = false; var dragEnd = operation(cm, function (e) { if (webkit) { display.scroller.draggable = false; } cm.state.draggingText = false; off(document, "mouseup", dragEnd); off(document, "mousemove", mouseMove); off(display.scroller, "dragstart", dragStart); off(display.scroller, "drop", dragEnd); if (!moved) { e_preventDefault(e); if (!behavior.addNew) { extendSelection(cm.doc, pos, null, null, behavior.extend); } // Work around unexplainable focus problem in IE9 (#2127) and Chrome (#3081) if (webkit || ie && ie_version == 9) { setTimeout(function () {document.body.focus(); display.input.focus();}, 20); } else { display.input.focus(); } } }); var mouseMove = function(e2) { moved = moved || Math.abs(event.clientX - e2.clientX) + Math.abs(event.clientY - e2.clientY) >= 10; }; var dragStart = function () { return moved = true; }; // Let the drag handler handle this. if (webkit) { display.scroller.draggable = true; } cm.state.draggingText = dragEnd; dragEnd.copy = !behavior.moveOnDrag; // IE's approach to draggable if (display.scroller.dragDrop) { display.scroller.dragDrop(); } on(document, "mouseup", dragEnd); on(document, "mousemove", mouseMove); on(display.scroller, "dragstart", dragStart); on(display.scroller, "drop", dragEnd); delayBlurEvent(cm); setTimeout(function () { return display.input.focus(); }, 20); } function rangeForUnit(cm, pos, unit) { if (unit == "char") { return new Range(pos, pos) } if (unit == "word") { return cm.findWordAt(pos) } if (unit == "line") { return new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))) } var result = unit(cm, pos); return new Range(result.from, result.to) } // Normal selection, as opposed to text dragging. function leftButtonSelect(cm, event, start, behavior) { var display = cm.display, doc = cm.doc; e_preventDefault(event); var ourRange, ourIndex, startSel = doc.sel, ranges = startSel.ranges; if (behavior.addNew && !behavior.extend) { ourIndex = doc.sel.contains(start); if (ourIndex > -1) { ourRange = ranges[ourIndex]; } else { ourRange = new Range(start, start); } } else { ourRange = doc.sel.primary(); ourIndex = doc.sel.primIndex; } if (behavior.unit == "rectangle") { if (!behavior.addNew) { ourRange = new Range(start, start); } start = posFromMouse(cm, event, true, true); ourIndex = -1; } else { var range$$1 = rangeForUnit(cm, start, behavior.unit); if (behavior.extend) { ourRange = extendRange(ourRange, range$$1.anchor, range$$1.head, behavior.extend); } else { ourRange = range$$1; } } if (!behavior.addNew) { ourIndex = 0; setSelection(doc, new Selection([ourRange], 0), sel_mouse); startSel = doc.sel; } else if (ourIndex == -1) { ourIndex = ranges.length; setSelection(doc, normalizeSelection(ranges.concat([ourRange]), ourIndex), {scroll: false, origin: "*mouse"}); } else if (ranges.length > 1 && ranges[ourIndex].empty() && behavior.unit == "char" && !behavior.extend) { setSelection(doc, normalizeSelection(ranges.slice(0, ourIndex).concat(ranges.slice(ourIndex + 1)), 0), {scroll: false, origin: "*mouse"}); startSel = doc.sel; } else { replaceOneSelection(doc, ourIndex, ourRange, sel_mouse); } var lastPos = start; function extendTo(pos) { if (cmp(lastPos, pos) == 0) { return } lastPos = pos; if (behavior.unit == "rectangle") { var ranges = [], tabSize = cm.options.tabSize; var startCol = countColumn(getLine(doc, start.line).text, start.ch, tabSize); var posCol = countColumn(getLine(doc, pos.line).text, pos.ch, tabSize); var left = Math.min(startCol, posCol), right = Math.max(startCol, posCol); for (var line = Math.min(start.line, pos.line), end = Math.min(cm.lastLine(), Math.max(start.line, pos.line)); line <= end; line++) { var text = getLine(doc, line).text, leftPos = findColumn(text, left, tabSize); if (left == right) { ranges.push(new Range(Pos(line, leftPos), Pos(line, leftPos))); } else if (text.length > leftPos) { ranges.push(new Range(Pos(line, leftPos), Pos(line, findColumn(text, right, tabSize)))); } } if (!ranges.length) { ranges.push(new Range(start, start)); } setSelection(doc, normalizeSelection(startSel.ranges.slice(0, ourIndex).concat(ranges), ourIndex), {origin: "*mouse", scroll: false}); cm.scrollIntoView(pos); } else { var oldRange = ourRange; var range$$1 = rangeForUnit(cm, pos, behavior.unit); var anchor = oldRange.anchor, head; if (cmp(range$$1.anchor, anchor) > 0) { head = range$$1.head; anchor = minPos(oldRange.from(), range$$1.anchor); } else { head = range$$1.anchor; anchor = maxPos(oldRange.to(), range$$1.head); } var ranges$1 = startSel.ranges.slice(0); ranges$1[ourIndex] = new Range(clipPos(doc, anchor), head); setSelection(doc, normalizeSelection(ranges$1, ourIndex), sel_mouse); } } var editorSize = display.wrapper.getBoundingClientRect(); // Used to ensure timeout re-tries don't fire when another extend // happened in the meantime (clearTimeout isn't reliable -- at // least on Chrome, the timeouts still happen even when cleared, // if the clear happens after their scheduled firing time). var counter = 0; function extend(e) { var curCount = ++counter; var cur = posFromMouse(cm, e, true, behavior.unit == "rectangle"); if (!cur) { return } if (cmp(cur, lastPos) != 0) { cm.curOp.focus = activeElt(); extendTo(cur); var visible = visibleLines(display, doc); if (cur.line >= visible.to || cur.line < visible.from) { setTimeout(operation(cm, function () {if (counter == curCount) { extend(e); }}), 150); } } else { var outside = e.clientY < editorSize.top ? -20 : e.clientY > editorSize.bottom ? 20 : 0; if (outside) { setTimeout(operation(cm, function () { if (counter != curCount) { return } display.scroller.scrollTop += outside; extend(e); }), 50); } } } function done(e) { cm.state.selectingText = false; counter = Infinity; e_preventDefault(e); display.input.focus(); off(document, "mousemove", move); off(document, "mouseup", up); doc.history.lastSelOrigin = null; } var move = operation(cm, function (e) { if (!e_button(e)) { done(e); } else { extend(e); } }); var up = operation(cm, done); cm.state.selectingText = up; on(document, "mousemove", move); on(document, "mouseup", up); } // Determines whether an event happened in the gutter, and fires the // handlers for the corresponding event. function gutterEvent(cm, e, type, prevent) { var mX, mY; try { mX = e.clientX; mY = e.clientY; } catch(e) { return false } if (mX >= Math.floor(cm.display.gutters.getBoundingClientRect().right)) { return false } if (prevent) { e_preventDefault(e); } var display = cm.display; var lineBox = display.lineDiv.getBoundingClientRect(); if (mY > lineBox.bottom || !hasHandler(cm, type)) { return e_defaultPrevented(e) } mY -= lineBox.top - display.viewOffset; for (var i = 0; i < cm.options.gutters.length; ++i) { var g = display.gutters.childNodes[i]; if (g && g.getBoundingClientRect().right >= mX) { var line = lineAtHeight(cm.doc, mY); var gutter = cm.options.gutters[i]; signal(cm, type, cm, line, gutter, e); return e_defaultPrevented(e) } } } function clickInGutter(cm, e) { return gutterEvent(cm, e, "gutterClick", true) } // CONTEXT MENU HANDLING // To make the context menu work, we need to briefly unhide the // textarea (making it as unobtrusive as possible) to let the // right-click take effect on it. function onContextMenu(cm, e) { if (eventInWidget(cm.display, e) || contextMenuInGutter(cm, e)) { return } if (signalDOMEvent(cm, e, "contextmenu")) { return } cm.display.input.onContextMenu(e); } function contextMenuInGutter(cm, e) { if (!hasHandler(cm, "gutterContextMenu")) { return false } return gutterEvent(cm, e, "gutterContextMenu", false) } function themeChanged(cm) { cm.display.wrapper.className = cm.display.wrapper.className.replace(/\s*cm-s-\S+/g, "") + cm.options.theme.replace(/(^|\s)\s*/g, " cm-s-"); clearCaches(cm); } var Init = {toString: function(){return "CodeMirror.Init"}}; var defaults = {}; var optionHandlers = {}; function defineOptions(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; function option(name, deflt, handle, notOnInit) { CodeMirror.defaults[name] = deflt; if (handle) { optionHandlers[name] = notOnInit ? function (cm, val, old) {if (old != Init) { handle(cm, val, old); }} : handle; } } CodeMirror.defineOption = option; // Passed to option handlers when there is no old value. CodeMirror.Init = Init; // These two are, on init, called from the constructor because they // have to be initialized before the editor can start at all. option("value", "", function (cm, val) { return cm.setValue(val); }, true); option("mode", null, function (cm, val) { cm.doc.modeOption = val; loadMode(cm); }, true); option("indentUnit", 2, loadMode, true); option("indentWithTabs", false); option("smartIndent", true); option("tabSize", 4, function (cm) { resetModeState(cm); clearCaches(cm); regChange(cm); }, true); option("lineSeparator", null, function (cm, val) { cm.doc.lineSep = val; if (!val) { return } var newBreaks = [], lineNo = cm.doc.first; cm.doc.iter(function (line) { for (var pos = 0;;) { var found = line.text.indexOf(val, pos); if (found == -1) { break } pos = found + val.length; newBreaks.push(Pos(lineNo, found)); } lineNo++; }); for (var i = newBreaks.length - 1; i >= 0; i--) { replaceRange(cm.doc, val, newBreaks[i], Pos(newBreaks[i].line, newBreaks[i].ch + val.length)); } }); option("specialChars", /[\u0000-\u001f\u007f-\u009f\u00ad\u061c\u200b-\u200f\u2028\u2029\ufeff]/g, function (cm, val, old) { cm.state.specialChars = new RegExp(val.source + (val.test("\t") ? "" : "|\t"), "g"); if (old != Init) { cm.refresh(); } }); option("specialCharPlaceholder", defaultSpecialCharPlaceholder, function (cm) { return cm.refresh(); }, true); option("electricChars", true); option("inputStyle", mobile ? "contenteditable" : "textarea", function () { throw new Error("inputStyle can not (yet) be changed in a running editor") // FIXME }, true); option("spellcheck", false, function (cm, val) { return cm.getInputField().spellcheck = val; }, true); option("rtlMoveVisually", !windows); option("wholeLineUpdateBefore", true); option("theme", "default", function (cm) { themeChanged(cm); guttersChanged(cm); }, true); option("keyMap", "default", function (cm, val, old) { var next = getKeyMap(val); var prev = old != Init && getKeyMap(old); if (prev && prev.detach) { prev.detach(cm, next); } if (next.attach) { next.attach(cm, prev || null); } }); option("extraKeys", null); option("configureMouse", null); option("lineWrapping", false, wrappingChanged, true); option("gutters", [], function (cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); }, true); option("fixedGutter", true, function (cm, val) { cm.display.gutters.style.left = val ? compensateForHScroll(cm.display) + "px" : "0"; cm.refresh(); }, true); option("coverGutterNextToScrollbar", false, function (cm) { return updateScrollbars(cm); }, true); option("scrollbarStyle", "native", function (cm) { initScrollbars(cm); updateScrollbars(cm); cm.display.scrollbars.setScrollTop(cm.doc.scrollTop); cm.display.scrollbars.setScrollLeft(cm.doc.scrollLeft); }, true); option("lineNumbers", false, function (cm) { setGuttersForLineNumbers(cm.options); guttersChanged(cm); }, true); option("firstLineNumber", 1, guttersChanged, true); option("lineNumberFormatter", function (integer) { return integer; }, guttersChanged, true); option("showCursorWhenSelecting", false, updateSelection, true); option("resetSelectionOnContextMenu", true); option("lineWiseCopyCut", true); option("pasteLinesPerSelection", true); option("readOnly", false, function (cm, val) { if (val == "nocursor") { onBlur(cm); cm.display.input.blur(); } cm.display.input.readOnlyChanged(val); }); option("disableInput", false, function (cm, val) {if (!val) { cm.display.input.reset(); }}, true); option("dragDrop", true, dragDropChanged); option("allowDropFileTypes", null); option("cursorBlinkRate", 530); option("cursorScrollMargin", 0); option("cursorHeight", 1, updateSelection, true); option("singleCursorHeightPerLine", true, updateSelection, true); option("workTime", 100); option("workDelay", 100); option("flattenSpans", true, resetModeState, true); option("addModeClass", false, resetModeState, true); option("pollInterval", 100); option("undoDepth", 200, function (cm, val) { return cm.doc.history.undoDepth = val; }); option("historyEventDelay", 1250); option("viewportMargin", 10, function (cm) { return cm.refresh(); }, true); option("maxHighlightLength", 10000, resetModeState, true); option("moveInputWithCursor", true, function (cm, val) { if (!val) { cm.display.input.resetPosition(); } }); option("tabindex", null, function (cm, val) { return cm.display.input.getField().tabIndex = val || ""; }); option("autofocus", null); option("direction", "ltr", function (cm, val) { return cm.doc.setDirection(val); }, true); } function guttersChanged(cm) { updateGutters(cm); regChange(cm); alignHorizontally(cm); } function dragDropChanged(cm, value, old) { var wasOn = old && old != Init; if (!value != !wasOn) { var funcs = cm.display.dragFunctions; var toggle = value ? on : off; toggle(cm.display.scroller, "dragstart", funcs.start); toggle(cm.display.scroller, "dragenter", funcs.enter); toggle(cm.display.scroller, "dragover", funcs.over); toggle(cm.display.scroller, "dragleave", funcs.leave); toggle(cm.display.scroller, "drop", funcs.drop); } } function wrappingChanged(cm) { if (cm.options.lineWrapping) { addClass(cm.display.wrapper, "CodeMirror-wrap"); cm.display.sizer.style.minWidth = ""; cm.display.sizerWidth = null; } else { rmClass(cm.display.wrapper, "CodeMirror-wrap"); findMaxLine(cm); } estimateLineHeights(cm); regChange(cm); clearCaches(cm); setTimeout(function () { return updateScrollbars(cm); }, 100); } // A CodeMirror instance represents an editor. This is the object // that user code is usually dealing with. function CodeMirror$1(place, options) { var this$1 = this; if (!(this instanceof CodeMirror$1)) { return new CodeMirror$1(place, options) } this.options = options = options ? copyObj(options) : {}; // Determine effective options based on given values and defaults. copyObj(defaults, options, false); setGuttersForLineNumbers(options); var doc = options.value; if (typeof doc == "string") { doc = new Doc(doc, options.mode, null, options.lineSeparator, options.direction); } this.doc = doc; var input = new CodeMirror$1.inputStyles[options.inputStyle](this); var display = this.display = new Display(place, doc, input); display.wrapper.CodeMirror = this; updateGutters(this); themeChanged(this); if (options.lineWrapping) { this.display.wrapper.className += " CodeMirror-wrap"; } initScrollbars(this); this.state = { keyMaps: [], // stores maps added by addKeyMap overlays: [], // highlighting overlays, as added by addOverlay modeGen: 0, // bumped when mode/overlay changes, used to invalidate highlighting info overwrite: false, delayingBlurEvent: false, focused: false, suppressEdits: false, // used to disable editing during key handlers when in readOnly mode pasteIncoming: false, cutIncoming: false, // help recognize paste/cut edits in input.poll selectingText: false, draggingText: false, highlight: new Delayed(), // stores highlight worker timeout keySeq: null, // Unfinished key sequence specialChars: null }; if (options.autofocus && !mobile) { display.input.focus(); } // Override magic textarea content restore that IE sometimes does // on our hidden textarea on reload if (ie && ie_version < 11) { setTimeout(function () { return this$1.display.input.reset(true); }, 20); } registerEventHandlers(this); ensureGlobalHandlers(); startOperation(this); this.curOp.forceUpdate = true; attachDoc(this, doc); if ((options.autofocus && !mobile) || this.hasFocus()) { setTimeout(bind(onFocus, this), 20); } else { onBlur(this); } for (var opt in optionHandlers) { if (optionHandlers.hasOwnProperty(opt)) { optionHandlers[opt](this$1, options[opt], Init); } } maybeUpdateLineNumberWidth(this); if (options.finishInit) { options.finishInit(this); } for (var i = 0; i < initHooks.length; ++i) { initHooks[i](this$1); } endOperation(this); // Suppress optimizelegibility in Webkit, since it breaks text // measuring on line wrapping boundaries. if (webkit && options.lineWrapping && getComputedStyle(display.lineDiv).textRendering == "optimizelegibility") { display.lineDiv.style.textRendering = "auto"; } } // The default configuration options. CodeMirror$1.defaults = defaults; // Functions to run when options are changed. CodeMirror$1.optionHandlers = optionHandlers; // Attach the necessary event handlers when initializing the editor function registerEventHandlers(cm) { var d = cm.display; on(d.scroller, "mousedown", operation(cm, onMouseDown)); // Older IE's will not fire a second mousedown for a double click if (ie && ie_version < 11) { on(d.scroller, "dblclick", operation(cm, function (e) { if (signalDOMEvent(cm, e)) { return } var pos = posFromMouse(cm, e); if (!pos || clickInGutter(cm, e) || eventInWidget(cm.display, e)) { return } e_preventDefault(e); var word = cm.findWordAt(pos); extendSelection(cm.doc, word.anchor, word.head); })); } else { on(d.scroller, "dblclick", function (e) { return signalDOMEvent(cm, e) || e_preventDefault(e); }); } // Some browsers fire contextmenu *after* opening the menu, at // which point we can't mess with it anymore. Context menu is // handled in onMouseDown for these browsers. if (!captureRightClick) { on(d.scroller, "contextmenu", function (e) { return onContextMenu(cm, e); }); } // Used to suppress mouse event handling when a touch happens var touchFinished, prevTouch = {end: 0}; function finishTouch() { if (d.activeTouch) { touchFinished = setTimeout(function () { return d.activeTouch = null; }, 1000); prevTouch = d.activeTouch; prevTouch.end = +new Date; } } function isMouseLikeTouchEvent(e) { if (e.touches.length != 1) { return false } var touch = e.touches[0]; return touch.radiusX <= 1 && touch.radiusY <= 1 } function farAway(touch, other) { if (other.left == null) { return true } var dx = other.left - touch.left, dy = other.top - touch.top; return dx * dx + dy * dy > 20 * 20 } on(d.scroller, "touchstart", function (e) { if (!signalDOMEvent(cm, e) && !isMouseLikeTouchEvent(e)) { d.input.ensurePolled(); clearTimeout(touchFinished); var now = +new Date; d.activeTouch = {start: now, moved: false, prev: now - prevTouch.end <= 300 ? prevTouch : null}; if (e.touches.length == 1) { d.activeTouch.left = e.touches[0].pageX; d.activeTouch.top = e.touches[0].pageY; } } }); on(d.scroller, "touchmove", function () { if (d.activeTouch) { d.activeTouch.moved = true; } }); on(d.scroller, "touchend", function (e) { var touch = d.activeTouch; if (touch && !eventInWidget(d, e) && touch.left != null && !touch.moved && new Date - touch.start < 300) { var pos = cm.coordsChar(d.activeTouch, "page"), range; if (!touch.prev || farAway(touch, touch.prev)) // Single tap { range = new Range(pos, pos); } else if (!touch.prev.prev || farAway(touch, touch.prev.prev)) // Double tap { range = cm.findWordAt(pos); } else // Triple tap { range = new Range(Pos(pos.line, 0), clipPos(cm.doc, Pos(pos.line + 1, 0))); } cm.setSelection(range.anchor, range.head); cm.focus(); e_preventDefault(e); } finishTouch(); }); on(d.scroller, "touchcancel", finishTouch); // Sync scrolling between fake scrollbars and real scrollable // area, ensure viewport is updated when scrolling. on(d.scroller, "scroll", function () { if (d.scroller.clientHeight) { updateScrollTop(cm, d.scroller.scrollTop); setScrollLeft(cm, d.scroller.scrollLeft, true); signal(cm, "scroll", cm); } }); // Listen to wheel events in order to try and update the viewport on time. on(d.scroller, "mousewheel", function (e) { return onScrollWheel(cm, e); }); on(d.scroller, "DOMMouseScroll", function (e) { return onScrollWheel(cm, e); }); // Prevent wrapper from ever scrolling on(d.wrapper, "scroll", function () { return d.wrapper.scrollTop = d.wrapper.scrollLeft = 0; }); d.dragFunctions = { enter: function (e) {if (!signalDOMEvent(cm, e)) { e_stop(e); }}, over: function (e) {if (!signalDOMEvent(cm, e)) { onDragOver(cm, e); e_stop(e); }}, start: function (e) { return onDragStart(cm, e); }, drop: operation(cm, onDrop), leave: function (e) {if (!signalDOMEvent(cm, e)) { clearDragCursor(cm); }} }; var inp = d.input.getField(); on(inp, "keyup", function (e) { return onKeyUp.call(cm, e); }); on(inp, "keydown", operation(cm, onKeyDown)); on(inp, "keypress", operation(cm, onKeyPress)); on(inp, "focus", function (e) { return onFocus(cm, e); }); on(inp, "blur", function (e) { return onBlur(cm, e); }); } var initHooks = []; CodeMirror$1.defineInitHook = function (f) { return initHooks.push(f); }; // Indent the given line. The how parameter can be "smart", // "add"/null, "subtract", or "prev". When aggressive is false // (typically set to true for forced single-line indents), empty // lines are not indented, and places where the mode returns Pass // are left alone. function indentLine(cm, n, how, aggressive) { var doc = cm.doc, state; if (how == null) { how = "add"; } if (how == "smart") { // Fall back to "prev" when the mode doesn't have an indentation // method. if (!doc.mode.indent) { how = "prev"; } else { state = getContextBefore(cm, n).state; } } var tabSize = cm.options.tabSize; var line = getLine(doc, n), curSpace = countColumn(line.text, null, tabSize); if (line.stateAfter) { line.stateAfter = null; } var curSpaceString = line.text.match(/^\s*/)[0], indentation; if (!aggressive && !/\S/.test(line.text)) { indentation = 0; how = "not"; } else if (how == "smart") { indentation = doc.mode.indent(state, line.text.slice(curSpaceString.length), line.text); if (indentation == Pass || indentation > 150) { if (!aggressive) { return } how = "prev"; } } if (how == "prev") { if (n > doc.first) { indentation = countColumn(getLine(doc, n-1).text, null, tabSize); } else { indentation = 0; } } else if (how == "add") { indentation = curSpace + cm.options.indentUnit; } else if (how == "subtract") { indentation = curSpace - cm.options.indentUnit; } else if (typeof how == "number") { indentation = curSpace + how; } indentation = Math.max(0, indentation); var indentString = "", pos = 0; if (cm.options.indentWithTabs) { for (var i = Math.floor(indentation / tabSize); i; --i) {pos += tabSize; indentString += "\t";} } if (pos < indentation) { indentString += spaceStr(indentation - pos); } if (indentString != curSpaceString) { replaceRange(doc, indentString, Pos(n, 0), Pos(n, curSpaceString.length), "+input"); line.stateAfter = null; return true } else { // Ensure that, if the cursor was in the whitespace at the start // of the line, it is moved to the end of that space. for (var i$1 = 0; i$1 < doc.sel.ranges.length; i$1++) { var range = doc.sel.ranges[i$1]; if (range.head.line == n && range.head.ch < curSpaceString.length) { var pos$1 = Pos(n, curSpaceString.length); replaceOneSelection(doc, i$1, new Range(pos$1, pos$1)); break } } } } // This will be set to a {lineWise: bool, text: [string]} object, so // that, when pasting, we know what kind of selections the copied // text was made out of. var lastCopied = null; function setLastCopied(newLastCopied) { lastCopied = newLastCopied; } function applyTextInput(cm, inserted, deleted, sel, origin) { var doc = cm.doc; cm.display.shift = false; if (!sel) { sel = doc.sel; } var paste = cm.state.pasteIncoming || origin == "paste"; var textLines = splitLinesAuto(inserted), multiPaste = null; // When pasing N lines into N selections, insert one line per selection if (paste && sel.ranges.length > 1) { if (lastCopied && lastCopied.text.join("\n") == inserted) { if (sel.ranges.length % lastCopied.text.length == 0) { multiPaste = []; for (var i = 0; i < lastCopied.text.length; i++) { multiPaste.push(doc.splitLines(lastCopied.text[i])); } } } else if (textLines.length == sel.ranges.length && cm.options.pasteLinesPerSelection) { multiPaste = map(textLines, function (l) { return [l]; }); } } var updateInput; // Normal behavior is to insert the new text into every selection for (var i$1 = sel.ranges.length - 1; i$1 >= 0; i$1--) { var range$$1 = sel.ranges[i$1]; var from = range$$1.from(), to = range$$1.to(); if (range$$1.empty()) { if (deleted && deleted > 0) // Handle deletion { from = Pos(from.line, from.ch - deleted); } else if (cm.state.overwrite && !paste) // Handle overwrite { to = Pos(to.line, Math.min(getLine(doc, to.line).text.length, to.ch + lst(textLines).length)); } else if (lastCopied && lastCopied.lineWise && lastCopied.text.join("\n") == inserted) { from = to = Pos(from.line, 0); } } updateInput = cm.curOp.updateInput; var changeEvent = {from: from, to: to, text: multiPaste ? multiPaste[i$1 % multiPaste.length] : textLines, origin: origin || (paste ? "paste" : cm.state.cutIncoming ? "cut" : "+input")}; makeChange(cm.doc, changeEvent); signalLater(cm, "inputRead", cm, changeEvent); } if (inserted && !paste) { triggerElectric(cm, inserted); } ensureCursorVisible(cm); cm.curOp.updateInput = updateInput; cm.curOp.typing = true; cm.state.pasteIncoming = cm.state.cutIncoming = false; } function handlePaste(e, cm) { var pasted = e.clipboardData && e.clipboardData.getData("Text"); if (pasted) { e.preventDefault(); if (!cm.isReadOnly() && !cm.options.disableInput) { runInOp(cm, function () { return applyTextInput(cm, pasted, 0, null, "paste"); }); } return true } } function triggerElectric(cm, inserted) { // When an 'electric' character is inserted, immediately trigger a reindent if (!cm.options.electricChars || !cm.options.smartIndent) { return } var sel = cm.doc.sel; for (var i = sel.ranges.length - 1; i >= 0; i--) { var range$$1 = sel.ranges[i]; if (range$$1.head.ch > 100 || (i && sel.ranges[i - 1].head.line == range$$1.head.line)) { continue } var mode = cm.getModeAt(range$$1.head); var indented = false; if (mode.electricChars) { for (var j = 0; j < mode.electricChars.length; j++) { if (inserted.indexOf(mode.electricChars.charAt(j)) > -1) { indented = indentLine(cm, range$$1.head.line, "smart"); break } } } else if (mode.electricInput) { if (mode.electricInput.test(getLine(cm.doc, range$$1.head.line).text.slice(0, range$$1.head.ch))) { indented = indentLine(cm, range$$1.head.line, "smart"); } } if (indented) { signalLater(cm, "electricInput", cm, range$$1.head.line); } } } function copyableRanges(cm) { var text = [], ranges = []; for (var i = 0; i < cm.doc.sel.ranges.length; i++) { var line = cm.doc.sel.ranges[i].head.line; var lineRange = {anchor: Pos(line, 0), head: Pos(line + 1, 0)}; ranges.push(lineRange); text.push(cm.getRange(lineRange.anchor, lineRange.head)); } return {text: text, ranges: ranges} } function disableBrowserMagic(field, spellcheck) { field.setAttribute("autocorrect", "off"); field.setAttribute("autocapitalize", "off"); field.setAttribute("spellcheck", !!spellcheck); } function hiddenTextarea() { var te = elt("textarea", null, null, "position: absolute; bottom: -1em; padding: 0; width: 1px; height: 1em; outline: none"); var div = elt("div", [te], null, "overflow: hidden; position: relative; width: 3px; height: 0px;"); // The textarea is kept positioned near the cursor to prevent the // fact that it'll be scrolled into view on input from scrolling // our fake cursor out of view. On webkit, when wrap=off, paste is // very slow. So make the area wide instead. if (webkit) { te.style.width = "1000px"; } else { te.setAttribute("wrap", "off"); } // If border: 0; -- iOS fails to open keyboard (issue #1287) if (ios) { te.style.border = "1px solid black"; } disableBrowserMagic(te); return div } // The publicly visible API. Note that methodOp(f) means // 'wrap f in an operation, performed on its `this` parameter'. // This is not the complete set of editor methods. Most of the // methods defined on the Doc type are also injected into // CodeMirror.prototype, for backwards compatibility and // convenience. var addEditorMethods = function(CodeMirror) { var optionHandlers = CodeMirror.optionHandlers; var helpers = CodeMirror.helpers = {}; CodeMirror.prototype = { constructor: CodeMirror, focus: function(){window.focus(); this.display.input.focus();}, setOption: function(option, value) { var options = this.options, old = options[option]; if (options[option] == value && option != "mode") { return } options[option] = value; if (optionHandlers.hasOwnProperty(option)) { operation(this, optionHandlers[option])(this, value, old); } signal(this, "optionChange", this, option); }, getOption: function(option) {return this.options[option]}, getDoc: function() {return this.doc}, addKeyMap: function(map$$1, bottom) { this.state.keyMaps[bottom ? "push" : "unshift"](getKeyMap(map$$1)); }, removeKeyMap: function(map$$1) { var maps = this.state.keyMaps; for (var i = 0; i < maps.length; ++i) { if (maps[i] == map$$1 || maps[i].name == map$$1) { maps.splice(i, 1); return true } } }, addOverlay: methodOp(function(spec, options) { var mode = spec.token ? spec : CodeMirror.getMode(this.options, spec); if (mode.startState) { throw new Error("Overlays may not be stateful.") } insertSorted(this.state.overlays, {mode: mode, modeSpec: spec, opaque: options && options.opaque, priority: (options && options.priority) || 0}, function (overlay) { return overlay.priority; }); this.state.modeGen++; regChange(this); }), removeOverlay: methodOp(function(spec) { var this$1 = this; var overlays = this.state.overlays; for (var i = 0; i < overlays.length; ++i) { var cur = overlays[i].modeSpec; if (cur == spec || typeof spec == "string" && cur.name == spec) { overlays.splice(i, 1); this$1.state.modeGen++; regChange(this$1); return } } }), indentLine: methodOp(function(n, dir, aggressive) { if (typeof dir != "string" && typeof dir != "number") { if (dir == null) { dir = this.options.smartIndent ? "smart" : "prev"; } else { dir = dir ? "add" : "subtract"; } } if (isLine(this.doc, n)) { indentLine(this, n, dir, aggressive); } }), indentSelection: methodOp(function(how) { var this$1 = this; var ranges = this.doc.sel.ranges, end = -1; for (var i = 0; i < ranges.length; i++) { var range$$1 = ranges[i]; if (!range$$1.empty()) { var from = range$$1.from(), to = range$$1.to(); var start = Math.max(end, from.line); end = Math.min(this$1.lastLine(), to.line - (to.ch ? 0 : 1)) + 1; for (var j = start; j < end; ++j) { indentLine(this$1, j, how); } var newRanges = this$1.doc.sel.ranges; if (from.ch == 0 && ranges.length == newRanges.length && newRanges[i].from().ch > 0) { replaceOneSelection(this$1.doc, i, new Range(from, newRanges[i].to()), sel_dontScroll); } } else if (range$$1.head.line > end) { indentLine(this$1, range$$1.head.line, how, true); end = range$$1.head.line; if (i == this$1.doc.sel.primIndex) { ensureCursorVisible(this$1); } } } }), // Fetch the parser token for a given character. Useful for hacks // that want to inspect the mode state (say, for completion). getTokenAt: function(pos, precise) { return takeToken(this, pos, precise) }, getLineTokens: function(line, precise) { return takeToken(this, Pos(line), precise, true) }, getTokenTypeAt: function(pos) { pos = clipPos(this.doc, pos); var styles = getLineStyles(this, getLine(this.doc, pos.line)); var before = 0, after = (styles.length - 1) / 2, ch = pos.ch; var type; if (ch == 0) { type = styles[2]; } else { for (;;) { var mid = (before + after) >> 1; if ((mid ? styles[mid * 2 - 1] : 0) >= ch) { after = mid; } else if (styles[mid * 2 + 1] < ch) { before = mid + 1; } else { type = styles[mid * 2 + 2]; break } } } var cut = type ? type.indexOf("overlay ") : -1; return cut < 0 ? type : cut == 0 ? null : type.slice(0, cut - 1) }, getModeAt: function(pos) { var mode = this.doc.mode; if (!mode.innerMode) { return mode } return CodeMirror.innerMode(mode, this.getTokenAt(pos).state).mode }, getHelper: function(pos, type) { return this.getHelpers(pos, type)[0] }, getHelpers: function(pos, type) { var this$1 = this; var found = []; if (!helpers.hasOwnProperty(type)) { return found } var help = helpers[type], mode = this.getModeAt(pos); if (typeof mode[type] == "string") { if (help[mode[type]]) { found.push(help[mode[type]]); } } else if (mode[type]) { for (var i = 0; i < mode[type].length; i++) { var val = help[mode[type][i]]; if (val) { found.push(val); } } } else if (mode.helperType && help[mode.helperType]) { found.push(help[mode.helperType]); } else if (help[mode.name]) { found.push(help[mode.name]); } for (var i$1 = 0; i$1 < help._global.length; i$1++) { var cur = help._global[i$1]; if (cur.pred(mode, this$1) && indexOf(found, cur.val) == -1) { found.push(cur.val); } } return found }, getStateAfter: function(line, precise) { var doc = this.doc; line = clipLine(doc, line == null ? doc.first + doc.size - 1: line); return getContextBefore(this, line + 1, precise).state }, cursorCoords: function(start, mode) { var pos, range$$1 = this.doc.sel.primary(); if (start == null) { pos = range$$1.head; } else if (typeof start == "object") { pos = clipPos(this.doc, start); } else { pos = start ? range$$1.from() : range$$1.to(); } return cursorCoords(this, pos, mode || "page") }, charCoords: function(pos, mode) { return charCoords(this, clipPos(this.doc, pos), mode || "page") }, coordsChar: function(coords, mode) { coords = fromCoordSystem(this, coords, mode || "page"); return coordsChar(this, coords.left, coords.top) }, lineAtHeight: function(height, mode) { height = fromCoordSystem(this, {top: height, left: 0}, mode || "page").top; return lineAtHeight(this.doc, height + this.display.viewOffset) }, heightAtLine: function(line, mode, includeWidgets) { var end = false, lineObj; if (typeof line == "number") { var last = this.doc.first + this.doc.size - 1; if (line < this.doc.first) { line = this.doc.first; } else if (line > last) { line = last; end = true; } lineObj = getLine(this.doc, line); } else { lineObj = line; } return intoCoordSystem(this, lineObj, {top: 0, left: 0}, mode || "page", includeWidgets || end).top + (end ? this.doc.height - heightAtLine(lineObj) : 0) }, defaultTextHeight: function() { return textHeight(this.display) }, defaultCharWidth: function() { return charWidth(this.display) }, getViewport: function() { return {from: this.display.viewFrom, to: this.display.viewTo}}, addWidget: function(pos, node, scroll, vert, horiz) { var display = this.display; pos = cursorCoords(this, clipPos(this.doc, pos)); var top = pos.bottom, left = pos.left; node.style.position = "absolute"; node.setAttribute("cm-ignore-events", "true"); this.display.input.setUneditable(node); display.sizer.appendChild(node); if (vert == "over") { top = pos.top; } else if (vert == "above" || vert == "near") { var vspace = Math.max(display.wrapper.clientHeight, this.doc.height), hspace = Math.max(display.sizer.clientWidth, display.lineSpace.clientWidth); // Default to positioning above (if specified and possible); otherwise default to positioning below if ((vert == 'above' || pos.bottom + node.offsetHeight > vspace) && pos.top > node.offsetHeight) { top = pos.top - node.offsetHeight; } else if (pos.bottom + node.offsetHeight <= vspace) { top = pos.bottom; } if (left + node.offsetWidth > hspace) { left = hspace - node.offsetWidth; } } node.style.top = top + "px"; node.style.left = node.style.right = ""; if (horiz == "right") { left = display.sizer.clientWidth - node.offsetWidth; node.style.right = "0px"; } else { if (horiz == "left") { left = 0; } else if (horiz == "middle") { left = (display.sizer.clientWidth - node.offsetWidth) / 2; } node.style.left = left + "px"; } if (scroll) { scrollIntoView(this, {left: left, top: top, right: left + node.offsetWidth, bottom: top + node.offsetHeight}); } }, triggerOnKeyDown: methodOp(onKeyDown), triggerOnKeyPress: methodOp(onKeyPress), triggerOnKeyUp: onKeyUp, triggerOnMouseDown: methodOp(onMouseDown), execCommand: function(cmd) { if (commands.hasOwnProperty(cmd)) { return commands[cmd].call(null, this) } }, triggerElectric: methodOp(function(text) { triggerElectric(this, text); }), findPosH: function(from, amount, unit, visually) { var this$1 = this; var dir = 1; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { cur = findPosH(this$1.doc, cur, dir, unit, visually); if (cur.hitSide) { break } } return cur }, moveH: methodOp(function(dir, unit) { var this$1 = this; this.extendSelectionsBy(function (range$$1) { if (this$1.display.shift || this$1.doc.extend || range$$1.empty()) { return findPosH(this$1.doc, range$$1.head, dir, unit, this$1.options.rtlMoveVisually) } else { return dir < 0 ? range$$1.from() : range$$1.to() } }, sel_move); }), deleteH: methodOp(function(dir, unit) { var sel = this.doc.sel, doc = this.doc; if (sel.somethingSelected()) { doc.replaceSelection("", null, "+delete"); } else { deleteNearSelection(this, function (range$$1) { var other = findPosH(doc, range$$1.head, dir, unit, false); return dir < 0 ? {from: other, to: range$$1.head} : {from: range$$1.head, to: other} }); } }), findPosV: function(from, amount, unit, goalColumn) { var this$1 = this; var dir = 1, x = goalColumn; if (amount < 0) { dir = -1; amount = -amount; } var cur = clipPos(this.doc, from); for (var i = 0; i < amount; ++i) { var coords = cursorCoords(this$1, cur, "div"); if (x == null) { x = coords.left; } else { coords.left = x; } cur = findPosV(this$1, coords, dir, unit); if (cur.hitSide) { break } } return cur }, moveV: methodOp(function(dir, unit) { var this$1 = this; var doc = this.doc, goals = []; var collapse = !this.display.shift && !doc.extend && doc.sel.somethingSelected(); doc.extendSelectionsBy(function (range$$1) { if (collapse) { return dir < 0 ? range$$1.from() : range$$1.to() } var headPos = cursorCoords(this$1, range$$1.head, "div"); if (range$$1.goalColumn != null) { headPos.left = range$$1.goalColumn; } goals.push(headPos.left); var pos = findPosV(this$1, headPos, dir, unit); if (unit == "page" && range$$1 == doc.sel.primary()) { addToScrollTop(this$1, charCoords(this$1, pos, "div").top - headPos.top); } return pos }, sel_move); if (goals.length) { for (var i = 0; i < doc.sel.ranges.length; i++) { doc.sel.ranges[i].goalColumn = goals[i]; } } }), // Find the word at the given position (as returned by coordsChar). findWordAt: function(pos) { var doc = this.doc, line = getLine(doc, pos.line).text; var start = pos.ch, end = pos.ch; if (line) { var helper = this.getHelper(pos, "wordChars"); if ((pos.sticky == "before" || end == line.length) && start) { --start; } else { ++end; } var startChar = line.charAt(start); var check = isWordChar(startChar, helper) ? function (ch) { return isWordChar(ch, helper); } : /\s/.test(startChar) ? function (ch) { return /\s/.test(ch); } : function (ch) { return (!/\s/.test(ch) && !isWordChar(ch)); }; while (start > 0 && check(line.charAt(start - 1))) { --start; } while (end < line.length && check(line.charAt(end))) { ++end; } } return new Range(Pos(pos.line, start), Pos(pos.line, end)) }, toggleOverwrite: function(value) { if (value != null && value == this.state.overwrite) { return } if (this.state.overwrite = !this.state.overwrite) { addClass(this.display.cursorDiv, "CodeMirror-overwrite"); } else { rmClass(this.display.cursorDiv, "CodeMirror-overwrite"); } signal(this, "overwriteToggle", this, this.state.overwrite); }, hasFocus: function() { return this.display.input.getField() == activeElt() }, isReadOnly: function() { return !!(this.options.readOnly || this.doc.cantEdit) }, scrollTo: methodOp(function (x, y) { scrollToCoords(this, x, y); }), getScrollInfo: function() { var scroller = this.display.scroller; return {left: scroller.scrollLeft, top: scroller.scrollTop, height: scroller.scrollHeight - scrollGap(this) - this.display.barHeight, width: scroller.scrollWidth - scrollGap(this) - this.display.barWidth, clientHeight: displayHeight(this), clientWidth: displayWidth(this)} }, scrollIntoView: methodOp(function(range$$1, margin) { if (range$$1 == null) { range$$1 = {from: this.doc.sel.primary().head, to: null}; if (margin == null) { margin = this.options.cursorScrollMargin; } } else if (typeof range$$1 == "number") { range$$1 = {from: Pos(range$$1, 0), to: null}; } else if (range$$1.from == null) { range$$1 = {from: range$$1, to: null}; } if (!range$$1.to) { range$$1.to = range$$1.from; } range$$1.margin = margin || 0; if (range$$1.from.line != null) { scrollToRange(this, range$$1); } else { scrollToCoordsRange(this, range$$1.from, range$$1.to, range$$1.margin); } }), setSize: methodOp(function(width, height) { var this$1 = this; var interpret = function (val) { return typeof val == "number" || /^\d+$/.test(String(val)) ? val + "px" : val; }; if (width != null) { this.display.wrapper.style.width = interpret(width); } if (height != null) { this.display.wrapper.style.height = interpret(height); } if (this.options.lineWrapping) { clearLineMeasurementCache(this); } var lineNo$$1 = this.display.viewFrom; this.doc.iter(lineNo$$1, this.display.viewTo, function (line) { if (line.widgets) { for (var i = 0; i < line.widgets.length; i++) { if (line.widgets[i].noHScroll) { regLineChange(this$1, lineNo$$1, "widget"); break } } } ++lineNo$$1; }); this.curOp.forceUpdate = true; signal(this, "refresh", this); }), operation: function(f){return runInOp(this, f)}, refresh: methodOp(function() { var oldHeight = this.display.cachedTextHeight; regChange(this); this.curOp.forceUpdate = true; clearCaches(this); scrollToCoords(this, this.doc.scrollLeft, this.doc.scrollTop); updateGutterSpace(this); if (oldHeight == null || Math.abs(oldHeight - textHeight(this.display)) > .5) { estimateLineHeights(this); } signal(this, "refresh", this); }), swapDoc: methodOp(function(doc) { var old = this.doc; old.cm = null; attachDoc(this, doc); clearCaches(this); this.display.input.reset(); scrollToCoords(this, doc.scrollLeft, doc.scrollTop); this.curOp.forceScroll = true; signalLater(this, "swapDoc", this, old); return old }), getInputField: function(){return this.display.input.getField()}, getWrapperElement: function(){return this.display.wrapper}, getScrollerElement: function(){return this.display.scroller}, getGutterElement: function(){return this.display.gutters} }; eventMixin(CodeMirror); CodeMirror.registerHelper = function(type, name, value) { if (!helpers.hasOwnProperty(type)) { helpers[type] = CodeMirror[type] = {_global: []}; } helpers[type][name] = value; }; CodeMirror.registerGlobalHelper = function(type, name, predicate, value) { CodeMirror.registerHelper(type, name, value); helpers[type]._global.push({pred: predicate, val: value}); }; }; // Used for horizontal relative motion. Dir is -1 or 1 (left or // right), unit can be "char", "column" (like char, but doesn't // cross line boundaries), "word" (across next word), or "group" (to // the start of next group of word or non-word-non-whitespace // chars). The visually param controls whether, in right-to-left // text, direction 1 means to move towards the next index in the // string, or towards the character to the right of the current // position. The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosH(doc, pos, dir, unit, visually) { var oldPos = pos; var origDir = dir; var lineObj = getLine(doc, pos.line); function findNextLine() { var l = pos.line + dir; if (l < doc.first || l >= doc.first + doc.size) { return false } pos = new Pos(l, pos.ch, pos.sticky); return lineObj = getLine(doc, l) } function moveOnce(boundToLine) { var next; if (visually) { next = moveVisually(doc.cm, lineObj, pos, dir); } else { next = moveLogically(lineObj, pos, dir); } if (next == null) { if (!boundToLine && findNextLine()) { pos = endOfLine(visually, doc.cm, lineObj, pos.line, dir); } else { return false } } else { pos = next; } return true } if (unit == "char") { moveOnce(); } else if (unit == "column") { moveOnce(true); } else if (unit == "word" || unit == "group") { var sawType = null, group = unit == "group"; var helper = doc.cm && doc.cm.getHelper(pos, "wordChars"); for (var first = true;; first = false) { if (dir < 0 && !moveOnce(!first)) { break } var cur = lineObj.text.charAt(pos.ch) || "\n"; var type = isWordChar(cur, helper) ? "w" : group && cur == "\n" ? "n" : !group || /\s/.test(cur) ? null : "p"; if (group && !first && !type) { type = "s"; } if (sawType && sawType != type) { if (dir < 0) {dir = 1; moveOnce(); pos.sticky = "after";} break } if (type) { sawType = type; } if (dir > 0 && !moveOnce(!first)) { break } } } var result = skipAtomic(doc, pos, oldPos, origDir, true); if (equalCursorPos(oldPos, result)) { result.hitSide = true; } return result } // For relative vertical movement. Dir may be -1 or 1. Unit can be // "page" or "line". The resulting position will have a hitSide=true // property if it reached the end of the document. function findPosV(cm, pos, dir, unit) { var doc = cm.doc, x = pos.left, y; if (unit == "page") { var pageSize = Math.min(cm.display.wrapper.clientHeight, window.innerHeight || document.documentElement.clientHeight); var moveAmount = Math.max(pageSize - .5 * textHeight(cm.display), 3); y = (dir > 0 ? pos.bottom : pos.top) + dir * moveAmount; } else if (unit == "line") { y = dir > 0 ? pos.bottom + 3 : pos.top - 3; } var target; for (;;) { target = coordsChar(cm, x, y); if (!target.outside) { break } if (dir < 0 ? y <= 0 : y >= doc.height) { target.hitSide = true; break } y += dir * 5; } return target } // CONTENTEDITABLE INPUT STYLE var ContentEditableInput = function(cm) { this.cm = cm; this.lastAnchorNode = this.lastAnchorOffset = this.lastFocusNode = this.lastFocusOffset = null; this.polling = new Delayed(); this.composing = null; this.gracePeriod = false; this.readDOMTimeout = null; }; ContentEditableInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = input.cm; var div = input.div = display.lineDiv; disableBrowserMagic(div, cm.options.spellcheck); on(div, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } // IE doesn't fire input events, so we schedule a read for the pasted content in this way if (ie_version <= 11) { setTimeout(operation(cm, function () { return this$1.updateFromDOM(); }), 20); } }); on(div, "compositionstart", function (e) { this$1.composing = {data: e.data, done: false}; }); on(div, "compositionupdate", function (e) { if (!this$1.composing) { this$1.composing = {data: e.data, done: false}; } }); on(div, "compositionend", function (e) { if (this$1.composing) { if (e.data != this$1.composing.data) { this$1.readFromDOMSoon(); } this$1.composing.done = true; } }); on(div, "touchstart", function () { return input.forceCompositionEnd(); }); on(div, "input", function () { if (!this$1.composing) { this$1.readFromDOMSoon(); } }); function onCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); if (e.type == "cut") { cm.replaceSelection("", null, "cut"); } } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.operation(function () { cm.setSelections(ranges.ranges, 0, sel_dontScroll); cm.replaceSelection("", null, "cut"); }); } } if (e.clipboardData) { e.clipboardData.clearData(); var content = lastCopied.text.join("\n"); // iOS exposes the clipboard API, but seems to discard content inserted into it e.clipboardData.setData("Text", content); if (e.clipboardData.getData("Text") == content) { e.preventDefault(); return } } // Old-fashioned briefly-focus-a-textarea hack var kludge = hiddenTextarea(), te = kludge.firstChild; cm.display.lineSpace.insertBefore(kludge, cm.display.lineSpace.firstChild); te.value = lastCopied.text.join("\n"); var hadFocus = document.activeElement; selectInput(te); setTimeout(function () { cm.display.lineSpace.removeChild(kludge); hadFocus.focus(); if (hadFocus == div) { input.showPrimarySelection(); } }, 50); } on(div, "copy", onCopyCut); on(div, "cut", onCopyCut); }; ContentEditableInput.prototype.prepareSelection = function () { var result = prepareSelection(this.cm, false); result.focus = this.cm.state.focused; return result }; ContentEditableInput.prototype.showSelection = function (info, takeFocus) { if (!info || !this.cm.display.view.length) { return } if (info.focus || takeFocus) { this.showPrimarySelection(); } this.showMultipleSelections(info); }; ContentEditableInput.prototype.showPrimarySelection = function () { var sel = window.getSelection(), cm = this.cm, prim = cm.doc.sel.primary(); var from = prim.from(), to = prim.to(); if (cm.display.viewTo == cm.display.viewFrom || from.line >= cm.display.viewTo || to.line < cm.display.viewFrom) { sel.removeAllRanges(); return } var curAnchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var curFocus = domToPos(cm, sel.focusNode, sel.focusOffset); if (curAnchor && !curAnchor.bad && curFocus && !curFocus.bad && cmp(minPos(curAnchor, curFocus), from) == 0 && cmp(maxPos(curAnchor, curFocus), to) == 0) { return } var view = cm.display.view; var start = (from.line >= cm.display.viewFrom && posToDOM(cm, from)) || {node: view[0].measure.map[2], offset: 0}; var end = to.line < cm.display.viewTo && posToDOM(cm, to); if (!end) { var measure = view[view.length - 1].measure; var map$$1 = measure.maps ? measure.maps[measure.maps.length - 1] : measure.map; end = {node: map$$1[map$$1.length - 1], offset: map$$1[map$$1.length - 2] - map$$1[map$$1.length - 3]}; } if (!start || !end) { sel.removeAllRanges(); return } var old = sel.rangeCount && sel.getRangeAt(0), rng; try { rng = range(start.node, start.offset, end.offset, end.node); } catch(e) {} // Our model of the DOM might be outdated, in which case the range we try to set can be impossible if (rng) { if (!gecko && cm.state.focused) { sel.collapse(start.node, start.offset); if (!rng.collapsed) { sel.removeAllRanges(); sel.addRange(rng); } } else { sel.removeAllRanges(); sel.addRange(rng); } if (old && sel.anchorNode == null) { sel.addRange(old); } else if (gecko) { this.startGracePeriod(); } } this.rememberSelection(); }; ContentEditableInput.prototype.startGracePeriod = function () { var this$1 = this; clearTimeout(this.gracePeriod); this.gracePeriod = setTimeout(function () { this$1.gracePeriod = false; if (this$1.selectionChanged()) { this$1.cm.operation(function () { return this$1.cm.curOp.selectionChanged = true; }); } }, 20); }; ContentEditableInput.prototype.showMultipleSelections = function (info) { removeChildrenAndAdd(this.cm.display.cursorDiv, info.cursors); removeChildrenAndAdd(this.cm.display.selectionDiv, info.selection); }; ContentEditableInput.prototype.rememberSelection = function () { var sel = window.getSelection(); this.lastAnchorNode = sel.anchorNode; this.lastAnchorOffset = sel.anchorOffset; this.lastFocusNode = sel.focusNode; this.lastFocusOffset = sel.focusOffset; }; ContentEditableInput.prototype.selectionInEditor = function () { var sel = window.getSelection(); if (!sel.rangeCount) { return false } var node = sel.getRangeAt(0).commonAncestorContainer; return contains(this.div, node) }; ContentEditableInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor") { if (!this.selectionInEditor()) { this.showSelection(this.prepareSelection(), true); } this.div.focus(); } }; ContentEditableInput.prototype.blur = function () { this.div.blur(); }; ContentEditableInput.prototype.getField = function () { return this.div }; ContentEditableInput.prototype.supportsTouch = function () { return true }; ContentEditableInput.prototype.receivedFocus = function () { var input = this; if (this.selectionInEditor()) { this.pollSelection(); } else { runInOp(this.cm, function () { return input.cm.curOp.selectionChanged = true; }); } function poll() { if (input.cm.state.focused) { input.pollSelection(); input.polling.set(input.cm.options.pollInterval, poll); } } this.polling.set(this.cm.options.pollInterval, poll); }; ContentEditableInput.prototype.selectionChanged = function () { var sel = window.getSelection(); return sel.anchorNode != this.lastAnchorNode || sel.anchorOffset != this.lastAnchorOffset || sel.focusNode != this.lastFocusNode || sel.focusOffset != this.lastFocusOffset }; ContentEditableInput.prototype.pollSelection = function () { if (this.readDOMTimeout != null || this.gracePeriod || !this.selectionChanged()) { return } var sel = window.getSelection(), cm = this.cm; // On Android Chrome (version 56, at least), backspacing into an // uneditable block element will put the cursor in that element, // and then, because it's not editable, hide the virtual keyboard. // Because Android doesn't allow us to actually detect backspace // presses in a sane way, this code checks for when that happens // and simulates a backspace press in this case. if (android && chrome && this.cm.options.gutters.length && isInGutter(sel.anchorNode)) { this.cm.triggerOnKeyDown({type: "keydown", keyCode: 8, preventDefault: Math.abs}); this.blur(); this.focus(); return } if (this.composing) { return } this.rememberSelection(); var anchor = domToPos(cm, sel.anchorNode, sel.anchorOffset); var head = domToPos(cm, sel.focusNode, sel.focusOffset); if (anchor && head) { runInOp(cm, function () { setSelection(cm.doc, simpleSelection(anchor, head), sel_dontScroll); if (anchor.bad || head.bad) { cm.curOp.selectionChanged = true; } }); } }; ContentEditableInput.prototype.pollContent = function () { if (this.readDOMTimeout != null) { clearTimeout(this.readDOMTimeout); this.readDOMTimeout = null; } var cm = this.cm, display = cm.display, sel = cm.doc.sel.primary(); var from = sel.from(), to = sel.to(); if (from.ch == 0 && from.line > cm.firstLine()) { from = Pos(from.line - 1, getLine(cm.doc, from.line - 1).length); } if (to.ch == getLine(cm.doc, to.line).text.length && to.line < cm.lastLine()) { to = Pos(to.line + 1, 0); } if (from.line < display.viewFrom || to.line > display.viewTo - 1) { return false } var fromIndex, fromLine, fromNode; if (from.line == display.viewFrom || (fromIndex = findViewIndex(cm, from.line)) == 0) { fromLine = lineNo(display.view[0].line); fromNode = display.view[0].node; } else { fromLine = lineNo(display.view[fromIndex].line); fromNode = display.view[fromIndex - 1].node.nextSibling; } var toIndex = findViewIndex(cm, to.line); var toLine, toNode; if (toIndex == display.view.length - 1) { toLine = display.viewTo - 1; toNode = display.lineDiv.lastChild; } else { toLine = lineNo(display.view[toIndex + 1].line) - 1; toNode = display.view[toIndex + 1].node.previousSibling; } if (!fromNode) { return false } var newText = cm.doc.splitLines(domTextBetween(cm, fromNode, toNode, fromLine, toLine)); var oldText = getBetween(cm.doc, Pos(fromLine, 0), Pos(toLine, getLine(cm.doc, toLine).text.length)); while (newText.length > 1 && oldText.length > 1) { if (lst(newText) == lst(oldText)) { newText.pop(); oldText.pop(); toLine--; } else if (newText[0] == oldText[0]) { newText.shift(); oldText.shift(); fromLine++; } else { break } } var cutFront = 0, cutEnd = 0; var newTop = newText[0], oldTop = oldText[0], maxCutFront = Math.min(newTop.length, oldTop.length); while (cutFront < maxCutFront && newTop.charCodeAt(cutFront) == oldTop.charCodeAt(cutFront)) { ++cutFront; } var newBot = lst(newText), oldBot = lst(oldText); var maxCutEnd = Math.min(newBot.length - (newText.length == 1 ? cutFront : 0), oldBot.length - (oldText.length == 1 ? cutFront : 0)); while (cutEnd < maxCutEnd && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { ++cutEnd; } // Try to move start of change to start of selection if ambiguous if (newText.length == 1 && oldText.length == 1 && fromLine == from.line) { while (cutFront && cutFront > from.ch && newBot.charCodeAt(newBot.length - cutEnd - 1) == oldBot.charCodeAt(oldBot.length - cutEnd - 1)) { cutFront--; cutEnd++; } } newText[newText.length - 1] = newBot.slice(0, newBot.length - cutEnd).replace(/^\u200b+/, ""); newText[0] = newText[0].slice(cutFront).replace(/\u200b+$/, ""); var chFrom = Pos(fromLine, cutFront); var chTo = Pos(toLine, oldText.length ? lst(oldText).length - cutEnd : 0); if (newText.length > 1 || newText[0] || cmp(chFrom, chTo)) { replaceRange(cm.doc, newText, chFrom, chTo, "+input"); return true } }; ContentEditableInput.prototype.ensurePolled = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.reset = function () { this.forceCompositionEnd(); }; ContentEditableInput.prototype.forceCompositionEnd = function () { if (!this.composing) { return } clearTimeout(this.readDOMTimeout); this.composing = null; this.updateFromDOM(); this.div.blur(); this.div.focus(); }; ContentEditableInput.prototype.readFromDOMSoon = function () { var this$1 = this; if (this.readDOMTimeout != null) { return } this.readDOMTimeout = setTimeout(function () { this$1.readDOMTimeout = null; if (this$1.composing) { if (this$1.composing.done) { this$1.composing = null; } else { return } } this$1.updateFromDOM(); }, 80); }; ContentEditableInput.prototype.updateFromDOM = function () { var this$1 = this; if (this.cm.isReadOnly() || !this.pollContent()) { runInOp(this.cm, function () { return regChange(this$1.cm); }); } }; ContentEditableInput.prototype.setUneditable = function (node) { node.contentEditable = "false"; }; ContentEditableInput.prototype.onKeyPress = function (e) { if (e.charCode == 0) { return } e.preventDefault(); if (!this.cm.isReadOnly()) { operation(this.cm, applyTextInput)(this.cm, String.fromCharCode(e.charCode == null ? e.keyCode : e.charCode), 0); } }; ContentEditableInput.prototype.readOnlyChanged = function (val) { this.div.contentEditable = String(val != "nocursor"); }; ContentEditableInput.prototype.onContextMenu = function () {}; ContentEditableInput.prototype.resetPosition = function () {}; ContentEditableInput.prototype.needsContentAttribute = true; function posToDOM(cm, pos) { var view = findViewForLine(cm, pos.line); if (!view || view.hidden) { return null } var line = getLine(cm.doc, pos.line); var info = mapFromLineView(view, line, pos.line); var order = getOrder(line, cm.doc.direction), side = "left"; if (order) { var partPos = getBidiPartAt(order, pos.ch); side = partPos % 2 ? "right" : "left"; } var result = nodeAndOffsetInLineMap(info.map, pos.ch, side); result.offset = result.collapse == "right" ? result.end : result.start; return result } function isInGutter(node) { for (var scan = node; scan; scan = scan.parentNode) { if (/CodeMirror-gutter-wrapper/.test(scan.className)) { return true } } return false } function badPos(pos, bad) { if (bad) { pos.bad = true; } return pos } function domTextBetween(cm, from, to, fromLine, toLine) { var text = "", closing = false, lineSep = cm.doc.lineSeparator(); function recognizeMarker(id) { return function (marker) { return marker.id == id; } } function close() { if (closing) { text += lineSep; closing = false; } } function addText(str) { if (str) { close(); text += str; } } function walk(node) { if (node.nodeType == 1) { var cmText = node.getAttribute("cm-text"); if (cmText != null) { addText(cmText || node.textContent.replace(/\u200b/g, "")); return } var markerID = node.getAttribute("cm-marker"), range$$1; if (markerID) { var found = cm.findMarks(Pos(fromLine, 0), Pos(toLine + 1, 0), recognizeMarker(+markerID)); if (found.length && (range$$1 = found[0].find())) { addText(getBetween(cm.doc, range$$1.from, range$$1.to).join(lineSep)); } return } if (node.getAttribute("contenteditable") == "false") { return } var isBlock = /^(pre|div|p)$/i.test(node.nodeName); if (isBlock) { close(); } for (var i = 0; i < node.childNodes.length; i++) { walk(node.childNodes[i]); } if (isBlock) { closing = true; } } else if (node.nodeType == 3) { addText(node.nodeValue); } } for (;;) { walk(from); if (from == to) { break } from = from.nextSibling; } return text } function domToPos(cm, node, offset) { var lineNode; if (node == cm.display.lineDiv) { lineNode = cm.display.lineDiv.childNodes[offset]; if (!lineNode) { return badPos(cm.clipPos(Pos(cm.display.viewTo - 1)), true) } node = null; offset = 0; } else { for (lineNode = node;; lineNode = lineNode.parentNode) { if (!lineNode || lineNode == cm.display.lineDiv) { return null } if (lineNode.parentNode && lineNode.parentNode == cm.display.lineDiv) { break } } } for (var i = 0; i < cm.display.view.length; i++) { var lineView = cm.display.view[i]; if (lineView.node == lineNode) { return locateNodeInLineView(lineView, node, offset) } } } function locateNodeInLineView(lineView, node, offset) { var wrapper = lineView.text.firstChild, bad = false; if (!node || !contains(wrapper, node)) { return badPos(Pos(lineNo(lineView.line), 0), true) } if (node == wrapper) { bad = true; node = wrapper.childNodes[offset]; offset = 0; if (!node) { var line = lineView.rest ? lst(lineView.rest) : lineView.line; return badPos(Pos(lineNo(line), line.text.length), bad) } } var textNode = node.nodeType == 3 ? node : null, topNode = node; if (!textNode && node.childNodes.length == 1 && node.firstChild.nodeType == 3) { textNode = node.firstChild; if (offset) { offset = textNode.nodeValue.length; } } while (topNode.parentNode != wrapper) { topNode = topNode.parentNode; } var measure = lineView.measure, maps = measure.maps; function find(textNode, topNode, offset) { for (var i = -1; i < (maps ? maps.length : 0); i++) { var map$$1 = i < 0 ? measure.map : maps[i]; for (var j = 0; j < map$$1.length; j += 3) { var curNode = map$$1[j + 2]; if (curNode == textNode || curNode == topNode) { var line = lineNo(i < 0 ? lineView.line : lineView.rest[i]); var ch = map$$1[j] + offset; if (offset < 0 || curNode != textNode) { ch = map$$1[j + (offset ? 1 : 0)]; } return Pos(line, ch) } } } } var found = find(textNode, topNode, offset); if (found) { return badPos(found, bad) } // FIXME this is all really shaky. might handle the few cases it needs to handle, but likely to cause problems for (var after = topNode.nextSibling, dist = textNode ? textNode.nodeValue.length - offset : 0; after; after = after.nextSibling) { found = find(after, after.firstChild, 0); if (found) { return badPos(Pos(found.line, found.ch - dist), bad) } else { dist += after.textContent.length; } } for (var before = topNode.previousSibling, dist$1 = offset; before; before = before.previousSibling) { found = find(before, before.firstChild, -1); if (found) { return badPos(Pos(found.line, found.ch + dist$1), bad) } else { dist$1 += before.textContent.length; } } } // TEXTAREA INPUT STYLE var TextareaInput = function(cm) { this.cm = cm; // See input.poll and input.reset this.prevInput = ""; // Flag that indicates whether we expect input to appear real soon // now (after some event like 'keypress' or 'input') and are // polling intensively. this.pollingFast = false; // Self-resetting timeout for the poller this.polling = new Delayed(); // Tracks when input.reset has punted to just putting a short // string into the textarea instead of the full selection. this.inaccurateSelection = false; // Used to work around IE issue with selection being forgotten when focus moves away from textarea this.hasSelection = false; this.composing = null; }; TextareaInput.prototype.init = function (display) { var this$1 = this; var input = this, cm = this.cm; // Wraps and hides input textarea var div = this.wrapper = hiddenTextarea(); // The semihidden textarea that is focused when the editor is // focused, and receives input. var te = this.textarea = div.firstChild; display.wrapper.insertBefore(div, display.wrapper.firstChild); // Needed to hide big blue blinking cursor on Mobile Safari (doesn't seem to work in iOS 8 anymore) if (ios) { te.style.width = "0px"; } on(te, "input", function () { if (ie && ie_version >= 9 && this$1.hasSelection) { this$1.hasSelection = null; } input.poll(); }); on(te, "paste", function (e) { if (signalDOMEvent(cm, e) || handlePaste(e, cm)) { return } cm.state.pasteIncoming = true; input.fastPoll(); }); function prepareCopyCut(e) { if (signalDOMEvent(cm, e)) { return } if (cm.somethingSelected()) { setLastCopied({lineWise: false, text: cm.getSelections()}); if (input.inaccurateSelection) { input.prevInput = ""; input.inaccurateSelection = false; te.value = lastCopied.text.join("\n"); selectInput(te); } } else if (!cm.options.lineWiseCopyCut) { return } else { var ranges = copyableRanges(cm); setLastCopied({lineWise: true, text: ranges.text}); if (e.type == "cut") { cm.setSelections(ranges.ranges, null, sel_dontScroll); } else { input.prevInput = ""; te.value = ranges.text.join("\n"); selectInput(te); } } if (e.type == "cut") { cm.state.cutIncoming = true; } } on(te, "cut", prepareCopyCut); on(te, "copy", prepareCopyCut); on(display.scroller, "paste", function (e) { if (eventInWidget(display, e) || signalDOMEvent(cm, e)) { return } cm.state.pasteIncoming = true; input.focus(); }); // Prevent normal selection in the editor (we handle our own) on(display.lineSpace, "selectstart", function (e) { if (!eventInWidget(display, e)) { e_preventDefault(e); } }); on(te, "compositionstart", function () { var start = cm.getCursor("from"); if (input.composing) { input.composing.range.clear(); } input.composing = { start: start, range: cm.markText(start, cm.getCursor("to"), {className: "CodeMirror-composing"}) }; }); on(te, "compositionend", function () { if (input.composing) { input.poll(); input.composing.range.clear(); input.composing = null; } }); }; TextareaInput.prototype.prepareSelection = function () { // Redraw the selection and/or cursor var cm = this.cm, display = cm.display, doc = cm.doc; var result = prepareSelection(cm); // Move the hidden textarea near the cursor to prevent scrolling artifacts if (cm.options.moveInputWithCursor) { var headPos = cursorCoords(cm, doc.sel.primary().head, "div"); var wrapOff = display.wrapper.getBoundingClientRect(), lineOff = display.lineDiv.getBoundingClientRect(); result.teTop = Math.max(0, Math.min(display.wrapper.clientHeight - 10, headPos.top + lineOff.top - wrapOff.top)); result.teLeft = Math.max(0, Math.min(display.wrapper.clientWidth - 10, headPos.left + lineOff.left - wrapOff.left)); } return result }; TextareaInput.prototype.showSelection = function (drawn) { var cm = this.cm, display = cm.display; removeChildrenAndAdd(display.cursorDiv, drawn.cursors); removeChildrenAndAdd(display.selectionDiv, drawn.selection); if (drawn.teTop != null) { this.wrapper.style.top = drawn.teTop + "px"; this.wrapper.style.left = drawn.teLeft + "px"; } }; // Reset the input to correspond to the selection (or to be empty, // when not typing and nothing is selected) TextareaInput.prototype.reset = function (typing) { if (this.contextMenuPending || this.composing) { return } var minimal, selected, cm = this.cm, doc = cm.doc; if (cm.somethingSelected()) { this.prevInput = ""; var range$$1 = doc.sel.primary(); minimal = hasCopyEvent && (range$$1.to().line - range$$1.from().line > 100 || (selected = cm.getSelection()).length > 1000); var content = minimal ? "-" : selected || cm.getSelection(); this.textarea.value = content; if (cm.state.focused) { selectInput(this.textarea); } if (ie && ie_version >= 9) { this.hasSelection = content; } } else if (!typing) { this.prevInput = this.textarea.value = ""; if (ie && ie_version >= 9) { this.hasSelection = null; } } this.inaccurateSelection = minimal; }; TextareaInput.prototype.getField = function () { return this.textarea }; TextareaInput.prototype.supportsTouch = function () { return false }; TextareaInput.prototype.focus = function () { if (this.cm.options.readOnly != "nocursor" && (!mobile || activeElt() != this.textarea)) { try { this.textarea.focus(); } catch (e) {} // IE8 will throw if the textarea is display: none or not in DOM } }; TextareaInput.prototype.blur = function () { this.textarea.blur(); }; TextareaInput.prototype.resetPosition = function () { this.wrapper.style.top = this.wrapper.style.left = 0; }; TextareaInput.prototype.receivedFocus = function () { this.slowPoll(); }; // Poll for input changes, using the normal rate of polling. This // runs as long as the editor is focused. TextareaInput.prototype.slowPoll = function () { var this$1 = this; if (this.pollingFast) { return } this.polling.set(this.cm.options.pollInterval, function () { this$1.poll(); if (this$1.cm.state.focused) { this$1.slowPoll(); } }); }; // When an event has just come in that is likely to add or change // something in the input textarea, we poll faster, to ensure that // the change appears on the screen quickly. TextareaInput.prototype.fastPoll = function () { var missed = false, input = this; input.pollingFast = true; function p() { var changed = input.poll(); if (!changed && !missed) {missed = true; input.polling.set(60, p);} else {input.pollingFast = false; input.slowPoll();} } input.polling.set(20, p); }; // Read input from the textarea, and update the document to match. // When something is selected, it is present in the textarea, and // selected (unless it is huge, in which case a placeholder is // used). When nothing is selected, the cursor sits after previously // seen text (can be empty), which is stored in prevInput (we must // not reset the textarea when typing, because that breaks IME). TextareaInput.prototype.poll = function () { var this$1 = this; var cm = this.cm, input = this.textarea, prevInput = this.prevInput; // Since this is called a *lot*, try to bail out as cheaply as // possible when it is clear that nothing happened. hasSelection // will be the case when there is a lot of text in the textarea, // in which case reading its value would be expensive. if (this.contextMenuPending || !cm.state.focused || (hasSelection(input) && !prevInput && !this.composing) || cm.isReadOnly() || cm.options.disableInput || cm.state.keySeq) { return false } var text = input.value; // If nothing changed, bail. if (text == prevInput && !cm.somethingSelected()) { return false } // Work around nonsensical selection resetting in IE9/10, and // inexplicable appearance of private area unicode characters on // some key combos in Mac (#2689). if (ie && ie_version >= 9 && this.hasSelection === text || mac && /[\uf700-\uf7ff]/.test(text)) { cm.display.input.reset(); return false } if (cm.doc.sel == cm.display.selForContextMenu) { var first = text.charCodeAt(0); if (first == 0x200b && !prevInput) { prevInput = "\u200b"; } if (first == 0x21da) { this.reset(); return this.cm.execCommand("undo") } } // Find the part of the input that is actually new var same = 0, l = Math.min(prevInput.length, text.length); while (same < l && prevInput.charCodeAt(same) == text.charCodeAt(same)) { ++same; } runInOp(cm, function () { applyTextInput(cm, text.slice(same), prevInput.length - same, null, this$1.composing ? "*compose" : null); // Don't leave long text in the textarea, since it makes further polling slow if (text.length > 1000 || text.indexOf("\n") > -1) { input.value = this$1.prevInput = ""; } else { this$1.prevInput = text; } if (this$1.composing) { this$1.composing.range.clear(); this$1.composing.range = cm.markText(this$1.composing.start, cm.getCursor("to"), {className: "CodeMirror-composing"}); } }); return true }; TextareaInput.prototype.ensurePolled = function () { if (this.pollingFast && this.poll()) { this.pollingFast = false; } }; TextareaInput.prototype.onKeyPress = function () { if (ie && ie_version >= 9) { this.hasSelection = null; } this.fastPoll(); }; TextareaInput.prototype.onContextMenu = function (e) { var input = this, cm = input.cm, display = cm.display, te = input.textarea; var pos = posFromMouse(cm, e), scrollPos = display.scroller.scrollTop; if (!pos || presto) { return } // Opera is difficult. // Reset the current text selection only if the click is done outside of the selection // and 'resetSelectionOnContextMenu' option is true. var reset = cm.options.resetSelectionOnContextMenu; if (reset && cm.doc.sel.contains(pos) == -1) { operation(cm, setSelection)(cm.doc, simpleSelection(pos), sel_dontScroll); } var oldCSS = te.style.cssText, oldWrapperCSS = input.wrapper.style.cssText; input.wrapper.style.cssText = "position: absolute"; var wrapperBox = input.wrapper.getBoundingClientRect(); te.style.cssText = "position: absolute; width: 30px; height: 30px;\n top: " + (e.clientY - wrapperBox.top - 5) + "px; left: " + (e.clientX - wrapperBox.left - 5) + "px;\n z-index: 1000; background: " + (ie ? "rgba(255, 255, 255, .05)" : "transparent") + ";\n outline: none; border-width: 0; outline: none; overflow: hidden; opacity: .05; filter: alpha(opacity=5);"; var oldScrollY; if (webkit) { oldScrollY = window.scrollY; } // Work around Chrome issue (#2712) display.input.focus(); if (webkit) { window.scrollTo(null, oldScrollY); } display.input.reset(); // Adds "Select all" to context menu in FF if (!cm.somethingSelected()) { te.value = input.prevInput = " "; } input.contextMenuPending = true; display.selForContextMenu = cm.doc.sel; clearTimeout(display.detectingSelectAll); // Select-all will be greyed out if there's nothing to select, so // this adds a zero-width space so that we can later check whether // it got selected. function prepareSelectAllHack() { if (te.selectionStart != null) { var selected = cm.somethingSelected(); var extval = "\u200b" + (selected ? te.value : ""); te.value = "\u21da"; // Used to catch context-menu undo te.value = extval; input.prevInput = selected ? "" : "\u200b"; te.selectionStart = 1; te.selectionEnd = extval.length; // Re-set this, in case some other handler touched the // selection in the meantime. display.selForContextMenu = cm.doc.sel; } } function rehide() { input.contextMenuPending = false; input.wrapper.style.cssText = oldWrapperCSS; te.style.cssText = oldCSS; if (ie && ie_version < 9) { display.scrollbars.setScrollTop(display.scroller.scrollTop = scrollPos); } // Try to detect the user choosing select-all if (te.selectionStart != null) { if (!ie || (ie && ie_version < 9)) { prepareSelectAllHack(); } var i = 0, poll = function () { if (display.selForContextMenu == cm.doc.sel && te.selectionStart == 0 && te.selectionEnd > 0 && input.prevInput == "\u200b") { operation(cm, selectAll)(cm); } else if (i++ < 10) { display.detectingSelectAll = setTimeout(poll, 500); } else { display.selForContextMenu = null; display.input.reset(); } }; display.detectingSelectAll = setTimeout(poll, 200); } } if (ie && ie_version >= 9) { prepareSelectAllHack(); } if (captureRightClick) { e_stop(e); var mouseup = function () { off(window, "mouseup", mouseup); setTimeout(rehide, 20); }; on(window, "mouseup", mouseup); } else { setTimeout(rehide, 50); } }; TextareaInput.prototype.readOnlyChanged = function (val) { if (!val) { this.reset(); } this.textarea.disabled = val == "nocursor"; }; TextareaInput.prototype.setUneditable = function () {}; TextareaInput.prototype.needsContentAttribute = false; function fromTextArea(textarea, options) { options = options ? copyObj(options) : {}; options.value = textarea.value; if (!options.tabindex && textarea.tabIndex) { options.tabindex = textarea.tabIndex; } if (!options.placeholder && textarea.placeholder) { options.placeholder = textarea.placeholder; } // Set autofocus to true if this textarea is focused, or if it has // autofocus and no other element is focused. if (options.autofocus == null) { var hasFocus = activeElt(); options.autofocus = hasFocus == textarea || textarea.getAttribute("autofocus") != null && hasFocus == document.body; } function save() {textarea.value = cm.getValue();} var realSubmit; if (textarea.form) { on(textarea.form, "submit", save); // Deplorable hack to make the submit method do the right thing. if (!options.leaveSubmitMethodAlone) { var form = textarea.form; realSubmit = form.submit; try { var wrappedSubmit = form.submit = function () { save(); form.submit = realSubmit; form.submit(); form.submit = wrappedSubmit; }; } catch(e) {} } } options.finishInit = function (cm) { cm.save = save; cm.getTextArea = function () { return textarea; }; cm.toTextArea = function () { cm.toTextArea = isNaN; // Prevent this from being ran twice save(); textarea.parentNode.removeChild(cm.getWrapperElement()); textarea.style.display = ""; if (textarea.form) { off(textarea.form, "submit", save); if (typeof textarea.form.submit == "function") { textarea.form.submit = realSubmit; } } }; }; textarea.style.display = "none"; var cm = CodeMirror$1(function (node) { return textarea.parentNode.insertBefore(node, textarea.nextSibling); }, options); return cm } function addLegacyProps(CodeMirror) { CodeMirror.off = off; CodeMirror.on = on; CodeMirror.wheelEventPixels = wheelEventPixels; CodeMirror.Doc = Doc; CodeMirror.splitLines = splitLinesAuto; CodeMirror.countColumn = countColumn; CodeMirror.findColumn = findColumn; CodeMirror.isWordChar = isWordCharBasic; CodeMirror.Pass = Pass; CodeMirror.signal = signal; CodeMirror.Line = Line; CodeMirror.changeEnd = changeEnd; CodeMirror.scrollbarModel = scrollbarModel; CodeMirror.Pos = Pos; CodeMirror.cmpPos = cmp; CodeMirror.modes = modes; CodeMirror.mimeModes = mimeModes; CodeMirror.resolveMode = resolveMode; CodeMirror.getMode = getMode; CodeMirror.modeExtensions = modeExtensions; CodeMirror.extendMode = extendMode; CodeMirror.copyState = copyState; CodeMirror.startState = startState; CodeMirror.innerMode = innerMode; CodeMirror.commands = commands; CodeMirror.keyMap = keyMap; CodeMirror.keyName = keyName; CodeMirror.isModifierKey = isModifierKey; CodeMirror.lookupKey = lookupKey; CodeMirror.normalizeKeyMap = normalizeKeyMap; CodeMirror.StringStream = StringStream; CodeMirror.SharedTextMarker = SharedTextMarker; CodeMirror.TextMarker = TextMarker; CodeMirror.LineWidget = LineWidget; CodeMirror.e_preventDefault = e_preventDefault; CodeMirror.e_stopPropagation = e_stopPropagation; CodeMirror.e_stop = e_stop; CodeMirror.addClass = addClass; CodeMirror.contains = contains; CodeMirror.rmClass = rmClass; CodeMirror.keyNames = keyNames; } // EDITOR CONSTRUCTOR defineOptions(CodeMirror$1); addEditorMethods(CodeMirror$1); // Set up methods on CodeMirror's prototype to redirect to the editor's document. var dontDelegate = "iter insert remove copy getEditor constructor".split(" "); for (var prop in Doc.prototype) { if (Doc.prototype.hasOwnProperty(prop) && indexOf(dontDelegate, prop) < 0) { CodeMirror$1.prototype[prop] = (function(method) { return function() {return method.apply(this.doc, arguments)} })(Doc.prototype[prop]); } } eventMixin(Doc); // INPUT HANDLING CodeMirror$1.inputStyles = {"textarea": TextareaInput, "contenteditable": ContentEditableInput}; // MODE DEFINITION AND QUERYING // Extra arguments are stored as the mode's dependencies, which is // used by (legacy) mechanisms like loadmode.js to automatically // load a mode. (Preferred mechanism is the require/define calls.) CodeMirror$1.defineMode = function(name/*, mode, …*/) { if (!CodeMirror$1.defaults.mode && name != "null") { CodeMirror$1.defaults.mode = name; } defineMode.apply(this, arguments); }; CodeMirror$1.defineMIME = defineMIME; // Minimal default mode. CodeMirror$1.defineMode("null", function () { return ({token: function (stream) { return stream.skipToEnd(); }}); }); CodeMirror$1.defineMIME("text/plain", "null"); // EXTENSIONS CodeMirror$1.defineExtension = function (name, func) { CodeMirror$1.prototype[name] = func; }; CodeMirror$1.defineDocExtension = function (name, func) { Doc.prototype[name] = func; }; CodeMirror$1.fromTextArea = fromTextArea; addLegacyProps(CodeMirror$1); CodeMirror$1.version = "5.27.4"; return CodeMirror$1; }))); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/codemirror.lib.clike-lint.js ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ (function(mod) { if (typeof exports == "object" && typeof module == "object") // CommonJS mod(require("../../lib/codemirror")); else if (typeof define == "function" && define.amd) // AMD define(["../../lib/codemirror"], mod); else // Plain browser env mod(CodeMirror); })(function(CodeMirror) { "use strict"; function Context(indented, column, type, info, align, prev) { this.indented = indented; this.column = column; this.type = type; this.info = info; this.align = align; this.prev = prev; } function pushContext(state, col, type, info) { var indent = state.indented; if (state.context && state.context.type == "statement" && type != "statement") indent = state.context.indented; return state.context = new Context(indent, col, type, info, null, state.context); } function popContext(state) { var t = state.context.type; if (t == ")" || t == "]" || t == "}") state.indented = state.context.indented; return state.context = state.context.prev; } function typeBefore(stream, state, pos) { if (state.prevToken == "variable" || state.prevToken == "type") return true; if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true; if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true; } function isTopScope(context) { for (;;) { if (!context || context.type == "top") return true; if (context.type == "}" && context.prev.info != "namespace") return false; context = context.prev; } } CodeMirror.defineMode("clike", function(config, parserConfig) { var indentUnit = config.indentUnit, statementIndentUnit = parserConfig.statementIndentUnit || indentUnit, dontAlignCalls = parserConfig.dontAlignCalls, keywords = parserConfig.keywords || {}, types = parserConfig.types || {}, builtin = parserConfig.builtin || {}, blockKeywords = parserConfig.blockKeywords || {}, defKeywords = parserConfig.defKeywords || {}, atoms = parserConfig.atoms || {}, hooks = parserConfig.hooks || {}, multiLineStrings = parserConfig.multiLineStrings, indentStatements = parserConfig.indentStatements !== false, indentSwitch = parserConfig.indentSwitch !== false, namespaceSeparator = parserConfig.namespaceSeparator, isPunctuationChar = parserConfig.isPunctuationChar || /[\[\]{}\(\),;\:\.]/, numberStart = parserConfig.numberStart || /[\d\.]/, number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i, isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/, isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/; var curPunc, isDefKeyword; function tokenBase(stream, state) { var ch = stream.next(); if (hooks[ch]) { var result = hooks[ch](stream, state); if (result !== false) return result; } if (ch == '"' || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); } if (isPunctuationChar.test(ch)) { curPunc = ch; return null; } if (numberStart.test(ch)) { stream.backUp(1) if (stream.match(number)) return "number" stream.next() } if (ch == "/") { if (stream.eat("*")) { state.tokenize = tokenComment; return tokenComment(stream, state); } if (stream.eat("/")) { stream.skipToEnd(); return "comment"; } } if (isOperatorChar.test(ch)) { while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {} return "operator"; } stream.eatWhile(isIdentifierChar); if (namespaceSeparator) while (stream.match(namespaceSeparator)) stream.eatWhile(isIdentifierChar); var cur = stream.current(); if (contains(keywords, cur)) { if (contains(blockKeywords, cur)) curPunc = "newstatement"; if (contains(defKeywords, cur)) isDefKeyword = true; return "keyword"; } if (contains(types, cur)) return "type"; if (contains(builtin, cur)) { if (contains(blockKeywords, cur)) curPunc = "newstatement"; return "builtin"; } if (contains(atoms, cur)) return "atom"; return "variable"; } function tokenString(quote) { return function(stream, state) { var escaped = false, next, end = false; while ((next = stream.next()) != null) { if (next == quote && !escaped) {end = true; break;} escaped = !escaped && next == "\\"; } if (end || !(escaped || multiLineStrings)) state.tokenize = null; return "string"; }; } function tokenComment(stream, state) { var maybeEnd = false, ch; while (ch = stream.next()) { if (ch == "/" && maybeEnd) { state.tokenize = null; break; } maybeEnd = (ch == "*"); } return "comment"; } function maybeEOL(stream, state) { if (parserConfig.typeFirstDefinitions && stream.eol() && isTopScope(state.context)) state.typeAtEndOfLine = typeBefore(stream, state, stream.pos) } // Interface return { startState: function(basecolumn) { return { tokenize: null, context: new Context((basecolumn || 0) - indentUnit, 0, "top", null, false), indented: 0, startOfLine: true, prevToken: null }; }, token: function(stream, state) { var ctx = state.context; if (stream.sol()) { if (ctx.align == null) ctx.align = false; state.indented = stream.indentation(); state.startOfLine = true; } if (stream.eatSpace()) { maybeEOL(stream, state); return null; } curPunc = isDefKeyword = null; var style = (state.tokenize || tokenBase)(stream, state); if (style == "comment" || style == "meta") return style; if (ctx.align == null) ctx.align = true; if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false))) while (state.context.type == "statement") popContext(state); else if (curPunc == "{") pushContext(state, stream.column(), "}"); else if (curPunc == "[") pushContext(state, stream.column(), "]"); else if (curPunc == "(") pushContext(state, stream.column(), ")"); else if (curPunc == "}") { while (ctx.type == "statement") ctx = popContext(state); if (ctx.type == "}") ctx = popContext(state); while (ctx.type == "statement") ctx = popContext(state); } else if (curPunc == ctx.type) popContext(state); else if (indentStatements && (((ctx.type == "}" || ctx.type == "top") && curPunc != ";") || (ctx.type == "statement" && curPunc == "newstatement"))) { pushContext(state, stream.column(), "statement", stream.current()); } if (style == "variable" && ((state.prevToken == "def" || (parserConfig.typeFirstDefinitions && typeBefore(stream, state, stream.start) && isTopScope(state.context) && stream.match(/^\s*\(/, false))))) style = "def"; if (hooks.token) { var result = hooks.token(stream, state, style); if (result !== undefined) style = result; } if (style == "def" && parserConfig.styleDefs === false) style = "variable"; state.startOfLine = false; state.prevToken = isDefKeyword ? "def" : style || curPunc; maybeEOL(stream, state); return style; }, indent: function(state, textAfter) { if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass; var ctx = state.context, firstChar = textAfter && textAfter.charAt(0); if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev; if (parserConfig.dontIndentStatements) while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info)) ctx = ctx.prev if (hooks.indent) { var hook = hooks.indent(state, ctx, textAfter); if (typeof hook == "number") return hook } var closing = firstChar == ctx.type; var switchBlock = ctx.prev && ctx.prev.info == "switch"; if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) { while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev return ctx.indented } if (ctx.type == "statement") return ctx.indented + (firstChar == "{" ? 0 : statementIndentUnit); if (ctx.align && (!dontAlignCalls || ctx.type != ")")) return ctx.column + (closing ? 0 : 1); if (ctx.type == ")" && !closing) return ctx.indented + statementIndentUnit; return ctx.indented + (closing ? 0 : indentUnit) + (!closing && switchBlock && !/^(?:case|default)\b/.test(textAfter) ? indentUnit : 0); }, electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/, blockCommentStart: "/*", blockCommentEnd: "*/", lineComment: "//", fold: "brace" }; }); function words(str) { var obj = {}, words = str.split(" "); for (var i = 0; i < words.length; ++i) obj[words[i]] = true; return obj; } function contains(words, word) { if (typeof words === "function") { return words(word); } else { return words.propertyIsEnumerable(word); } } var cKeywords = "auto if break case register continue return default do sizeof " + "static else struct switch extern typedef union for goto while enum const volatile"; var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t"; function cppHook(stream, state) { if (!state.startOfLine) return false for (var ch, next = null; ch = stream.peek();) { if (ch == "\\" && stream.match(/^.$/)) { next = cppHook break } else if (ch == "/" && stream.match(/^\/[\/\*]/, false)) { break } stream.next() } state.tokenize = next return "meta" } function pointerHook(_stream, state) { if (state.prevToken == "type") return "type"; return false; } function cpp14Literal(stream) { stream.eatWhile(/[\w\.']/); return "number"; } function cpp11StringHook(stream, state) { stream.backUp(1); // Raw strings. if (stream.match(/(R|u8R|uR|UR|LR)/)) { var match = stream.match(/"([^\s\\()]{0,16})\(/); if (!match) { return false; } state.cpp11RawStringDelim = match[1]; state.tokenize = tokenRawString; return tokenRawString(stream, state); } // Unicode strings/chars. if (stream.match(/(u8|u|U|L)/)) { if (stream.match(/["']/, /* eat */ false)) { return "string"; } return false; } // Ignore this hook. stream.next(); return false; } function cppLooksLikeConstructor(word) { var lastTwo = /(\w+)::~?(\w+)$/.exec(word); return lastTwo && lastTwo[1] == lastTwo[2]; } // C#-style strings where "" escapes a quote. function tokenAtString(stream, state) { var next; while ((next = stream.next()) != null) { if (next == '"' && !stream.eat('"')) { state.tokenize = null; break; } } return "string"; } // C++11 raw string literal is "( anything )", where // can be a string up to 16 characters long. function tokenRawString(stream, state) { // Escape characters that have special regex meanings. var delim = state.cpp11RawStringDelim.replace(/[^\w\s]/g, '\\$&'); var match = stream.match(new RegExp(".*?\\)" + delim + '"')); if (match) state.tokenize = null; else stream.skipToEnd(); return "string"; } function def(mimes, mode) { if (typeof mimes == "string") mimes = [mimes]; var words = []; function add(obj) { if (obj) for (var prop in obj) if (obj.hasOwnProperty(prop)) words.push(prop); } add(mode.keywords); add(mode.types); add(mode.builtin); add(mode.atoms); if (words.length) { mode.helperType = mimes[0]; CodeMirror.registerHelper("hintWords", mimes[0], words); } for (var i = 0; i < mimes.length; ++i) CodeMirror.defineMIME(mimes[i], mode); } def(["text/x-csrc", "text/x-c", "text/x-chdr"], { name: "clike", keywords: words(cKeywords), types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " + "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " + "uint32_t uint64_t"), blockKeywords: words("case do else for if switch while struct"), defKeywords: words("struct"), typeFirstDefinitions: true, atoms: words("null true false"), hooks: {"#": cppHook, "*": pointerHook}, modeProps: {fold: ["brace", "include"]} }); def(["text/x-c++src", "text/x-c++hdr"], { name: "clike", keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " + "static_cast typeid catch operator template typename class friend private " + "this using const_cast inline public throw virtual delete mutable protected " + "alignas alignof constexpr decltype nullptr noexcept thread_local final " + "static_assert override"), types: words(cTypes + " bool wchar_t"), blockKeywords: words("catch class do else finally for if struct switch try while"), defKeywords: words("class namespace struct enum union"), typeFirstDefinitions: true, atoms: words("true false null"), dontIndentStatements: /^template$/, isIdentifierChar: /[\w\$_~\xa1-\uffff]/, hooks: { "#": cppHook, "*": pointerHook, "u": cpp11StringHook, "U": cpp11StringHook, "L": cpp11StringHook, "R": cpp11StringHook, "0": cpp14Literal, "1": cpp14Literal, "2": cpp14Literal, "3": cpp14Literal, "4": cpp14Literal, "5": cpp14Literal, "6": cpp14Literal, "7": cpp14Literal, "8": cpp14Literal, "9": cpp14Literal, token: function(stream, state, style) { if (style == "variable" && stream.peek() == "(" && (state.prevToken == ";" || state.prevToken == null || state.prevToken == "}") && cppLooksLikeConstructor(stream.current())) return "def"; } }, namespaceSeparator: "::", modeProps: {fold: ["brace", "include"]} }); def("text/x-java", { name: "clike", keywords: words("abstract assert break case catch class const continue default " + "do else enum extends final finally float for goto if implements import " + "instanceof interface native new package private protected public " + "return static strictfp super switch synchronized this throw throws transient " + "try volatile while @interface"), types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " + "Integer Long Number Object Short String StringBuffer StringBuilder Void"), blockKeywords: words("catch class do else finally for if switch try while"), defKeywords: words("class interface package enum @interface"), typeFirstDefinitions: true, atoms: words("true false null"), number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, hooks: { "@": function(stream) { // Don't match the @interface keyword. if (stream.match('interface', false)) return false; stream.eatWhile(/[\w\$_]/); return "meta"; } }, modeProps: {fold: ["brace", "import"]} }); def("text/x-csharp", { name: "clike", keywords: words("abstract as async await base break case catch checked class const continue" + " default delegate do else enum event explicit extern finally fixed for" + " foreach goto if implicit in interface internal is lock namespace new" + " operator out override params private protected public readonly ref return sealed" + " sizeof stackalloc static struct switch this throw try typeof unchecked" + " unsafe using virtual void volatile while add alias ascending descending dynamic from get" + " global group into join let orderby partial remove select set value var yield"), types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" + " Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" + " UInt64 bool byte char decimal double short int long object" + " sbyte float string ushort uint ulong"), blockKeywords: words("catch class do else finally for foreach if struct switch try while"), defKeywords: words("class interface namespace struct var"), typeFirstDefinitions: true, atoms: words("true false null"), hooks: { "@": function(stream, state) { if (stream.eat('"')) { state.tokenize = tokenAtString; return tokenAtString(stream, state); } stream.eatWhile(/[\w\$_]/); return "meta"; } } }); function tokenTripleString(stream, state) { var escaped = false; while (!stream.eol()) { if (!escaped && stream.match('"""')) { state.tokenize = null; break; } escaped = stream.next() == "\\" && !escaped; } return "string"; } def("text/x-scala", { name: "clike", keywords: words( /* scala */ "abstract case catch class def do else extends final finally for forSome if " + "implicit import lazy match new null object override package private protected return " + "sealed super this throw trait try type val var while with yield _ " + /* package scala */ "assert assume require print println printf readLine readBoolean readByte readShort " + "readChar readInt readLong readFloat readDouble" ), types: words( "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " + "Enumeration Equiv Error Exception Fractional Function IndexedSeq Int Integral Iterable " + "Iterator List Map Numeric Nil NotNull Option Ordered Ordering PartialFunction PartialOrdering " + "Product Proxy Range Responder Seq Serializable Set Specializable Stream StringBuilder " + "StringContext Symbol Throwable Traversable TraversableOnce Tuple Unit Vector " + /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" ), multiLineStrings: true, blockKeywords: words("catch class enum do else finally for forSome if match switch try while"), defKeywords: words("class enum def object package trait type val var"), atoms: words("true false null"), indentStatements: false, indentSwitch: false, isOperatorChar: /[+\-*&%=<>!?|\/#:@]/, hooks: { "@": function(stream) { stream.eatWhile(/[\w\$_]/); return "meta"; }, '"': function(stream, state) { if (!stream.match('""')) return false; state.tokenize = tokenTripleString; return state.tokenize(stream, state); }, "'": function(stream) { stream.eatWhile(/[\w\$_\xa1-\uffff]/); return "atom"; }, "=": function(stream, state) { var cx = state.context if (cx.type == "}" && cx.align && stream.eat(">")) { state.context = new Context(cx.indented, cx.column, cx.type, cx.info, null, cx.prev) return "operator" } else { return false } } }, modeProps: {closeBrackets: {triples: '"'}} }); function tokenKotlinString(tripleString){ return function (stream, state) { var escaped = false, next, end = false; while (!stream.eol()) { if (!tripleString && !escaped && stream.match('"') ) {end = true; break;} if (tripleString && stream.match('"""')) {end = true; break;} next = stream.next(); if(!escaped && next == "$" && stream.match('{')) stream.skipTo("}"); escaped = !escaped && next == "\\" && !tripleString; } if (end || !tripleString) state.tokenize = null; return "string"; } } def("text/x-kotlin", { name: "clike", keywords: words( /*keywords*/ "package as typealias class interface this super val " + "var fun for is in This throw return " + "break continue object if else while do try when !in !is as? " + /*soft keywords*/ "file import where by get set abstract enum open inner override private public internal " + "protected catch finally out final vararg reified dynamic companion constructor init " + "sealed field property receiver param sparam lateinit data inline noinline tailrec " + "external annotation crossinline const operator infix suspend" ), types: words( /* package java.lang */ "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " + "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " + "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " + "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void" ), intendSwitch: false, indentStatements: false, multiLineStrings: true, number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i, blockKeywords: words("catch class do else finally for if where try while enum"), defKeywords: words("class val var object package interface fun"), atoms: words("true false null this"), hooks: { '"': function(stream, state) { state.tokenize = tokenKotlinString(stream.match('""')); return state.tokenize(stream, state); } }, modeProps: {closeBrackets: {triples: '"'}} }); def(["x-shader/x-vertex", "x-shader/x-fragment"], { name: "clike", keywords: words("sampler1D sampler2D sampler3D samplerCube " + "sampler1DShadow sampler2DShadow " + "const attribute uniform varying " + "break continue discard return " + "for while do if else struct " + "in out inout"), types: words("float int bool void " + "vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4 " + "mat2 mat3 mat4"), blockKeywords: words("for while do if else struct"), builtin: words("radians degrees sin cos tan asin acos atan " + "pow exp log exp2 sqrt inversesqrt " + "abs sign floor ceil fract mod min max clamp mix step smoothstep " + "length distance dot cross normalize ftransform faceforward " + "reflect refract matrixCompMult " + "lessThan lessThanEqual greaterThan greaterThanEqual " + "equal notEqual any all not " + "texture1D texture1DProj texture1DLod texture1DProjLod " + "texture2D texture2DProj texture2DLod texture2DProjLod " + "texture3D texture3DProj texture3DLod texture3DProjLod " + "textureCube textureCubeLod " + "shadow1D shadow2D shadow1DProj shadow2DProj " + "shadow1DLod shadow2DLod shadow1DProjLod shadow2DProjLod " + "dFdx dFdy fwidth " + "noise1 noise2 noise3 noise4"), atoms: words("true false " + "gl_FragColor gl_SecondaryColor gl_Normal gl_Vertex " + "gl_MultiTexCoord0 gl_MultiTexCoord1 gl_MultiTexCoord2 gl_MultiTexCoord3 " + "gl_MultiTexCoord4 gl_MultiTexCoord5 gl_MultiTexCoord6 gl_MultiTexCoord7 " + "gl_FogCoord gl_PointCoord " + "gl_Position gl_PointSize gl_ClipVertex " + "gl_FrontColor gl_BackColor gl_FrontSecondaryColor gl_BackSecondaryColor " + "gl_TexCoord gl_FogFragCoord " + "gl_FragCoord gl_FrontFacing " + "gl_FragData gl_FragDepth " + "gl_ModelViewMatrix gl_ProjectionMatrix gl_ModelViewProjectionMatrix " + "gl_TextureMatrix gl_NormalMatrix gl_ModelViewMatrixInverse " + "gl_ProjectionMatrixInverse gl_ModelViewProjectionMatrixInverse " + "gl_TexureMatrixTranspose gl_ModelViewMatrixInverseTranspose " + "gl_ProjectionMatrixInverseTranspose " + "gl_ModelViewProjectionMatrixInverseTranspose " + "gl_TextureMatrixInverseTranspose " + "gl_NormalScale gl_DepthRange gl_ClipPlane " + "gl_Point gl_FrontMaterial gl_BackMaterial gl_LightSource gl_LightModel " + "gl_FrontLightModelProduct gl_BackLightModelProduct " + "gl_TextureColor gl_EyePlaneS gl_EyePlaneT gl_EyePlaneR gl_EyePlaneQ " + "gl_FogParameters " + "gl_MaxLights gl_MaxClipPlanes gl_MaxTextureUnits gl_MaxTextureCoords " + "gl_MaxVertexAttribs gl_MaxVertexUniformComponents gl_MaxVaryingFloats " + "gl_MaxVertexTextureImageUnits gl_MaxTextureImageUnits " + "gl_MaxFragmentUniformComponents gl_MaxCombineTextureImageUnits " + "gl_MaxDrawBuffers"), indentSwitch: false, hooks: {"#": cppHook}, modeProps: {fold: ["brace", "include"]} }); def("text/x-nesc", { name: "clike", keywords: words(cKeywords + "as atomic async call command component components configuration event generic " + "implementation includes interface module new norace nx_struct nx_union post provides " + "signal task uses abstract extends"), types: words(cTypes), blockKeywords: words("case do else for if switch while struct"), atoms: words("null true false"), hooks: {"#": cppHook}, modeProps: {fold: ["brace", "include"]} }); def("text/x-objectivec", { name: "clike", keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in " + "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"), types: words(cTypes), atoms: words("YES NO NULL NILL ON OFF true false"), hooks: { "@": function(stream) { stream.eatWhile(/[\w\$]/); return "keyword"; }, "#": cppHook, indent: function(_state, ctx, textAfter) { if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented } }, modeProps: {fold: "brace"} }); def("text/x-squirrel", { name: "clike", keywords: words("base break clone continue const default delete enum extends function in class" + " foreach local resume return this throw typeof yield constructor instanceof static"), types: words(cTypes), blockKeywords: words("case catch class else for foreach if switch try while"), defKeywords: words("function local class"), typeFirstDefinitions: true, atoms: words("true false null"), hooks: {"#": cppHook}, modeProps: {fold: ["brace", "include"]} }); // Ceylon Strings need to deal with interpolation var stringTokenizer = null; function tokenCeylonString(type) { return function(stream, state) { var escaped = false, next, end = false; while (!stream.eol()) { if (!escaped && stream.match('"') && (type == "single" || stream.match('""'))) { end = true; break; } if (!escaped && stream.match('``')) { stringTokenizer = tokenCeylonString(type); end = true; break; } next = stream.next(); escaped = type == "single" && !escaped && next == "\\"; } if (end) state.tokenize = null; return "string"; } } def("text/x-ceylon", { name: "clike", keywords: words("abstracts alias assembly assert assign break case catch class continue dynamic else" + " exists extends finally for function given if import in interface is let module new" + " nonempty object of out outer package return satisfies super switch then this throw" + " try value void while"), types: function(word) { // In Ceylon all identifiers that start with an uppercase are types var first = word.charAt(0); return (first === first.toUpperCase() && first !== first.toLowerCase()); }, blockKeywords: words("case catch class dynamic else finally for function if interface module new object switch try while"), defKeywords: words("class dynamic function interface module object package value"), builtin: words("abstract actual aliased annotation by default deprecated doc final formal late license" + " native optional sealed see serializable shared suppressWarnings tagged throws variable"), isPunctuationChar: /[\[\]{}\(\),;\:\.`]/, isOperatorChar: /[+\-*&%=<>!?|^~:\/]/, numberStart: /[\d#$]/, number: /^(?:#[\da-fA-F_]+|\$[01_]+|[\d_]+[kMGTPmunpf]?|[\d_]+\.[\d_]+(?:[eE][-+]?\d+|[kMGTPmunpf]|)|)/i, multiLineStrings: true, typeFirstDefinitions: true, atoms: words("true false null larger smaller equal empty finished"), indentSwitch: false, styleDefs: false, hooks: { "@": function(stream) { stream.eatWhile(/[\w\$_]/); return "meta"; }, '"': function(stream, state) { state.tokenize = tokenCeylonString(stream.match('""') ? "triple" : "single"); return state.tokenize(stream, state); }, '`': function(stream, state) { if (!stringTokenizer || !stream.match('`')) return false; state.tokenize = stringTokenizer; stringTokenizer = null; return state.tokenize(stream, state); }, "'": function(stream) { stream.eatWhile(/[\w\$_\xa1-\uffff]/); return "atom"; }, token: function(_stream, state, style) { if ((style == "variable" || style == "type") && state.prevToken == ".") { return "variable-2"; } } }, modeProps: { fold: ["brace", "import"], closeBrackets: {triples: '"'} } }); }); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/codemirror.lib.json-lint.js ================================================ /* * Copyright 1999-2018 Alibaba Group Holding Ltd. * * 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. */ /* Jison generated parser */ var jsonlint = (function(){ var parser = {trace: function trace() { }, yy: {}, symbols_: {"error":2,"JSONString":3,"STRING":4,"JSONNumber":5,"NUMBER":6,"JSONNullLiteral":7,"NULL":8,"JSONBooleanLiteral":9,"TRUE":10,"FALSE":11,"JSONText":12,"JSONValue":13,"EOF":14,"JSONObject":15,"JSONArray":16,"{":17,"}":18,"JSONMemberList":19,"JSONMember":20,":":21,",":22,"[":23,"]":24,"JSONElementList":25,"$accept":0,"$end":1}, terminals_: {2:"error",4:"STRING",6:"NUMBER",8:"NULL",10:"TRUE",11:"FALSE",14:"EOF",17:"{",18:"}",21:":",22:",",23:"[",24:"]"}, productions_: [0,[3,1],[5,1],[7,1],[9,1],[9,1],[12,2],[13,1],[13,1],[13,1],[13,1],[13,1],[13,1],[15,2],[15,3],[20,3],[19,1],[19,3],[16,2],[16,3],[25,1],[25,3]], performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { var $0 = $$.length - 1; switch (yystate) { case 1: // replace escaped characters with actual character this.$ = yytext.replace(/\\(\\|")/g, "$"+"1") .replace(/\\n/g,'\n') .replace(/\\r/g,'\r') .replace(/\\t/g,'\t') .replace(/\\v/g,'\v') .replace(/\\f/g,'\f') .replace(/\\b/g,'\b'); break; case 2:this.$ = Number(yytext); break; case 3:this.$ = null; break; case 4:this.$ = true; break; case 5:this.$ = false; break; case 6:return this.$ = $$[$0-1]; break; case 13:this.$ = {}; break; case 14:this.$ = $$[$0-1]; break; case 15:this.$ = [$$[$0-2], $$[$0]]; break; case 16:this.$ = {}; this.$[$$[$0][0]] = $$[$0][1]; break; case 17:this.$ = $$[$0-2]; $$[$0-2][$$[$0][0]] = $$[$0][1]; break; case 18:this.$ = []; break; case 19:this.$ = $$[$0-1]; break; case 20:this.$ = [$$[$0]]; break; case 21:this.$ = $$[$0-2]; $$[$0-2].push($$[$0]); break; } }, table: [{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],12:1,13:2,15:7,16:8,17:[1,14],23:[1,15]},{1:[3]},{14:[1,16]},{14:[2,7],18:[2,7],22:[2,7],24:[2,7]},{14:[2,8],18:[2,8],22:[2,8],24:[2,8]},{14:[2,9],18:[2,9],22:[2,9],24:[2,9]},{14:[2,10],18:[2,10],22:[2,10],24:[2,10]},{14:[2,11],18:[2,11],22:[2,11],24:[2,11]},{14:[2,12],18:[2,12],22:[2,12],24:[2,12]},{14:[2,3],18:[2,3],22:[2,3],24:[2,3]},{14:[2,4],18:[2,4],22:[2,4],24:[2,4]},{14:[2,5],18:[2,5],22:[2,5],24:[2,5]},{14:[2,1],18:[2,1],21:[2,1],22:[2,1],24:[2,1]},{14:[2,2],18:[2,2],22:[2,2],24:[2,2]},{3:20,4:[1,12],18:[1,17],19:18,20:19},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:23,15:7,16:8,17:[1,14],23:[1,15],24:[1,21],25:22},{1:[2,6]},{14:[2,13],18:[2,13],22:[2,13],24:[2,13]},{18:[1,24],22:[1,25]},{18:[2,16],22:[2,16]},{21:[1,26]},{14:[2,18],18:[2,18],22:[2,18],24:[2,18]},{22:[1,28],24:[1,27]},{22:[2,20],24:[2,20]},{14:[2,14],18:[2,14],22:[2,14],24:[2,14]},{3:20,4:[1,12],20:29},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:30,15:7,16:8,17:[1,14],23:[1,15]},{14:[2,19],18:[2,19],22:[2,19],24:[2,19]},{3:5,4:[1,12],5:6,6:[1,13],7:3,8:[1,9],9:4,10:[1,10],11:[1,11],13:31,15:7,16:8,17:[1,14],23:[1,15]},{18:[2,17],22:[2,17]},{18:[2,15],22:[2,15]},{22:[2,21],24:[2,21]}], defaultActions: {16:[2,6]}, parseError: function parseError(str, hash) { throw new Error(str); }, parse: function parse(input) { var self = this, stack = [0], vstack = [null], // semantic value stack lstack = [], // location stack table = this.table, yytext = '', yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; //this.reductionCount = this.shiftCount = 0; this.lexer.setInput(input); this.lexer.yy = this.yy; this.yy.lexer = this.lexer; if (typeof this.lexer.yylloc == 'undefined') this.lexer.yylloc = {}; var yyloc = this.lexer.yylloc; lstack.push(yyloc); if (typeof this.yy.parseError === 'function') this.parseError = this.yy.parseError; function popStack (n) { stack.length = stack.length - 2*n; vstack.length = vstack.length - n; lstack.length = lstack.length - n; } function lex() { var token; token = self.lexer.lex() || 1; // $end = 1 // if token isn't its numeric value, convert if (typeof token !== 'number') { token = self.symbols_[token] || token; } return token; } var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; while (true) { // retreive state number from top of stack state = stack[stack.length-1]; // use default actions if available if (this.defaultActions[state]) { action = this.defaultActions[state]; } else { if (symbol == null) symbol = lex(); // read action for current state and first input action = table[state] && table[state][symbol]; } // handle parse error _handle_error: if (typeof action === 'undefined' || !action.length || !action[0]) { if (!recovering) { // Report error expected = []; for (p in table[state]) if (this.terminals_[p] && p > 2) { expected.push("'"+this.terminals_[p]+"'"); } var errStr = ''; if (this.lexer.showPosition) { errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'"; } else { errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + (symbol == 1 /*EOF*/ ? "end of input" : ("'"+(this.terminals_[symbol] || symbol)+"'")); } this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); } // just recovered from another error if (recovering == 3) { if (symbol == EOF) { throw new Error(errStr || 'Parsing halted.'); } // discard current lookahead and grab another yyleng = this.lexer.yyleng; yytext = this.lexer.yytext; yylineno = this.lexer.yylineno; yyloc = this.lexer.yylloc; symbol = lex(); } // try to recover from error while (1) { // check for error recovery rule in this state if ((TERROR.toString()) in table[state]) { break; } if (state == 0) { throw new Error(errStr || 'Parsing halted.'); } popStack(1); state = stack[stack.length-1]; } preErrorSymbol = symbol; // save the lookahead token symbol = TERROR; // insert generic error symbol as new lookahead state = stack[stack.length-1]; action = table[state] && table[state][TERROR]; recovering = 3; // allow 3 real symbols to be shifted before reporting a new error } // this shouldn't happen, unless resolve defaults are off if (action[0] instanceof Array && action.length > 1) { throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); } switch (action[0]) { case 1: // shift //this.shiftCount++; stack.push(symbol); vstack.push(this.lexer.yytext); lstack.push(this.lexer.yylloc); stack.push(action[1]); // push state symbol = null; if (!preErrorSymbol) { // normal execution/no error yyleng = this.lexer.yyleng; yytext = this.lexer.yytext; yylineno = this.lexer.yylineno; yyloc = this.lexer.yylloc; if (recovering > 0) recovering--; } else { // error just occurred, resume old lookahead f/ before error symbol = preErrorSymbol; preErrorSymbol = null; } break; case 2: // reduce //this.reductionCount++; len = this.productions_[action[1]][1]; // perform semantic action yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 // default location, uses first token for firsts, last for lasts yyval._$ = { first_line: lstack[lstack.length-(len||1)].first_line, last_line: lstack[lstack.length-1].last_line, first_column: lstack[lstack.length-(len||1)].first_column, last_column: lstack[lstack.length-1].last_column }; r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); if (typeof r !== 'undefined') { return r; } // pop off stack if (len) { stack = stack.slice(0,-1*len*2); vstack = vstack.slice(0, -1*len); lstack = lstack.slice(0, -1*len); } stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) vstack.push(yyval.$); lstack.push(yyval._$); // goto new state = table[STATE][NONTERMINAL] newState = table[stack[stack.length-2]][stack[stack.length-1]]; stack.push(newState); break; case 3: // accept return true; } } return true; }}; /* Jison generated lexer */ var lexer = (function(){ var lexer = ({EOF:1, parseError:function parseError(str, hash) { if (this.yy.parseError) { this.yy.parseError(str, hash); } else { throw new Error(str); } }, setInput:function (input) { this._input = input; this._more = this._less = this.done = false; this.yylineno = this.yyleng = 0; this.yytext = this.matched = this.match = ''; this.conditionStack = ['INITIAL']; this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; return this; }, input:function () { var ch = this._input[0]; this.yytext+=ch; this.yyleng++; this.match+=ch; this.matched+=ch; var lines = ch.match(/\n/); if (lines) this.yylineno++; this._input = this._input.slice(1); return ch; }, unput:function (ch) { this._input = ch + this._input; return this; }, more:function () { this._more = true; return this; }, less:function (n) { this._input = this.match.slice(n) + this._input; }, pastInput:function () { var past = this.matched.substr(0, this.matched.length - this.match.length); return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); }, upcomingInput:function () { var next = this.match; if (next.length < 20) { next += this._input.substr(0, 20-next.length); } return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); }, showPosition:function () { var pre = this.pastInput(); var c = new Array(pre.length + 1).join("-"); return pre + this.upcomingInput() + "\n" + c+"^"; }, next:function () { if (this.done) { return this.EOF; } if (!this._input) this.done = true; var token, match, tempMatch, index, col, lines; if (!this._more) { this.yytext = ''; this.match = ''; } var rules = this._currentRules(); for (var i=0;i < rules.length; i++) { tempMatch = this._input.match(this.rules[rules[i]]); if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { match = tempMatch; index = i; if (!this.options.flex) break; } } if (match) { lines = match[0].match(/\n.*/g); if (lines) this.yylineno += lines.length; this.yylloc = {first_line: this.yylloc.last_line, last_line: this.yylineno+1, first_column: this.yylloc.last_column, last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length} this.yytext += match[0]; this.match += match[0]; this.yyleng = this.yytext.length; this._more = false; this._input = this._input.slice(match[0].length); this.matched += match[0]; token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); if (this.done && this._input) this.done = false; if (token) return token; else return; } if (this._input === "") { return this.EOF; } else { this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), {text: "", token: null, line: this.yylineno}); } }, lex:function lex() { var r = this.next(); if (typeof r !== 'undefined') { return r; } else { return this.lex(); } }, begin:function begin(condition) { this.conditionStack.push(condition); }, popState:function popState() { return this.conditionStack.pop(); }, _currentRules:function _currentRules() { return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; }, topState:function () { return this.conditionStack[this.conditionStack.length-2]; }, pushState:function begin(condition) { this.begin(condition); }}); lexer.options = {}; lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { var YYSTATE=YY_START switch($avoiding_name_collisions) { case 0:/* skip whitespace */ break; case 1:return 6 break; case 2:yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2); return 4 break; case 3:return 17 break; case 4:return 18 break; case 5:return 23 break; case 6:return 24 break; case 7:return 22 break; case 8:return 21 break; case 9:return 10 break; case 10:return 11 break; case 11:return 8 break; case 12:return 14 break; case 13:return 'INVALID' break; } }; lexer.rules = [/^(?:\s+)/,/^(?:(-?([0-9]|[1-9][0-9]+))(\.[0-9]+)?([eE][-+]?[0-9]+)?\b)/,/^(?:"(?:\\[\\"bfnrt/]|\\u[a-fA-F0-9]{4}|[^\\\0-\x09\x0a-\x1f"])*")/,/^(?:\{)/,/^(?:\})/,/^(?:\[)/,/^(?:\])/,/^(?:,)/,/^(?::)/,/^(?:true\b)/,/^(?:false\b)/,/^(?:null\b)/,/^(?:$)/,/^(?:.)/]; lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"inclusive":true}}; ; return lexer;})() parser.lexer = lexer; return parser; })(); if (typeof require !== 'undefined' && typeof exports !== 'undefined') { exports.parser = jsonlint; exports.parse = function () { return jsonlint.parse.apply(jsonlint, arguments); } exports.main = function commonjsMain(args) { if (!args[1]) throw new Error('Usage: '+args[0]+' FILE'); if (typeof process !== 'undefined') { var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8"); } else { var cwd = require("file").path(require("file").cwd()); var source = cwd.join(args[1]).read({charset: "utf-8"}); } return exports.parser.parse(source); } if (typeof module !== 'undefined' && require.main === module) { exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); } } ================================================ FILE: console/src/main/resources/static/console-ui/public/js/diff_match_patch.js ================================================ (function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32} diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b);c=a.substring(0,f);a=a.substring(f);b=b.substring(f);var f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f);a=a.substring(0,a.length-f);b=b.substring(0,b.length-f);a=this.diff_compute_(a, b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a}; diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);return-1!=g?(c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c):1==f.length?[[-1,a],[1,b]]:(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100c);v++){for(var n=-v+r;n<=v-t;n+=2){var l=g+n,m;m=n==-v||n!=v&&j[l-1]d)t+=2;else if(s>e)r+=2;else if(q&&(l=g+k-n,0<=l&&l= u)return this.diff_bisectSplit_(a,b,m,s,c)}}for(n=-v+p;n<=v-w;n+=2){l=g+n;u=n==-v||n!=v&&i[l-1]d)w+=2;else if(m>e)p+=2;else if(!q&&(l=g+k-n,0<=l&&(l=u)))return this.diff_bisectSplit_(a,b,m,s,c)}}return[[-1,a],[1,b]]}; diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d);a=a.substring(c);b=b.substring(d);f=this.diff_main(f,g,!1,e);e=this.diff_main(a,b,!1,e);return f.concat(e)}; diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;fd?a=a.substring(c-d):c=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null; var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.lengthd[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]}; diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}}; diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_); return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]= h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/; diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;fb)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)}; diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=//g,f=/\n/g,g=0;g");switch(h){case 1:b[g]=''+j+"";break;case -1:b[g]=''+j+"";break;case 0:b[g]=""+j+""}}return b.join("")}; diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;cthis.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<=i;p--){var w=e[a.charAt(p-1)];k[p]=0===t?(k[p+1]<<1|1)&w:(k[p+1]<<1|1)&w|((r[p+1]|r[p])<<1|1)|r[p+1];if(k[p]&j&&(w=d(t,p-1),w<=g))if(g=w,h=p-1,h>c)i=Math.max(1,2*c-h);else break}if(d(t+1,c)>g)break;r=k}return h}; diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c=2*this.Patch_Margin&& e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;cthis.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g); if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;ie[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0, c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c}; diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&& (h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c)$/.test(state.lastType) || (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0)))) } CodeMirror.defineMode("javascript", function(config, parserConfig) { var indentUnit = config.indentUnit; var statementIndent = parserConfig.statementIndent; var jsonldMode = parserConfig.jsonld; var jsonMode = parserConfig.json || jsonldMode; var isTS = parserConfig.typescript; var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/; // Tokenizer var keywords = function(){ function kw(type) {return {type: type, style: "keyword"};} var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"); var operator = kw("operator"), atom = {type: "atom", style: "atom"}; var jsKeywords = { "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B, "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C, "var": kw("var"), "const": kw("var"), "let": kw("var"), "function": kw("function"), "catch": kw("catch"), "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"), "in": operator, "typeof": operator, "instanceof": operator, "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom, "this": kw("this"), "class": kw("class"), "super": kw("atom"), "yield": C, "export": kw("export"), "import": kw("import"), "extends": C, "await": C }; // Extend the 'normal' keywords with the TypeScript language extensions if (isTS) { var type = {type: "variable", style: "type"}; var tsKeywords = { // object-like things "interface": kw("class"), "implements": C, "namespace": C, "module": kw("module"), "enum": kw("module"), // scope modifiers "public": kw("modifier"), "private": kw("modifier"), "protected": kw("modifier"), "abstract": kw("modifier"), // types "string": type, "number": type, "boolean": type, "any": type }; for (var attr in tsKeywords) { jsKeywords[attr] = tsKeywords[attr]; } } return jsKeywords; }(); var isOperatorChar = /[+\-*&%=<>!?|~^@]/; var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/; function readRegexp(stream) { var escaped = false, next, inSet = false; while ((next = stream.next()) != null) { if (!escaped) { if (next == "/" && !inSet) return; if (next == "[") inSet = true; else if (inSet && next == "]") inSet = false; } escaped = !escaped && next == "\\"; } } // Used as scratch variables to communicate multiple values without // consing up tons of objects. var type, content; function ret(tp, style, cont) { type = tp; content = cont; return style; } function tokenBase(stream, state) { var ch = stream.next(); if (ch == '"' || ch == "'") { state.tokenize = tokenString(ch); return state.tokenize(stream, state); } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) { return ret("number", "number"); } else if (ch == "." && stream.match("..")) { return ret("spread", "meta"); } else if (/[\[\]{}\(\),;\:\.]/.test(ch)) { return ret(ch); } else if (ch == "=" && stream.eat(">")) { return ret("=>", "operator"); } else if (ch == "0" && stream.eat(/x/i)) { stream.eatWhile(/[\da-f]/i); return ret("number", "number"); } else if (ch == "0" && stream.eat(/o/i)) { stream.eatWhile(/[0-7]/i); return ret("number", "number"); } else if (ch == "0" && stream.eat(/b/i)) { stream.eatWhile(/[01]/i); return ret("number", "number"); } else if (/\d/.test(ch)) { stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/); return ret("number", "number"); } else if (ch == "/") { if (stream.eat("*")) { state.tokenize = tokenComment; return tokenComment(stream, state); } else if (stream.eat("/")) { stream.skipToEnd(); return ret("comment", "comment"); } else if (expressionAllowed(stream, state, 1)) { readRegexp(stream); stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/); return ret("regexp", "string-2"); } else { stream.eatWhile(isOperatorChar); return ret("operator", "operator", stream.current()); } } else if (ch == "`") { state.tokenize = tokenQuasi; return tokenQuasi(stream, state); } else if (ch == "#") { stream.skipToEnd(); return ret("error", "error"); } else if (isOperatorChar.test(ch)) { if (ch != ">" || !state.lexical || state.lexical.type != ">") stream.eatWhile(isOperatorChar); return ret("operator", "operator", stream.current()); } else if (wordRE.test(ch)) { stream.eatWhile(wordRE); var word = stream.current() if (state.lastType != ".") { if (keywords.propertyIsEnumerable(word)) { var kw = keywords[word] return ret(kw.type, kw.style, word) } if (word == "async" && stream.match(/^\s*[\(\w]/, false)) return ret("async", "keyword", word) } return ret("variable", "variable", word) } } function tokenString(quote) { return function(stream, state) { var escaped = false, next; if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){ state.tokenize = tokenBase; return ret("jsonld-keyword", "meta"); } while ((next = stream.next()) != null) { if (next == quote && !escaped) break; escaped = !escaped && next == "\\"; } if (!escaped) state.tokenize = tokenBase; return ret("string", "string"); }; } function tokenComment(stream, state) { var maybeEnd = false, ch; while (ch = stream.next()) { if (ch == "/" && maybeEnd) { state.tokenize = tokenBase; break; } maybeEnd = (ch == "*"); } return ret("comment", "comment"); } function tokenQuasi(stream, state) { var escaped = false, next; while ((next = stream.next()) != null) { if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) { state.tokenize = tokenBase; break; } escaped = !escaped && next == "\\"; } return ret("quasi", "string-2", stream.current()); } var brackets = "([{}])"; // This is a crude lookahead trick to try and notice that we're // parsing the argument patterns for a fat-arrow function before we // actually hit the arrow token. It only works if the arrow is on // the same line as the arguments and there's no strange noise // (comments) in between. Fallback is to only notice when we hit the // arrow, and not declare the arguments as locals for the arrow // body. function findFatArrow(stream, state) { if (state.fatArrowAt) state.fatArrowAt = null; var arrow = stream.string.indexOf("=>", stream.start); if (arrow < 0) return; if (isTS) { // Try to skip TypeScript return type declarations after the arguments var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow)) if (m) arrow = m.index } var depth = 0, sawSomething = false; for (var pos = arrow - 1; pos >= 0; --pos) { var ch = stream.string.charAt(pos); var bracket = brackets.indexOf(ch); if (bracket >= 0 && bracket < 3) { if (!depth) { ++pos; break; } if (--depth == 0) { if (ch == "(") sawSomething = true; break; } } else if (bracket >= 3 && bracket < 6) { ++depth; } else if (wordRE.test(ch)) { sawSomething = true; } else if (/["'\/]/.test(ch)) { return; } else if (sawSomething && !depth) { ++pos; break; } } if (sawSomething && !depth) state.fatArrowAt = pos; } // Parser var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true}; function JSLexical(indented, column, type, align, prev, info) { this.indented = indented; this.column = column; this.type = type; this.prev = prev; this.info = info; if (align != null) this.align = align; } function inScope(state, varname) { for (var v = state.localVars; v; v = v.next) if (v.name == varname) return true; for (var cx = state.context; cx; cx = cx.prev) { for (var v = cx.vars; v; v = v.next) if (v.name == varname) return true; } } function parseJS(state, style, type, content, stream) { var cc = state.cc; // Communicate our context to the combinators. // (Less wasteful than consing up a hundred closures on every call.) cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style; if (!state.lexical.hasOwnProperty("align")) state.lexical.align = true; while(true) { var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement; if (combinator(type, content)) { while(cc.length && cc[cc.length - 1].lex) cc.pop()(); if (cx.marked) return cx.marked; if (type == "variable" && inScope(state, content)) return "variable-2"; return style; } } } // Combinator utils var cx = {state: null, column: null, marked: null, cc: null}; function pass() { for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]); } function cont() { pass.apply(null, arguments); return true; } function register(varname) { function inList(list) { for (var v = list; v; v = v.next) if (v.name == varname) return true; return false; } var state = cx.state; cx.marked = "def"; if (state.context) { if (inList(state.localVars)) return; state.localVars = {name: varname, next: state.localVars}; } else { if (inList(state.globalVars)) return; if (parserConfig.globalVars) state.globalVars = {name: varname, next: state.globalVars}; } } // Combinators var defaultVars = {name: "this", next: {name: "arguments"}}; function pushcontext() { cx.state.context = {prev: cx.state.context, vars: cx.state.localVars}; cx.state.localVars = defaultVars; } function popcontext() { cx.state.localVars = cx.state.context.vars; cx.state.context = cx.state.context.prev; } function pushlex(type, info) { var result = function() { var state = cx.state, indent = state.indented; if (state.lexical.type == "stat") indent = state.lexical.indented; else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev) indent = outer.indented; state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info); }; result.lex = true; return result; } function poplex() { var state = cx.state; if (state.lexical.prev) { if (state.lexical.type == ")") state.indented = state.lexical.indented; state.lexical = state.lexical.prev; } } poplex.lex = true; function expect(wanted) { function exp(type) { if (type == wanted) return cont(); else if (wanted == ";") return pass(); else return cont(exp); }; return exp; } function statement(type, value) { if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex); if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex); if (type == "keyword b") return cont(pushlex("form"), statement, poplex); if (type == "{") return cont(pushlex("}"), block, poplex); if (type == ";") return cont(); if (type == "if") { if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex) cx.state.cc.pop()(); return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse); } if (type == "function") return cont(functiondef); if (type == "for") return cont(pushlex("form"), forspec, statement, poplex); if (type == "variable") { if (isTS && value == "type") { cx.marked = "keyword" return cont(typeexpr, expect("operator"), typeexpr, expect(";")); } else { return cont(pushlex("stat"), maybelabel); } } if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), block, poplex, poplex); if (type == "case") return cont(expression, expect(":")); if (type == "default") return cont(expect(":")); if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"), statement, poplex, popcontext); if (type == "class") return cont(pushlex("form"), className, poplex); if (type == "export") return cont(pushlex("stat"), afterExport, poplex); if (type == "import") return cont(pushlex("stat"), afterImport, poplex); if (type == "module") return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex) if (type == "async") return cont(statement) if (value == "@") return cont(expression, statement) return pass(pushlex("stat"), expression, expect(";"), poplex); } function expression(type) { return expressionInner(type, false); } function expressionNoComma(type) { return expressionInner(type, true); } function parenExpr(type) { if (type != "(") return pass() return cont(pushlex(")"), expression, expect(")"), poplex) } function expressionInner(type, noComma) { if (cx.state.fatArrowAt == cx.stream.start) { var body = noComma ? arrowBodyNoComma : arrowBody; if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext); else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext); } var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma; if (atomicTypes.hasOwnProperty(type)) return cont(maybeop); if (type == "function") return cont(functiondef, maybeop); if (type == "class") return cont(pushlex("form"), classExpression, poplex); if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression); if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop); if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression); if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop); if (type == "{") return contCommasep(objprop, "}", null, maybeop); if (type == "quasi") return pass(quasi, maybeop); if (type == "new") return cont(maybeTarget(noComma)); return cont(); } function maybeexpression(type) { if (type.match(/[;\}\)\],]/)) return pass(); return pass(expression); } function maybeexpressionNoComma(type) { if (type.match(/[;\}\)\],]/)) return pass(); return pass(expressionNoComma); } function maybeoperatorComma(type, value) { if (type == ",") return cont(expression); return maybeoperatorNoComma(type, value, false); } function maybeoperatorNoComma(type, value, noComma) { var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma; var expr = noComma == false ? expression : expressionNoComma; if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext); if (type == "operator") { if (/\+\+|--/.test(value)) return cont(me); if (value == "?") return cont(expression, expect(":"), expr); return cont(expr); } if (type == "quasi") { return pass(quasi, me); } if (type == ";") return; if (type == "(") return contCommasep(expressionNoComma, ")", "call", me); if (type == ".") return cont(property, me); if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me); if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) } } function quasi(type, value) { if (type != "quasi") return pass(); if (value.slice(value.length - 2) != "${") return cont(quasi); return cont(expression, continueQuasi); } function continueQuasi(type) { if (type == "}") { cx.marked = "string-2"; cx.state.tokenize = tokenQuasi; return cont(quasi); } } function arrowBody(type) { findFatArrow(cx.stream, cx.state); return pass(type == "{" ? statement : expression); } function arrowBodyNoComma(type) { findFatArrow(cx.stream, cx.state); return pass(type == "{" ? statement : expressionNoComma); } function maybeTarget(noComma) { return function(type) { if (type == ".") return cont(noComma ? targetNoComma : target); else return pass(noComma ? expressionNoComma : expression); }; } function target(_, value) { if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); } } function targetNoComma(_, value) { if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); } } function maybelabel(type) { if (type == ":") return cont(poplex, statement); return pass(maybeoperatorComma, expect(";"), poplex); } function property(type) { if (type == "variable") {cx.marked = "property"; return cont();} } function objprop(type, value) { if (type == "async") { cx.marked = "property"; return cont(objprop); } else if (type == "variable" || cx.style == "keyword") { cx.marked = "property"; if (value == "get" || value == "set") return cont(getterSetter); return cont(afterprop); } else if (type == "number" || type == "string") { cx.marked = jsonldMode ? "property" : (cx.style + " property"); return cont(afterprop); } else if (type == "jsonld-keyword") { return cont(afterprop); } else if (type == "modifier") { return cont(objprop) } else if (type == "[") { return cont(expression, expect("]"), afterprop); } else if (type == "spread") { return cont(expression, afterprop); } else if (type == ":") { return pass(afterprop) } } function getterSetter(type) { if (type != "variable") return pass(afterprop); cx.marked = "property"; return cont(functiondef); } function afterprop(type) { if (type == ":") return cont(expressionNoComma); if (type == "(") return pass(functiondef); } function commasep(what, end, sep) { function proceed(type, value) { if (sep ? sep.indexOf(type) > -1 : type == ",") { var lex = cx.state.lexical; if (lex.info == "call") lex.pos = (lex.pos || 0) + 1; return cont(function(type, value) { if (type == end || value == end) return pass() return pass(what) }, proceed); } if (type == end || value == end) return cont(); return cont(expect(end)); } return function(type, value) { if (type == end || value == end) return cont(); return pass(what, proceed); }; } function contCommasep(what, end, info) { for (var i = 3; i < arguments.length; i++) cx.cc.push(arguments[i]); return cont(pushlex(end, info), commasep(what, end), poplex); } function block(type) { if (type == "}") return cont(); return pass(statement, block); } function maybetype(type, value) { if (isTS) { if (type == ":") return cont(typeexpr); if (value == "?") return cont(maybetype); } } function typeexpr(type) { if (type == "variable") {cx.marked = "type"; return cont(afterType);} if (type == "string" || type == "number" || type == "atom") return cont(afterType); if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType) if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType) } function maybeReturnType(type) { if (type == "=>") return cont(typeexpr) } function typeprop(type, value) { if (type == "variable" || cx.style == "keyword") { cx.marked = "property" return cont(typeprop) } else if (value == "?") { return cont(typeprop) } else if (type == ":") { return cont(typeexpr) } else if (type == "[") { return cont(expression, maybetype, expect("]"), typeprop) } } function typearg(type) { if (type == "variable") return cont(typearg) else if (type == ":") return cont(typeexpr) } function afterType(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType) if (value == "|" || type == ".") return cont(typeexpr) if (type == "[") return cont(expect("]"), afterType) if (value == "extends") return cont(typeexpr) } function vardef() { return pass(pattern, maybetype, maybeAssign, vardefCont); } function pattern(type, value) { if (type == "modifier") return cont(pattern) if (type == "variable") { register(value); return cont(); } if (type == "spread") return cont(pattern); if (type == "[") return contCommasep(pattern, "]"); if (type == "{") return contCommasep(proppattern, "}"); } function proppattern(type, value) { if (type == "variable" && !cx.stream.match(/^\s*:/, false)) { register(value); return cont(maybeAssign); } if (type == "variable") cx.marked = "property"; if (type == "spread") return cont(pattern); if (type == "}") return pass(); return cont(expect(":"), pattern, maybeAssign); } function maybeAssign(_type, value) { if (value == "=") return cont(expressionNoComma); } function vardefCont(type) { if (type == ",") return cont(vardef); } function maybeelse(type, value) { if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex); } function forspec(type) { if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex); } function forspec1(type) { if (type == "var") return cont(vardef, expect(";"), forspec2); if (type == ";") return cont(forspec2); if (type == "variable") return cont(formaybeinof); return pass(expression, expect(";"), forspec2); } function formaybeinof(_type, value) { if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } return cont(maybeoperatorComma, forspec2); } function forspec2(type, value) { if (type == ";") return cont(forspec3); if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); } return pass(expression, expect(";"), forspec3); } function forspec3(type) { if (type != ")") cont(expression); } function functiondef(type, value) { if (value == "*") {cx.marked = "keyword"; return cont(functiondef);} if (type == "variable") {register(value); return cont(functiondef);} if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext); if (isTS && value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, functiondef) } function funarg(type) { if (type == "spread") return cont(funarg); return pass(pattern, maybetype, maybeAssign); } function classExpression(type, value) { // Class expressions may have an optional name. if (type == "variable") return className(type, value); return classNameAfter(type, value); } function className(type, value) { if (type == "variable") {register(value); return cont(classNameAfter);} } function classNameAfter(type, value) { if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, classNameAfter) if (value == "extends" || value == "implements" || (isTS && type == ",")) return cont(isTS ? typeexpr : expression, classNameAfter); if (type == "{") return cont(pushlex("}"), classBody, poplex); } function classBody(type, value) { if (type == "variable" || cx.style == "keyword") { if ((value == "async" || value == "static" || value == "get" || value == "set" || (isTS && (value == "public" || value == "private" || value == "protected" || value == "readonly" || value == "abstract"))) && cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false)) { cx.marked = "keyword"; return cont(classBody); } cx.marked = "property"; return cont(isTS ? classfield : functiondef, classBody); } if (type == "[") return cont(expression, expect("]"), isTS ? classfield : functiondef, classBody) if (value == "*") { cx.marked = "keyword"; return cont(classBody); } if (type == ";") return cont(classBody); if (type == "}") return cont(); if (value == "@") return cont(expression, classBody) } function classfield(type, value) { if (value == "?") return cont(classfield) if (type == ":") return cont(typeexpr, maybeAssign) if (value == "=") return cont(expressionNoComma) return pass(functiondef) } function afterExport(type, value) { if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); } if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); } if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";")); return pass(statement); } function exportField(type, value) { if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); } if (type == "variable") return pass(expressionNoComma, exportField); } function afterImport(type) { if (type == "string") return cont(); return pass(importSpec, maybeMoreImports, maybeFrom); } function importSpec(type, value) { if (type == "{") return contCommasep(importSpec, "}"); if (type == "variable") register(value); if (value == "*") cx.marked = "keyword"; return cont(maybeAs); } function maybeMoreImports(type) { if (type == ",") return cont(importSpec, maybeMoreImports) } function maybeAs(_type, value) { if (value == "as") { cx.marked = "keyword"; return cont(importSpec); } } function maybeFrom(_type, value) { if (value == "from") { cx.marked = "keyword"; return cont(expression); } } function arrayLiteral(type) { if (type == "]") return cont(); return pass(commasep(expressionNoComma, "]")); } function isContinuedStatement(state, textAfter) { return state.lastType == "operator" || state.lastType == "," || isOperatorChar.test(textAfter.charAt(0)) || /[,.]/.test(textAfter.charAt(0)); } // Interface return { startState: function(basecolumn) { var state = { tokenize: tokenBase, lastType: "sof", cc: [], lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false), localVars: parserConfig.localVars, context: parserConfig.localVars && {vars: parserConfig.localVars}, indented: basecolumn || 0 }; if (parserConfig.globalVars && typeof parserConfig.globalVars == "object") state.globalVars = parserConfig.globalVars; return state; }, token: function(stream, state) { if (stream.sol()) { if (!state.lexical.hasOwnProperty("align")) state.lexical.align = false; state.indented = stream.indentation(); findFatArrow(stream, state); } if (state.tokenize != tokenComment && stream.eatSpace()) return null; var style = state.tokenize(stream, state); if (type == "comment") return style; state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type; return parseJS(state, style, type, content, stream); }, indent: function(state, textAfter) { if (state.tokenize == tokenComment) return CodeMirror.Pass; if (state.tokenize != tokenBase) return 0; var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top // Kludge to prevent 'maybelse' from blocking lexical scope pops if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) { var c = state.cc[i]; if (c == poplex) lexical = lexical.prev; else if (c != maybeelse) break; } while ((lexical.type == "stat" || lexical.type == "form") && (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) && (top == maybeoperatorComma || top == maybeoperatorNoComma) && !/^[,\.=+\-*:?[\(]/.test(textAfter)))) lexical = lexical.prev; if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat") lexical = lexical.prev; var type = lexical.type, closing = firstChar == type; if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0); else if (type == "form" && firstChar == "{") return lexical.indented; else if (type == "form") return lexical.indented + indentUnit; else if (type == "stat") return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0); else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false) return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit); else if (lexical.align) return lexical.column + (closing ? 0 : 1); else return lexical.indented + (closing ? 0 : indentUnit); }, electricInput: /^\s*(?:case .*?:|default:|\{|\})$/, blockCommentStart: jsonMode ? null : "/*", blockCommentEnd: jsonMode ? null : "*/", lineComment: jsonMode ? null : "//", fold: "brace", closeBrackets: "()[]{}''\"\"``", helperType: jsonMode ? "json" : "javascript", jsonldMode: jsonldMode, jsonMode: jsonMode, expressionAllowed: expressionAllowed, skipExpression: function(state) { var top = state.cc[state.cc.length - 1] if (top == expression || top == expressionNoComma) state.cc.pop() } }; }); CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/); CodeMirror.defineMIME("text/javascript", "javascript"); CodeMirror.defineMIME("text/ecmascript", "javascript"); CodeMirror.defineMIME("application/javascript", "javascript"); CodeMirror.defineMIME("application/x-javascript", "javascript"); CodeMirror.defineMIME("application/ecmascript", "javascript"); CodeMirror.defineMIME("application/json", {name: "javascript", json: true}); CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true}); CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true}); CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true }); CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true }); }); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/jquery.js ================================================ /*! jQuery v3.5.1 | (c) JS Foundation and other contributors | jquery.org/license */ !function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.5.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function D(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||j,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,j=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function qe(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function Le(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function He(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Oe(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
    ",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):("number"==typeof f.top&&(f.top+="px"),"number"==typeof f.left&&(f.left+="px"),c.css(f))}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=$e(y.pixelPosition,function(e,t){if(t)return t=Be(e,n),Me.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0=0)||"undefined"!=typeof process&&"win32"===process.platform},t}();e.Environment=t}(AMDLoader||(AMDLoader={}));var AMDLoader;!function(e){var t;!function(e){e[e.LoaderAvailable=1]="LoaderAvailable",e[e.BeginLoadingScript=10]="BeginLoadingScript",e[e.EndLoadingScriptOK=11]="EndLoadingScriptOK",e[e.EndLoadingScriptError=12]="EndLoadingScriptError",e[e.BeginInvokeFactory=21]="BeginInvokeFactory",e[e.EndInvokeFactory=22]="EndInvokeFactory",e[e.NodeBeginEvaluatingScript=31]="NodeBeginEvaluatingScript",e[e.NodeEndEvaluatingScript=32]="NodeEndEvaluatingScript",e[e.NodeBeginNativeRequire=33]="NodeBeginNativeRequire",e[e.NodeEndNativeRequire=34]="NodeEndNativeRequire"}(t=e.LoaderEventType||(e.LoaderEventType={}));var r=function(){return function(e,t,r){this.type=e,this.detail=t,this.timestamp=r}}();e.LoaderEvent=r;var n=function(){function n(e){this._events=[new r(t.LoaderAvailable,"",e)]}return n.prototype.record=function(t,n){this._events.push(new r(t,n,e.Utilities.getHighPerformanceTimestamp()))},n.prototype.getEvents=function(){return this._events},n}();e.LoaderEventRecorder=n;var o=function(){function e(){}return e.prototype.record=function(e,t){},e.prototype.getEvents=function(){return[]},e}();o.INSTANCE=new o,e.NullLoaderEventRecorder=o}(AMDLoader||(AMDLoader={}));var AMDLoader;!function(e){var t=function(){function t(){}return t.fileUriToFilePath=function(e,t){if(t=decodeURI(t),e){if(/^file:\/\/\//.test(t))return t.substr(8);if(/^file:\/\//.test(t))return t.substr(5)}else if(/^file:\/\//.test(t))return t.substr(7);return t},t.startsWith=function(e,t){return e.length>=t.length&&e.substr(0,t.length)===t},t.endsWith=function(e,t){return e.length>=t.length&&e.substr(e.length-t.length)===t},t.containsQueryString=function(e){return/^[^\#]*\?/gi.test(e)},t.isAbsolutePath=function(e){return/^((http:\/\/)|(https:\/\/)|(file:\/\/)|(\/))/.test(e)},t.forEachProperty=function(e,t){if(e){var r=void 0;for(r in e)e.hasOwnProperty(r)&&t(r,e[r])}},t.isEmpty=function(e){var r=!0;return t.forEachProperty(e,function(){r=!1}),r},t.recursiveClone=function(e){if(!e||"object"!=typeof e)return e;var r=Array.isArray(e)?[]:{};return t.forEachProperty(e,function(e,n){r[e]=n&&"object"==typeof n?t.recursiveClone(n):n}),r},t.generateAnonymousModule=function(){return"===anonymous"+t.NEXT_ANONYMOUS_ID+++"==="},t.isAnonymousModule=function(e){return/^===anonymous/.test(e)},t.getHighPerformanceTimestamp=function(){return this.PERFORMANCE_NOW_PROBED||(this.PERFORMANCE_NOW_PROBED=!0,this.HAS_PERFORMANCE_NOW=e.global.performance&&"function"==typeof e.global.performance.now),this.HAS_PERFORMANCE_NOW?e.global.performance.now():Date.now()},t}();t.NEXT_ANONYMOUS_ID=1,t.PERFORMANCE_NOW_PROBED=!1,t.HAS_PERFORMANCE_NOW=!1,e.Utilities=t}(AMDLoader||(AMDLoader={}));var AMDLoader;!function(e){var t=function(){function t(){}return t.validateConfigurationOptions=function(t,r){return"string"!=typeof(r=r||{}).baseUrl&&(r.baseUrl=""),"boolean"!=typeof r.isBuild&&(r.isBuild=!1),"object"!=typeof r.paths&&(r.paths={}),"object"!=typeof r.config&&(r.config={}),void 0===r.catchError&&(r.catchError=t),"string"!=typeof r.urlArgs&&(r.urlArgs=""),"function"!=typeof r.onError&&(r.onError=function(e){return"load"===e.errorCode?(console.error('Loading "'+e.moduleId+'" failed'),console.error("Detail: ",e.detail),e.detail&&e.detail.stack&&console.error(e.detail.stack),console.error("Here are the modules that depend on it:"),void console.error(e.neededBy)):"factory"===e.errorCode?(console.error('The factory method of "'+e.moduleId+'" has thrown an exception'),console.error(e.detail),void(e.detail&&e.detail.stack&&console.error(e.detail.stack))):void 0}),"object"==typeof r.ignoreDuplicateModules&&Array.isArray(r.ignoreDuplicateModules)||(r.ignoreDuplicateModules=[]),r.baseUrl.length>0&&(e.Utilities.endsWith(r.baseUrl,"/")||(r.baseUrl+="/")),Array.isArray(r.nodeModules)||(r.nodeModules=[]),("number"!=typeof r.nodeCachedDataWriteDelay||r.nodeCachedDataWriteDelay<0)&&(r.nodeCachedDataWriteDelay=7e3),"function"!=typeof r.onNodeCachedData&&(r.onNodeCachedData=function(e,t){e&&("cachedDataRejected"===e.errorCode?console.warn("Rejected cached data from file: "+e.path):"unlink"===e.errorCode||"writeFile"===e.errorCode?(console.error("Problems writing cached data file: "+e.path),console.error(e.detail)):console.error(e))}),r},t.mergeConfigurationOptions=function(r,n,o){void 0===n&&(n=null),void 0===o&&(o=null);var i=e.Utilities.recursiveClone(o||{});return e.Utilities.forEachProperty(n,function(t,r){"ignoreDuplicateModules"===t&&void 0!==i.ignoreDuplicateModules?i.ignoreDuplicateModules=i.ignoreDuplicateModules.concat(r):"paths"===t&&void 0!==i.paths?e.Utilities.forEachProperty(r,function(e,t){return i.paths[e]=t}):"config"===t&&void 0!==i.config?e.Utilities.forEachProperty(r,function(e,t){return i.config[e]=t}):i[t]=e.Utilities.recursiveClone(r)}),t.validateConfigurationOptions(r,i)},t}();e.ConfigurationOptionsUtil=t;var r=function(){function r(e,r){if(this._env=e,this.options=t.mergeConfigurationOptions(this._env.isWebWorker,r),this._createIgnoreDuplicateModulesMap(),this._createNodeModulesMap(),this._createSortedPathsRules(),""===this.options.baseUrl){if(this._env.isNode&&this.options.nodeRequire&&this.options.nodeRequire.main&&this.options.nodeRequire.main.filename){var n=this.options.nodeRequire.main.filename,o=Math.max(n.lastIndexOf("/"),n.lastIndexOf("\\"));this.options.baseUrl=n.substring(0,o+1)}if(this._env.isNode&&this.options.nodeMain){var n=this.options.nodeMain,o=Math.max(n.lastIndexOf("/"),n.lastIndexOf("\\"));this.options.baseUrl=n.substring(0,o+1)}}}return r.prototype._createIgnoreDuplicateModulesMap=function(){this.ignoreDuplicateModulesMap={};for(var e=0;e=0){var n=t.resolveModule(e.substr(0,r)),s=t.resolveModule(e.substr(r+1)),a=this._moduleIdProvider.getModuleId(n+"!"+s),d=this._moduleIdProvider.getModuleId(n);return new i(a,d,s)}return new o(this._moduleIdProvider.getModuleId(t.resolveModule(e)))},s.prototype._normalizeDependencies=function(e,t){for(var r=[],n=0,o=0,i=e.length;o0;){var d=a.shift(),l=this._modules2[d];l&&(s=l.onDependencyError(r)||s);var u=this._inverseDependencies2[d];if(u)for(var o=0,i=u.length;o0;){var a=s.shift().dependencies;if(a)for(var o=0,i=a.length;o=o.length)r._onLoadError(t,n);else{var a=o[i],d=r.getRecorder();if(r._config.isBuild()&&"empty:"===a)return r._buildInfoPath[t]=a,r.defineModule(r._moduleIdProvider.getStrModuleId(t),[],null,null,null),void r._onLoad(t);d.record(e.LoaderEventType.BeginLoadingScript,a),r._scriptLoader.load(r,a,function(){r._config.isBuild()&&(r._buildInfoPath[t]=a),d.record(e.LoaderEventType.EndLoadingScriptOK,a),r._onLoad(t)},function(t){d.record(e.LoaderEventType.EndLoadingScriptError,a),s(t)})}};s(null)}},s.prototype._loadPluginDependency=function(e,r){var n=this;if(!this._modules2[r.id]&&!this._knownModules2[r.id]){this._knownModules2[r.id]=!0;var o=function(e){n.defineModule(n._moduleIdProvider.getStrModuleId(r.id),[],e,null,null)};o.error=function(e){n._config.onError(n._createLoadError(r.id,e))},e.load(r.pluginParam,this._createRequire(t.ROOT),o,this._config.getOptionsLiteral())}},s.prototype._resolve=function(e){for(var t=this,r=e.dependencies,n=0,s=r.length;n \n")),e.unresolvedDependenciesCount--}else if(this._inverseDependencies2[a.id]=this._inverseDependencies2[a.id]||[],this._inverseDependencies2[a.id].push(e.id),a instanceof i){var u=this._modules2[a.pluginId];if(u&&u.isComplete()){this._loadPluginDependency(u.exports,a);continue}var c=this._inversePluginDependencies2.get(a.pluginId);c||(c=[],this._inversePluginDependencies2.set(a.pluginId,c)),c.push(a),this._loadModule(a.pluginId)}else this._loadModule(a.id)}else e.unresolvedDependenciesCount--;else e.unresolvedDependenciesCount--;else e.exportsPassedIn=!0,e.unresolvedDependenciesCount--}0===e.unresolvedDependenciesCount&&this._onModuleComplete(e)},s.prototype._onModuleComplete=function(e){var t=this,r=this.getRecorder();if(!e.isComplete()){for(var n=e.dependencies,i=[],s=0,a=n.length;s now) return false; var sInfo = editor.getScrollInfo(); if (dv.mv.options.connect == "align") { targetPos = sInfo.top; } else { var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen; var mid = editor.lineAtHeight(midY, "local"); var around = chunkBoundariesAround(dv.chunks, mid, toOrig); var off = getOffsets(editor, toOrig ? around.edit : around.orig); var offOther = getOffsets(other, toOrig ? around.orig : around.edit); var ratio = (midY - off.top) / (off.bot - off.top); var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top); var botDist, mix; // Some careful tweaking to make sure no space is left out of view // when scrolling to top or bottom. if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) { targetPos = targetPos * mix + sInfo.top * (1 - mix); } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) { var otherInfo = other.getScrollInfo(); var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos; if (botDistOther > botDist && (mix = botDist / halfScreen) < 1) targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix); } } other.scrollTo(sInfo.left, targetPos); other.state.scrollSetAt = now; other.state.scrollSetBy = dv; return true; } function getOffsets(editor, around) { var bot = around.after; if (bot == null) bot = editor.lastLine() + 1; return {top: editor.heightAtLine(around.before || 0, "local"), bot: editor.heightAtLine(bot, "local")}; } function setScrollLock(dv, val, action) { dv.lockScroll = val; if (val && action != false) syncScroll(dv, DIFF_INSERT) && makeConnections(dv); dv.lockButton.innerHTML = val ? "\u21db\u21da" : "\u21db  \u21da"; } // Updating the marks for editor content function removeClass(editor, line, classes) { var locs = classes.classLocation for (var i = 0; i < locs.length; i++) { editor.removeLineClass(line, locs[i], classes.chunk); editor.removeLineClass(line, locs[i], classes.start); editor.removeLineClass(line, locs[i], classes.end); } } function clearMarks(editor, arr, classes) { for (var i = 0; i < arr.length; ++i) { var mark = arr[i]; if (mark instanceof CodeMirror.TextMarker) mark.clear(); else if (mark.parent) removeClass(editor, mark, classes); } arr.length = 0; } // FIXME maybe add a margin around viewport to prevent too many updates function updateMarks(editor, diff, state, type, classes) { var vp = editor.getViewport(); editor.operation(function() { if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) { clearMarks(editor, state.marked, classes); markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes); state.from = vp.from; state.to = vp.to; } else { if (vp.from < state.from) { markChanges(editor, diff, type, state.marked, vp.from, state.from, classes); state.from = vp.from; } if (vp.to > state.to) { markChanges(editor, diff, type, state.marked, state.to, vp.to, classes); state.to = vp.to; } } }); } function addClass(editor, lineNr, classes, main, start, end) { var locs = classes.classLocation, line = editor.getLineHandle(lineNr); for (var i = 0; i < locs.length; i++) { if (main) editor.addLineClass(line, locs[i], classes.chunk); if (start) editor.addLineClass(line, locs[i], classes.start); if (end) editor.addLineClass(line, locs[i], classes.end); } return line; } function markChanges(editor, diff, type, marks, from, to, classes) { var pos = Pos(0, 0); var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1)); var cls = type == DIFF_DELETE ? classes.del : classes.insert; function markChunk(start, end) { var bfrom = Math.max(from, start), bto = Math.min(to, end); for (var i = bfrom; i < bto; ++i) marks.push(addClass(editor, i, classes, true, i == start, i == end - 1)); // When the chunk is empty, make sure a horizontal line shows up if (start == end && bfrom == end && bto == end) { if (bfrom) marks.push(addClass(editor, bfrom - 1, classes, false, false, true)); else marks.push(addClass(editor, bfrom, classes, false, true, false)); } } var chunkStart = 0, pending = false; for (var i = 0; i < diff.length; ++i) { var part = diff[i], tp = part[0], str = part[1]; if (tp == DIFF_EQUAL) { var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1); moveOver(pos, str); var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0); if (cleanTo > cleanFrom) { if (pending) { markChunk(chunkStart, cleanFrom); pending = false } chunkStart = cleanTo; } } else { pending = true if (tp == type) { var end = moveOver(pos, str, true); var a = posMax(top, pos), b = posMin(bot, end); if (!posEq(a, b)) marks.push(editor.markText(a, b, {className: cls})); pos = end; } } } if (pending) markChunk(chunkStart, pos.line + 1); } // Updating the gap between editor and original function makeConnections(dv) { if (!dv.showDifferences) return; if (dv.svg) { clear(dv.svg); var w = dv.gap.offsetWidth; attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight); } if (dv.copyButtons) clear(dv.copyButtons); var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport(); var outerTop = dv.mv.wrap.getBoundingClientRect().top var sTopEdit = outerTop - dv.edit.getScrollerElement().getBoundingClientRect().top + dv.edit.getScrollInfo().top var sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top; for (var i = 0; i < dv.chunks.length; i++) { var ch = dv.chunks[i]; if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from && ch.origFrom <= vpOrig.to && ch.origTo >= vpOrig.from) drawConnectorsForChunk(dv, ch, sTopOrig, sTopEdit, w); } } function getMatchingOrigLine(editLine, chunks) { var editStart = 0, origStart = 0; for (var i = 0; i < chunks.length; i++) { var chunk = chunks[i]; if (chunk.editTo > editLine && chunk.editFrom <= editLine) return null; if (chunk.editFrom > editLine) break; editStart = chunk.editTo; origStart = chunk.origTo; } return origStart + (editLine - editStart); } // Combines information about chunks and widgets/markers to return // an array of lines, in a single editor, that probably need to be // aligned with their counterparts in the editor next to it. function alignableFor(cm, chunks, isOrig) { var tracker = cm.state.trackAlignable var start = cm.firstLine(), trackI = 0 var result = [] for (var i = 0;; i++) { var chunk = chunks[i] var chunkStart = !chunk ? 1e9 : isOrig ? chunk.origFrom : chunk.editFrom for (; trackI < tracker.alignable.length; trackI += 2) { var n = tracker.alignable[trackI] + 1 if (n <= start) continue if (n <= chunkStart) result.push(n) else break } if (!chunk) break result.push(start = isOrig ? chunk.origTo : chunk.editTo) } return result } // Given information about alignable lines in two editors, fill in // the result (an array of three-element arrays) to reflect the // lines that need to be aligned with each other. function mergeAlignable(result, origAlignable, chunks, setIndex) { var rI = 0, origI = 0, chunkI = 0, diff = 0 outer: for (;; rI++) { var nextR = result[rI], nextO = origAlignable[origI] if (!nextR && nextO == null) break var rLine = nextR ? nextR[0] : 1e9, oLine = nextO == null ? 1e9 : nextO while (chunkI < chunks.length) { var chunk = chunks[chunkI] if (chunk.origFrom <= oLine && chunk.origTo > oLine) { origI++ rI-- continue outer; } if (chunk.editTo > rLine) { if (chunk.editFrom <= rLine) continue outer; break } diff += (chunk.origTo - chunk.origFrom) - (chunk.editTo - chunk.editFrom) chunkI++ } if (rLine == oLine - diff) { nextR[setIndex] = oLine origI++ } else if (rLine < oLine - diff) { nextR[setIndex] = rLine + diff } else { var record = [oLine - diff, null, null] record[setIndex] = oLine result.splice(rI, 0, record) origI++ } } } function findAlignedLines(dv, other) { var alignable = alignableFor(dv.edit, dv.chunks, false), result = [] if (other) for (var i = 0, j = 0; i < other.chunks.length; i++) { var n = other.chunks[i].editTo while (j < alignable.length && alignable[j] < n) j++ if (j == alignable.length || alignable[j] != n) alignable.splice(j++, 0, n) } for (var i = 0; i < alignable.length; i++) result.push([alignable[i], null, null]) mergeAlignable(result, alignableFor(dv.orig, dv.chunks, true), dv.chunks, 1) if (other) mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2) return result } function alignChunks(dv, force) { if (!dv.dealigned && !force) return; if (!dv.orig.curOp) return dv.orig.operation(function() { alignChunks(dv, force); }); dv.dealigned = false; var other = dv.mv.left == dv ? dv.mv.right : dv.mv.left; if (other) { ensureDiff(other); other.dealigned = false; } var linesToAlign = findAlignedLines(dv, other); // Clear old aligners var aligners = dv.mv.aligners; for (var i = 0; i < aligners.length; i++) aligners[i].clear(); aligners.length = 0; var cm = [dv.edit, dv.orig], scroll = []; if (other) cm.push(other.orig); for (var i = 0; i < cm.length; i++) scroll.push(cm[i].getScrollInfo().top); for (var ln = 0; ln < linesToAlign.length; ln++) alignLines(cm, linesToAlign[ln], aligners); for (var i = 0; i < cm.length; i++) cm[i].scrollTo(null, scroll[i]); } function alignLines(cm, lines, aligners) { var maxOffset = 0, offset = []; for (var i = 0; i < cm.length; i++) if (lines[i] != null) { var off = cm[i].heightAtLine(lines[i], "local"); offset[i] = off; maxOffset = Math.max(maxOffset, off); } for (var i = 0; i < cm.length; i++) if (lines[i] != null) { var diff = maxOffset - offset[i]; if (diff > 1) aligners.push(padAbove(cm[i], lines[i], diff)); } } function padAbove(cm, line, size) { var above = true; if (line > cm.lastLine()) { line--; above = false; } var elt = document.createElement("div"); elt.className = "CodeMirror-merge-spacer"; elt.style.height = size + "px"; elt.style.minWidth = "1px"; return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true, handleMouseEvents: true}); } function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) { var flip = dv.type == "left"; var top = dv.orig.heightAtLine(chunk.origFrom, "local", true) - sTopOrig; if (dv.svg) { var topLpx = top; var topRpx = dv.edit.heightAtLine(chunk.editFrom, "local", true) - sTopEdit; if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; } var botLpx = dv.orig.heightAtLine(chunk.origTo, "local", true) - sTopOrig; var botRpx = dv.edit.heightAtLine(chunk.editTo, "local", true) - sTopEdit; if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; } var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx; var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx; attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")), "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z", "class", dv.classes.connect); } if (dv.copyButtons) { var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc", "CodeMirror-merge-copy")); var editOriginals = dv.mv.options.allowEditingOriginals; copy.title = editOriginals ? "Push to left" : "Revert chunk"; copy.chunk = chunk; copy.style.top = (chunk.origTo > chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px"; if (editOriginals) { var topReverse = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit; var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc", "CodeMirror-merge-copy-reverse")); copyReverse.title = "Push to right"; copyReverse.chunk = {editFrom: chunk.origFrom, editTo: chunk.origTo, origFrom: chunk.editFrom, origTo: chunk.editTo}; copyReverse.style.top = topReverse + "px"; dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px"; } } } function copyChunk(dv, to, from, chunk) { if (dv.diffOutOfDate) return; var origStart = chunk.origTo > from.lastLine() ? Pos(chunk.origFrom - 1) : Pos(chunk.origFrom, 0) var origEnd = Pos(chunk.origTo, 0) var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0) var editEnd = Pos(chunk.editTo, 0) var handler = dv.mv.options.revertChunk if (handler) handler(dv.mv, from, origStart, origEnd, to, editStart, editEnd) else to.replaceRange(from.getRange(origStart, origEnd), editStart, editEnd) } // Merge view, containing 0, 1, or 2 diff views. var MergeView = CodeMirror.MergeView = function(node, options) { if (!(this instanceof MergeView)) return new MergeView(node, options); this.options = options; var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight; var hasLeft = origLeft != null, hasRight = origRight != null; var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0); var wrap = [], left = this.left = null, right = this.right = null; var self = this; if (hasLeft) { left = this.left = new DiffView(this, "left"); var leftPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-left"); wrap.push(leftPane); wrap.push(buildGap(left)); } var editPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-editor"); wrap.push(editPane); if (hasRight) { right = this.right = new DiffView(this, "right"); wrap.push(buildGap(right)); var rightPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-right"); wrap.push(rightPane); } (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost"; wrap.push(elt("div", null, null, "height: 0; clear: both;")); var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane")); this.edit = CodeMirror(editPane, copyObj(options)); if (left) left.init(leftPane, origLeft, options); if (right) right.init(rightPane, origRight, options); if (options.collapseIdentical) this.editor().operation(function() { collapseIdenticalStretches(self, options.collapseIdentical); }); if (options.connect == "align") { this.aligners = []; alignChunks(this.left || this.right, true); } if (left) left.registerEvents(right) if (right) right.registerEvents(left) var onResize = function() { if (left) makeConnections(left); if (right) makeConnections(right); }; CodeMirror.on(window, "resize", onResize); var resizeInterval = setInterval(function() { for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {} if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, "resize", onResize); } }, 5000); }; function buildGap(dv) { var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock"); lock.title = "Toggle locked scrolling"; var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap"); CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); }); var gapElts = [lockWrap]; if (dv.mv.options.revertButtons !== false) { dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type); CodeMirror.on(dv.copyButtons, "click", function(e) { var node = e.target || e.srcElement; if (!node.chunk) return; if (node.className == "CodeMirror-merge-copy-reverse") { copyChunk(dv, dv.orig, dv.edit, node.chunk); return; } copyChunk(dv, dv.edit, dv.orig, node.chunk); }); gapElts.unshift(dv.copyButtons); } if (dv.mv.options.connect != "align") { var svg = document.createElementNS && document.createElementNS(svgNS, "svg"); if (svg && !svg.createSVGRect) svg = null; dv.svg = svg; if (svg) gapElts.push(svg); } return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap"); } MergeView.prototype = { constructor: MergeView, editor: function() { return this.edit; }, rightOriginal: function() { return this.right && this.right.orig; }, leftOriginal: function() { return this.left && this.left.orig; }, setShowDifferences: function(val) { if (this.right) this.right.setShowDifferences(val); if (this.left) this.left.setShowDifferences(val); }, rightChunks: function() { if (this.right) { ensureDiff(this.right); return this.right.chunks; } }, leftChunks: function() { if (this.left) { ensureDiff(this.left); return this.left.chunks; } } }; function asString(obj) { if (typeof obj == "string") return obj; else return obj.getValue(); } // Operations on diffs var dmp = new diff_match_patch(); function getDiff(a, b, ignoreWhitespace) { var diff = dmp.diff_main(a, b); // The library sometimes leaves in empty parts, which confuse the algorithm for (var i = 0; i < diff.length; ++i) { var part = diff[i]; if (ignoreWhitespace ? !/[^ \t]/.test(part[1]) : !part[1]) { diff.splice(i--, 1); } else if (i && diff[i - 1][0] == part[0]) { diff.splice(i--, 1); diff[i][1] += part[1]; } } return diff; } function getChunks(diff) { var chunks = []; var startEdit = 0, startOrig = 0; var edit = Pos(0, 0), orig = Pos(0, 0); for (var i = 0; i < diff.length; ++i) { var part = diff[i], tp = part[0]; if (tp == DIFF_EQUAL) { var startOff = !startOfLineClean(diff, i) || edit.line < startEdit || orig.line < startOrig ? 1 : 0; var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff; moveOver(edit, part[1], null, orig); var endOff = endOfLineClean(diff, i) ? 1 : 0; var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff; if (cleanToEdit > cleanFromEdit) { if (i) chunks.push({origFrom: startOrig, origTo: cleanFromOrig, editFrom: startEdit, editTo: cleanFromEdit}); startEdit = cleanToEdit; startOrig = cleanToOrig; } } else { moveOver(tp == DIFF_INSERT ? edit : orig, part[1]); } } if (startEdit <= edit.line || startOrig <= orig.line) chunks.push({origFrom: startOrig, origTo: orig.line + 1, editFrom: startEdit, editTo: edit.line + 1}); return chunks; } function endOfLineClean(diff, i) { if (i == diff.length - 1) return true; var next = diff[i + 1][1]; if ((next.length == 1 && i < diff.length - 2) || next.charCodeAt(0) != 10) return false; if (i == diff.length - 2) return true; next = diff[i + 2][1]; return (next.length > 1 || i == diff.length - 3) && next.charCodeAt(0) == 10; } function startOfLineClean(diff, i) { if (i == 0) return true; var last = diff[i - 1][1]; if (last.charCodeAt(last.length - 1) != 10) return false; if (i == 1) return true; last = diff[i - 2][1]; return last.charCodeAt(last.length - 1) == 10; } function chunkBoundariesAround(chunks, n, nInEdit) { var beforeE, afterE, beforeO, afterO; for (var i = 0; i < chunks.length; i++) { var chunk = chunks[i]; var fromLocal = nInEdit ? chunk.editFrom : chunk.origFrom; var toLocal = nInEdit ? chunk.editTo : chunk.origTo; if (afterE == null) { if (fromLocal > n) { afterE = chunk.editFrom; afterO = chunk.origFrom; } else if (toLocal > n) { afterE = chunk.editTo; afterO = chunk.origTo; } } if (toLocal <= n) { beforeE = chunk.editTo; beforeO = chunk.origTo; } else if (fromLocal <= n) { beforeE = chunk.editFrom; beforeO = chunk.origFrom; } } return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}}; } function collapseSingle(cm, from, to) { cm.addLineClass(from, "wrap", "CodeMirror-merge-collapsed-line"); var widget = document.createElement("span"); widget.className = "CodeMirror-merge-collapsed-widget"; widget.title = "Identical text collapsed. Click to expand."; var mark = cm.markText(Pos(from, 0), Pos(to - 1), { inclusiveLeft: true, inclusiveRight: true, replacedWith: widget, clearOnEnter: true }); function clear() { mark.clear(); cm.removeLineClass(from, "wrap", "CodeMirror-merge-collapsed-line"); } CodeMirror.on(widget, "click", clear); return {mark: mark, clear: clear}; } function collapseStretch(size, editors) { var marks = []; function clear() { for (var i = 0; i < marks.length; i++) marks[i].clear(); } for (var i = 0; i < editors.length; i++) { var editor = editors[i]; var mark = collapseSingle(editor.cm, editor.line, editor.line + size); marks.push(mark); mark.mark.on("clear", clear); } return marks[0].mark; } function unclearNearChunks(dv, margin, off, clear) { for (var i = 0; i < dv.chunks.length; i++) { var chunk = dv.chunks[i]; for (var l = chunk.editFrom - margin; l < chunk.editTo + margin; l++) { var pos = l + off; if (pos >= 0 && pos < clear.length) clear[pos] = false; } } } function collapseIdenticalStretches(mv, margin) { if (typeof margin != "number") margin = 2; var clear = [], edit = mv.editor(), off = edit.firstLine(); for (var l = off, e = edit.lastLine(); l <= e; l++) clear.push(true); if (mv.left) unclearNearChunks(mv.left, margin, off, clear); if (mv.right) unclearNearChunks(mv.right, margin, off, clear); for (var i = 0; i < clear.length; i++) { if (clear[i]) { var line = i + off; for (var size = 1; i < clear.length - 1 && clear[i + 1]; i++, size++) {} if (size > margin) { var editors = [{line: line, cm: edit}]; if (mv.left) editors.push({line: getMatchingOrigLine(line, mv.left.chunks), cm: mv.left.orig}); if (mv.right) editors.push({line: getMatchingOrigLine(line, mv.right.chunks), cm: mv.right.orig}); var mark = collapseStretch(size, editors); if (mv.options.onCollapse) mv.options.onCollapse(mv, line, size, mark); } } } } // General utilities function elt(tag, content, className, style) { var e = document.createElement(tag); if (className) e.className = className; if (style) e.style.cssText = style; if (typeof content == "string") e.appendChild(document.createTextNode(content)); else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]); return e; } function clear(node) { for (var count = node.childNodes.length; count > 0; --count) node.removeChild(node.firstChild); } function attrs(elt) { for (var i = 1; i < arguments.length; i += 2) elt.setAttribute(arguments[i], arguments[i+1]); } function copyObj(obj, target) { if (!target) target = {}; for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop]; return target; } function moveOver(pos, str, copy, other) { var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0; for (;;) { var nl = str.indexOf("\n", at); if (nl == -1) break; ++out.line; if (other) ++other.line; at = nl + 1; } out.ch = (at ? 0 : out.ch) + (str.length - at); if (other) other.ch = (at ? 0 : other.ch) + (str.length - at); return out; } // Tracks collapsed markers and line widgets, in order to be able to // accurately align the content of two editors. var F_WIDGET = 1, F_WIDGET_BELOW = 2, F_MARKER = 4 function TrackAlignable(cm) { this.cm = cm this.alignable = [] this.height = cm.doc.height var self = this cm.on("markerAdded", function(_, marker) { if (!marker.collapsed) return var found = marker.find(1) if (found != null) self.set(found.line, F_MARKER) }) cm.on("markerCleared", function(_, marker, _min, max) { if (max != null && marker.collapsed) self.check(max, F_MARKER, self.hasMarker) }) cm.on("markerChanged", this.signal.bind(this)) cm.on("lineWidgetAdded", function(_, widget, lineNo) { if (widget.mergeSpacer) return if (widget.above) self.set(lineNo - 1, F_WIDGET_BELOW) else self.set(lineNo, F_WIDGET) }) cm.on("lineWidgetCleared", function(_, widget, lineNo) { if (widget.mergeSpacer) return if (widget.above) self.check(lineNo - 1, F_WIDGET_BELOW, self.hasWidgetBelow) else self.check(lineNo, F_WIDGET, self.hasWidget) }) cm.on("lineWidgetChanged", this.signal.bind(this)) cm.on("change", function(_, change) { var start = change.from.line, nBefore = change.to.line - change.from.line var nAfter = change.text.length - 1, end = start + nAfter if (nBefore || nAfter) self.map(start, nBefore, nAfter) self.check(end, F_MARKER, self.hasMarker) if (nBefore || nAfter) self.check(change.from.line, F_MARKER, self.hasMarker) }) cm.on("viewportChange", function() { if (self.cm.doc.height != self.height) self.signal() }) } TrackAlignable.prototype = { signal: function() { CodeMirror.signal(this, "realign") this.height = this.cm.doc.height }, set: function(n, flags) { var pos = -1 for (; pos < this.alignable.length; pos += 2) { var diff = this.alignable[pos] - n if (diff == 0) { if ((this.alignable[pos + 1] & flags) == flags) return this.alignable[pos + 1] |= flags this.signal() return } if (diff > 0) break } this.signal() this.alignable.splice(pos, 0, n, flags) }, find: function(n) { for (var i = 0; i < this.alignable.length; i += 2) if (this.alignable[i] == n) return i return -1 }, check: function(n, flag, pred) { var found = this.find(n) if (found == -1 || !(this.alignable[found + 1] & flag)) return if (!pred.call(this, n)) { this.signal() var flags = this.alignable[found + 1] & ~flag if (flags) this.alignable[found + 1] = flags else this.alignable.splice(found, 2) } }, hasMarker: function(n) { var handle = this.cm.getLineHandle(n) if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++) if (handle.markedSpans[i].mark.collapsed && handle.markedSpans[i].to != null) return true return false }, hasWidget: function(n) { var handle = this.cm.getLineHandle(n) if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++) if (!handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true return false }, hasWidgetBelow: function(n) { if (n == this.cm.lastLine()) return false var handle = this.cm.getLineHandle(n + 1) if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++) if (handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true return false }, map: function(from, nBefore, nAfter) { var diff = nAfter - nBefore, to = from + nBefore, widgetFrom = -1, widgetTo = -1 for (var i = 0; i < this.alignable.length; i += 2) { var n = this.alignable[i] if (n == from && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetFrom = i if (n == to && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetTo = i if (n <= from) continue else if (n < to) this.alignable.splice(i--, 2) else this.alignable[i] += diff } if (widgetFrom > -1) { var flags = this.alignable[widgetFrom + 1] if (flags == F_WIDGET_BELOW) this.alignable.splice(widgetFrom, 2) else this.alignable[widgetFrom + 1] = flags & ~F_WIDGET_BELOW } if (widgetTo > -1 && nAfter) this.set(from + nAfter, F_WIDGET_BELOW) } } function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; } function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; } function posEq(a, b) { return a.line == b.line && a.ch == b.ch; } function findPrevDiff(chunks, start, isOrig) { for (var i = chunks.length - 1; i >= 0; i--) { var chunk = chunks[i]; var to = (isOrig ? chunk.origTo : chunk.editTo) - 1; if (to < start) return to; } } function findNextDiff(chunks, start, isOrig) { for (var i = 0; i < chunks.length; i++) { var chunk = chunks[i]; var from = (isOrig ? chunk.origFrom : chunk.editFrom); if (from > start) return from; } } function goNearbyDiff(cm, dir) { var found = null, views = cm.state.diffViews, line = cm.getCursor().line; if (views) for (var i = 0; i < views.length; i++) { var dv = views[i], isOrig = cm == dv.orig; ensureDiff(dv); var pos = dir < 0 ? findPrevDiff(dv.chunks, line, isOrig) : findNextDiff(dv.chunks, line, isOrig); if (pos != null && (found == null || (dir < 0 ? pos > found : pos < found))) found = pos; } if (found != null) cm.setCursor(found, 0); else return CodeMirror.Pass; } CodeMirror.commands.goNextDiff = function(cm) { return goNearbyDiff(cm, 1); }; CodeMirror.commands.goPrevDiff = function(cm) { return goNearbyDiff(cm, -1); }; }); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/base/worker/workerMain.js ================================================ /*!----------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Version: 0.10.1(ebbf400719be21761361804bf63fb3916e64a845) * Released under the MIT license * https://github.com/Microsoft/vscode/blob/master/LICENSE.txt *-----------------------------------------------------------*/ (function(){var e=["exports","require","vs/base/common/winjs.base","vs/editor/common/core/position","vs/base/common/platform","vs/editor/common/core/range","vs/base/common/uri","vs/editor/common/core/uint","vs/base/common/errors","vs/base/common/event","vs/base/common/lifecycle","vs/base/common/functional","vs/base/common/diff/diff","vs/base/common/cancellation","vs/base/common/types","vs/base/common/callbackList","vs/base/common/diff/diffChange","vs/base/common/map","vs/base/common/async","vs/editor/common/viewModel/prefixSumComputer","vs/base/common/strings","vs/base/common/keyCodes","vs/editor/common/core/selection","vs/editor/common/core/token","vs/editor/common/model/mirrorModel","vs/editor/common/core/characterClassifier","vs/editor/common/diff/diffComputer","vs/editor/common/model/wordHelper","vs/editor/common/modes/linkComputer","vs/editor/common/modes/supports/inplaceReplaceSupport","vs/editor/common/standalone/standaloneBase","vs/base/common/worker/simpleWorker","vs/base/common/winjs.base.raw","vs/editor/common/services/editorSimpleWorker"],t=function(t){for(var n=[],r=0,i=t.length;r=0)||"undefined"!=typeof process&&"win32"===process.platform},t}();e.Environment=t}(i||(i={}));!function(e){var t;!function(e){e[e.LoaderAvailable=1]="LoaderAvailable",e[e.BeginLoadingScript=10]="BeginLoadingScript",e[e.EndLoadingScriptOK=11]="EndLoadingScriptOK",e[e.EndLoadingScriptError=12]="EndLoadingScriptError",e[e.BeginInvokeFactory=21]="BeginInvokeFactory",e[e.EndInvokeFactory=22]="EndInvokeFactory",e[e.NodeBeginEvaluatingScript=31]="NodeBeginEvaluatingScript",e[e.NodeEndEvaluatingScript=32]="NodeEndEvaluatingScript",e[e.NodeBeginNativeRequire=33]="NodeBeginNativeRequire",e[e.NodeEndNativeRequire=34]="NodeEndNativeRequire"}(t=e.LoaderEventType||(e.LoaderEventType={}));var n=function(){return function(e,t,n){this.type=e,this.detail=t,this.timestamp=n}}();e.LoaderEvent=n;var r=function(){function r(e){this._events=[new n(t.LoaderAvailable,"",e)]}return r.prototype.record=function(t,r){this._events.push(new n(t,r,e.Utilities.getHighPerformanceTimestamp()))},r.prototype.getEvents=function(){return this._events},r}();e.LoaderEventRecorder=r;var i=function(){function e(){}return e.prototype.record=function(e,t){},e.prototype.getEvents=function(){return[]},e}();i.INSTANCE=new i,e.NullLoaderEventRecorder=i}(i||(i={}));!function(e){var t=function(){function t(){}return t.fileUriToFilePath=function(e,t){if(t=decodeURI(t),e){if(/^file:\/\/\//.test(t))return t.substr(8);if(/^file:\/\//.test(t))return t.substr(5)}else if(/^file:\/\//.test(t))return t.substr(7);return t},t.startsWith=function(e,t){return e.length>=t.length&&e.substr(0,t.length)===t},t.endsWith=function(e,t){return e.length>=t.length&&e.substr(e.length-t.length)===t},t.containsQueryString=function(e){return/^[^\#]*\?/gi.test(e)},t.isAbsolutePath=function(e){return/^((http:\/\/)|(https:\/\/)|(file:\/\/)|(\/))/.test(e)},t.forEachProperty=function(e,t){if(e){var n=void 0;for(n in e)e.hasOwnProperty(n)&&t(n,e[n])}},t.isEmpty=function(e){var n=!0;return t.forEachProperty(e,function(){n=!1}),n},t.recursiveClone=function(e){if(!e||"object"!=typeof e)return e;var n=Array.isArray(e)?[]:{};return t.forEachProperty(e,function(e,r){n[e]=r&&"object"==typeof r?t.recursiveClone(r):r}),n},t.generateAnonymousModule=function(){return"===anonymous"+t.NEXT_ANONYMOUS_ID+++"==="},t.isAnonymousModule=function(e){return/^===anonymous/.test(e)},t.getHighPerformanceTimestamp=function(){return this.PERFORMANCE_NOW_PROBED||(this.PERFORMANCE_NOW_PROBED=!0,this.HAS_PERFORMANCE_NOW=e.global.performance&&"function"==typeof e.global.performance.now),this.HAS_PERFORMANCE_NOW?e.global.performance.now():Date.now()},t}();t.NEXT_ANONYMOUS_ID=1,t.PERFORMANCE_NOW_PROBED=!1,t.HAS_PERFORMANCE_NOW=!1,e.Utilities=t}(i||(i={}));!function(e){var t=function(){function t(){}return t.validateConfigurationOptions=function(t,n){return"string"!=typeof(n=n||{}).baseUrl&&(n.baseUrl=""),"boolean"!=typeof n.isBuild&&(n.isBuild=!1),"object"!=typeof n.paths&&(n.paths={}),"object"!=typeof n.config&&(n.config={}),void 0===n.catchError&&(n.catchError=t),"string"!=typeof n.urlArgs&&(n.urlArgs=""),"function"!=typeof n.onError&&(n.onError=function(e){return"load"===e.errorCode?(console.error('Loading "'+e.moduleId+'" failed'),console.error("Detail: ",e.detail),e.detail&&e.detail.stack&&console.error(e.detail.stack),console.error("Here are the modules that depend on it:"),void console.error(e.neededBy)):"factory"===e.errorCode?(console.error('The factory method of "'+e.moduleId+'" has thrown an exception'),console.error(e.detail),void(e.detail&&e.detail.stack&&console.error(e.detail.stack))):void 0}),"object"==typeof n.ignoreDuplicateModules&&Array.isArray(n.ignoreDuplicateModules)||(n.ignoreDuplicateModules=[]),n.baseUrl.length>0&&(e.Utilities.endsWith(n.baseUrl,"/")||(n.baseUrl+="/")),Array.isArray(n.nodeModules)||(n.nodeModules=[]),("number"!=typeof n.nodeCachedDataWriteDelay||n.nodeCachedDataWriteDelay<0)&&(n.nodeCachedDataWriteDelay=7e3),"function"!=typeof n.onNodeCachedData&&(n.onNodeCachedData=function(e,t){e&&("cachedDataRejected"===e.errorCode?console.warn("Rejected cached data from file: "+e.path):"unlink"===e.errorCode||"writeFile"===e.errorCode?(console.error("Problems writing cached data file: "+e.path),console.error(e.detail)):console.error(e))}),n},t.mergeConfigurationOptions=function(n,r,i){void 0===r&&(r=null),void 0===i&&(i=null);var o=e.Utilities.recursiveClone(i||{});return e.Utilities.forEachProperty(r,function(t,n){"ignoreDuplicateModules"===t&&void 0!==o.ignoreDuplicateModules?o.ignoreDuplicateModules=o.ignoreDuplicateModules.concat(n):"paths"===t&&void 0!==o.paths?e.Utilities.forEachProperty(n,function(e,t){return o.paths[e]=t}):"config"===t&&void 0!==o.config?e.Utilities.forEachProperty(n,function(e,t){return o.config[e]=t}):o[t]=e.Utilities.recursiveClone(n)}),t.validateConfigurationOptions(n,o)},t}();e.ConfigurationOptionsUtil=t;var n=function(){function n(e,n){if(this._env=e,this.options=t.mergeConfigurationOptions(this._env.isWebWorker,n),this._createIgnoreDuplicateModulesMap(),this._createNodeModulesMap(),this._createSortedPathsRules(),""===this.options.baseUrl){if(this._env.isNode&&this.options.nodeRequire&&this.options.nodeRequire.main&&this.options.nodeRequire.main.filename){var r=this.options.nodeRequire.main.filename,i=Math.max(r.lastIndexOf("/"),r.lastIndexOf("\\"));this.options.baseUrl=r.substring(0,i+1)}if(this._env.isNode&&this.options.nodeMain){var r=this.options.nodeMain,i=Math.max(r.lastIndexOf("/"),r.lastIndexOf("\\"));this.options.baseUrl=r.substring(0,i+1)}}}return n.prototype._createIgnoreDuplicateModulesMap=function(){this.ignoreDuplicateModulesMap={};for(var e=0;e=0){var r=t.resolveModule(e.substr(0,n)),s=t.resolveModule(e.substr(n+1)),u=this._moduleIdProvider.getModuleId(r+"!"+s),a=this._moduleIdProvider.getModuleId(r);return new o(u,a,s)}return new i(this._moduleIdProvider.getModuleId(t.resolveModule(e)))},s.prototype._normalizeDependencies=function(e,t){for(var n=[],r=0,i=0,o=e.length;i0;){var a=u.shift(),l=this._modules2[a];l&&(s=l.onDependencyError(n)||s);var c=this._inverseDependencies2[a];if(c)for(var i=0,o=c.length;i0;){var u=s.shift().dependencies;if(u)for(var i=0,o=u.length;i=i.length)n._onLoadError(t,r);else{var u=i[o],a=n.getRecorder();if(n._config.isBuild()&&"empty:"===u)return n._buildInfoPath[t]=u,n.defineModule(n._moduleIdProvider.getStrModuleId(t),[],null,null,null),void n._onLoad(t);a.record(e.LoaderEventType.BeginLoadingScript,u),n._scriptLoader.load(n,u,function(){n._config.isBuild()&&(n._buildInfoPath[t]=u),a.record(e.LoaderEventType.EndLoadingScriptOK,u),n._onLoad(t)},function(t){a.record(e.LoaderEventType.EndLoadingScriptError,u),s(t)})}};s(null)}},s.prototype._loadPluginDependency=function(e,n){var r=this;if(!this._modules2[n.id]&&!this._knownModules2[n.id]){this._knownModules2[n.id]=!0;var i=function(e){r.defineModule(r._moduleIdProvider.getStrModuleId(n.id),[],e,null,null)};i.error=function(e){r._config.onError(r._createLoadError(n.id,e))},e.load(n.pluginParam,this._createRequire(t.ROOT),i,this._config.getOptionsLiteral())}},s.prototype._resolve=function(e){for(var t=this,n=e.dependencies,r=0,s=n.length;r \n")),e.unresolvedDependenciesCount--}else if(this._inverseDependencies2[u.id]=this._inverseDependencies2[u.id]||[],this._inverseDependencies2[u.id].push(e.id),u instanceof o){var c=this._modules2[u.pluginId];if(c&&c.isComplete()){this._loadPluginDependency(c.exports,u);continue}var f=this._inversePluginDependencies2.get(u.pluginId);f||(f=[],this._inversePluginDependencies2.set(u.pluginId,f)),f.push(u),this._loadModule(u.pluginId)}else this._loadModule(u.id)}else e.unresolvedDependenciesCount--;else e.unresolvedDependenciesCount--;else e.exportsPassedIn=!0,e.unresolvedDependenciesCount--}0===e.unresolvedDependenciesCount&&this._onModuleComplete(e)},s.prototype._onModuleComplete=function(e){var t=this,n=this.getRecorder();if(!e.isComplete()){for(var r=e.dependencies,o=[],s=0,u=r.length;s0||this.m_modifiedCount>0)&&this.m_changes.push(new n.DiffChange(this.m_originalStart,this.m_originalCount,this.m_modifiedStart,this.m_modifiedCount)),this.m_originalCount=0,this.m_modifiedCount=0,this.m_originalStart=Number.MAX_VALUE,this.m_modifiedStart=Number.MAX_VALUE},e.prototype.AddOriginalElement=function(e,t){this.m_originalStart=Math.min(this.m_originalStart,e),this.m_modifiedStart=Math.min(this.m_modifiedStart,t),this.m_originalCount++},e.prototype.AddModifiedElement=function(e,t){this.m_originalStart=Math.min(this.m_originalStart,e),this.m_modifiedStart=Math.min(this.m_modifiedStart,t),this.m_modifiedCount++},e.prototype.getChanges=function(){return(this.m_originalCount>0||this.m_modifiedCount>0)&&this.MarkNextChange(),this.m_changes},e.prototype.getReverseChanges=function(){return(this.m_originalCount>0||this.m_modifiedCount>0)&&this.MarkNextChange(),this.m_changes.reverse(),this.m_changes},e}(),u=Object.prototype.hasOwnProperty,a=function(){function e(e,t,n){void 0===n&&(n=null),this.OriginalSequence=e,this.ModifiedSequence=t,this.ContinueProcessingPredicate=n,this.m_originalIds=[],this.m_modifiedIds=[],this.m_forwardHistory=[],this.m_reverseHistory=[],this.ComputeUniqueIdentifiers()}return e.prototype.ComputeUniqueIdentifiers=function(){var e=this.OriginalSequence.getLength(),t=this.ModifiedSequence.getLength();this.m_originalIds=new Array(e),this.m_modifiedIds=new Array(t);var n,r={},i=1;for(n=0;n=e&&o>=r&&this.ElementsAreEqual(t,o);)t--,o--;if(e>t||r>o){var u=void 0;return r<=o?(i.Assert(e===t+1,"originalStart should only be one more than originalEnd"),u=[new n.DiffChange(e,0,r,o-r+1)]):e<=t?(i.Assert(r===o+1,"modifiedStart should only be one more than modifiedEnd"),u=[new n.DiffChange(e,t-e+1,r,0)]):(i.Assert(e===t+1,"originalStart should only be one more than originalEnd"),i.Assert(r===o+1,"modifiedStart should only be one more than modifiedEnd"),u=[]),u}var a=[0],l=[0],c=this.ComputeRecursionPoint(e,t,r,o,a,l,s),f=a[0],h=l[0];if(null!==c)return c;if(!s[0]){var d=this.ComputeDiffRecursive(e,f,r,h,s),p=[];return p=s[0]?[new n.DiffChange(f+1,t-(f+1)+1,h+1,o-(h+1)+1)]:this.ComputeDiffRecursive(f+1,t,h+1,o,s),this.ConcatenateChanges(d,p)}return[new n.DiffChange(e,t-e+1,r,o-r+1)]},e.prototype.WALKTRACE=function(e,t,r,i,o,u,a,l,c,f,h,d,p,m,_,g,v,y){var b,C=null,E=null,S=new s,L=t,N=r,A=p[0]-g[0]-i,P=Number.MIN_VALUE,M=this.m_forwardHistory.length-1;do{(b=A+e)===L||b=0&&(e=(c=this.m_forwardHistory[M])[0],L=1,N=c.length-1)}while(--M>=-1);if(C=S.getReverseChanges(),y[0]){var w=p[0]+1,D=g[0]+1;if(null!==C&&C.length>0){var I=C[C.length-1];w=Math.max(w,I.getOriginalEnd()),D=Math.max(D,I.getModifiedEnd())}E=[new n.DiffChange(w,d-w+1,D,_-D+1)]}else{S=new s,L=u,N=a,A=p[0]-g[0]-l,P=Number.MAX_VALUE,M=v?this.m_reverseHistory.length-1:this.m_reverseHistory.length-2;do{(b=A+o)===L||b=f[b+1]?(m=(h=f[b+1]-1)-A-l,h>P&&S.MarkNextChange(),P=h+1,S.AddOriginalElement(h+1,m+1),A=b+1-o):(m=(h=f[b-1])-A-l,h>P&&S.MarkNextChange(),P=h,S.AddModifiedElement(h+1,m+1),A=b-1-o),M>=0&&(o=(f=this.m_reverseHistory[M])[0],L=1,N=f.length-1)}while(--M>=-1);E=S.getChanges()}return this.ConcatenateChanges(C,E)},e.prototype.ComputeRecursionPoint=function(e,t,r,i,s,u,a){var l,c,f,h=0,d=0,p=0,m=0;e--,r--,s[0]=0,u[0]=0,this.m_forwardHistory=[],this.m_reverseHistory=[];var _=t-e+(i-r),g=_+1,v=new Array(g),y=new Array(g),b=i-r,C=t-e,E=e-r,S=t-i,L=(C-b)%2==0;v[b]=e,y[C]=t,a[0]=!1;var N,A;for(f=1;f<=_/2+1;f++){var P=0,M=0;for(h=this.ClipDiagonalBound(b-f,f,b,g),d=this.ClipDiagonalBound(b+f,f,b,g),N=h;N<=d;N+=2){for(c=(l=N===h||NP+M&&(P=l,M=c),!L&&Math.abs(N-C)<=f-1&&l>=y[N])return s[0]=l,u[0]=c,A<=y[N]&&f<=1448?this.WALKTRACE(b,h,d,E,C,p,m,S,v,y,l,t,s,c,i,u,L,a):null}var w=(P-e+(M-r)-f)/2;if(null!==this.ContinueProcessingPredicate&&!this.ContinueProcessingPredicate(P,this.OriginalSequence,w))return a[0]=!0,s[0]=P,u[0]=M,w>0&&f<=1448?this.WALKTRACE(b,h,d,E,C,p,m,S,v,y,l,t,s,c,i,u,L,a):(e++,r++,[new n.DiffChange(e,t-e+1,r,i-r+1)]);for(p=this.ClipDiagonalBound(C-f,f,C,g),m=this.ClipDiagonalBound(C+f,f,C,g),N=p;N<=m;N+=2){for(c=(l=N===p||N=y[N+1]?y[N+1]-1:y[N-1])-(N-C)-S,A=l;l>e&&c>r&&this.ElementsAreEqual(l,c);)l--,c--;if(y[N]=l,L&&Math.abs(N-b)<=f&&l<=v[N])return s[0]=l,u[0]=c,A>=v[N]&&f<=1448?this.WALKTRACE(b,h,d,E,C,p,m,S,v,y,l,t,s,c,i,u,L,a):null}if(f<=1447){var D=new Array(d-h+2);D[0]=b-h+1,o.Copy(v,h,D,1,d-h+1),this.m_forwardHistory.push(D),(D=new Array(m-p+2))[0]=C-p+1,o.Copy(y,p,D,1,m-p+1),this.m_reverseHistory.push(D)}}return this.WALKTRACE(b,h,d,E,C,p,m,S,v,y,l,t,s,c,i,u,L,a)},e.prototype.ShiftChanges=function(e){var t;do{t=!1;for(l=0;l0,s=n.modifiedLength>0;n.originalStart+n.originalLength=0;l--){var n=e[l],r=0,i=0;if(l>0){var c=e[l-1];c.originalLength>0&&(r=c.originalStart+c.originalLength),c.modifiedLength>0&&(i=c.modifiedStart+c.modifiedLength)}for(var o=n.originalLength>0,s=n.modifiedLength>0,f=0,h=this._boundaryScore(n.originalStart,n.originalLength,n.modifiedStart,n.modifiedLength),d=1;;d++){var p=n.originalStart-d,m=n.modifiedStart-d;if(ph&&(h=_,f=d)}n.originalStart-=f,n.modifiedStart-=f}return e},e.prototype._OriginalIsBoundary=function(e){return e<=0||e>=this.OriginalSequence.getLength()-1||/^\s*$/.test(this.OriginalSequence.getElementHash(e))},e.prototype._OriginalRegionIsBoundary=function(e,t){if(this._OriginalIsBoundary(e)||this._OriginalIsBoundary(e-1))return!0;if(t>0){var n=e+t;if(this._OriginalIsBoundary(n-1)||this._OriginalIsBoundary(n))return!0}return!1},e.prototype._ModifiedIsBoundary=function(e){return e<=0||e>=this.ModifiedSequence.getLength()-1||/^\s*$/.test(this.ModifiedSequence.getElementHash(e))},e.prototype._ModifiedRegionIsBoundary=function(e,t){if(this._ModifiedIsBoundary(e)||this._ModifiedIsBoundary(e-1))return!0;if(t>0){var n=e+t;if(this._ModifiedIsBoundary(n-1)||this._ModifiedIsBoundary(n))return!0}return!1},e.prototype._boundaryScore=function(e,t,n,r){return(this._OriginalRegionIsBoundary(e,t)?1:0)+(this._ModifiedRegionIsBoundary(n,r)?1:0)},e.prototype.ConcatenateChanges=function(e,t){var n=[],r=null;return 0===e.length||0===t.length?t.length>0?t:e:this.ChangesOverlap(e[e.length-1],t[0],n)?(r=new Array(e.length+t.length-1),o.Copy(e,0,r,0,e.length-1),r[e.length-1]=n[0],o.Copy(t,1,r,e.length,t.length-1),r):(r=new Array(e.length+t.length),o.Copy(e,0,r,0,e.length),o.Copy(t,0,r,e.length,t.length),r)},e.prototype.ChangesOverlap=function(e,t,r){if(i.Assert(e.originalStart<=t.originalStart,"Left change is not less than or equal to right change"),i.Assert(e.modifiedStart<=t.modifiedStart,"Left change is not less than or equal to right change"),e.originalStart+e.originalLength>=t.originalStart||e.modifiedStart+e.modifiedLength>=t.modifiedStart){var o=e.originalStart,s=e.originalLength,u=e.modifiedStart,a=e.modifiedLength;return e.originalStart+e.originalLength>=t.originalStart&&(s=t.originalStart+t.originalLength-e.originalStart),e.modifiedStart+e.modifiedLength>=t.modifiedStart&&(a=t.modifiedStart+t.modifiedLength-e.modifiedStart),r[0]=new n.DiffChange(o,s,u,a),!0}return r[0]=null,!1},e.prototype.ClipDiagonalBound=function(e,t,n,r){if(e>=0&&e>>0)>>>0},t.createKeybinding=function(e,t){if(0===e)return null;var r=(65535&e)>>>0,i=(4294901760&e)>>>16;return 0!==i?new l(n(r,t),n(i,t)):n(r,t)},t.createSimpleKeybinding=n;!function(e){e[e.Simple=1]="Simple",e[e.Chord=2]="Chord"}(t.KeybindingType||(t.KeybindingType={}));var a=function(){function e(e,t,n,r,i){this.type=1,this.ctrlKey=e,this.shiftKey=t,this.altKey=n,this.metaKey=r,this.keyCode=i}return e.prototype.equals=function(e){return 1===e.type&&(this.ctrlKey===e.ctrlKey&&this.shiftKey===e.shiftKey&&this.altKey===e.altKey&&this.metaKey===e.metaKey&&this.keyCode===e.keyCode)},e.prototype.isModifierKey=function(){return 0===this.keyCode||5===this.keyCode||57===this.keyCode||6===this.keyCode||4===this.keyCode},e.prototype.isDuplicateModifierCase=function(){return this.ctrlKey&&5===this.keyCode||this.shiftKey&&4===this.keyCode||this.altKey&&6===this.keyCode||this.metaKey&&57===this.keyCode},e}();t.SimpleKeybinding=a;var l=function(){return function(e,t){this.type=2,this.firstPart=e,this.chordPart=t}}();t.ChordKeybinding=l;var c=function(){return function(e,t,n,r,i,o){this.ctrlKey=e,this.shiftKey=t,this.altKey=n,this.metaKey=r,this.keyLabel=i,this.keyAriaLabel=o}}();t.ResolvedKeybindingPart=c;var f=function(){return function(){}}();t.ResolvedKeybinding=f}),r(e[10],t([1,0,11]),function(e,t,n){"use strict";function r(e){for(var t=[],n=1;n=0,r=d.indexOf("Macintosh")>=0,i=d.indexOf("Linux")>=0,u=!0,l=a=navigator.language}var p;!function(e){e[e.Web=0]="Web",e[e.Mac=1]="Mac",e[e.Linux=2]="Linux",e[e.Windows=3]="Windows"}(p=t.Platform||(t.Platform={}));var m=p.Web;s&&(r?m=p.Mac:n?m=p.Windows:i&&(m=p.Linux)),t.isWindows=n,t.isMacintosh=r,t.isLinux=i,t.isRootUser=o,t.isNative=s,t.isWeb=u,t.platform=m,t.language=l,t.locale=a;var _="object"==typeof self?self:global;t.globals=_,t.hasWebWorkerSupport=function(){return void 0!==_.Worker},t.setTimeout=_.setTimeout.bind(_),t.clearTimeout=_.clearTimeout.bind(_),t.setInterval=_.setInterval.bind(_),t.clearInterval=_.clearInterval.bind(_);!function(e){e[e.Windows=1]="Windows",e[e.Macintosh=2]="Macintosh",e[e.Linux=3]="Linux"}(t.OperatingSystem||(t.OperatingSystem={})),t.OS=r?2:n?1:3;!function(e){e[e.Unknown=0]="Unknown",e[e.Disabled=1]="Disabled",e[e.Enabled=2]="Enabled"}(t.AccessibilitySupport||(t.AccessibilitySupport={}))}),r(e[14],t([1,0]),function(e,t){"use strict";function n(e){return Array.isArray?Array.isArray(e):!(!e||typeof e.length!==a.number||e.constructor!==Array)}function r(e){return typeof e===a.string||e instanceof String}function i(e){return!(typeof e!==a.object||null===e||Array.isArray(e)||e instanceof RegExp||e instanceof Date)}function o(e){return typeof e===a.undefined}function s(e){return typeof e===a.function}function u(e,t){if(r(t)){if(typeof e!==t)throw new Error("argument does not match constraint: typeof "+t)}else if(s(t)){if(e instanceof t)return;if(e&&e.constructor===t)return;if(1===t.length&&!0===t.call(void 0,e))return;throw new Error("argument does not match one of these constraints: arg instanceof constraint, arg.constructor === constraint, nor constraint(arg) === true")}}Object.defineProperty(t,"__esModule",{value:!0});var a={number:"number",string:"string",undefined:"undefined",object:"object",function:"function"};t.isArray=n,t.isString=r,t.isStringArray=function(e){return n(e)&&e.every(function(e){return r(e)})},t.isObject=i,t.isNumber=function(e){return(typeof e===a.number||e instanceof Number)&&!isNaN(e)},t.isBoolean=function(e){return!0===e||!1===e},t.isUndefined=o,t.isUndefinedOrNull=function(e){return o(e)||null===e};var l=Object.prototype.hasOwnProperty;t.isEmptyObject=function(e){if(!i(e))return!1;for(var t in e)if(l.call(e,t))return!1;return!0},t.isFunction=s,t.areFunctions=function(){for(var e=[],t=0;t0&&e.every(s)},t.validateConstraints=function(e,t){for(var n=Math.min(e.length,t.length),r=0;rthis.limit;)this.trim()},e.prototype.serialize=function(){var e={entries:[]};return this.map.forEach(function(t){e.entries.push({key:t.key,value:t.value})}),e},Object.defineProperty(e.prototype,"size",{get:function(){return this.map.size},enumerable:!0,configurable:!0}),e.prototype.set=function(e,t){if(this.map.has(e))return!1;var n={key:e,value:t};return this.push(n),this.size>this.limit&&this.trim(),!0},e.prototype.get=function(e){var t=this.map.get(e);return t?t.value:null},e.prototype.getOrSet=function(e,t){var n=this.get(e);return n||(this.set(e,t),t)},e.prototype.delete=function(e){var t=this.map.get(e);return t?(this.map.delete(e),t.next?t.next.prev=t.prev:this.head=t.prev,t.prev?t.prev.next=t.next:this.tail=t.next,t.value):null},e.prototype.has=function(e){return this.map.has(e)},e.prototype.clear=function(){this.map.clear(),this.head=null,this.tail=null},e.prototype.push=function(e){this.head&&(e.prev=this.head,this.head.next=e),this.tail||(this.tail=e),this.head=e,this.map.set(e.key,e)},e.prototype.trim=function(){if(this.tail)if(this.ratiot?1:0}function u(e){return e>=97&&e<=122}function a(e){return e>=65&&e<=90}function l(e){return u(e)||a(e)}function c(e,t,n){if(void 0===n&&(n=e.length),"string"!=typeof e||"string"!=typeof t)return!1;for(var r=0;r=11904&&e<=55215||e>=63744&&e<=64255||e>=65281&&e<=65374}Object.defineProperty(t,"__esModule",{value:!0}),t.empty="",t.isFalsyOrWhitespace=function(e){return!e||"string"!=typeof e||0===e.trim().length},t.pad=function(e,t,n){void 0===n&&(n="0");for(var r=""+e,i=[r],o=r.length;o=t.length?e:t[r]})},t.escape=function(e){return e.replace(/[<|>|&]/g,function(e){switch(e){case"<":return"<";case">":return">";case"&":return"&";default:return e}})},t.escapeRegExpCharacters=r,t.trim=function(e,t){return void 0===t&&(t=" "),o(i(e,t),t)},t.ltrim=i,t.rtrim=o,t.convertSimple2RegExpPattern=function(e){return e.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g,"\\$&").replace(/[\*]/g,".*")},t.stripWildcards=function(e){return e.replace(/\*/g,"")},t.startsWith=function(e,t){if(e.length0?e.indexOf(t,n)===n:0===n&&e===t},t.indexOfIgnoreCase=function(e,t,n){void 0===n&&(n=0);var i=e.indexOf(t,n);return i<0&&(n>0&&(e=e.substr(n)),t=r(t),i=e.search(new RegExp(t,"i"))),i},t.createRegExp=function(e,t,n){if(void 0===n&&(n={}),!e)throw new Error("Cannot create regex from empty string");t||(e=r(e)),n.wholeWord&&(/\B/.test(e.charAt(0))||(e="\\b"+e),/\B/.test(e.charAt(e.length-1))||(e+="\\b"));var i="";return n.global&&(i+="g"),n.matchCase||(i+="i"),n.multiline&&(i+="m"),new RegExp(e,i)},t.regExpLeadsToEndlessLoop=function(e){return"^"!==e.source&&"^$"!==e.source&&"$"!==e.source&&e.exec("")&&0===e.lastIndex},t.canNormalize="function"==typeof"".normalize;var p=/[^\u0000-\u0080]/,m=new n.BoundedMap(1e4);t.normalizeNFC=function(e){if(!t.canNormalize||!e)return e;var n=m.get(e);if(n)return n;var r;return r=p.test(e)?e.normalize("NFC"):e,m.set(e,r),r},t.firstNonWhitespaceIndex=function(e){for(var t=0,n=e.length;t=0;n--){var r=e.charCodeAt(n);if(32!==r&&9!==r)return n}return-1},t.compare=s,t.compareIgnoreCase=function(e,t){for(var n=Math.min(e.length,t.length),r=0;rt.length?1:0},t.equalsIgnoreCase=function(e,t){return(e?e.length:0)===(t?t.length:0)&&c(e,t)},t.beginsWithIgnoreCase=function(e,t){var n=t.length;return!(t.length>e.length)&&c(e,t,n)},t.commonPrefixLength=function(e,t){var n,r=Math.min(e.length,t.length);for(n=0;n0;){if(f(e,i,n,t,0,r))return r;r-=1,i+=1}return 0},t.isHighSurrogate=function(e){return 55296<=e&&e<=56319},t.isLowSurrogate=function(e){return 56320<=e&&e<=57343};var _=/(?:[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05F4\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u0710\u0712-\u072F\u074D-\u07A5\u07B1-\u07EA\u07F4\u07F5\u07FA-\u0815\u081A\u0824\u0828\u0830-\u0858\u085E-\u08BD\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFD3D\uFD50-\uFDFC\uFE70-\uFEFC]|\uD802[\uDC00-\uDD1B\uDD20-\uDE00\uDE10-\uDE33\uDE40-\uDEE4\uDEEB-\uDF35\uDF40-\uDFFF]|\uD803[\uDC00-\uDCFF]|\uD83A[\uDC00-\uDCCF\uDD00-\uDD43\uDD50-\uDFFF]|\uD83B[\uDC00-\uDEBB])/;t.containsRTL=function(e){return _.test(e)};var g=/(?:[\u231A\u231B\u23F0\u23F3\u2600-\u27BF\u2B50\u2B55]|\uD83C[\uDDE6-\uDDFF\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F\uDE80-\uDEF8]|\uD83E[\uDD00-\uDDE6])/;t.containsEmoji=function(e){return g.test(e)};var v=/^[\t\n\r\x20-\x7E]*$/;t.isBasicASCII=function(e){return v.test(e)},t.containsFullWidthCharacter=function(e){for(var t=0,n=e.length;tn)return 0;var i,o,s=[],u=[];for(i=0;i=0;o--)if((i+=r[o].length)>n){r.splice(0,o);break}return r.join(t.empty).replace(/^\s/,t.empty)};var y=/\x1B\x5B[12]?K/g,b=/\x1b\[\d+m/g,C=/\x1b\[0?m/g;t.removeAnsiEscapeCodes=function(e){return e&&(e=(e=(e=e.replace(y,"")).replace(b,"")).replace(C,"")),e},t.UTF8_BOM_CHARACTER=String.fromCharCode(65279),t.startsWithUTF8BOM=function(e){return e&&e.length>0&&65279===e.charCodeAt(0)},t.appendWithLimit=function(e,t,n){var r=e.length+t.length;return r>n&&(e="..."+e.substr(r-n)),t.length>n?e+=t.substr(t.length-n):e+=t,e},t.safeBtoa=function(e){return btoa(encodeURIComponent(e))},t.repeat=function(e,t){for(var n="",r=0;r"),o}var s=e;s.Namespace||(s.Namespace=Object.create(Object.prototype));var u={uninitialized:1,working:2,initialized:3};Object.defineProperties(s.Namespace,{defineWithParent:{value:o,writable:!0,enumerable:!0,configurable:!0},define:{value:function(e,n){return o(t,e,n)},writable:!0,enumerable:!0,configurable:!0},_lazy:{value:function(e){var t,n,i=u.uninitialized;return{setName:function(e){t=e},get:function(){switch(i){case u.initialized:return n;case u.uninitialized:i=u.working;try{r("WinJS.Namespace._lazy:"+t+",StartTM"),n=e()}finally{r("WinJS.Namespace._lazy:"+t+",StopTM"),i=u.uninitialized}return e=null,i=u.initialized,n;case u.working:throw"Illegal: reentrancy on initialization";default:throw"Illegal"}},set:function(e){switch(i){case u.working:throw"Illegal: reentrancy on initialization";default:i=u.initialized,n=e}},enumerable:!0,configurable:!0}},writable:!0,enumerable:!0,configurable:!0},_moduleDefine:{value:function(e,r,o){var s=[e],u=null;return r&&(u=n(t,r),s.push(u)),i(s,o,r||""),u},writable:!0,enumerable:!0,configurable:!0}})}(),function(){function t(e,t,r){return e=e||function(){},n.markSupportedForProcessing(e),t&&i(e.prototype,t),r&&i(e,r),e}e.Namespace.define("WinJS.Class",{define:t,derive:function(e,r,o,s){if(e){r=r||function(){};var u=e.prototype;return r.prototype=Object.create(u),n.markSupportedForProcessing(r),Object.defineProperty(r.prototype,"constructor",{value:r,writable:!0,configurable:!0,enumerable:!0}),o&&i(r.prototype,o),s&&i(r,s),r}return t(r,o,s)},mix:function(e){e=e||function(){};var t,n;for(t=1,n=arguments.length;t1)&&a.fire(e),s=null,u=0},n)})},onLastListenerRemove:function(){i.dispose()}});return a.event};var h=function(){function e(){this.buffers=[]}return e.prototype.wrapEvent=function(e){var t=this;return function(n,r,i){return e(function(e){var i=t.buffers[t.buffers.length-1];i?i.push(function(){return n.call(r,e)}):n.call(r,e)},void 0,i)}},e.prototype.bufferEvents=function(e){var t=[];this.buffers.push(t),e(),this.buffers.pop(),t.forEach(function(e){return e()})},e}();t.EventBufferer=h,t.mapEvent=u,t.filterEvent=a;var d=function(){function e(e){this._event=e}return Object.defineProperty(e.prototype,"event",{get:function(){return this._event},enumerable:!0,configurable:!0}),e.prototype.map=function(t){return new e(u(this._event,t))},e.prototype.filter=function(t){return new e(a(this._event,t))},e.prototype.on=function(e,t,n){return this._event(e,t,n)},e}();t.chain=function(e){return new d(e)},t.stopwatch=function(e){var t=(new Date).getTime();return u(s(e),function(e){return(new Date).getTime()-t})},t.buffer=function(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=[]),n=n.slice();var r=e(function(e){n?n.push(e):o.fire(e)}),i=function(){n.forEach(function(e){return o.fire(e)}),n=null},o=new c({onFirstListenerAdd:function(){r||(r=e(function(e){return o.fire(e)}))},onFirstListenerDidAdd:function(){n&&(t?setTimeout(i):i())},onLastListenerRemove:function(){r.dispose(),r=null}});return o.event},t.echo=function(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=[]),n=n.slice(),e(function(e){n.push(e),i.fire(e)});var r=function(e,t){return n.forEach(function(n){return e.call(t,n)})},i=new c({onListenerDidAdd:function(e,n,i){t?setTimeout(function(){return r(n,i)}):r(n,i)}});return i.event}}),r(e[13],t([1,0,9]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r,i=Object.freeze(function(e,t){var n=setTimeout(e.bind(t),0);return{dispose:function(){clearTimeout(n)}}});!function(e){e.None=Object.freeze({isCancellationRequested:!1,onCancellationRequested:n.default.None}),e.Cancelled=Object.freeze({isCancellationRequested:!0,onCancellationRequested:i})}(r=t.CancellationToken||(t.CancellationToken={}));var o=function(){function e(){this._isCancelled=!1}return e.prototype.cancel=function(){this._isCancelled||(this._isCancelled=!0,this._emitter&&(this._emitter.fire(void 0),this._emitter=void 0))},Object.defineProperty(e.prototype,"isCancellationRequested",{get:function(){return this._isCancelled},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onCancellationRequested",{get:function(){return this._isCancelled?i:(this._emitter||(this._emitter=new n.Emitter),this._emitter.event)},enumerable:!0,configurable:!0}),e}(),s=function(){function e(){}return Object.defineProperty(e.prototype,"token",{get:function(){return this._token||(this._token=new o),this._token},enumerable:!0,configurable:!0}),e.prototype.cancel=function(){this._token?this._token.cancel():this._token=r.Cancelled},e.prototype.dispose=function(){this.cancel()},e}();t.CancellationTokenSource=s}),r(e[18],t([1,0,8,4,2,13,10,9]),function(e,t,n,r,i,s,u,a){"use strict";function l(e){return e&&"function"==typeof e.then}function c(e,t){return new i.TPromise(function(r,i,o){e.done(function(e){try{t(e)}catch(e){n.onUnexpectedError(e)}r(e)},function(e){try{t(e)}catch(e){n.onUnexpectedError(e)}i(e)},function(e){o(e)})},function(){e.cancel()})}Object.defineProperty(t,"__esModule",{value:!0}),t.toThenable=function(e){return l(e)?e:i.TPromise.as(e)},t.asWinJsPromise=function(e){var t=new s.CancellationTokenSource;return new i.TPromise(function(n,r,o){var s=e(t.token);s instanceof i.TPromise?s.then(n,r,o):l(s)?s.then(n,r):n(s)},function(){t.cancel()})},t.wireCancellationToken=function(e,t,r){var o=e.onCancellationRequested(function(){return t.cancel()});return r&&(t=t.then(void 0,function(e){if(!n.isPromiseCanceledError(e))return i.TPromise.wrapError(e)})),c(t,function(){return o.dispose()})};var f=function(){function e(){this.activePromise=null,this.queuedPromise=null,this.queuedPromiseFactory=null}return e.prototype.queue=function(e){var t=this;if(this.activePromise){if(this.queuedPromiseFactory=e,!this.queuedPromise){var n=function(){t.queuedPromise=null;var e=t.queue(t.queuedPromiseFactory);return t.queuedPromiseFactory=null,e};this.queuedPromise=new i.TPromise(function(e,r,i){t.activePromise.then(n,n,i).done(e)},function(){t.activePromise.cancel()})}return new i.TPromise(function(e,n,r){t.queuedPromise.then(e,n,r)},function(){})}return this.activePromise=e(),new i.TPromise(function(e,n,r){t.activePromise.done(function(n){t.activePromise=null,e(n)},function(e){t.activePromise=null,n(e)},r)},function(){t.activePromise.cancel()})},e}();t.Throttler=f;var h=function(){function e(){this.current=i.TPromise.as(null)}return e.prototype.queue=function(e){return this.current=this.current.then(function(){return e()})},e}();t.SimpleThrottler=h;var d=function(){function e(e){this.defaultDelay=e,this.timeout=null,this.completionPromise=null,this.onSuccess=null,this.task=null}return e.prototype.trigger=function(e,t){var n=this;return void 0===t&&(t=this.defaultDelay),this.task=e,this.cancelTimeout(),this.completionPromise||(this.completionPromise=new i.TPromise(function(e){n.onSuccess=e},function(){}).then(function(){n.completionPromise=null,n.onSuccess=null;var e=n.task;return n.task=null,e()})),this.timeout=setTimeout(function(){n.timeout=null,n.onSuccess(null)},t),this.completionPromise},e.prototype.isTriggered=function(){return null!==this.timeout},e.prototype.cancel=function(){this.cancelTimeout(),this.completionPromise&&(this.completionPromise.cancel(),this.completionPromise=null)},e.prototype.cancelTimeout=function(){null!==this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},e}();t.Delayer=d;var p=function(e){function t(t){var n=e.call(this,t)||this;return n.throttler=new f,n}return o(t,e),t.prototype.trigger=function(t,n){var r=this;return e.prototype.trigger.call(this,function(){return r.throttler.queue(t)},n)},t}(d);t.ThrottledDelayer=p;var m=function(e){function t(t,n){void 0===n&&(n=0);var r=e.call(this,t)||this;return r.minimumPeriod=n,r.periodThrottler=new f,r}return o(t,e),t.prototype.trigger=function(t,n){var r=this;return e.prototype.trigger.call(this,function(){return r.periodThrottler.queue(function(){return i.Promise.join([i.TPromise.timeout(r.minimumPeriod),t()]).then(function(e){return e[1]})})},n)},t}(p);t.PeriodThrottledDelayer=m;var _=function(){function e(){var e=this;this._value=new i.TPromise(function(t,n){e._completeCallback=t,e._errorCallback=n})}return Object.defineProperty(e.prototype,"value",{get:function(){return this._value},enumerable:!0,configurable:!0}),e.prototype.complete=function(e){this._completeCallback(e)},e.prototype.error=function(e){this._errorCallback(e)},e}();t.PromiseSource=_;var g=function(e){function t(t){var r,i,o,s=this;return s=e.call(this,function(e,t,n){r=e,i=t,o=n},function(){i(n.canceled())})||this,t.then(r,i,o),s}return o(t,e),t}(i.TPromise);t.ShallowCancelThenPromise=g,t.always=c,t.sequence=function(e){function t(){return e.length?e.pop()():null}function n(e){void 0!==e&&null!==e&&r.push(e);var o=t();return o?o.then(n):i.TPromise.as(r)}var r=[];return e=e.reverse(),i.TPromise.as(null).then(n)},t.first=function(e,t){void 0===t&&(t=function(e){return!!e}),e=e.reverse().slice();var n=function(){return 0===e.length?i.TPromise.as(null):e.pop()().then(function(e){return t(e)?i.TPromise.as(e):n()})};return n()};var v=function(){function e(e){this.maxDegreeOfParalellism=e,this.outstandingPromises=[],this.runningPromises=0,this._onFinished=new a.Emitter}return Object.defineProperty(e.prototype,"onFinished",{get:function(){return this._onFinished.event},enumerable:!0,configurable:!0}),e.prototype.queue=function(e){var t=this;return new i.TPromise(function(n,r,i){t.outstandingPromises.push({factory:e,c:n,e:r,p:i}),t.consume()})},e.prototype.consume=function(){for(var e=this;this.outstandingPromises.length&&this.runningPromises0?this.consume():this._onFinished.fire()},e.prototype.dispose=function(){this._onFinished.dispose()},e}();t.Limiter=v;var y=function(e){function t(){return e.call(this,1)||this}return o(t,e),t}(v);t.Queue=y,t.setDisposableTimeout=function(e,t){for(var n=[],r=2;rn||e===n&&t>r?(this.startLineNumber=n,this.startColumn=r,this.endLineNumber=e,this.endColumn=t):(this.startLineNumber=e,this.startColumn=t,this.endLineNumber=n,this.endColumn=r)}return e.prototype.isEmpty=function(){return e.isEmpty(this)},e.isEmpty=function(e){return e.startLineNumber===e.endLineNumber&&e.startColumn===e.endColumn},e.prototype.containsPosition=function(t){return e.containsPosition(this,t)},e.containsPosition=function(e,t){return!(t.lineNumbere.endLineNumber)&&(!(t.lineNumber===e.startLineNumber&&t.columne.endColumn))},e.prototype.containsRange=function(t){return e.containsRange(this,t)},e.containsRange=function(e,t){return!(t.startLineNumbere.endLineNumber||t.endLineNumber>e.endLineNumber)&&(!(t.startLineNumber===e.startLineNumber&&t.startColumne.endColumn)))},e.prototype.plusRange=function(t){return e.plusRange(this,t)},e.plusRange=function(t,n){var r,i,o,s;return n.startLineNumbert.endLineNumber?(o=n.endLineNumber,s=n.endColumn):n.endLineNumber===t.endLineNumber?(o=n.endLineNumber,s=Math.max(n.endColumn,t.endColumn)):(o=t.endLineNumber,s=t.endColumn),new e(r,i,o,s)},e.prototype.intersectRanges=function(t){return e.intersectRanges(this,t)},e.intersectRanges=function(t,n){var r=t.startLineNumber,i=t.startColumn,o=t.endLineNumber,s=t.endColumn,u=n.startLineNumber,a=n.startColumn,l=n.endLineNumber,c=n.endColumn;return rl?(o=l,s=c):o===l&&(s=Math.min(s,c)),r>o?null:r===o&&i>s?null:new e(r,i,o,s)},e.prototype.equalsRange=function(t){return e.equalsRange(this,t)},e.equalsRange=function(e,t){return!!e&&!!t&&e.startLineNumber===t.startLineNumber&&e.startColumn===t.startColumn&&e.endLineNumber===t.endLineNumber&&e.endColumn===t.endColumn},e.prototype.getEndPosition=function(){return new n.Position(this.endLineNumber,this.endColumn)},e.prototype.getStartPosition=function(){return new n.Position(this.startLineNumber,this.startColumn)},e.prototype.cloneRange=function(){return new e(this.startLineNumber,this.startColumn,this.endLineNumber,this.endColumn)},e.prototype.toString=function(){return"["+this.startLineNumber+","+this.startColumn+" -> "+this.endLineNumber+","+this.endColumn+"]"},e.prototype.setEndPosition=function(t,n){return new e(this.startLineNumber,this.startColumn,t,n)},e.prototype.setStartPosition=function(t,n){return new e(t,n,this.endLineNumber,this.endColumn)},e.prototype.collapseToStart=function(){return e.collapseToStart(this)},e.collapseToStart=function(t){return new e(t.startLineNumber,t.startColumn,t.startLineNumber,t.startColumn)},e.fromPositions=function(t,n){return void 0===n&&(n=t),new e(t.lineNumber,t.column,n.lineNumber,n.column)},e.lift=function(t){return t?new e(t.startLineNumber,t.startColumn,t.endLineNumber,t.endColumn):null},e.isIRange=function(e){return e&&"number"==typeof e.startLineNumber&&"number"==typeof e.startColumn&&"number"==typeof e.endLineNumber&&"number"==typeof e.endColumn},e.areIntersectingOrTouching=function(e,t){return!(e.endLineNumbere.startLineNumber},e}();t.Range=r}),r(e[22],t([1,0,5,3]),function(e,t,n,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i;!function(e){e[e.LTR=0]="LTR",e[e.RTL=1]="RTL"}(i=t.SelectionDirection||(t.SelectionDirection={}));var s=function(e){function t(t,n,r,i){var o=e.call(this,t,n,r,i)||this;return o.selectionStartLineNumber=t,o.selectionStartColumn=n,o.positionLineNumber=r,o.positionColumn=i,o}return o(t,e),t.prototype.clone=function(){return new t(this.selectionStartLineNumber,this.selectionStartColumn,this.positionLineNumber,this.positionColumn)},t.prototype.toString=function(){return"["+this.selectionStartLineNumber+","+this.selectionStartColumn+" -> "+this.positionLineNumber+","+this.positionColumn+"]"},t.prototype.equalsSelection=function(e){return t.selectionsEqual(this,e)},t.selectionsEqual=function(e,t){return e.selectionStartLineNumber===t.selectionStartLineNumber&&e.selectionStartColumn===t.selectionStartColumn&&e.positionLineNumber===t.positionLineNumber&&e.positionColumn===t.positionColumn},t.prototype.getDirection=function(){return this.selectionStartLineNumber===this.startLineNumber&&this.selectionStartColumn===this.startColumn?i.LTR:i.RTL},t.prototype.setEndPosition=function(e,n){return this.getDirection()===i.LTR?new t(this.startLineNumber,this.startColumn,e,n):new t(e,n,this.startLineNumber,this.startColumn)},t.prototype.getPosition=function(){return new r.Position(this.positionLineNumber,this.positionColumn)},t.prototype.setStartPosition=function(e,n){return this.getDirection()===i.LTR?new t(e,n,this.endLineNumber,this.endColumn):new t(this.endLineNumber,this.endColumn,e,n)},t.fromPositions=function(e,n){return void 0===n&&(n=e),new t(e.lineNumber,e.column,n.lineNumber,n.column)},t.liftSelection=function(e){return new t(e.selectionStartLineNumber,e.selectionStartColumn,e.positionLineNumber,e.positionColumn)},t.selectionsArrEqual=function(e,t){if(e&&!t||!e&&t)return!1;if(!e&&!t)return!0;if(e.length!==t.length)return!1;for(var n=0,r=e.length;n4294967295?4294967295:0|e}Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t,n){for(var r=new Uint8Array(e*t),i=0,o=e*t;i255?255:0|e},t.toUint32=n,t.toUint32Array=function(e){for(var t=e.length,r=new Uint32Array(t),i=0;i=0&&e<256?this._asciiMap[e]=r:this._map.set(e,r)},e.prototype.get=function(e){return e>=0&&e<256?this._asciiMap[e]:this._map.get(e)||this._defaultValue},e}();t.CharacterClassifier=r;var i;!function(e){e[e.False=0]="False",e[e.True=1]="True"}(i||(i={}));var o=function(){function e(){this._actual=new r(0)}return e.prototype.add=function(e){this._actual.set(e,1)},e.prototype.has=function(e){return 1===this._actual.get(e)},e}();t.CharacterSet=o}),r(e[26],t([1,0,12,20]),function(e,t,n,r){"use strict";function i(e,t,r,i){return new n.LcsDiff(e,t,r).ComputeDiff(i)}function s(e){if(e.length<=1)return e;for(var t=[e[0]],n=t[0],r=1,i=e.length;r1&&g>1&&(E=p.charCodeAt(_-2))===(S=m.charCodeAt(g-2));)_--,g--;(_>1||g>1)&&this._pushTrimWhitespaceCharChange(o,s+1,1,_,u+1,1,g);for(var v=c._getLastNonBlankColumn(p,1),y=c._getLastNonBlankColumn(m,1),b=p.length+1,C=m.length+1;v=i)return{word:u[0],startColumn:r+1+u.index,endColumn:r+1+t.lastIndex};return null}function r(e,t,n,r){var i=e-1-r;t.lastIndex=0;for(var o;o=t.exec(n);){if(o.index>i)return null;if(t.lastIndex>=i)return{word:o[0],startColumn:r+1+o.index,endColumn:r+1+t.lastIndex}}return null}Object.defineProperty(t,"__esModule",{value:!0}),t.USUAL_WORD_SEPARATORS="`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?",t.DEFAULT_WORD_REGEXP=function(e){void 0===e&&(e="");for(var n=t.USUAL_WORD_SEPARATORS,r="(-?\\d*\\.\\d\\w*)|([^",i=0;i=0||(r+="\\"+n[i]);return r+="\\s]+)",new RegExp(r,"g")}(),t.ensureValidWordDefinition=function(e){var n=t.DEFAULT_WORD_REGEXP;if(e&&e instanceof RegExp)if(e.global)n=e;else{var r="g";e.ignoreCase&&(r+="i"),e.multiline&&(r+="m"),n=new RegExp(e.source,r)}return n.lastIndex=0,n},t.getWordAtText=function(e,t,i,o){t.lastIndex=0;var s=t.exec(i);if(!s)return null;var u=s[0].indexOf(" ")>=0?r(e,t,i,o):n(e,t,i,o);return t.lastIndex=0,u}}),r(e[28],t([1,0,25,7]),function(e,t,n,r){"use strict";function i(){return null===l&&(l=new a([[1,104,2],[1,72,2],[1,102,6],[1,70,6],[2,116,3],[2,84,3],[3,116,4],[3,84,4],[4,112,5],[4,80,5],[5,115,9],[5,83,9],[5,58,10],[6,105,7],[6,73,7],[7,108,8],[7,76,8],[8,101,9],[8,69,9],[9,58,10],[10,47,11],[11,47,12]])),l}function o(){if(null===c){c=new n.CharacterClassifier(0);for(e=0;e<" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".length;e++)c.set(" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".charCodeAt(e),1);for(var e=0;e<".,;".length;e++)c.set(".,;".charCodeAt(e),2)}return c}Object.defineProperty(t,"__esModule",{value:!0});var s;!function(e){e[e.Invalid=0]="Invalid",e[e.Start=1]="Start",e[e.H=2]="H",e[e.HT=3]="HT",e[e.HTT=4]="HTT",e[e.HTTP=5]="HTTP",e[e.F=6]="F",e[e.FI=7]="FI",e[e.FIL=8]="FIL",e[e.BeforeColon=9]="BeforeColon",e[e.AfterColon=10]="AfterColon",e[e.AlmostThere=11]="AlmostThere",e[e.End=12]="End",e[e.Accept=13]="Accept"}(s||(s={}));var u,a=function(){function e(e){for(var t=0,n=0,i=0,o=e.length;it&&(t=a),u>n&&(n=u),l>n&&(n=l)}t++,n++;for(var c=new r.Uint8Matrix(n,t,0),i=0,o=e.length;i=this._maxCharCode?0:this._states.get(e,t)},e}(),l=null;!function(e){e[e.None=0]="None",e[e.ForceTermination=1]="ForceTermination",e[e.CannotEndIn=2]="CannotEndIn"}(u||(u={}));var c=null,f=function(){function e(){}return e._createLink=function(e,t,n,r,i){var o=i-1;do{var s=t.charCodeAt(o);if(2!==e.get(s))break;o--}while(o>r);return{range:{startLineNumber:n,startColumn:r+1,endLineNumber:n,endColumn:o+2},url:t.substring(r,o+1)}},e.computeLinks=function(t){for(var n=i(),r=o(),s=[],u=1,a=t.getLineCount();u<=a;u++){for(var l=t.getLineContent(u),c=l.length,f=0,h=0,d=0,p=1,m=!1,_=!1,g=!1;f=0?((r+=n?1:-1)<0?r=e.length-1:r%=e.length,e[r]):null},e.INSTANCE=new e,e}();t.BasicInplaceReplace=n}),r(e[30],t([1,0,9,21,3,5,22,2,13,23,6]),function(e,t,n,r,i,o,s,u,a,l,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var f;!function(e){e[e.Ignore=0]="Ignore",e[e.Info=1]="Info",e[e.Warning=2]="Warning",e[e.Error=3]="Error"}(f=t.Severity||(t.Severity={}));var h=function(){function e(){}return e.chord=function(e,t){return r.KeyChord(e,t)},e.CtrlCmd=2048,e.Shift=1024,e.Alt=512,e.WinCtrl=256,e}();t.KeyMod=h;var d;!function(e){e[e.Unknown=0]="Unknown",e[e.Backspace=1]="Backspace",e[e.Tab=2]="Tab",e[e.Enter=3]="Enter",e[e.Shift=4]="Shift",e[e.Ctrl=5]="Ctrl",e[e.Alt=6]="Alt",e[e.PauseBreak=7]="PauseBreak",e[e.CapsLock=8]="CapsLock",e[e.Escape=9]="Escape",e[e.Space=10]="Space",e[e.PageUp=11]="PageUp",e[e.PageDown=12]="PageDown",e[e.End=13]="End",e[e.Home=14]="Home",e[e.LeftArrow=15]="LeftArrow",e[e.UpArrow=16]="UpArrow",e[e.RightArrow=17]="RightArrow",e[e.DownArrow=18]="DownArrow",e[e.Insert=19]="Insert",e[e.Delete=20]="Delete",e[e.KEY_0=21]="KEY_0",e[e.KEY_1=22]="KEY_1",e[e.KEY_2=23]="KEY_2",e[e.KEY_3=24]="KEY_3",e[e.KEY_4=25]="KEY_4",e[e.KEY_5=26]="KEY_5",e[e.KEY_6=27]="KEY_6",e[e.KEY_7=28]="KEY_7",e[e.KEY_8=29]="KEY_8",e[e.KEY_9=30]="KEY_9",e[e.KEY_A=31]="KEY_A",e[e.KEY_B=32]="KEY_B",e[e.KEY_C=33]="KEY_C",e[e.KEY_D=34]="KEY_D",e[e.KEY_E=35]="KEY_E",e[e.KEY_F=36]="KEY_F",e[e.KEY_G=37]="KEY_G",e[e.KEY_H=38]="KEY_H",e[e.KEY_I=39]="KEY_I",e[e.KEY_J=40]="KEY_J",e[e.KEY_K=41]="KEY_K",e[e.KEY_L=42]="KEY_L",e[e.KEY_M=43]="KEY_M",e[e.KEY_N=44]="KEY_N",e[e.KEY_O=45]="KEY_O",e[e.KEY_P=46]="KEY_P",e[e.KEY_Q=47]="KEY_Q",e[e.KEY_R=48]="KEY_R",e[e.KEY_S=49]="KEY_S",e[e.KEY_T=50]="KEY_T",e[e.KEY_U=51]="KEY_U",e[e.KEY_V=52]="KEY_V",e[e.KEY_W=53]="KEY_W",e[e.KEY_X=54]="KEY_X",e[e.KEY_Y=55]="KEY_Y",e[e.KEY_Z=56]="KEY_Z",e[e.Meta=57]="Meta",e[e.ContextMenu=58]="ContextMenu",e[e.F1=59]="F1",e[e.F2=60]="F2",e[e.F3=61]="F3",e[e.F4=62]="F4",e[e.F5=63]="F5",e[e.F6=64]="F6",e[e.F7=65]="F7",e[e.F8=66]="F8",e[e.F9=67]="F9",e[e.F10=68]="F10",e[e.F11=69]="F11",e[e.F12=70]="F12",e[e.F13=71]="F13",e[e.F14=72]="F14",e[e.F15=73]="F15",e[e.F16=74]="F16",e[e.F17=75]="F17",e[e.F18=76]="F18",e[e.F19=77]="F19",e[e.NumLock=78]="NumLock",e[e.ScrollLock=79]="ScrollLock",e[e.US_SEMICOLON=80]="US_SEMICOLON",e[e.US_EQUAL=81]="US_EQUAL",e[e.US_COMMA=82]="US_COMMA",e[e.US_MINUS=83]="US_MINUS",e[e.US_DOT=84]="US_DOT",e[e.US_SLASH=85]="US_SLASH",e[e.US_BACKTICK=86]="US_BACKTICK",e[e.US_OPEN_SQUARE_BRACKET=87]="US_OPEN_SQUARE_BRACKET",e[e.US_BACKSLASH=88]="US_BACKSLASH",e[e.US_CLOSE_SQUARE_BRACKET=89]="US_CLOSE_SQUARE_BRACKET",e[e.US_QUOTE=90]="US_QUOTE",e[e.OEM_8=91]="OEM_8",e[e.OEM_102=92]="OEM_102",e[e.NUMPAD_0=93]="NUMPAD_0",e[e.NUMPAD_1=94]="NUMPAD_1",e[e.NUMPAD_2=95]="NUMPAD_2",e[e.NUMPAD_3=96]="NUMPAD_3",e[e.NUMPAD_4=97]="NUMPAD_4",e[e.NUMPAD_5=98]="NUMPAD_5",e[e.NUMPAD_6=99]="NUMPAD_6",e[e.NUMPAD_7=100]="NUMPAD_7",e[e.NUMPAD_8=101]="NUMPAD_8",e[e.NUMPAD_9=102]="NUMPAD_9",e[e.NUMPAD_MULTIPLY=103]="NUMPAD_MULTIPLY",e[e.NUMPAD_ADD=104]="NUMPAD_ADD",e[e.NUMPAD_SEPARATOR=105]="NUMPAD_SEPARATOR",e[e.NUMPAD_SUBTRACT=106]="NUMPAD_SUBTRACT",e[e.NUMPAD_DECIMAL=107]="NUMPAD_DECIMAL",e[e.NUMPAD_DIVIDE=108]="NUMPAD_DIVIDE",e[e.KEY_IN_COMPOSITION=109]="KEY_IN_COMPOSITION",e[e.ABNT_C1=110]="ABNT_C1",e[e.ABNT_C2=111]="ABNT_C2",e[e.MAX_VALUE=112]="MAX_VALUE"}(d=t.KeyCode||(t.KeyCode={})),t.createMonacoBaseAPI=function(){return{editor:void 0,languages:void 0,CancellationTokenSource:a.CancellationTokenSource,Emitter:n.Emitter,KeyCode:d,KeyMod:h,Position:i.Position,Range:o.Range,Selection:s.Selection,SelectionDirection:s.SelectionDirection,Severity:f,Promise:u.TPromise,Uri:c.default,Token:l.Token}}}),r(e[19],t([1,0,7]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){return function(e,t){this.index=e,this.remainder=t}}();t.PrefixSumIndexOfResult=r;var i=function(){function e(e){this.values=e,this.prefixSum=new Uint32Array(e.length),this.prefixSumValidIndex=new Int32Array(1),this.prefixSumValidIndex[0]=-1}return e.prototype.getCount=function(){return this.values.length},e.prototype.insertValues=function(e,t){e=n.toUint32(e);var r=this.values,i=this.prefixSum,o=t.length;return 0!==o&&(this.values=new Uint32Array(r.length+o),this.values.set(r.subarray(0,e),0),this.values.set(r.subarray(e),e+o),this.values.set(t,e),e-1=0&&this.prefixSum.set(i.subarray(0,this.prefixSumValidIndex[0]+1)),!0)},e.prototype.changeValue=function(e,t){return e=n.toUint32(e),t=n.toUint32(t),this.values[e]!==t&&(this.values[e]=t,e-1=r.length)return!1;var o=r.length-e;return t>=o&&(t=o),0!==t&&(this.values=new Uint32Array(r.length-t),this.values.set(r.subarray(0,e),0),this.values.set(r.subarray(e+t),e),this.prefixSum=new Uint32Array(this.values.length),e-1=0&&this.prefixSum.set(i.subarray(0,this.prefixSumValidIndex[0]+1)),!0)},e.prototype.getTotalValue=function(){return 0===this.values.length?0:this._getAccumulatedValue(this.values.length-1)},e.prototype.getAccumulatedValue=function(e){return e<0?0:(e=n.toUint32(e),this._getAccumulatedValue(e))},e.prototype._getAccumulatedValue=function(e){if(e<=this.prefixSumValidIndex[0])return this.prefixSum[e];var t=this.prefixSumValidIndex[0]+1;0===t&&(this.prefixSum[0]=this.values[0],t++),e>=this.values.length&&(e=this.values.length-1);for(var n=t;n<=e;n++)this.prefixSum[n]=this.prefixSum[n-1]+this.values[n];return this.prefixSumValidIndex[0]=Math.max(this.prefixSumValidIndex[0],e),this.prefixSum[e]},e.prototype.getIndexOf=function(e){e=Math.floor(e),this.getTotalValue();for(var t,n,i,o=0,s=this.values.length-1;o<=s;)if(t=o+(s-o)/2|0,n=this.prefixSum[t],i=n-this.values[t],e=n))break;o=t+1}return new r(t,e-i)},e}();t.PrefixSumComputer=i;var o=function(){function e(e){this._cacheAccumulatedValueStart=0,this._cache=null,this._actual=new i(e),this._bustCache()}return e.prototype._bustCache=function(){this._cacheAccumulatedValueStart=0,this._cache=null},e.prototype.getCount=function(){return this._actual.getCount()},e.prototype.insertValues=function(e,t){this._actual.insertValues(e,t)&&this._bustCache()},e.prototype.changeValue=function(e,t){this._actual.changeValue(e,t)&&this._bustCache()},e.prototype.removeValues=function(e,t){this._actual.removeValues(e,t)&&this._bustCache()},e.prototype.getTotalValue=function(){return this._actual.getTotalValue()},e.prototype.getAccumulatedValue=function(e){return this._actual.getAccumulatedValue(e)},e.prototype.getIndexOf=function(e){if(e=Math.floor(e),null!==this._cache){var t=e-this._cacheAccumulatedValueStart;if(t>=0&&tthis._lines.length)t=this._lines.length,n=this._lines[t-1].length+1,r=!0;else{var i=this._lines[t-1].length+1;n<1?(n=1,r=!0):n>i&&(n=i,r=!0)}return r?{lineNumber:t,column:n}:e},t}(l.MirrorModel),m=function(){function e(){this._foreignModule=null}return e.prototype.computeDiff=function(e,t,n){var i=this._getModel(e),o=this._getModel(t);if(!i||!o)return null;var u=i.getLinesContent(),a=o.getLinesContent(),l=new s.DiffComputer(u,a,{shouldPostProcessCharChanges:!0,shouldIgnoreTrimWhitespace:n,shouldConsiderTrimWhitespaceInEmptyCase:!0,shouldMakePrettyDiff:!0});return r.TPromise.as(l.computeDiff())},e.prototype.computeDirtyDiff=function(e,t,n){var i=this._getModel(e),o=this._getModel(t);if(!i||!o)return null;var u=i.getLinesContent(),a=o.getLinesContent(),l=new s.DiffComputer(u,a,{shouldPostProcessCharChanges:!1,shouldIgnoreTrimWhitespace:n,shouldConsiderTrimWhitespaceInEmptyCase:!1,shouldMakePrettyDiff:!0});return r.TPromise.as(l.computeDiff())},e.prototype.computeMoreMinimalEdits=function(t,n,o){var s=this._getModel(t);if(!s)return r.TPromise.as(n);for(var a,l=[],c=0,f=n;ce._diffLimit)l.push({range:d,text:p});else for(var g=u.stringDiff(_,p,!1),v=s.offsetAt(i.Range.lift(d).getStartPosition()),y=0,b=g;y0;)self.onmessage(i.shift())},0)})},r=!0,i=[];self.onmessage=function(e){r?(r=!1,n(e.data)):i.push(e)}}()}).call(this); //# sourceMappingURL=../../../../min-maps/vs/base/worker/workerMain.js.map ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/bat.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/bat",["require","exports"],function(e,s){"use strict";Object.defineProperty(s,"__esModule",{value:!0}),s.conf={comments:{lineComment:"REM"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],surroundingPairs:[{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}]},s.language={defaultToken:"",ignoreCase:!0,tokenPostfix:".bat",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.square",open:"[",close:"]"}],keywords:/call|defined|echo|errorlevel|exist|for|goto|if|pause|set|shift|start|title|not|pushd|popd/,symbols:/[=>\/\?\s]+)/g,comments:{blockComment:["###","###"],lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",ignoreCase:!0,tokenPostfix:".coffee",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"}],regEx:/\/(?!\/\/)(?:[^\/\\]|\\.)*\/[igm]*/,keywords:["and","or","is","isnt","not","on","yes","@","no","off","true","false","null","this","new","delete","typeof","in","instanceof","return","throw","break","continue","debugger","if","else","switch","for","while","do","try","catch","finally","class","extends","super","undefined","then","unless","until","loop","of","by","when"],symbols:/[=>"}],keywords:["abstract","amp","array","auto","bool","break","case","catch","char","class","const","constexpr","const_cast","continue","cpu","decltype","default","delegate","delete","do","double","dynamic_cast","each","else","enum","event","explicit","export","extern","false","final","finally","float","for","friend","gcnew","generic","goto","if","in","initonly","inline","int","interface","interior_ptr","internal","literal","long","mutable","namespace","new","noexcept","nullptr","__nullptr","operator","override","partial","pascal","pin_ptr","private","property","protected","public","ref","register","reinterpret_cast","restrict","return","safe_cast","sealed","short","signed","sizeof","static","static_assert","static_cast","struct","switch","template","this","thread_local","throw","tile_static","true","try","typedef","typeid","typename","union","unsigned","using","virtual","void","volatile","wchar_t","where","while","_asm","_based","_cdecl","_declspec","_fastcall","_if_exists","_if_not_exists","_inline","_multiple_inheritance","_pascal","_single_inheritance","_stdcall","_virtual_inheritance","_w64","__abstract","__alignof","__asm","__assume","__based","__box","__builtin_alignof","__cdecl","__clrcall","__declspec","__delegate","__event","__except","__fastcall","__finally","__forceinline","__gc","__hook","__identifier","__if_exists","__if_not_exists","__inline","__int128","__int16","__int32","__int64","__int8","__interface","__leave","__m128","__m128d","__m128i","__m256","__m256d","__m256i","__m64","__multiple_inheritance","__newslot","__nogc","__noop","__nounwind","__novtordisp","__pascal","__pin","__pragma","__property","__ptr32","__ptr64","__raise","__restrict","__resume","__sealed","__single_inheritance","__stdcall","__super","__thiscall","__try","__try_cast","__typeof","__unaligned","__unhook","__uuidof","__value","__virtual_inheritance","__w64","__wchar_t"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/,"number.float"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/,"number.hex"],[/0[0-7']*[0-7](@integersuffix)/,"number.octal"],[/0[bB][0-1']*[0-1](@integersuffix)/,"number.binary"],[/\d[\d']*\d(@integersuffix)/,"number"],[/\d(@integersuffix)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@doccomment"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],doccomment:[[/[^\/*]+/,"comment.doc"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/csharp.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/csharp",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"'",close:"'",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}]},t.language={defaultToken:"",tokenPostfix:".cs",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],keywords:["extern","alias","using","bool","decimal","sbyte","byte","short","ushort","int","uint","long","ulong","char","float","double","object","dynamic","string","assembly","is","as","ref","out","this","base","new","typeof","void","checked","unchecked","default","delegate","var","const","if","else","switch","case","while","do","for","foreach","in","break","continue","goto","return","throw","try","catch","finally","lock","yield","from","let","where","join","on","equals","into","orderby","ascending","descending","select","group","by","namespace","partial","class","field","event","method","param","property","public","protected","internal","private","abstract","sealed","static","struct","readonly","volatile","virtual","override","params","get","set","add","remove","operator","true","false","implicit","explicit","interface","enum","null","async","await","fixed","sizeof","stackalloc","unsafe","nameof","when"],namespaceFollows:["namespace","using"],parenFollows:["if","for","while","switch","foreach","using","catch","when"],operators:["=","??","||","&&","|","^","&","==","!=","<=",">=","<<","+","-","*","/","%","!","~","++","--","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=",">>","=>"],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/[0-9_]*\.[0-9_]+([eE][\-+]?\d+)?[fFdD]?/,"number.float"],[/0[xX][0-9a-fA-F_]+/,"number.hex"],[/0[bB][01_]+/,"number.hex"],[/[0-9_]+/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,{token:"string.quote",next:"@string"}],[/\$\@"/,{token:"string.quote",next:"@litinterpstring"}],[/\@"/,{token:"string.quote",next:"@litstring"}],[/\$"/,{token:"string.quote",next:"@interpolatedstring"}],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],qualified:[[/[a-zA-Z_][\w]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":"identifier"}}],[/\./,"delimiter"],["","","@pop"]],namespace:[{include:"@whitespace"},[/[A-Z]\w*/,"namespace"],[/[\.=]/,"delimiter"],["","","@pop"]],comment:[[/[^\/*]+/,"comment"],["\\*/","comment","@pop"],[/[\/*]/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,{token:"string.quote",next:"@pop"}]],litstring:[[/[^"]+/,"string"],[/""/,"string.escape"],[/"/,{token:"string.quote",next:"@pop"}]],litinterpstring:[[/[^"{]+/,"string"],[/""/,"string.escape"],[/{{/,"string.escape"],[/}}/,"string.escape"],[/{/,{token:"string.quote",next:"root.litinterpstring"}],[/"/,{token:"string.quote",next:"@pop"}]],interpolatedstring:[[/[^\\"{]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/{{/,"string.escape"],[/}}/,"string.escape"],[/{/,{token:"string.quote",next:"root.interpolatedstring"}],[/"/,{token:"string.quote",next:"@pop"}]],whitespace:[[/^[ \t\v\f]*#((r)|(load))(?=\s)/,"directive.csx"],[/^[ \t\v\f]*#\w.*$/,"namespace.cpp"],[/[ \t\v\f\r\n]+/,""],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/css.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/css",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(#?-?\d*\.\d\w*%?)|((::|[@#.!:])?[\w-?]+%?)|::|[@#.!:]/g,comments:{blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",tokenPostfix:".css",ws:"[ \t\n\r\f]*",identifier:"-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",brackets:[{open:"{",close:"}",token:"delimiter.bracket"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],tokenizer:{root:[{include:"@selector"}],selector:[{include:"@comments"},{include:"@import"},{include:"@strings"},["[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)",{token:"keyword",next:"@keyframedeclaration"}],["[@](page|content|font-face|-moz-document)",{token:"keyword"}],["[@](charset|namespace)",{token:"keyword",next:"@declarationbody"}],["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@selectorname"},["[\\*]","tag"],["[>\\+,]","delimiter"],["\\[",{token:"delimiter.bracket",next:"@selectorattribute"}],["{",{token:"delimiter.bracket",next:"@selectorbody"}]],selectorbody:[{include:"@comments"},["[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))","attribute.name","@rulevalue"],["}",{token:"delimiter.bracket",next:"@pop"}]],selectorname:[["(\\.|#(?=[^{])|%|(@identifier)|:)+","tag"]],selectorattribute:[{include:"@term"},["]",{token:"delimiter.bracket",next:"@pop"}]],term:[{include:"@comments"},["(url-prefix)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],["(url)(\\()",["attribute.value",{token:"delimiter.parenthesis",next:"@urldeclaration"}]],{include:"@functioninvocation"},{include:"@numbers"},{include:"@name"},["([<>=\\+\\-\\*\\/\\^\\|\\~,])","delimiter"],[",","delimiter"]],rulevalue:[{include:"@comments"},{include:"@strings"},{include:"@term"},["!important","keyword"],[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],warndebug:[["[@](warn|debug)",{token:"keyword",next:"@declarationbody"}]],import:[["[@](import)",{token:"keyword",next:"@declarationbody"}]],urldeclaration:[{include:"@strings"},["[^)\r\n]+","string"],["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],parenthizedterm:[{include:"@term"},["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],declarationbody:[{include:"@term"},[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[/[^*/]+/,"comment"],[/./,"comment"]],name:[["@identifier","attribute.value"]],numbers:[["-?(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"attribute.value.number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","attribute.value.hex"]],units:[["(em|ex|ch|rem|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","attribute.value.unit","@pop"]],keyframedeclaration:[["@identifier","attribute.value"],["{",{token:"delimiter.bracket",switchTo:"@keyframebody"}]],keyframebody:[{include:"@term"},["{",{token:"delimiter.bracket",next:"@selectorbody"}],["}",{token:"delimiter.bracket",next:"@pop"}]],functioninvocation:[["@identifier\\(",{token:"attribute.value",next:"@functionarguments"}]],functionarguments:[["\\$@identifier@ws:","attribute.name"],["[,]","delimiter"],{include:"@term"},["\\)",{token:"attribute.value",next:"@pop"}]],strings:[['~?"',{token:"string",next:"@stringenddoublequote"}],["~?'",{token:"string",next:"@stringendquote"}]],stringenddoublequote:[["\\\\.","string"],['"',{token:"string",next:"@pop"}],[/[^\\"]+/,"string"],[".","string"]],stringendquote:[["\\\\.","string"],["'",{token:"string",next:"@pop"}],[/[^\\']+/,"string"],[".","string"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/dockerfile.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/dockerfile",["require","exports"],function(e,s){"use strict";Object.defineProperty(s,"__esModule",{value:!0}),s.conf={brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},s.language={defaultToken:"",tokenPostfix:".dockerfile",instructions:/FROM|MAINTAINER|RUN|EXPOSE|ENV|ADD|VOLUME|LABEL|USER|WORKDIR|COPY|CMD|STOPSIGNAL|SHELL|ENTRYPOINT/,instructionAfter:/ONBUILD/,variableAfter:/ENV/,variable:/\${?[\w]+}?/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/(@instructionAfter)(\s+)/,["keyword",{token:"",next:"@instructions"}]],["","keyword","@instructions"]],instructions:[[/(@variableAfter)(\s+)([\w]+)/,["keyword","",{token:"variable",next:"@arguments"}]],[/(@instructions)/,"keyword","@arguments"]],arguments:[{include:"@whitespace"},{include:"@strings"},[/(@variable)/,{cases:{"@eos":{token:"variable",next:"@popall"},"@default":"variable"}}],[/\\/,{cases:{"@eos":"","@default":""}}],[/./,{cases:{"@eos":{token:"",next:"@popall"},"@default":""}}]],whitespace:[[/\s+/,{cases:{"@eos":{token:"",next:"@popall"},"@default":""}}]],comment:[[/(^#.*$)/,"comment","@popall"]],strings:[[/'$/,"string","@popall"],[/'/,"string","@stringBody"],[/"$/,"string","@popall"],[/"/,"string","@dblStringBody"]],stringBody:[[/[^\\\$']/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}],[/\\./,"string.escape"],[/'$/,"string","@popall"],[/'/,"string","@pop"],[/(@variable)/,"variable"],[/\\$/,"string"],[/$/,"string","@popall"]],dblStringBody:[[/[^\\\$"]/,{cases:{"@eos":{token:"string",next:"@popall"},"@default":"string"}}],[/\\./,"string.escape"],[/"$/,"string","@popall"],[/"/,"string","@pop"],[/(@variable)/,"variable"],[/\\$/,"string"],[/$/,"string","@popall"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/fsharp.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/fsharp",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={comments:{lineComment:"//",blockComment:["(*","*)"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",tokenPostfix:".fs",keywords:["abstract","and","atomic","as","assert","asr","base","begin","break","checked","component","const","constraint","constructor","continue","class","default","delegate","do","done","downcast","downto","elif","else","end","exception","eager","event","external","extern","false","finally","for","fun","function","fixed","functor","global","if","in","include","inherit","inline","interface","internal","land","lor","lsl","lsr","lxor","lazy","let","match","member","mod","module","mutable","namespace","method","mixin","new","not","null","of","open","or","object","override","private","parallel","process","protected","pure","public","rec","return","static","sealed","struct","sig","then","to","true","tailcall","trait","try","type","upcast","use","val","void","virtual","volatile","when","while","with","yield"],symbols:/[=>\]/,"annotation"],[/^#(if|else|endif)/,"keyword"],[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/@symbols/,"delimiter"],[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/,"number.float"],[/0x[0-9a-fA-F]+LF/,"number.float"],[/0x[0-9a-fA-F]+(@integersuffix)/,"number.hex"],[/0b[0-1]+(@integersuffix)/,"number.bin"],[/\d+(@integersuffix)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"""/,"string",'@string."""'],[/"/,"string",'@string."'],[/\@"/,{token:"string.quote",next:"@litstring"}],[/'[^\\']'B?/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\(\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\*]+/,"comment"],[/\*\)/,"comment","@pop"],[/\*/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/("""|"B?)/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]],litstring:[[/[^"]+/,"string"],[/""/,"string.escape"],[/"/,{token:"string.quote",next:"@pop"}]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/go.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/go",["require","exports"],function(e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.conf={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"`",close:"`",notIn:["string"]},{open:'"',close:'"',notIn:["string"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"`",close:"`"},{open:'"',close:'"'},{open:"'",close:"'"}]},n.language={defaultToken:"",tokenPostfix:".go",keywords:["break","case","chan","const","continue","default","defer","else","fallthrough","for","func","go","goto","if","import","interface","map","package","range","return","select","struct","switch","type","var","bool","true","false","uint8","uint16","uint32","uint64","int8","int16","int32","int64","float32","float64","complex64","complex128","byte","rune","uint","int","uintptr","string","nil"],operators:["+","-","*","/","%","&","|","^","<<",">>","&^","+=","-=","*=","/=","%=","&=","|=","^=","<<=",">>=","&^=","&&","||","<-","++","--","==","<",">","=","!","!=","<=",">=",":=","...","(",")","","]","{","}",",",";",".",":"],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\d+[eE]([\-+]?\d+)?/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F]/,"number.hex"],[/0[0-7']*[0-7]/,"number.octal"],[/0[bB][0-1']*[0-1]/,"number.binary"],[/\d[\d']*/,"number"],[/\d/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/`/,"string","@rawstring"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@doccomment"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],doccomment:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],rawstring:[[/[^\`]/,"string"],[/`/,"string","@pop"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/handlebars.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/handlebars",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n="undefined"==typeof monaco?self.monaco:monaco,a=["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"];t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,comments:{blockComment:["{{!--","--}}"]},brackets:[["\x3c!--","--\x3e"],["<",">"],["{{","}}"],["{","}"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"<",close:">"},{open:'"',close:'"'},{open:"'",close:"'"}],onEnterRules:[{beforeText:new RegExp("<(?!(?:"+a.join("|")+"))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/(\w[\w\d]*)\s*>$/i,action:{indentAction:n.languages.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(?!(?:"+a.join("|")+"))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:n.languages.IndentAction.Indent}}]},t.language={defaultToken:"",tokenPostfix:"",tokenizer:{root:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.root"}],[/)/,["delimiter.html","tag.html","delimiter.html"]],[/(<)(script)/,["delimiter.html",{token:"tag.html",next:"@script"}]],[/(<)(style)/,["delimiter.html",{token:"tag.html",next:"@style"}]],[/(<)([:\w]+)/,["delimiter.html",{token:"tag.html",next:"@otherTag"}]],[/(<\/)(\w+)/,["delimiter.html",{token:"tag.html",next:"@otherTag"}]],[/]+/,"metatag.content.html"],[/>/,"metatag.html","@pop"]],comment:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.comment"}],[/-->/,"comment.html","@pop"],[/[^-]+/,"comment.content.html"],[/./,"comment.content.html"]],otherTag:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.otherTag"}],[/\/?>/,"delimiter.html","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.script"}],[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],scriptAfterType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptAfterType"}],[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.scriptWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInEmbeddedState.scriptEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],style:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.style"}],[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],styleAfterType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleAfterType"}],[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInSimpleState.styleWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/\{\{/,{token:"@rematch",switchTo:"@handlebarsInEmbeddedState.styleEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],handlebarsInSimpleState:[[/\{\{\{?/,"delimiter.handlebars"],[/\}\}\}?/,{token:"delimiter.handlebars",switchTo:"@$S2.$S3"}],{include:"handlebarsRoot"}],handlebarsInEmbeddedState:[[/\{\{\{?/,"delimiter.handlebars"],[/\}\}\}?/,{token:"delimiter.handlebars",switchTo:"@$S2.$S3",nextEmbedded:"$S3"}],{include:"handlebarsRoot"}],handlebarsRoot:[[/[#/][^\s}]+/,"keyword.helper.handlebars"],[/else\b/,"keyword.helper.handlebars"],[/[\s]+/],[/[^}]/,"variable.parameter.handlebars"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/html.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/html",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n="undefined"==typeof monaco?self.monaco:monaco,i=["area","base","br","col","embed","hr","img","input","keygen","link","menuitem","meta","param","source","track","wbr"];t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\$\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\s]+)/g,comments:{blockComment:["\x3c!--","--\x3e"]},brackets:[["\x3c!--","--\x3e"],["<",">"],["{","}"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:'"',close:'"'},{open:"'",close:"'"},{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:"<",close:">"}],onEnterRules:[{beforeText:new RegExp("<(?!(?:"+i.join("|")+"))([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:n.languages.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(?!(?:"+i.join("|")+"))(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:n.languages.IndentAction.Indent}}]},t.language={defaultToken:"",tokenPostfix:".html",ignoreCase:!0,tokenizer:{root:[[/)/,["delimiter","tag","","delimiter"]],[/(<)(script)/,["delimiter",{token:"tag",next:"@script"}]],[/(<)(style)/,["delimiter",{token:"tag",next:"@style"}]],[/(<)((?:[\w\-]+:)?[\w\-]+)/,["delimiter",{token:"tag",next:"@otherTag"}]],[/(<\/)((?:[\w\-]+:)?[\w\-]+)/,["delimiter",{token:"tag",next:"@otherTag"}]],[/]+/,"metatag.content"],[/>/,"metatag","@pop"]],comment:[[/-->/,"comment","@pop"],[/[^-]+/,"comment.content"],[/./,"comment.content"]],otherTag:[[/\/?>/,"delimiter","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],scriptAfterType:[[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@scriptEmbedded",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/>/,{token:"delimiter",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]],style:[[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter","tag",{token:"delimiter",next:"@pop"}]]],styleAfterType:[[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter",next:"@styleEmbedded",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/>/,{token:"delimiter",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}],[/[^<]+/,""]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/ini.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/ini",["require","exports"],function(e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.conf={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},n.language={defaultToken:"",tokenPostfix:".ini",escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/^\[[^\]]*\]/,"metatag"],[/(^\w+)(\s*)(\=)/,["key","","delimiter"]],{include:"@whitespace"},[/\d+/,"number"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/"/,"string",'@string."'],[/'/,"string","@string.'"]],whitespace:[[/[ \t\r\n]+/,""],[/^\s*[#;].*$/,"comment"]],string:[[/[^\\"']+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/["']/,{cases:{"$#==$S2":{token:"string",next:"@pop"},"@default":"string"}}]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/java.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/java",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"<",close:">"}]},t.language={defaultToken:"",tokenPostfix:".java",keywords:["abstract","continue","for","new","switch","assert","default","goto","package","synchronized","boolean","do","if","private","this","break","double","implements","protected","throw","byte","else","import","public","throws","case","enum","instanceof","return","transient","catch","extends","int","short","try","char","final","interface","static","void","class","finally","long","strictfp","volatile","const","float","native","super","while","true","false"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/@\s*[a-zA-Z_\$][\w\$]*/,"annotation"],[/(@digits)[eE]([\-+]?(@digits))?[fFdD]?/,"number.float"],[/(@digits)\.(@digits)([eE][\-+]?(@digits))?[fFdD]?/,"number.float"],[/0[xX](@hexdigits)[Ll]?/,"number.hex"],[/0(@octaldigits)[Ll]?/,"number.octal"],[/0[bB](@binarydigits)[Ll]?/,"number.binary"],[/(@digits)[fFdD]/,"number.float"],[/(@digits)[lL]?/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@javadoc"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],javadoc:[[/[^\/*]+/,"comment.doc"],[/\/\*/,"comment.doc.invalid"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/less.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/less",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(#?-?\d*\.\d\w*%?)|([@#!.:]?[\w-?]+%?)|[@#!.]/g,comments:{blockComment:["/*","*/"],lineComment:"//"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",tokenPostfix:".less",identifier:"-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",identifierPlus:"-?-?([a-zA-Z:.]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-:.]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],tokenizer:{root:[{include:"@nestedJSBegin"},["[ \\t\\r\\n]+",""],{include:"@comments"},{include:"@keyword"},{include:"@strings"},{include:"@numbers"},["[*_]?[a-zA-Z\\-\\s]+(?=:.*(;|(\\\\$)))","attribute.name","@attribute"],["url(\\-prefix)?\\(",{token:"tag",next:"@urldeclaration"}],["[{}()\\[\\]]","@brackets"],["[,:;]","delimiter"],["#@identifierPlus","tag.id"],["&","tag"],["\\.@identifierPlus(?=\\()","tag.class","@attribute"],["\\.@identifierPlus","tag.class"],["@identifierPlus","tag"],{include:"@operators"},["@(@identifier(?=[:,\\)]))","variable","@attribute"],["@(@identifier)","variable"],["@","key","@atRules"]],nestedJSBegin:[["``","delimiter.backtick"],["`",{token:"delimiter.backtick",next:"@nestedJSEnd",nextEmbedded:"text/javascript"}]],nestedJSEnd:[["`",{token:"delimiter.backtick",next:"@pop",nextEmbedded:"@pop"}]],operators:[["[<>=\\+\\-\\*\\/\\^\\|\\~]","operator"]],keyword:[["(@[\\s]*import|![\\s]*important|true|false|when|iscolor|isnumber|isstring|iskeyword|isurl|ispixel|ispercentage|isem|hue|saturation|lightness|alpha|lighten|darken|saturate|desaturate|fadein|fadeout|fade|spin|mix|round|ceil|floor|percentage)\\b","keyword"]],urldeclaration:[{include:"@strings"},["[^)\r\n]+","string"],["\\)",{token:"tag",next:"@pop"}]],attribute:[{include:"@nestedJSBegin"},{include:"@comments"},{include:"@strings"},{include:"@numbers"},{include:"@keyword"},["[a-zA-Z\\-]+(?=\\()","attribute.value","@attribute"],[">","operator","@pop"],["@identifier","attribute.value"],{include:"@operators"},["@(@identifier)","variable"],["[)\\}]","@brackets","@pop"],["[{}()\\[\\]>]","@brackets"],["[;]","delimiter","@pop"],["[,=:]","delimiter"],["\\s",""],[".","attribute.value"]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[".","comment"]],numbers:[["(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"attribute.value.number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","attribute.value.hex"]],units:[["(em|ex|ch|rem|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","attribute.value.unit","@pop"]],strings:[['~?"',{token:"string.delimiter",next:"@stringsEndDoubleQuote"}],["~?'",{token:"string.delimiter",next:"@stringsEndQuote"}]],stringsEndDoubleQuote:[['\\\\"',"string"],['"',{token:"string.delimiter",next:"@popall"}],[".","string"]],stringsEndQuote:[["\\\\'","string"],["'",{token:"string.delimiter",next:"@popall"}],[".","string"]],atRules:[{include:"@comments"},{include:"@strings"},["[()]","delimiter"],["[\\{;]","delimiter","@pop"],[".","key"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/lua.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/lua",["require","exports"],function(e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.conf={comments:{lineComment:"--",blockComment:["--[[","]]"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},n.language={defaultToken:"",tokenPostfix:".lua",keywords:["and","break","do","else","elseif","end","false","for","function","goto","if","in","local","nil","not","or","repeat","return","then","true","until","while"],brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"}],operators:["+","-","*","/","%","^","#","==","~=","<=",">=","<",">","=",";",":",",",".","..","..."],symbols:/[=>",notIn:["string"]}],surroundingPairs:[{open:"(",close:")"},{open:"[",close:"]"},{open:"`",close:"`"}]},t.language={defaultToken:"",tokenPostfix:".md",control:/[\\`*_\[\]{}()#+\-\.!]/,noncontrol:/[^\\`*_\[\]{}()#+\-\.!]/,escapes:/\\(?:@control)/,jsescapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,empty:["area","base","basefont","br","col","frame","hr","img","input","isindex","link","meta","param"],tokenizer:{root:[[/^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/,["white","keyword","keyword","keyword"]],[/^\s*(=+|\-+)\s*$/,"keyword"],[/^\s*((\*[ ]?)+)\s*$/,"meta.separator"],[/^\s*>+/,"comment"],[/^\s*([\*\-+:]|\d+\.)\s/,"keyword"],[/^(\t|[ ]{4})[^ ].*$/,"string"],[/^\s*~~~\s*((?:\w|[\/\-#])+)?\s*$/,{token:"string",next:"@codeblock"}],[/^\s*```\s*((?:\w|[\/\-#])+)\s*$/,{token:"string",next:"@codeblockgh",nextEmbedded:"$1"}],[/^\s*```\s*$/,{token:"string",next:"@codeblock"}],{include:"@linecontent"}],codeblock:[[/^\s*~~~\s*$/,{token:"string",next:"@pop"}],[/^\s*```\s*$/,{token:"string",next:"@pop"}],[/.*$/,"variable.source"]],codeblockgh:[[/```\s*$/,{token:"variable.source",next:"@pop",nextEmbedded:"@pop"}],[/[^`]+/,"variable.source"]],linecontent:[[/&\w+;/,"string.escape"],[/@escapes/,"escape"],[/\b__([^\\_]|@escapes|_(?!_))+__\b/,"strong"],[/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/,"strong"],[/\b_[^_]+_\b/,"emphasis"],[/\*([^\\*]|@escapes)+\*/,"emphasis"],[/`([^\\`]|@escapes)+`/,"variable"],[/\{[^}]+\}/,"string.target"],[/(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/,["string.link","","string.link"]],[/(!?\[)((?:[^\]\\]|@escapes)*)(\])/,"string.link"],{include:"html"}],html:[[/<(\w+)\/>/,"tag"],[/<(\w+)/,{cases:{"@empty":{token:"tag",next:"@tag.$1"},"@default":{token:"tag",next:"@tag.$1"}}}],[/<\/(\w+)\s*>/,{token:"tag"}],[//,"comment","@pop"],[//,"comment.html","@pop"],[/[^-]+/,"comment.content.html"],[/./,"comment.content.html"]],otherTag:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.otherTag"}],[/\/?>/,"delimiter.html","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.script"}],[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],scriptAfterType:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.scriptAfterType"}],[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.scriptAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.scriptWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInEmbeddedState.scriptEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],style:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.style"}],[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],styleAfterType:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.styleAfterType"}],[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.styleAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInSimpleState.styleWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/<\?((php)|=)?/,{token:"@rematch",switchTo:"@phpInEmbeddedState.styleEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],phpInSimpleState:[[/<\?((php)|=)?/,"metatag.php"],[/\?>/,{token:"metatag.php",switchTo:"@$S2.$S3"}],{include:"phpRoot"}],phpInEmbeddedState:[[/<\?((php)|=)?/,"metatag.php"],[/\?>/,{token:"metatag.php",switchTo:"@$S2.$S3",nextEmbedded:"$S3"}],{include:"phpRoot"}],phpRoot:[[/[a-zA-Z_]\w*/,{cases:{"@phpKeywords":{token:"keyword.php"},"@phpCompileTimeConstants":{token:"constant.php"},"@default":"identifier.php"}}],[/[$a-zA-Z_]\w*/,{cases:{"@phpPreDefinedVariables":{token:"variable.predefined.php"},"@default":"variable.php"}}],[/[{}]/,"delimiter.bracket.php"],[/[\[\]]/,"delimiter.array.php"],[/[()]/,"delimiter.parenthesis.php"],[/[ \t\r\n]+/],[/#/,"comment.php","@phpLineComment"],[/\/\//,"comment.php","@phpLineComment"],[/\/\*/,"comment.php","@phpComment"],[/"/,"string.php","@phpDoubleQuoteString"],[/'/,"string.php","@phpSingleQuoteString"],[/[\+\-\*\%\&\|\^\~\!\=\<\>\/\?\;\:\.\,\@]/,"delimiter.php"],[/\d*\d+[eE]([\-+]?\d+)?/,"number.float.php"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float.php"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F]/,"number.hex.php"],[/0[0-7']*[0-7]/,"number.octal.php"],[/0[bB][0-1']*[0-1]/,"number.binary.php"],[/\d[\d']*/,"number.php"],[/\d/,"number.php"]],phpComment:[[/\*\//,"comment.php","@pop"],[/[^*]+/,"comment.php"],[/./,"comment.php"]],phpLineComment:[[/\?>/,{token:"@rematch",next:"@pop"}],[/.$/,"comment.php","@pop"],[/[^?]+$/,"comment.php","@pop"],[/[^?]+/,"comment.php"],[/./,"comment.php"]],phpDoubleQuoteString:[[/[^\\"]+/,"string.php"],[/@escapes/,"string.escape.php"],[/\\./,"string.escape.invalid.php"],[/"/,"string.php","@pop"]],phpSingleQuoteString:[[/[^\\']+/,"string.php"],[/@escapes/,"string.escape.php"],[/\\./,"string.escape.invalid.php"],[/'/,"string.php","@pop"]]},phpKeywords:["abstract","and","array","as","break","callable","case","catch","cfunction","class","clone","const","continue","declare","default","do","else","elseif","enddeclare","endfor","endforeach","endif","endswitch","endwhile","extends","false","final","for","foreach","function","global","goto","if","implements","interface","instanceof","insteadof","namespace","new","null","object","old_function","or","private","protected","public","resource","static","switch","throw","trait","try","true","use","var","while","xor","die","echo","empty","exit","eval","include","include_once","isset","list","require","require_once","return","print","unset","yield","__construct"],phpCompileTimeConstants:["__CLASS__","__DIR__","__FILE__","__LINE__","__NAMESPACE__","__METHOD__","__FUNCTION__","__TRAIT__"],phpPreDefinedVariables:["$GLOBALS","$_SERVER","$_GET","$_POST","$_FILES","$_REQUEST","$_SESSION","$_ENV","$_COOKIE","$php_errormsg","$HTTP_RAW_POST_DATA","$http_response_header","$argc","$argv"],escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/postiats.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/postiats",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={comments:{lineComment:"//",blockComment:["(*","*)"]},brackets:[["{","}"],["[","]"],["(",")"],["<",">"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]}]},t.language={tokenPostfix:".pats",defaultToken:"invalid",keywords:["abstype","abst0ype","absprop","absview","absvtype","absviewtype","absvt0ype","absviewt0ype","as","and","assume","begin","classdec","datasort","datatype","dataprop","dataview","datavtype","dataviewtype","do","end","extern","extype","extvar","exception","fn","fnx","fun","prfn","prfun","praxi","castfn","if","then","else","ifcase","in","infix","infixl","infixr","prefix","postfix","implmnt","implement","primplmnt","primplement","import","let","local","macdef","macrodef","nonfix","symelim","symintr","overload","of","op","rec","sif","scase","sortdef","sta","stacst","stadef","static","staload","dynload","try","tkindef","typedef","propdef","viewdef","vtypedef","viewtypedef","prval","var","prvar","when","where","with","withtype","withprop","withview","withvtype","withviewtype"],keywords_dlr:["$delay","$ldelay","$arrpsz","$arrptrsize","$d2ctype","$effmask","$effmask_ntm","$effmask_exn","$effmask_ref","$effmask_wrt","$effmask_all","$extern","$extkind","$extype","$extype_struct","$extval","$extfcall","$extmcall","$literal","$myfilename","$mylocation","$myfunction","$lst","$lst_t","$lst_vt","$list","$list_t","$list_vt","$rec","$rec_t","$rec_vt","$record","$record_t","$record_vt","$tup","$tup_t","$tup_vt","$tuple","$tuple_t","$tuple_vt","$break","$continue","$raise","$showtype","$vcopyenv_v","$vcopyenv_vt","$tempenver","$solver_assert","$solver_verify"],keywords_srp:["#if","#ifdef","#ifndef","#then","#elif","#elifdef","#elifndef","#else","#endif","#error","#prerr","#print","#assert","#undef","#define","#include","#require","#pragma","#codegen2","#codegen3"],irregular_keyword_list:["val+","val-","val","case+","case-","case","addr@","addr","fold@","free@","fix@","fix","lam@","lam","llam@","llam","viewt@ype+","viewt@ype-","viewt@ype","viewtype+","viewtype-","viewtype","view+","view-","view@","view","type+","type-","type","vtype+","vtype-","vtype","vt@ype+","vt@ype-","vt@ype","viewt@ype+","viewt@ype-","viewt@ype","viewtype+","viewtype-","viewtype","prop+","prop-","prop","type+","type-","type","t@ype","t@ype+","t@ype-","abst@ype","abstype","absviewt@ype","absvt@ype","for*","for","while*","while"],keywords_types:["bool","double","byte","int","short","char","void","unit","long","float","string","strptr"],keywords_effects:["0","fun","clo","prf","funclo","cloptr","cloref","ref","ntm","1"],operators:["@","!","|","`",":","$",".","=","#","~","..","...","=>","=<>","=/=>","=>>","=/=>>","<",">","><",".<",">.",".<>.","->","-<>"],brackets:[{open:",(",close:")",token:"delimiter.parenthesis"},{open:"`(",close:")",token:"delimiter.parenthesis"},{open:"%(",close:")",token:"delimiter.parenthesis"},{open:"'(",close:")",token:"delimiter.parenthesis"},{open:"'{",close:"}",token:"delimiter.parenthesis"},{open:"@(",close:")",token:"delimiter.parenthesis"},{open:"@{",close:"}",token:"delimiter.brace"},{open:"@[",close:"]",token:"delimiter.square"},{open:"#[",close:"]",token:"delimiter.square"},{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],symbols:/[=>]/,digit:/[0-9]/,digitseq0:/@digit*/,xdigit:/[0-9A-Za-z]/,xdigitseq0:/@xdigit*/,INTSP:/[lLuU]/,FLOATSP:/[fFlL]/,fexponent:/[eE][+-]?[0-9]+/,fexponent_bin:/[pP][+-]?[0-9]+/,deciexp:/\.[0-9]*@fexponent?/,hexiexp:/\.[0-9a-zA-Z]*@fexponent_bin?/,irregular_keywords:/val[+-]?|case[+-]?|addr\@?|fold\@|free\@|fix\@?|lam\@?|llam\@?|prop[+-]?|type[+-]?|view[+-@]?|viewt@?ype[+-]?|t@?ype[+-]?|v(iew)?t@?ype[+-]?|abst@?ype|absv(iew)?t@?ype|for\*?|while\*?/,ESCHAR:/[ntvbrfa\\\?'"\(\[\{]/,start:"root",tokenizer:{root:[{regex:/[ \t\r\n]+/,action:{token:""}},{regex:/\(\*\)/,action:{token:"invalid"}},{regex:/\(\*/,action:{token:"comment",next:"lexing_COMMENT_block_ml"}},{regex:/\(/,action:"@brackets"},{regex:/\)/,action:"@brackets"},{regex:/\[/,action:"@brackets"},{regex:/\]/,action:"@brackets"},{regex:/\{/,action:"@brackets"},{regex:/\}/,action:"@brackets"},{regex:/,\(/,action:"@brackets"},{regex:/,/,action:{token:"delimiter.comma"}},{regex:/;/,action:{token:"delimiter.semicolon"}},{regex:/@\(/,action:"@brackets"},{regex:/@\[/,action:"@brackets"},{regex:/@\{/,action:"@brackets"},{regex:/:/,action:{token:"@rematch",next:"@pop"}}],lexing_EXTCODE:[{regex:/^%}/,action:{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}},{regex:/[^%]+/,action:""}],lexing_DQUOTE:[{regex:/"/,action:{token:"string.quote",next:"@pop"}},{regex:/(\{\$)(@IDENTFST@IDENTRST*)(\})/,action:[{token:"string.escape"},{token:"identifier"},{token:"string.escape"}]},{regex:/\\$/,action:{token:"string.escape"}},{regex:/\\(@ESCHAR|[xX]@xdigit+|@digit+)/,action:{token:"string.escape"}},{regex:/[^\\"]+/,action:{token:"string"}}]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/powershell.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/powershell",["require","exports"],function(e,s){"use strict";Object.defineProperty(s,"__esModule",{value:!0}),s.conf={wordPattern:/(-?\d*\.\d\w*)|([^\`\~\!\@\#%\^\&\*\(\)\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g,comments:{lineComment:"#",blockComment:["<#","#>"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"',notIn:["string"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},s.language={defaultToken:"",ignoreCase:!0,tokenPostfix:".ps1",brackets:[{token:"delimiter.curly",open:"{",close:"}"},{token:"delimiter.square",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"}],keywords:["begin","break","catch","class","continue","data","define","do","dynamicparam","else","elseif","end","exit","filter","finally","for","foreach","from","function","if","in","param","process","return","switch","throw","trap","try","until","using","var","while","workflow","parallel","sequence","inlinescript","configuration"],helpKeywords:/SYNOPSIS|DESCRIPTION|PARAMETER|EXAMPLE|INPUTS|OUTPUTS|NOTES|LINK|COMPONENT|ROLE|FUNCTIONALITY|FORWARDHELPTARGETNAME|FORWARDHELPCATEGORY|REMOTEHELPRUNSPACE|EXTERNALHELP/,symbols:/[=>/,"comment","@pop"],[/(\.)(@helpKeywords)(?!\w)/,{token:"comment.keyword.$2"}],[/[\.#]/,"comment"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/pug.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/pug",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={comments:{lineComment:"//"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]},{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]}]},t.language={defaultToken:"",tokenPostfix:".pug",ignoreCase:!0,brackets:[{token:"delimiter.curly",open:"{",close:"}"},{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"}],keywords:["append","block","case","default","doctype","each","else","extends","for","if","in","include","mixin","typeof","unless","var","when"],tags:["a","abbr","acronym","address","area","article","aside","audio","b","base","basefont","bdi","bdo","blockquote","body","br","button","canvas","caption","center","cite","code","col","colgroup","command","datalist","dd","del","details","dfn","div","dl","dt","em","embed","fieldset","figcaption","figure","font","footer","form","frame","frameset","h1","h2","h3","h4","h5","h6","head","header","hgroup","hr","html","i","iframe","img","input","ins","keygen","kbd","label","li","link","map","mark","menu","meta","meter","nav","noframes","noscript","object","ol","optgroup","option","output","p","param","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","source","span","strike","strong","style","sub","summary","sup","table","tbody","td","textarea","tfoot","th","thead","time","title","tr","tracks","tt","u","ul","video","wbr"],symbols:/[\+\-\*\%\&\|\!\=\/\.\,\:]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[[/^(\s*)([a-zA-Z_-][\w-]*)/,{cases:{"$2@tags":{cases:{"@eos":["","tag"],"@default":["",{token:"tag",next:"@tag.$1"}]}},"$2@keywords":["",{token:"keyword.$2"}],"@default":["",""]}}],[/^(\s*)(#[a-zA-Z_-][\w-]*)/,{cases:{"@eos":["","tag.id"],"@default":["",{token:"tag.id",next:"@tag.$1"}]}}],[/^(\s*)(\.[a-zA-Z_-][\w-]*)/,{cases:{"@eos":["","tag.class"],"@default":["",{token:"tag.class",next:"@tag.$1"}]}}],[/^(\s*)(\|.*)$/,""],{include:"@whitespace"},[/[a-zA-Z_$][\w$]*/,{cases:{"@keywords":{token:"keyword.$0"},"@default":""}}],[/[{}()\[\]]/,"@brackets"],[/@symbols/,"delimiter"],[/\d+\.\d+([eE][\-+]?\d+)?/,"number.float"],[/\d+/,"number"],[/"/,"string",'@string."'],[/'/,"string","@string.'"]],tag:[[/(\.)(\s*$)/,[{token:"delimiter",next:"@blockText.$S2."},""]],[/\s+/,{token:"",next:"@simpleText"}],[/#[a-zA-Z_-][\w-]*/,{cases:{"@eos":{token:"tag.id",next:"@pop"},"@default":"tag.id"}}],[/\.[a-zA-Z_-][\w-]*/,{cases:{"@eos":{token:"tag.class",next:"@pop"},"@default":"tag.class"}}],[/\(/,{token:"delimiter.parenthesis",next:"@attributeList"}]],simpleText:[[/[^#]+$/,{token:"",next:"@popall"}],[/[^#]+/,{token:""}],[/(#{)([^}]*)(})/,{cases:{"@eos":["interpolation.delimiter","interpolation",{token:"interpolation.delimiter",next:"@popall"}],"@default":["interpolation.delimiter","interpolation","interpolation.delimiter"]}}],[/#$/,{token:"",next:"@popall"}],[/#/,""]],attributeList:[[/\s+/,""],[/(\w+)(\s*=\s*)("|')/,["attribute.name","delimiter",{token:"attribute.value",next:"@value.$3"}]],[/\w+/,"attribute.name"],[/,/,{cases:{"@eos":{token:"attribute.delimiter",next:"@popall"},"@default":"attribute.delimiter"}}],[/\)$/,{token:"delimiter.parenthesis",next:"@popall"}],[/\)/,{token:"delimiter.parenthesis",next:"@pop"}]],whitespace:[[/^(\s*)(\/\/.*)$/,{token:"comment",next:"@blockText.$1.comment"}],[/[ \t\r\n]+/,""],[//,{token:"comment",next:"@pop"}],[//,"comment.html","@pop"],[/[^-]+/,"comment.content.html"],[/./,"comment.content.html"]],otherTag:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.otherTag"}],[/\/?>/,"delimiter.html","@pop"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/]],script:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.script"}],[/type/,"attribute.name","@scriptAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/(<\/)(script\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],scriptAfterType:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.scriptAfterType"}],[/=/,"delimiter","@scriptAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptAfterTypeEquals:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.scriptAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@scriptWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.text/javascript",nextEmbedded:"text/javascript"}],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptWithCustomType:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.scriptWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@scriptEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/script\s*>/,{token:"@rematch",next:"@pop"}]],scriptEmbedded:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInEmbeddedState.scriptEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/script/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],style:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.style"}],[/type/,"attribute.name","@styleAfterType"],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/(<\/)(style\s*)(>)/,["delimiter.html","tag.html",{token:"delimiter.html",next:"@pop"}]]],styleAfterType:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.styleAfterType"}],[/=/,"delimiter","@styleAfterTypeEquals"],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleAfterTypeEquals:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.styleAfterTypeEquals"}],[/"([^"]*)"/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/'([^']*)'/,{token:"attribute.value",switchTo:"@styleWithCustomType.$1"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.text/css",nextEmbedded:"text/css"}],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleWithCustomType:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInSimpleState.styleWithCustomType.$S2"}],[/>/,{token:"delimiter.html",next:"@styleEmbedded.$S2",nextEmbedded:"$S2"}],[/"([^"]*)"/,"attribute.value"],[/'([^']*)'/,"attribute.value"],[/[\w\-]+/,"attribute.name"],[/=/,"delimiter"],[/[ \t\r\n]+/],[/<\/style\s*>/,{token:"@rematch",next:"@pop"}]],styleEmbedded:[[/@[^@]/,{token:"@rematch",switchTo:"@razorInEmbeddedState.styleEmbedded.$S2",nextEmbedded:"@pop"}],[/<\/style/,{token:"@rematch",next:"@pop",nextEmbedded:"@pop"}]],razorInSimpleState:[[/@\*/,"comment.cs","@razorBlockCommentTopLevel"],[/@[{(]/,"metatag.cs","@razorRootTopLevel"],[/(@)(\s*[\w]+)/,["metatag.cs",{token:"identifier.cs",switchTo:"@$S2.$S3"}]],[/[})]/,{token:"metatag.cs",switchTo:"@$S2.$S3"}],[/\*@/,{token:"comment.cs",switchTo:"@$S2.$S3"}]],razorInEmbeddedState:[[/@\*/,"comment.cs","@razorBlockCommentTopLevel"],[/@[{(]/,"metatag.cs","@razorRootTopLevel"],[/(@)(\s*[\w]+)/,["metatag.cs",{token:"identifier.cs",switchTo:"@$S2.$S3",nextEmbedded:"$S3"}]],[/[})]/,{token:"metatag.cs",switchTo:"@$S2.$S3",nextEmbedded:"$S3"}],[/\*@/,{token:"comment.cs",switchTo:"@$S2.$S3",nextEmbedded:"$S3"}]],razorBlockCommentTopLevel:[[/\*@/,"@rematch","@pop"],[/[^*]+/,"comment.cs"],[/./,"comment.cs"]],razorBlockComment:[[/\*@/,"comment.cs","@pop"],[/[^*]+/,"comment.cs"],[/./,"comment.cs"]],razorRootTopLevel:[[/\{/,"delimiter.bracket.cs","@razorRoot"],[/\(/,"delimiter.parenthesis.cs","@razorRoot"],[/[})]/,"@rematch","@pop"],{include:"razorCommon"}],razorRoot:[[/\{/,"delimiter.bracket.cs","@razorRoot"],[/\(/,"delimiter.parenthesis.cs","@razorRoot"],[/\}/,"delimiter.bracket.cs","@pop"],[/\)/,"delimiter.parenthesis.cs","@pop"],{include:"razorCommon"}],razorCommon:[[/[a-zA-Z_]\w*/,{cases:{"@razorKeywords":{token:"keyword.cs"},"@default":"identifier.cs"}}],[/[\[\]]/,"delimiter.array.cs"],[/[ \t\r\n]+/],[/\/\/.*$/,"comment.cs"],[/@\*/,"comment.cs","@razorBlockComment"],[/"([^"]*)"/,"string.cs"],[/'([^']*)'/,"string.cs"],[/(<)(\w+)(\/>)/,["delimiter.html","tag.html","delimiter.html"]],[/(<)(\w+)(>)/,["delimiter.html","tag.html","delimiter.html"]],[/(<\/)(\w+)(>)/,["delimiter.html","tag.html","delimiter.html"]],[/[\+\-\*\%\&\|\^\~\!\=\<\>\/\?\;\:\.\,]/,"delimiter.cs"],[/\d*\d+[eE]([\-+]?\d+)?/,"number.float.cs"],[/\d*\.\d+([eE][\-+]?\d+)?/,"number.float.cs"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F]/,"number.hex.cs"],[/0[0-7']*[0-7]/,"number.octal.cs"],[/0[bB][0-1']*[0-1]/,"number.binary.cs"],[/\d[\d']*/,"number.cs"],[/\d/,"number.cs"]]},razorKeywords:["abstract","as","async","await","base","bool","break","by","byte","case","catch","char","checked","class","const","continue","decimal","default","delegate","do","double","descending","explicit","event","extern","else","enum","false","finally","fixed","float","for","foreach","from","goto","group","if","implicit","in","int","interface","internal","into","is","lock","long","nameof","new","null","namespace","object","operator","out","override","orderby","params","private","protected","public","readonly","ref","return","switch","struct","sbyte","sealed","short","sizeof","stackalloc","static","string","select","this","throw","true","try","typeof","uint","ulong","unchecked","unsafe","ushort","using","var","virtual","volatile","void","when","while","where","yield","model","inject"],escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/ruby.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/ruby",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={comments:{lineComment:"#",blockComment:["=begin","=end"]},brackets:[["(",")"],["{","}"],["[","]"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={tokenPostfix:".ruby",keywords:["__LINE__","__ENCODING__","__FILE__","BEGIN","END","alias","and","begin","break","case","class","def","defined?","do","else","elsif","end","ensure","for","false","if","in","module","next","nil","not","or","redo","rescue","retry","return","self","super","then","true","undef","unless","until","when","while","yield"],keywordops:["::","..","...","?",":","=>"],builtins:["require","public","private","include","extend","attr_reader","protected","private_class_method","protected_class_method","new"],declarations:["module","class","def","case","do","begin","for","if","while","until","unless"],linedecls:["def","case","do","begin","for","if","while","until","unless"],operators:["^","&","|","<=>","==","===","!~","=~",">",">=","<","<=","<<",">>","+","-","*","/","%","**","~","+@","-@","[]","[]=","`","+=","-=","*=","**=","/=","^=","%=","<<=",">>=","&=","&&=","||=","|="],brackets:[{open:"(",close:")",token:"delimiter.parenthesis"},{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"}],symbols:/[=>"}],[/%([qws])(@delim)/,{token:"string.$1.delim",switchTo:"@qstring.$1.$2.$2"}],[/%r\(/,{token:"regexp.delim",switchTo:"@pregexp.(.)"}],[/%r\[/,{token:"regexp.delim",switchTo:"@pregexp.[.]"}],[/%r\{/,{token:"regexp.delim",switchTo:"@pregexp.{.}"}],[/%r"}],[/%r(@delim)/,{token:"regexp.delim",switchTo:"@pregexp.$1.$1"}],[/%(x|W|Q?)\(/,{token:"string.$1.delim",switchTo:"@qqstring.$1.(.)"}],[/%(x|W|Q?)\[/,{token:"string.$1.delim",switchTo:"@qqstring.$1.[.]"}],[/%(x|W|Q?)\{/,{token:"string.$1.delim",switchTo:"@qqstring.$1.{.}"}],[/%(x|W|Q?)"}],[/%(x|W|Q?)(@delim)/,{token:"string.$1.delim",switchTo:"@qqstring.$1.$2.$2"}],[/%([rqwsxW]|Q?)./,{token:"invalid",next:"@pop"}],[/./,{token:"invalid",next:"@pop"}]],qstring:[[/\\$/,"string.$S2.escape"],[/\\./,"string.$S2.escape"],[/./,{cases:{"$#==$S4":{token:"string.$S2.delim",next:"@pop"},"$#==$S3":{token:"string.$S2.delim",next:"@push"},"@default":"string.$S2"}}]],qqstring:[[/#/,"string.$S2.escape","@interpolated"],{include:"@qstring"}],whitespace:[[/[ \t\r\n]+/,""],[/^\s*=begin\b/,"comment","@comment"],[/#.*$/,"comment"]],comment:[[/[^=]+/,"comment"],[/^\s*=begin\b/,"comment.invalid"],[/^\s*=end\b.*/,"comment","@pop"],[/[=]/,"comment"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/sb.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/sb",["require","exports"],function(e,o){"use strict";Object.defineProperty(o,"__esModule",{value:!0}),o.conf={comments:{lineComment:"'"},brackets:[["(",")"],["[","]"],["If","EndIf"],["While","EndWhile"],["For","EndFor"],["Sub","EndSub"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]}]},o.language={defaultToken:"",tokenPostfix:".sb",ignoreCase:!0,brackets:[{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"keyword.tag-if",open:"If",close:"EndIf"},{token:"keyword.tag-while",open:"While",close:"EndWhile"},{token:"keyword.tag-for",open:"For",close:"EndFor"},{token:"keyword.tag-sub",open:"Sub",close:"EndSub"}],keywords:["Else","ElseIf","EndFor","EndIf","EndSub","EndWhile","For","Goto","If","Step","Sub","Then","To","While"],tagwords:["If","Sub","While","For"],operators:[">","<","<>","<=",">=","And","Or","+","-","*","/","="],identifier:/[a-zA-Z_][\w]*/,symbols:/[=><:+\-*\/%\.,]+/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[{include:"@whitespace"},[/(@identifier)(?=[.])/,"type"],[/@identifier/,{cases:{"@keywords":{token:"keyword.$0"},"@operators":"operator","@default":"variable.name"}}],[/([.])(@identifier)/,{cases:{$2:["delimiter","type.member"],"@default":""}}],[/\d*\.\d+/,"number.float"],[/\d+/,"number"],[/[()\[\]]/,"@brackets"],[/@symbols/,{cases:{"@operators":"operator","@default":"delimiter"}}],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"]],whitespace:[[/[ \t\r\n]+/,""],[/(\').*$/,"comment"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"C?/,"string","@pop"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/scss.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/scss",["require","exports"],function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.conf={wordPattern:/(#?-?\d*\.\d\w*%?)|([@$#!.:]?[\w-?]+%?)|[@#!.]/g,comments:{blockComment:["/*","*/"],lineComment:"//"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"'",close:"'",notIn:["string","comment"]}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},t.language={defaultToken:"",tokenPostfix:".scss",ws:"[ \t\n\r\f]*",identifier:"-?-?([a-zA-Z]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))([\\w\\-]|(\\\\(([0-9a-fA-F]{1,6}\\s?)|[^[0-9a-fA-F])))*",brackets:[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.bracket"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}],tokenizer:{root:[{include:"@selector"}],selector:[{include:"@comments"},{include:"@import"},{include:"@variabledeclaration"},{include:"@warndebug"},["[@](include)",{token:"keyword",next:"@includedeclaration"}],["[@](keyframes|-webkit-keyframes|-moz-keyframes|-o-keyframes)",{token:"keyword",next:"@keyframedeclaration"}],["[@](page|content|font-face|-moz-document)",{token:"keyword"}],["[@](charset|namespace)",{token:"keyword",next:"@declarationbody"}],["[@](function)",{token:"keyword",next:"@functiondeclaration"}],["[@](mixin)",{token:"keyword",next:"@mixindeclaration"}],["url(\\-prefix)?\\(",{token:"meta",next:"@urldeclaration"}],{include:"@controlstatement"},{include:"@selectorname"},["[&\\*]","tag"],["[>\\+,]","delimiter"],["\\[",{token:"delimiter.bracket",next:"@selectorattribute"}],["{",{token:"delimiter.curly",next:"@selectorbody"}]],selectorbody:[["[*_]?@identifier@ws:(?=(\\s|\\d|[^{;}]*[;}]))","attribute.name","@rulevalue"],{include:"@selector"},["[@](extend)",{token:"keyword",next:"@extendbody"}],["[@](return)",{token:"keyword",next:"@declarationbody"}],["}",{token:"delimiter.curly",next:"@pop"}]],selectorname:[["#{",{token:"meta",next:"@variableinterpolation"}],["(\\.|#(?=[^{])|%|(@identifier)|:)+","tag"]],selectorattribute:[{include:"@term"},["]",{token:"delimiter.bracket",next:"@pop"}]],term:[{include:"@comments"},["url(\\-prefix)?\\(",{token:"meta",next:"@urldeclaration"}],{include:"@functioninvocation"},{include:"@numbers"},{include:"@strings"},{include:"@variablereference"},["(and\\b|or\\b|not\\b)","operator"],{include:"@name"},["([<>=\\+\\-\\*\\/\\^\\|\\~,])","operator"],[",","delimiter"],["!default","literal"],["\\(",{token:"delimiter.parenthesis",next:"@parenthizedterm"}]],rulevalue:[{include:"@term"},["!important","literal"],[";","delimiter","@pop"],["{",{token:"delimiter.curly",switchTo:"@nestedproperty"}],["(?=})",{token:"",next:"@pop"}]],nestedproperty:[["[*_]?@identifier@ws:","attribute.name","@rulevalue"],{include:"@comments"},["}",{token:"delimiter.curly",next:"@pop"}]],warndebug:[["[@](warn|debug)",{token:"keyword",next:"@declarationbody"}]],import:[["[@](import)",{token:"keyword",next:"@declarationbody"}]],variabledeclaration:[["\\$@identifier@ws:","variable.decl","@declarationbody"]],urldeclaration:[{include:"@strings"},["[^)\r\n]+","string"],["\\)",{token:"meta",next:"@pop"}]],parenthizedterm:[{include:"@term"},["\\)",{token:"delimiter.parenthesis",next:"@pop"}]],declarationbody:[{include:"@term"},[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],extendbody:[{include:"@selectorname"},["!optional","literal"],[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}]],variablereference:[["\\$@identifier","variable.ref"],["\\.\\.\\.","operator"],["#{",{token:"meta",next:"@variableinterpolation"}]],variableinterpolation:[{include:"@variablereference"},["}",{token:"meta",next:"@pop"}]],comments:[["\\/\\*","comment","@comment"],["\\/\\/+.*","comment"]],comment:[["\\*\\/","comment","@pop"],[".","comment"]],name:[["@identifier","attribute.value"]],numbers:[["(\\d*\\.)?\\d+([eE][\\-+]?\\d+)?",{token:"number",next:"@units"}],["#[0-9a-fA-F_]+(?!\\w)","number.hex"]],units:[["(em|ex|ch|rem|vmin|vmax|vw|vh|vm|cm|mm|in|px|pt|pc|deg|grad|rad|turn|s|ms|Hz|kHz|%)?","number","@pop"]],functiondeclaration:[["@identifier@ws\\(",{token:"meta",next:"@parameterdeclaration"}],["{",{token:"delimiter.curly",switchTo:"@functionbody"}]],mixindeclaration:[["@identifier@ws\\(",{token:"meta",next:"@parameterdeclaration"}],["@identifier","meta"],["{",{token:"delimiter.curly",switchTo:"@selectorbody"}]],parameterdeclaration:[["\\$@identifier@ws:","variable.decl"],["\\.\\.\\.","operator"],[",","delimiter"],{include:"@term"},["\\)",{token:"meta",next:"@pop"}]],includedeclaration:[{include:"@functioninvocation"},["@identifier","meta"],[";","delimiter","@pop"],["(?=})",{token:"",next:"@pop"}],["{",{token:"delimiter.curly",switchTo:"@selectorbody"}]],keyframedeclaration:[["@identifier","meta"],["{",{token:"delimiter.curly",switchTo:"@keyframebody"}]],keyframebody:[{include:"@term"},["{",{token:"delimiter.curly",next:"@selectorbody"}],["}",{token:"delimiter.curly",next:"@pop"}]],controlstatement:[["[@](if|else|for|while|each|media)",{token:"keyword.flow",next:"@controlstatementdeclaration"}]],controlstatementdeclaration:[["(in|from|through|if|to)\\b",{token:"keyword.flow"}],{include:"@term"},["{",{token:"delimiter.curly",switchTo:"@selectorbody"}]],functionbody:[["[@](return)",{token:"keyword"}],{include:"@variabledeclaration"},{include:"@term"},{include:"@controlstatement"},[";","delimiter"],["}",{token:"delimiter.curly",next:"@pop"}]],functioninvocation:[["@identifier\\(",{token:"meta",next:"@functionarguments"}]],functionarguments:[["\\$@identifier@ws:","attribute.name"],["[,]","delimiter"],{include:"@term"},["\\)",{token:"meta",next:"@pop"}]],strings:[['~?"',{token:"string.delimiter",next:"@stringenddoublequote"}],["~?'",{token:"string.delimiter",next:"@stringendquote"}]],stringenddoublequote:[["\\\\.","string"],['"',{token:"string.delimiter",next:"@pop"}],[".","string"]],stringendquote:[["\\\\.","string"],["'",{token:"string.delimiter",next:"@pop"}],[".","string"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/solidity.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/solidity",["require","exports"],function(x,e){"use strict";Object.defineProperty(e,"__esModule",{value:!0}),e.conf={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"],["<",">"]],autoClosingPairs:[{open:'"',close:'"',notIn:["string","comment"]},{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]}]},e.language={defaultToken:"",tokenPostfix:".sol",brackets:[{token:"delimiter.curly",open:"{",close:"}"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.square",open:"[",close:"]"},{token:"delimiter.angle",open:"<",close:">"}],keywords:["pragma","solidity","contract","library","using","struct","function","modifier","address","string","bool","Int","Uint","Byte","Fixed","Ufixed","int","int8","int16","int24","int32","int40","int48","int56","int64","int72","int80","int88","int96","int104","int112","int120","int128","int136","int144","int152","int160","int168","int176","int184","int192","int200","int208","int216","int224","int232","int240","int248","int256","uint","uint8","uint16","uint24","uint32","uint40","uint48","uint56","uint64","uint72","uint80","uint88","uint96","uint104","uint112","uint120","uint128","uint136","uint144","uint152","uint160","uint168","uint176","uint184","uint192","uint200","uint208","uint216","uint224","uint232","uint240","uint248","uint256","byte","bytes","bytes1","bytes2","bytes3","bytes4","bytes5","bytes6","bytes7","bytes8","bytes9","bytes10","bytes11","bytes12","bytes13","bytes14","bytes15","bytes16","bytes17","bytes18","bytes19","bytes20","bytes21","bytes22","bytes23","bytes24","bytes25","bytes26","bytes27","bytes28","bytes29","bytes30","bytes31","bytes32","fixed","fixed0x8","fixed0x16","fixed0x24","fixed0x32","fixed0x40","fixed0x48","fixed0x56","fixed0x64","fixed0x72","fixed0x80","fixed0x88","fixed0x96","fixed0x104","fixed0x112","fixed0x120","fixed0x128","fixed0x136","fixed0x144","fixed0x152","fixed0x160","fixed0x168","fixed0x176","fixed0x184","fixed0x192","fixed0x200","fixed0x208","fixed0x216","fixed0x224","fixed0x232","fixed0x240","fixed0x248","fixed0x256","fixed8x8","fixed8x16","fixed8x24","fixed8x32","fixed8x40","fixed8x48","fixed8x56","fixed8x64","fixed8x72","fixed8x80","fixed8x88","fixed8x96","fixed8x104","fixed8x112","fixed8x120","fixed8x128","fixed8x136","fixed8x144","fixed8x152","fixed8x160","fixed8x168","fixed8x176","fixed8x184","fixed8x192","fixed8x200","fixed8x208","fixed8x216","fixed8x224","fixed8x232","fixed8x240","fixed8x248","fixed16x8","fixed16x16","fixed16x24","fixed16x32","fixed16x40","fixed16x48","fixed16x56","fixed16x64","fixed16x72","fixed16x80","fixed16x88","fixed16x96","fixed16x104","fixed16x112","fixed16x120","fixed16x128","fixed16x136","fixed16x144","fixed16x152","fixed16x160","fixed16x168","fixed16x176","fixed16x184","fixed16x192","fixed16x200","fixed16x208","fixed16x216","fixed16x224","fixed16x232","fixed16x240","fixed24x8","fixed24x16","fixed24x24","fixed24x32","fixed24x40","fixed24x48","fixed24x56","fixed24x64","fixed24x72","fixed24x80","fixed24x88","fixed24x96","fixed24x104","fixed24x112","fixed24x120","fixed24x128","fixed24x136","fixed24x144","fixed24x152","fixed24x160","fixed24x168","fixed24x176","fixed24x184","fixed24x192","fixed24x200","fixed24x208","fixed24x216","fixed24x224","fixed24x232","fixed32x8","fixed32x16","fixed32x24","fixed32x32","fixed32x40","fixed32x48","fixed32x56","fixed32x64","fixed32x72","fixed32x80","fixed32x88","fixed32x96","fixed32x104","fixed32x112","fixed32x120","fixed32x128","fixed32x136","fixed32x144","fixed32x152","fixed32x160","fixed32x168","fixed32x176","fixed32x184","fixed32x192","fixed32x200","fixed32x208","fixed32x216","fixed32x224","fixed40x8","fixed40x16","fixed40x24","fixed40x32","fixed40x40","fixed40x48","fixed40x56","fixed40x64","fixed40x72","fixed40x80","fixed40x88","fixed40x96","fixed40x104","fixed40x112","fixed40x120","fixed40x128","fixed40x136","fixed40x144","fixed40x152","fixed40x160","fixed40x168","fixed40x176","fixed40x184","fixed40x192","fixed40x200","fixed40x208","fixed40x216","fixed48x8","fixed48x16","fixed48x24","fixed48x32","fixed48x40","fixed48x48","fixed48x56","fixed48x64","fixed48x72","fixed48x80","fixed48x88","fixed48x96","fixed48x104","fixed48x112","fixed48x120","fixed48x128","fixed48x136","fixed48x144","fixed48x152","fixed48x160","fixed48x168","fixed48x176","fixed48x184","fixed48x192","fixed48x200","fixed48x208","fixed56x8","fixed56x16","fixed56x24","fixed56x32","fixed56x40","fixed56x48","fixed56x56","fixed56x64","fixed56x72","fixed56x80","fixed56x88","fixed56x96","fixed56x104","fixed56x112","fixed56x120","fixed56x128","fixed56x136","fixed56x144","fixed56x152","fixed56x160","fixed56x168","fixed56x176","fixed56x184","fixed56x192","fixed56x200","fixed64x8","fixed64x16","fixed64x24","fixed64x32","fixed64x40","fixed64x48","fixed64x56","fixed64x64","fixed64x72","fixed64x80","fixed64x88","fixed64x96","fixed64x104","fixed64x112","fixed64x120","fixed64x128","fixed64x136","fixed64x144","fixed64x152","fixed64x160","fixed64x168","fixed64x176","fixed64x184","fixed64x192","fixed72x8","fixed72x16","fixed72x24","fixed72x32","fixed72x40","fixed72x48","fixed72x56","fixed72x64","fixed72x72","fixed72x80","fixed72x88","fixed72x96","fixed72x104","fixed72x112","fixed72x120","fixed72x128","fixed72x136","fixed72x144","fixed72x152","fixed72x160","fixed72x168","fixed72x176","fixed72x184","fixed80x8","fixed80x16","fixed80x24","fixed80x32","fixed80x40","fixed80x48","fixed80x56","fixed80x64","fixed80x72","fixed80x80","fixed80x88","fixed80x96","fixed80x104","fixed80x112","fixed80x120","fixed80x128","fixed80x136","fixed80x144","fixed80x152","fixed80x160","fixed80x168","fixed80x176","fixed88x8","fixed88x16","fixed88x24","fixed88x32","fixed88x40","fixed88x48","fixed88x56","fixed88x64","fixed88x72","fixed88x80","fixed88x88","fixed88x96","fixed88x104","fixed88x112","fixed88x120","fixed88x128","fixed88x136","fixed88x144","fixed88x152","fixed88x160","fixed88x168","fixed96x8","fixed96x16","fixed96x24","fixed96x32","fixed96x40","fixed96x48","fixed96x56","fixed96x64","fixed96x72","fixed96x80","fixed96x88","fixed96x96","fixed96x104","fixed96x112","fixed96x120","fixed96x128","fixed96x136","fixed96x144","fixed96x152","fixed96x160","fixed104x8","fixed104x16","fixed104x24","fixed104x32","fixed104x40","fixed104x48","fixed104x56","fixed104x64","fixed104x72","fixed104x80","fixed104x88","fixed104x96","fixed104x104","fixed104x112","fixed104x120","fixed104x128","fixed104x136","fixed104x144","fixed104x152","fixed112x8","fixed112x16","fixed112x24","fixed112x32","fixed112x40","fixed112x48","fixed112x56","fixed112x64","fixed112x72","fixed112x80","fixed112x88","fixed112x96","fixed112x104","fixed112x112","fixed112x120","fixed112x128","fixed112x136","fixed112x144","fixed120x8","fixed120x16","fixed120x24","fixed120x32","fixed120x40","fixed120x48","fixed120x56","fixed120x64","fixed120x72","fixed120x80","fixed120x88","fixed120x96","fixed120x104","fixed120x112","fixed120x120","fixed120x128","fixed120x136","fixed128x8","fixed128x16","fixed128x24","fixed128x32","fixed128x40","fixed128x48","fixed128x56","fixed128x64","fixed128x72","fixed128x80","fixed128x88","fixed128x96","fixed128x104","fixed128x112","fixed128x120","fixed128x128","fixed136x8","fixed136x16","fixed136x24","fixed136x32","fixed136x40","fixed136x48","fixed136x56","fixed136x64","fixed136x72","fixed136x80","fixed136x88","fixed136x96","fixed136x104","fixed136x112","fixed136x120","fixed144x8","fixed144x16","fixed144x24","fixed144x32","fixed144x40","fixed144x48","fixed144x56","fixed144x64","fixed144x72","fixed144x80","fixed144x88","fixed144x96","fixed144x104","fixed144x112","fixed152x8","fixed152x16","fixed152x24","fixed152x32","fixed152x40","fixed152x48","fixed152x56","fixed152x64","fixed152x72","fixed152x80","fixed152x88","fixed152x96","fixed152x104","fixed160x8","fixed160x16","fixed160x24","fixed160x32","fixed160x40","fixed160x48","fixed160x56","fixed160x64","fixed160x72","fixed160x80","fixed160x88","fixed160x96","fixed168x8","fixed168x16","fixed168x24","fixed168x32","fixed168x40","fixed168x48","fixed168x56","fixed168x64","fixed168x72","fixed168x80","fixed168x88","fixed176x8","fixed176x16","fixed176x24","fixed176x32","fixed176x40","fixed176x48","fixed176x56","fixed176x64","fixed176x72","fixed176x80","fixed184x8","fixed184x16","fixed184x24","fixed184x32","fixed184x40","fixed184x48","fixed184x56","fixed184x64","fixed184x72","fixed192x8","fixed192x16","fixed192x24","fixed192x32","fixed192x40","fixed192x48","fixed192x56","fixed192x64","fixed200x8","fixed200x16","fixed200x24","fixed200x32","fixed200x40","fixed200x48","fixed200x56","fixed208x8","fixed208x16","fixed208x24","fixed208x32","fixed208x40","fixed208x48","fixed216x8","fixed216x16","fixed216x24","fixed216x32","fixed216x40","fixed224x8","fixed224x16","fixed224x24","fixed224x32","fixed232x8","fixed232x16","fixed232x24","fixed240x8","fixed240x16","fixed248x8","ufixed","ufixed0x8","ufixed0x16","ufixed0x24","ufixed0x32","ufixed0x40","ufixed0x48","ufixed0x56","ufixed0x64","ufixed0x72","ufixed0x80","ufixed0x88","ufixed0x96","ufixed0x104","ufixed0x112","ufixed0x120","ufixed0x128","ufixed0x136","ufixed0x144","ufixed0x152","ufixed0x160","ufixed0x168","ufixed0x176","ufixed0x184","ufixed0x192","ufixed0x200","ufixed0x208","ufixed0x216","ufixed0x224","ufixed0x232","ufixed0x240","ufixed0x248","ufixed0x256","ufixed8x8","ufixed8x16","ufixed8x24","ufixed8x32","ufixed8x40","ufixed8x48","ufixed8x56","ufixed8x64","ufixed8x72","ufixed8x80","ufixed8x88","ufixed8x96","ufixed8x104","ufixed8x112","ufixed8x120","ufixed8x128","ufixed8x136","ufixed8x144","ufixed8x152","ufixed8x160","ufixed8x168","ufixed8x176","ufixed8x184","ufixed8x192","ufixed8x200","ufixed8x208","ufixed8x216","ufixed8x224","ufixed8x232","ufixed8x240","ufixed8x248","ufixed16x8","ufixed16x16","ufixed16x24","ufixed16x32","ufixed16x40","ufixed16x48","ufixed16x56","ufixed16x64","ufixed16x72","ufixed16x80","ufixed16x88","ufixed16x96","ufixed16x104","ufixed16x112","ufixed16x120","ufixed16x128","ufixed16x136","ufixed16x144","ufixed16x152","ufixed16x160","ufixed16x168","ufixed16x176","ufixed16x184","ufixed16x192","ufixed16x200","ufixed16x208","ufixed16x216","ufixed16x224","ufixed16x232","ufixed16x240","ufixed24x8","ufixed24x16","ufixed24x24","ufixed24x32","ufixed24x40","ufixed24x48","ufixed24x56","ufixed24x64","ufixed24x72","ufixed24x80","ufixed24x88","ufixed24x96","ufixed24x104","ufixed24x112","ufixed24x120","ufixed24x128","ufixed24x136","ufixed24x144","ufixed24x152","ufixed24x160","ufixed24x168","ufixed24x176","ufixed24x184","ufixed24x192","ufixed24x200","ufixed24x208","ufixed24x216","ufixed24x224","ufixed24x232","ufixed32x8","ufixed32x16","ufixed32x24","ufixed32x32","ufixed32x40","ufixed32x48","ufixed32x56","ufixed32x64","ufixed32x72","ufixed32x80","ufixed32x88","ufixed32x96","ufixed32x104","ufixed32x112","ufixed32x120","ufixed32x128","ufixed32x136","ufixed32x144","ufixed32x152","ufixed32x160","ufixed32x168","ufixed32x176","ufixed32x184","ufixed32x192","ufixed32x200","ufixed32x208","ufixed32x216","ufixed32x224","ufixed40x8","ufixed40x16","ufixed40x24","ufixed40x32","ufixed40x40","ufixed40x48","ufixed40x56","ufixed40x64","ufixed40x72","ufixed40x80","ufixed40x88","ufixed40x96","ufixed40x104","ufixed40x112","ufixed40x120","ufixed40x128","ufixed40x136","ufixed40x144","ufixed40x152","ufixed40x160","ufixed40x168","ufixed40x176","ufixed40x184","ufixed40x192","ufixed40x200","ufixed40x208","ufixed40x216","ufixed48x8","ufixed48x16","ufixed48x24","ufixed48x32","ufixed48x40","ufixed48x48","ufixed48x56","ufixed48x64","ufixed48x72","ufixed48x80","ufixed48x88","ufixed48x96","ufixed48x104","ufixed48x112","ufixed48x120","ufixed48x128","ufixed48x136","ufixed48x144","ufixed48x152","ufixed48x160","ufixed48x168","ufixed48x176","ufixed48x184","ufixed48x192","ufixed48x200","ufixed48x208","ufixed56x8","ufixed56x16","ufixed56x24","ufixed56x32","ufixed56x40","ufixed56x48","ufixed56x56","ufixed56x64","ufixed56x72","ufixed56x80","ufixed56x88","ufixed56x96","ufixed56x104","ufixed56x112","ufixed56x120","ufixed56x128","ufixed56x136","ufixed56x144","ufixed56x152","ufixed56x160","ufixed56x168","ufixed56x176","ufixed56x184","ufixed56x192","ufixed56x200","ufixed64x8","ufixed64x16","ufixed64x24","ufixed64x32","ufixed64x40","ufixed64x48","ufixed64x56","ufixed64x64","ufixed64x72","ufixed64x80","ufixed64x88","ufixed64x96","ufixed64x104","ufixed64x112","ufixed64x120","ufixed64x128","ufixed64x136","ufixed64x144","ufixed64x152","ufixed64x160","ufixed64x168","ufixed64x176","ufixed64x184","ufixed64x192","ufixed72x8","ufixed72x16","ufixed72x24","ufixed72x32","ufixed72x40","ufixed72x48","ufixed72x56","ufixed72x64","ufixed72x72","ufixed72x80","ufixed72x88","ufixed72x96","ufixed72x104","ufixed72x112","ufixed72x120","ufixed72x128","ufixed72x136","ufixed72x144","ufixed72x152","ufixed72x160","ufixed72x168","ufixed72x176","ufixed72x184","ufixed80x8","ufixed80x16","ufixed80x24","ufixed80x32","ufixed80x40","ufixed80x48","ufixed80x56","ufixed80x64","ufixed80x72","ufixed80x80","ufixed80x88","ufixed80x96","ufixed80x104","ufixed80x112","ufixed80x120","ufixed80x128","ufixed80x136","ufixed80x144","ufixed80x152","ufixed80x160","ufixed80x168","ufixed80x176","ufixed88x8","ufixed88x16","ufixed88x24","ufixed88x32","ufixed88x40","ufixed88x48","ufixed88x56","ufixed88x64","ufixed88x72","ufixed88x80","ufixed88x88","ufixed88x96","ufixed88x104","ufixed88x112","ufixed88x120","ufixed88x128","ufixed88x136","ufixed88x144","ufixed88x152","ufixed88x160","ufixed88x168","ufixed96x8","ufixed96x16","ufixed96x24","ufixed96x32","ufixed96x40","ufixed96x48","ufixed96x56","ufixed96x64","ufixed96x72","ufixed96x80","ufixed96x88","ufixed96x96","ufixed96x104","ufixed96x112","ufixed96x120","ufixed96x128","ufixed96x136","ufixed96x144","ufixed96x152","ufixed96x160","ufixed104x8","ufixed104x16","ufixed104x24","ufixed104x32","ufixed104x40","ufixed104x48","ufixed104x56","ufixed104x64","ufixed104x72","ufixed104x80","ufixed104x88","ufixed104x96","ufixed104x104","ufixed104x112","ufixed104x120","ufixed104x128","ufixed104x136","ufixed104x144","ufixed104x152","ufixed112x8","ufixed112x16","ufixed112x24","ufixed112x32","ufixed112x40","ufixed112x48","ufixed112x56","ufixed112x64","ufixed112x72","ufixed112x80","ufixed112x88","ufixed112x96","ufixed112x104","ufixed112x112","ufixed112x120","ufixed112x128","ufixed112x136","ufixed112x144","ufixed120x8","ufixed120x16","ufixed120x24","ufixed120x32","ufixed120x40","ufixed120x48","ufixed120x56","ufixed120x64","ufixed120x72","ufixed120x80","ufixed120x88","ufixed120x96","ufixed120x104","ufixed120x112","ufixed120x120","ufixed120x128","ufixed120x136","ufixed128x8","ufixed128x16","ufixed128x24","ufixed128x32","ufixed128x40","ufixed128x48","ufixed128x56","ufixed128x64","ufixed128x72","ufixed128x80","ufixed128x88","ufixed128x96","ufixed128x104","ufixed128x112","ufixed128x120","ufixed128x128","ufixed136x8","ufixed136x16","ufixed136x24","ufixed136x32","ufixed136x40","ufixed136x48","ufixed136x56","ufixed136x64","ufixed136x72","ufixed136x80","ufixed136x88","ufixed136x96","ufixed136x104","ufixed136x112","ufixed136x120","ufixed144x8","ufixed144x16","ufixed144x24","ufixed144x32","ufixed144x40","ufixed144x48","ufixed144x56","ufixed144x64","ufixed144x72","ufixed144x80","ufixed144x88","ufixed144x96","ufixed144x104","ufixed144x112","ufixed152x8","ufixed152x16","ufixed152x24","ufixed152x32","ufixed152x40","ufixed152x48","ufixed152x56","ufixed152x64","ufixed152x72","ufixed152x80","ufixed152x88","ufixed152x96","ufixed152x104","ufixed160x8","ufixed160x16","ufixed160x24","ufixed160x32","ufixed160x40","ufixed160x48","ufixed160x56","ufixed160x64","ufixed160x72","ufixed160x80","ufixed160x88","ufixed160x96","ufixed168x8","ufixed168x16","ufixed168x24","ufixed168x32","ufixed168x40","ufixed168x48","ufixed168x56","ufixed168x64","ufixed168x72","ufixed168x80","ufixed168x88","ufixed176x8","ufixed176x16","ufixed176x24","ufixed176x32","ufixed176x40","ufixed176x48","ufixed176x56","ufixed176x64","ufixed176x72","ufixed176x80","ufixed184x8","ufixed184x16","ufixed184x24","ufixed184x32","ufixed184x40","ufixed184x48","ufixed184x56","ufixed184x64","ufixed184x72","ufixed192x8","ufixed192x16","ufixed192x24","ufixed192x32","ufixed192x40","ufixed192x48","ufixed192x56","ufixed192x64","ufixed200x8","ufixed200x16","ufixed200x24","ufixed200x32","ufixed200x40","ufixed200x48","ufixed200x56","ufixed208x8","ufixed208x16","ufixed208x24","ufixed208x32","ufixed208x40","ufixed208x48","ufixed216x8","ufixed216x16","ufixed216x24","ufixed216x32","ufixed216x40","ufixed224x8","ufixed224x16","ufixed224x24","ufixed224x32","ufixed232x8","ufixed232x16","ufixed232x24","ufixed240x8","ufixed240x16","ufixed248x8","event","enum","let","mapping","private","public","external","inherited","payable","true","false","var","import","constant","if","else","for","else","for","while","do","break","continue","throw","returns","return","suicide","new","is","this","super"],operators:["=",">","<","!","~","?",":","==","<=",">=","!=","&&","||","++","--","+","-","*","/","&","|","^","%","<<",">>",">>>","+=","-=","*=","/=","&=","|=","^=","%=","<<=",">>=",">>>="],symbols:/[=>](?!@symbols)/,"@brackets"],[/@symbols/,{cases:{"@operators":"delimiter","@default":""}}],[/\d*\d+[eE]([\-+]?\d+)?(@floatsuffix)/,"number.float"],[/\d*\.\d+([eE][\-+]?\d+)?(@floatsuffix)/,"number.float"],[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/,"number.hex"],[/0[0-7']*[0-7](@integersuffix)/,"number.octal"],[/0[bB][0-1']*[0-1](@integersuffix)/,"number.binary"],[/\d[\d']*\d(@integersuffix)/,"number"],[/\d(@integersuffix)/,"number"],[/[;,.]/,"delimiter"],[/"([^"\\]|\\.)*$/,"string.invalid"],[/"/,"string","@string"],[/'[^\\']'/,"string"],[/(')(@escapes)(')/,["string","string.escape","string"]],[/'/,"string.invalid"]],whitespace:[[/[ \t\r\n]+/,""],[/\/\*\*(?!\/)/,"comment.doc","@doccomment"],[/\/\*/,"comment","@comment"],[/\/\/.*$/,"comment"]],comment:[[/[^\/*]+/,"comment"],[/\*\//,"comment","@pop"],[/[\/*]/,"comment"]],doccomment:[[/[^\/*]+/,"comment.doc"],[/\*\//,"comment.doc","@pop"],[/[\/*]/,"comment.doc"]],string:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/sql.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/sql",["require","exports"],function(E,T){"use strict";Object.defineProperty(T,"__esModule",{value:!0}),T.conf={comments:{lineComment:"--",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}]},T.language={defaultToken:"",tokenPostfix:".sql",ignoreCase:!0,brackets:[{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"}],keywords:["ABORT_AFTER_WAIT","ABSENT","ABSOLUTE","ACCENT_SENSITIVITY","ACTION","ACTIVATION","ACTIVE","ADD","ADDRESS","ADMIN","AES","AES_128","AES_192","AES_256","AFFINITY","AFTER","AGGREGATE","ALGORITHM","ALL_CONSTRAINTS","ALL_ERRORMSGS","ALL_INDEXES","ALL_LEVELS","ALL_SPARSE_COLUMNS","ALLOW_CONNECTIONS","ALLOW_MULTIPLE_EVENT_LOSS","ALLOW_PAGE_LOCKS","ALLOW_ROW_LOCKS","ALLOW_SINGLE_EVENT_LOSS","ALLOW_SNAPSHOT_ISOLATION","ALLOWED","ALTER","ANONYMOUS","ANSI_DEFAULTS","ANSI_NULL_DEFAULT","ANSI_NULL_DFLT_OFF","ANSI_NULL_DFLT_ON","ANSI_NULLS","ANSI_PADDING","ANSI_WARNINGS","APPEND","APPLICATION","APPLICATION_LOG","ARITHABORT","ARITHIGNORE","AS","ASC","ASSEMBLY","ASYMMETRIC","ASYNCHRONOUS_COMMIT","AT","ATOMIC","ATTACH","ATTACH_REBUILD_LOG","AUDIT","AUDIT_GUID","AUTHENTICATION","AUTHORIZATION","AUTO","AUTO_CLEANUP","AUTO_CLOSE","AUTO_CREATE_STATISTICS","AUTO_SHRINK","AUTO_UPDATE_STATISTICS","AUTO_UPDATE_STATISTICS_ASYNC","AUTOMATED_BACKUP_PREFERENCE","AUTOMATIC","AVAILABILITY","AVAILABILITY_MODE","BACKUP","BACKUP_PRIORITY","BASE64","BATCHSIZE","BEGIN","BEGIN_DIALOG","BIGINT","BINARY","BINDING","BIT","BLOCKERS","BLOCKSIZE","BOUNDING_BOX","BREAK","BROKER","BROKER_INSTANCE","BROWSE","BUCKET_COUNT","BUFFER","BUFFERCOUNT","BULK","BULK_LOGGED","BY","CACHE","CALL","CALLED","CALLER","CAP_CPU_PERCENT","CASCADE","CASE","CATALOG","CATCH","CELLS_PER_OBJECT","CERTIFICATE","CHANGE_RETENTION","CHANGE_TRACKING","CHANGES","CHAR","CHARACTER","CHECK","CHECK_CONSTRAINTS","CHECK_EXPIRATION","CHECK_POLICY","CHECKALLOC","CHECKCATALOG","CHECKCONSTRAINTS","CHECKDB","CHECKFILEGROUP","CHECKIDENT","CHECKPOINT","CHECKTABLE","CLASSIFIER_FUNCTION","CLEANTABLE","CLEANUP","CLEAR","CLOSE","CLUSTER","CLUSTERED","CODEPAGE","COLLATE","COLLECTION","COLUMN","COLUMN_SET","COLUMNS","COLUMNSTORE","COLUMNSTORE_ARCHIVE","COMMIT","COMMITTED","COMPATIBILITY_LEVEL","COMPRESSION","COMPUTE","CONCAT","CONCAT_NULL_YIELDS_NULL","CONFIGURATION","CONNECT","CONSTRAINT","CONTAINMENT","CONTENT","CONTEXT","CONTINUE","CONTINUE_AFTER_ERROR","CONTRACT","CONTRACT_NAME","CONTROL","CONVERSATION","COOKIE","COPY_ONLY","COUNTER","CPU","CREATE","CREATE_NEW","CREATION_DISPOSITION","CREDENTIAL","CRYPTOGRAPHIC","CUBE","CURRENT","CURRENT_DATE","CURSOR","CURSOR_CLOSE_ON_COMMIT","CURSOR_DEFAULT","CYCLE","DATA","DATA_COMPRESSION","DATA_PURITY","DATABASE","DATABASE_DEFAULT","DATABASE_MIRRORING","DATABASE_SNAPSHOT","DATAFILETYPE","DATE","DATE_CORRELATION_OPTIMIZATION","DATEFIRST","DATEFORMAT","DATETIME","DATETIME2","DATETIMEOFFSET","DAY","DAYOFYEAR","DAYS","DB_CHAINING","DBCC","DBREINDEX","DDL_DATABASE_LEVEL_EVENTS","DEADLOCK_PRIORITY","DEALLOCATE","DEC","DECIMAL","DECLARE","DECRYPTION","DEFAULT","DEFAULT_DATABASE","DEFAULT_FULLTEXT_LANGUAGE","DEFAULT_LANGUAGE","DEFAULT_SCHEMA","DEFINITION","DELAY","DELAYED_DURABILITY","DELETE","DELETED","DENSITY_VECTOR","DENY","DEPENDENTS","DES","DESC","DESCRIPTION","DESX","DHCP","DIAGNOSTICS","DIALOG","DIFFERENTIAL","DIRECTORY_NAME","DISABLE","DISABLE_BROKER","DISABLED","DISK","DISTINCT","DISTRIBUTED","DOCUMENT","DOUBLE","DROP","DROP_EXISTING","DROPCLEANBUFFERS","DUMP","DURABILITY","DYNAMIC","EDITION","ELEMENTS","ELSE","EMERGENCY","EMPTY","EMPTYFILE","ENABLE","ENABLE_BROKER","ENABLED","ENCRYPTION","END","ENDPOINT","ENDPOINT_URL","ERRLVL","ERROR","ERROR_BROKER_CONVERSATIONS","ERRORFILE","ESCAPE","ESTIMATEONLY","EVENT","EVENT_RETENTION_MODE","EXEC","EXECUTABLE","EXECUTE","EXIT","EXPAND","EXPIREDATE","EXPIRY_DATE","EXPLICIT","EXTENDED_LOGICAL_CHECKS","EXTENSION","EXTERNAL","EXTERNAL_ACCESS","FAIL_OPERATION","FAILOVER","FAILOVER_MODE","FAILURE_CONDITION_LEVEL","FALSE","FAN_IN","FAST","FAST_FORWARD","FETCH","FIELDTERMINATOR","FILE","FILEGROUP","FILEGROWTH","FILELISTONLY","FILENAME","FILEPATH","FILESTREAM","FILESTREAM_ON","FILETABLE_COLLATE_FILENAME","FILETABLE_DIRECTORY","FILETABLE_FULLPATH_UNIQUE_CONSTRAINT_NAME","FILETABLE_NAMESPACE","FILETABLE_PRIMARY_KEY_CONSTRAINT_NAME","FILETABLE_STREAMID_UNIQUE_CONSTRAINT_NAME","FILLFACTOR","FILTERING","FIRE_TRIGGERS","FIRST","FIRSTROW","FLOAT","FMTONLY","FOLLOWING","FOR","FORCE","FORCE_FAILOVER_ALLOW_DATA_LOSS","FORCE_SERVICE_ALLOW_DATA_LOSS","FORCED","FORCEPLAN","FORCESCAN","FORCESEEK","FOREIGN","FORMATFILE","FORMSOF","FORWARD_ONLY","FREE","FREEPROCCACHE","FREESESSIONCACHE","FREESYSTEMCACHE","FROM","FULL","FULLSCAN","FULLTEXT","FUNCTION","GB","GEOGRAPHY_AUTO_GRID","GEOGRAPHY_GRID","GEOMETRY_AUTO_GRID","GEOMETRY_GRID","GET","GLOBAL","GO","GOTO","GOVERNOR","GRANT","GRIDS","GROUP","GROUP_MAX_REQUESTS","HADR","HASH","HASHED","HAVING","HEADERONLY","HEALTH_CHECK_TIMEOUT","HELP","HIERARCHYID","HIGH","HINT","HISTOGRAM","HOLDLOCK","HONOR_BROKER_PRIORITY","HOUR","HOURS","IDENTITY","IDENTITY_INSERT","IDENTITY_VALUE","IDENTITYCOL","IF","IGNORE_CONSTRAINTS","IGNORE_DUP_KEY","IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX","IGNORE_TRIGGERS","IMAGE","IMMEDIATE","IMPERSONATE","IMPLICIT_TRANSACTIONS","IMPORTANCE","INCLUDE","INCREMENT","INCREMENTAL","INDEX","INDEXDEFRAG","INFINITE","INFLECTIONAL","INIT","INITIATOR","INPUT","INPUTBUFFER","INSENSITIVE","INSERT","INSERTED","INSTEAD","INT","INTEGER","INTO","IO","IP","ISABOUT","ISOLATION","JOB","KB","KEEP","KEEP_CDC","KEEP_NULLS","KEEP_REPLICATION","KEEPDEFAULTS","KEEPFIXED","KEEPIDENTITY","KEEPNULLS","KERBEROS","KEY","KEY_SOURCE","KEYS","KEYSET","KILL","KILOBYTES_PER_BATCH","LABELONLY","LANGUAGE","LAST","LASTROW","LEVEL","LEVEL_1","LEVEL_2","LEVEL_3","LEVEL_4","LIFETIME","LIMIT","LINENO","LIST","LISTENER","LISTENER_IP","LISTENER_PORT","LOAD","LOADHISTORY","LOB_COMPACTION","LOCAL","LOCAL_SERVICE_NAME","LOCK_ESCALATION","LOCK_TIMEOUT","LOGIN","LOGSPACE","LOOP","LOW","MANUAL","MARK","MARK_IN_USE_FOR_REMOVAL","MASTER","MAX_CPU_PERCENT","MAX_DISPATCH_LATENCY","MAX_DOP","MAX_DURATION","MAX_EVENT_SIZE","MAX_FILES","MAX_IOPS_PER_VOLUME","MAX_MEMORY","MAX_MEMORY_PERCENT","MAX_QUEUE_READERS","MAX_ROLLOVER_FILES","MAX_SIZE","MAXDOP","MAXERRORS","MAXLENGTH","MAXRECURSION","MAXSIZE","MAXTRANSFERSIZE","MAXVALUE","MB","MEDIADESCRIPTION","MEDIANAME","MEDIAPASSWORD","MEDIUM","MEMBER","MEMORY_OPTIMIZED","MEMORY_OPTIMIZED_DATA","MEMORY_OPTIMIZED_ELEVATE_TO_SNAPSHOT","MEMORY_PARTITION_MODE","MERGE","MESSAGE","MESSAGE_FORWARD_SIZE","MESSAGE_FORWARDING","MICROSECOND","MILLISECOND","MIN_CPU_PERCENT","MIN_IOPS_PER_VOLUME","MIN_MEMORY_PERCENT","MINUTE","MINUTES","MINVALUE","MIRROR","MIRROR_ADDRESS","MODIFY","MONEY","MONTH","MOVE","MULTI_USER","MUST_CHANGE","NAME","NANOSECOND","NATIONAL","NATIVE_COMPILATION","NCHAR","NEGOTIATE","NESTED_TRIGGERS","NEW_ACCOUNT","NEW_BROKER","NEW_PASSWORD","NEWNAME","NEXT","NO","NO_BROWSETABLE","NO_CHECKSUM","NO_COMPRESSION","NO_EVENT_LOSS","NO_INFOMSGS","NO_TRUNCATE","NO_WAIT","NOCHECK","NOCOUNT","NOEXEC","NOEXPAND","NOFORMAT","NOINDEX","NOINIT","NOLOCK","NON","NON_TRANSACTED_ACCESS","NONCLUSTERED","NONE","NORECOMPUTE","NORECOVERY","NORESEED","NORESET","NOREWIND","NORMAL","NOSKIP","NOTIFICATION","NOTRUNCATE","NOUNLOAD","NOWAIT","NTEXT","NTLM","NUMANODE","NUMERIC","NUMERIC_ROUNDABORT","NVARCHAR","OBJECT","OF","OFF","OFFLINE","OFFSET","OFFSETS","OLD_ACCOUNT","OLD_PASSWORD","ON","ON_FAILURE","ONLINE","ONLY","OPEN","OPEN_EXISTING","OPENTRAN","OPTIMISTIC","OPTIMIZE","OPTION","ORDER","OUT","OUTPUT","OUTPUTBUFFER","OVER","OVERRIDE","OWNER","OWNERSHIP","PAD_INDEX","PAGE","PAGE_VERIFY","PAGECOUNT","PAGLOCK","PARAMETERIZATION","PARSEONLY","PARTIAL","PARTITION","PARTITIONS","PARTNER","PASSWORD","PATH","PER_CPU","PER_NODE","PERCENT","PERMISSION_SET","PERSISTED","PHYSICAL_ONLY","PLAN","POISON_MESSAGE_HANDLING","POOL","POPULATION","PORT","PRECEDING","PRECISION","PRIMARY","PRIMARY_ROLE","PRINT","PRIOR","PRIORITY","PRIORITY_LEVEL","PRIVATE","PRIVILEGES","PROC","PROCCACHE","PROCEDURE","PROCEDURE_NAME","PROCESS","PROFILE","PROPERTY","PROPERTY_DESCRIPTION","PROPERTY_INT_ID","PROPERTY_SET_GUID","PROVIDER","PROVIDER_KEY_NAME","PUBLIC","PUT","QUARTER","QUERY","QUERY_GOVERNOR_COST_LIMIT","QUEUE","QUEUE_DELAY","QUOTED_IDENTIFIER","RAISERROR","RANGE","RAW","RC2","RC4","RC4_128","READ","READ_COMMITTED_SNAPSHOT","READ_ONLY","READ_ONLY_ROUTING_LIST","READ_ONLY_ROUTING_URL","READ_WRITE","READ_WRITE_FILEGROUPS","READCOMMITTED","READCOMMITTEDLOCK","READONLY","READPAST","READTEXT","READUNCOMMITTED","READWRITE","REAL","REBUILD","RECEIVE","RECOMPILE","RECONFIGURE","RECOVERY","RECURSIVE","RECURSIVE_TRIGGERS","REFERENCES","REGENERATE","RELATED_CONVERSATION","RELATED_CONVERSATION_GROUP","RELATIVE","REMOTE","REMOTE_PROC_TRANSACTIONS","REMOTE_SERVICE_NAME","REMOVE","REORGANIZE","REPAIR_ALLOW_DATA_LOSS","REPAIR_FAST","REPAIR_REBUILD","REPEATABLE","REPEATABLEREAD","REPLICA","REPLICATION","REQUEST_MAX_CPU_TIME_SEC","REQUEST_MAX_MEMORY_GRANT_PERCENT","REQUEST_MEMORY_GRANT_TIMEOUT_SEC","REQUIRED","RESAMPLE","RESEED","RESERVE_DISK_SPACE","RESET","RESOURCE","RESTART","RESTORE","RESTRICT","RESTRICTED_USER","RESULT","RESUME","RETAINDAYS","RETENTION","RETURN","RETURNS","REVERT","REVOKE","REWIND","REWINDONLY","ROBUST","ROLE","ROLLBACK","ROLLUP","ROOT","ROUTE","ROW","ROWCOUNT","ROWGUIDCOL","ROWLOCK","ROWS","ROWS_PER_BATCH","ROWTERMINATOR","ROWVERSION","RSA_1024","RSA_2048","RSA_512","RULE","SAFE","SAFETY","SAMPLE","SAVE","SCHEDULER","SCHEMA","SCHEMA_AND_DATA","SCHEMA_ONLY","SCHEMABINDING","SCHEME","SCROLL","SCROLL_LOCKS","SEARCH","SECOND","SECONDARY","SECONDARY_ONLY","SECONDARY_ROLE","SECONDS","SECRET","SECURITY_LOG","SECURITYAUDIT","SELECT","SELECTIVE","SELF","SEND","SENT","SEQUENCE","SERIALIZABLE","SERVER","SERVICE","SERVICE_BROKER","SERVICE_NAME","SESSION","SESSION_TIMEOUT","SET","SETS","SETUSER","SHOW_STATISTICS","SHOWCONTIG","SHOWPLAN","SHOWPLAN_ALL","SHOWPLAN_TEXT","SHOWPLAN_XML","SHRINKDATABASE","SHRINKFILE","SHUTDOWN","SID","SIGNATURE","SIMPLE","SINGLE_BLOB","SINGLE_CLOB","SINGLE_NCLOB","SINGLE_USER","SINGLETON","SIZE","SKIP","SMALLDATETIME","SMALLINT","SMALLMONEY","SNAPSHOT","SORT_IN_TEMPDB","SOURCE","SPARSE","SPATIAL","SPATIAL_WINDOW_MAX_CELLS","SPECIFICATION","SPLIT","SQL","SQL_VARIANT","SQLPERF","STANDBY","START","START_DATE","STARTED","STARTUP_STATE","STAT_HEADER","STATE","STATEMENT","STATIC","STATISTICAL_SEMANTICS","STATISTICS","STATISTICS_INCREMENTAL","STATISTICS_NORECOMPUTE","STATS","STATS_STREAM","STATUS","STATUSONLY","STOP","STOP_ON_ERROR","STOPAT","STOPATMARK","STOPBEFOREMARK","STOPLIST","STOPPED","SUBJECT","SUBSCRIPTION","SUPPORTED","SUSPEND","SWITCH","SYMMETRIC","SYNCHRONOUS_COMMIT","SYNONYM","SYSNAME","SYSTEM","TABLE","TABLERESULTS","TABLESAMPLE","TABLOCK","TABLOCKX","TAKE","TAPE","TARGET","TARGET_RECOVERY_TIME","TB","TCP","TEXT","TEXTIMAGE_ON","TEXTSIZE","THEN","THESAURUS","THROW","TIES","TIME","TIMEOUT","TIMER","TIMESTAMP","TINYINT","TO","TOP","TORN_PAGE_DETECTION","TRACEOFF","TRACEON","TRACESTATUS","TRACK_CAUSALITY","TRACK_COLUMNS_UPDATED","TRAN","TRANSACTION","TRANSFER","TRANSFORM_NOISE_WORDS","TRIGGER","TRIPLE_DES","TRIPLE_DES_3KEY","TRUE","TRUNCATE","TRUNCATEONLY","TRUSTWORTHY","TRY","TSQL","TWO_DIGIT_YEAR_CUTOFF","TYPE","TYPE_WARNING","UNBOUNDED","UNCHECKED","UNCOMMITTED","UNDEFINED","UNIQUE","UNIQUEIDENTIFIER","UNKNOWN","UNLIMITED","UNLOAD","UNSAFE","UPDATE","UPDATETEXT","UPDATEUSAGE","UPDLOCK","URL","USE","USED","USER","USEROPTIONS","USING","VALID_XML","VALIDATION","VALUE","VALUES","VARBINARY","VARCHAR","VARYING","VERIFYONLY","VERSION","VIEW","VIEW_METADATA","VIEWS","VISIBILITY","WAIT_AT_LOW_PRIORITY","WAITFOR","WEEK","WEIGHT","WELL_FORMED_XML","WHEN","WHERE","WHILE","WINDOWS","WITH","WITHIN","WITHOUT","WITNESS","WORK","WORKLOAD","WRITETEXT","XACT_ABORT","XLOCK","XMAX","XMIN","XML","XMLDATA","XMLNAMESPACES","XMLSCHEMA","XQUERY","XSINIL","YEAR","YMAX","YMIN"],operators:["ALL","AND","ANY","BETWEEN","EXISTS","IN","LIKE","NOT","OR","SOME","EXCEPT","INTERSECT","UNION","APPLY","CROSS","FULL","INNER","JOIN","LEFT","OUTER","RIGHT","CONTAINS","FREETEXT","IS","NULL","PIVOT","UNPIVOT","MATCHED"],builtinFunctions:["AVG","CHECKSUM_AGG","COUNT","COUNT_BIG","GROUPING","GROUPING_ID","MAX","MIN","SUM","STDEV","STDEVP","VAR","VARP","CUME_DIST","FIRST_VALUE","LAG","LAST_VALUE","LEAD","PERCENTILE_CONT","PERCENTILE_DISC","PERCENT_RANK","COLLATE","COLLATIONPROPERTY","TERTIARY_WEIGHTS","FEDERATION_FILTERING_VALUE","CAST","CONVERT","PARSE","TRY_CAST","TRY_CONVERT","TRY_PARSE","ASYMKEY_ID","ASYMKEYPROPERTY","CERTPROPERTY","CERT_ID","CRYPT_GEN_RANDOM","DECRYPTBYASYMKEY","DECRYPTBYCERT","DECRYPTBYKEY","DECRYPTBYKEYAUTOASYMKEY","DECRYPTBYKEYAUTOCERT","DECRYPTBYPASSPHRASE","ENCRYPTBYASYMKEY","ENCRYPTBYCERT","ENCRYPTBYKEY","ENCRYPTBYPASSPHRASE","HASHBYTES","IS_OBJECTSIGNED","KEY_GUID","KEY_ID","KEY_NAME","SIGNBYASYMKEY","SIGNBYCERT","SYMKEYPROPERTY","VERIFYSIGNEDBYCERT","VERIFYSIGNEDBYASYMKEY","CURSOR_STATUS","DATALENGTH","IDENT_CURRENT","IDENT_INCR","IDENT_SEED","IDENTITY","SQL_VARIANT_PROPERTY","CURRENT_TIMESTAMP","DATEADD","DATEDIFF","DATEFROMPARTS","DATENAME","DATEPART","DATETIME2FROMPARTS","DATETIMEFROMPARTS","DATETIMEOFFSETFROMPARTS","DAY","EOMONTH","GETDATE","GETUTCDATE","ISDATE","MONTH","SMALLDATETIMEFROMPARTS","SWITCHOFFSET","SYSDATETIME","SYSDATETIMEOFFSET","SYSUTCDATETIME","TIMEFROMPARTS","TODATETIMEOFFSET","YEAR","CHOOSE","COALESCE","IIF","NULLIF","ABS","ACOS","ASIN","ATAN","ATN2","CEILING","COS","COT","DEGREES","EXP","FLOOR","LOG","LOG10","PI","POWER","RADIANS","RAND","ROUND","SIGN","SIN","SQRT","SQUARE","TAN","APP_NAME","APPLOCK_MODE","APPLOCK_TEST","ASSEMBLYPROPERTY","COL_LENGTH","COL_NAME","COLUMNPROPERTY","DATABASE_PRINCIPAL_ID","DATABASEPROPERTYEX","DB_ID","DB_NAME","FILE_ID","FILE_IDEX","FILE_NAME","FILEGROUP_ID","FILEGROUP_NAME","FILEGROUPPROPERTY","FILEPROPERTY","FULLTEXTCATALOGPROPERTY","FULLTEXTSERVICEPROPERTY","INDEX_COL","INDEXKEY_PROPERTY","INDEXPROPERTY","OBJECT_DEFINITION","OBJECT_ID","OBJECT_NAME","OBJECT_SCHEMA_NAME","OBJECTPROPERTY","OBJECTPROPERTYEX","ORIGINAL_DB_NAME","PARSENAME","SCHEMA_ID","SCHEMA_NAME","SCOPE_IDENTITY","SERVERPROPERTY","STATS_DATE","TYPE_ID","TYPE_NAME","TYPEPROPERTY","DENSE_RANK","NTILE","RANK","ROW_NUMBER","PUBLISHINGSERVERNAME","OPENDATASOURCE","OPENQUERY","OPENROWSET","OPENXML","CERTENCODED","CERTPRIVATEKEY","CURRENT_USER","HAS_DBACCESS","HAS_PERMS_BY_NAME","IS_MEMBER","IS_ROLEMEMBER","IS_SRVROLEMEMBER","LOGINPROPERTY","ORIGINAL_LOGIN","PERMISSIONS","PWDENCRYPT","PWDCOMPARE","SESSION_USER","SESSIONPROPERTY","SUSER_ID","SUSER_NAME","SUSER_SID","SUSER_SNAME","SYSTEM_USER","USER","USER_ID","USER_NAME","ASCII","CHAR","CHARINDEX","CONCAT","DIFFERENCE","FORMAT","LEFT","LEN","LOWER","LTRIM","NCHAR","PATINDEX","QUOTENAME","REPLACE","REPLICATE","REVERSE","RIGHT","RTRIM","SOUNDEX","SPACE","STR","STUFF","SUBSTRING","UNICODE","UPPER","BINARY_CHECKSUM","CHECKSUM","CONNECTIONPROPERTY","CONTEXT_INFO","CURRENT_REQUEST_ID","ERROR_LINE","ERROR_NUMBER","ERROR_MESSAGE","ERROR_PROCEDURE","ERROR_SEVERITY","ERROR_STATE","FORMATMESSAGE","GETANSINULL","GET_FILESTREAM_TRANSACTION_CONTEXT","HOST_ID","HOST_NAME","ISNULL","ISNUMERIC","MIN_ACTIVE_ROWVERSION","NEWID","NEWSEQUENTIALID","ROWCOUNT_BIG","XACT_STATE","TEXTPTR","TEXTVALID","COLUMNS_UPDATED","EVENTDATA","TRIGGER_NESTLEVEL","UPDATE","CHANGETABLE","CHANGE_TRACKING_CONTEXT","CHANGE_TRACKING_CURRENT_VERSION","CHANGE_TRACKING_IS_COLUMN_IN_MASK","CHANGE_TRACKING_MIN_VALID_VERSION","CONTAINSTABLE","FREETEXTTABLE","SEMANTICKEYPHRASETABLE","SEMANTICSIMILARITYDETAILSTABLE","SEMANTICSIMILARITYTABLE","FILETABLEROOTPATH","GETFILENAMESPACEPATH","GETPATHLOCATOR","PATHNAME","GET_TRANSMISSION_STATUS"],builtinVariables:["@@DATEFIRST","@@DBTS","@@LANGID","@@LANGUAGE","@@LOCK_TIMEOUT","@@MAX_CONNECTIONS","@@MAX_PRECISION","@@NESTLEVEL","@@OPTIONS","@@REMSERVER","@@SERVERNAME","@@SERVICENAME","@@SPID","@@TEXTSIZE","@@VERSION","@@CURSOR_ROWS","@@FETCH_STATUS","@@DATEFIRST","@@PROCID","@@ERROR","@@IDENTITY","@@ROWCOUNT","@@TRANCOUNT","@@CONNECTIONS","@@CPU_BUSY","@@IDLE","@@IO_BUSY","@@PACKET_ERRORS","@@PACK_RECEIVED","@@PACK_SENT","@@TIMETICKS","@@TOTAL_ERRORS","@@TOTAL_READ","@@TOTAL_WRITE"],pseudoColumns:["$ACTION","$IDENTITY","$ROWGUID","$PARTITION"],tokenizer:{root:[{include:"@comments"},{include:"@whitespace"},{include:"@pseudoColumns"},{include:"@numbers"},{include:"@strings"},{include:"@complexIdentifiers"},{include:"@scopes"},[/[;,.]/,"delimiter"],[/[()]/,"@brackets"],[/[\w@#$]+/,{cases:{"@keywords":"keyword","@operators":"operator","@builtinVariables":"predefined","@builtinFunctions":"predefined","@default":"identifier"}}],[/[<>=!%&+\-*/|~^]/,"operator"]],whitespace:[[/\s+/,"white"]],comments:[[/--+.*/,"comment"],[/\/\*/,{token:"comment.quote",next:"@comment"}]],comment:[[/[^*/]+/,"comment"],[/\*\//,{token:"comment.quote",next:"@pop"}],[/./,"comment"]],pseudoColumns:[[/[$][A-Za-z_][\w@#$]*/,{cases:{"@pseudoColumns":"predefined","@default":"identifier"}}]],numbers:[[/0[xX][0-9a-fA-F]*/,"number"],[/[$][+-]*\d*(\.\d*)?/,"number"],[/((\d+(\.\d*)?)|(\.\d+))([eE][\-+]?\d+)?/,"number"]],strings:[[/N'/,{token:"string",next:"@string"}],[/'/,{token:"string",next:"@string"}]],string:[[/[^']+/,"string"],[/''/,"string"],[/'/,{token:"string",next:"@pop"}]],complexIdentifiers:[[/\[/,{token:"identifier.quote",next:"@bracketedIdentifier"}],[/"/,{token:"identifier.quote",next:"@quotedIdentifier"}]],bracketedIdentifier:[[/[^\]]+/,"identifier"],[/]]/,"identifier"],[/]/,{token:"identifier.quote",next:"@pop"}]],quotedIdentifier:[[/[^"]+/,"identifier"],[/""/,"identifier"],[/"/,{token:"identifier.quote",next:"@pop"}]],scopes:[[/BEGIN\s+(DISTRIBUTED\s+)?TRAN(SACTION)?\b/i,"keyword"],[/BEGIN\s+TRY\b/i,{token:"keyword.try"}],[/END\s+TRY\b/i,{token:"keyword.try"}],[/BEGIN\s+CATCH\b/i,{token:"keyword.catch"}],[/END\s+CATCH\b/i,{token:"keyword.catch"}],[/(BEGIN|CASE)\b/i,{token:"keyword.block"}],[/END\b/i,{token:"keyword.block"}],[/WHEN\b/i,{token:"keyword.choice"}],[/THEN\b/i,{token:"keyword.choice"}]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/swift.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ /*!--------------------------------------------------------------------------------------------- * Copyright (C) David Owens II, owensd.io. All rights reserved. *--------------------------------------------------------------------------------------------*/ define("vs/basic-languages/src/swift",["require","exports"],function(e,o){"use strict";Object.defineProperty(o,"__esModule",{value:!0}),o.conf={comments:{lineComment:"//",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"`",close:"`"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"},{open:"`",close:"`"}]},o.language={defaultToken:"",tokenPostfix:".swift",identifier:/[a-zA-Z_][\w$]*/,attributes:["@autoclosure","@noescape","@noreturn","@NSApplicationMain","@NSCopying","@NSManaged","@objc","@UIApplicationMain","@noreturn","@availability","@IBAction","@IBDesignable","@IBInspectable","@IBOutlet"],accessmodifiers:["public","private","internal"],keywords:["__COLUMN__","__FILE__","__FUNCTION__","__LINE__","as","as!","as?","associativity","break","case","catch","class","continue","convenience","default","deinit","didSet","do","dynamic","dynamicType","else","enum","extension","fallthrough","final","for","func","get","guard","if","import","in","infix","init","inout","internal","is","lazy","left","let","mutating","nil","none","nonmutating","operator","optional","override","postfix","precedence","prefix","private","protocol","Protocol","public","repeat","required","return","right","self","Self","set","static","struct","subscript","super","switch","throw","throws","try","try!","Type","typealias","unowned","var","weak","where","while","willSet","FALSE","TRUE"],symbols:/[=(){}\[\].,:;@#\_&\-<>`?!+*\\\/]/,operatorstart:/[\/=\-+!*%<>&|^~?\u00A1-\u00A7\u00A9\u00AB\u00AC\u00AE\u00B0-\u00B1\u00B6\u00BB\u00BF\u00D7\u00F7\u2016-\u2017\u2020-\u2027\u2030-\u203E\u2041-\u2053\u2055-\u205E\u2190-\u23FF\u2500-\u2775\u2794-\u2BFF\u2E00-\u2E7F\u3001-\u3003\u3008-\u3030]/,operatorend:/[\u0300-\u036F\u1DC0-\u1DFF\u20D0-\u20FF\uFE00-\uFE0F\uFE20-\uFE2F\uE0100-\uE01EF]/,operators:/(@operatorstart)((@operatorstart)|(@operatorend))*/,escapes:/\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,tokenizer:{root:[{include:"@comment"},{include:"@attribute"},{include:"@literal"},{include:"@keyword"},{include:"@invokedmethod"},{include:"@symbol"}],symbol:[[/[{}()\[\]]/,"@brackets"],[/[<>](?!@symbols)/,"@brackets"],[/[.]/,"delimiter"],[/@operators/,"operator"],[/@symbols/,"operator"]],comment:[[/\/\/\/.*$/,"comment.doc"],[/\/\*\*/,"comment.doc","@commentdocbody"],[/\/\/.*$/,"comment"],[/\/\*/,"comment","@commentbody"]],commentdocbody:[[/\/\*/,"comment","@commentbody"],[/\*\//,"comment.doc","@pop"],[/\:[a-zA-Z]+\:/,"comment.doc.param"],[/./,"comment.doc"]],commentbody:[[/\/\*/,"comment","@commentbody"],[/\*\//,"comment","@pop"],[/./,"comment"]],attribute:[[/\@@identifier/,{cases:{"@attributes":"keyword.control","@default":""}}]],literal:[[/"/,{token:"string.quote",next:"@stringlit"}],[/0[b]([01]_?)+/,"number.binary"],[/0[o]([0-7]_?)+/,"number.octal"],[/0[x]([0-9a-fA-F]_?)+([pP][\-+](\d_?)+)?/,"number.hex"],[/(\d_?)*\.(\d_?)+([eE][\-+]?(\d_?)+)?/,"number.float"],[/(\d_?)+/,"number"]],stringlit:[[/\\\(/,{token:"operator",next:"@interpolatedexpression"}],[/@escapes/,"string"],[/\\./,"string.escape.invalid"],[/"/,{token:"string.quote",next:"@pop"}],[/./,"string"]],interpolatedexpression:[[/\(/,{token:"operator",next:"@interpolatedexpression"}],[/\)/,{token:"operator",next:"@pop"}],{include:"@literal"},{include:"@keyword"},{include:"@symbol"}],keyword:[[/`/,{token:"operator",next:"@escapedkeyword"}],[/@identifier/,{cases:{"@keywords":"keyword","[A-Z][a-zA-Z0-9$]*":"type.identifier","@default":"identifier"}}]],escapedkeyword:[[/`/,{token:"operator",next:"@pop"}],[/./,"identifier"]],invokedmethod:[[/([.])(@identifier)/,{cases:{$2:["delimeter","type.identifier"],"@default":""}}]]}}}); ================================================ FILE: console/src/main/resources/static/console-ui/public/js/vs/basic-languages/src/vb.js ================================================ /*!----------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * monaco-languages version: 0.9.0(e162b4ba29044167bc7181c42b3270fa8a467424) * Released under the MIT license * https://github.com/Microsoft/monaco-languages/blob/master/LICENSE.md *-----------------------------------------------------------------------------*/ define("vs/basic-languages/src/vb",["require","exports"],function(e,n){"use strict";Object.defineProperty(n,"__esModule",{value:!0}),n.conf={comments:{lineComment:"'",blockComment:["/*","*/"]},brackets:[["{","}"],["[","]"],["(",")"],["<",">"],["addhandler","end addhandler"],["class","end class"],["enum","end enum"],["event","end event"],["function","end function"],["get","end get"],["if","end if"],["interface","end interface"],["module","end module"],["namespace","end namespace"],["operator","end operator"],["property","end property"],["raiseevent","end raiseevent"],["removehandler","end removehandler"],["select","end select"],["set","end set"],["structure","end structure"],["sub","end sub"],["synclock","end synclock"],["try","end try"],["while","end while"],["with","end with"],["using","end using"],["do","loop"],["for","next"]],autoClosingPairs:[{open:"{",close:"}",notIn:["string","comment"]},{open:"[",close:"]",notIn:["string","comment"]},{open:"(",close:")",notIn:["string","comment"]},{open:'"',close:'"',notIn:["string","comment"]},{open:"<",close:">",notIn:["string","comment"]}]},n.language={defaultToken:"",tokenPostfix:".vb",ignoreCase:!0,brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.array",open:"[",close:"]"},{token:"delimiter.parenthesis",open:"(",close:")"},{token:"delimiter.angle",open:"<",close:">"},{token:"keyword.tag-addhandler",open:"addhandler",close:"end addhandler"},{token:"keyword.tag-class",open:"class",close:"end class"},{token:"keyword.tag-enum",open:"enum",close:"end enum"},{token:"keyword.tag-event",open:"event",close:"end event"},{token:"keyword.tag-function",open:"function",close:"end function"},{token:"keyword.tag-get",open:"get",close:"end get"},{token:"keyword.tag-if",open:"if",close:"end if"},{token:"keyword.tag-interface",open:"interface",close:"end interface"},{token:"keyword.tag-module",open:"module",close:"end module"},{token:"keyword.tag-namespace",open:"namespace",close:"end namespace"},{token:"keyword.tag-operator",open:"operator",close:"end operator"},{token:"keyword.tag-property",open:"property",close:"end property"},{token:"keyword.tag-raiseevent",open:"raiseevent",close:"end raiseevent"},{token:"keyword.tag-removehandler",open:"removehandler",close:"end removehandler"},{token:"keyword.tag-select",open:"select",close:"end select"},{token:"keyword.tag-set",open:"set",close:"end set"},{token:"keyword.tag-structure",open:"structure",close:"end structure"},{token:"keyword.tag-sub",open:"sub",close:"end sub"},{token:"keyword.tag-synclock",open:"synclock",close:"end synclock"},{token:"keyword.tag-try",open:"try",close:"end try"},{token:"keyword.tag-while",open:"while",close:"end while"},{token:"keyword.tag-with",open:"with",close:"end with"},{token:"keyword.tag-using",open:"using",close:"end using"},{token:"keyword.tag-do",open:"do",close:"loop"},{token:"keyword.tag-for",open:"for",close:"next"}],keywords:["AddHandler","AddressOf","Alias","And","AndAlso","As","Async","Boolean","ByRef","Byte","ByVal","Call","Case","Catch","CBool","CByte","CChar","CDate","CDbl","CDec","Char","CInt","Class","CLng","CObj","Const","Continue","CSByte","CShort","CSng","CStr","CType","CUInt","CULng","CUShort","Date","Decimal","Declare","Default","Delegate","Dim","DirectCast","Do","Double","Each","Else","ElseIf","End","EndIf","Enum","Erase","Error","Event","Exit","False","Finally","For","Friend","Function","Get","GetType","GetXMLNamespace","Global","GoSub","GoTo","Handles","If","Implements","Imports","In","Inherits","Integer","Interface","Is","IsNot","Let","Lib","Like","Long","Loop","Me","Mod","Module","MustInherit","MustOverride","MyBase","MyClass","NameOf","Namespace","Narrowing","New","Next","Not","Nothing","NotInheritable","NotOverridable","Object","Of","On","Operator","Option","Optional","Or","OrElse","Out","Overloads","Overridable","Overrides","ParamArray","Partial","Private","Property","Protected","Public","RaiseEvent","ReadOnly","ReDim","RemoveHandler","Resume","Return","SByte","Select","Set","Shadows","Shared","Short","Single","Static","Step","Stop","String","Structure","Sub","SyncLock","Then","Throw","To","True","Try","TryCast","TypeOf","UInteger","ULong","UShort","Using","Variant","Wend","When","While","Widening","With","WithEvents","WriteOnly","Xor"],tagwords:["If","Sub","Select","Try","Class","Enum","Function","Get","Interface","Module","Namespace","Operator","Set","Structure","Using","While","With","Do","Loop","For","Next","Property","Continue","AddHandler","RemoveHandler","Event","RaiseEvent","SyncLock"],symbols:/[=>"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}]},t.language={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[//,{token:"comment",next:"@pop"}],[//)("closed",/<(tag)[\s\S]+?<\/\1>/)("closing",/])*?>/)(/tag/g,g._tag)(),g.paragraph=s(g.paragraph)("hr",g.hr)("heading",g.heading)("lheading",g.lheading)("blockquote",g.blockquote)("tag","<"+g._tag)("def",g.def)(),g.normal=u({},g),g.gfm=u({},g.normal,{fences:/^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,paragraph:/^/,heading:/^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/}),g.gfm.paragraph=s(g.paragraph)("(?!","(?!"+g.gfm.fences.source.replace("\\1","\\2")+"|"+g.list.source.replace("\\1","\\3")+"|")(),g.tables=u({},g.gfm,{nptable:/^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,table:/^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/}),e.rules=g,e.lex=function(t,n){return new e(n).lex(t)},e.prototype.lex=function(e){return e=e.replace(/\r\n|\r/g,"\n").replace(/\t/g," ").replace(/\u00a0/g," ").replace(/\u2424/g,"\n"),this.token(e,!0)},e.prototype.token=function(e,t,n){for(var i,o,r,s,a,u,l,c,d,e=e.replace(/^ +$/gm,"");e;)if((r=this.rules.newline.exec(e))&&(e=e.substring(r[0].length),r[0].length>1&&this.tokens.push({type:"space"})),r=this.rules.code.exec(e))e=e.substring(r[0].length),r=r[0].replace(/^ {4}/gm,""),this.tokens.push({type:"code",text:this.options.pedantic?r:r.replace(/\n+$/,"")});else if(r=this.rules.fences.exec(e))e=e.substring(r[0].length),this.tokens.push({type:"code",lang:r[2],text:r[3]||""});else if(r=this.rules.heading.exec(e))e=e.substring(r[0].length),this.tokens.push({type:"heading",depth:r[1].length,text:r[2]});else if(t&&(r=this.rules.nptable.exec(e))){for(e=e.substring(r[0].length),u={type:"table",header:r[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:r[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:r[3].replace(/\n$/,"").split("\n")},c=0;c ?/gm,""),this.token(r,t,!0),this.tokens.push({type:"blockquote_end"});else if(r=this.rules.list.exec(e)){for(e=e.substring(r[0].length),s=r[2],this.tokens.push({type:"list_start",ordered:s.length>1}),i=!1,d=(r=r[0].match(this.rules.item)).length,c=0;c1&&a.length>1||(e=r.slice(c+1).join("\n")+e,c=d-1)),o=i||/\n\n(?!\s*$)/.test(u),c!==d-1&&(i="\n"===u.charAt(u.length-1),o||(o=i)),this.tokens.push({type:o?"loose_item_start":"list_item_start"}),this.token(u,!1,n),this.tokens.push({type:"list_item_end"});this.tokens.push({type:"list_end"})}else if(r=this.rules.html.exec(e))e=e.substring(r[0].length),this.tokens.push({type:this.options.sanitize?"paragraph":"html",pre:!this.options.sanitizer&&("pre"===r[1]||"script"===r[1]||"style"===r[1]),text:r[0]});else if(!n&&t&&(r=this.rules.def.exec(e)))e=e.substring(r[0].length),this.tokens.links[r[1].toLowerCase()]={href:r[2],title:r[3]};else if(t&&(r=this.rules.table.exec(e))){for(e=e.substring(r[0].length),u={type:"table",header:r[1].replace(/^ *| *\| *$/g,"").split(/ *\| */),align:r[2].replace(/^ *|\| *$/g,"").split(/ *\| */),cells:r[3].replace(/(?: *\| *)?\n$/,"").split("\n")},c=0;c])/,autolink:/^<([^ >]+(@|:\/)[^ >]+)>/,url:a,tag:/^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,link:/^!?\[(inside)\]\(href\)/,reflink:/^!?\[(inside)\]\s*\[([^\]]*)\]/,nolink:/^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,strong:/^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,em:/^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,code:/^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,br:/^ {2,}\n(?!\s*$)/,del:a,text:/^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/,m.link=s(m.link)("inside",m._inside)("href",m._href)(),m.reflink=s(m.reflink)("inside",m._inside)(),m.normal=u({},m),m.pedantic=u({},m.normal,{strong:/^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,em:/^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/}),m.gfm=u({},m.normal,{escape:s(m.escape)("])","~|])")(),url:/^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,del:/^~~(?=\S)([\s\S]*?\S)~~/,text:s(m.text)("]|","~]|")("|","|https?://|")()}),m.breaks=u({},m.gfm,{br:s(m.br)("{2,}","*")(),text:s(m.gfm.text)("{2,}","*")()}),t.rules=m,t.output=function(e,n,i){return new t(n,i).output(e)},t.prototype.output=function(e){for(var t,n,i,r,s="";e;)if(r=this.rules.escape.exec(e))e=e.substring(r[0].length),s+=r[1];else if(r=this.rules.autolink.exec(e))e=e.substring(r[0].length),"@"===r[2]?(n=":"===r[1].charAt(6)?this.mangle(r[1].substring(7)):this.mangle(r[1]),i=this.mangle("mailto:")+n):i=n=o(r[1]),s+=this.renderer.link(i,null,n);else if(this.inLink||!(r=this.rules.url.exec(e))){if(r=this.rules.tag.exec(e))!this.inLink&&/^/i.test(r[0])&&(this.inLink=!1),e=e.substring(r[0].length),s+=this.options.sanitize?this.options.sanitizer?this.options.sanitizer(r[0]):o(r[0]):r[0];else if(r=this.rules.link.exec(e))e=e.substring(r[0].length),this.inLink=!0,s+=this.outputLink(r,{href:r[2],title:r[3]}),this.inLink=!1;else if((r=this.rules.reflink.exec(e))||(r=this.rules.nolink.exec(e))){if(e=e.substring(r[0].length),t=(r[2]||r[1]).replace(/\s+/g," "),!(t=this.links[t.toLowerCase()])||!t.href){s+=r[0].charAt(0),e=r[0].substring(1)+e;continue}this.inLink=!0,s+=this.outputLink(r,t),this.inLink=!1}else if(r=this.rules.strong.exec(e))e=e.substring(r[0].length),s+=this.renderer.strong(this.output(r[2]||r[1]));else if(r=this.rules.em.exec(e))e=e.substring(r[0].length),s+=this.renderer.em(this.output(r[2]||r[1]));else if(r=this.rules.code.exec(e))e=e.substring(r[0].length),s+=this.renderer.codespan(o(r[2],!0));else if(r=this.rules.br.exec(e))e=e.substring(r[0].length),s+=this.renderer.br();else if(r=this.rules.del.exec(e))e=e.substring(r[0].length),s+=this.renderer.del(this.output(r[1]));else if(r=this.rules.text.exec(e))e=e.substring(r[0].length),s+=this.renderer.text(o(this.smartypants(r[0])));else if(e)throw new Error("Infinite loop on byte: "+e.charCodeAt(0))}else e=e.substring(r[0].length),i=n=o(r[1]),s+=this.renderer.link(i,null,n);return s},t.prototype.outputLink=function(e,t){var n=o(t.href),i=t.title?o(t.title):null;return"!"!==e[0].charAt(0)?this.renderer.link(n,i,this.output(e[1])):this.renderer.image(n,i,o(e[1]))},t.prototype.smartypants=function(e){return this.options.smartypants?e.replace(/---/g,"—").replace(/--/g,"–").replace(/(^|[-\u2014/(\[{"\s])'/g,"$1‘").replace(/'/g,"’").replace(/(^|[-\u2014/(\[{\u2018\s])"/g,"$1“").replace(/"/g,"”").replace(/\.{3}/g,"…"):e},t.prototype.mangle=function(e){if(!this.options.mangle)return e;for(var t,n="",i=e.length,o=0;o.5&&(t="x"+t.toString(16)),n+="&#"+t+";";return n},n.prototype.code=function(e,t,n){if(this.options.highlight){var i=this.options.highlight(e,t);null!=i&&i!==e&&(n=!0,e=i)}return t?'
    '+(n?e:o(e,!0))+"\n
    \n":"
    "+(n?e:o(e,!0))+"\n
    "},n.prototype.blockquote=function(e){return"
    \n"+e+"
    \n"},n.prototype.html=function(e){return e},n.prototype.heading=function(e,t,n){return"'+e+"\n"},n.prototype.hr=function(){return this.options.xhtml?"
    \n":"
    \n"},n.prototype.list=function(e,t){var n=t?"ol":"ul";return"<"+n+">\n"+e+"\n"},n.prototype.listitem=function(e){return"
  • "+e+"
  • \n"},n.prototype.paragraph=function(e){return"

    "+e+"

    \n"},n.prototype.table=function(e,t){return"\n\n"+e+"\n\n"+t+"\n
    \n"},n.prototype.tablerow=function(e){return"\n"+e+"\n"},n.prototype.tablecell=function(e,t){var n=t.header?"th":"td";return(t.align?"<"+n+' style="text-align:'+t.align+'">':"<"+n+">")+e+"\n"},n.prototype.strong=function(e){return""+e+""},n.prototype.em=function(e){return""+e+""},n.prototype.codespan=function(e){return""+e+""},n.prototype.br=function(){return this.options.xhtml?"
    ":"
    "},n.prototype.del=function(e){return""+e+""},n.prototype.link=function(e,t,n){if(this.options.sanitize){try{var i=decodeURIComponent(r(e)).replace(/[^\w:]/g,"").toLowerCase()}catch(e){return""}if(0===i.indexOf("javascript:")||0===i.indexOf("vbscript:")||0===i.indexOf("data:"))return""}var o='
    "},n.prototype.image=function(e,t,n){var i=''+n+'":">"},n.prototype.text=function(e){return e},i.parse=function(e,t,n){return new i(t,n).parse(e)},i.prototype.parse=function(e){this.inline=new t(e.links,this.options,this.renderer),this.tokens=e.reverse();for(var n="";this.next();)n+=this.tok();return n},i.prototype.next=function(){return this.token=this.tokens.pop()},i.prototype.peek=function(){return this.tokens[this.tokens.length-1]||0},i.prototype.parseText=function(){for(var e=this.token.text;"text"===this.peek().type;)e+="\n"+this.next().text;return this.inline.output(e)},i.prototype.tok=function(){switch(this.token.type){case"space":return"";case"hr":return this.renderer.hr();case"heading":return this.renderer.heading(this.inline.output(this.token.text),this.token.depth,this.token.text);case"code":return this.renderer.code(this.token.text,this.token.lang,this.token.escaped);case"table":var e,t,n,i,o="",r="";for(n="",e=0;e=0,i=p.indexOf("Macintosh")>=0,o=p.indexOf("Linux")>=0,a=!0,l=u=navigator.language}var f;!function(e){e[e.Web=0]="Web",e[e.Mac=1]="Mac",e[e.Linux=2]="Linux",e[e.Windows=3]="Windows"}(f=t.Platform||(t.Platform={}));var g=f.Web;s&&(i?g=f.Mac:n?g=f.Windows:o&&(g=f.Linux)),t.isWindows=n,t.isMacintosh=i,t.isLinux=o,t.isRootUser=r,t.isNative=s,t.isWeb=a,t.platform=g,t.language=l,t.locale=u;var m="object"==typeof self?self:global;t.globals=m,t.hasWebWorkerSupport=function(){return void 0!==m.Worker},t.setTimeout=m.setTimeout.bind(m),t.clearTimeout=m.clearTimeout.bind(m),t.setInterval=m.setInterval.bind(m),t.clearInterval=m.clearInterval.bind(m);!function(e){e[e.Windows=1]="Windows",e[e.Macintosh=2]="Macintosh",e[e.Linux=3]="Linux"}(t.OperatingSystem||(t.OperatingSystem={})),t.OS=i?2:n?1:3;!function(e){e[e.Unknown=0]="Unknown",e[e.Disabled=1]="Disabled",e[e.Enabled=2]="Enabled"}(t.AccessibilitySupport||(t.AccessibilitySupport={}))}),define(d[423],h([1,0]),function(e,t){"use strict";function n(e){return 65<=e&&e<=90}Object.defineProperty(t,"__esModule",{value:!0});/*! BEGIN THIRD PARTY */ /*! * string_score.js: String Scoring Algorithm 0.1.22 * * http://joshaven.com/string_score * https://github.com/joshaven/string_score * * Copyright (C) 2009-2014 Joshaven Potter * Special thanks to all of the contributors listed here https://github.com/joshaven/string_score * MIT License: http://opensource.org/licenses/MIT * * Date: Tue Mar 1 2011 * Updated: Tue Mar 10 2015 */ var i=["-","_"," ","/","\\","."];t.score=function(e,t,o){if(!e||!t)return 0;var r=e+t,s=o&&o[r];if("number"==typeof s)return s;for(var a=t.length,u=e.toLowerCase(),l=t.toLowerCase(),c=0,d=0,h=0;c0&&e.every(s)},t.validateConstraints=function(e,t){for(var n=Math.min(e.length,t.length),i=0;i=0)throw new Error("Cannot clone recursive data-structure");i.push(e);var u={};for(var l in e)c.call(e,l)&&(u[l]=r(e[l],t,i));return i.pop(),u}return e}function s(e,t,i){return void 0===i&&(i=!0),n.isObject(e)?(n.isObject(t)&&Object.keys(t).forEach(function(o){o in e?i&&(n.isObject(e[o])&&n.isObject(t[o])?s(e[o],t[o],i):e[o]=t[o]):e[o]=t[o]}),e):t}function a(e){for(var t=[],n=1;nthis.limit;)this.trim()},e.prototype.serialize=function(){var e={entries:[]};return this.map.forEach(function(t){e.entries.push({key:t.key,value:t.value})}),e},Object.defineProperty(e.prototype,"size",{get:function(){return this.map.size},enumerable:!0,configurable:!0}),e.prototype.set=function(e,t){if(this.map.has(e))return!1;var n={key:e,value:t};return this.push(n),this.size>this.limit&&this.trim(),!0},e.prototype.get=function(e){var t=this.map.get(e);return t?t.value:null},e.prototype.getOrSet=function(e,t){var n=this.get(e);return n||(this.set(e,t),t)},e.prototype.delete=function(e){var t=this.map.get(e);return t?(this.map.delete(e),t.next?t.next.prev=t.prev:this.head=t.prev,t.prev?t.prev.next=t.next:this.tail=t.next,t.value):null},e.prototype.has=function(e){return this.map.has(e)},e.prototype.clear=function(){this.map.clear(),this.head=null,this.tail=null},e.prototype.push=function(e){this.head&&(e.prev=this.head,this.head.next=e),this.tail||(this.tail=e),this.head=e,this.map.set(e.key,e)},e.prototype.trim=function(){if(this.tail)if(this.ratiot?1:0}function a(e){return e>=97&&e<=122}function u(e){return e>=65&&e<=90}function l(e){return a(e)||u(e)}function c(e,t,n){if(void 0===n&&(n=e.length),"string"!=typeof e||"string"!=typeof t)return!1;for(var i=0;i=11904&&e<=55215||e>=63744&&e<=64255||e>=65281&&e<=65374}Object.defineProperty(t,"__esModule",{value:!0}),t.empty="",t.isFalsyOrWhitespace=function(e){return!e||"string"!=typeof e||0===e.trim().length},t.pad=function(e,t,n){void 0===n&&(n="0");for(var i=""+e,o=[i],r=i.length;r=t.length?e:t[i]})},t.escape=function(e){return e.replace(/[<|>|&]/g,function(e){switch(e){case"<":return"<";case">":return">";case"&":return"&";default:return e}})},t.escapeRegExpCharacters=i,t.trim=function(e,t){return void 0===t&&(t=" "),r(o(e,t),t)},t.ltrim=o,t.rtrim=r,t.convertSimple2RegExpPattern=function(e){return e.replace(/[\-\\\{\}\+\?\|\^\$\.\,\[\]\(\)\#\s]/g,"\\$&").replace(/[\*]/g,".*")},t.stripWildcards=function(e){return e.replace(/\*/g,"")},t.startsWith=function(e,t){if(e.length0?e.indexOf(t,n)===n:0===n&&e===t},t.indexOfIgnoreCase=function(e,t,n){void 0===n&&(n=0);var o=e.indexOf(t,n);return o<0&&(n>0&&(e=e.substr(n)),t=i(t),o=e.search(new RegExp(t,"i"))),o},t.createRegExp=function(e,t,n){if(void 0===n&&(n={}),!e)throw new Error("Cannot create regex from empty string");t||(e=i(e)),n.wholeWord&&(/\B/.test(e.charAt(0))||(e="\\b"+e),/\B/.test(e.charAt(e.length-1))||(e+="\\b"));var o="";return n.global&&(o+="g"),n.matchCase||(o+="i"),n.multiline&&(o+="m"),new RegExp(e,o)},t.regExpLeadsToEndlessLoop=function(e){return"^"!==e.source&&"^$"!==e.source&&"$"!==e.source&&e.exec("")&&0===e.lastIndex},t.canNormalize="function"==typeof"".normalize;var f=/[^\u0000-\u0080]/,g=new n.BoundedMap(1e4);t.normalizeNFC=function(e){if(!t.canNormalize||!e)return e;var n=g.get(e);if(n)return n;var i;return i=f.test(e)?e.normalize("NFC"):e,g.set(e,i),i},t.firstNonWhitespaceIndex=function(e){for(var t=0,n=e.length;t=0;n--){var i=e.charCodeAt(n);if(32!==i&&9!==i)return n}return-1},t.compare=s,t.compareIgnoreCase=function(e,t){for(var n=Math.min(e.length,t.length),i=0;it.length?1:0},t.equalsIgnoreCase=function(e,t){return(e?e.length:0)===(t?t.length:0)&&c(e,t)},t.beginsWithIgnoreCase=function(e,t){var n=t.length;return!(t.length>e.length)&&c(e,t,n)},t.commonPrefixLength=function(e,t){var n,i=Math.min(e.length,t.length);for(n=0;n0;){if(d(e,o,n,t,0,i))return i;i-=1,o+=1}return 0},t.isHighSurrogate=function(e){return 55296<=e&&e<=56319},t.isLowSurrogate=function(e){return 56320<=e&&e<=57343};var m=/(?:[\u05BE\u05C0\u05C3\u05C6\u05D0-\u05F4\u0608\u060B\u060D\u061B-\u064A\u066D-\u066F\u0671-\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u0710\u0712-\u072F\u074D-\u07A5\u07B1-\u07EA\u07F4\u07F5\u07FA-\u0815\u081A\u0824\u0828\u0830-\u0858\u085E-\u08BD\u200F\uFB1D\uFB1F-\uFB28\uFB2A-\uFD3D\uFD50-\uFDFC\uFE70-\uFEFC]|\uD802[\uDC00-\uDD1B\uDD20-\uDE00\uDE10-\uDE33\uDE40-\uDEE4\uDEEB-\uDF35\uDF40-\uDFFF]|\uD803[\uDC00-\uDCFF]|\uD83A[\uDC00-\uDCCF\uDD00-\uDD43\uDD50-\uDFFF]|\uD83B[\uDC00-\uDEBB])/;t.containsRTL=function(e){return m.test(e)};var v=/(?:[\u231A\u231B\u23F0\u23F3\u2600-\u27BF\u2B50\u2B55]|\uD83C[\uDDE6-\uDDFF\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F\uDE80-\uDEF8]|\uD83E[\uDD00-\uDDE6])/;t.containsEmoji=function(e){return v.test(e)};var _=/^[\t\n\r\x20-\x7E]*$/;t.isBasicASCII=function(e){return _.test(e)},t.containsFullWidthCharacter=function(e){for(var t=0,n=e.length;tn)return 0;var o,r,s=[],a=[];for(o=0;o=0;r--)if((o+=i[r].length)>n){i.splice(0,r);break}return i.join(t.empty).replace(/^\s/,t.empty)};var y=/\x1B\x5B[12]?K/g,C=/\x1b\[\d+m/g,b=/\x1b\[0?m/g;t.removeAnsiEscapeCodes=function(e){return e&&(e=(e=(e=e.replace(y,"")).replace(C,"")).replace(b,"")),e},t.UTF8_BOM_CHARACTER=String.fromCharCode(65279),t.startsWithUTF8BOM=function(e){return e&&e.length>0&&65279===e.charCodeAt(0)},t.appendWithLimit=function(e,t,n){var i=e.length+t.length;return i>n&&(e="..."+e.substr(i-n)),t.length>n?e+=t.substr(t.length-n):e+=t,e},t.safeBtoa=function(e){return btoa(encodeURIComponent(e))},t.repeat=function(e,t){for(var n="",i=0;i0?[{start:0,end:t.length}]:[]:null}function s(e,t){var n=t.toLowerCase().indexOf(e.toLowerCase());return-1===n?null:[{start:n,end:n+e.length}]}function a(e,t){return u(e.toLowerCase(),t.toLowerCase(),0,0)}function u(e,t,n,i){if(n===e.length)return[];if(i===t.length)return null;if(e[n]===t[i]){var o=null;if(o=u(e,t,n+1,i+1))return f({start:i,end:i+1},o)}return u(e,t,n,i+1)}function l(e){return 97<=e&&e<=122}function c(e){return 65<=e&&e<=90}function d(e){return 48<=e&&e<=57}function h(e){return 32===e||9===e||10===e||13===e}function p(e){return l(e)||c(e)||d(e)}function f(e,t){return 0===t.length?t=[e]:e.end===t[0].start?t[0].start=e.start:t.unshift(e),t}function g(e,t){for(var n=t;n0&&!p(e.charCodeAt(n-1)))return n}return e.length}function m(e,t,n,i){if(n===e.length)return[];if(i===t.length)return null;if(e[n]!==t[i].toLowerCase())return null;var o=null,r=i+1;for(o=m(e,t,n+1,i+1);!o&&(r=g(t,r)).6}function y(e){var t=e.upperPercent,n=e.lowerPercent,i=e.alphaPercent,o=e.numericPercent;return n>.2&&t<.8&&i>.6&&o<.2}function C(e){for(var t=0,n=0,i=0,o=0,r=0;r60)return null;var n=v(t);if(!y(n)){if(!_(n))return null;t=t.toLowerCase()}for(var i=null,o=0;o0&&h(e.charCodeAt(n-1)))return n;return e.length}function E(){for(var e=[],t=[0],n=1;n<=100;n++)t.push(-n);for(n=0;n<=100;n++){var i=t.slice(0);i[0]=-n,e.push(i)}return e}function L(e,t,n,i,o){function r(e,t,n){for(void 0===n&&(n=" ");e.length100?100:e.length,o=t.length>100?100:t.length,r=0;for(void 0===n&&(n=i);ro)){for(var s=e.toLowerCase(),a=t.toLowerCase(),u=r,l=0;u1?1:d),f=I[u-1][l]+-1,g=I[u][l-1]+-1;g>=f?g>p?(I[u][l]=g,O[u][l]=4):g===p?(I[u][l]=g,O[u][l]=6):(I[u][l]=p,O[u][l]=2):f>p?(I[u][l]=f,O[u][l]=1):f===p?(I[u][l]=f,O[u][l]=3):(I[u][l]=p,O[u][l]=2),c=h}}if(R&&(console.log(L(I,e,i,t,o)),console.log(L(O,e,i,t,o)),console.log(L(D,e,i,t,o))),W.length=0,B=-100,V=r,N(i,o,0,new H,!1),0!==W.length)return[B,W[0].toArray()]}}}function N(e,t,n,i,o){if(!(W.length>=10||n<-25)){for(var r=0;e>V&&t>0;){var s=D[e][t],a=O[e][t];if(4===a)t-=1,o?n-=5:i.isEmpty()||(n-=1),o=!1,r=0;else{if(!(2&a))return;if(4&a&&N(e,t-1,i.isEmpty()?n:n-1,i.slice(),o),n+=s,e-=1,t-=1,i.unshift(t),o=!0,1===s){if(r+=1,e===V)return}else n+=1+r*(s-1),r=0}}(n-=t>=3?9:3*t)>B?(B=n,W.unshift(i)):W.push(i)}}function M(e,t){if(!(t+1>=e.length))return e.slice(0,t)+e[t+1]+e[t]+e.slice(t+2)}Object.defineProperty(t,"__esModule",{value:!0}),t.or=o,t.and=function(){for(var e=[],t=0;t0)&&".."!==g&&(h=-1===f?"":h.slice(0,f),d=!0)}else u(e,c,p,".")&&(a||h||p=65&&i<=90||i>=97&&i<=122)&&58===e.charCodeAt(1))return 47===(i=e.charCodeAt(2))||92===i?e.slice(0,2)+t:e.slice(0,2);var s=e.indexOf("://");if(-1!==s)for(s+=3;s=65&&t<=90||t>=97&&t<=122)&&e.length>2&&58===e.charCodeAt(1)){var n=e.charCodeAt(2);if(47===n||92===n)return!0}return!1}function d(e){return e&&47===e.charCodeAt(0)}Object.defineProperty(t,"__esModule",{value:!0}),t.sep="/",t.nativeSep=n.isWindows?"\\":"/",t.relative=function(e,r){for(var s=o.rtrim(a(e),t.sep),u=o.rtrim(a(r),t.sep),l=n.isLinux?s:s.toLowerCase(),c=n.isLinux?u:u.toLowerCase(),d=l.split(t.sep),h=c.split(t.sep),p=0,f=Math.min(d.length,h.length);p0){var o=e.charCodeAt(e.length-1);if(47!==o&&92!==o){var r=i.charCodeAt(0);47!==r&&92!==r&&(e+=t.sep)}}e+=i}return a(e)},t.isUNC=function(e){if(!n.isWindows)return!1;if(!e||e.length<5)return!1;var t=e.charCodeAt(0);if(92!==t)return!1;if(92!==(t=e.charCodeAt(1)))return!1;for(var i=2,o=i;i\|]/g:/[\\/]/g,g=/^(con|prn|aux|clock\$|nul|lpt[0-9]|com[0-9])$/i;t.isValidBasename=function(e){return!(!e||0===e.length||/^\s+$/.test(e)||(f.lastIndex=0,f.test(e)||n.isWindows&&g.test(e)||"."===e||".."===e||n.isWindows&&"."===e[e.length-1]||n.isWindows&&e.length!==e.trim().length))},t.isEqual=function(e,t,n){var i=e===t;return!n||i?i:!(!e||!t)&&o.equalsIgnoreCase(e,t)},t.isEqualOrParent=function(e,n,i){if(e===n)return!0;if(!e||!n)return!1;if(n.length>e.length)return!1;if(i){if(!o.beginsWithIgnoreCase(e,n))return!1;if(n.length===e.length)return!0;var r=n.length;return n.charAt(n.length-1)===t.nativeSep&&r--,e.charAt(r)===t.nativeSep}return n.charAt(n.length-1)!==t.nativeSep&&(n+=t.nativeSep),0===e.indexOf(n)},t.isAbsolute=function(e){return n.isWindows?c(e):d(e)},t.isAbsolute_win32=c,t.isAbsolute_posix=d}),define(d[229],h([1,0,423,9,45]),function(e,t,n,i,o){"use strict";function r(e,t){if(c){var n=e||"",i=t||"",o=c.compare(n,i);return d&&0===o&&n!==i?nr.length)return 1}return 0}Object.defineProperty(t,"__esModule",{value:!0});var c,d;t.setFileNameComparer=function(e){c=e,d=e.resolvedOptions().numeric},t.compareFileNames=r;var h=/^(.*?)(\.([^.]*))?$/;t.noIntlCompareFileNames=s,t.compareFileExtensions=function(e,t){if(c){var n=e?h.exec(e):[],i=t?h.exec(t):[],o=n[1]||"",r=n[3]||"",s=i[1]||"",u=i[3]||"",l=c.compare(r,u);if(0===l){if(d&&r!==u)return rp?-1:1;var f=i.getResourcePath(e),g=i.getResourcePath(t);if(f&&g){var m=n.score(f,o,s),v=n.score(g,o,s);if(m!==v)return m>v?-1:1}return a.length!==c.length?a.length1,c=void 0;if(c=o.isEqual(u.fsPath,e.fsPath,!i.isLinux)?"":o.normalize(r.ltrim(e.fsPath.substr(u.fsPath.length),o.nativeSep),!0),l){var d=o.basename(u.fsPath);c=c?o.join(d,c):d}return c}if(i.isWindows&&e.fsPath&&":"===e.fsPath[1])return o.normalize(e.fsPath.charAt(0).toUpperCase()+e.fsPath.slice(1),!0);var h=o.normalize(e.fsPath,!0);return!i.isWindows&&a&&(h=s(h,a.userHome)),h},t.tildify=s;var a="…",u="\\\\";t.shorten=function(e){for(var t=new Array(e.length),n=!1,i=0;i=0;h--){n=!1;for(var p=c.slice(h,h+d).join(o.nativeSep),f=0;!n&&f-1){var g=h+d===c.length,m=h>0&&e[f].indexOf(o.nativeSep)>-1?o.nativeSep+p:p,v=r.endsWith(e[f],m);n=!g||v}if(!n){var _="";(r.endsWith(c[0],":")||""!==l)&&(1===h&&(h=0,d++,p=c[0]+o.nativeSep+p),h>0&&(_=c[0]+o.nativeSep),_=l+_),h>0&&(_=_+a+o.nativeSep),_+=p,h+d0})}).map(function(e){return e.value}).join("")},t.mnemonicButtonLabel=function(e){return i.isWindows?e.replace(/&&/g,"&"):e.replace(/\(&&\w\)|&&/g,"")}}),function(){var e={};e["WinJS/Core/_WinJS"]={};var t=function(t,n,i){var o={},r=!1,s=n.map(function(t){return"exports"===t?(r=!0,o):e[t]}),a=i.apply({},s);e[t]=r?o:a};t("WinJS/Core/_Global",[],function(){"use strict";return"undefined"!=typeof window?window:"undefined"!=typeof self?self:"undefined"!=typeof global?global:{}}),t("WinJS/Core/_BaseCoreUtils",["WinJS/Core/_Global"],function(e){"use strict";return{hasWinRT:!!e.Windows,markSupportedForProcessing:function(e){return e.supportedForProcessing=!0,e},_setImmediate:e.setImmediate?e.setImmediate.bind(e):function(t){e.setTimeout(t,0)}}}),t("WinJS/Core/_WriteProfilerMark",["WinJS/Core/_Global"],function(e){"use strict";return e.msWriteProfilerMark||function(){}}),t("WinJS/Core/_Base",["WinJS/Core/_WinJS","WinJS/Core/_Global","WinJS/Core/_BaseCoreUtils","WinJS/Core/_WriteProfilerMark"],function(e,t,n,i){"use strict";function o(e,t,n){var i,o,r,s=Object.keys(t),a=Array.isArray(e);for(o=0,r=s.length;o"),r}var s=e;s.Namespace||(s.Namespace=Object.create(Object.prototype));var a={uninitialized:1,working:2,initialized:3};Object.defineProperties(s.Namespace,{defineWithParent:{value:r,writable:!0,enumerable:!0,configurable:!0},define:{value:function(e,n){return r(t,e,n)},writable:!0,enumerable:!0,configurable:!0},_lazy:{value:function(e){var t,n,o=a.uninitialized;return{setName:function(e){t=e},get:function(){switch(o){case a.initialized:return n;case a.uninitialized:o=a.working;try{i("WinJS.Namespace._lazy:"+t+",StartTM"),n=e()}finally{i("WinJS.Namespace._lazy:"+t+",StopTM"),o=a.uninitialized}return e=null,o=a.initialized,n;case a.working:throw"Illegal: reentrancy on initialization";default:throw"Illegal"}},set:function(e){switch(o){case a.working:throw"Illegal: reentrancy on initialization";default:o=a.initialized,n=e}},enumerable:!0,configurable:!0}},writable:!0,enumerable:!0,configurable:!0},_moduleDefine:{value:function(e,i,r){var s=[e],a=null;return i&&(a=n(t,i),s.push(a)),o(s,r,i||""),a},writable:!0,enumerable:!0,configurable:!0}})}(),function(){function t(e,t,i){return e=e||function(){},n.markSupportedForProcessing(e),t&&o(e.prototype,t),i&&o(e,i),e}e.Namespace.define("WinJS.Class",{define:t,derive:function(e,i,r,s){if(e){i=i||function(){};var a=e.prototype;return i.prototype=Object.create(a),n.markSupportedForProcessing(i),Object.defineProperty(i.prototype,"constructor",{value:i,writable:!0,configurable:!0,enumerable:!0}),r&&o(i.prototype,r),s&&o(i,s),i}return t(i,r,s)},mix:function(e){e=e||function(){};var t,n;for(t=1,n=arguments.length;t1)&&u.fire(e),s=null,a=0},n)})},onLastListenerRemove:function(){o.dispose()}});return u.event};var h=function(){function e(){this.buffers=[]}return e.prototype.wrapEvent=function(e){var t=this;return function(n,i,o){return e(function(e){var o=t.buffers[t.buffers.length-1];o?o.push(function(){return n.call(i,e)}):n.call(i,e)},void 0,o)}},e.prototype.bufferEvents=function(e){var t=[];this.buffers.push(t),e(),this.buffers.pop(),t.forEach(function(e){return e()})},e}();t.EventBufferer=h,t.mapEvent=a,t.filterEvent=u;var p=function(){function e(e){this._event=e}return Object.defineProperty(e.prototype,"event",{get:function(){return this._event},enumerable:!0,configurable:!0}),e.prototype.map=function(t){return new e(a(this._event,t))},e.prototype.filter=function(t){return new e(u(this._event,t))},e.prototype.on=function(e,t,n){return this._event(e,t,n)},e}();t.chain=function(e){return new p(e)},t.stopwatch=function(e){var t=(new Date).getTime();return a(s(e),function(e){return(new Date).getTime()-t})},t.buffer=function(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=[]),n=n.slice();var i=e(function(e){n?n.push(e):r.fire(e)}),o=function(){n.forEach(function(e){return r.fire(e)}),n=null},r=new c({onFirstListenerAdd:function(){i||(i=e(function(e){return r.fire(e)}))},onFirstListenerDidAdd:function(){n&&(t?setTimeout(o):o())},onLastListenerRemove:function(){i.dispose(),i=null}});return r.event},t.echo=function(e,t,n){void 0===t&&(t=!1),void 0===n&&(n=[]),n=n.slice(),e(function(e){n.push(e),o.fire(e)});var i=function(e,t){return n.forEach(function(n){return e.call(t,n)})},o=new c({onListenerDidAdd:function(e,n,o){t?setTimeout(function(){return i(n,o)}):i(n,o)}});return o.event}}),define(d[28],h([1,0,15,11]),function(e,t,n,i){"use strict";function o(){return s.INSTANCE.getZoomLevel()}function r(){return s.INSTANCE.getPixelRatio()}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(){this._zoomLevel=0,this._lastZoomLevelChangeTime=0,this._onDidChangeZoomLevel=new i.Emitter,this.onDidChangeZoomLevel=this._onDidChangeZoomLevel.event,this._zoomFactor=0,this._onDidChangeFullscreen=new i.Emitter,this.onDidChangeFullscreen=this._onDidChangeFullscreen.event,this._accessibilitySupport=0,this._onDidChangeAccessibilitySupport=new i.Emitter,this.onDidChangeAccessibilitySupport=this._onDidChangeAccessibilitySupport.event}return e.prototype.getZoomLevel=function(){return this._zoomLevel},e.prototype.getTimeSinceLastZoomLevelChanged=function(){return Date.now()-this._lastZoomLevelChangeTime},e.prototype.setZoomLevel=function(e,t){this._zoomLevel!==e&&(this._zoomLevel=e,this._lastZoomLevelChangeTime=t?0:Date.now(),this._onDidChangeZoomLevel.fire(this._zoomLevel))},e.prototype.getZoomFactor=function(){return this._zoomFactor},e.prototype.setZoomFactor=function(e){this._zoomFactor=e},e.prototype.getPixelRatio=function(){var e=document.createElement("canvas").getContext("2d");return(window.devicePixelRatio||1)/(e.webkitBackingStorePixelRatio||e.mozBackingStorePixelRatio||e.msBackingStorePixelRatio||e.oBackingStorePixelRatio||e.backingStorePixelRatio||1)},e.prototype.setFullscreen=function(e){this._fullscreen!==e&&(this._fullscreen=e,this._onDidChangeFullscreen.fire())},e.prototype.isFullscreen=function(){return this._fullscreen},e.prototype.setAccessibilitySupport=function(e){this._accessibilitySupport!==e&&(this._accessibilitySupport=e,this._onDidChangeAccessibilitySupport.fire())},e.prototype.getAccessibilitySupport=function(){return this._accessibilitySupport},e.INSTANCE=new e,e}();t.setZoomLevel=function(e,t){s.INSTANCE.setZoomLevel(e,t)},t.getZoomLevel=o,t.getTimeSinceLastZoomLevelChanged=function(){return s.INSTANCE.getTimeSinceLastZoomLevelChanged()},t.onDidChangeZoomLevel=function(e){return s.INSTANCE.onDidChangeZoomLevel(e)},t.getZoomFactor=function(){return s.INSTANCE.getZoomFactor()},t.setZoomFactor=function(e){s.INSTANCE.setZoomFactor(e)},t.getPixelRatio=r,t.setFullscreen=function(e){s.INSTANCE.setFullscreen(e)},t.isFullscreen=function(){return s.INSTANCE.isFullscreen()},t.onDidChangeFullscreen=function(e){return s.INSTANCE.onDidChangeFullscreen(e)},t.setAccessibilitySupport=function(e){s.INSTANCE.setAccessibilitySupport(e)},t.getAccessibilitySupport=function(){return s.INSTANCE.getAccessibilitySupport()},t.onDidChangeAccessibilitySupport=function(e){return s.INSTANCE.onDidChangeAccessibilitySupport(e)};var a=navigator.userAgent;t.isIE=a.indexOf("Trident")>=0,t.isEdge=a.indexOf("Edge/")>=0,t.isEdgeOrIE=t.isIE||t.isEdge,t.isOpera=a.indexOf("Opera")>=0,t.isFirefox=a.indexOf("Firefox")>=0,t.isWebKit=a.indexOf("AppleWebKit")>=0,t.isChrome=a.indexOf("Chrome")>=0,t.isSafari=-1===a.indexOf("Chrome")&&a.indexOf("Safari")>=0,t.isIPad=a.indexOf("iPad")>=0,t.isChromev56=a.indexOf("Chrome/56.")>=0&&-1===a.indexOf("Edge/"),t.supportsTranslate3d=!t.isFirefox,t.canUseTranslate3d=function(){if(!t.supportsTranslate3d)return!1;if(0!==o())return!1;if(t.isChromev56){var e=r();if(Math.floor(e)!==e)return!1}return!0}}),define(d[124],h([1,0,11]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.domEvent=function(e,t,i){var o=function(e){return r.fire(e)},r=new n.Emitter({onFirstListenerAdd:function(){e.addEventListener(t,o,i)},onLastListenerRemove:function(){e.removeEventListener(t,o,i)}});return r.event},t.stop=function(e){return n.mapEvent(e,function(e){return e.preventDefault(),e.stopPropagation(),e})}}),define(d[65],h([1,0,40,15,28]),function(e,t,n,i,o){"use strict";function r(e){if(e.charCode){var t=String.fromCharCode(e.charCode).toUpperCase();return n.KeyCodeUtils.fromString(t)}return s[e.keyCode]||0}Object.defineProperty(t,"__esModule",{value:!0});var s={};s[3]=7,s[8]=1,s[9]=2,s[13]=3,s[16]=4,s[17]=5,s[18]=6,s[19]=7,s[20]=8,s[27]=9,s[32]=10,s[33]=11,s[34]=12,s[35]=13,s[36]=14,s[37]=15,s[38]=16,s[39]=17,s[40]=18,s[45]=19,s[46]=20,s[48]=21,s[49]=22,s[50]=23,s[51]=24,s[52]=25,s[53]=26,s[54]=27,s[55]=28,s[56]=29,s[57]=30,s[65]=31,s[66]=32,s[67]=33,s[68]=34,s[69]=35,s[70]=36,s[71]=37,s[72]=38,s[73]=39,s[74]=40,s[75]=41,s[76]=42,s[77]=43,s[78]=44,s[79]=45,s[80]=46,s[81]=47,s[82]=48,s[83]=49,s[84]=50,s[85]=51,s[86]=52,s[87]=53,s[88]=54,s[89]=55,s[90]=56,s[93]=58,s[96]=93,s[97]=94,s[98]=95,s[99]=96,s[100]=97,s[101]=98,s[102]=99,s[103]=100,s[104]=101,s[105]=102,s[106]=103,s[107]=104,s[108]=105,s[109]=106,s[110]=107,s[111]=108,s[112]=59,s[113]=60,s[114]=61,s[115]=62,s[116]=63,s[117]=64,s[118]=65,s[119]=66,s[120]=67,s[121]=68,s[122]=69,s[123]=70,s[124]=71,s[125]=72,s[126]=73,s[127]=74,s[128]=75,s[129]=76,s[130]=77,s[144]=78,s[145]=79,s[186]=80,s[187]=81,s[188]=82,s[189]=83,s[190]=84,s[191]=85,s[192]=86,s[193]=110,s[194]=111,s[219]=87,s[220]=88,s[221]=89,s[222]=90,s[223]=91,s[226]=92,s[229]=109,o.isIE?s[91]=57:o.isFirefox?(s[59]=80,s[107]=81,s[109]=83,i.isMacintosh&&(s[224]=57)):o.isWebKit&&(s[91]=57,i.isMacintosh?s[93]=57:s[92]=57);var a=i.isMacintosh?256:2048,u=i.isMacintosh?2048:256,l=function(){function e(e){var t=e;this.browserEvent=t,this.target=t.target,this.ctrlKey=t.ctrlKey,this.shiftKey=t.shiftKey,this.altKey=t.altKey,this.metaKey=t.metaKey,this.keyCode=r(t),this.code=t.code,this.ctrlKey=this.ctrlKey||5===this.keyCode,this.altKey=this.altKey||6===this.keyCode,this.shiftKey=this.shiftKey||4===this.keyCode,this.metaKey=this.metaKey||57===this.keyCode,this._asKeybinding=this._computeKeybinding(),this._asRuntimeKeybinding=this._computeRuntimeKeybinding()}return e.prototype.preventDefault=function(){this.browserEvent&&this.browserEvent.preventDefault&&this.browserEvent.preventDefault()},e.prototype.stopPropagation=function(){this.browserEvent&&this.browserEvent.stopPropagation&&this.browserEvent.stopPropagation()},e.prototype.toKeybinding=function(){return this._asRuntimeKeybinding},e.prototype.equals=function(e){return this._asKeybinding===e},e.prototype._computeKeybinding=function(){var e=0;5!==this.keyCode&&4!==this.keyCode&&6!==this.keyCode&&57!==this.keyCode&&(e=this.keyCode);var t=0;return this.ctrlKey&&(t|=a),this.altKey&&(t|=512),this.shiftKey&&(t|=1024),this.metaKey&&(t|=u),t|=e},e.prototype._computeRuntimeKeybinding=function(){var e=0;return 5!==this.keyCode&&4!==this.keyCode&&6!==this.keyCode&&57!==this.keyCode&&(e=this.keyCode),new n.SimpleKeybinding(this.ctrlKey,this.shiftKey,this.altKey,this.metaKey,e)},e}();t.StandardKeyboardEvent=l}),define(d[47],h([1,0,15,28,148]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e){this.timestamp=Date.now(),this.browserEvent=e,this.leftButton=0===e.button,this.middleButton=1===e.button,this.rightButton=2===e.button,this.target=e.target,this.detail=e.detail||1,"dblclick"===e.type&&(this.detail=2),this.ctrlKey=e.ctrlKey,this.shiftKey=e.shiftKey,this.altKey=e.altKey,this.metaKey=e.metaKey,"number"==typeof e.pageX?(this.posx=e.pageX,this.posy=e.pageY):(this.posx=e.clientX+document.body.scrollLeft+document.documentElement.scrollLeft,this.posy=e.clientY+document.body.scrollTop+document.documentElement.scrollTop);var t=o.IframeUtils.getPositionOfChildWindowRelativeToAncestorWindow(self,e.view);this.posx-=t.left,this.posy-=t.top}return e.prototype.preventDefault=function(){this.browserEvent.preventDefault&&this.browserEvent.preventDefault()},e.prototype.stopPropagation=function(){this.browserEvent.stopPropagation&&this.browserEvent.stopPropagation()},e}();t.StandardMouseEvent=r;var s=function(e){function t(t){var n=e.call(this,t)||this;return n.dataTransfer=t.dataTransfer,n}return f(t,e),t}(r);t.DragMouseEvent=s;var a=function(e){function t(t){return e.call(this,t)||this}return f(t,e),t}(s);t.DropMouseEvent=a;var u=function(){function e(e,t,o){if(void 0===t&&(t=0),void 0===o&&(o=0),this.browserEvent=e||null,this.target=e?e.target||e.targetNode||e.srcElement:null,this.deltaY=o,this.deltaX=t,e){var r=e,s=e;void 0!==r.wheelDeltaY?this.deltaY=r.wheelDeltaY/120:void 0!==s.VERTICAL_AXIS&&s.axis===s.VERTICAL_AXIS&&(this.deltaY=-s.detail/3),void 0!==r.wheelDeltaX?i.isSafari&&n.isWindows?this.deltaX=-r.wheelDeltaX/120:this.deltaX=r.wheelDeltaX/120:void 0!==s.HORIZONTAL_AXIS&&s.axis===s.HORIZONTAL_AXIS&&(this.deltaX=-e.detail/3),0===this.deltaY&&0===this.deltaX&&e.wheelDelta&&(this.deltaY=e.wheelDelta/120)}}return e.prototype.preventDefault=function(){this.browserEvent&&this.browserEvent.preventDefault&&this.browserEvent.preventDefault()},e.prototype.stopPropagation=function(){this.browserEvent&&this.browserEvent.stopPropagation&&this.browserEvent.stopPropagation()},e}();t.StandardMouseWheelEvent=u}),define(d[123],h([1,0,11]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i,o=Object.freeze(function(e,t){var n=setTimeout(e.bind(t),0);return{dispose:function(){clearTimeout(n)}}});!function(e){e.None=Object.freeze({isCancellationRequested:!1,onCancellationRequested:n.default.None}),e.Cancelled=Object.freeze({isCancellationRequested:!0,onCancellationRequested:o})}(i=t.CancellationToken||(t.CancellationToken={}));var r=function(){function e(){this._isCancelled=!1}return e.prototype.cancel=function(){this._isCancelled||(this._isCancelled=!0,this._emitter&&(this._emitter.fire(void 0),this._emitter=void 0))},Object.defineProperty(e.prototype,"isCancellationRequested",{get:function(){return this._isCancelled},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onCancellationRequested",{get:function(){return this._isCancelled?o:(this._emitter||(this._emitter=new n.Emitter),this._emitter.event)},enumerable:!0,configurable:!0}),e}(),s=function(){function e(){}return Object.defineProperty(e.prototype,"token",{get:function(){return this._token||(this._token=new r),this._token},enumerable:!0,configurable:!0}),e.prototype.cancel=function(){this._token?this._token.cancel():this._token=i.Cancelled},e.prototype.dispose=function(){this.cancel()},e}();t.CancellationTokenSource=s}),define(d[18],h([1,0,10,15,7,123,3,11]),function(e,t,n,i,o,r,s,a){"use strict";function u(e){return e&&"function"==typeof e.then}function l(e,t){return new o.TPromise(function(i,o,r){e.done(function(e){try{t(e)}catch(e){n.onUnexpectedError(e)}i(e)},function(e){try{t(e)}catch(e){n.onUnexpectedError(e)}o(e)},function(e){r(e)})},function(){e.cancel()})}Object.defineProperty(t,"__esModule",{value:!0}),t.toThenable=function(e){return u(e)?e:o.TPromise.as(e)},t.asWinJsPromise=function(e){var t=new r.CancellationTokenSource;return new o.TPromise(function(n,i,r){var s=e(t.token);s instanceof o.TPromise?s.then(n,i,r):u(s)?s.then(n,i):n(s)},function(){t.cancel()})},t.wireCancellationToken=function(e,t,i){var r=e.onCancellationRequested(function(){return t.cancel()});return i&&(t=t.then(void 0,function(e){if(!n.isPromiseCanceledError(e))return o.TPromise.wrapError(e)})),l(t,function(){return r.dispose()})};var c=function(){function e(){this.activePromise=null,this.queuedPromise=null,this.queuedPromiseFactory=null}return e.prototype.queue=function(e){var t=this;if(this.activePromise){if(this.queuedPromiseFactory=e,!this.queuedPromise){var n=function(){t.queuedPromise=null;var e=t.queue(t.queuedPromiseFactory);return t.queuedPromiseFactory=null,e};this.queuedPromise=new o.TPromise(function(e,i,o){t.activePromise.then(n,n,o).done(e)},function(){t.activePromise.cancel()})}return new o.TPromise(function(e,n,i){t.queuedPromise.then(e,n,i)},function(){})}return this.activePromise=e(),new o.TPromise(function(e,n,i){t.activePromise.done(function(n){t.activePromise=null,e(n)},function(e){t.activePromise=null,n(e)},i)},function(){t.activePromise.cancel()})},e}();t.Throttler=c;var d=function(){function e(){this.current=o.TPromise.as(null)}return e.prototype.queue=function(e){return this.current=this.current.then(function(){return e()})},e}();t.SimpleThrottler=d;var h=function(){function e(e){this.defaultDelay=e,this.timeout=null,this.completionPromise=null,this.onSuccess=null,this.task=null}return e.prototype.trigger=function(e,t){var n=this;return void 0===t&&(t=this.defaultDelay),this.task=e,this.cancelTimeout(),this.completionPromise||(this.completionPromise=new o.TPromise(function(e){n.onSuccess=e},function(){}).then(function(){n.completionPromise=null,n.onSuccess=null;var e=n.task;return n.task=null,e()})),this.timeout=setTimeout(function(){n.timeout=null,n.onSuccess(null)},t),this.completionPromise},e.prototype.isTriggered=function(){return null!==this.timeout},e.prototype.cancel=function(){this.cancelTimeout(),this.completionPromise&&(this.completionPromise.cancel(),this.completionPromise=null)},e.prototype.cancelTimeout=function(){null!==this.timeout&&(clearTimeout(this.timeout),this.timeout=null)},e}();t.Delayer=h;var p=function(e){function t(t){var n=e.call(this,t)||this;return n.throttler=new c,n}return f(t,e),t.prototype.trigger=function(t,n){var i=this;return e.prototype.trigger.call(this,function(){return i.throttler.queue(t)},n)},t}(h);t.ThrottledDelayer=p;var g=function(e){function t(t,n){void 0===n&&(n=0);var i=e.call(this,t)||this;return i.minimumPeriod=n,i.periodThrottler=new c,i}return f(t,e),t.prototype.trigger=function(t,n){var i=this;return e.prototype.trigger.call(this,function(){return i.periodThrottler.queue(function(){return o.Promise.join([o.TPromise.timeout(i.minimumPeriod),t()]).then(function(e){return e[1]})})},n)},t}(p);t.PeriodThrottledDelayer=g;var m=function(){function e(){var e=this;this._value=new o.TPromise(function(t,n){e._completeCallback=t,e._errorCallback=n})}return Object.defineProperty(e.prototype,"value",{get:function(){return this._value},enumerable:!0,configurable:!0}),e.prototype.complete=function(e){this._completeCallback(e)},e.prototype.error=function(e){this._errorCallback(e)},e}();t.PromiseSource=m;var v=function(e){function t(t){var i,o,r,s=this;return s=e.call(this,function(e,t,n){i=e,o=t,r=n},function(){o(n.canceled())})||this,t.then(i,o,r),s}return f(t,e),t}(o.TPromise);t.ShallowCancelThenPromise=v,t.always=l,t.sequence=function(e){function t(){return e.length?e.pop()():null}function n(e){void 0!==e&&null!==e&&i.push(e);var r=t();return r?r.then(n):o.TPromise.as(i)}var i=[];return e=e.reverse(),o.TPromise.as(null).then(n)},t.first=function(e,t){void 0===t&&(t=function(e){return!!e}),e=e.reverse().slice();var n=function(){return 0===e.length?o.TPromise.as(null):e.pop()().then(function(e){return t(e)?o.TPromise.as(e):n()})};return n()};var _=function(){function e(e){this.maxDegreeOfParalellism=e,this.outstandingPromises=[],this.runningPromises=0,this._onFinished=new a.Emitter}return Object.defineProperty(e.prototype,"onFinished",{get:function(){return this._onFinished.event},enumerable:!0,configurable:!0}),e.prototype.queue=function(e){var t=this;return new o.TPromise(function(n,i,o){t.outstandingPromises.push({factory:e,c:n,e:i,p:o}),t.consume()})},e.prototype.consume=function(){for(var e=this;this.outstandingPromises.length&&this.runningPromises0?this.consume():this._onFinished.fire()},e.prototype.dispose=function(){this._onFinished.dispose()},e}();t.Limiter=_;var y=function(e){function t(){return e.call(this,1)||this}return f(t,e),t}(_);t.Queue=y,t.setDisposableTimeout=function(e,t){for(var n=[],i=2;i0&&this._emitToBulkListeners(e);for(var t=0,n=e.length;t0;){var n=this._emitQueue.shift();o(n.target,n.arg)}},t}(s);t.OrderGuaranteeEventEmitter=u}),define(d[4],h([1,0,7,18,10,38,3,29,28,65,47]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";function d(e,t,n,i){return new N(e,t,n,i)}function h(e){return function(t){return e(new c.StandardMouseEvent(t))}}function p(e){return function(t){return e(new l.StandardKeyboardEvent(t))}}function g(e){return document.defaultView.getComputedStyle(e,null)}function m(e,t,n){var i=g(e),o="0";return i&&(o=i.getPropertyValue?i.getPropertyValue(t):i.getAttribute(n)),O(e,o)}function v(e){for(var t=e.offsetParent,n=e.offsetTop,i=e.offsetLeft;null!==(e=e.parentNode)&&e!==document.body&&e!==document.documentElement;){n-=e.scrollTop;var o=g(e);o&&(i-="rtl"!==o.direction?e.scrollLeft:-e.scrollLeft),e===t&&(i+=R.getBorderLeftWidth(e),n+=R.getBorderTopWidth(e),n+=e.offsetTop,i+=e.offsetLeft,t=e.offsetParent)}return{left:i,top:n}}function _(e){var t=R.getMarginLeft(e)+R.getMarginRight(e);return e.offsetWidth+t}function y(e){var t=R.getMarginLeft(e)+R.getMarginRight(e);return e.scrollWidth+t}function C(e,t){if(null===e)return 0;var n=v(e),i=v(t);return n.left-i.left}function b(e){void 0===e&&(e=document.getElementsByTagName("head")[0]);var t=document.createElement("style");return t.type="text/css",t.media="screen",e.appendChild(t),t}function w(e){return e&&e.sheet&&e.sheet.rules?e.sheet.rules:e&&e.sheet&&e.sheet.cssRules?e.sheet.cssRules:[]}function S(e,t){for(;e;){if(e instanceof HTMLElement&&e.hasAttribute(t))return e;e=e.parentNode}return null}Object.defineProperty(t,"__esModule",{value:!0}),t.clearNode=function(e){for(;e.firstChild;)e.removeChild(e.firstChild)},t.safeStringifyDOMAware=function(e){var t=[];return JSON.stringify(e,function(e,n){if(n instanceof Element)return"[Element]";if(a.isObject(n)||Array.isArray(n)){if(-1!==t.indexOf(n))return"[Circular]";t.push(n)}return n})},t.isInDOM=function(e){for(;e;){if(e===document.body)return!0;e=e.parentNode}return!1};var E=new(function(){function e(){}return e.prototype._findClassName=function(e,t){var n=e.className;if(n){t=t.trim();var i=n.length,o=t.length;if(0!==o)if(i=0;){if(r=s+o,(0===s||32===n.charCodeAt(s-1))&&32===n.charCodeAt(r))return this._lastStart=s,void(this._lastEnd=r+1);if(s>0&&32===n.charCodeAt(s-1)&&r===i)return this._lastStart=s-1,void(this._lastEnd=r);if(0===s&&r===i)return this._lastStart=0,void(this._lastEnd=r)}this._lastStart=-1}else this._lastStart=-1}else this._lastStart=-1},e.prototype.hasClass=function(e,t){return this._findClassName(e,t),-1!==this._lastStart},e.prototype.addClass=function(e,t){e.className?(this._findClassName(e,t),-1===this._lastStart&&(e.className=e.className+" "+t)):e.className=t},e.prototype.removeClass=function(e,t){this._findClassName(e,t),-1!==this._lastStart&&(e.className=e.className.substring(0,this._lastStart)+e.className.substring(this._lastEnd))},e.prototype.toggleClass=function(e,t,n){this._findClassName(e,t),-1===this._lastStart||void 0!==n&&n||this.removeClass(e,t),-1!==this._lastStart||void 0!==n&&!n||this.addClass(e,t)},e}()),L=new(function(){function e(){}return e.prototype.hasClass=function(e,t){return t&&e.classList&&e.classList.contains(t)},e.prototype.addClass=function(e,t){t&&e.classList&&e.classList.add(t)},e.prototype.removeClass=function(e,t){t&&e.classList&&e.classList.remove(t)},e.prototype.toggleClass=function(e,t,n){e.classList&&e.classList.toggle(t,n)},e}()),x=u.isIE?E:L;t.hasClass=x.hasClass.bind(x),t.addClass=x.addClass.bind(x),t.removeClass=x.removeClass.bind(x),t.toggleClass=x.toggleClass.bind(x);var N=function(){function e(e,t,n,i){this._node=e,this._type=t,this._handler=n,this._useCapture=i||!1,this._node.addEventListener(this._type,this._handler,this._useCapture)}return e.prototype.dispose=function(){this._handler&&(this._node.removeEventListener(this._type,this._handler,this._useCapture),this._node=null,this._handler=null)},e}();t.addDisposableListener=d,t.addStandardDisposableListener=function(e,t,n,i){var o=n;return"click"===t||"mousedown"===t?o=h(n):"keydown"!==t&&"keypress"!==t&&"keyup"!==t||(o=p(n)),d(e,t,o,i)},t.addDisposableNonBubblingMouseOutListener=function(e,t){return d(e,"mouseout",function(n){for(var i=n.relatedTarget||n.toElement;i&&i!==e;)i=i.parentNode;i!==e&&t(n)})};var M=function(){var e=self.requestAnimationFrame||self.msRequestAnimationFrame||self.webkitRequestAnimationFrame||self.mozRequestAnimationFrame||self.oRequestAnimationFrame,t=self.cancelAnimationFrame||self.cancelRequestAnimationFrame||self.msCancelAnimationFrame||self.msCancelRequestAnimationFrame||self.webkitCancelAnimationFrame||self.webkitCancelRequestAnimationFrame||self.mozCancelAnimationFrame||self.mozCancelRequestAnimationFrame||self.oCancelAnimationFrame||self.oCancelRequestAnimationFrame,n=!!e,i=e||function(e){return setTimeout(function(){return e((new Date).getTime())},0)},o=t||function(e){};return{isNative:n,request:function(e){return i(e)},cancel:function(e){return o(e)}}}(),T=function(){function e(e,t){this._runner=e,this.priority=t,this._canceled=!1}return e.prototype.dispose=function(){this._canceled=!0},e.prototype.execute=function(){if(!this._canceled)try{this._runner()}catch(e){o.onUnexpectedError(e)}},e.sort=function(e,t){return t.priority-e.priority},e}();!function(){var e=[],n=null,i=!1,o=!1,r=function(){for(i=!1,n=e,e=[],o=!0;n.length>0;)n.sort(T.sort),n.shift().execute();o=!1};t.scheduleAtNextAnimationFrame=function(t,n){void 0===n&&(n=0);var o=new T(t,n);return e.push(o),i||(i=!0,M.request(r)),o},t.runAtThisOrScheduleAtNextAnimationFrame=function(e,i){if(o){var r=new T(e,i);return n.push(r),r}return t.scheduleAtNextAnimationFrame(e,i)}}();var k=16,I=function(e,t){return t},D=function(e){function t(t,n,o,r,s){void 0===r&&(r=I),void 0===s&&(s=k);var a=e.call(this)||this,u=null,l=0,c=a._register(new i.TimeoutTimer),h=function(){l=(new Date).getTime(),o(u),u=null};return a._register(d(t,n,function(e){u=r(u,e);var t=(new Date).getTime()-l;t>=s?(c.cancel(),h()):c.setIfNotSet(h,s-t)})),a}return f(t,e),t}(s.Disposable);t.addDisposableThrottledListener=function(e,t,n,i,o){return new D(e,t,n,i,o)},t.getComputedStyle=g;var O=function(e,t){return parseFloat(t)||0},R={getBorderLeftWidth:function(e){return m(e,"border-left-width","borderLeftWidth")},getBorderTopWidth:function(e){return m(e,"border-top-width","borderTopWidth")},getBorderRightWidth:function(e){return m(e,"border-right-width","borderRightWidth")},getBorderBottomWidth:function(e){return m(e,"border-bottom-width","borderBottomWidth")},getPaddingLeft:function(e){return m(e,"padding-left","paddingLeft")},getPaddingTop:function(e){return m(e,"padding-top","paddingTop")},getPaddingRight:function(e){return m(e,"padding-right","paddingRight")},getPaddingBottom:function(e){return m(e,"padding-bottom","paddingBottom")},getMarginLeft:function(e){return m(e,"margin-left","marginLeft")},getMarginTop:function(e){return m(e,"margin-top","marginTop")},getMarginRight:function(e){return m(e,"margin-right","marginRight")},getMarginBottom:function(e){return m(e,"margin-bottom","marginBottom")},__commaSentinel:!1};t.getTopLeftOffset=v,t.getDomNodePagePosition=function(e){var n=e.getBoundingClientRect();return{left:n.left+t.StandardWindow.scrollX,top:n.top+t.StandardWindow.scrollY,width:n.width,height:n.height}},t.StandardWindow=new(function(){function e(){}return Object.defineProperty(e.prototype,"scrollX",{get:function(){return"number"==typeof window.scrollX?window.scrollX:document.body.scrollLeft+document.documentElement.scrollLeft},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"scrollY",{get:function(){return"number"==typeof window.scrollY?window.scrollY:document.body.scrollTop+document.documentElement.scrollTop},enumerable:!0,configurable:!0}),e}()),t.getContentWidth=function(e){var t=R.getBorderLeftWidth(e)+R.getBorderRightWidth(e),n=R.getPaddingLeft(e)+R.getPaddingRight(e);return e.offsetWidth-t-n},t.getTotalWidth=_,t.getTotalScrollWidth=y,t.getContentHeight=function(e){var t=R.getBorderTopWidth(e)+R.getBorderBottomWidth(e),n=R.getPaddingTop(e)+R.getPaddingBottom(e);return e.offsetHeight-t-n},t.getTotalHeight=function(e){var t=R.getMarginTop(e)+R.getMarginBottom(e);return e.offsetHeight+t},t.getLargestChildWidth=function(e,t){var n=t.map(function(t){return Math.max(y(t),_(t))+C(t,e)||0});return Math.max.apply(Math,n)},t.isAncestor=function(e,t){for(;e;){if(e===t)return!0;e=e.parentNode}return!1},t.findParentWithClass=function(e,n,i){for(;e;){if(t.hasClass(e,n))return e;if(i&&t.hasClass(e,i))return null;e=e.parentNode}return null},t.createStyleSheet=b;var P=b();t.createCSSRule=function(e,t,n){void 0===n&&(n=P),n&&t&&n.sheet.insertRule(e+"{"+t+"}",0)},t.getCSSRule=function(e,t){if(void 0===t&&(t=P),!t)return null;for(var n=w(t),i=0;i=0;o--)t.sheet.deleteRule(i[o])}},t.isHTMLElement=function(e){return"object"==typeof HTMLElement?e instanceof HTMLElement:e&&"object"==typeof e&&1===e.nodeType&&"string"==typeof e.nodeName},t.EventType={CLICK:"click",AUXCLICK:"auxclick",DBLCLICK:"dblclick",MOUSE_UP:"mouseup",MOUSE_DOWN:"mousedown",MOUSE_OVER:"mouseover",MOUSE_MOVE:"mousemove",MOUSE_OUT:"mouseout",CONTEXT_MENU:"contextmenu",WHEEL:"wheel",KEY_DOWN:"keydown",KEY_PRESS:"keypress",KEY_UP:"keyup",LOAD:"load",UNLOAD:"unload",ABORT:"abort",ERROR:"error",RESIZE:"resize",SCROLL:"scroll",SELECT:"select",CHANGE:"change",SUBMIT:"submit",RESET:"reset",FOCUS:"focus",BLUR:"blur",INPUT:"input",STORAGE:"storage",DRAG_START:"dragstart",DRAG:"drag",DRAG_ENTER:"dragenter",DRAG_LEAVE:"dragleave",DRAG_OVER:"dragover",DROP:"drop",DRAG_END:"dragend",ANIMATION_START:u.isWebKit?"webkitAnimationStart":"animationstart",ANIMATION_END:u.isWebKit?"webkitAnimationEnd":"animationend",ANIMATION_ITERATION:u.isWebKit?"webkitAnimationIteration":"animationiteration"},t.EventHelper={stop:function(e,t){e.preventDefault?e.preventDefault():e.returnValue=!1,t&&(e.stopPropagation?e.stopPropagation():e.cancelBubble=!0)}},t.saveParentsScrollTop=function(e){for(var t=[],n=0;e&&e.nodeType===e.ELEMENT_NODE;n++)t[n]=e.scrollTop,e=e.parentNode;return t},t.restoreParentsScrollTop=function(e,t){for(var n=0;e&&e.nodeType===e.ELEMENT_NODE;n++)e.scrollTop!==t[n]&&(e.scrollTop=t[n]),e=e.parentNode};var A=function(e){function n(n){var i=e.call(this)||this,o=!1,s=!1;i._eventEmitter=i._register(new r.EventEmitter);return i._register(d(n,t.EventType.FOCUS,function(e){s=!1,o||(o=!0,i._eventEmitter.emit("focus",{}))},!0)),i._register(d(n,t.EventType.BLUR,function(e){o&&(s=!0,window.setTimeout(function(){s&&(s=!1,o=!1,i._eventEmitter.emit("blur",{}))},0))},!0)),i}return f(n,e),n.prototype.addFocusListener=function(e){return this._eventEmitter.addListener("focus",e)},n.prototype.addBlurListener=function(e){return this._eventEmitter.addListener("blur",e)},n}(s.Disposable);t.trackFocus=function(e){return new A(e)},t.append=function(e){for(var t=[],n=1;n0&&(t instanceof Node?n.push(t.cloneNode()):n.push(document.createTextNode(t))),n.push(e)}),n},t.show=function(){for(var e=[],t=0;t0},t.prototype.startMonitoring=function(e,t,n){var s=this;if(!this.isMonitoring()){this.mouseMoveEventMerger=e,this.mouseMoveCallback=t,this.onStopCallback=n;for(var a=o.IframeUtils.getSameOriginWindowChain(),u=0;u"},h.link=function(e,t,n){return e===n&&(n=s.removeMarkdownEscapes(n)),t=s.removeMarkdownEscapes(t),!(e=s.removeMarkdownEscapes(e))||e.match(/^data:|javascript:/i)?n:''+n+""},h.paragraph=function(e){return"

    "+e+"

    "},t.codeBlockRenderer&&(h.code=function(e,n){var s=t.codeBlockRenderer(n,e);if("string"==typeof s)return s;if(r.TPromise.is(s)){var a=i.defaultGenerator.nextId();return r.TPromise.join([s,d]).done(function(e){var t=e[0],n=c.querySelector('div[data-code="'+a+'"]');n&&(n.innerHTML=t)},function(e){}),'
    '+o.escape(e)+"
    "}return e}),t.actionCallback&&n.addStandardDisposableListener(c,"click",function(e){if("A"===e.target.tagName){var n=e.target.dataset.href;n&&t.actionCallback(n,e)}}),c.innerHTML=a.marked(e,{sanitize:!0,renderer:h}),l(),c}function c(e,t,i){var o;if(2===t.type)o=document.createTextNode(t.content);else if(3===t.type)o=document.createElement("b");else if(4===t.type)o=document.createElement("i");else if(5===t.type){var r=document.createElement("a");r.href="#",n.addStandardDisposableListener(r,"click",function(e){i(String(t.index),e)}),o=r}else 7===t.type?o=document.createElement("br"):1===t.type&&(o=e);e!==o&&e.appendChild(o),Array.isArray(t.children)&&t.children.forEach(function(e){c(o,e,i)})}function d(e){for(var t={type:1,children:[]},n=0,i=t,o=[],r=new g(e);!r.eos();){var s=r.next(),a="\\"===s&&0!==p(r.peek());if(a&&(s=r.next()),!a&&h(s)&&s===r.peek()){r.advance(),2===i.type&&(i=o.pop());var u=p(s);if(i.type===u||5===i.type&&6===u)i=o.pop();else{var l={type:u,children:[]};5===u&&(l.index=n,n++),i.children.push(l),o.push(i),i=l}}else if("\n"===s)2===i.type&&(i=o.pop()),i.children.push({type:7});else if(2!==i.type){var c={type:2,content:s};i.children.push(c),o.push(i),i=c}else i.content+=s}return 2===i.type&&(i=o.pop()),o.length,t}function h(e){return 0!==p(e)}function p(e){switch(e){case"*":return 3;case"_":return 4;case"[":return 5;case"]":return 6;default:return 0}}Object.defineProperty(t,"__esModule",{value:!0}),t.renderMarkedString=function(e,t){void 0===t&&(t={});var n;return n="string"==typeof e?e:"```"+e.language+"\n"+e.value+"\n```",l(n,t)},t.renderText=function(e,t){void 0===t&&(t={});var n=u(t);return n.textContent=e,n},t.renderFormattedText=function(e,t){void 0===t&&(t={});var n=u(t);return c(n,d(e),t.actionCallback),n},t.renderMarkdown=l;var f,g=function(){function e(e){this.source=e,this.index=0}return e.prototype.eos=function(){return this.index>=this.source.length},e.prototype.next=function(){var e=this.peek();return this.advance(),e},e.prototype.peek=function(){return this.source[this.index]},e.prototype.advance=function(){this.index++},e}();!function(e){e[e.Invalid=0]="Invalid",e[e.Root=1]="Root",e[e.Text=2]="Text",e[e.Bold=3]="Bold",e[e.Italics=4]="Italics",e[e.Action=5]="Action",e[e.ActionClose=6]="ActionClose",e[e.NewLine=7]="NewLine"}(f||(f={}))}),define(d[74],h([1,0,33,3,4]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r;!function(e){e.Tap="-monaco-gesturetap",e.Change="-monaco-gesturechange",e.Start="-monaco-gesturestart",e.End="-monaco-gesturesend",e.Contextmenu="-monaco-gesturecontextmenu"}(r=t.EventType||(t.EventType={}));var s=function(){function e(e){this.callOnTarget=[],this.activeTouches={},this.target=e,this.handle=null}return e.prototype.dispose=function(){this.target=null,this.handle&&(this.handle.dispose(),this.handle=null)},Object.defineProperty(e.prototype,"target",{set:function(e){var t=this;this.callOnTarget=i.dispose(this.callOnTarget),this.activeTouches={},this.targetElement=e,this.targetElement&&(this.callOnTarget.push(o.addDisposableListener(this.targetElement,"touchstart",function(e){return t.onTouchStart(e)})),this.callOnTarget.push(o.addDisposableListener(this.targetElement,"touchend",function(e){return t.onTouchEnd(e)})),this.callOnTarget.push(o.addDisposableListener(this.targetElement,"touchmove",function(e){return t.onTouchMove(e)})))},enumerable:!0,configurable:!0}),e.newGestureEvent=function(e){var t=document.createEvent("CustomEvent");return t.initEvent(e,!1,!0),t},e.prototype.onTouchStart=function(t){var n=Date.now();t.preventDefault(),this.handle&&(this.handle.dispose(),this.handle=null);for(var i=0,o=t.targetTouches.length;i=e.HOLD_DELAY&&Math.abs(l.initialPageX-n.tail(l.rollingPageX))<30&&Math.abs(l.initialPageY-n.tail(l.rollingPageY))<30){var d=e.newGestureEvent(r.Contextmenu);d.initialTarget=l.initialTarget,d.pageX=n.tail(l.rollingPageX),d.pageY=n.tail(l.rollingPageY),this.targetElement.dispatchEvent(d)}else if(1===o){var h=n.tail(l.rollingPageX),p=n.tail(l.rollingPageY),f=n.tail(l.rollingTimestamps)-l.rollingTimestamps[0],g=h-l.rollingPageX[0],m=p-l.rollingPageY[0];this.inertia(i,Math.abs(g)/f,g>0?1:-1,h,Math.abs(m)/f,m>0?1:-1,p)}delete this.activeTouches[u.identifier]}else console.warn("move of an UNKNOWN touch",u)}},e.prototype.inertia=function(t,n,i,s,a,u,l){var c=this;this.handle=o.scheduleAtNextAnimationFrame(function(){var o=Date.now(),d=o-t,h=0,p=0,f=!0;n+=e.SCROLL_FRICTION*d,a+=e.SCROLL_FRICTION*d,n>0&&(f=!1,h=i*n*d),a>0&&(f=!1,p=u*a*d);var g=e.newGestureEvent(r.Change);g.translationX=h,g.translationY=p,c.targetElement.dispatchEvent(g),f||c.inertia(o,n,i,s+h,a,u,l+p)})},e.prototype.onTouchMove=function(t){var i=Date.now();t.preventDefault(),t.stopPropagation();for(var o=0,s=t.changedTouches.length;o3&&(u.rollingPageX.shift(),u.rollingPageY.shift(),u.rollingTimestamps.shift()),u.rollingPageX.push(a.pageX),u.rollingPageY.push(a.pageY),u.rollingTimestamps.push(i)}else console.warn("end of an UNKNOWN touch",a)}},e.HOLD_DELAY=700,e.SCROLL_FRICTION=-.005,e}();t.Gesture=s}),define(d[110],h([1,0,9,4,26,512]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e){this.domNode=document.createElement("span"),this.domNode.className="monaco-highlighted-label",this.didEverRender=!1,e.appendChild(this.domNode)}return Object.defineProperty(e.prototype,"element",{get:function(){return this.domNode},enumerable:!0,configurable:!0}),e.prototype.set=function(e,t){void 0===t&&(t=[]),e||(e=""),this.didEverRender&&this.text===e&&o.equals(this.highlights,t)||(Array.isArray(t)||(t=[]),this.text=e,this.highlights=t,this.render())},e.prototype.render=function(){i.clearNode(this.domNode);for(var e,t=[],o=0,s=0;s"),t.push(r.expand(n.escape(this.text.substring(o,e.start)))),t.push(""),o=e.end),t.push(''),t.push(r.expand(n.escape(this.text.substring(e.start,e.end)))),t.push(""),o=e.end);o"),t.push(r.expand(n.escape(this.text.substring(o)))),t.push("")),this.domNode.innerHTML=t.join(""),this.didEverRender=!0},e.prototype.dispose=function(){this.text=null,this.highlights=null},e}();t.HighlightedLabel=s}),define(d[416],h([1,0,4]),function(e,t,n){"use strict";function i(e){try{e.parentElement.removeChild(e)}catch(e){}}Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e){this.renderers=e,this.cache=Object.create(null)}return e.prototype.alloc=function(e){var t=this.getTemplateCache(e).pop();if(!t){var i=n.$(".monaco-list-row");t={domNode:i,templateId:e,templateData:this.renderers[e].renderTemplate(i)}}return t},e.prototype.release=function(e){e&&this.releaseRow(e)},e.prototype.releaseRow=function(e){var t=e.domNode,o=e.templateId;n.removeClass(t,"scrolling"),i(t),this.getTemplateCache(o).push(e)},e.prototype.getTemplateCache=function(e){return this.cache[e]||(this.cache[e]=[])},e.prototype.garbageCollect=function(){var e=this;this.cache&&Object.keys(this.cache).forEach(function(t){e.cache[t].forEach(function(n){e.renderers[t].disposeTemplate(n.templateData),n.domNode=null,n.templateData=null}),delete e.cache[t]})},e.prototype.dispose=function(){this.garbageCollect(),this.cache=null,this.renderers=null},e}();t.RowCache=o}),define(d[41],h([1,0,3,47,65,4]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.onclick=function(e,t){this._register(r.addDisposableListener(e,r.EventType.CLICK,function(e){return t(new i.StandardMouseEvent(e))}))},t.prototype.onmousedown=function(e,t){this._register(r.addDisposableListener(e,r.EventType.MOUSE_DOWN,function(e){return t(new i.StandardMouseEvent(e))}))},t.prototype.onmouseover=function(e,t){this._register(r.addDisposableListener(e,r.EventType.MOUSE_OVER,function(e){return t(new i.StandardMouseEvent(e))}))},t.prototype.onnonbubblingmouseout=function(e,t){this._register(r.addDisposableNonBubblingMouseOutListener(e,function(e){return t(new i.StandardMouseEvent(e))}))},t.prototype.onkeydown=function(e,t){this._register(r.addDisposableListener(e,r.EventType.KEY_DOWN,function(e){return t(new o.StandardKeyboardEvent(e))}))},t.prototype.onkeyup=function(e,t){this._register(r.addDisposableListener(e,r.EventType.KEY_UP,function(e){return t(new o.StandardKeyboardEvent(e))}))},t.prototype.oninput=function(e,t){this._register(r.addDisposableListener(e,r.EventType.INPUT,t))},t.prototype.onblur=function(e,t){this._register(r.addDisposableListener(e,r.EventType.BLUR,t))},t.prototype.onfocus=function(e,t){this._register(r.addDisposableListener(e,r.EventType.FOCUS,t))},t.prototype.onchange=function(e,t){this._register(r.addDisposableListener(e,r.EventType.CHANGE,t))},t}(n.Disposable);t.Widget=s}),define(d[117],h([1,0,83,41,18]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ARROW_IMG_SIZE=11;var r=function(e){function i(i){var r=e.call(this)||this;return r._onActivate=i.onActivate,r.bgDomNode=document.createElement("div"),r.bgDomNode.className="arrow-background",r.bgDomNode.style.position="absolute",r.bgDomNode.style.width=i.bgWidth+"px",r.bgDomNode.style.height=i.bgHeight+"px",void 0!==i.top&&(r.bgDomNode.style.top="0px"),void 0!==i.left&&(r.bgDomNode.style.left="0px"),void 0!==i.bottom&&(r.bgDomNode.style.bottom="0px"),void 0!==i.right&&(r.bgDomNode.style.right="0px"),r.domNode=document.createElement("div"),r.domNode.className=i.className,r.domNode.style.position="absolute",r.domNode.style.width=t.ARROW_IMG_SIZE+"px",r.domNode.style.height=t.ARROW_IMG_SIZE+"px",void 0!==i.top&&(r.domNode.style.top=i.top+"px"),void 0!==i.left&&(r.domNode.style.left=i.left+"px"),void 0!==i.bottom&&(r.domNode.style.bottom=i.bottom+"px"),void 0!==i.right&&(r.domNode.style.right=i.right+"px"),r._mouseMoveMonitor=r._register(new n.GlobalMouseMoveMonitor),r.onmousedown(r.bgDomNode,function(e){return r._arrowMouseDown(e)}),r.onmousedown(r.domNode,function(e){return r._arrowMouseDown(e)}),r._mousedownRepeatTimer=r._register(new o.IntervalTimer),r._mousedownScheduleRepeatTimer=r._register(new o.TimeoutTimer),r}return f(i,e),i.prototype._arrowMouseDown=function(e){var t=this;this._onActivate(),this._mousedownRepeatTimer.cancel(),this._mousedownScheduleRepeatTimer.cancelAndSet(function(){t._mousedownRepeatTimer.cancelAndSet(function(){return t._onActivate()},1e3/24)},200),this._mouseMoveMonitor.startMonitoring(n.standardMouseMoveMerger,function(e){},function(){t._mousedownRepeatTimer.cancel(),t._mousedownScheduleRepeatTimer.cancel()}),e.preventDefault()},i}(i.Widget);t.ScrollbarArrow=r}),define(d[53],h([1,0,7,38,79,11]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isAction=function(e){return!!e&&(e instanceof s||"string"==typeof e.id&&"string"==typeof e.label&&"string"==typeof e.class&&"boolean"==typeof e.enabled&&"boolean"==typeof e.checked&&"function"==typeof e.run)};var s=function(){function e(e,t,n,i,o){void 0===t&&(t=""),void 0===n&&(n=""),void 0===i&&(i=!0),this._onDidChange=new r.Emitter,this._id=e,this._label=t,this._cssClass=n,this._enabled=i,this._actionCallback=o}return e.prototype.dispose=function(){this._onDidChange.dispose()},Object.defineProperty(e.prototype,"onDidChange",{get:function(){return this._onDidChange.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"id",{get:function(){return this._id},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"label",{get:function(){return this._label},set:function(e){this._setLabel(e)},enumerable:!0,configurable:!0}),e.prototype._setLabel=function(e){this._label!==e&&(this._label=e,this._onDidChange.fire({label:e}))},Object.defineProperty(e.prototype,"tooltip",{get:function(){return this._tooltip},set:function(e){this._setTooltip(e)},enumerable:!0,configurable:!0}),e.prototype._setTooltip=function(e){this._tooltip!==e&&(this._tooltip=e,this._onDidChange.fire({tooltip:e}))},Object.defineProperty(e.prototype,"class",{get:function(){return this._cssClass},set:function(e){this._setClass(e)},enumerable:!0,configurable:!0}),e.prototype._setClass=function(e){this._cssClass!==e&&(this._cssClass=e,this._onDidChange.fire({class:e}))},Object.defineProperty(e.prototype,"enabled",{get:function(){return this._enabled},set:function(e){this._setEnabled(e)},enumerable:!0,configurable:!0}),e.prototype._setEnabled=function(e){this._enabled!==e&&(this._enabled=e,this._onDidChange.fire({enabled:e}))},Object.defineProperty(e.prototype,"checked",{get:function(){return this._checked},set:function(e){this._setChecked(e)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"radio",{get:function(){return this._radio},set:function(e){this._setRadio(e)},enumerable:!0,configurable:!0}),e.prototype._setChecked=function(e){this._checked!==e&&(this._checked=e,this._onDidChange.fire({checked:e}))},e.prototype._setRadio=function(e){this._radio!==e&&(this._radio=e,this._onDidChange.fire({radio:e}))},Object.defineProperty(e.prototype,"order",{get:function(){return this._order},set:function(e){this._order=e},enumerable:!0,configurable:!0}),e.prototype.run=function(e,t){return void 0!==this._actionCallback?this._actionCallback(e):n.TPromise.as(!0)},e}();t.Action=s;var a=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.run=function(e,t){var i=this;return e.enabled?(this.emit(o.EventType.BEFORE_RUN,{action:e}),this.runAction(e,t).then(function(t){i.emit(o.EventType.RUN,{action:e,result:t})},function(t){i.emit(o.EventType.RUN,{action:e,error:t})})):n.TPromise.as(null)},t.prototype.runAction=function(e,t){return n.TPromise.as(t?e.run(t):e.run())},t}(i.EventEmitter);t.ActionRunner=a}),define(d[165],h([1,0,33,26,9,45,80,7]),function(e,t,n,i,o,r,s,a){"use strict";function u(){return Object.create(null)}function l(e){switch(e){case 0:return"";case 1:return S+"*?";default:return"(?:"+w+"|"+S+"+"+w+"|"+w+S+"+)*?"}}function c(e,t){if(!e)return[];for(var n,i=[],o=!1,r=!1,s="",a=0;a0;n--){var r=e.charCodeAt(n-1);if(47===r||92===r)break}t=e.substr(n)}var s=o.indexOf(t);return-1!==s?i[s]:null};a.basenames=o,a.patterns=i,a.allBasenames=o;var u=e.filter(function(e){return!e.basenames});return u.push(a),u}Object.defineProperty(t,"__esModule",{value:!0}),t.getEmptyExpression=u,t.mergeExpressions=function(){for(var e=[],t=0;t=0}}function a(e,t,n){for(var i,s,a,u=n.length-1;u>=0;u--){var l=n[u];if(t===l.filenameLowercase){i=l;break}if(l.filepattern&&(!s||l.filepattern.length>s.filepattern.length)){var c=l.filepatternOnPath?e:t;r.match(l.filepatternLowercase,c)&&(s=l)}l.extension&&(!a||l.extension.length>a.extension.length)&&o.endsWith(t,l.extensionLowercase)&&(a=l)}return i?i.mime:s?s.mime:a?a.mime:null}function u(e){if(o.startsWithUTF8BOM(e)&&(e=e.substr(1)),e.length>0)for(var t=0;t0)return n.mime}}return null}function l(e){return!e||("string"==typeof e?e===t.MIME_BINARY||e===t.MIME_TEXT||e===t.MIME_UNKNOWN:1===e.length&&l(e[0]))}Object.defineProperty(t,"__esModule",{value:!0}),t.MIME_TEXT="text/plain",t.MIME_BINARY="application/octet-stream",t.MIME_UNKNOWN="application/unknown";var c=[],d=[],h=[];t.registerTextMime=function(e){var t=s(e);c.push(t),t.userConfigured?h.push(t):d.push(t),t.userConfigured||c.forEach(function(e){e.mime===t.mime||e.userConfigured||(t.extension&&e.extension===t.extension&&console.warn("Overwriting extension <<"+t.extension+">> to now point to mime <<"+t.mime+">>"),t.filename&&e.filename===t.filename&&console.warn("Overwriting filename <<"+t.filename+">> to now point to mime <<"+t.mime+">>"),t.filepattern&&e.filepattern===t.filepattern&&console.warn("Overwriting filepattern <<"+t.filepattern+">> to now point to mime <<"+t.mime+">>"),t.firstline&&e.firstline===t.firstline&&console.warn("Overwriting firstline <<"+t.firstline+">> to now point to mime <<"+t.mime+">>"))})},t.clearTextMimes=function(e){e?(c=c.filter(function(e){return!e.userConfigured}),h=[]):(c=[],d=[],h=[])},t.guessMimeTypes=function(e,i){if(!e)return[t.MIME_UNKNOWN];e=e.toLowerCase();var o=n.basename(e),r=a(e,o,h);if(r)return[r,t.MIME_TEXT];var s=a(e,o,d);if(s)return[s,t.MIME_TEXT];if(i){var l=u(i);if(l)return[l,t.MIME_TEXT]}return[t.MIME_UNKNOWN]},t.isBinaryMime=function(e){if(!e)return!1;return(i.isArray(e)?e:e.split(",").map(function(e){return e.trim()})).indexOf(t.MIME_BINARY)>=0},t.isUnspecific=l,t.suggestFilename=function(e,t){for(var n=0;nt&&(n=t-e),n<0&&(n=0),i<0&&(i=0),r+i>o&&(r=o-i),r<0&&(r=0),this.width=e,this.scrollWidth=t,this.scrollLeft=n,this.height=i,this.scrollHeight=o,this.scrollTop=r}return e.prototype.equals=function(e){return this.width===e.width&&this.scrollWidth===e.scrollWidth&&this.scrollLeft===e.scrollLeft&&this.height===e.height&&this.scrollHeight===e.scrollHeight&&this.scrollTop===e.scrollTop},e.prototype.createScrollEvent=function(e){var t=this.width!==e.width,n=this.scrollWidth!==e.scrollWidth,i=this.scrollLeft!==e.scrollLeft,o=this.height!==e.height,r=this.scrollHeight!==e.scrollHeight,s=this.scrollTop!==e.scrollTop;return{width:this.width,scrollWidth:this.scrollWidth,scrollLeft:this.scrollLeft,height:this.height,scrollHeight:this.scrollHeight,scrollTop:this.scrollTop,widthChanged:t,scrollWidthChanged:n,scrollLeftChanged:i,heightChanged:o,scrollHeightChanged:r,scrollTopChanged:s}},e}();t.ScrollState=o;var r=function(e){function t(){var t=e.call(this)||this;return t._onScroll=t._register(new i.Emitter),t.onScroll=t._onScroll.event,t._state=new o(0,0,0,0,0,0),t}return f(t,e),t.prototype.getState=function(){return this._state},t.prototype.validateScrollTop=function(e){return e=Math.round(e),e=Math.max(e,0),e=Math.min(e,this._state.scrollHeight-this._state.height)},t.prototype.validateScrollLeft=function(e){return e=Math.round(e),e=Math.max(e,0),e=Math.min(e,this._state.scrollWidth-this._state.width)},t.prototype.updateState=function(e){var t=this._state,n=new o(void 0!==e.width?e.width:t.width,void 0!==e.scrollWidth?e.scrollWidth:t.scrollWidth,void 0!==e.scrollLeft?e.scrollLeft:t.scrollLeft,void 0!==e.height?e.height:t.height,void 0!==e.scrollHeight?e.scrollHeight:t.scrollHeight,void 0!==e.scrollTop?e.scrollTop:t.scrollTop);t.equals(n)||(this._state=n,this._onScroll.fire(this._state.createScrollEvent(t)))},t}(n.Disposable);t.Scrollable=r}),define(d[429],h([1,0,3,18,48]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){function t(t,n,o){var r=e.call(this)||this;return r._visibility=t,r._visibleClassName=n,r._invisibleClassName=o,r._domNode=null,r._isVisible=!1,r._isNeeded=!1,r._shouldBeVisible=!1,r._revealTimer=r._register(new i.TimeoutTimer),r}return f(t,e),t.prototype.applyVisibilitySetting=function(e){return this._visibility!==o.ScrollbarVisibility.Hidden&&(this._visibility===o.ScrollbarVisibility.Visible||e)},t.prototype.setShouldBeVisible=function(e){var t=this.applyVisibilitySetting(e);this._shouldBeVisible!==t&&(this._shouldBeVisible=t,this.ensureVisibility())},t.prototype.setIsNeeded=function(e){this._isNeeded!==e&&(this._isNeeded=e,this.ensureVisibility())},t.prototype.setDomNode=function(e){this._domNode=e,this._domNode.setClassName(this._invisibleClassName),this.setShouldBeVisible(!1)},t.prototype.ensureVisibility=function(){this._isNeeded?this._shouldBeVisible?this._reveal():this._hide(!0):this._hide(!1)},t.prototype._reveal=function(){var e=this;this._isVisible||(this._isVisible=!0,this._revealTimer.setIfNotSet(function(){e._domNode.setClassName(e._visibleClassName)},0))},t.prototype._hide=function(e){this._revealTimer.cancel(),this._isVisible&&(this._isVisible=!1,this._domNode.setClassName(this._invisibleClassName+(e?" fade":"")))},t}(n.Disposable);t.ScrollbarVisibilityController=r}),define(d[184],h([1,0,15,4,83,41,27,117,429]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l=function(e){function t(t){var n=e.call(this)||this;return n._lazyRender=t.lazyRender,n._host=t.host,n._scrollable=t.scrollable,n._scrollbarState=t.scrollbarState,n._visibilityController=n._register(new u.ScrollbarVisibilityController(t.visibility,"visible scrollbar "+t.extraScrollbarClassName,"invisible scrollbar "+t.extraScrollbarClassName)),n._mouseMoveMonitor=n._register(new o.GlobalMouseMoveMonitor),n._shouldRender=!0,n.domNode=s.createFastDomNode(document.createElement("div")),n.domNode.setAttribute("role","presentation"),n.domNode.setAttribute("aria-hidden","true"),n._visibilityController.setDomNode(n.domNode),n.domNode.setPosition("absolute"),n.onmousedown(n.domNode.domNode,function(e){return n._domNodeMouseDown(e)}),n}return f(t,e),t.prototype._createArrow=function(e){var t=this._register(new a.ScrollbarArrow(e));this.domNode.domNode.appendChild(t.bgDomNode),this.domNode.domNode.appendChild(t.domNode)},t.prototype._createSlider=function(e,t,n,i){var o=this;this.slider=s.createFastDomNode(document.createElement("div")),this.slider.setClassName("slider"),this.slider.setPosition("absolute"),this.slider.setTop(e),this.slider.setLeft(t),this.slider.setWidth(n),this.slider.setHeight(i),this.slider.setLayerHinting(!0),this.domNode.domNode.appendChild(this.slider.domNode),this.onmousedown(this.slider.domNode,function(e){e.leftButton&&(e.preventDefault(),o._sliderMouseDown(e,function(){}))})},t.prototype._onElementSize=function(e){return this._scrollbarState.setVisibleSize(e)&&(this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()),this._shouldRender=!0,this._lazyRender||this.render()),this._shouldRender},t.prototype._onElementScrollSize=function(e){return this._scrollbarState.setScrollSize(e)&&(this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()),this._shouldRender=!0,this._lazyRender||this.render()),this._shouldRender},t.prototype._onElementScrollPosition=function(e){return this._scrollbarState.setScrollPosition(e)&&(this._visibilityController.setIsNeeded(this._scrollbarState.isNeeded()),this._shouldRender=!0,this._lazyRender||this.render()),this._shouldRender},t.prototype.beginReveal=function(){this._visibilityController.setShouldBeVisible(!0)},t.prototype.beginHide=function(){this._visibilityController.setShouldBeVisible(!1)},t.prototype.render=function(){this._shouldRender&&(this._shouldRender=!1,this._renderDomNode(this._scrollbarState.getRectangleLargeSize(),this._scrollbarState.getRectangleSmallSize()),this._updateSlider(this._scrollbarState.getSliderSize(),this._scrollbarState.getArrowSize()+this._scrollbarState.getSliderPosition()))},t.prototype._domNodeMouseDown=function(e){e.target===this.domNode.domNode&&this._onMouseDown(e)},t.prototype.delegateMouseDown=function(e){var t=this.domNode.domNode.getClientRects()[0].top,n=t+this._scrollbarState.getSliderPosition(),i=t+this._scrollbarState.getSliderPosition()+this._scrollbarState.getSliderSize(),o=this._sliderMousePosition(e);n<=o&&o<=i?e.leftButton&&(e.preventDefault(),this._sliderMouseDown(e,function(){})):this._onMouseDown(e)},t.prototype.delegateSliderMouseDown=function(e,t){this._sliderMouseDown(e,t)},t.prototype._onMouseDown=function(e){var t=i.getDomNodePagePosition(this.domNode.domNode);this.setDesiredScrollPosition(this._scrollbarState.getDesiredScrollPositionFromOffset(this._mouseDownRelativePosition(e,t))),e.leftButton&&(e.preventDefault(),this._sliderMouseDown(e,function(){}))},t.prototype._sliderMouseDown=function(e,t){var i=this,r=this._sliderMousePosition(e),s=this._sliderOrthogonalMousePosition(e),a=this._scrollbarState.clone();this.slider.toggleClassName("active",!0),this._mouseMoveMonitor.startMonitoring(o.standardMouseMoveMerger,function(e){var t=i._sliderOrthogonalMousePosition(e),o=Math.abs(t-s);if(n.isWindows&&o>140)i.setDesiredScrollPosition(a.getScrollPosition());else{var u=i._sliderMousePosition(e)-r;i.setDesiredScrollPosition(a.getDesiredScrollPositionFromDelta(u))}},function(){i.slider.toggleClassName("active",!1),i._host.onDragEnd(),t()}),this._host.onDragStart()},t.prototype.setDesiredScrollPosition=function(e){e=this.validateScrollPosition(e);var t=this._getScrollPosition();return this._setScrollPosition(e),t!==this._getScrollPosition()&&(this._onElementScrollPosition(this._getScrollPosition()),!0)},t}(r.Widget);t.AbstractScrollbar=l}),define(d[439],h([1,0,184,47,48,150,117]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(e){function t(t,n,a){var u=e.call(this,{lazyRender:n.lazyRender,host:a,scrollbarState:new r.ScrollbarState(n.horizontalHasArrows?n.arrowSize:0,n.horizontal===o.ScrollbarVisibility.Hidden?0:n.horizontalScrollbarSize,n.vertical===o.ScrollbarVisibility.Hidden?0:n.verticalScrollbarSize),visibility:n.horizontal,extraScrollbarClassName:"horizontal",scrollable:t})||this;if(n.horizontalHasArrows){var l=(n.arrowSize-s.ARROW_IMG_SIZE)/2,c=(n.horizontalScrollbarSize-s.ARROW_IMG_SIZE)/2;u._createArrow({className:"left-arrow",top:c,left:l,bottom:void 0,right:void 0,bgWidth:n.arrowSize,bgHeight:n.horizontalScrollbarSize,onActivate:function(){return u._host.onMouseWheel(new i.StandardMouseWheelEvent(null,1,0))}}),u._createArrow({className:"right-arrow",top:c,left:void 0,bottom:void 0,right:l,bgWidth:n.arrowSize,bgHeight:n.horizontalScrollbarSize,onActivate:function(){return u._host.onMouseWheel(new i.StandardMouseWheelEvent(null,-1,0))}})}return u._createSlider(Math.floor((n.horizontalScrollbarSize-n.horizontalSliderSize)/2),0,null,n.horizontalSliderSize),u}return f(t,e),t.prototype._updateSlider=function(e,t){this.slider.setWidth(e),this.slider.setLeft(t)},t.prototype._renderDomNode=function(e,t){this.domNode.setWidth(e),this.domNode.setHeight(t),this.domNode.setLeft(0),this.domNode.setBottom(0)},t.prototype.onDidScroll=function(e){return this._shouldRender=this._onElementScrollSize(e.scrollWidth)||this._shouldRender,this._shouldRender=this._onElementScrollPosition(e.scrollLeft)||this._shouldRender,this._shouldRender=this._onElementSize(e.width)||this._shouldRender,this._shouldRender},t.prototype._mouseDownRelativePosition=function(e,t){return e.posx-t.left},t.prototype._sliderMousePosition=function(e){return e.posx},t.prototype._sliderOrthogonalMousePosition=function(e){return e.posy},t.prototype._getScrollPosition=function(){return this._scrollable.getState().scrollLeft},t.prototype._setScrollPosition=function(e){this._scrollable.updateState({scrollLeft:e})},t.prototype.validateScrollPosition=function(e){return this._scrollable.validateScrollLeft(e)},t}(n.AbstractScrollbar);t.HorizontalScrollbar=a}),define(d[440],h([1,0,184,47,48,150,117]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(e){function t(t,n,a){var u=e.call(this,{lazyRender:n.lazyRender,host:a,scrollbarState:new r.ScrollbarState(n.verticalHasArrows?n.arrowSize:0,n.vertical===o.ScrollbarVisibility.Hidden?0:n.verticalScrollbarSize,0),visibility:n.vertical,extraScrollbarClassName:"vertical",scrollable:t})||this;if(n.verticalHasArrows){var l=(n.arrowSize-s.ARROW_IMG_SIZE)/2,c=(n.verticalScrollbarSize-s.ARROW_IMG_SIZE)/2;u._createArrow({className:"up-arrow",top:l,left:c,bottom:void 0,right:void 0,bgWidth:n.verticalScrollbarSize,bgHeight:n.arrowSize,onActivate:function(){return u._host.onMouseWheel(new i.StandardMouseWheelEvent(null,0,1))}}),u._createArrow({className:"down-arrow",top:void 0,left:c,bottom:l,right:void 0,bgWidth:n.verticalScrollbarSize,bgHeight:n.arrowSize,onActivate:function(){return u._host.onMouseWheel(new i.StandardMouseWheelEvent(null,0,-1))}})}return u._createSlider(0,Math.floor((n.verticalScrollbarSize-n.verticalSliderSize)/2),n.verticalSliderSize,null),u}return f(t,e),t.prototype._updateSlider=function(e,t){this.slider.setHeight(e),this.slider.setTop(t)},t.prototype._renderDomNode=function(e,t){this.domNode.setWidth(t),this.domNode.setHeight(e),this.domNode.setRight(0),this.domNode.setTop(0)},t.prototype.onDidScroll=function(e){return this._shouldRender=this._onElementScrollSize(e.scrollHeight)||this._shouldRender,this._shouldRender=this._onElementScrollPosition(e.scrollTop)||this._shouldRender,this._shouldRender=this._onElementSize(e.height)||this._shouldRender,this._shouldRender},t.prototype._mouseDownRelativePosition=function(e,t){return e.posy-t.top},t.prototype._sliderMousePosition=function(e){return e.posy},t.prototype._sliderOrthogonalMousePosition=function(e){return e.posx},t.prototype._getScrollPosition=function(){return this._scrollable.getState().scrollTop},t.prototype._setScrollPosition=function(e){this._scrollable.updateState({scrollTop:e})},t.prototype.validateScrollPosition=function(e){return this._scrollable.validateScrollTop(e)},t}(n.AbstractScrollbar);t.VerticalScrollbar=a}),define(d[197],h([1,0,10,3,7,18,15]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a="$initialize",u=!1;t.logOnceWebWorkerWarning=function(e){s.isWeb&&(u||(u=!0,console.warn("Could not create web worker(s). Falling back to loading web worker code in main thread, which might cause UI freezes. Please see https://github.com/Microsoft/monaco-editor#faq")),console.warn(e.message))};var l=function(){function e(e){this._workerId=-1,this._handler=e,this._lastSentReq=0,this._pendingReplies=Object.create(null)}return e.prototype.setWorkerId=function(e){this._workerId=e},e.prototype.sendMessage=function(e,t){var n=String(++this._lastSentReq),i={c:null,e:null},r=new o.TPromise(function(e,t,n){i.c=e,i.e=t},function(){});return this._pendingReplies[n]=i,this._send({vsWorker:this._workerId,req:n,method:e,args:t}),r},e.prototype.handleMessage=function(e){var t;try{t=JSON.parse(e)}catch(e){}t.vsWorker&&(-1!==this._workerId&&t.vsWorker!==this._workerId||this._handleMessage(t))},e.prototype._handleMessage=function(e){var t=this;if(e.seq){var i=e;if(!this._pendingReplies[i.seq])return void console.warn("Got reply to unknown seq");var o=this._pendingReplies[i.seq];if(delete this._pendingReplies[i.seq],i.err){var r=i.err;return i.err.$isError&&((r=new Error).name=i.err.name,r.message=i.err.message,r.stack=i.err.stack),void o.e(r)}o.c(i.res)}else{var s=e,a=s.req;this._handler.handleMessage(s.method,s.args).then(function(e){t._send({vsWorker:t._workerId,seq:a,res:e,err:void 0})},function(e){t._send({vsWorker:t._workerId,seq:a,res:void 0,err:n.transformErrorForSerialization(e)})})}},e.prototype._send=function(e){var t=JSON.stringify(e);this._handler.sendMessage(t)},e}(),c=function(e){function t(t,n){var i=e.call(this)||this;i._lastRequestTimestamp=-1;var r=null,s=null;i._worker=i._register(t.create("vs/base/common/worker/simpleWorker",function(e){i._protocol.handleMessage(e)},function(e){s(e)})),i._protocol=new l({sendMessage:function(e){i._worker.postMessage(e)},handleMessage:function(e,t){return o.TPromise.as(null)}}),i._protocol.setWorkerId(i._worker.getId());var u=null,c=self.require;"function"==typeof c.getConfig?u=c.getConfig():void 0!==self.requirejs&&(u=self.requirejs.s.contexts._.config),i._lazyProxy=new o.TPromise(function(e,t,n){r=e,s=t},function(){}),i._onModuleLoaded=i._protocol.sendMessage(a,[i._worker.getId(),n,u]),i._onModuleLoaded.then(function(e){for(var t={},n=0;n0},e.prototype.getChildren=function(e,t){var i=this.modelProvider.getModel();return n.TPromise.as(i===t?i.entries:[])},e.prototype.getParent=function(e,t){return n.TPromise.as(null)},e}();t.DataSource=o;var r=function(){function e(e){this.modelProvider=e}return e.prototype.getAriaLabel=function(e,t){var n=this.modelProvider.getModel();return n.accessibilityProvider&&n.accessibilityProvider.getAriaLabel(t)},e.prototype.getPosInSet=function(e,t){var n=this.modelProvider.getModel();return String(n.entries.indexOf(t)+1)},e.prototype.getSetSize=function(){var e=this.modelProvider.getModel();return String(e.entries.length)},e}();t.AccessibilityProvider=r;var s=function(){function e(e){this.modelProvider=e}return e.prototype.isVisible=function(e,t){var n=this.modelProvider.getModel();return!n.filter||n.filter.isVisible(t)},e}();t.Filter=s;var a=function(){function e(e,t){this.modelProvider=e,this.styles=t}return e.prototype.updateStyles=function(e){this.styles=e},e.prototype.getHeight=function(e,t){return this.modelProvider.getModel().renderer.getHeight(t)},e.prototype.getTemplateId=function(e,t){return this.modelProvider.getModel().renderer.getTemplateId(t)},e.prototype.renderTemplate=function(e,t,n){return this.modelProvider.getModel().renderer.renderTemplate(t,n,this.styles)},e.prototype.renderElement=function(e,t,n,i){this.modelProvider.getModel().renderer.renderElement(t,n,i,this.styles)},e.prototype.disposeTemplate=function(e,t,n){this.modelProvider.getModel().renderer.disposeTemplate(t,n)},e}();t.Renderer=a}),define(d[100],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});!function(e){e[e.PREVIEW=0]="PREVIEW",e[e.OPEN=1]="OPEN",e[e.OPEN_IN_BACKGROUND=2]="OPEN_IN_BACKGROUND"}(t.Mode||(t.Mode={}))}),define(d[448],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e,t,n){this._posx=e,this._posy=t,this._target=n}return e.prototype.preventDefault=function(){},e.prototype.stopPropagation=function(){},Object.defineProperty(e.prototype,"posx",{get:function(){return this._posx},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"posy",{get:function(){return this._posy},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"target",{get:function(){return this._target},enumerable:!0,configurable:!0}),e}();t.ContextMenuEvent=n;var i=function(e){function t(t){var n=e.call(this,t.posx,t.posy,t.target)||this;return n.originalEvent=t,n}return f(t,e),t.prototype.preventDefault=function(){this.originalEvent.preventDefault()},t.prototype.stopPropagation=function(){this.originalEvent.stopPropagation()},t}(n);t.MouseContextMenuEvent=i;var o=function(e){function t(t,n,i){var o=e.call(this,t,n,i.target)||this;return o.originalEvent=i,o}return f(t,e),t.prototype.preventDefault=function(){this.originalEvent.preventDefault()},t.prototype.stopPropagation=function(){this.originalEvent.stopPropagation()},t}(n);t.KeyboardContextMenuEvent=o;var r;!function(e){e[e.COPY=0]="COPY",e[e.MOVE=1]="MOVE"}(r=t.DragOverEffect||(t.DragOverEffect={}));var s;!function(e){e[e.BUBBLE_DOWN=0]="BUBBLE_DOWN",e[e.BUBBLE_UP=1]="BUBBLE_UP"}(s=t.DragOverBubble||(t.DragOverBubble={})),t.DRAG_OVER_REJECT={accept:!1},t.DRAG_OVER_ACCEPT={accept:!0},t.DRAG_OVER_ACCEPT_BUBBLE_UP={accept:!0,bubble:s.BUBBLE_UP},t.DRAG_OVER_ACCEPT_BUBBLE_DOWN=function(e){return void 0===e&&(e=!1),{accept:!0,bubble:s.BUBBLE_DOWN,autoExpand:e}},t.DRAG_OVER_ACCEPT_BUBBLE_UP_COPY={accept:!0,bubble:s.BUBBLE_UP,effect:r.COPY},t.DRAG_OVER_ACCEPT_BUBBLE_DOWN_COPY=function(e){return void 0===e&&(e=!1),{accept:!0,bubble:s.BUBBLE_DOWN,effect:r.COPY,autoExpand:e}}}),define(d[461],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e){this.elements=e}return e.prototype.update=function(e){},e.prototype.getData=function(){return this.elements},e}();t.ElementsDragAndDropData=n;var i=function(){function e(e){this.elements=e}return e.prototype.update=function(e){},e.prototype.getData=function(){return this.elements},e}();t.ExternalElementsDragAndDropData=i;var o=function(){function e(){this.types=[],this.files=[]}return e.prototype.update=function(e){e.dataTransfer.types&&(this.types=[],Array.prototype.push.apply(this.types,e.dataTransfer.types)),e.dataTransfer.files&&(this.files=[],Array.prototype.push.apply(this.files,e.dataTransfer.files),this.files=this.files.filter(function(e){return e.size||e.type}))},e.prototype.getData=function(){return{types:this.types,files:this.files}},e}();t.DesktopDragAndDropData=o}),define(d[466],h([1,0,72,10,3,33,38,7]),function(e,t,n,i,o,r,s,a){"use strict";function u(e,t){for(var n=e.getHierarchy(),i=t.getHierarchy(),o=n[r.commonPrefixLength(n,i)-1],s=o.getNavigator(),a=null,u=null,l=0,c=[];o&&(null===a||null===u);)c.push(o),o===e&&(a=l),o===t&&(u=l),l++,o=s.next();if(null===a||null===u)return[];var d=Math.min(a,u),h=Math.max(a,u);return c.slice(d,h+1)}Object.defineProperty(t,"__esModule",{value:!0});var l=function(e){function t(t){var n=e.call(this)||this;return n._item=t,n}return f(t,e),Object.defineProperty(t.prototype,"item",{get:function(){return this._item},enumerable:!0,configurable:!0}),t.prototype.dispose=function(){this.emit("unlock"),e.prototype.dispose.call(this)},t}(s.EventEmitter);t.LockData=l;var c=function(){function e(){this.locks=Object.create({})}return e.prototype.isLocked=function(e){return!!this.locks[e.id]},e.prototype.run=function(e,t){var n=this,i=this.getLock(e);if(i){var o;return new a.TPromise(function(r,s){o=i.addOneTimeListener("unlock",function(){return n.run(e,t).then(r,s)})},function(){o.dispose()})}var r;return new a.TPromise(function(i,o){if(e.isDisposed())return o(new Error("Item is disposed."));var s=n.locks[e.id]=new l(e);return r=t().then(function(t){return delete n.locks[e.id],s.dispose(),t}).then(i,o)},function(){return r.cancel()})},e.prototype.getLock=function(e){var t;for(t in this.locks){var n=this.locks[t];if(e.intersects(n.item))return n}return null},e}();t.Lock=c;var d=function(e){function t(){var t=e.call(this)||this;return t._isDisposed=!1,t.items={},t}return f(t,e),t.prototype.register=function(e){n.ok(!this.isRegistered(e.id),"item already registered: "+e.id),this.items[e.id]={item:e,disposable:this.addEmitter(e)}},t.prototype.deregister=function(e){n.ok(this.isRegistered(e.id),"item not registered: "+e.id),this.items[e.id].disposable.dispose(),delete this.items[e.id]},t.prototype.isRegistered=function(e){return this.items.hasOwnProperty(e)},t.prototype.getItem=function(e){var t=this.items[e];return t?t.item:null},t.prototype.dispose=function(){e.prototype.dispose.call(this),this.items=null,this._isDisposed=!0},t.prototype.isDisposed=function(){return this._isDisposed},t}(s.EventEmitter);t.ItemRegistry=d;var h=function(e){function t(t,n,i,o,r){var s=e.call(this)||this;return s.registry=n,s.context=i,s.lock=o,s.element=r,s.id=t,s.registry.register(s),s.doesHaveChildren=s.context.dataSource.hasChildren(s.context.tree,s.element),s.needsChildrenRefresh=!0,s.parent=null,s.previous=null,s.next=null,s.firstChild=null,s.lastChild=null,s.userContent=null,s.traits={},s.depth=0,s.expanded=s.context.dataSource.shouldAutoexpand&&s.context.dataSource.shouldAutoexpand(s.context.tree,r),s.emit("item:create",{item:s}),s.visible=s._isVisible(),s.height=s._getHeight(),s._isDisposed=!1,s}return f(t,e),t.prototype.getElement=function(){return this.element},t.prototype.hasChildren=function(){return this.doesHaveChildren},t.prototype.getDepth=function(){return this.depth},t.prototype.isVisible=function(){return this.visible},t.prototype.setVisible=function(e){this.visible=e},t.prototype.isExpanded=function(){return this.expanded},t.prototype._setExpanded=function(e){this.expanded=e},t.prototype.reveal=function(e){void 0===e&&(e=null);var t={item:this,relativeTop:e};this.emit("item:reveal",t)},t.prototype.expand=function(){var e=this;return this.isExpanded()||!this.doesHaveChildren||this.lock.isLocked(this)?a.TPromise.as(!1):this.lock.run(this,function(){var t={item:e};return e.emit("item:expanding",t),(e.needsChildrenRefresh?e.refreshChildren(!1,!0,!0):a.TPromise.as(null)).then(function(){return e._setExpanded(!0),e.emit("item:expanded",t),!0})}).then(function(t){return!e.isDisposed()&&(e.context.options.autoExpandSingleChildren&&t&&null!==e.firstChild&&e.firstChild===e.lastChild&&e.firstChild.isVisible()?e.firstChild.expand().then(function(){return!0}):t)})},t.prototype.collapse=function(e){var t=this;if(void 0===e&&(e=!1),e){var n=a.TPromise.as(null);return this.forEachChild(function(e){n=n.then(function(){return e.collapse(!0)})}),n.then(function(){return t.collapse(!1)})}return!this.isExpanded()||this.lock.isLocked(this)?a.TPromise.as(!1):this.lock.run(this,function(){var e={item:t};return t.emit("item:collapsing",e),t._setExpanded(!1),t.emit("item:collapsed",e),a.TPromise.as(!0)})},t.prototype.addTrait=function(e){var t={item:this,trait:e};this.traits[e]=!0,this.emit("item:addTrait",t)},t.prototype.removeTrait=function(e){var t={item:this,trait:e};delete this.traits[e],this.emit("item:removeTrait",t)},t.prototype.hasTrait=function(e){return this.traits[e]||!1},t.prototype.getAllTraits=function(){var e,t=[];for(e in this.traits)this.traits.hasOwnProperty(e)&&this.traits[e]&&t.push(e);return t},t.prototype.getHeight=function(){return this.height},t.prototype.refreshChildren=function(e,n,o){var r=this;if(void 0===n&&(n=!1),void 0===o&&(o=!1),!o&&!this.isExpanded())return this.needsChildrenRefresh=!0,a.TPromise.as(this);this.needsChildrenRefresh=!1;var s=function(){var o={item:r,isNested:n};r.emit("item:childrenRefreshing",o);return(r.doesHaveChildren?r.context.dataSource.getChildren(r.context.tree,r.element):a.TPromise.as([])).then(function(n){if(r.isDisposed()||r.registry.isDisposed())return a.TPromise.as(null);if(!Array.isArray(n))return a.TPromise.wrapError(new Error("Please return an array of children."));n=n?n.slice(0):[],n=r.sort(n);for(var i={};null!==r.firstChild;)i[r.firstChild.id]=r.firstChild,r.removeChild(r.firstChild);for(var o=0,s=n.length;o0?o[0]:this.input,s=this.getNavigator(r,!1),a=0;a0?n[0]:this.input,o=this.getNavigator(i,!1).parent();o&&(t?this.setSelection([o],e):this.select(o,e))},t.prototype.setFocus=function(e,t){this.setTraits("focused",e?[e]:[]);var n={focus:this.getFocus(),payload:t};this.emit("focus",n)},t.prototype.isFocused=function(e){var t=this.getItem(e);return!!t&&t.hasTrait("focused")},t.prototype.getFocus=function(e){var t=this.getElementsWithTrait("focused",e);return 0===t.length?null:t[0]},t.prototype.focusNext=function(e,t){void 0===e&&(e=1);for(var n,i=this.getFocus()||this.input,o=this.getNavigator(i,!1),r=0;r=0;r--)this.onInsertItem(l[r]);for(r=this.heightMap.length-1;r>=o;r--)this.onRefreshItem(this.heightMap[r]);return a},t.prototype.onInsertItem=function(e){},t.prototype.onRemoveItems=function(e){for(var t,n,i,o=null,r=0;t=e.next();){if(i=this.indexes[t],!(n=this.heightMap[i]))return void console.error("view item doesnt exist");r-=n.height,delete this.indexes[t],this.onRemoveItem(n),null===o&&(o=i)}if(0!==r)for(this.heightMap.splice(o,i-o+1),i=o;i=n.top+n.height))return t;if(i===t)break;i=t}return this.heightMap.length},t.prototype.indexAfter=function(e){return Math.min(this.indexAt(e)+1,this.heightMap.length)},t.prototype.itemAtIndex=function(e){return this.heightMap[e]},t.prototype.itemAfter=function(e){return this.heightMap[this.indexes[e.model.id]+1]||null},t.prototype.createViewItem=function(e){throw new Error("not implemented")},t.prototype.dispose=function(){this.heightMap=null,this.indexes=null},t}(n.EventEmitter);t.HeightMap=o}),define(d[473],h([1,0,15,197]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(e,t){return void 0===t&&(t=!1),n.globals.MonacoEnvironment&&n.globals.MonacoEnvironment.hasOwnProperty(e)?n.globals.MonacoEnvironment[e]:t}("getWorkerUrl",null)||function(t,n){return e.toUrl("./"+t)+"#"+n},r=function(){function e(e,t,n,i,r){this.id=t,this.worker=new Worker(o("workerMain.js",n)),this.postMessage(e),this.worker.onmessage=function(e){i(e.data)},"function"==typeof this.worker.addEventListener&&this.worker.addEventListener("error",r)}return e.prototype.getId=function(){return this.id},e.prototype.postMessage=function(e){this.worker&&this.worker.postMessage(e)},e.prototype.dispose=function(){this.worker.terminate(),this.worker=null},e}(),s=function(){function e(e){this._label=e,this._webWorkerFailedBeforeError=!1}return e.prototype.create=function(t,n,o){var s=this,a=++e.LAST_WORKER_ID;if(this._webWorkerFailedBeforeError)throw this._webWorkerFailedBeforeError;return new r(t,a,this._label||"anonymous"+a,n,function(e){i.logOnceWebWorkerWarning(e),s._webWorkerFailedBeforeError=e,o(e)})},e.LAST_WORKER_ID=0,e}();t.DefaultWorkerFactory=s}),define(d[484],h([5]),{}),define(d[52],h([1,0,7,29,3,9,72,4,484]),function(e,t,n,i,o,r,s,a){"use strict";function u(e,t){s.ok(i.isString(e),"Expected String as parameter");var n=document.getElementById(e);return n?new x(n,t):null}function l(e){return e[b]||(e[b]={}),e[b]}function c(e){return!!e[b]}function d(e,t){return e instanceof N?new N(e):new x(e.getHTMLElement(),t)}function h(e,t){return new x(e,t)}function p(){return new x(null,!0)}function g(e,t,n){l(e)[t]=n}function m(e,t,n){if(c(e)){var o=l(e)[t];if(!i.isUndefined(o))return o}return n}function v(e,t){c(e)&&delete l(e)[t]}function _(e,t){g(e,w,t)}function y(e){v(e,w)}function C(e){return m(e,w)}Object.defineProperty(t,"__esModule",{value:!0}),t.withElementById=u,t.Build={withElementById:u};var b="_msDataKey",w="__$binding",S=function(){return function(e,t){this.x=e,this.y=t}}();t.Position=S;var E=function(){return function(e,t,n,i){this.top=e,this.right=t,this.bottom=n,this.left=i}}();t.Box=E;var L=function(){function e(e,t){this.width=e,this.height=t}return e.prototype.substract=function(t){return new e(this.width-t.left-t.right,this.height-t.top-t.bottom)},e}();t.Dimension=L;var x=function(){function e(e,t){this.offdom=t,this.container=e,this.currentElement=e,this.createdElements=[],this.toUnbind={},this.captureToUnbind={}}return e.prototype.asContainer=function(){return d(this,this.offdom)},e.prototype.clone=function(){var t=new e(this.container,this.offdom);return t.currentElement=this.currentElement,t.createdElements=this.createdElements,t.captureToUnbind=this.captureToUnbind,t.toUnbind=this.toUnbind,t},e.prototype.and=function(t){t instanceof e||t instanceof N||(t=new e(t,this.offdom));var n=[this];if(t instanceof N)for(var i=0;i=0){var n=e.split("-");e=n[0];for(var i=1;i=0){var t=e.split("-");e=t[0];for(var n=1;n=0?this.padding.apply(this,e.split(" ")):(i.isUndefinedOrNull(e)||(this.currentElement.style.paddingTop=this.toPixel(e)),i.isUndefinedOrNull(t)||(this.currentElement.style.paddingRight=this.toPixel(t)),i.isUndefinedOrNull(n)||(this.currentElement.style.paddingBottom=this.toPixel(n)),i.isUndefinedOrNull(o)||(this.currentElement.style.paddingLeft=this.toPixel(o)),this)},e.prototype.margin=function(e,t,n,o){return i.isString(e)&&e.indexOf(" ")>=0?this.margin.apply(this,e.split(" ")):(i.isUndefinedOrNull(e)||(this.currentElement.style.marginTop=this.toPixel(e)),i.isUndefinedOrNull(t)||(this.currentElement.style.marginRight=this.toPixel(t)),i.isUndefinedOrNull(n)||(this.currentElement.style.marginBottom=this.toPixel(n)),i.isUndefinedOrNull(o)||(this.currentElement.style.marginLeft=this.toPixel(o)),this)},e.prototype.position=function(e,t,n,o,r){return i.isString(e)&&e.indexOf(" ")>=0?this.position.apply(this,e.split(" ")):(i.isUndefinedOrNull(e)||(this.currentElement.style.top=this.toPixel(e)),i.isUndefinedOrNull(t)||(this.currentElement.style.right=this.toPixel(t)),i.isUndefinedOrNull(n)||(this.currentElement.style.bottom=this.toPixel(n)),i.isUndefinedOrNull(o)||(this.currentElement.style.left=this.toPixel(o)),r||(r="absolute"),this.currentElement.style.position=r,this)},e.prototype.size=function(e,t){return i.isString(e)&&e.indexOf(" ")>=0?this.size.apply(this,e.split(" ")):(i.isUndefinedOrNull(e)||(this.currentElement.style.width=this.toPixel(e)),i.isUndefinedOrNull(t)||(this.currentElement.style.height=this.toPixel(t)),this)},e.prototype.minSize=function(e,t){return i.isString(e)&&e.indexOf(" ")>=0?this.minSize.apply(this,e.split(" ")):(i.isUndefinedOrNull(e)||(this.currentElement.style.minWidth=this.toPixel(e)),i.isUndefinedOrNull(t)||(this.currentElement.style.minHeight=this.toPixel(t)),this)},e.prototype.maxSize=function(e,t){return i.isString(e)&&e.indexOf(" ")>=0?this.maxSize.apply(this,e.split(" ")):(i.isUndefinedOrNull(e)||(this.currentElement.style.maxWidth=this.toPixel(e)),i.isUndefinedOrNull(t)||(this.currentElement.style.maxHeight=this.toPixel(t)),this)},e.prototype.float=function(e){return this.currentElement.style.cssFloat=e,this},e.prototype.clear=function(e){return this.currentElement.style.clear=e,this},e.prototype.normal=function(){return this.currentElement.style.fontStyle="normal",this.currentElement.style.fontWeight="normal",this.currentElement.style.textDecoration="none",this},e.prototype.italic=function(){return this.currentElement.style.fontStyle="italic",this},e.prototype.bold=function(){return this.currentElement.style.fontWeight="bold",this},e.prototype.underline=function(){return this.currentElement.style.textDecoration="underline",this},e.prototype.overflow=function(e){return this.currentElement.style.overflow=e,this},e.prototype.display=function(e){return this.currentElement.style.display=e,this},e.prototype.disable=function(){return this.currentElement.setAttribute("disabled","disabled"),this},e.prototype.enable=function(){return this.currentElement.removeAttribute("disabled"),this},e.prototype.show=function(){return this.hasClass("builder-hidden")&&this.removeClass("builder-hidden"),this.attr("aria-hidden","false"),this.cancelVisibilityPromise(),this},e.prototype.showDelayed=function(e){var t=this;this.cancelVisibilityPromise();var i=n.TPromise.timeout(e);return this.setProperty("__$visibility",i),i.done(function(){t.removeProperty("__$visibility"),t.show()}),this},e.prototype.hide=function(){return this.hasClass("builder-hidden")||this.addClass("builder-hidden"),this.attr("aria-hidden","true"),this.cancelVisibilityPromise(),this},e.prototype.isHidden=function(){return this.hasClass("builder-hidden")||"none"===this.currentElement.style.display},e.prototype.toggleVisibility=function(){return this.cancelVisibilityPromise(),this.swapClass("builder-visible","builder-hidden"),this.isHidden()?this.attr("aria-hidden","true"):this.attr("aria-hidden","false"),this},e.prototype.cancelVisibilityPromise=function(){var e=this.getProperty("__$visibility");e&&(e.cancel(),this.removeProperty("__$visibility"))},e.prototype.border=function(e,t,n){return i.isString(e)&&e.indexOf(" ")>=0?this.border.apply(this,e.split(" ")):(this.currentElement.style.borderWidth=this.toPixel(e),n&&(this.currentElement.style.borderColor=n),t&&(this.currentElement.style.borderStyle=t),this)},e.prototype.borderTop=function(e,t,n){return i.isString(e)&&e.indexOf(" ")>=0?this.borderTop.apply(this,e.split(" ")):(this.currentElement.style.borderTopWidth=this.toPixel(e),n&&(this.currentElement.style.borderTopColor=n),t&&(this.currentElement.style.borderTopStyle=t),this)},e.prototype.borderBottom=function(e,t,n){return i.isString(e)&&e.indexOf(" ")>=0?this.borderBottom.apply(this,e.split(" ")):(this.currentElement.style.borderBottomWidth=this.toPixel(e),n&&(this.currentElement.style.borderBottomColor=n),t&&(this.currentElement.style.borderBottomStyle=t),this)},e.prototype.borderLeft=function(e,t,n){return i.isString(e)&&e.indexOf(" ")>=0?this.borderLeft.apply(this,e.split(" ")):(this.currentElement.style.borderLeftWidth=this.toPixel(e),n&&(this.currentElement.style.borderLeftColor=n),t&&(this.currentElement.style.borderLeftStyle=t),this)},e.prototype.borderRight=function(e,t,n){return i.isString(e)&&e.indexOf(" ")>=0?this.borderRight.apply(this,e.split(" ")):(this.currentElement.style.borderRightWidth=this.toPixel(e),n&&(this.currentElement.style.borderRightColor=n),t&&(this.currentElement.style.borderRightStyle=t),this)},e.prototype.textAlign=function(e){return this.currentElement.style.textAlign=e,this},e.prototype.verticalAlign=function(e){return this.currentElement.style.verticalAlign=e,this},e.prototype.toPixel=function(e){return-1===e.toString().indexOf("px")?e.toString()+"px":e},e.prototype.innerHtml=function(e,t){return t?this.currentElement.innerHTML+=e:this.currentElement.innerHTML=e,this},e.prototype.text=function(e,t){return t?0===this.currentElement.children.length?this.currentElement.textContent+=e:this.currentElement.appendChild(document.createTextNode(e)):this.currentElement.textContent=e,this},e.prototype.safeInnerHtml=function(e,t){return this.innerHtml(r.escape(e),t)},e.prototype.bind=function(e){return _(this.currentElement,e),this},e.prototype.unbind=function(){return y(this.currentElement),this},e.prototype.getBinding=function(){return C(this.currentElement)},e.prototype.setProperty=function(e,t){return g(this.currentElement,e,t),this},e.prototype.getProperty=function(e,t){return m(this.currentElement,e,t)},e.prototype.removeProperty=function(e){return c(this.currentElement)&&delete l(this.currentElement)[e],this},e.prototype.parent=function(e){return s.ok(!this.offdom,"Builder was created with offdom = true and thus has no parent set"),h(this.currentElement.parentNode,e)},e.prototype.children=function(e){for(var t=this.currentElement.children,n=[],i=0;i=n.top&&o+e.height<=n.top+n.height,l=r>=n.top&&r+e.height<=n.top+n.height;return s(o,a,r,l,i===u.ABOVE)}(),left:function(){var i=t.left,r=t.left+t.width-e.width,u=i>=n.left&&i+e.width<=n.left+n.width,l=r>=n.left&&r+e.width<=n.left+n.width;return s(i,u,r,l,o===a.LEFT)}()}}Object.defineProperty(t,"__esModule",{value:!0});var a;!function(e){e[e.LEFT=0]="LEFT",e[e.RIGHT=1]="RIGHT"}(a=t.AnchorAlignment||(t.AnchorAlignment={}));var u;!function(e){e[e.BELOW=0]="BELOW",e[e.ABOVE=1]="ABOVE"}(u=t.AnchorPosition||(t.AnchorPosition={}));var l=function(e){function t(t){var i=e.call(this)||this;return i.$view=n.$(".context-view").hide(),i.setContainer(t),i.toDispose=[{dispose:function(){i.setContainer(null)}}],i.toDisposeOnClean=null,i}return f(t,e),t.prototype.setContainer=function(e){var i=this;this.$container&&(this.$container.off(t.BUBBLE_UP_EVENTS),this.$container.off(t.BUBBLE_DOWN_EVENTS,!0),this.$container=null),e&&(this.$container=n.$(e),this.$view.appendTo(this.$container),this.$container.on(t.BUBBLE_UP_EVENTS,function(e){i.onDOMEvent(e,document.activeElement,!1)}),this.$container.on(t.BUBBLE_DOWN_EVENTS,function(e){i.onDOMEvent(e,document.activeElement,!0)},null,!0))},t.prototype.show=function(e){this.isVisible()&&this.hide(),this.$view.setClass("context-view").empty().style({top:"0px",left:"0px"}).show(),this.toDisposeOnClean=e.render(this.$view.getHTMLElement()),this.delegate=e,this.doLayout()},t.prototype.layout=function(){this.isVisible()&&(!1!==this.delegate.canRelayout?(this.delegate.layout&&this.delegate.layout(),this.doLayout()):this.hide())},t.prototype.doLayout=function(){var e,t=this.delegate.getAnchor();if(i.isHTMLElement(t)){var n=i.getDomNodePagePosition(t);e={top:n.top,left:n.left,width:n.width,height:n.height}}else{var o=t;e={top:o.y,left:o.x,width:o.width||0,height:o.height||0}}var r={top:i.StandardWindow.scrollY,left:i.StandardWindow.scrollX,height:window.innerHeight,width:window.innerWidth},l=this.$view.getTotalSize(),c={width:l.width,height:l.height},d=this.delegate.anchorPosition||u.BELOW,h=this.delegate.anchorAlignment||a.LEFT,p=s(c,e,r,d,h),f=i.getDomNodePagePosition(this.$container.getHTMLElement());p.top-=f.top,p.left-=f.left,this.$view.removeClass("top","bottom","left","right"),this.$view.addClass(d===u.BELOW?"bottom":"top"),this.$view.addClass(h===a.LEFT?"left":"right"),this.$view.style({top:p.top+"px",left:p.left+"px",width:"initial"})},t.prototype.hide=function(e){this.delegate&&this.delegate.onHide&&this.delegate.onHide(e),this.delegate=null,this.toDisposeOnClean&&(this.toDisposeOnClean.dispose(),this.toDisposeOnClean=null),this.$view.hide()},t.prototype.isVisible=function(){return!!this.delegate},t.prototype.onDOMEvent=function(e,t,n){this.delegate&&(this.delegate.onDOMEvent?this.delegate.onDOMEvent(e,document.activeElement):n&&!i.isAncestor(e.target,this.$container.getHTMLElement())&&this.hide())},t.prototype.dispose=function(){e.prototype.dispose.call(this),this.hide(),this.toDispose=o.dispose(this.toDispose)},t.BUBBLE_UP_EVENTS=["click","keydown","focus","blur"],t.BUBBLE_DOWN_EVENTS=["click"],t}(r.EventEmitter);t.ContextView=l}),define(d[201],h([5]),{}),define(d[204],h([1,0,4,9,32,26,201]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s={badgeBackground:o.Color.fromHex("#4D4D4D"),badgeForeground:o.Color.fromHex("#FFFFFF")},a=function(){function e(e,t){this.options=t||Object.create(null),r.mixin(this.options,s,!1),this.badgeBackground=this.options.badgeBackground,this.badgeForeground=this.options.badgeForeground,this.badgeBorder=this.options.badgeBorder,this.element=n.append(e,n.$(".monaco-count-badge")),this.titleFormat=this.options.titleFormat||"",this.setCount(this.options.count||0)}return e.prototype.setCount=function(e){this.count=e,this.render()},e.prototype.setTitleFormat=function(e){this.titleFormat=e,this.render()},e.prototype.render=function(){this.element.textContent=""+this.count,this.element.title=i.format(this.titleFormat,this.count),this.applyStyles()},e.prototype.style=function(e){this.badgeBackground=e.badgeBackground,this.badgeForeground=e.badgeForeground,this.badgeBorder=e.badgeBorder,this.applyStyles()},e.prototype.applyStyles=function(){if(this.element){var e=this.badgeBackground?this.badgeBackground.toString():null,t=this.badgeForeground?this.badgeForeground.toString():null,n=this.badgeBorder?this.badgeBorder.toString():null;this.element.style.backgroundColor=e,this.element.style.color=t,this.element.style.borderWidth=n?"1px":null,this.element.style.borderStyle=n?"solid":null,this.element.style.borderColor=n}},e}();t.CountBadge=a}),define(d[205],h([5]),{}),define(d[206],h([5]),{}),define(d[208],h([5]),{}),define(d[162],h([1,0,4,110,45,174,208]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t){this.domNode=n.append(e,n.$(".monaco-icon-label")),t&&t.supportHighlights?this.labelNode=new i.HighlightedLabel(n.append(this.domNode,n.$("a.label-name"))):this.labelNode=n.append(this.domNode,n.$("a.label-name")),this.descriptionNode=n.append(this.domNode,n.$("span.label-description"))}return Object.defineProperty(e.prototype,"element",{get:function(){return this.domNode},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"labelElement",{get:function(){var e=this.labelNode;return e instanceof i.HighlightedLabel?e.element:e},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"descriptionElement",{get:function(){return this.descriptionNode},enumerable:!0,configurable:!0}),e.prototype.setValue=function(e,t,o){var r=this.labelNode;r instanceof i.HighlightedLabel?r.set(e||"",o?o.matches:void 0):r.textContent=e||"",this.descriptionNode.textContent=t||"",t?n.removeClass(this.descriptionNode,"empty"):n.addClass(this.descriptionNode,"empty"),this.domNode.title=o&&o.title?o.title:"";var s=["monaco-icon-label"];o&&(o.extraClasses&&s.push.apply(s,o.extraClasses),o.italic&&s.push("italic")),this.domNode.className=s.join(" ")},e.prototype.dispose=function(){var e=this.labelNode;e instanceof i.HighlightedLabel&&e.dispose()},e}();t.IconLabel=s;var a=function(e){function t(t,n,i,o){var r=e.call(this,t)||this;return r.setFile(n,i,o),r}return f(t,e),t.prototype.setFile=function(e,t,n){var i=o.dirname(e.fsPath);this.setValue(o.basename(e.fsPath),i&&"."!==i?r.getPathLabel(i,t,n):"",{title:e.fsPath})},t}(s);t.FileLabel=a}),define(d[211],h([5]),{}),define(d[212],h([5]),{}),define(d[213],h([5]),{}),define(d[218],h([5]),{}),define(d[219],h([5]),{}),define(d[220],h([1,0,7,72,52,4,3,32,26,219]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l={progressBarBackground:a.Color.fromHex("#0E70C0")},c=function(){function e(e,t){this.options=t||Object.create(null),u.mixin(this.options,l,!1),this.toUnbind=[],this.workedVal=0,this.progressBarBackground=this.options.progressBarBackground,this.create(e)}return e.prototype.create=function(e){var t=this;e.div({class:"progress-container"},function(e){t.element=e.clone(),e.div({class:"progress-bit"}).on([r.EventType.ANIMATION_START,r.EventType.ANIMATION_END,r.EventType.ANIMATION_ITERATION],function(e){switch(e.type){case r.EventType.ANIMATION_START:case r.EventType.ANIMATION_END:t.animationRunning=e.type===r.EventType.ANIMATION_START;break;case r.EventType.ANIMATION_ITERATION:t.animationStopToken&&t.animationStopToken(null)}},t.toUnbind),t.bit=e.getHTMLElement()}),this.applyStyles()},e.prototype.off=function(){this.bit.style.width="inherit",this.bit.style.opacity="1",this.element.removeClass("active"),this.element.removeClass("infinite"),this.element.removeClass("discrete"),this.workedVal=0,this.totalWork=void 0},e.prototype.done=function(){return this.doDone(!0)},e.prototype.stop=function(){return this.doDone(!1)},e.prototype.doDone=function(e){var t=this;return this.element.addClass("done"),this.element.hasClass("infinite")?(this.bit.style.opacity="0",e?n.TPromise.timeout(200).then(function(){return t.off()}):this.off()):(this.bit.style.width="inherit",e?n.TPromise.timeout(200).then(function(){return t.off()}):this.off()),this},e.prototype.infinite=function(){return this.bit.style.width="2%",this.bit.style.opacity="1",this.element.removeClass("discrete"),this.element.removeClass("done"),this.element.addClass("active"),this.element.addClass("infinite"),this},e.prototype.total=function(e){return this.workedVal=0,this.totalWork=e,this},e.prototype.hasTotal=function(){return!isNaN(this.totalWork)},e.prototype.worked=function(e){return i.ok(!isNaN(this.totalWork),"Total work not set"),e=Number(e),i.ok(!isNaN(e),"Value is not a number"),e=Math.max(1,e),this.workedVal+=e,this.workedVal=Math.min(this.totalWork,this.workedVal),this.element.hasClass("infinite")&&this.element.removeClass("infinite"),this.element.hasClass("done")&&this.element.removeClass("done"),this.element.hasClass("active")||this.element.addClass("active"),this.element.hasClass("discrete")||this.element.addClass("discrete"),this.bit.style.width=this.workedVal/this.totalWork*100+"%",this},e.prototype.getContainer=function(){return o.$(this.element)},e.prototype.style=function(e){this.progressBarBackground=e.progressBarBackground,this.applyStyles()},e.prototype.applyStyles=function(){if(this.bit){var e=this.progressBarBackground?this.progressBarBackground.toString():null;this.bit.style.backgroundColor=e}},e.prototype.dispose=function(){this.toUnbind=s.dispose(this.toUnbind)},e}();t.ProgressBar=c}),define(d[224],h([5]),{}),define(d[99],h([1,0,3,52,28,15,29,4,74,38,47,11,224]),function(e,t,n,i,o,r,s,a,u,l,c,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var h;!function(e){e[e.VERTICAL=0]="VERTICAL",e[e.HORIZONTAL=1]="HORIZONTAL"}(h=t.Orientation||(t.Orientation={}));var p=function(e){function t(t,n,s){void 0===s&&(s={});var l=e.call(this)||this;return l.$e=i.$(".monaco-sash").appendTo(t),r.isMacintosh&&l.$e.addClass("mac"),l.gesture=new u.Gesture(l.$e.getHTMLElement()),l.$e.on(a.EventType.MOUSE_DOWN,function(e){l.onMouseDown(e)}),l.$e.on(a.EventType.DBLCLICK,function(e){l.emit("reset",e)}),l.$e.on(u.EventType.Start,function(e){l.onTouchStart(e)}),l.size=s.baseSize||5,o.isIPad&&(l.size*=4,l.$e.addClass("touch")),l.setOrientation(s.orientation||h.VERTICAL),l.isDisabled=!1,l.hidden=!1,l.layoutProvider=n,l}return f(t,e),t.prototype.getHTMLElement=function(){return this.$e.getHTMLElement()},t.prototype.setOrientation=function(e){this.orientation=e,this.$e.removeClass("horizontal","vertical"),this.$e.addClass(this.getOrientation()),this.orientation===h.HORIZONTAL?this.$e.size(null,this.size):this.$e.size(this.size),this.layoutProvider&&this.layout()},t.prototype.getOrientation=function(){return this.orientation===h.HORIZONTAL?"horizontal":"vertical"},t.prototype.onMouseDown=function(e){var t=this;if(a.EventHelper.stop(e,!1),!this.isDisabled){var n=i.$(a.getElementsByTagName("iframe"));n&&n.style("pointer-events","none");var o=new c.StandardMouseEvent(e),s=o.posx,u=o.posy,l={startX:s,currentX:s,startY:u,currentY:u};this.$e.addClass("active"),this.emit("start",l);var d=i.$(window),h=this.getOrientation()+"-cursor-container"+(r.isMacintosh?"-mac":""),p=s,f=u;d.on("mousemove",function(e){a.EventHelper.stop(e,!1);var n=new c.StandardMouseEvent(e),i={startX:s,currentX:n.posx,startY:u,currentY:n.posy};p=n.posx,f=n.posy,t.emit("change",i)}).once("mouseup",function(e){a.EventHelper.stop(e,!1),t.$e.removeClass("active"),t.emit("end"),d.off("mousemove"),document.body.classList.remove(h);var n=i.$(a.getElementsByTagName("iframe"));n&&n.style("pointer-events","auto")}),document.body.classList.add(h)}},t.prototype.onTouchStart=function(e){var t=this;a.EventHelper.stop(e);var i=[],o=e.pageX,r=e.pageY;this.emit("start",{startX:o,currentX:o,startY:r,currentY:r});var l=o,c=r;i.push(a.addDisposableListener(this.$e.getHTMLElement(),u.EventType.Change,function(e){s.isNumber(e.pageX)&&s.isNumber(e.pageY)&&(t.emit("change",{startX:o,currentX:e.pageX,startY:r,currentY:e.pageY}),l=e.pageX,c=e.pageY)})),i.push(a.addDisposableListener(this.$e.getHTMLElement(),u.EventType.End,function(e){t.emit("end"),n.dispose(i)}))},t.prototype.layout=function(){var e;if(this.orientation===h.VERTICAL){var t=this.layoutProvider;e={left:t.getVerticalSashLeft(this)-this.size/2+"px"},t.getVerticalSashTop&&(e.top=t.getVerticalSashTop(this)+"px"),t.getVerticalSashHeight&&(e.height=t.getVerticalSashHeight(this)+"px")}else{var n=this.layoutProvider;e={top:n.getHorizontalSashTop(this)-this.size/2+"px"},n.getHorizontalSashLeft&&(e.left=n.getHorizontalSashLeft(this)+"px"),n.getHorizontalSashWidth&&(e.width=n.getHorizontalSashWidth(this)+"px")}this.$e.style(e)},t.prototype.show=function(){this.hidden=!1,this.$e.show()},t.prototype.hide=function(){this.hidden=!0,this.$e.hide()},t.prototype.isHidden=function(){return this.hidden},t.prototype.enable=function(){this.$e.removeClass("disabled"),this.isDisabled=!1},t.prototype.disable=function(){this.$e.addClass("disabled"),this.isDisabled=!0},t.prototype.dispose=function(){this.$e&&(this.$e.destroy(),this.$e=null),e.prototype.dispose.call(this)},t}(l.EventEmitter);t.Sash=p;var g=function(e){function t(t,n){var i=e.call(this)||this;return i.minWidth=n,i._onPositionChange=new d.Emitter,i.ratio=.5,i.sash=new p(t,i),i._register(i.sash.addListener("start",function(){return i.onSashDragStart()})),i._register(i.sash.addListener("change",function(e){return i.onSashDrag(e)})),i._register(i.sash.addListener("end",function(){return i.onSashDragEnd()})),i._register(i.sash.addListener("reset",function(){return i.onSashReset()})),i}return f(t,e),Object.defineProperty(t.prototype,"onPositionChange",{get:function(){return this._onPositionChange.event},enumerable:!0,configurable:!0}),t.prototype.getVerticalSashTop=function(){return 0},t.prototype.getVerticalSashLeft=function(){return this.position},t.prototype.getVerticalSashHeight=function(){return this.dimension.height},t.prototype.setDimenesion=function(e){this.dimension=e,this.compute(this.ratio)},t.prototype.onSashDragStart=function(){this.startPosition=this.position},t.prototype.onSashDrag=function(e){this.compute((this.startPosition+(e.currentX-e.startX))/this.dimension.width)},t.prototype.compute=function(e){this.computeSashPosition(e),this.ratio=this.position/this.dimension.width,this._onPositionChange.fire(this.position)},t.prototype.onSashDragEnd=function(){this.sash.layout()},t.prototype.onSashReset=function(){this.ratio=.5,this._onPositionChange.fire(this.position),this.sash.layout()},t.prototype.computeSashPosition=function(e){void 0===e&&(e=this.ratio);var t=this.dimension.width,n=Math.floor((e||.5)*t),i=Math.floor(.5*t);t>2*this.minWidth?(nt-this.minWidth&&(n=t-this.minWidth)):n=i,this.position!==n&&(this.position=n,this.sash.layout())},t}(n.Disposable);t.VSash=g}),define(d[228],h([5]),{}),define(d[63],h([1,0,4,15,47,439,440,3,48,41,18,27,11,228]),function(e,t,n,i,o,r,s,a,u,l,c,d,h){"use strict";function p(e){var t={lazyRender:void 0!==e.lazyRender&&e.lazyRender,className:void 0!==e.className?e.className:"",useShadows:void 0===e.useShadows||e.useShadows,handleMouseWheel:void 0===e.handleMouseWheel||e.handleMouseWheel,flipAxes:void 0!==e.flipAxes&&e.flipAxes,alwaysConsumeMouseWheel:void 0!==e.alwaysConsumeMouseWheel&&e.alwaysConsumeMouseWheel,scrollYToX:void 0!==e.scrollYToX&&e.scrollYToX,mouseWheelScrollSensitivity:void 0!==e.mouseWheelScrollSensitivity?e.mouseWheelScrollSensitivity:1,arrowSize:void 0!==e.arrowSize?e.arrowSize:11,listenOnDomNode:void 0!==e.listenOnDomNode?e.listenOnDomNode:null,horizontal:void 0!==e.horizontal?e.horizontal:u.ScrollbarVisibility.Auto,horizontalScrollbarSize:void 0!==e.horizontalScrollbarSize?e.horizontalScrollbarSize:10,horizontalSliderSize:void 0!==e.horizontalSliderSize?e.horizontalSliderSize:0,horizontalHasArrows:void 0!==e.horizontalHasArrows&&e.horizontalHasArrows,vertical:void 0!==e.vertical?e.vertical:u.ScrollbarVisibility.Auto,verticalScrollbarSize:void 0!==e.verticalScrollbarSize?e.verticalScrollbarSize:10,verticalHasArrows:void 0!==e.verticalHasArrows&&e.verticalHasArrows,verticalSliderSize:void 0!==e.verticalSliderSize?e.verticalSliderSize:0};return t.horizontalSliderSize=void 0!==e.horizontalSliderSize?e.horizontalSliderSize:t.horizontalScrollbarSize,t.verticalSliderSize=void 0!==e.verticalSliderSize?e.verticalSliderSize:t.verticalScrollbarSize,i.isMacintosh&&(t.className+=" mac"),t}Object.defineProperty(t,"__esModule",{value:!0});var g=function(e){function t(t,n,i){var o=e.call(this)||this;o._onScroll=o._register(new h.Emitter),o.onScroll=o._onScroll.event,t.style.overflow="hidden",o._options=p(n),o._scrollable=void 0===i?o._register(new u.Scrollable):i,o._register(o._scrollable.onScroll(function(e){o._onDidScroll(e),o._onScroll.fire(e)}));var a={onMouseWheel:function(e){return o._onMouseWheel(e)},onDragStart:function(){return o._onDragStart()},onDragEnd:function(){return o._onDragEnd()}};return o._verticalScrollbar=o._register(new s.VerticalScrollbar(o._scrollable,o._options,a)),o._horizontalScrollbar=o._register(new r.HorizontalScrollbar(o._scrollable,o._options,a)),o._domNode=document.createElement("div"),o._domNode.className="monaco-scrollable-element "+o._options.className,o._domNode.setAttribute("role","presentation"),o._domNode.style.position="relative",o._domNode.style.overflow="hidden",o._domNode.appendChild(t),o._domNode.appendChild(o._horizontalScrollbar.domNode.domNode),o._domNode.appendChild(o._verticalScrollbar.domNode.domNode),o._options.useShadows&&(o._leftShadowDomNode=d.createFastDomNode(document.createElement("div")),o._leftShadowDomNode.setClassName("shadow"),o._domNode.appendChild(o._leftShadowDomNode.domNode),o._topShadowDomNode=d.createFastDomNode(document.createElement("div")),o._topShadowDomNode.setClassName("shadow"),o._domNode.appendChild(o._topShadowDomNode.domNode),o._topLeftShadowDomNode=d.createFastDomNode(document.createElement("div")),o._topLeftShadowDomNode.setClassName("shadow top-left-corner"),o._domNode.appendChild(o._topLeftShadowDomNode.domNode)),o._listenOnDomNode=o._options.listenOnDomNode||o._domNode,o._mouseWheelToDispose=[],o._setListeningToMouseWheel(o._options.handleMouseWheel),o.onmouseover(o._listenOnDomNode,function(e){return o._onMouseOver(e)}),o.onnonbubblingmouseout(o._listenOnDomNode,function(e){return o._onMouseOut(e)}),o._hideTimeout=o._register(new c.TimeoutTimer),o._isDragging=!1,o._mouseIsOver=!1,o._shouldRender=!0,o}return f(t,e),t.prototype.dispose=function(){this._mouseWheelToDispose=a.dispose(this._mouseWheelToDispose),e.prototype.dispose.call(this)},t.prototype.getDomNode=function(){return this._domNode},t.prototype.getOverviewRulerLayoutInfo=function(){return{parent:this._domNode,insertBefore:this._verticalScrollbar.domNode.domNode}},t.prototype.delegateVerticalScrollbarMouseDown=function(e){this._verticalScrollbar.delegateMouseDown(e)},t.prototype.delegateSliderMouseDown=function(e,t){this._verticalScrollbar.delegateSliderMouseDown(e,t)},t.prototype.updateState=function(e){this._scrollable.updateState(e)},t.prototype.getScrollState=function(){return this._scrollable.getState()},t.prototype.updateClassName=function(e){this._options.className=e,i.isMacintosh&&(this._options.className+=" mac"),this._domNode.className="monaco-scrollable-element "+this._options.className},t.prototype.updateOptions=function(e){var t=p(e);this._options.handleMouseWheel=t.handleMouseWheel,this._options.mouseWheelScrollSensitivity=t.mouseWheelScrollSensitivity,this._setListeningToMouseWheel(this._options.handleMouseWheel),this._options.lazyRender||this._render()},t.prototype._setListeningToMouseWheel=function(e){var t=this;if(this._mouseWheelToDispose.length>0!==e&&(this._mouseWheelToDispose=a.dispose(this._mouseWheelToDispose),e)){var i=function(e){var n=new o.StandardMouseWheelEvent(e);t._onMouseWheel(n)};this._mouseWheelToDispose.push(n.addDisposableListener(this._listenOnDomNode,"mousewheel",i)),this._mouseWheelToDispose.push(n.addDisposableListener(this._listenOnDomNode,"DOMMouseScroll",i))}},t.prototype._onMouseWheel=function(e){var t=-1,n=-1;if(e.deltaY||e.deltaX){var o=e.deltaY*this._options.mouseWheelScrollSensitivity,r=e.deltaX*this._options.mouseWheelScrollSensitivity;this._options.flipAxes&&(o=(c=[r,o])[0],r=c[1]);var s=!i.isMacintosh&&e.browserEvent.shiftKey;!this._options.scrollYToX&&!s||r||(r=o,o=0),i.isMacintosh&&(o&&Math.abs(r)<.2&&(r=0),Math.abs(o)>.5*Math.abs(r)&&(r=0));var a=this._scrollable.getState();if(o){var u=a.scrollTop;(t=this._verticalScrollbar.validateScrollPosition((-1!==t?t:u)-50*o))===u&&(t=-1)}if(r){var l=a.scrollLeft;(n=this._horizontalScrollbar.validateScrollPosition((-1!==n?n:l)-50*r))===l&&(n=-1)}-1===t&&-1===n||(-1!==t&&(this._shouldRender=this._verticalScrollbar.setDesiredScrollPosition(t)||this._shouldRender,t=-1),-1!==n&&(this._shouldRender=this._horizontalScrollbar.setDesiredScrollPosition(n)||this._shouldRender,n=-1))}(this._options.alwaysConsumeMouseWheel||this._shouldRender)&&(e.preventDefault(),e.stopPropagation());var c},t.prototype._onDidScroll=function(e){this._shouldRender=this._horizontalScrollbar.onDidScroll(e)||this._shouldRender,this._shouldRender=this._verticalScrollbar.onDidScroll(e)||this._shouldRender,this._options.useShadows&&(this._shouldRender=!0),this._reveal(),this._options.lazyRender||this._render()},t.prototype.renderNow=function(){if(!this._options.lazyRender)throw new Error("Please use `lazyRender` together with `renderNow`!");this._render()},t.prototype._render=function(){if(this._shouldRender&&(this._shouldRender=!1,this._horizontalScrollbar.render(),this._verticalScrollbar.render(),this._options.useShadows)){var e=this._scrollable.getState(),t=e.scrollTop>0,n=e.scrollLeft>0;this._leftShadowDomNode.setClassName("shadow"+(n?" left":"")),this._topShadowDomNode.setClassName("shadow"+(t?" top":"")),this._topLeftShadowDomNode.setClassName("shadow top-left-corner"+(t?" top":"")+(n?" left":""))}},t.prototype._onDragStart=function(){this._isDragging=!0,this._reveal()},t.prototype._onDragEnd=function(){this._isDragging=!1,this._hide()},t.prototype._onMouseOut=function(e){this._mouseIsOver=!1,this._hide()},t.prototype._onMouseOver=function(e){this._mouseIsOver=!0,this._reveal()},t.prototype._reveal=function(){this._verticalScrollbar.beginReveal(),this._horizontalScrollbar.beginReveal(),this._scheduleHide()},t.prototype._hide=function(){this._mouseIsOver||this._isDragging||(this._verticalScrollbar.beginHide(),this._horizontalScrollbar.beginHide())},t.prototype._scheduleHide=function(){var e=this;this._mouseIsOver||this._isDragging||this._hideTimeout.cancelAndSet(function(){return e._hide()},500)},t}(l.Widget);t.ScrollableElement=g;var m=function(e){function t(t,n){var i=e.call(this,t,n)||this;return i._element=t,i.onScroll(function(e){e.scrollTopChanged&&(i._element.scrollTop=e.scrollTop),e.scrollLeftChanged&&(i._element.scrollLeft=e.scrollLeft)}),i.scanDomNode(),i}return f(t,e),t.prototype.scanDomNode=function(){this.updateState({width:this._element.clientWidth,scrollWidth:this._element.scrollWidth,scrollLeft:this._element.scrollLeft,height:this._element.clientHeight,scrollHeight:this._element.scrollHeight,scrollTop:this._element.scrollTop})},t}(g);t.DomScrollableElement=m}),define(d[232],h([1,0,26,3,74,4,124,63,48,415,416,15,28]),function(e,t,n,i,o,r,s,a,u,l,c,d,h){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var p=["click","dblclick","mouseup","mousedown","mouseover","mousemove","mouseout","contextmenu","touchstart"],f={useShadows:!0},g=function(){function e(e,t,i,r){void 0===r&&(r=f),this.delegate=t,this.items=[],this.itemId=0,this.rangeMap=new l.RangeMap,this.renderers=n.toObject(i,function(e){return e.templateId}),this.cache=new c.RowCache(this.renderers),this.lastRenderTop=0,this.lastRenderHeight=0,this._domNode=document.createElement("div"),this._domNode.className="monaco-list",this.rowsContainer=document.createElement("div"),this.rowsContainer.className="monaco-list-rows",this.gesture=new o.Gesture(this.rowsContainer),this.scrollableElement=new a.ScrollableElement(this.rowsContainer,{alwaysConsumeMouseWheel:!0,horizontal:u.ScrollbarVisibility.Hidden,vertical:u.ScrollbarVisibility.Auto,useShadows:n.getOrDefault(r,function(e){return e.useShadows},f.useShadows)}),this._domNode.appendChild(this.scrollableElement.getDomNode()),e.appendChild(this._domNode),this.disposables=[this.rangeMap,this.gesture,this.scrollableElement],this.scrollableElement.onScroll(this.onScroll,this,this.disposables),s.domEvent(this.rowsContainer,o.EventType.Change)(this.onTouchChange,this,this.disposables),this.layout()}return Object.defineProperty(e.prototype,"domNode",{get:function(){return this._domNode},enumerable:!0,configurable:!0}),e.prototype.splice=function(e,t,n){var i=this;void 0===n&&(n=[]);var o=this.getRenderRange(this.lastRenderTop,this.lastRenderHeight);l.each(o,function(e){return i.removeItemFromDOM(i.items[e])});var r=n.map(function(e){return{id:String(i.itemId++),element:e,size:i.delegate.getHeight(e),templateId:i.delegate.getTemplateId(e),row:null}});(c=this.rangeMap).splice.apply(c,[e,t].concat(r));var s=(d=this.items).splice.apply(d,[e,t].concat(r)),a=this.getRenderRange(this.lastRenderTop,this.lastRenderHeight);l.each(a,function(e){return i.insertItemInDOM(i.items[e],e)});var u=this.getContentHeight();return this.rowsContainer.style.height=u+"px",this.scrollableElement.updateState({scrollHeight:u}),s.map(function(e){return e.element});var c,d},Object.defineProperty(e.prototype,"length",{get:function(){return this.items.length},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"renderHeight",{get:function(){return this.scrollableElement.getScrollState().height},enumerable:!0,configurable:!0}),e.prototype.element=function(e){return this.items[e].element},e.prototype.domElement=function(e){var t=this.items[e].row;return t&&t.domNode},e.prototype.elementHeight=function(e){return this.items[e].size},e.prototype.elementTop=function(e){return this.rangeMap.positionAt(e)},e.prototype.indexAt=function(e){return this.rangeMap.indexAt(e)},e.prototype.indexAfter=function(e){return this.rangeMap.indexAfter(e)},e.prototype.layout=function(e){this.scrollableElement.updateState({height:e||r.getContentHeight(this._domNode)})},e.prototype.render=function(e,t){var n=this,i=this.getRenderRange(this.lastRenderTop,this.lastRenderHeight),o=this.getRenderRange(e,t),r=l.relativeComplement(o,i),s=l.relativeComplement(i,o);if(r.forEach(function(e){return l.each(e,function(e){return n.insertItemInDOM(n.items[e],e)})}),s.forEach(function(e){return l.each(e,function(e){return n.removeItemFromDOM(n.items[e])})}),h.canUseTranslate3d()&&!d.isWindows){var a="translate3d(0px, -"+e+"px, 0px)";this.rowsContainer.style.transform=a,this.rowsContainer.style.webkitTransform=a}else this.rowsContainer.style.top="-"+e+"px";this.lastRenderTop=e,this.lastRenderHeight=t},e.prototype.insertItemInDOM=function(e,t){e.row||(e.row=this.cache.alloc(e.templateId)),e.row.domNode.parentElement||this.rowsContainer.appendChild(e.row.domNode);var n=this.renderers[e.templateId];e.row.domNode.style.top=this.elementTop(t)+"px",e.row.domNode.style.height=e.size+"px",e.row.domNode.setAttribute("data-index",""+t),n.renderElement(e.element,t,e.row.templateData)},e.prototype.removeItemFromDOM=function(e){this.cache.release(e.row),e.row=null},e.prototype.getContentHeight=function(){return this.rangeMap.size},e.prototype.getScrollTop=function(){return this.scrollableElement.getScrollState().scrollTop},e.prototype.setScrollTop=function(e){this.scrollableElement.updateState({scrollTop:e})},Object.defineProperty(e.prototype,"scrollTop",{get:function(){return this.getScrollTop()},set:function(e){this.setScrollTop(e)},enumerable:!0,configurable:!0}),e.prototype.addListener=function(e,t,n){var i=this,s=t,a=this.domNode;return p.indexOf(e)>-1?t=function(e){return i.fireScopedEvent(e,s,i.getItemIndexFromMouseEvent(e))}:e===o.EventType.Tap&&(a=this.rowsContainer,t=function(e){return i.fireScopedEvent(e,s,i.getItemIndexFromGestureEvent(e))}),r.addDisposableListener(a,e,t,n)},e.prototype.fireScopedEvent=function(e,t,i){if(!(i<0)){var o=this.items[i].element;t(n.assign(e,{element:o,index:i}))}},e.prototype.onScroll=function(e){this.render(e.scrollTop,e.height)},e.prototype.onTouchChange=function(e){e.preventDefault(),e.stopPropagation(),this.scrollTop-=e.translationY},e.prototype.getItemIndexFromMouseEvent=function(e){return this.getItemIndexFromEventTarget(e.target)},e.prototype.getItemIndexFromGestureEvent=function(e){return this.getItemIndexFromEventTarget(e.initialTarget)},e.prototype.getItemIndexFromEventTarget=function(e){for(;e instanceof HTMLElement&&e!==this.rowsContainer;){var t=e,n=t.getAttribute("data-index");if(n){var i=Number(n);if(!isNaN(i))return i}e=t.parentElement}return-1},e.prototype.getRenderRange=function(e,t){return{start:this.rangeMap.indexAt(e),end:this.rangeMap.indexAfter(e+t-1)}},e.prototype.dispose=function(){this.items=null,this._domNode&&this._domNode.parentElement&&(this._domNode.parentNode.removeChild(this._domNode),this._domNode=null),this.disposables=i.dispose(this.disposables)},e}();t.ListView=g});var v=this&&this.__decorate||function(e,t,n,i){var o,r=arguments.length,s=r<3?t:null===i?i=Object.getOwnPropertyDescriptor(t,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)s=Reflect.decorate(e,t,n,i);else for(var a=e.length-1;a>=0;a--)(o=e[a])&&(s=(r<3?o(s):r>3?o(t,n,s):o(t,n))||s);return r>3&&s&&Object.defineProperty(t,n,s),s};define(d[234],h([1,0,3,29,33,136,446,4,15,74,65,11,124,232,32,26,213]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m){"use strict";function _(e){return u.isMacintosh?e.metaKey:e.ctrlKey}function y(e){return e.shiftKey}function C(e){return _(e)||y(e)}function b(e,t){var n=e.indexOf(t);if(-1===n)return[];for(var i=[],o=n-1;o>=0&&e[o]===t-(n-o);)i.push(e[o--]);for(i.reverse(),o=n;o=e.length)n.push(t[o++]);else if(o>=t.length)n.push(e[i++]);else{if(e[i]===t[o]){n.push(e[i]),i++,o++;continue}e[i]=e.length)n.push(t[o++]);else if(o>=t.length)n.push(e[i++]);else{if(e[i]===t[o]){i++,o++;continue}e[i]-1}).forEach(function(e){var n=e.index,i=e.templateData;return t.trait.renderIndex(n,i.container)})},e.prototype.splice=function(e,t){for(var n=0;n=o}).map(function(e){return e+i}));this.renderer.splice(e,t),this.set(r)},e.prototype.renderIndex=function(e,t){a.toggleClass(t,this._trait,this.contains(e))},e.prototype.set=function(e){var t=this.indexes;this.indexes=e;var n=w(t,e);return this.renderer.renderIndexes(n),this._onChange.fire({indexes:e}),t},e.prototype.get=function(){return this.indexes},e.prototype.contains=function(e){return this.indexes.some(function(t){return t===e})},e.prototype.dispose=function(){this.indexes=null,this._onChange=n.dispose(this._onChange)},v([s.memoize],e.prototype,"renderer",null),e}(),N=function(e){function t(t){var n=e.call(this,"focused")||this;return n.getDomId=t,n}return f(t,e),t.prototype.renderIndex=function(t,n){e.prototype.renderIndex.call(this,t,n),n.setAttribute("role","treeitem"),n.setAttribute("id",this.getDomId(t))},t}(x),M=function(){function e(){this.length=0}return Object.defineProperty(e.prototype,"templateId",{get:function(){return"aria"},enumerable:!0,configurable:!0}),e.prototype.splice=function(e,t,n){this.length+=n.length-t},e.prototype.renderTemplate=function(e){return e},e.prototype.renderElement=function(e,t,n){n.setAttribute("aria-setsize",""+this.length),n.setAttribute("aria-posinset",""+(t+1))},e.prototype.disposeTemplate=function(e){},e}(),T=function(){function e(e,t,n){this.trait=e,this.view=t,this.getId=n}return e.prototype.splice=function(e,t,n){var i=this;if(!this.getId)return this.trait.splice(e,t,n.map(function(e){return!1}));var o=this.trait.get().map(function(e){return i.getId(i.view.element(e))}),r=n.map(function(e){return o.indexOf(i.getId(e))>-1});this.trait.splice(e,t,r)},e}(),k=function(){function e(e,t){this.list=e,this.view=t,this.disposables=[];var n=d.chain(h.domEvent(t.domNode,"keydown")).map(function(e){return new c.StandardKeyboardEvent(e)});n.filter(function(e){return 3===e.keyCode}).on(this.onEnter,this,this.disposables),n.filter(function(e){return 16===e.keyCode}).on(this.onUpArrow,this,this.disposables),n.filter(function(e){return 18===e.keyCode}).on(this.onDownArrow,this,this.disposables),n.filter(function(e){return 11===e.keyCode}).on(this.onPageUpArrow,this,this.disposables),n.filter(function(e){return 12===e.keyCode}).on(this.onPageDownArrow,this,this.disposables)}return e.prototype.onEnter=function(e){e.preventDefault(),e.stopPropagation(),this.list.setSelection(this.list.getFocus()),this.list.open(this.list.getFocus())},e.prototype.onUpArrow=function(e){e.preventDefault(),e.stopPropagation(),this.list.focusPrevious(),this.list.reveal(this.list.getFocus()[0]),this.view.domNode.focus()},e.prototype.onDownArrow=function(e){e.preventDefault(),e.stopPropagation(),this.list.focusNext(),this.list.reveal(this.list.getFocus()[0]),this.view.domNode.focus()},e.prototype.onPageUpArrow=function(e){e.preventDefault(),e.stopPropagation(),this.list.focusPreviousPage(),this.list.reveal(this.list.getFocus()[0]),this.view.domNode.focus()},e.prototype.onPageDownArrow=function(e){e.preventDefault(),e.stopPropagation(),this.list.focusNextPage(),this.list.reveal(this.list.getFocus()[0]),this.view.domNode.focus()},e.prototype.dispose=function(){this.disposables=n.dispose(this.disposables)},e}(),I=function(){function e(e,t,n){void 0===n&&(n={});var i=this;this.list=e,this.view=t,this.options=n,this.disposables=[],this.disposables.push(t.addListener("mousedown",function(e){return i.onMouseDown(e)})),this.disposables.push(t.addListener("click",function(e){return i.onPointer(e)})),this.disposables.push(t.addListener("dblclick",function(e){return i.onDoubleClick(e)})),this.disposables.push(t.addListener("touchstart",function(e){return i.onMouseDown(e)})),this.disposables.push(t.addListener(l.EventType.Tap,function(e){return i.onPointer(e)}))}return Object.defineProperty(e.prototype,"onContextMenu",{get:function(){var e=this,t=d.chain(h.domEvent(this.view.domNode,"keydown")).map(function(e){return new c.StandardKeyboardEvent(e)}).filter(function(t){return e.list.getFocus().length>0}).filter(function(e){return 58===e.keyCode||e.shiftKey&&68===e.keyCode}).map(function(t){var n=e.list.getFocus()[0];return{index:n,element:e.view.element(n),anchor:e.view.domElement(n)}}).filter(function(e){return!!e.anchor}).event,n=d.chain(d.fromCallback(function(t){return e.view.addListener("contextmenu",t)})).map(function(e){return{element:e.element,index:e.index,anchor:{x:e.clientX+1,y:e.clientY}}}).event;return d.any(t,n)},enumerable:!0,configurable:!0}),e.prototype.onMouseDown=function(e){e.preventDefault(),e.stopPropagation(),this.view.domNode.focus();var t=this.list.getFocus()[0];if(t=void 0===t?this.list.getSelection()[0]:t,y(e))return this.changeSelection(e,t);var n=e.index;if(this.list.setFocus([n]),C(e))return this.changeSelection(e,t);this.options.selectOnMouseDown&&(this.list.setSelection([n]),this.list.open([n]))},e.prototype.onPointer=function(e){if(e.preventDefault(),e.stopPropagation(),!C(e)){var t=this.list.getFocus();this.list.setSelection(t),this.list.open(t)}},e.prototype.onDoubleClick=function(e){if(e.preventDefault(),e.stopPropagation(),!C(e)){var t=this.list.getFocus();this.list.setSelection(t),this.list.pin(t)}},e.prototype.changeSelection=function(e,t){var n=e.index;if(y(e)&&void 0!==t){var i=Math.min(t,n),r=Math.max(t,n),s=o.range(r+1,i),a=b(w(u=this.list.getSelection(),[t]),t);if(0===a.length)return;l=w(s,S(u,a));this.list.setSelection(l)}else if(_(e)){var u=this.list.getSelection(),l=u.filter(function(e){return e!==n});u.length===l.length?this.list.setSelection(l.concat([n])):this.list.setSelection(l)}},e.prototype.dispose=function(){this.disposables=n.dispose(this.disposables)},v([s.memoize],e.prototype,"onContextMenu",null),e}(),D={listFocusBackground:g.Color.fromHex("#073655"),listActiveSelectionBackground:g.Color.fromHex("#0E639C"),listActiveSelectionForeground:g.Color.fromHex("#FFFFFF"),listFocusAndSelectionBackground:g.Color.fromHex("#094771"),listFocusAndSelectionForeground:g.Color.fromHex("#FFFFFF"),listInactiveSelectionBackground:g.Color.fromHex("#3F3F46"),listHoverBackground:g.Color.fromHex("#2A2D2E"),listDropBackground:g.Color.fromHex("#383B3D")},O={keyboardSupport:!0,mouseSupport:!0},R=function(e,t){return e-t},P=function(){function e(e,t){this._templateId=e,this.renderers=t}return Object.defineProperty(e.prototype,"templateId",{get:function(){return this._templateId},enumerable:!0,configurable:!0}),e.prototype.renderTemplate=function(e){return this.renderers.map(function(t){return t.renderTemplate(e)})},e.prototype.renderElement=function(e,t,n){this.renderers.forEach(function(i,o){return i.renderElement(e,t,n[o])})},e.prototype.disposeTemplate=function(e){this.renderers.forEach(function(t,n){return t.disposeTemplate(e[n])})},e}(),A=function(){function e(t,n,i,o){void 0===o&&(o=O);var r=this;this.idPrefix="list_id_"+ ++e.InstanceCount,this._onContextMenu=d.default.None,this._onOpen=new d.Emitter,this._onPin=new d.Emitter,this._onDispose=new d.Emitter;var s=new M;if(this.focus=new N(function(e){return r.getElementDomId(e)}),this.selection=new x("selected"),this.eventBufferer=new d.EventBufferer,m.mixin(o,D,!1),i=i.map(function(e){return new P(e.templateId,[s,r.focus.renderer,r.selection.renderer,e])}),this.view=new p.ListView(t,n,i,o),this.view.domNode.setAttribute("role","tree"),a.addClass(this.view.domNode,this.idPrefix),this.view.domNode.tabIndex=0,this.styleElement=a.createStyleSheet(this.view.domNode),this.spliceable=new E([s,new T(this.focus,this.view,o.identityProvider),new T(this.selection,this.view,o.identityProvider),this.view]),this.disposables=[this.focus,this.selection,this.view,this._onDispose],this.onDOMFocus=d.mapEvent(h.domEvent(this.view.domNode,"focus",!0),function(){return null}),this.onDOMBlur=d.mapEvent(h.domEvent(this.view.domNode,"blur",!0),function(){return null}),"boolean"!=typeof o.keyboardSupport||o.keyboardSupport){u=new k(this,this.view);this.disposables.push(u)}if("boolean"!=typeof o.mouseSupport||o.mouseSupport){var u=new I(this,this.view,o);this.disposables.push(u),this._onContextMenu=u.onContextMenu}this.onFocusChange(this._onFocusChange,this,this.disposables),this.onSelectionChange(this._onSelectionChange,this,this.disposables),o.ariaLabel&&this.view.domNode.setAttribute("aria-label",o.ariaLabel),this.style(o)}return Object.defineProperty(e.prototype,"onFocusChange",{get:function(){var e=this;return d.mapEvent(this.eventBufferer.wrapEvent(this.focus.onChange),function(t){return e.toListEvent(t)})},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onSelectionChange",{get:function(){var e=this;return d.mapEvent(this.eventBufferer.wrapEvent(this.selection.onChange),function(t){return e.toListEvent(t)})},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onContextMenu",{get:function(){return this._onContextMenu},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onOpen",{get:function(){var e=this;return d.mapEvent(this._onOpen.event,function(t){return e.toListEvent({indexes:t})})},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onPin",{get:function(){var e=this;return d.mapEvent(this._onPin.event,function(t){return e.toListEvent({indexes:t})})},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onDispose",{get:function(){return this._onDispose.event},enumerable:!0,configurable:!0}),e.prototype.splice=function(e,t,n){var i=this;void 0===n&&(n=[]),this.eventBufferer.bufferEvents(function(){return i.spliceable.splice(e,t,n)})},Object.defineProperty(e.prototype,"length",{get:function(){return this.view.length},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"contentHeight",{get:function(){return this.view.getContentHeight()},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"scrollTop",{get:function(){return this.view.getScrollTop()},set:function(e){this.view.setScrollTop(e)},enumerable:!0,configurable:!0}),e.prototype.layout=function(e){this.view.layout(e)},e.prototype.setSelection=function(e){e=e.sort(R),this.selection.set(e)},e.prototype.selectNext=function(e,t){if(void 0===e&&(e=1),void 0===t&&(t=!1),0!==this.length){var n=this.selection.get(),i=n.length>0?n[0]+e:0;this.setSelection(t?[i%this.length]:[Math.min(i,this.length-1)])}},e.prototype.selectPrevious=function(e,t){if(void 0===e&&(e=1),void 0===t&&(t=!1),0!==this.length){var n=this.selection.get(),i=n.length>0?n[0]-e:0;t&&i<0&&(i=this.length+i%this.length),this.setSelection([Math.max(i,0)])}},e.prototype.getSelection=function(){return this.selection.get()},e.prototype.getSelectedElements=function(){var e=this;return this.getSelection().map(function(t){return e.view.element(t)})},e.prototype.setFocus=function(e){e=e.sort(R),this.focus.set(e)},e.prototype.focusNext=function(e,t){if(void 0===e&&(e=1),void 0===t&&(t=!1),0!==this.length){var n=this.focus.get(),i=n.length>0?n[0]+e:0;this.setFocus(t?[i%this.length]:[Math.min(i,this.length-1)])}},e.prototype.focusPrevious=function(e,t){if(void 0===e&&(e=1),void 0===t&&(t=!1),0!==this.length){var n=this.focus.get(),i=n.length>0?n[0]-e:0;t&&i<0&&(i=(this.length+i%this.length)%this.length),this.setFocus([Math.max(i,0)])}},e.prototype.focusNextPage=function(){var e=this,t=this.view.indexAt(this.view.getScrollTop()+this.view.renderHeight);t=0===t?0:t-1;var n=this.view.element(t);if(this.getFocusedElements()[0]!==n)this.setFocus([t]);else{var i=this.view.getScrollTop();this.view.setScrollTop(i+this.view.renderHeight-this.view.elementHeight(t)),this.view.getScrollTop()!==i&&setTimeout(function(){return e.focusNextPage()},0)}},e.prototype.focusPreviousPage=function(){var e,t=this,n=this.view.getScrollTop();e=0===n?this.view.indexAt(n):this.view.indexAfter(n-1);var i=this.view.element(e);if(this.getFocusedElements()[0]!==i)this.setFocus([e]);else{var o=n;this.view.setScrollTop(n-this.view.renderHeight),this.view.getScrollTop()!==o&&setTimeout(function(){return t.focusPreviousPage()},0)}},e.prototype.focusLast=function(){0!==this.length&&this.setFocus([this.length-1])},e.prototype.focusFirst=function(){0!==this.length&&this.setFocus([0])},e.prototype.getFocus=function(){return this.focus.get()},e.prototype.getFocusedElements=function(){var e=this;return this.getFocus().map(function(t){return e.view.element(t)})},e.prototype.reveal=function(e,t){var n=this.view.getScrollTop(),o=this.view.elementTop(e),r=this.view.elementHeight(e);if(i.isNumber(t)){t=(t=t<0?0:t)>1?1:t;var s=r-this.view.renderHeight;this.view.setScrollTop(s*t+o)}else{var a=o+r,u=n+this.view.renderHeight;o=u&&this.view.setScrollTop(a-this.view.renderHeight)}},e.prototype.getElementDomId=function(e){return this.idPrefix+"_"+e},e.prototype.isDOMFocused=function(){return this.view.domNode===document.activeElement},e.prototype.getHTMLElement=function(){return this.view.domNode},e.prototype.open=function(e){this._onOpen.fire(e)},e.prototype.pin=function(e){this._onPin.fire(e)},e.prototype.style=function(e){var t=[];e.listFocusBackground&&t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.focused { background-color: "+e.listFocusBackground+"; }"),e.listFocusForeground&&t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.focused { color: "+e.listFocusForeground+"; }"),e.listActiveSelectionBackground&&(t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.selected { background-color: "+e.listActiveSelectionBackground+"; }"),t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.selected:hover { background-color: "+e.listActiveSelectionBackground+"; }")),e.listActiveSelectionForeground&&t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.selected { color: "+e.listActiveSelectionForeground+"; }"),e.listFocusAndSelectionBackground&&t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.selected.focused { background-color: "+e.listFocusAndSelectionBackground+"; }"),e.listFocusAndSelectionForeground&&t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.selected.focused { color: "+e.listFocusAndSelectionForeground+"; }"),e.listInactiveFocusBackground&&(t.push(".monaco-list."+this.idPrefix+" .monaco-list-row.focused { background-color: "+e.listInactiveFocusBackground+"; }"),t.push(".monaco-list."+this.idPrefix+" .monaco-list-row.focused:hover { background-color: "+e.listInactiveFocusBackground+"; }")),e.listInactiveSelectionBackground&&(t.push(".monaco-list."+this.idPrefix+" .monaco-list-row.selected { background-color: "+e.listInactiveSelectionBackground+"; }"),t.push(".monaco-list."+this.idPrefix+" .monaco-list-row.selected:hover { background-color: "+e.listInactiveSelectionBackground+"; }")),e.listInactiveSelectionForeground&&t.push(".monaco-list."+this.idPrefix+" .monaco-list-row.selected { color: "+e.listInactiveSelectionForeground+"; }"),e.listHoverBackground&&t.push(".monaco-list."+this.idPrefix+" .monaco-list-row:hover { background-color: "+e.listHoverBackground+"; }"),e.listHoverForeground&&t.push(".monaco-list."+this.idPrefix+" .monaco-list-row:hover { color: "+e.listHoverForeground+"; }"),e.listSelectionOutline&&t.push(".monaco-list."+this.idPrefix+" .monaco-list-row.selected { outline: 1px dotted "+e.listSelectionOutline+"; outline-offset: -1px; }"),e.listFocusOutline&&t.push(".monaco-list."+this.idPrefix+":focus .monaco-list-row.focused { outline: 1px solid "+e.listFocusOutline+"; outline-offset: -1px; }"),e.listInactiveFocusOutline&&t.push(".monaco-list."+this.idPrefix+" .monaco-list-row.focused { outline: 1px dotted "+e.listInactiveFocusOutline+"; outline-offset: -1px; }"),e.listHoverOutline&&t.push(".monaco-list."+this.idPrefix+" .monaco-list-row:hover { outline: 1px dashed "+e.listHoverOutline+"; outline-offset: -1px; }"),this.styleElement.innerHTML=t.join("\n")},e.prototype.toListEvent=function(e){var t=this,n=e.indexes;return{indexes:n,elements:n.map(function(e){return t.view.element(e)})}},e.prototype._onFocusChange=function(){var e=this.focus.get();e.length>0?this.view.domNode.setAttribute("aria-activedescendant",this.getElementDomId(e[0])):this.view.domNode.removeAttribute("aria-activedescendant"),this.view.domNode.setAttribute("role","tree"),a.toggleClass(this.view.domNode,"element-focused",e.length>0)},e.prototype._onSelectionChange=function(){var e=this.selection.get();a.toggleClass(this.view.domNode,"selection-none",0===e.length),a.toggleClass(this.view.domNode,"selection-single",1===e.length),a.toggleClass(this.view.domNode,"selection-multiple",e.length>1)},e.prototype.dispose=function(){this._onDispose.fire(),this.disposables=n.dispose(this.disposables)},e.InstanceCount=0,v([s.memoize],e.prototype,"onFocusChange",null),v([s.memoize],e.prototype,"onSelectionChange",null),v([s.memoize],e.prototype,"onOpen",null),v([s.memoize],e.prototype,"onPin",null),e}();t.List=A}),define(d[238],h([1,0,15,28,7,3,4,139,74,9,47,65,461,107,63,48,472,448,11]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_,y){"use strict";function C(e){try{e.parentElement.removeChild(e)}catch(e){}}function b(e,t){return!e&&!t||!(!e||!t)&&(e.accept===t.accept&&(e.bubble===t.bubble&&e.effect===t.effect))}Object.defineProperty(t,"__esModule",{value:!0});var w=function(){function e(e){this.context=e,this._cache={"":[]}}return e.prototype.alloc=function(e){var t=this.cache(e).pop();if(!t){var n=document.createElement("div");n.className="content";var i=document.createElement("div");i.appendChild(n),t={element:i,templateId:e,templateData:this.context.renderer.renderTemplate(this.context.tree,e,n)}}return t},e.prototype.release=function(e,t){C(t.element),this.cache(e).push(t)},e.prototype.cache=function(e){return this._cache[e]||(this._cache[e]=[])},e.prototype.garbageCollect=function(){var e=this;this._cache&&Object.keys(this._cache).forEach(function(t){e._cache[t].forEach(function(n){e.context.renderer.disposeTemplate(e.context.tree,t,n.templateData),n.element=null,n.templateData=null}),delete e._cache[t]})},e.prototype.dispose=function(){this.garbageCollect(),this._cache=null,this.context=null},e}();t.RowCache=w;var S=function(){function e(e,t){var n=this;this.context=e,this.model=t,this.id=this.model.id,this.row=null,this.top=0,this.height=t.getHeight(),this._styles={},t.getAllTraits().forEach(function(e){return n._styles[e]=!0}),t.isExpanded()&&this.addClass("expanded")}return Object.defineProperty(e.prototype,"expanded",{set:function(e){e?this.addClass("expanded"):this.removeClass("expanded")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"loading",{set:function(e){e?this.addClass("loading"):this.removeClass("loading")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"draggable",{get:function(){return this._draggable},set:function(e){this._draggable=e,this.render(!0)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"dropTarget",{set:function(e){e?this.addClass("drop-target"):this.removeClass("drop-target")},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"element",{get:function(){return this.row&&this.row.element},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"templateId",{get:function(){return this._templateId||(this._templateId=this.context.renderer.getTemplateId&&this.context.renderer.getTemplateId(this.context.tree,this.model.getElement()))},enumerable:!0,configurable:!0}),e.prototype.addClass=function(e){this._styles[e]=!0,this.render(!0)},e.prototype.removeClass=function(e){delete this._styles[e],this.render(!0)},e.prototype.render=function(e){var t=this;if(void 0===e&&(e=!1),this.model&&this.element){var n=["monaco-tree-row"];n.push.apply(n,Object.keys(this._styles)),this.model.hasChildren()&&n.push("has-children"),this.element.className=n.join(" "),this.element.draggable=this.draggable,this.element.style.height=this.height+"px",this.element.setAttribute("role","treeitem");var i=this.context.accessibilityProvider,o=i.getAriaLabel(this.context.tree,this.model.getElement());if(o&&this.element.setAttribute("aria-label",o),i.getPosInSet&&i.getSetSize&&(this.element.setAttribute("aria-setsize",i.getSetSize()),this.element.setAttribute("aria-posinset",i.getPosInSet(this.context.tree,this.model.getElement()))),this.model.hasTrait("focused")){var r=l.safeBtoa(this.model.id);this.element.setAttribute("aria-selected","true"),this.element.setAttribute("id",r)}else this.element.setAttribute("aria-selected","false"),this.element.removeAttribute("id");this.model.hasChildren()?this.element.setAttribute("aria-expanded",String(!!this.model.isExpanded())):this.element.removeAttribute("aria-expanded"),this.element.setAttribute("aria-level",String(this.model.getDepth())),this.context.options.paddingOnRow?this.element.style.paddingLeft=this.context.options.twistiePixels+(this.model.getDepth()-1)*this.context.options.indentPixels+"px":(this.element.style.paddingLeft=(this.model.getDepth()-1)*this.context.options.indentPixels+"px",this.row.element.firstElementChild.style.paddingLeft=this.context.options.twistiePixels+"px");var a=this.context.dnd.getDragURI(this.context.tree,this.model.getElement());a!==this.uri&&(this.unbindDragStart&&(this.unbindDragStart.dispose(),this.unbindDragStart=null),a?(this.uri=a,this.draggable=!0,this.unbindDragStart=s.addDisposableListener(this.element,"dragstart",function(e){t.onDragStart(e)})):this.uri=null),e||this.context.renderer.renderElement(this.context.tree,this.model.getElement(),this.templateId,this.row.templateData)}},e.prototype.insertInDOM=function(e,t){if(this.row||(this.row=this.context.cache.alloc(this.templateId),this.element[L.BINDING]=this),!this.element.parentElement){if(null===t)e.appendChild(this.element);else try{e.insertBefore(this.element,t)}catch(t){console.warn("Failed to locate previous tree element"),e.appendChild(this.element)}this.render()}},e.prototype.removeFromDOM=function(){this.row&&(this.unbindDragStart&&(this.unbindDragStart.dispose(),this.unbindDragStart=null),this.uri=null,this.element[L.BINDING]=null,this.context.cache.release(this.templateId,this.row),this.row=null)},e.prototype.dispose=function(){this.row=null,this.model=null},e}();t.ViewItem=S;var E=function(e){function t(t,n,i){var o=e.call(this,t,n)||this;return o.row={element:i,templateData:null,templateId:null},o}return f(t,e),t.prototype.render=function(){if(this.model&&this.element){var e=["monaco-tree-wrapper"];e.push.apply(e,Object.keys(this._styles)),this.model.hasChildren()&&e.push("has-children"),this.element.className=e.join(" ")}},t.prototype.insertInDOM=function(e,t){},t.prototype.removeFromDOM=function(){},t}(S),L=function(e){function t(n,o){var r=e.call(this)||this;r.lastClickTimeStamp=0,r.isRefreshing=!1,r.refreshingPreviousChildrenIds={},r._onDOMFocus=new y.Emitter,r._onDOMBlur=new y.Emitter,t.counter++,r.instance=t.counter,r.context={dataSource:n.dataSource,renderer:n.renderer,controller:n.controller,dnd:n.dnd,filter:n.filter,sorter:n.sorter,tree:n.tree,accessibilityProvider:n.accessibilityProvider,options:n.options,cache:new w(n)},r.modelListeners=[],r.viewListeners=[],r.dragAndDropListeners=[],r.model=null,r.items={},r.domNode=document.createElement("div"),r.domNode.className="monaco-tree no-focused-item monaco-tree-instance-"+r.instance,r.domNode.tabIndex=0,r.styleElement=s.createStyleSheet(r.domNode),r.domNode.setAttribute("role","tree"),r.context.options.ariaLabel&&r.domNode.setAttribute("aria-label",r.context.options.ariaLabel),r.context.options.alwaysFocused&&s.addClass(r.domNode,"focused"),r.context.options.paddingOnRow||s.addClass(r.domNode,"no-row-padding"),r.wrapper=document.createElement("div"),r.wrapper.className="monaco-tree-wrapper",r.scrollableElement=new g.ScrollableElement(r.wrapper,{alwaysConsumeMouseWheel:!0,horizontal:m.ScrollbarVisibility.Hidden,vertical:void 0!==n.options.verticalScrollMode?n.options.verticalScrollMode:m.ScrollbarVisibility.Auto,useShadows:n.options.useShadows}),r.scrollableElement.onScroll(function(e){r.render(e.scrollTop,e.height),r.emit("scroll",e)}),i.isIE?(r.wrapper.style.msTouchAction="none",r.wrapper.style.msContentZooming="none"):r.wrapperGesture=new u.Gesture(r.wrapper),r.rowsContainer=document.createElement("div"),r.rowsContainer.className="monaco-tree-rows",n.options.showTwistie&&(r.rowsContainer.className+=" show-twisties");var a=s.trackFocus(r.domNode);return a.addFocusListener(function(){return r.onFocus()}),a.addBlurListener(function(){return r.onBlur()}),r.viewListeners.push(a),r.viewListeners.push(s.addDisposableListener(r.domNode,"keydown",function(e){return r.onKeyDown(e)})),r.viewListeners.push(s.addDisposableListener(r.domNode,"keyup",function(e){return r.onKeyUp(e)})),r.viewListeners.push(s.addDisposableListener(r.domNode,"mousedown",function(e){return r.onMouseDown(e)})),r.viewListeners.push(s.addDisposableListener(r.domNode,"mouseup",function(e){return r.onMouseUp(e)})),r.viewListeners.push(s.addDisposableListener(r.wrapper,"click",function(e){return r.onClick(e)})),r.viewListeners.push(s.addDisposableListener(r.wrapper,"auxclick",function(e){return r.onClick(e)})),r.viewListeners.push(s.addDisposableListener(r.domNode,"contextmenu",function(e){return r.onContextMenu(e)})),r.viewListeners.push(s.addDisposableListener(r.wrapper,u.EventType.Tap,function(e){return r.onTap(e)})),r.viewListeners.push(s.addDisposableListener(r.wrapper,u.EventType.Change,function(e){return r.onTouchChange(e)})),i.isIE&&(r.viewListeners.push(s.addDisposableListener(r.wrapper,"MSPointerDown",function(e){return r.onMsPointerDown(e)})),r.viewListeners.push(s.addDisposableListener(r.wrapper,"MSGestureTap",function(e){return r.onMsGestureTap(e)})),r.viewListeners.push(s.addDisposableThrottledListener(r.wrapper,"MSGestureChange",function(e){return r.onThrottledMsGestureChange(e)},function(e,t){t.stopPropagation(),t.preventDefault();var n={translationY:t.translationY,translationX:t.translationX};return e&&(n.translationY+=e.translationY,n.translationX+=e.translationX),n}))),r.viewListeners.push(s.addDisposableListener(window,"dragover",function(e){return r.onDragOver(e)})),r.viewListeners.push(s.addDisposableListener(r.wrapper,"drop",function(e){return r.onDrop(e)})),r.viewListeners.push(s.addDisposableListener(window,"dragend",function(e){return r.onDragEnd(e)})),r.viewListeners.push(s.addDisposableListener(window,"dragleave",function(e){return r.onDragOver(e)})),r.wrapper.appendChild(r.rowsContainer),r.domNode.appendChild(r.scrollableElement.getDomNode()),o.appendChild(r.domNode),r.lastRenderTop=0,r.lastRenderHeight=0,r.didJustPressContextMenuKey=!1,r.currentDropTarget=null,r.currentDropTargets=[],r.shouldInvalidateDropReaction=!1,r.dragAndDropScrollInterval=null,r.dragAndDropScrollTimeout=null,r.onHiddenScrollTop=null,r.onRowsChanged(),r.layout(),r.setupMSGesture(),r.applyStyles(n.options),r}return f(t,e),Object.defineProperty(t.prototype,"onDOMFocus",{get:function(){return this._onDOMFocus.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onDOMBlur",{get:function(){return this._onDOMBlur.event},enumerable:!0,configurable:!0}),t.prototype.applyStyles=function(e){var t=[];e.listFocusBackground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { background-color: "+e.listFocusBackground+"; }"),e.listFocusForeground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) { color: "+e.listFocusForeground+"; }"),e.listActiveSelectionBackground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: "+e.listActiveSelectionBackground+"; }"),e.listActiveSelectionForeground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: "+e.listActiveSelectionForeground+"; }"),e.listFocusAndSelectionBackground&&t.push("\n\t\t\t\t.monaco-tree-drag-image,\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { background-color: "+e.listFocusAndSelectionBackground+"; }\n\t\t\t"),e.listFocusAndSelectionForeground&&t.push("\n\t\t\t\t.monaco-tree-drag-image,\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.focused.selected:not(.highlighted) { color: "+e.listFocusAndSelectionForeground+"; }\n\t\t\t"),e.listInactiveSelectionBackground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: "+e.listInactiveSelectionBackground+"; }"),e.listInactiveSelectionForeground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: "+e.listInactiveSelectionForeground+"; }"),e.listHoverBackground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { background-color: "+e.listHoverBackground+"; }"),e.listHoverForeground&&t.push(".monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) { color: "+e.listHoverForeground+"; }"),e.listDropBackground&&t.push("\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-wrapper.drop-target,\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row.drop-target { background-color: "+e.listDropBackground+" !important; color: inherit !important; }\n\t\t\t"),e.listFocusOutline&&t.push("\n\t\t\t\t.monaco-tree-drag-image\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{ border: 1px solid "+e.listFocusOutline+"; background: #000; }\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row \t\t\t\t\t\t\t\t\t\t\t\t\t\t{ border: 1px solid transparent; }\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.focused:not(.highlighted) \t\t\t\t\t\t{ border: 1px dotted "+e.listFocusOutline+"; }\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+".focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) \t\t\t\t\t\t{ border: 1px solid "+e.listFocusOutline+"; }\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) \t\t\t\t\t\t\t{ border: 1px solid "+e.listFocusOutline+"; }\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row:hover:not(.highlighted):not(.selected):not(.focused) \t{ border: 1px dashed "+e.listFocusOutline+"; }\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-wrapper.drop-target,\n\t\t\t\t.monaco-tree.monaco-tree-instance-"+this.instance+" .monaco-tree-rows > .monaco-tree-row.drop-target\t\t\t\t\t\t\t\t\t\t\t\t{ border: 1px dashed "+e.listFocusOutline+"; }\n\t\t\t"),this.styleElement.innerHTML=t.join("\n")},t.prototype.createViewItem=function(e){return new S(this.context,e)},t.prototype.getHTMLElement=function(){return this.domNode},t.prototype.focus=function(){this.domNode.focus()},t.prototype.isFocused=function(){return document.activeElement===this.domNode},t.prototype.blur=function(){this.domNode.blur()},t.prototype.onVisible=function(){this.scrollTop=this.onHiddenScrollTop,this.onHiddenScrollTop=null,this.setupMSGesture()},t.prototype.setupMSGesture=function(){var e=this;window.MSGesture&&(this.msGesture=new MSGesture,setTimeout(function(){return e.msGesture.target=e.wrapper},100))},t.prototype.onHidden=function(){this.onHiddenScrollTop=this.scrollTop},t.prototype.isTreeVisible=function(){return null===this.onHiddenScrollTop},t.prototype.layout=function(e){this.isTreeVisible()&&(this.viewHeight=e||s.getContentHeight(this.wrapper))},t.prototype.render=function(e,t){var n,i,o=e,r=e+t,s=this.lastRenderTop+this.lastRenderHeight;for(n=this.indexAfter(r)-1,i=this.indexAt(Math.max(s,o));n>=i;n--)this.insertItemInDOM(this.itemAtIndex(n));for(n=Math.min(this.indexAt(this.lastRenderTop),this.indexAfter(r))-1,i=this.indexAt(o);n>=i;n--)this.insertItemInDOM(this.itemAtIndex(n));for(n=this.indexAt(this.lastRenderTop),i=Math.min(this.indexAt(o),this.indexAfter(s));n0&&this.onItemsRefresh(t)},t.prototype.onRefreshing=function(){this.isRefreshing=!0},t.prototype.onRefreshed=function(){this.isRefreshing=!1,this.onRowsChanged()},t.prototype.onRowsChanged=function(e){void 0===e&&(e=this.scrollTop),this.isRefreshing||(this.scrollTop=e)},t.prototype.focusNextPage=function(e){var t=this,n=this.indexAt(this.scrollTop+this.viewHeight);n=0===n?0:n-1;var i=this.itemAtIndex(n).model.getElement();if(this.model.getFocus()!==i)this.model.setFocus(i,e);else{var o=this.scrollTop;this.scrollTop+=this.viewHeight,this.scrollTop!==o&&setTimeout(function(){t.focusNextPage(e)},0)}},t.prototype.focusPreviousPage=function(e){var t,n=this;t=0===this.scrollTop?this.indexAt(this.scrollTop):this.indexAfter(this.scrollTop-1);var i=this.itemAtIndex(t).model.getElement();if(this.model.getFocus()!==i)this.model.setFocus(i,e);else{var o=this.scrollTop;this.scrollTop-=this.viewHeight,this.scrollTop!==o&&setTimeout(function(){n.focusPreviousPage(e)},0)}},Object.defineProperty(t.prototype,"viewHeight",{get:function(){return this.scrollableElement.getScrollState().height},set:function(e){this.scrollableElement.updateState({height:e,scrollHeight:this.getTotalHeight()})},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"scrollTop",{get:function(){return this.scrollableElement.getScrollState().scrollTop},set:function(e){this.scrollableElement.updateState({scrollTop:e,scrollHeight:this.getTotalHeight()})},enumerable:!0,configurable:!0}),t.prototype.getScrollPosition=function(){var e=this.getTotalHeight()-this.viewHeight;return e<=0?0:this.scrollTop/e},t.prototype.setScrollPosition=function(e){var t=this.getTotalHeight()-this.viewHeight;this.scrollTop=t*e},t.prototype.onClearingInput=function(e){var t=e.item;t&&(this.onRemoveItems(new p.MappedIterator(t.getNavigator(),function(e){return e&&e.id})),this.onRowsChanged())},t.prototype.onSetInput=function(e){this.context.cache.garbageCollect(),this.inputItem=new E(this.context,e.item,this.wrapper),this.emit("viewItem:create",{item:this.inputItem.model})},t.prototype.onItemChildrenRefreshing=function(e){var n=e.item,i=this.items[n.id];if(i&&(i.loadingTimer=setTimeout(function(){i.loadingTimer=0,i.loading=!0},t.LOADING_DECORATION_DELAY)),!e.isNested){for(var o,r=[],s=n.getNavigator();o=s.next();)r.push(o.id);this.refreshingPreviousChildrenIds[n.id]=r}},t.prototype.onItemChildrenRefreshed=function(e){var t=this,n=e.item,i=this.items[n.id];if(i&&(i.loadingTimer&&(clearTimeout(i.loadingTimer),i.loadingTimer=0),i.loading=!1),!e.isNested){for(var o,r=this.refreshingPreviousChildrenIds[n.id],s=[],u=n.getNavigator();o=u.next();)s.push(o);var l=Math.abs(r.length-s.length)>1e3,c=void 0,d=void 0;if(l||(d=(c=new a.LcsDiff({getLength:function(){return r.length},getElementHash:function(e){return r[e]}},{getLength:function(){return s.length},getElementHash:function(e){return s[e].id}},null).ComputeDiff(!1)).some(function(e){if(e.modifiedLength>0)for(var n=e.modifiedStart,i=e.modifiedStart+e.modifiedLength;n0&&this.onRemoveItems(new p.ArrayIterator(r,g.originalStart,g.originalStart+g.originalLength)),g.modifiedLength>0){var m=s[g.modifiedStart-1]||n;m=m.getDepth()>0?m:null,this.onInsertItems(new p.ArrayIterator(s,g.modifiedStart,g.modifiedStart+g.modifiedLength),m?m.id:null)}}else(l||c.length)&&(this.onRemoveItems(new p.ArrayIterator(r)),this.onInsertItems(new p.ArrayIterator(s),n.getDepth()>0?n.id:null));(l||c.length)&&this.onRowsChanged()}},t.prototype.onItemsRefresh=function(e){var t=this;this.onRefreshItemSet(e.filter(function(e){return t.items.hasOwnProperty(e.id)})),this.onRowsChanged()},t.prototype.onItemExpanding=function(e){var t=this.items[e.item.id];t&&(t.expanded=!0)},t.prototype.onItemExpanded=function(e){var t=e.item,n=this.items[t.id];if(n){n.expanded=!0;var i=this.onInsertItems(t.getNavigator(),t.id),o=this.scrollTop;n.top+n.height<=this.scrollTop&&(o+=i),this.onRowsChanged(o)}},t.prototype.onItemCollapsing=function(e){var t=e.item,n=this.items[t.id];n&&(n.expanded=!1,this.onRemoveItems(new p.MappedIterator(t.getNavigator(),function(e){return e&&e.id})),this.onRowsChanged())},t.prototype.getRelativeTop=function(e){if(e&&e.isVisible()){var t=this.items[e.id];if(t)return(t.top-this.scrollTop)/(this.viewHeight-t.height)}return-1},t.prototype.onItemReveal=function(e){var t=e.item,n=e.relativeTop,i=this.items[t.id];if(i)if(null!==n){n=(n=n<0?0:n)>1?1:n;var o=i.height-this.viewHeight;this.scrollTop=o*n+i.top}else{var r=i.top+i.height,s=this.scrollTop+this.viewHeight;i.top=s&&(this.scrollTop=r-this.viewHeight)}},t.prototype.onItemAddTrait=function(e){var t=e.item,n=e.trait,i=this.items[t.id];i&&i.addClass(n),"highlighted"===n&&(s.addClass(this.domNode,n),i&&(this.highlightedItemWasDraggable=!!i.draggable,i.draggable&&(i.draggable=!1)))},t.prototype.onItemRemoveTrait=function(e){var t=e.item,n=e.trait,i=this.items[t.id];i&&i.removeClass(n),"highlighted"===n&&(s.removeClass(this.domNode,n),this.highlightedItemWasDraggable&&(i.draggable=!0),this.highlightedItemWasDraggable=!1)},t.prototype.onModelFocusChange=function(){var e=this.model&&this.model.getFocus();s.toggleClass(this.domNode,"no-focused-item",!e),e?this.domNode.setAttribute("aria-activedescendant",l.safeBtoa(this.context.dataSource.getId(this.context.tree,e))):this.domNode.removeAttribute("aria-activedescendant")},t.prototype.onInsertItem=function(e){var t=this;e.onDragStart=function(n){t.onDragStart(e,n)},e.needsRender=!0,this.refreshViewItem(e),this.items[e.id]=e},t.prototype.onRefreshItem=function(e,t){void 0===t&&(t=!1),e.needsRender=e.needsRender||t,this.refreshViewItem(e)},t.prototype.onRemoveItem=function(e){this.removeItemFromDOM(e),e.dispose(),this.emit("viewItem:dispose",{item:this.inputItem.model}),delete this.items[e.id]},t.prototype.refreshViewItem=function(e){e.render(),this.shouldBeRendered(e)?this.insertItemInDOM(e):this.removeItemFromDOM(e)},t.prototype.onClick=function(e){if(!this.lastPointerType||"mouse"===this.lastPointerType){var t=new c.StandardMouseEvent(e),n=this.getItemAround(t.target);n&&(i.isIE&&Date.now()-this.lastClickTimeStamp<300&&(t.detail=2),this.lastClickTimeStamp=Date.now(),this.context.controller.onClick(this.context.tree,n.model.getElement(),t))}},t.prototype.onMouseDown=function(e){if(this.didJustPressContextMenuKey=!1,this.context.controller.onMouseDown&&(!this.lastPointerType||"mouse"===this.lastPointerType)){var t=new c.StandardMouseEvent(e);if(!(t.ctrlKey&&n.isNative&&n.isMacintosh)){var i=this.getItemAround(t.target);i&&this.context.controller.onMouseDown(this.context.tree,i.model.getElement(),t)}}},t.prototype.onMouseUp=function(e){if(this.context.controller.onMouseUp&&(!this.lastPointerType||"mouse"===this.lastPointerType)){var t=new c.StandardMouseEvent(e);if(!(t.ctrlKey&&n.isNative&&n.isMacintosh)){var i=this.getItemAround(t.target);i&&this.context.controller.onMouseUp(this.context.tree,i.model.getElement(),t)}}},t.prototype.onTap=function(e){var t=this.getItemAround(e.initialTarget);t&&this.context.controller.onTap(this.context.tree,t.model.getElement(),e)},t.prototype.onTouchChange=function(e){e.preventDefault(),e.stopPropagation(),this.scrollTop-=e.translationY},t.prototype.onContextMenu=function(e){var t,n;if(e instanceof KeyboardEvent||this.didJustPressContextMenuKey){this.didJustPressContextMenuKey=!1;var i=new d.StandardKeyboardEvent(e);if(!(n=this.model.getFocus()))return;var o=this.context.dataSource.getId(this.context.tree,n),r=this.items[o],a=s.getDomNodePagePosition(r.element);t=new _.KeyboardContextMenuEvent(a.left+a.width,a.top,i)}else{var u=new c.StandardMouseEvent(e),l=this.getItemAround(u.target);if(!l)return;n=l.model.getElement(),t=new _.MouseContextMenuEvent(u)}this.context.controller.onContextMenu(this.context.tree,n,t)},t.prototype.onKeyDown=function(e){var t=new d.StandardKeyboardEvent(e);this.didJustPressContextMenuKey=58===t.keyCode||t.shiftKey&&68===t.keyCode,this.didJustPressContextMenuKey&&(t.preventDefault(),t.stopPropagation()),t.target&&t.target.tagName&&"input"===t.target.tagName.toLowerCase()||this.context.controller.onKeyDown(this.context.tree,t)},t.prototype.onKeyUp=function(e){this.didJustPressContextMenuKey&&this.onContextMenu(e),this.didJustPressContextMenuKey=!1,this.context.controller.onKeyUp(this.context.tree,new d.StandardKeyboardEvent(e))},t.prototype.onDragStart=function(e,n){if(!this.model.getHighlight()){var i,o=e.model.getElement(),r=this.model.getSelection();if(i=r.indexOf(o)>-1?r:[o],n.dataTransfer.effectAllowed="copyMove",n.dataTransfer.setData("URL",e.uri),n.dataTransfer.setDragImage){var s=void 0;s=this.context.dnd.getDragLabel?this.context.dnd.getDragLabel(this.context.tree,i):String(i.length);var a=document.createElement("div");a.className="monaco-tree-drag-image",a.textContent=s,document.body.appendChild(a),n.dataTransfer.setDragImage(a,-10,-10),setTimeout(function(){return document.body.removeChild(a)},0)}this.currentDragAndDropData=new h.ElementsDragAndDropData(i),t.currentExternalDragAndDropData=new h.ExternalElementsDragAndDropData(i),this.context.dnd.onDragStart(this.context.tree,this.currentDragAndDropData,new c.DragMouseEvent(n))}},t.prototype.setupDragAndDropScrollInterval=function(){var e=this,t=s.getTopLeftOffset(this.wrapper).top;this.dragAndDropScrollInterval||(this.dragAndDropScrollInterval=window.setInterval(function(){if(void 0!==e.dragAndDropMouseY){var n=e.dragAndDropMouseY-t,i=0,o=e.viewHeight-35;n<35?i=Math.max(-14,.2*(n-35)):n>o&&(i=Math.min(14,.2*(n-o))),e.scrollTop+=i}},10),this.cancelDragAndDropScrollTimeout(),this.dragAndDropScrollTimeout=window.setTimeout(function(){e.cancelDragAndDropScrollInterval(),e.dragAndDropScrollTimeout=null},1e3))},t.prototype.cancelDragAndDropScrollInterval=function(){this.dragAndDropScrollInterval&&(window.clearInterval(this.dragAndDropScrollInterval),this.dragAndDropScrollInterval=null),this.cancelDragAndDropScrollTimeout()},t.prototype.cancelDragAndDropScrollTimeout=function(){this.dragAndDropScrollTimeout&&(window.clearTimeout(this.dragAndDropScrollTimeout),this.dragAndDropScrollTimeout=null)},t.prototype.onDragOver=function(e){var n=this,i=new c.DragMouseEvent(e),r=this.getItemAround(i.target);if(!r||0===i.posx&&0===i.posy&&i.browserEvent.type===s.EventType.DRAG_LEAVE)return this.currentDropTarget&&(this.currentDropTargets.forEach(function(e){return e.dropTarget=!1}),this.currentDropTargets=[],this.currentDropPromise&&(this.currentDropPromise.cancel(),this.currentDropPromise=null)),this.cancelDragAndDropScrollInterval(),this.currentDropTarget=null,this.currentDropElement=null,this.dragAndDropMouseY=null,!1;if(this.setupDragAndDropScrollInterval(),this.dragAndDropMouseY=i.posy,!this.currentDragAndDropData)if(t.currentExternalDragAndDropData)this.currentDragAndDropData=t.currentExternalDragAndDropData;else{if(!i.dataTransfer.types)return!1;this.currentDragAndDropData=new h.DesktopDragAndDropData}this.currentDragAndDropData.update(i);var a,u,l=r.model;do{if(a=l?l.getElement():this.model.getInput(),!(u=this.context.dnd.onDragOver(this.context.tree,this.currentDragAndDropData,a,i))||u.bubble!==_.DragOverBubble.BUBBLE_UP)break;l=l&&l.parent}while(l);if(!l)return this.currentDropElement=null,!1;var d=u&&u.accept;d?(this.currentDropElement=l.getElement(),i.preventDefault(),i.dataTransfer.dropEffect=u.effect===_.DragOverEffect.COPY?"copy":"move"):this.currentDropElement=null;var p=l.id===this.inputItem.id?this.inputItem:this.items[l.id];if((this.shouldInvalidateDropReaction||this.currentDropTarget!==p||!b(this.currentDropElementReaction,u))&&(this.shouldInvalidateDropReaction=!1,this.currentDropTarget&&(this.currentDropTargets.forEach(function(e){return e.dropTarget=!1}),this.currentDropTargets=[],this.currentDropPromise&&(this.currentDropPromise.cancel(),this.currentDropPromise=null)),this.currentDropTarget=p,this.currentDropElementReaction=u,d)){if(this.currentDropTarget&&(this.currentDropTarget.dropTarget=!0,this.currentDropTargets.push(this.currentDropTarget)),u.bubble===_.DragOverBubble.BUBBLE_DOWN)for(var f,g=l.getNavigator();f=g.next();)(r=this.items[f.id])&&(r.dropTarget=!0,this.currentDropTargets.push(r));u.autoExpand&&(this.currentDropPromise=o.TPromise.timeout(500).then(function(){return n.context.tree.expand(n.currentDropElement)}).then(function(){return n.shouldInvalidateDropReaction=!0}))}return!0},t.prototype.onDrop=function(e){if(this.currentDropElement){var t=new c.DragMouseEvent(e);t.preventDefault(),this.currentDragAndDropData.update(t),this.context.dnd.drop(this.context.tree,this.currentDragAndDropData,this.currentDropElement,t),this.onDragEnd(e)}this.cancelDragAndDropScrollInterval()},t.prototype.onDragEnd=function(e){this.currentDropTarget&&(this.currentDropTargets.forEach(function(e){return e.dropTarget=!1}),this.currentDropTargets=[]),this.currentDropPromise&&(this.currentDropPromise.cancel(),this.currentDropPromise=null),this.cancelDragAndDropScrollInterval(),this.currentDragAndDropData=null,t.currentExternalDragAndDropData=null,this.currentDropElement=null,this.currentDropTarget=null,this.dragAndDropMouseY=null},t.prototype.onFocus=function(){this.context.options.alwaysFocused||s.addClass(this.domNode,"focused"),this._onDOMFocus.fire()},t.prototype.onBlur=function(){this.context.options.alwaysFocused||s.removeClass(this.domNode,"focused"),this.domNode.removeAttribute("aria-activedescendant"),this._onDOMBlur.fire()},t.prototype.onMsPointerDown=function(e){if(this.msGesture){var t=e.pointerType;t!==(e.MSPOINTER_TYPE_MOUSE||"mouse")?t===(e.MSPOINTER_TYPE_TOUCH||"touch")&&(this.lastPointerType="touch",e.stopPropagation(),e.preventDefault(),this.msGesture.addPointer(e.pointerId)):this.lastPointerType="mouse"}},t.prototype.onThrottledMsGestureChange=function(e){this.scrollTop-=e.translationY},t.prototype.onMsGestureTap=function(e){e.initialTarget=document.elementFromPoint(e.clientX,e.clientY),this.onTap(e)},t.prototype.insertItemInDOM=function(e){var t=null,n=this.itemAfter(e);n&&n.element&&(t=n.element),e.insertInDOM(this.rowsContainer,t)},t.prototype.removeItemFromDOM=function(e){e&&e.removeFromDOM()},t.prototype.shouldBeRendered=function(e){return e.topthis.lastRenderTop},t.prototype.getItemAround=function(e){var n=this.inputItem;do{if(e[t.BINDING]&&(n=e[t.BINDING]),e===this.wrapper||e===this.domNode)return n;if(e===document.body)return null}while(e=e.parentElement)},t.prototype.releaseModel=function(){this.model&&(this.modelListeners=r.dispose(this.modelListeners),this.model=null)},t.prototype.dispose=function(){this.scrollableElement.dispose(),this.releaseModel(),this.modelListeners=null,this.viewListeners=r.dispose(this.viewListeners),this._onDOMFocus.dispose(),this._onDOMBlur.dispose(),this.domNode.parentNode&&this.domNode.parentNode.removeChild(this.domNode),this.domNode=null,this.wrapperGesture&&(this.wrapperGesture.dispose(),this.wrapperGesture=null),this.context.cache&&(this.context.cache.dispose(),this.context.cache=null),e.prototype.dispose.call(this)},t.BINDING="monaco-tree-row",t.LOADING_DECORATION_DELAY=800,t.counter=0,t.currentExternalDragAndDropData=null,t}(v.HeightMap);t.TreeView=L}),define(d[249],h([5]),{}),define(d[259],h([1,0,3,11,41,4,33,32,26,249]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.defaultStyles={selectBackground:a.Color.fromHex("#3C3C3C"),selectForeground:a.Color.fromHex("#F0F0F0"),selectBorder:a.Color.fromHex("#3C3C3C")};var l=function(e){function o(n,o,s){void 0===s&&(s=u.clone(t.defaultStyles));var a=e.call(this)||this;return a.selectElement=document.createElement("select"),a.selectElement.className="select-box",a.setOptions(n,o),a.toDispose=[],a._onDidSelect=new i.Emitter,a.selectBackground=s.selectBackground,a.selectForeground=s.selectForeground,a.selectBorder=s.selectBorder,a.toDispose.push(r.addStandardDisposableListener(a.selectElement,"change",function(e){a.selectElement.title=e.target.value,a._onDidSelect.fire({index:e.target.selectedIndex,selected:e.target.value})})),a.toDispose.push(r.addStandardDisposableListener(a.selectElement,"keydown",function(e){(e.equals(10)||e.equals(3))&&e.stopPropagation()})),a}return f(o,e),Object.defineProperty(o.prototype,"onDidSelect",{get:function(){return this._onDidSelect.event},enumerable:!0,configurable:!0}),o.prototype.setOptions=function(e,t,n){var i=this;if(!this.options||!s.equals(this.options,e)){this.options=e,this.selectElement.options.length=0;var o=0;this.options.forEach(function(e){i.selectElement.add(i.createOption(e,n===o++))})}this.select(t)},o.prototype.select=function(e){e>=0&&e=this._lines.length)throw new Error("Illegal value for lineNumber: "+e);return this._lines[t]},e.prototype.onLinesDeleted=function(e,t){if(0===this.getCount())return null;var n=this.getStartLineNumber(),i=this.getEndLineNumber();if(ti)return null;for(var r=0,s=0,a=n;a<=i;a++){var u=a-this._rendLineNumberStart;e<=a&&a<=t&&(0===s?(r=u,s=1):s++)}if(e=n&&r<=i&&(this._lines[r-this._rendLineNumberStart].onContentChanged(),o=!0);return o},e.prototype.onLinesInserted=function(e,t){if(0===this.getCount())return null;var n=t-e+1,i=this.getStartLineNumber(),o=this.getEndLineNumber();if(e<=i)return this._rendLineNumberStart+=n,null;if(e>o)return null;if(n+e>o)return this._lines.splice(e-this._rendLineNumberStart,o-e+1);for(var r=[],s=0;sn))for(var a=Math.max(t,s.fromLineNumber),u=Math.min(n,s.toLineNumber),l=a;l<=u;l++){var c=l-this._rendLineNumberStart;this._lines[c].onTokensChanged(),i=!0}}return i},e}();t.RenderedLinesCollection=i;var o=function(){function e(e){var t=this;this._host=e,this.domNode=this._createDomNode(),this._linesCollection=new i(function(){return t._host.createVisibleLine()})}return e.prototype._createDomNode=function(){var e=n.createFastDomNode(document.createElement("div"));return e.setClassName("view-layer"),e.setPosition("absolute"),e.domNode.setAttribute("role","presentation"),e.domNode.setAttribute("aria-hidden","true"),e},e.prototype.onConfigurationChanged=function(e){return e.layoutInfo},e.prototype.onFlushed=function(e){return this._linesCollection.flush(),!0},e.prototype.onLinesChanged=function(e){return this._linesCollection.onLinesChanged(e.fromLineNumber,e.toLineNumber)},e.prototype.onLinesDeleted=function(e){var t=this._linesCollection.onLinesDeleted(e.fromLineNumber,e.toLineNumber);if(t)for(var n=0,i=t.length;nt?(s=t)<=(a=Math.min(n,o.rendLineNumberStart-1))&&(this._insertLinesBefore(o,s,a,i,t),o.linesLength+=a-s+1):o.rendLineNumberStart0&&(this._removeLinesBefore(o,u),o.linesLength-=u),o.rendLineNumberStart=t,o.rendLineNumberStart+o.linesLength-1n){var s=Math.max(0,n-o.rendLineNumberStart+1),a=o.linesLength-1,u=a-s+1;u>0&&(this._removeLinesAfter(o,u),o.linesLength-=u)}return this._finishRendering(o,!1,i),o},e.prototype._renderUntouchedLines=function(e,t,n,i,o){for(var r=e.rendLineNumberStart,s=e.lines,a=t;a<=n;a++){var u=r+a;s[a].layoutLine(u,i[u-o])}},e.prototype._insertLinesBefore=function(e,t,n,i,o){for(var r=[],s=0,a=t;a<=n;a++)r[s++]=this.host.createVisibleLine();e.lines=r.concat(e.lines)},e.prototype._removeLinesBefore=function(e,t){for(var n=0;n=0;s--){var a=e.lines[s];i[s]&&(a.setDomNode(r),r=r.previousSibling)}},e.prototype._finishRenderingInvalidLines=function(e,t,n){var i=document.createElement("div");i.innerHTML=t.join("");for(var o=0;on||e===n&&t>i?(this.startLineNumber=n,this.startColumn=i,this.endLineNumber=e,this.endColumn=t):(this.startLineNumber=e,this.startColumn=t,this.endLineNumber=n,this.endColumn=i)}return e.prototype.isEmpty=function(){return e.isEmpty(this)},e.isEmpty=function(e){return e.startLineNumber===e.endLineNumber&&e.startColumn===e.endColumn},e.prototype.containsPosition=function(t){return e.containsPosition(this,t)},e.containsPosition=function(e,t){return!(t.lineNumbere.endLineNumber)&&(!(t.lineNumber===e.startLineNumber&&t.columne.endColumn))},e.prototype.containsRange=function(t){return e.containsRange(this,t)},e.containsRange=function(e,t){return!(t.startLineNumbere.endLineNumber||t.endLineNumber>e.endLineNumber)&&(!(t.startLineNumber===e.startLineNumber&&t.startColumne.endColumn)))},e.prototype.plusRange=function(t){return e.plusRange(this,t)},e.plusRange=function(t,n){var i,o,r,s;return n.startLineNumbert.endLineNumber?(r=n.endLineNumber,s=n.endColumn):n.endLineNumber===t.endLineNumber?(r=n.endLineNumber,s=Math.max(n.endColumn,t.endColumn)):(r=t.endLineNumber,s=t.endColumn),new e(i,o,r,s)},e.prototype.intersectRanges=function(t){return e.intersectRanges(this,t)},e.intersectRanges=function(t,n){var i=t.startLineNumber,o=t.startColumn,r=t.endLineNumber,s=t.endColumn,a=n.startLineNumber,u=n.startColumn,l=n.endLineNumber,c=n.endColumn;return il?(r=l,s=c):r===l&&(s=Math.min(s,c)),i>r?null:i===r&&o>s?null:new e(i,o,r,s)},e.prototype.equalsRange=function(t){return e.equalsRange(this,t)},e.equalsRange=function(e,t){return!!e&&!!t&&e.startLineNumber===t.startLineNumber&&e.startColumn===t.startColumn&&e.endLineNumber===t.endLineNumber&&e.endColumn===t.endColumn},e.prototype.getEndPosition=function(){return new n.Position(this.endLineNumber,this.endColumn)},e.prototype.getStartPosition=function(){return new n.Position(this.startLineNumber,this.startColumn)},e.prototype.cloneRange=function(){return new e(this.startLineNumber,this.startColumn,this.endLineNumber,this.endColumn)},e.prototype.toString=function(){return"["+this.startLineNumber+","+this.startColumn+" -> "+this.endLineNumber+","+this.endColumn+"]"},e.prototype.setEndPosition=function(t,n){return new e(this.startLineNumber,this.startColumn,t,n)},e.prototype.setStartPosition=function(t,n){return new e(t,n,this.endLineNumber,this.endColumn)},e.prototype.collapseToStart=function(){return e.collapseToStart(this)},e.collapseToStart=function(t){return new e(t.startLineNumber,t.startColumn,t.startLineNumber,t.startColumn)},e.fromPositions=function(t,n){return void 0===n&&(n=t),new e(t.lineNumber,t.column,n.lineNumber,n.column)},e.lift=function(t){return t?new e(t.startLineNumber,t.startColumn,t.endLineNumber,t.endColumn):null},e.isIRange=function(e){return e&&"number"==typeof e.startLineNumber&&"number"==typeof e.startColumn&&"number"==typeof e.endLineNumber&&"number"==typeof e.endColumn},e.areIntersectingOrTouching=function(e,t){return!(e.endLineNumbere.startLineNumber},e}();t.Range=i}),define(d[164],h([1,0,72,38,26,2,3]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a={followsCaret:!0,ignoreCharChanges:!0,alwaysRevealFirst:!0},u=function(e){function t(n,i){void 0===i&&(i={});var r=e.call(this,[t.Events.UPDATED])||this;return r.editor=n,r.options=o.mixin(i,a,!1),r.disposed=!1,r.toUnbind=[],r.nextIdx=-1,r.ranges=[],r.ignoreSelectionChange=!1,r.revealFirst=r.options.alwaysRevealFirst,r.toUnbind.push(r.editor.onDidDispose(function(){return r.dispose()})),r.toUnbind.push(r.editor.onDidUpdateDiff(function(){return r.onDiffUpdated()})),r.options.followsCaret&&r.toUnbind.push(r.editor.getModifiedEditor().onDidChangeCursorPosition(function(e){r.ignoreSelectionChange||(r.nextIdx=-1)})),r.options.alwaysRevealFirst&&r.toUnbind.push(r.editor.getModifiedEditor().onDidChangeModel(function(e){r.revealFirst=!0})),r.init(),r}return f(t,e),t.prototype.init=function(){this.editor.getLineChanges()},t.prototype.onDiffUpdated=function(){this.init(),this.compute(this.editor.getLineChanges()),this.revealFirst&&null!==this.editor.getLineChanges()&&(this.revealFirst=!1,this.nextIdx=-1,this.next())},t.prototype.compute=function(e){var n=this;this.ranges=[],e&&e.forEach(function(e){!n.options.ignoreCharChanges&&e.charChanges?e.charChanges.forEach(function(e){n.ranges.push({rhs:!0,range:new r.Range(e.modifiedStartLineNumber,e.modifiedStartColumn,e.modifiedEndLineNumber,e.modifiedEndColumn)})}):n.ranges.push({rhs:!0,range:new r.Range(e.modifiedStartLineNumber,1,e.modifiedStartLineNumber,1)})}),this.ranges.sort(function(e,t){return e.range.getStartPosition().isBeforeOrEqual(t.range.getStartPosition())?-1:t.range.getStartPosition().isBeforeOrEqual(e.range.getStartPosition())?1:0}),this.emit(t.Events.UPDATED,{})},t.prototype.initIdx=function(e){for(var t=!1,n=this.editor.getPosition(),i=0,o=this.ranges.length;i=this.ranges.length&&(this.nextIdx=0)):(this.nextIdx-=1,this.nextIdx<0&&(this.nextIdx=this.ranges.length-1));var t=this.ranges[this.nextIdx];this.ignoreSelectionChange=!0;try{var i=t.range.getStartPosition();this.editor.setPosition(i),this.editor.revealPositionInCenter(i)}finally{this.ignoreSelectionChange=!1}}},t.prototype.canNavigate=function(){return this.ranges&&this.ranges.length>0},t.prototype.next=function(){this.move(!0)},t.prototype.previous=function(){this.move(!1)},t.prototype.dispose=function(){this.toUnbind=s.dispose(this.toUnbind),this.ranges=null,this.disposed=!0,e.prototype.dispose.call(this)},t.Events={UPDATED:"navigation.updated"},t}(i.EventEmitter);t.DiffNavigator=u}),define(d[59],h([1,0,2]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){}return e.insert=function(e,t){return{identifier:null,range:new n.Range(e.lineNumber,e.column,e.lineNumber,e.column),text:t,forceMoveMarkers:!0}},e.delete=function(e){return{identifier:null,range:e,text:null,forceMoveMarkers:!0}},e.replace=function(e,t){return{identifier:null,range:e,text:t,forceMoveMarkers:!1}},e.replaceMove=function(e,t){return{identifier:null,range:e,text:t,forceMoveMarkers:!0}},e}();t.EditOperation=i}),define(d[435],h([1,0,9,59,2]),function(e,t,n,i,o){"use strict";function r(e,t){t.sort(function(e,t){return e.lineNumber===t.lineNumber?e.column-t.column:e.lineNumber-t.lineNumber});for(var r=t.length-2;r>=0;r--)t[r].lineNumber===t[r+1].lineNumber&&t.splice(r,1);for(var s=[],a=0,u=0,l=t.length,c=1,d=e.getLineCount();c<=d;c++){var h=e.getLineContent(c),p=h.length+1,f=0;if(!(u "+this.positionLineNumber+","+this.positionColumn+"]"},t.prototype.equalsSelection=function(e){return t.selectionsEqual(this,e)},t.selectionsEqual=function(e,t){return e.selectionStartLineNumber===t.selectionStartLineNumber&&e.selectionStartColumn===t.selectionStartColumn&&e.positionLineNumber===t.positionLineNumber&&e.positionColumn===t.positionColumn},t.prototype.getDirection=function(){return this.selectionStartLineNumber===this.startLineNumber&&this.selectionStartColumn===this.startColumn?o.LTR:o.RTL},t.prototype.setEndPosition=function(e,n){return this.getDirection()===o.LTR?new t(this.startLineNumber,this.startColumn,e,n):new t(e,n,this.startLineNumber,this.startColumn)},t.prototype.getPosition=function(){return new i.Position(this.positionLineNumber,this.positionColumn)},t.prototype.setStartPosition=function(e,n){return this.getDirection()===o.LTR?new t(e,n,this.endLineNumber,this.endColumn):new t(this.endLineNumber,this.endColumn,e,n)},t.fromPositions=function(e,n){return void 0===n&&(n=e),new t(e.lineNumber,e.column,n.lineNumber,n.column)},t.liftSelection=function(e){return new t(e.selectionStartLineNumber,e.selectionStartColumn,e.positionLineNumber,e.positionColumn)},t.selectionsArrEqual=function(e,t){if(e&&!t||!e&&t)return!1;if(!e&&!t)return!0;if(e.length!==t.length)return!1;for(var n=0,i=e.length;n4294967295?4294967295:0|e}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t,n){for(var i=new Uint8Array(e*t),o=0,r=e*t;o255?255:0|e},t.toUint32=n,t.toUint32Array=function(e){for(var t=e.length,i=new Uint32Array(t),o=0;o=0&&e<256?this._asciiMap[e]=i:this._map.set(e,i)},e.prototype.get=function(e){return e>=0&&e<256?this._asciiMap[e]:this._map.get(e)||this._defaultValue},e}();t.CharacterClassifier=i;var o;!function(e){e[e.False=0]="False",e[e.True=1]="True"}(o||(o={}));var r=function(){function e(){this._actual=new i(0)}return e.prototype.add=function(e){this._actual.set(e,1)},e.prototype.has=function(e){return 1===this._actual.get(e)},e}();t.CharacterSet=r}),define(d[94],h([1,0,89]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});!function(e){e[e.Regular=0]="Regular",e[e.Whitespace=1]="Whitespace",e[e.WordSeparator=2]="WordSeparator"}(t.WordCharacterClass||(t.WordCharacterClass={}));var i=function(e){function t(t){for(var n=e.call(this,0)||this,i=0,o=t.length;i1&&v>1&&(w=f.charCodeAt(m-2))===(S=g.charCodeAt(v-2));)m--,v--;(m>1||v>1)&&this._pushTrimWhitespaceCharChange(r,s+1,1,m,a+1,1,v);for(var _=l._getLastNonBlankColumn(f,1),y=l._getLastNonBlankColumn(g,1),C=f.length+1,b=g.length+1;_, selectionStart: "+this.selectionStart+", selectionEnd: "+this.selectionEnd+"]"},e.prototype.readFromTextArea=function(t){return new e(t.getValue(),t.getSelectionStart(),t.getSelectionEnd())},e.prototype.collapseSelection=function(){return new e(this.value,this.value.length,this.value.length)},e.prototype.writeToTextArea=function(e,t,n){t.setValue(e,this.value),n&&t.setSelectionRange(e,this.selectionStart,this.selectionEnd)},e.selectedText=function(t){return new e(t,0,t.length)},e.deduceInput=function(e,t,n){if(!e)return{text:"",replaceCharCnt:0};var i=e.value,r=e.selectionStart,s=e.selectionEnd,a=t.value,u=t.selectionStart,l=t.selectionEnd,c=i.substring(s),d=a.substring(l),h=o.commonSuffixLength(c,d);a=a.substring(0,a.length-h);var p=(i=i.substring(0,i.length-h)).substring(0,r),f=a.substring(0,u),g=o.commonPrefixLength(p,f);if(a=a.substring(g),i=i.substring(g),u-=g,r-=g,l-=g,s-=g,n&&u===l&&i.length>0){var m=null;if(u===a.length?o.startsWith(a,i)&&(m=a.substring(i.length)):o.endsWith(a,i)&&(m=a.substring(0,a.length-i.length)),null!==m&&m.length>0&&(/\uFE0F/.test(m)||o.containsEmoji(m)))return{text:m,replaceCharCnt:0}}return u===l?i===a&&0===r&&s===i.length&&u===a.length&&-1===a.indexOf("\n")&&o.containsFullWidthCharacter(a)?{text:"",replaceCharCnt:0}:{text:a,replaceCharCnt:p.length-g}:{text:a,replaceCharCnt:s-r}},e.EMPTY=new e("",0,0),e}();t.TextAreaState=r;var s=function(){function e(){}return e._getPageOfLine=function(t){return Math.floor((t-1)/e._LINES_PER_PAGE)},e._getRangeForPage=function(t){var i=t*e._LINES_PER_PAGE,o=i+1,r=i+e._LINES_PER_PAGE;return new n.Range(o,1,r+1,1)},e.fromEditorSelection=function(t,o,s){var a=e._getPageOfLine(s.startLineNumber),u=e._getRangeForPage(a),l=e._getPageOfLine(s.endLineNumber),c=e._getRangeForPage(l),d=u.intersectRanges(new n.Range(1,1,s.startLineNumber,s.startColumn)),h=o.getValueInRange(d,i.EndOfLinePreference.LF),p=o.getLineCount(),f=o.getLineMaxColumn(p),g=c.intersectRanges(new n.Range(s.endLineNumber,s.endColumn,p,f)),m=o.getValueInRange(g,i.EndOfLinePreference.LF),v=null;if(a===l||a+1===l)v=o.getValueInRange(s,i.EndOfLinePreference.LF);else{var _=u.intersectRanges(s),y=c.intersectRanges(s);v=o.getValueInRange(_,i.EndOfLinePreference.LF)+String.fromCharCode(8230)+o.getValueInRange(y,i.EndOfLinePreference.LF)}return h.length>500&&(h=h.substring(h.length-500,h.length)),m.length>500&&(m=m.substring(0,500)),v.length>1e3&&(v=v.substring(0,500)+String.fromCharCode(8230)+v.substring(v.length-500,v.length)),new r(h+v+m,h.length,h.length+v.length)},e._LINES_PER_PAGE=10,e}();t.PagedScreenReaderStrategy=s}),define(d[154],h([1,0,18,9,11,3,151,28,15,4]),function(e,t,n,i,o,r,s,a,u,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.CopyOptions={forceCopyWithSyntaxHighlighting:!1};var c;!function(e){e[e.Type=0]="Type",e[e.Paste=1]="Paste"}(c||(c={}));var d=function(e){function r(t,r){var c=e.call(this)||this;c._onFocus=c._register(new o.Emitter),c.onFocus=c._onFocus.event,c._onBlur=c._register(new o.Emitter),c.onBlur=c._onBlur.event,c._onKeyDown=c._register(new o.Emitter),c.onKeyDown=c._onKeyDown.event,c._onKeyUp=c._register(new o.Emitter),c.onKeyUp=c._onKeyUp.event,c._onCut=c._register(new o.Emitter),c.onCut=c._onCut.event,c._onPaste=c._register(new o.Emitter),c.onPaste=c._onPaste.event,c._onType=c._register(new o.Emitter),c.onType=c._onType.event,c._onCompositionStart=c._register(new o.Emitter),c.onCompositionStart=c._onCompositionStart.event,c._onCompositionUpdate=c._register(new o.Emitter),c.onCompositionUpdate=c._onCompositionUpdate.event,c._onCompositionEnd=c._register(new o.Emitter),c.onCompositionEnd=c._onCompositionEnd.event,c._host=t,c._textArea=c._register(new p(r)),c._asyncTriggerCut=c._register(new n.RunOnceScheduler(function(){return c._onCut.fire()},0)),c._textAreaState=s.TextAreaState.EMPTY,c.writeScreenReaderContent("ctor"),c._hasFocus=!1,c._isDoingComposition=!1,c._nextCommand=0,c._register(l.addStandardDisposableListener(r.domNode,"keydown",function(e){c._isDoingComposition&&e.equals(109)&&e.stopPropagation(),e.equals(9)&&e.preventDefault(),c._onKeyDown.fire(e)})),c._register(l.addStandardDisposableListener(r.domNode,"keyup",function(e){c._onKeyUp.fire(e)})),c._register(l.addDisposableListener(r.domNode,"compositionstart",function(e){c._isDoingComposition||(c._isDoingComposition=!0,a.isEdgeOrIE||c._setAndWriteTextAreaState("compositionstart",s.TextAreaState.EMPTY),c._onCompositionStart.fire())}));var d=function(e){var t=c._textAreaState,n=c._textAreaState.readFromTextArea(c._textArea);return[n,s.TextAreaState.deduceInput(t,n,e)]},f=function(e){var t=c._textAreaState,n=s.TextAreaState.selectedText(e);return[n,{text:n.value,replaceCharCnt:t.selectionEnd-t.selectionStart}]};return c._register(l.addDisposableListener(r.domNode,"compositionupdate",function(e){if(!a.isChromev56){if(a.isEdgeOrIE&&"ja"===e.locale){var t=d(!1),n=t[0],i=t[1];return c._textAreaState=n,c._onType.fire(i),void c._onCompositionUpdate.fire(e)}var o=f(e.data),r=o[0],s=o[1];c._textAreaState=r,c._onType.fire(s),c._onCompositionUpdate.fire(e)}})),c._register(l.addDisposableListener(r.domNode,"compositionend",function(e){if(a.isEdgeOrIE&&"ja"===e.locale){var t=d(!1),n=t[0],i=t[1];c._textAreaState=n,c._onType.fire(i)}else{var o=f(e.data),n=o[0],i=o[1];c._textAreaState=n,c._onType.fire(i)}(a.isEdgeOrIE||a.isChrome)&&(c._textAreaState=c._textAreaState.readFromTextArea(c._textArea)),c._isDoingComposition&&(c._isDoingComposition=!1,c._onCompositionEnd.fire())})),c._register(l.addDisposableListener(r.domNode,"input",function(){if(c._isDoingComposition){if(a.isChromev56){var e=f(c._textArea.getValue()),t=e[0],n=e[1];c._textAreaState=t,c._onType.fire(n);var o={data:n.text};c._onCompositionUpdate.fire(o)}}else{var r=d(u.isMacintosh),s=r[0],l=r[1];0===l.replaceCharCnt&&1===l.text.length&&i.isHighSurrogate(l.text.charCodeAt(0))||(c._textAreaState=s,0===c._nextCommand?""!==l.text&&c._onType.fire(l):(""!==l.text&&c._onPaste.fire({text:l.text}),c._nextCommand=0))}})),c._register(l.addDisposableListener(r.domNode,"cut",function(e){c._ensureClipboardGetsEditorSelection(e),c._asyncTriggerCut.schedule()})),c._register(l.addDisposableListener(r.domNode,"copy",function(e){c._ensureClipboardGetsEditorSelection(e)})),c._register(l.addDisposableListener(r.domNode,"paste",function(e){if(h.canUseTextData(e)){var t=h.getTextData(e);""!==t&&c._onPaste.fire({text:t})}else c._textArea.getSelectionStart()!==c._textArea.getSelectionEnd()&&c._setAndWriteTextAreaState("paste",s.TextAreaState.EMPTY),c._nextCommand=1})),c._register(l.addDisposableListener(r.domNode,"focus",function(){return c._setHasFocus(!0)})),c._register(l.addDisposableListener(r.domNode,"blur",function(){return c._setHasFocus(!1)})),c}return f(r,e),r.prototype.dispose=function(){e.prototype.dispose.call(this)},r.prototype.focusTextArea=function(){this._setHasFocus(!0)},r.prototype.isFocused=function(){return this._hasFocus},r.prototype._setHasFocus=function(e){this._hasFocus!==e&&(this._hasFocus=e,this._hasFocus&&(a.isEdge?this._setAndWriteTextAreaState("focusgain",s.TextAreaState.EMPTY):this.writeScreenReaderContent("focusgain")),this._hasFocus?this._onFocus.fire():this._onBlur.fire())},r.prototype._setAndWriteTextAreaState=function(e,t){this._hasFocus||(t=t.collapseSelection()),t.writeToTextArea(e,this._textArea,this._hasFocus),this._textAreaState=t},r.prototype.writeScreenReaderContent=function(e){this._isDoingComposition||this._setAndWriteTextAreaState(e,this._host.getScreenReaderContent(this._textAreaState))},r.prototype._ensureClipboardGetsEditorSelection=function(e){var n=this._host.getPlainTextToCopy();if(h.canUseTextData(e)){var i=null;!a.isEdgeOrIE&&(n.length<65536||t.CopyOptions.forceCopyWithSyntaxHighlighting)&&(i=this._host.getHTMLToCopy()),h.setTextData(e,n,i)}else this._setAndWriteTextAreaState("copy or cut",s.TextAreaState.selectedText(n))},r}(r.Disposable);t.TextAreaInput=d;var h=function(){function e(){}return e.canUseTextData=function(e){return!!e.clipboardData||!!window.clipboardData},e.getTextData=function(e){if(e.clipboardData)return e.preventDefault(),e.clipboardData.getData("text/plain");if(window.clipboardData)return e.preventDefault(),window.clipboardData.getData("Text");throw new Error("ClipboardEventUtils.getTextData: Cannot use text data!")},e.setTextData=function(e,t,n){if(e.clipboardData)return e.clipboardData.setData("text/plain",t),null!==n&&e.clipboardData.setData("text/html",n),void e.preventDefault();if(window.clipboardData)return window.clipboardData.setData("Text",t),void e.preventDefault();throw new Error("ClipboardEventUtils.setTextData: Cannot use text data!")},e}(),p=function(e){function t(t){var n=e.call(this)||this;return n._actual=t,n}return f(t,e),t.prototype.getValue=function(){return this._actual.domNode.value},t.prototype.setValue=function(e,t){var n=this._actual.domNode;n.value!==t&&(n.value=t)},t.prototype.getSelectionStart=function(){return this._actual.domNode.selectionStart},t.prototype.getSelectionEnd=function(){return this._actual.domNode.selectionEnd},t.prototype.setSelectionRange=function(e,t,n){var i=this._actual.domNode,o=document.activeElement===i,r=i.selectionStart,s=i.selectionEnd;if(!o||r!==t||s!==n)if(o)i.setSelectionRange(t,n);else try{var a=l.saveParentsScrollTop(i);i.focus(),i.setSelectionRange(t,n),l.restoreParentsScrollTop(i,a)}catch(e){}},t}(r.Disposable)}),define(d[475],h([1,0,10]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e){this.model=e,this.currentOpenStackElement=null,this.past=[],this.future=[]}return e.prototype.pushStackElement=function(){null!==this.currentOpenStackElement&&(this.past.push(this.currentOpenStackElement),this.currentOpenStackElement=null)},e.prototype.clear=function(){this.currentOpenStackElement=null,this.past=[],this.future=[]},e.prototype.pushEditOperation=function(e,t,i){this.future=[],this.currentOpenStackElement||(this.currentOpenStackElement={beforeVersionId:this.model.getAlternativeVersionId(),beforeCursorState:e,editOperations:[],afterCursorState:null,afterVersionId:-1});var o={operations:this.model.applyEdits(t)};this.currentOpenStackElement.editOperations.push(o);try{this.currentOpenStackElement.afterCursorState=i?i(o.operations):null}catch(e){n.onUnexpectedError(e),this.currentOpenStackElement.afterCursorState=null}return this.currentOpenStackElement.afterVersionId=this.model.getVersionId(),this.currentOpenStackElement.afterCursorState},e.prototype.undo=function(){if(this.pushStackElement(),this.past.length>0){var e=this.past.pop();try{for(var t=e.editOperations.length-1;t>=0;t--)e.editOperations[t]={operations:this.model.applyEdits(e.editOperations[t].operations)}}catch(e){return this.clear(),null}return this.future.push(e),{selections:e.beforeCursorState,recordedVersionId:e.beforeVersionId}}return null},e.prototype.redo=function(){if(this.future.length>0){if(this.currentOpenStackElement)throw new Error("How is this possible?");var e=this.future.pop();try{for(var t=0;t0;r--){var s=e.getIndentLevel(r);if(-1!==s){var a=o[o.length-1];if(a.indent>s){do{o.pop(),a=o[o.length-1]}while(a.indent>s);var u=a.line-1;u-r>=t&&i.push(new n(r,u,s))}a.indent===s?a.line=r:o.push({indent:s,line:r})}}return i.reverse()}}),define(d[482],h([1,0]),function(e,t){"use strict";function n(e,t,n,i){var o;for(o=0;o0&&s>0)return 0;if(l>0&&c>0)return 0;var h=Math.abs(s-c),p=Math.abs(r-l);return 0===h?p:p%h==0?p/h:0}Object.defineProperty(t,"__esModule",{value:!0}),t.guessIndentation=function(e,t,i){for(var o=Math.min(e.length,1e4),r=0,s=0,a="",u=0,l=[2,4,6,8],c=[0,0,0,0,0,0,0,0,0],d=0;d0?r++:g>1&&s++;var C=n(a,u,h,f);C<=8&&c[C]++,a=h,u=f}}var b=n(a,u,"",0);b<=8&&c[b]++;var w=i;r!==s&&(w=rE&&(E=t,S=e)}),{insertSpaces:w,tabSize:S}}}),define(d[55],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.TextModelEventType={ModelDispose:"modelDispose",ModelTokensChanged:"modelTokensChanged",ModelLanguageChanged:"modelLanguageChanged",ModelOptionsChanged:"modelOptionsChanged",ModelContentChanged:"contentChanged",ModelRawContentChanged2:"rawContentChanged2",ModelDecorationsChanged:"decorationsChanged"};!function(e){e[e.Flush=1]="Flush",e[e.LineChanged=2]="LineChanged",e[e.LinesDeleted=3]="LinesDeleted",e[e.LinesInserted=4]="LinesInserted",e[e.EOLChanged=5]="EOLChanged"}(t.RawContentChangedType||(t.RawContentChangedType={}));var n=function(){return function(){this.changeType=1}}();t.ModelRawFlush=n;var i=function(){return function(e,t){this.changeType=2,this.lineNumber=e,this.detail=t}}();t.ModelRawLineChanged=i;var o=function(){return function(e,t){this.changeType=3,this.fromLineNumber=e,this.toLineNumber=t}}();t.ModelRawLinesDeleted=o;var r=function(){return function(e,t,n){this.changeType=4,this.fromLineNumber=e,this.toLineNumber=t,this.detail=n}}();t.ModelRawLinesInserted=r;var s=function(){return function(){this.changeType=5}}();t.ModelRawEOLChanged=s;var a=function(){function e(e,t,n,i){this.changes=e,this.versionId=t,this.isUndoing=n,this.isRedoing=i}return e.prototype.containsEvent=function(e){for(var t=0,n=this.changes.length;t0){var s=t.charCodeAt(i);if(0!==e.get(s))return!0}return!1}function l(e,t,n,i,o){if(i+o===n)return!0;var r=t.charCodeAt(i+o);if(0!==e.get(r))return!0;if(o>0){var s=t.charCodeAt(i+o-1);if(0!==e.get(s))return!0}return!1}function c(e,t,n,i,o){return u(e,t,n,i,o)&&l(e,t,n,i,o)}Object.defineProperty(t,"__esModule",{value:!0});var d=function(){function e(e,t,n,i){this.searchString=e,this.isRegex=t,this.matchCase=n,this.wordSeparators=i}return e._isMultilineRegexSource=function(e){if(!e||0===e.length)return!1;for(var t=0,n=e.length;t=n)break;var i=e.charCodeAt(t);if(110===i||114===i)return!0}return!1},e.prototype.parseSearchRequest=function(){if(""===this.searchString)return null;var t;t=this.isRegex?e._isMultilineRegexSource(this.searchString):this.searchString.indexOf("\n")>=0;var i=null;try{i=n.createRegExp(this.searchString,this.isRegex,{matchCase:this.matchCase,wholeWord:!1,multiline:t,global:!0})}catch(e){return null}if(!i)return null;var o=!this.isRegex&&!t;return o&&this.searchString.toLowerCase()!==this.searchString.toUpperCase()&&(o=this.matchCase),new h(i,this.wordSeparators?s.getMapForWordSeparators(this.wordSeparators):null,o?this.searchString:null)},e}();t.SearchParams=d;var h=function(){return function(e,t,n){this.regex=e,this.wordSeparators=t,this.simpleSearch=n}}();t.SearchData=h;var p=function(){function e(){}return e.findMatches=function(e,t,n,i,o){var r=t.parseSearchRequest();return r?r.regex.multiline?this._doFindMatchesMultiline(e,n,new f(r.wordSeparators,r.regex),i,o):this._doFindMatchesLineByLine(e,n,r,i,o):[]},e._getMultilineMatchRange=function(e,t,n,i,r){var s;if("\r\n"===e.getEOL()){for(var a=0,u=0;u=o)return c;return c},e._doFindMatchesLineByLine=function(e,t,n,i,o){var r=[],s=0;if(t.startLineNumber===t.endLineNumber){var a=e.getLineContent(t.startLineNumber).substring(t.startColumn-1,t.endColumn-1);return s=this._findMatchesInLine(n,a,t.startLineNumber,t.startColumn-1,s,r,i,o),r}var u=e.getLineContent(t.startLineNumber).substring(t.startColumn-1);s=this._findMatchesInLine(n,u,t.startLineNumber,t.startColumn-1,s,r,i,o);for(var l=t.startLineNumber+1;l=d))return s;return s}var _,y=new f(e.wordSeparators,e.regex);y.reset(0);do{if((_=y.next(t))&&(u[s++]=a(new o.Range(n,_.index+1+i,n,_.index+1+_[0].length+i),_,l),s>=d))return s}while(_);return s},e.findNextMatch=function(e,t,n,i){var o=t.parseSearchRequest();if(!o)return null;var r=new f(o.wordSeparators,o.regex);return o.regex.multiline?this._doFindNextMatchMultiline(e,n,r,i):this._doFindNextMatchLineByLine(e,n,r,i)},e._doFindNextMatchMultiline=function(e,t,n,s){var u=new i.Position(t.lineNumber,1),l=e.getOffsetAt(u),c=e.getLineCount(),d=e.getValueInRange(new o.Range(u.lineNumber,u.column,c,e.getLineMaxColumn(c)),r.EndOfLinePreference.LF);n.reset(t.column-1);var h=n.next(d);return h?a(this._getMultilineMatchRange(e,l,d,h.index,h[0]),h,s):1!==t.lineNumber||1!==t.column?this._doFindNextMatchMultiline(e,new i.Position(1,1),n,s):null},e._doFindNextMatchLineByLine=function(e,t,n,i){var o=e.getLineCount(),r=t.lineNumber,s=e.getLineContent(r),a=this._findFirstMatchInLine(n,s,r,t.column,i);if(a)return a;for(var u=1;u<=o;u++){var l=(r+u-1)%o,c=e.getLineContent(l+1),d=this._findFirstMatchInLine(n,c,l+1,1,i);if(d)return d}return null},e._findFirstMatchInLine=function(e,t,n,i,r){e.reset(i-1);var s=e.next(t);return s?a(new o.Range(n,s.index+1,n,s.index+1+s[0].length),s,r):null},e.findPreviousMatch=function(e,t,n,i){var o=t.parseSearchRequest();if(!o)return null;var r=new f(o.wordSeparators,o.regex);return o.regex.multiline?this._doFindPreviousMatchMultiline(e,n,r,i):this._doFindPreviousMatchLineByLine(e,n,r,i)},e._doFindPreviousMatchMultiline=function(e,t,n,r){var s=this._doFindMatchesMultiline(e,new o.Range(1,1,t.lineNumber,t.column),n,r,9990);if(s.length>0)return s[s.length-1];var a=e.getLineCount();return t.lineNumber!==a||t.column!==e.getLineMaxColumn(a)?this._doFindPreviousMatchMultiline(e,new i.Position(a,e.getLineMaxColumn(a)),n,r):null},e._doFindPreviousMatchLineByLine=function(e,t,n,i){var o=e.getLineCount(),r=t.lineNumber,s=e.getLineContent(r).substring(0,t.column-1),a=this._findLastMatchInLine(n,s,r,i);if(a)return a;for(var u=1;u<=o;u++){var l=(o+r-u-1)%o,c=e.getLineContent(l+1),d=this._findLastMatchInLine(n,c,l+1,i);if(d)return d}return null},e._findLastMatchInLine=function(e,t,n,i){var r,s=null;for(e.reset(0);r=e.next(t);)s=a(new o.Range(n,r.index+1,n,r.index+1+r[0].length),r,i);return s},e}();t.TextModelSearch=p;var f=function(){function e(e,t){this._wordSeparators=e,this._searchRegex=t,this._prevMatchStartIndex=-1,this._prevMatchLength=0}return e.prototype.reset=function(e){this._searchRegex.lastIndex=e,this._prevMatchStartIndex=-1,this._prevMatchLength=0},e.prototype.next=function(e){var t,n=e.length;do{if(this._prevMatchStartIndex+this._prevMatchLength===n)return null;if(!(t=this._searchRegex.exec(e)))return null;var i=t.index,o=t[0].length;if(i===this._prevMatchStartIndex&&o===this._prevMatchLength)return null;if(this._prevMatchStartIndex=i,this._prevMatchLength=o,!this._wordSeparators||c(this._wordSeparators,e,n,i,o))return t}while(t);return null},e}()}),define(d[101],h([1,0,9,20]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(){}return e.fromString=function(e){for(var t=0,i=-1;-1!==(i=e.indexOf("\r",i+1));)t++;var o=n.containsRTL(e),r=!o&&n.isBasicASCII(e),s=e.split(/\r\n|\r|\n/),a="";return n.startsWithUTF8BOM(s[0])&&(a=n.UTF8_BOM_CHARACTER,s[0]=s[0].substr(1)),{BOM:a,lines:s,length:e.length,containsRTL:o,isBasicASCII:r,totalCRCount:t}},e}();t.RawTextSource=o;var r=function(){function e(){}return e._getEOL=function(e,t){var n=e.lines.length-1;return 0===n?t===i.DefaultEndOfLine.LF?"\n":"\r\n":e.totalCRCount>n/2?"\r\n":"\n"},e.fromRawTextSource=function(e,t){return{length:e.length,lines:e.lines,BOM:e.BOM,EOL:this._getEOL(e,t),containsRTL:e.containsRTL,isBasicASCII:e.isBasicASCII}},e.fromString=function(e,t){return this.fromRawTextSource(o.fromString(e),t)},e.create=function(e,t){return"string"==typeof e?this.fromString(e,t):this.fromRawTextSource(e,t)},e}();t.TextSource=r}),define(d[116],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){}return e.getLanguageId=function(e){return(255&e)>>>0},e.getTokenType=function(e){return(1792&e)>>>8},e.getFontStyle=function(e){return(14336&e)>>>11},e.getForeground=function(e){return(8372224&e)>>>14},e.getBackground=function(e){return(4286578688&e)>>>23},e.getClassNameFromMetadata=function(e){var t="mtk"+this.getForeground(e),n=this.getFontStyle(e);return 1&n&&(t+=" mtki"),2&n&&(t+=" mtkb"),4&n&&(t+=" mtku"),t},e.getInlineStyleFromMetadata=function(e,t){var n=this.getForeground(e),i=this.getFontStyle(e),o="color: "+t[n]+";";return 1&i&&(o+="font-style: italic;"),2&i&&(o+="font-weight: bold;"),4&i&&(o+="text-decoration: underline;"),o},e}();t.TokenMetadata=n}),define(d[90],h([1,0,116]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){this.endIndex=e,this._metadata=t}return e.prototype.getForeground=function(){return n.TokenMetadata.getForeground(this._metadata)},e.prototype.getType=function(){return n.TokenMetadata.getClassNameFromMetadata(this._metadata)},e.prototype.getInlineStyle=function(e){return n.TokenMetadata.getInlineStyleFromMetadata(this._metadata,e)},e._equals=function(e,t){return e.endIndex===t.endIndex&&e._metadata===t._metadata},e.equalsArr=function(e,t){var n=e.length;if(n!==t.length)return!1;for(var i=0;i>>1;o>>1;l=n);l++){var d=l+1>>1)-1;nt?i=o-1:n=o}return n},e}();t.ViewLineTokenFactory=o}),define(d[130],h([1,0,116,90]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t,n,i,o,r){this._source=e,this._tokenIndex=t,this._metadata=r,this.startOffset=i,this.endOffset=o,this.hasPrev=this._tokenIndex>0,this.hasNext=this._tokenIndex+1>>1,this._text=t,this._textLength=this._text.length}return e.prototype.getTokenCount=function(){return this._tokensCount},e.prototype.getLineContent=function(){return this._text},e.prototype.getLineLength=function(){return this._textLength},e.prototype.getTokenStartOffset=function(e){return this._tokens[e<<1]},e.prototype.getLanguageId=function(e){var t=this._tokens[1+(e<<1)];return n.TokenMetadata.getLanguageId(t)},e.prototype.getStandardTokenType=function(e){var t=this._tokens[1+(e<<1)];return n.TokenMetadata.getTokenType(t)},e.prototype.getTokenEndOffset=function(e){return e+1>>0}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t,n,i){this.id=e,this.internalDecorationId=t,this.position=n,this.stickToPreviousCharacter=i}return e.prototype.toString=function(){return"{'"+this.id+"';"+this.position.toString()+","+this.stickToPreviousCharacter+"}"},e.prototype.updateLineNumber=function(e,t){this.position.lineNumber!==t&&(e.addChangedMarker(this),this.position=new i.Position(t,this.position.column))},e.prototype.updateColumn=function(e,t){this.position.column!==t&&(e.addChangedMarker(this),this.position=new i.Position(this.position.lineNumber,t))},e.prototype.updatePosition=function(e,t){this.position.lineNumber===t.lineNumber&&this.position.column===t.column||(e.addChangedMarker(this),this.position=t)},e.prototype.setPosition=function(e){this.position=e},e.compareMarkers=function(e,t){return e.position.column===t.position.column?(e.stickToPreviousCharacter?0:1)-(t.stickToPreviousCharacter?0:1):e.position.column-t.position.column},e}();t.LineMarker=s;var a=function(){function e(){this._changedDecorations=[],this._changedDecorationsLen=0}return e.prototype.addChangedMarker=function(e){var t=e.internalDecorationId;0!==t&&(this._changedDecorations[this._changedDecorationsLen++]=t)},e.prototype.getDecorationIds=function(){return this._changedDecorations},e}();t.MarkersTracker=a;var u,l={adjust:function(){},finish:function(){}},c={adjustDelta:function(){},adjustSet:function(){},finish:function(){}};!function(e){e[e.MarkerDefined=0]="MarkerDefined",e[e.ForceMove=1]="ForceMove",e[e.ForceStay=2]="ForceStay"}(u||(u={}));var d=function(){function e(e){e&&(this._markers=null)}return e.prototype._createMarkersAdjuster=function(e){if(!this._markers)return c;if(0===this._markers.length)return c;this._markers.sort(s.compareMarkers);var t=this._markers,n=t.length,i=0,o=t[i],r=function(e,t){return o.position.columne)&&(1!==t&&(2===t||o.stickToPreviousCharacter))},a=function(s,a,u,l){for(;i0?2:0);var f=Math.min(h,p);f>0&&(r.adjust(l.startColumn-1+f,i,c),l.forceMoveMarkers||s.adjustDelta(l.startColumn+f,i,c,l.forceMoveMarkers?1:h>p?2:0)),o=o.substring(0,c-1)+l.text+o.substring(d-1),i+=p-h,r.adjust(l.endColumn,i,c),s.adjustSet(l.endColumn,c+p,l.forceMoveMarkers?1:0)}return r.finish(i,o.length),s.finish(i,o.length),this._setText(o,n),i},e.prototype.split=function(e,t,n,i){var o=this.text.substring(0,t-1),r=this.text.substring(t-1),a=null;if(this._markers){this._markers.sort(s.compareMarkers);for(var u=0,l=this._markers.length;ut||d.position.column===t&&(n||!d.stickToPreviousCharacter)){var c=this._markers.slice(0,u);a=this._markers.slice(u),this._markers=c;break}if(a)for(var u=0,l=a.length;u>1)-1},t.prototype._setPlusOneIndentLevel=function(e){this._metadata=1&this._metadata|(4026531839&e)<<1},t.prototype.updateTabSize=function(e){0===e?this._metadata=1&this._metadata:this._setPlusOneIndentLevel(o(this._text,e))},t.prototype._createModelLine=function(e,n){return new t(e,n)},t.prototype.split=function(t,n,i,o){var r=e.prototype.split.call(this,t,n,i,o);return this._deleteMarkedTokens(this._markOverflowingTokensForDeletion(0,this.text.length)),r},t.prototype.append=function(n,i,o,r){var s=this.text.length;if(e.prototype.append.call(this,n,i,o,r),o instanceof t){var a=o._lineTokens;if(a){var u=new Uint32Array(a);if(s>0)for(var l=0,c=u.length>>>1;l>>1,i=0,o=0,r=0,s=function(e,s,a){for(var u=a-1;o0&&0!==s){var l=Math.max(u,o+s);if(t[i<<1]=l,s<0)for(var c=i;c>0;){var d=t[c-1<<1];if(!(d>=l))break;4294967295!==d&&(t[c-1<<1]=4294967295,r++),c--}}++i>>1;if(e+1===i)return e;for(var o=i-1;o>0;o--){var r=n[o<<1];if(r>>1,i=new Uint32Array(n-e<<1),o=0,r=0;r=o)return{word:a[0],startColumn:i+1+a.index,endColumn:i+1+t.lastIndex};return null}function i(e,t,n,i){var o=e-1-i;t.lastIndex=0;for(var r;r=t.exec(n);){if(r.index>o)return null;if(t.lastIndex>=o)return{word:r[0],startColumn:i+1+r.index,endColumn:i+1+t.lastIndex}}return null}Object.defineProperty(t,"__esModule",{value:!0}),t.USUAL_WORD_SEPARATORS="`~!@#$%^&*()-=+[{]}\\|;:'\",.<>/?",t.DEFAULT_WORD_REGEXP=function(e){void 0===e&&(e="");for(var n=t.USUAL_WORD_SEPARATORS,i="(-?\\d*\\.\\d\\w*)|([^",o=0;o=0||(i+="\\"+n[o]);return i+="\\s]+)",new RegExp(i,"g")}(),t.ensureValidWordDefinition=function(e){var n=t.DEFAULT_WORD_REGEXP;if(e&&e instanceof RegExp)if(e.global)n=e;else{var i="g";e.ignoreCase&&(i+="i"),e.multiline&&(i+="m"),n=new RegExp(e.source,i)}return n.lastIndex=0,n},t.getWordAtText=function(e,t,o,r){t.lastIndex=0;var s=t.exec(o);if(!s)return null;var a=s[0].indexOf(" ")>=0?i(e,t,o,r):n(e,t,o,r);return t.lastIndex=0,a}}),define(d[511],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(e){this._languageIdentifier=e}return e.prototype.getId=function(){return this._languageIdentifier.language},e.prototype.getLanguageIdentifier=function(){return this._languageIdentifier},e}();t.FrankensteinMode=n}),define(d[60],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});!function(e){e[e.None=0]="None",e[e.Indent=1]="Indent",e[e.IndentOutdent=2]="IndentOutdent",e[e.Outdent=3]="Outdent"}(t.IndentAction||(t.IndentAction={}));var n=function(){function e(e){if(this.open=e.open,this.close=e.close,this._standardTokenMask=0,Array.isArray(e.notIn))for(var t=0,n=e.notIn.length;tr&&(r=u)}return r}if("string"==typeof e)return"*"===e?5:e===o?10:0;if(e){var l=e.language,c=e.pattern,d=e.scheme,r=0;if(d)if(d===t.scheme)r=10;else{if("*"!==d)return 0;r=5}if(l)if(l===o)r=10;else{if("*"!==l)return 0;r=Math.max(r,5)}if(c){if(c!==t.fsPath&&!n.match(c,t.fsPath))return 0;r=10}return r}return 0}Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(e,t,n){return i(e,t,n)>0},t.score=i}),define(d[533],h([1,0,11,513]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(){this._clock=0,this._entries=[],this._onDidChange=new n.Emitter}return Object.defineProperty(e.prototype,"onDidChange",{get:function(){return this._onDidChange.event},enumerable:!0,configurable:!0}),e.prototype.register=function(e,t){var n=this,i={selector:e,provider:t,_score:-1,_time:this._clock++};return this._entries.push(i),this._lastCandidate=void 0,this._onDidChange.fire(this._entries.length),{dispose:function(){if(i){var e=n._entries.indexOf(i);e>=0&&(n._entries.splice(e,1),n._lastCandidate=void 0,n._onDidChange.fire(n._entries.length),i=void 0)}}}},e.prototype.has=function(e){return this.all(e).length>0},e.prototype.all=function(e){if(!e||e.isTooLargeForHavingARichMode())return[];this._updateScores(e);for(var t=[],n=0,i=this._entries;n0&&t.push(o.provider)}return t},e.prototype.ordered=function(e){var t=[];return this._orderedForEach(e,function(e){return t.push(e.provider)}),t},e.prototype.orderedGroups=function(e){var t,n,i=[];return this._orderedForEach(e,function(e){t&&n===e._score?t.push(e.provider):(n=e._score,t=[e.provider],i.push(t))}),i},e.prototype._orderedForEach=function(e,t){if(e&&!e.isTooLargeForHavingARichMode()){this._updateScores(e);for(var n=0;n0&&t(i)}}},e.prototype._updateScores=function(t){var n={uri:t.uri.toString(),language:t.getLanguageIdentifier().language};if(!this._lastCandidate||this._lastCandidate.language!==n.language||this._lastCandidate.uri!==n.uri){this._lastCandidate=n;for(var o=0,r=this._entries;ot._score?-1:e._timet._time?-1:0},e}();t.default=o}),define(d[536],h([1,0,89,96]),function(e,t,n,i){"use strict";function o(){return null===l&&(l=new u([[1,104,2],[1,72,2],[1,102,6],[1,70,6],[2,116,3],[2,84,3],[3,116,4],[3,84,4],[4,112,5],[4,80,5],[5,115,9],[5,83,9],[5,58,10],[6,105,7],[6,73,7],[7,108,8],[7,76,8],[8,101,9],[8,69,9],[9,58,10],[10,47,11],[11,47,12]])),l}function r(){if(null===c){c=new n.CharacterClassifier(0);for(e=0;e<" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".length;e++)c.set(" \t<>'\"、。。、,.:;?!@#$%&*‘“〈《「『【〔([{「」}])〕】』」》〉”’`~…".charCodeAt(e),1);for(var e=0;e<".,;".length;e++)c.set(".,;".charCodeAt(e),2)}return c}Object.defineProperty(t,"__esModule",{value:!0});var s;!function(e){e[e.Invalid=0]="Invalid",e[e.Start=1]="Start",e[e.H=2]="H",e[e.HT=3]="HT",e[e.HTT=4]="HTT",e[e.HTTP=5]="HTTP",e[e.F=6]="F",e[e.FI=7]="FI",e[e.FIL=8]="FIL",e[e.BeforeColon=9]="BeforeColon",e[e.AfterColon=10]="AfterColon",e[e.AlmostThere=11]="AlmostThere",e[e.End=12]="End",e[e.Accept=13]="Accept"}(s||(s={}));var a,u=function(){function e(e){for(var t=0,n=0,o=0,r=e.length;ot&&(t=u),a>n&&(n=a),l>n&&(n=l)}t++,n++;for(var c=new i.Uint8Matrix(n,t,0),o=0,r=e.length;o=this._maxCharCode?0:this._states.get(e,t)},e}(),l=null;!function(e){e[e.None=0]="None",e[e.ForceTermination=1]="ForceTermination",e[e.CannotEndIn=2]="CannotEndIn"}(a||(a={}));var c=null,d=function(){function e(){}return e._createLink=function(e,t,n,i,o){var r=o-1;do{var s=t.charCodeAt(r);if(2!==e.get(s))break;r--}while(r>i);return{range:{startLineNumber:n,startColumn:i+1,endLineNumber:n,endColumn:r+2},url:t.substring(i,r+1)}},e.computeLinks=function(t){for(var n=o(),i=r(),s=[],a=1,u=t.getLineCount();a<=u;a++){for(var l=t.getLineContent(a),c=l.length,d=0,h=0,p=0,f=1,g=!1,m=!1,v=!1;d=0},e.prototype.shouldIncrease=function(e){return!!(this._indentationRules&&this._indentationRules.increaseIndentPattern&&this._indentationRules.increaseIndentPattern.test(e))},e.prototype.shouldDecrease=function(e){return!!(this._indentationRules&&this._indentationRules.decreaseIndentPattern&&this._indentationRules.decreaseIndentPattern.test(e))},e.prototype.shouldIndentNextLine=function(e){return!!(this._indentationRules&&this._indentationRules.indentNextLinePattern&&this._indentationRules.indentNextLinePattern.test(e))},e.prototype.shouldIgnore=function(e){return!!(this._indentationRules&&this._indentationRules.unIndentedLinePattern&&this._indentationRules.unIndentedLinePattern.test(e))},e.prototype.getIndentMetadata=function(e){var t=0;return this.shouldIncrease(e)&&(t+=1),this.shouldDecrease(e)&&(t+=2),this.shouldIndentNextLine(e)&&(t+=4),this.shouldIgnore(e)&&(t+=8),t},e}();t.IndentRulesSupport=o}),define(d[546],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){this._defaultValueSet=[["true","false"],["True","False"],["Private","Public","Friend","ReadOnly","Partial","Protected","WriteOnly"],["public","protected","private"]]}return e.prototype.navigateValueSet=function(e,t,n,i,o){if(e&&t&&(r=this.doNavigateValueSet(t,o)))return{range:e,value:r};if(n&&i){var r=this.doNavigateValueSet(i,o);if(r)return{range:n,value:r}}return null},e.prototype.doNavigateValueSet=function(e,t){var n=this.numberReplace(e,t);return null!==n?n:this.textReplace(e,t)},e.prototype.numberReplace=function(e,t){var n=Math.pow(10,e.length-(e.lastIndexOf(".")+1)),i=Number(e),o=parseFloat(e);return isNaN(i)||isNaN(o)||i!==o?null:0!==i||t?(i=Math.floor(i*n),i+=t?n:-n,String(i/n)):null},e.prototype.textReplace=function(e,t){return this.valueSetsReplace(this._defaultValueSet,e,t)},e.prototype.valueSetsReplace=function(e,t,n){for(var i=null,o=0,r=e.length;null===i&&o=0?((i+=n?1:-1)<0?i=e.length-1:i%=e.length,e[i]):null},e.INSTANCE=new e,e}();t.BasicInplaceReplace=n}),define(d[550],h([1,0,10,9,60]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(t){(t=t||{}).brackets=t.brackets||[["(",")"],["{","}"],["[","]"]],this._brackets=t.brackets.map(function(t){return{open:t[0],openRegExp:e._createOpenBracketRegExp(t[0]),close:t[1],closeRegExp:e._createCloseBracketRegExp(t[1])}}),this._regExpRules=t.regExpRules||[],this._indentationRules=t.indentationRules}return e.prototype.onEnter=function(e,t,n){for(var i=0,r=this._regExpRules.length;i0&&n.length>0)for(var i=0,r=this._brackets.length;i0)for(var i=0,r=this._brackets.length;i=0;n--)t+=e.charAt(n);return t}var t=null,n=null;return function(i){return t!==i&&(n=e(t=i)),n}}(),p=function(){function e(){}return e._findPrevBracketInText=function(e,t,n,o){var r=n.match(e);if(!r)return null;var s=n.length-r.index,a=r[0].length,u=o+s;return new i.Range(t,u-a+1,t,u+1)},e.findPrevBracketInToken=function(e,t,n,i,o){var r=h(n).substring(n.length-o,n.length-i);return this._findPrevBracketInText(e,t,r,i)},e.findNextBracketInText=function(e,t,n,o){var r=n.match(e);if(!r)return null;var s=r.index,a=r[0].length,u=o+s;return new i.Range(t,u+1,t,u+1+a)},e.findNextBracketInToken=function(e,t,n,i,o){var r=n.substring(i,o);return this.findNextBracketInText(e,t,r,i)},e}();t.BracketsUtils=p}),define(d[202],h([1,0,32]),function(e,t,n){"use strict";function i(e){if(!e||!Array.isArray(e))return[];for(var t=[],n=0,i=0,o=e.length;i=1&&""===e[0].token;){var o=e.shift();-1!==o.fontStyle&&(t=o.fontStyle),null!==o.foreground&&(n=o.foreground),null!==o.background&&(i=o.background)}for(var r=new u,a=new d(t,r.getId(n),r.getId(i)),c=new p(a),h=0,f=e.length;ht?1:0}Object.defineProperty(t,"__esModule",{value:!0});var a=function(){return function(e,t,n,i,o){this.token=e,this.index=t,this.fontStyle=n,this.foreground=i,this.background=o}}();t.ParsedTokenThemeRule=a,t.parseTokenTheme=i;var u=function(){function e(){this._lastColorId=0,this._id2color=[],this._color2id=new Map}return e.prototype.getId=function(e){if(null===e)return 0;if(e=e.toUpperCase(),!/^[0-9A-F]{6}$/.test(e))throw new Error("Illegal color name: "+e);var t=this._color2id.get(e);return t||(t=++this._lastColorId,this._color2id.set(e,t),this._id2color[t]=n.Color.fromHex("#"+e),t)},e.prototype.getColorMap=function(){return this._id2color.slice(0)},e}();t.ColorMap=u;var l=function(){function e(e,t){this._colorMap=e,this._root=t,this._cache=new Map}return e.createFromRawTokenTheme=function(e){return this.createFromParsedTokenTheme(i(e))},e.createFromParsedTokenTheme=function(e){return o(e)},e.prototype.getColorMap=function(){return this._colorMap.getColorMap()},e.prototype.getThemeTrieElement=function(){return this._root.toExternalThemeTrieElement()},e.prototype._match=function(e){return this._root.match(e)},e.prototype.match=function(e,t){var n=this._cache.get(t);if(void 0===n){var i=this._match(t),o=r(t);n=(i.metadata|o<<8)>>>0,this._cache.set(t,n)}return(n|e<<0)>>>0},e}();t.TokenTheme=l;var c=/\b(comment|string|regex)\b/;t.toStandardTokenType=r,t.strcmp=s;var d=function(){function e(e,t,n){this._fontStyle=e,this._foreground=t,this._background=n,this.metadata=(this._fontStyle<<11|this._foreground<<14|this._background<<23)>>>0}return e.prototype.clone=function(){return new e(this._fontStyle,this._foreground,this._background)},e.cloneArr=function(e){for(var t=[],n=0,i=e.length;n>>0},e}();t.ThemeTrieElementRule=d;var h=function(){return function(e,t){this.mainRule=e,this.children=t||Object.create(null)}}();t.ExternalThemeTrieElement=h;var p=function(){function e(e){this._mainRule=e,this._children=new Map}return e.prototype.toExternalThemeTrieElement=function(){var e=Object.create(null);return this._children.forEach(function(t,n){e[n]=t.toExternalThemeTrieElement()}),new h(this._mainRule,e)},e.prototype.match=function(e){if(""===e)return this._mainRule;var t,n,i=e.indexOf(".");-1===i?(t=e,n=""):(t=e.substring(0,i),n=e.substring(i+1));var o=this._children.get(t);return void 0!==o?o.match(n):this._mainRule},e.prototype.insert=function(t,n,i,o){if(""!==t){var r,s,a=t.indexOf(".");-1===a?(r=t,s=""):(r=t.substring(0,a),s=t.substring(a+1));var u=this._children.get(r);void 0===u&&(u=new e(this._mainRule.clone()),this._children.set(r,u)),u.insert(s,n,i,o)}else this._mainRule.acceptOverwrite(n,i,o)},e}();t.ThemeTrieElement=p,t.generateTokensCSSForColorMap=function(e){for(var t=[],n=1,i=e.length;n>>0,new i.TokenizationResult2(r,n)}}),define(d[91],h([1,0,17]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.createScopedLineTokens=function(e,t){for(var n=e.getTokenCount(),o=e.findTokenIndexAtOffset(t),r=e.getLanguageId(o),s=o;s+10&&e.getLanguageId(a-1)===r;)a--;return new i(e,r,a,s+1,e.getTokenStartOffset(a),e.getTokenEndOffset(s))};var i=function(){function e(e,t,n,i,o,r){this._actual=e,this.languageId=t,this._firstTokenIndex=n,this._lastTokenIndex=i,this.firstCharOffset=o,this._lastCharOffset=r}return e.prototype.getLineContent=function(){return this._actual.getLineContent().substring(this.firstCharOffset,this._lastCharOffset)},e.prototype.getTokenCount=function(){return this._lastTokenIndex-this._firstTokenIndex},e.prototype.findTokenIndexAtOffset=function(e){return this._actual.findTokenIndexAtOffset(e+this.firstCharOffset)-this._firstTokenIndex},e.prototype.getTokenStartOffset=function(e){return this._actual.getTokenStartOffset(e+this._firstTokenIndex)-this.firstCharOffset},e.prototype.getStandardTokenType=function(e){return this._actual.getStandardTokenType(e+this._firstTokenIndex)},e}();t.ScopedLineTokens=i;var o;!function(e){e[e.value=7]="value"}(o||(o={})),t.ignoreBracketsInToken=function(e){return 0!=(7&e)}}),define(d[207],h([1,0,91,98,60]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t,n){n=n||{},this._richEditBrackets=e,this._complexAutoClosePairs=t.filter(function(e){return e.open.length>1&&!!e.close}).map(function(e){return new o.StandardAutoClosingPairConditional(e)}),n.docComment&&this._complexAutoClosePairs.push(new o.StandardAutoClosingPairConditional({open:n.docComment.open,close:n.docComment.close}))}return e.prototype.getElectricCharacters=function(){var e=[];if(this._richEditBrackets)for(var t=0,n=this._richEditBrackets.brackets.length;t=0))return{appendText:s.close}}}return null},e}();t.BracketElectricCharacterSupport=r}),define(d[43],h([1,0,543,207,550,545,98,11,10,9,95,91,2,60]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var f=function(){function e(t,o,a){var u=null;o&&(u=o._conf),this._conf=e._mergeConf(u,a),this._conf.brackets&&(this.brackets=new s.RichEditBrackets(t,this._conf.brackets)),this.onEnter=e._handleOnEnter(this._conf),this.comments=e._handleComments(this._conf),this.characterPair=new n.CharacterPairSupport(this._conf),this.electricCharacter=new i.BracketElectricCharacterSupport(this.brackets,this.characterPair.getAutoClosingPairs(),this._conf.__electricCharacterSupport),this.wordDefinition=this._conf.wordPattern||c.DEFAULT_WORD_REGEXP,this.indentationRules=this._conf.indentationRules,this._conf.indentationRules&&(this.indentRulesSupport=new r.IndentRulesSupport(this._conf.indentationRules))}return e._mergeConf=function(e,t){return{comments:e?t.comments||e.comments:t.comments,brackets:e?t.brackets||e.brackets:t.brackets,wordPattern:e?t.wordPattern||e.wordPattern:t.wordPattern,indentationRules:e?t.indentationRules||e.indentationRules:t.indentationRules,onEnterRules:e?t.onEnterRules||e.onEnterRules:t.onEnterRules,autoClosingPairs:e?t.autoClosingPairs||e.autoClosingPairs:t.autoClosingPairs,surroundingPairs:e?t.surroundingPairs||e.surroundingPairs:t.surroundingPairs,__electricCharacterSupport:e?t.__electricCharacterSupport||e.__electricCharacterSupport:t.__electricCharacterSupport}},e._handleOnEnter=function(e){var t={},n=!0;return e.brackets&&(n=!1,t.brackets=e.brackets),e.indentationRules&&(n=!1,t.indentationRules=e.indentationRules),e.onEnterRules&&(n=!1,t.regExpRules=e.onEnterRules),n?null:new o.OnEnterSupport(t)},e._handleComments=function(e){var t=e.comments;if(!t)return null;var n={};if(t.lineComment&&(n.lineCommentToken=t.lineComment),t.blockComment){var i=t.blockComment,o=i[0],r=i[1];n.blockCommentStartToken=o,n.blockCommentEndToken=r}return n},e}();t.RichEditSupport=f;var g=function(){function e(){this._onDidChange=new a.Emitter,this.onDidChange=this._onDidChange.event,this._entries=[]}return e.prototype.register=function(e,t){var n=this,i=this._getRichEditSupport(e.id),o=new f(e,i,t);return this._entries[e.id]=o,this._onDidChange.fire(void 0),{dispose:function(){n._entries[e.id]===o&&(n._entries[e.id]=i,n._onDidChange.fire(void 0))}}},e.prototype._getRichEditSupport=function(e){return this._entries[e]||null},e.prototype.getIndentationRules=function(e){var t=this._entries[e];return t?t.indentationRules||null:null},e.prototype._getElectricCharacterSupport=function(e){var t=this._getRichEditSupport(e);return t?t.electricCharacter||null:null},e.prototype.getElectricCharacters=function(e){var t=this._getElectricCharacterSupport(e);return t?t.getElectricCharacters():[]},e.prototype.onElectricCharacter=function(e,t,n){var i=d.createScopedLineTokens(t,n-1),o=this._getElectricCharacterSupport(i.languageId);return o?o.onElectricCharacter(e,i,n-i.firstCharOffset):null},e.prototype.getComments=function(e){var t=this._getRichEditSupport(e);return t?t.comments||null:null},e.prototype._getCharacterPairSupport=function(e){var t=this._getRichEditSupport(e);return t?t.characterPair||null:null},e.prototype.getAutoClosingPairs=function(e){var t=this._getCharacterPairSupport(e);return t?t.getAutoClosingPairs():[]},e.prototype.getSurroundingPairs=function(e){var t=this._getCharacterPairSupport(e);return t?t.getSurroundingPairs():[]},e.prototype.shouldAutoClosePair=function(e,t,n){var i=d.createScopedLineTokens(t,n-1),o=this._getCharacterPairSupport(i.languageId);return!!o&&o.shouldAutoClosePair(e,i,n-i.firstCharOffset)},e.prototype.getWordDefinition=function(e){var t=this._getRichEditSupport(e);return t?c.ensureValidWordDefinition(t.wordDefinition||null):c.ensureValidWordDefinition(null)},e.prototype.getIndentRulesSupport=function(e){var t=this._getRichEditSupport(e);return t?t.indentRulesSupport||null:null},e.prototype.getPrecedingValidLine=function(e,t,n){var i=e.getLanguageIdAtPosition(t,0);if(t>1){var o=t-1,r=-1;for(o=t-1;o>=1;o--){if(e.getLanguageIdAtPosition(o,0)!==i)return r;var s=e.getLineContent(o);if(!n.shouldIgnore(s)&&!/^\s+$/.test(s)&&""!==s)return o;r=o}}return-1},e.prototype.getInheritIndentForLine=function(e,t,n){void 0===n&&(n=!0);var i=this.getIndentRulesSupport(e.getLanguageIdentifier().id);if(!i)return null;if(t<=1)return{indentation:"",action:null};var o=this.getPrecedingValidLine(e,t,i);if(o<0)return null;if(o<1)return{indentation:"",action:null};var r=e.getLineContent(o);if(i.shouldIncrease(r)||i.shouldIndentNextLine(r))return{indentation:l.getLeadingWhitespace(r),action:p.IndentAction.Indent,line:o};if(i.shouldDecrease(r))return{indentation:l.getLeadingWhitespace(r),action:null,line:o};if(1===o)return{indentation:l.getLeadingWhitespace(e.getLineContent(o)),action:null,line:o};var s=o-1,a=i.getIndentMetadata(e.getLineContent(s));if(!(3&a)&&4&a){for(var u=0,c=s-1;c>0;c--)if(!i.shouldIndentNextLine(e.getLineContent(c))){u=c;break}return{indentation:l.getLeadingWhitespace(e.getLineContent(u+1)),action:null,line:u+1}}if(n)return{indentation:l.getLeadingWhitespace(e.getLineContent(o)),action:null,line:o};for(c=o;c>0;c--){var d=e.getLineContent(c);if(i.shouldIncrease(d))return{indentation:l.getLeadingWhitespace(d),action:p.IndentAction.Indent,line:c};if(i.shouldIndentNextLine(d)){for(var u=0,h=c-1;h>0;h--)if(!i.shouldIndentNextLine(e.getLineContent(c))){u=h;break}return{indentation:l.getLeadingWhitespace(e.getLineContent(u+1)),action:null,line:u+1}}if(i.shouldDecrease(d))return{indentation:l.getLeadingWhitespace(d),action:null,line:c}}return{indentation:l.getLeadingWhitespace(e.getLineContent(1)),action:null,line:1}},e.prototype.getGoodIndentForLine=function(e,t,n,i){var o=this.getIndentRulesSupport(t);if(!o)return null;var r=this.getInheritIndentForLine(e,n),s=e.getLineContent(n);if(r){var a=r.line;if(void 0!==a){var c=this._getOnEnterSupport(t),d=null;try{d=c.onEnter("",e.getLineContent(a),"")}catch(e){u.onUnexpectedError(e)}if(d){var h=l.getLeadingWhitespace(e.getLineContent(a));return d.removeText&&(h=h.substring(0,h.length-d.removeText)),d.indentAction===p.IndentAction.Indent||d.indentAction===p.IndentAction.IndentOutdent?h=i.shiftIndent(h):d.indentAction===p.IndentAction.Outdent&&(h=i.unshiftIndent(h)),o.shouldDecrease(s)&&(h=i.unshiftIndent(h)),d.appendText&&(h+=d.appendText),l.getLeadingWhitespace(h)}}return o.shouldDecrease(s)?r.action===p.IndentAction.Indent?r.indentation:i.unshiftIndent(r.indentation):r.action===p.IndentAction.Indent?i.shiftIndent(r.indentation):r.indentation}return null},e.prototype.getIndentForEnter=function(e,t,n,i){e.forceTokenization(t.startLineNumber);var o,r,s=e.getLineTokens(t.startLineNumber),a=d.createScopedLineTokens(s,t.startColumn-1),u=a.getLineContent(),c=!1;a.firstCharOffset>0&&s.getLanguageId(0)!==a.languageId?(c=!0,o=u.substr(0,t.startColumn-1-a.firstCharOffset)):o=s.getLineContent().substring(0,t.startColumn-1),r=t.isEmpty()?u.substr(t.startColumn-1-a.firstCharOffset):this.getScopedLineTokens(e,t.endLineNumber,t.endColumn).getLineContent().substr(t.endColumn-1-a.firstCharOffset);var h=this.getIndentRulesSupport(a.languageId);if(!h)return null;var f=o,g=l.getLeadingWhitespace(o);if(!i&&!c){var m=this.getInheritIndentForLine(e,t.startLineNumber);h.shouldDecrease(o)&&m&&(g=m.indentation,m.action!==p.IndentAction.Indent&&(g=n.unshiftIndent(g))),f=g+l.ltrim(l.ltrim(o," "),"\t")}var v={getLineTokens:function(t){return e.getLineTokens(t)},getLanguageIdentifier:function(){return e.getLanguageIdentifier()},getLanguageIdAtPosition:function(t,n){return e.getLanguageIdAtPosition(t,n)},getLineContent:function(n){return n===t.startLineNumber?f:e.getLineContent(n)}},_=l.getLeadingWhitespace(s.getLineContent()),y=this.getInheritIndentForLine(v,t.startLineNumber+1);if(!y){var C=c?_:g;return{beforeEnter:C,afterEnter:C}}var b=c?_:y.indentation;return y.action===p.IndentAction.Indent&&(b=n.shiftIndent(b)),h.shouldDecrease(r)&&(b=n.unshiftIndent(b)),{beforeEnter:c?_:g,afterEnter:b}},e.prototype.getIndentActionForType=function(e,t,n,i){var o=this.getScopedLineTokens(e,t.startLineNumber,t.startColumn),r=this.getIndentRulesSupport(o.languageId);if(!r)return null;var s,a=o.getLineContent(),u=a.substr(0,t.startColumn-1-o.firstCharOffset);if(s=t.isEmpty()?a.substr(t.startColumn-1-o.firstCharOffset):this.getScopedLineTokens(e,t.endLineNumber,t.endColumn).getLineContent().substr(t.endColumn-1-o.firstCharOffset),!r.shouldDecrease(u+s)&&r.shouldDecrease(u+n+s)){var l=this.getInheritIndentForLine(e,t.startLineNumber,!1);if(!l)return null;var c=l.indentation;return l.action!==p.IndentAction.Indent&&(c=i.unshiftIndent(c)),c}return null},e.prototype.getIndentMetadata=function(e,t){var n=this.getIndentRulesSupport(e.getLanguageIdentifier().id);return n?t<1||t>e.getLineCount()?null:n.getIndentMetadata(e.getLineContent(t)):null},e.prototype._getOnEnterSupport=function(e){var t=this._getRichEditSupport(e);return t?t.onEnter||null:null},e.prototype.getRawEnterActionAtPosition=function(e,t,n){var i=this.getEnterAction(e,new h.Range(t,n,t,n));return i?i.enterAction:null},e.prototype.getEnterAction=function(e,t){var n=this.getIndentationAtPosition(e,t.startLineNumber,t.startColumn),i=this.getScopedLineTokens(e,t.startLineNumber,t.startColumn),o=this._getOnEnterSupport(i.languageId);if(!o)return null;var r,s=i.getLineContent(),a=s.substr(0,t.startColumn-1-i.firstCharOffset);r=t.isEmpty()?s.substr(t.startColumn-1-i.firstCharOffset):this.getScopedLineTokens(e,t.endLineNumber,t.endColumn).getLineContent().substr(t.endColumn-1-i.firstCharOffset);var l=t.startLineNumber,c="";if(l>1&&0===i.firstCharOffset){var d=this.getScopedLineTokens(e,l-1);d.languageId===i.languageId&&(c=d.getLineContent())}var h=null;try{h=o.onEnter(c,a,r)}catch(e){u.onUnexpectedError(e)}return h?(h.appendText||(h.indentAction===p.IndentAction.Indent||h.indentAction===p.IndentAction.IndentOutdent?h.appendText="\t":h.appendText=""),h.removeText&&(n=n.substring(0,n.length-h.removeText)),{enterAction:h,indentation:n}):null},e.prototype.getIndentationAtPosition=function(e,t,n){var i=e.getLineContent(t),o=l.getLeadingWhitespace(i);return o.length>n-1&&(o=o.substring(0,n-1)),o},e.prototype.getScopedLineTokens=function(e,t,n){e.forceTokenization(t);var i=e.getLineTokens(t),o=isNaN(n)?e.getLineMaxColumn(t)-1:n-1;return d.createScopedLineTokens(i,o)},e.prototype.getBracketsSupport=function(e){var t=this._getRichEditSupport(e);return t?t.brackets||null:null},e}();t.LanguageConfigurationRegistryImpl=g,t.LanguageConfigurationRegistry=new g}),define(d[122],h([1,0,9,17,68,130]),function(e,t,n,i,o,r){"use strict";function s(e){var t=i.TokenizationRegistry.get(e);return t||{getInitialState:function(){return o.NULL_STATE},tokenize:void 0,tokenize2:function(e,t,n){return o.nullTokenize2(0,e,t,n)}}}function a(e,t){for(var i='
    ',o=e.split(/\r\n|\r|\n/),s=t.getInitialState(),a=0,u=o.length;a0&&(i+="
    ");for(var c=t.tokenize2(l,s,0),d=new r.LineTokens(c.tokens,l).inflate(),h=0,p=0,f=d.length;p'+n.escape(l.substring(h,g.endIndex))+"",h=g.endIndex}s=c.endState}return i+="
    "}Object.defineProperty(t,"__esModule",{value:!0}),t.tokenizeToString=function(e,t){return a(e,s(t))},t.tokenizeLineToHTML=function(e,t,n,i,o,r){for(var s="
    ",a=i,u=0,l=0,c=t.length;l0;)p+=" ",g--;break;case 60:p+="<";break;case 62:p+=">";break;case 38:p+="&";break;case 0:p+="�";break;case 65279:case 8232:p+="�";break;case 13:p+="​";break;default:p+=String.fromCharCode(f)}}if(s+=''+p+"",d.endIndex>o||a>=o)break}}return s+="
    "}}),define(d[210],h([1,0,11]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(){this._transientWatchers={},this._codeEditors=Object.create(null),this._diffEditors=Object.create(null),this._onCodeEditorAdd=new n.Emitter,this._onCodeEditorRemove=new n.Emitter,this._onDiffEditorAdd=new n.Emitter,this._onDiffEditorRemove=new n.Emitter}return e.prototype.addCodeEditor=function(e){this._codeEditors[e.getId()]=e,this._onCodeEditorAdd.fire(e)},Object.defineProperty(e.prototype,"onCodeEditorAdd",{get:function(){return this._onCodeEditorAdd.event},enumerable:!0,configurable:!0}),e.prototype.removeCodeEditor=function(e){delete this._codeEditors[e.getId()]&&this._onCodeEditorRemove.fire(e)},Object.defineProperty(e.prototype,"onCodeEditorRemove",{get:function(){return this._onCodeEditorRemove.event},enumerable:!0,configurable:!0}),e.prototype.getCodeEditor=function(e){return this._codeEditors[e]||null},e.prototype.listCodeEditors=function(){var e=this;return Object.keys(this._codeEditors).map(function(t){return e._codeEditors[t]})},e.prototype.addDiffEditor=function(e){this._diffEditors[e.getId()]=e,this._onDiffEditorAdd.fire(e)},Object.defineProperty(e.prototype,"onDiffEditorAdd",{get:function(){return this._onDiffEditorAdd.event},enumerable:!0,configurable:!0}),e.prototype.removeDiffEditor=function(e){delete this._diffEditors[e.getId()]&&this._onDiffEditorRemove.fire(e)},Object.defineProperty(e.prototype,"onDiffEditorRemove",{get:function(){return this._onDiffEditorRemove.event},enumerable:!0,configurable:!0}),e.prototype.getDiffEditor=function(e){return this._diffEditors[e]||null},e.prototype.listDiffEditors=function(){var e=this;return Object.keys(this._diffEditors).map(function(t){return e._diffEditors[t]})},e.prototype.getFocusedCodeEditor=function(){for(var e=null,t=this.listCodeEditors(),n=0;n=.5,this._onDidChange.fire(void 0)},e.prototype.getColor=function(e){return(e<1||e>=this._colors.length)&&(e=2),this._colors[e]},e.prototype.backgroundIsLight=function(){return this._backgroundIsLight},e._INSTANCE=null,e}();t.MinimapTokensColorTracker=o;!function(e){e[e.START_CH_CODE=32]="START_CH_CODE",e[e.END_CH_CODE=126]="END_CH_CODE",e[e.CHAR_COUNT=95]="CHAR_COUNT",e[e.SAMPLED_CHAR_HEIGHT=16]="SAMPLED_CHAR_HEIGHT",e[e.SAMPLED_CHAR_WIDTH=10]="SAMPLED_CHAR_WIDTH",e[e.SAMPLED_HALF_CHAR_WIDTH=5]="SAMPLED_HALF_CHAR_WIDTH",e[e.x2_CHAR_HEIGHT=4]="x2_CHAR_HEIGHT",e[e.x2_CHAR_WIDTH=2]="x2_CHAR_WIDTH",e[e.x1_CHAR_HEIGHT=2]="x1_CHAR_HEIGHT",e[e.x1_CHAR_WIDTH=1]="x1_CHAR_WIDTH",e[e.RGBA_CHANNELS_CNT=4]="RGBA_CHANNELS_CNT"}(t.Constants||(t.Constants={}));var r=function(){function e(t,n){if(760!==t.length)throw new Error("Invalid x2CharData");if(190!==n.length)throw new Error("Invalid x1CharData");this.x2charData=t,this.x1charData=n,this.x2charDataLight=e.soften(t,.8),this.x1charDataLight=e.soften(n,50/60)}return e.soften=function(e,t){for(var n=new Uint8ClampedArray(e.length),i=0,o=e.length;it.width||i+4>t.height)console.warn("bad render request outside image data");else{var u=a?this.x2charDataLight:this.x2charData,l=e._getChIndex(o),c=4*t.width,d=s.r,h=s.g,p=s.b,f=r.r-d,g=r.g-h,m=r.b-p,v=t.data,_=4*l*2,y=i*c+4*n,C=u[_]/255;v[y+0]=d+f*C,v[y+1]=h+g*C,v[y+2]=p+m*C;C=u[_+1]/255;v[y+4]=d+f*C,v[y+5]=h+g*C,v[y+6]=p+m*C,y+=c;C=u[_+2]/255;v[y+0]=d+f*C,v[y+1]=h+g*C,v[y+2]=p+m*C;C=u[_+3]/255;v[y+4]=d+f*C,v[y+5]=h+g*C,v[y+6]=p+m*C,y+=c;C=u[_+4]/255;v[y+0]=d+f*C,v[y+1]=h+g*C,v[y+2]=p+m*C;C=u[_+5]/255;v[y+4]=d+f*C,v[y+5]=h+g*C,v[y+6]=p+m*C,y+=c;C=u[_+6]/255;v[y+0]=d+f*C,v[y+1]=h+g*C,v[y+2]=p+m*C;C=u[_+7]/255;v[y+4]=d+f*C,v[y+5]=h+g*C,v[y+6]=p+m*C}},e.prototype.x1RenderChar=function(t,n,i,o,r,s,a){if(n+1>t.width||i+2>t.height)console.warn("bad render request outside image data");else{var u=a?this.x1charDataLight:this.x1charData,l=e._getChIndex(o),c=4*t.width,d=s.r,h=s.g,p=s.b,f=r.r-d,g=r.g-h,m=r.b-p,v=t.data,_=2*l*1,y=i*c+4*n,C=u[_]/255;v[y+0]=d+f*C,v[y+1]=h+g*C,v[y+2]=p+m*C,y+=c;C=u[_+1]/255;v[y+0]=d+f*C,v[y+1]=h+g*C,v[y+2]=p+m*C}},e.prototype.x2BlockRenderChar=function(e,t,n,i,o,r){if(t+2>e.width||n+4>e.height)console.warn("bad render request outside image data");else{var s=4*e.width,a=o.r,u=o.g,l=o.b,c=a+.5*(i.r-a),d=u+.5*(i.g-u),h=l+.5*(i.b-l),p=e.data,f=n*s+4*t;p[f+0]=c,p[f+1]=d,p[f+2]=h,p[f+4]=c,p[f+5]=d,p[f+6]=h,p[(f+=s)+0]=c,p[f+1]=d,p[f+2]=h,p[f+4]=c,p[f+5]=d,p[f+6]=h,p[(f+=s)+0]=c,p[f+1]=d,p[f+2]=h,p[f+4]=c,p[f+5]=d,p[f+6]=h,p[(f+=s)+0]=c,p[f+1]=d,p[f+2]=h,p[f+4]=c,p[f+5]=d,p[f+6]=h}},e.prototype.x1BlockRenderChar=function(e,t,n,i,o,r){if(t+1>e.width||n+2>e.height)console.warn("bad render request outside image data");else{var s=4*e.width,a=o.r,u=o.g,l=o.b,c=a+.5*(i.r-a),d=u+.5*(i.g-u),h=l+.5*(i.b-l),p=e.data,f=n*s+4*t;p[f+0]=c,p[f+1]=d,p[f+2]=h,p[(f+=s)+0]=c,p[f+1]=d,p[f+2]=h}},e}();t.MinimapCharRenderer=r}),define(d[78],h([1,0,2]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){this._viewLayout=e,this.viewportData=t,this.scrollWidth=this._viewLayout.getScrollWidth(),this.scrollHeight=this._viewLayout.getScrollHeight(),this.visibleRange=this.viewportData.visibleRange,this.bigNumbersDelta=this.viewportData.bigNumbersDelta;var n=this._viewLayout.getCurrentViewport();this.scrollTop=n.top,this.scrollLeft=n.left,this.viewportWidth=n.width,this.viewportHeight=n.height}return e.prototype.getScrolledTopFromAbsoluteTop=function(e){return e-this.scrollTop},e.prototype.getVerticalOffsetForLineNumber=function(e){return this._viewLayout.getVerticalOffsetForLineNumber(e)},e.prototype.lineIsVisible=function(e){return this.visibleRange.startLineNumber<=e&&e<=this.visibleRange.endLineNumber},e.prototype.getDecorationsInViewport=function(){return this.viewportData.getDecorationsInViewport()},e}();t.RestrictedRenderingContext=i;var o=function(e){function t(t,n,i){var o=e.call(this,t,n)||this;return o._viewLines=i,o}return f(t,e),t.prototype.linesVisibleRangesForRange=function(e,t){return this._viewLines.linesVisibleRangesForRange(e,t)},t.prototype.visibleRangeForPosition=function(e){var t=this._viewLines.visibleRangesForRange2(new n.Range(e.lineNumber,e.column,e.lineNumber,e.column));return t?t[0]:null},t}(i);t.RenderingContext=o;var r=function(){return function(e,t){this.lineNumber=e,this.ranges=t}}();t.LineVisibleRanges=r;var s=function(){function e(e,t){this.left=Math.round(e),this.width=Math.round(t)}return e.prototype.toString=function(){return"["+this.left+","+this.width+"]"},e}();t.HorizontalRange=s}),define(d[214],h([1,0,78]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){this.left=e,this.width=t}return e.prototype.toString=function(){return"["+this.left+","+this.width+"]"},e.compare=function(e,t){return e.left-t.left},e}(),o=function(){function e(){}return e._createRange=function(){return this._handyReadyRange||(this._handyReadyRange=document.createRange()),this._handyReadyRange},e._detachRange=function(e,t){e.selectNodeContents(t)},e._readClientRects=function(e,t,n,i,o){var r=this._createRange();try{return r.setStart(e,t),r.setEnd(n,i),r.getClientRects()}catch(e){return null}finally{this._detachRange(r,o)}},e._mergeAdjacentRanges=function(e){if(1===e.length)return[new n.HorizontalRange(e[0].left,e[0].width)];e.sort(i.compare);for(var t=[],o=0,r=e[0].left,s=e[0].width,a=1,u=e.length;a=c?s=Math.max(s,c+d-r):(t[o++]=new n.HorizontalRange(r,s),r=c,s=d)}return t[o++]=new n.HorizontalRange(r,s),t},e._createHorizontalRangesFromClientRects=function(e,t){if(!e||0===e.length)return null;for(var n=[],o=0,r=e.length;oa)return null;(t=Math.min(a,Math.max(0,t)))!==(i=Math.min(a,Math.max(0,i)))&&i>0&&0===o&&(i--,o=Number.MAX_VALUE);var u=e.children[t].firstChild,l=e.children[i].firstChild;if(!u||!l)return null;n=Math.min(u.textContent.length,Math.max(0,n)),o=Math.min(l.textContent.length,Math.max(0,o));var c=this._readClientRects(u,n,l,o,s);return this._createHorizontalRangesFromClientRects(c,r)},e}();t.RangeUtil=o}),define(d[215],h([1,0,111]),function(e,t,n){"use strict";function i(e){for(var t=new Uint8ClampedArray(e.length),n=0,i=e.length;nn)&&!c.isEmpty()){var d=c.startLineNumber===n?c.startColumn:i,h=c.endLineNumber===n?c.endColumn:o;h<=1||(r[s++]=new e(d,h,l.inlineClassName,l.insertsBeforeOrAfter))}}return r},e.compare=function(e,t){return e.startColumn===t.startColumn?e.endColumn===t.endColumn?e.classNamet.className?1:0:e.endColumn-t.endColumn:e.startColumn-t.startColumn},e}();t.LineDecoration=n;var i=function(){return function(e,t,n){this.startOffset=e,this.endOffset=t,this.className=n}}();t.DecorationSegment=i;var o=function(){function e(){this.stopOffsets=[],this.classNames=[],this.count=0}return e.prototype.consumeLowerThan=function(e,t,n){for(;this.count>0&&this.stopOffsets[0]0&&t=e){this.stopOffsets.splice(n,0,e),this.classNames.splice(n,0,t);break}this.count++},e}(),r=function(){function e(){}return e.normalize=function(e){if(0===e.length)return[];for(var t=[],n=new o,i=0,r=0,s=e.length;r0){for(var h=0,p=e.lineDecorations.length;h0&&(i[o++]=new d(t,""));for(var r=0,s=e.length;r=n){i[o++]=new d(n,l);break}i[o++]=new d(u,l)}}return i}function a(e,t){for(var n=0,i=[],r=0,s=0,a=t.length;s50){for(var h=u.type,p=Math.ceil(c/50),f=1;fu)C=!0;else if(9===y)C=!0;else if(32===y)if(a)if(_)C=!0;else{var b=v+1=r)&&(l[c++]=new d(v,"vs-whitespace"),m%=r):(v===f||C&&v>i)&&(l[c++]=new d(v,p),m%=r),9===y?m=r:m++,_=C,v===f&&(p=n[++h].type,f=n[h].endIndex)}return l[c++]=_?new d(t,"vs-whitespace"):new d(t,p),l}function l(e,t,n,o){o.sort(i.LineDecoration.compare);for(var r=i.LineDecorationsNormalizer.normalize(o),s=r.length,a=0,u=[],l=0,c=0,h=0,p=n.length;hc&&(c=v.startOffset,u[l++]=new d(c,m)),!(v.endOffset+1<=g)){c=g,u[l++]=new d(c,m+" "+v.className);break}c=v.endOffset+1,u[l++]=new d(c,m+" "+v.className),a++}g>c&&(c=g,u[l++]=new d(c,m))}return u}function c(e){for(var t=e.fontIsMonospace,n=e.containsForeignElements,i=e.lineContent,o=e.len,r=e.isOverflowing,s=e.parts,a=e.tabSize,u=e.containsRTL,l=e.spaceWidth,c=e.renderWhitespace,d=e.renderControlCharacters,h=new p(o+1,s.length),g=0,m=0,v=0,_="",y=0,C=s.length;y=0){for(var E=0,L="";g0&&(L+="→",E++,N--);N>0;)L+=" ",E++,N--;else L+="·",E++;v++}h.setPartLength(y,E),_+=t||n?''+L+"":''+L+""}else{for(var E=0,L="";g0;)L+=" ",E++,N--;break;case 32:L+=" ",E++;break;case 60:L+="<",E++;break;case 62:L+=">",E++;break;case 38:L+="&",E++;break;case 0:L+="�",E++;break;case 65279:case 8232:L+="�",E++;break;case 13:L+="​",E++;break;default:d&&x<32?(L+=String.fromCharCode(9216+x),E++):(L+=String.fromCharCode(x),E++)}v++}h.setPartLength(y,E),_+=u?''+L+"":''+L+""}}return h.setPartData(o,s.length-1,v),r&&(_+=""),_+="",new f(h,_,u,n)}Object.defineProperty(t,"__esModule",{value:!0});!function(e){e[e.None=0]="None",e[e.Boundary=1]="Boundary",e[e.All=2]="All"}(t.RenderWhitespace||(t.RenderWhitespace={}));var d=function(){return function(e,t){this.endIndex=e,this.type=t}}(),h=function(){function e(e,t,n,i,o,r,s,a,u,l,c,d){this.useMonospaceOptimizations=e,this.lineContent=t,this.mightContainRTL=n,this.fauxIndentLength=i,this.lineTokens=o,this.lineDecorations=r,this.tabSize=s,this.spaceWidth=a,this.stopRenderingLineAfter=u,this.renderWhitespace="all"===l?2:"boundary"===l?1:0,this.renderControlCharacters=c,this.fontLigatures=d}return e.prototype.equals=function(e){return this.useMonospaceOptimizations===e.useMonospaceOptimizations&&this.lineContent===e.lineContent&&this.mightContainRTL===e.mightContainRTL&&this.fauxIndentLength===e.fauxIndentLength&&this.tabSize===e.tabSize&&this.spaceWidth===e.spaceWidth&&this.stopRenderingLineAfter===e.stopRenderingLineAfter&&this.renderWhitespace===e.renderWhitespace&&this.renderControlCharacters===e.renderControlCharacters&&this.fontLigatures===e.fontLigatures&&i.LineDecoration.equalsArr(this.lineDecorations,e.lineDecorations)&&n.ViewLineToken.equalsArr(this.lineTokens,e.lineTokens)},e}();t.RenderLineInput=h;!function(e){e[e.PART_INDEX_MASK=4294901760]="PART_INDEX_MASK",e[e.CHAR_INDEX_MASK=65535]="CHAR_INDEX_MASK",e[e.CHAR_INDEX_OFFSET=0]="CHAR_INDEX_OFFSET",e[e.PART_INDEX_OFFSET=16]="PART_INDEX_OFFSET"}(t.CharacterMappingConstants||(t.CharacterMappingConstants={}));var p=function(){function e(e,t){this.length=e,this._data=new Uint32Array(this.length),this._partLengths=new Uint16Array(t)}return e.getPartIndex=function(e){return(4294901760&e)>>>16},e.getCharIndex=function(e){return(65535&e)>>>0},e.prototype.setPartData=function(e,t,n){var i=(t<<16|n<<0)>>>0;this._data[e]=i},e.prototype.setPartLength=function(e,t){this._partLengths[e]=t},e.prototype.getPartLengths=function(){return this._partLengths},e.prototype.charOffsetToPartData=function(e){return 0===this.length?0:e<0?this._data[0]:e>=this.length?this._data[this.length-1]:this._data[e]},e.prototype.partDataToCharOffset=function(t,n,i){if(0===this.length)return 0;for(var o=(t<<16|i<<0)>>>0,r=0,s=this.length-1;r+1>>1,u=this._data[a];if(u===o)return a;u>o?s=a:r=a}if(r===s)return r;var l=this._data[r],c=this._data[s];if(l===o)return r;if(c===o)return s;var d=e.getPartIndex(l);return i-e.getCharIndex(l)<=(d!==e.getPartIndex(c)?n:e.getCharIndex(c))-i?r:s},e}();t.CharacterMapping=p;var f=function(){return function(e,t,n,i){this.characterMapping=e,this.html=t,this.containsRTL=n,this.containsForeignElements=i}}();t.RenderLineOutput=f,t.renderViewLine=function(e){if(0===e.lineContent.length){var t=!1,n=" ";if(e.lineDecorations.length>0){for(var i=[],o=0,s=e.lineDecorations.length;o ')}return new f(new p(0,0),n,!1,t)}return c(r(e))};var g,m=function(){return function(e,t,n,i,o,r,s,a,u,l,c){this.fontIsMonospace=e,this.lineContent=t,this.len=n,this.isOverflowing=i,this.parts=o,this.containsForeignElements=r,this.tabSize=s,this.containsRTL=a,this.spaceWidth=u,this.renderWhitespace=l,this.renderControlCharacters=c}}();!function(e){e[e.LongToken=50]="LongToken"}(g||(g={}))}),define(d[221],h([1,0,2]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t,i,o){this.selections=e,this.startLineNumber=0|t.startLineNumber,this.endLineNumber=0|t.endLineNumber,this.relativeVerticalOffset=t.relativeVerticalOffset,this.bigNumbersDelta=0|t.bigNumbersDelta,this.whitespaceViewportData=i,this._model=o,this.visibleRange=new n.Range(t.startLineNumber,this._model.getLineMinColumn(t.startLineNumber),t.endLineNumber,this._model.getLineMaxColumn(t.endLineNumber))}return e.prototype.getViewLineRenderingData=function(e){return this._model.getViewLineRenderingData(this.visibleRange,e)},e.prototype.getDecorationsInViewport=function(){return this._model.getDecorationsInViewport(this.visibleRange)},e}();t.ViewportData=i}),define(d[222],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){this._heights=[],this._ids=[],this._afterLineNumbers=[],this._ordinals=[],this._prefixSum=[],this._prefixSumValidIndex=-1,this._whitespaceId2Index={},this._lastWhitespaceId=0}return e.findInsertionIndex=function(e,t,n,i){for(var o=0,r=e.length;o>>1;t===e[s]?i=t&&(this._whitespaceId2Index[u]=l+1)}this._whitespaceId2Index[e.toString()]=t,this._prefixSumValidIndex=Math.min(this._prefixSumValidIndex,t-1)},e.prototype.changeWhitespace=function(e,t,n){e|=0,t|=0,n|=0;var i=!1;return i=this.changeWhitespaceHeight(e,n)||i,i=this.changeWhitespaceAfterLineNumber(e,t)||i},e.prototype.changeWhitespaceHeight=function(e,t){t|=0;var n=(e|=0).toString();if(this._whitespaceId2Index.hasOwnProperty(n)){var i=this._whitespaceId2Index[n];if(this._heights[i]!==t)return this._heights[i]=t,this._prefixSumValidIndex=Math.min(this._prefixSumValidIndex,i-1),!0}return!1},e.prototype.changeWhitespaceAfterLineNumber=function(t,n){n|=0;var i=(t|=0).toString();if(this._whitespaceId2Index.hasOwnProperty(i)){var o=this._whitespaceId2Index[i];if(this._afterLineNumbers[o]!==n){var r=this._ordinals[o],s=this._heights[o];this.removeWhitespace(t);var a=e.findInsertionIndex(this._afterLineNumbers,n,this._ordinals,r);return this._insertWhitespaceAtIndex(t,a,n,r,s),!0}}return!1},e.prototype.removeWhitespace=function(e){var t=(e|=0).toString();if(this._whitespaceId2Index.hasOwnProperty(t)){var n=this._whitespaceId2Index[t];return delete this._whitespaceId2Index[t],this._removeWhitespaceAtIndex(n),!0}return!1},e.prototype._removeWhitespaceAtIndex=function(e){e|=0,this._heights.splice(e,1),this._ids.splice(e,1),this._afterLineNumbers.splice(e,1),this._ordinals.splice(e,1),this._prefixSum.splice(e,1),this._prefixSumValidIndex=Math.min(this._prefixSumValidIndex,e-1);for(var t=Object.keys(this._whitespaceId2Index),n=0,i=t.length;n=e&&(this._whitespaceId2Index[o]=r-1)}},e.prototype.onLinesDeleted=function(e,t){e|=0,t|=0;for(var n=0,i=this._afterLineNumbers.length;nt&&(this._afterLineNumbers[n]-=t-e+1)}},e.prototype.onLinesInserted=function(e,t){e|=0,t|=0;for(var n=0,i=this._afterLineNumbers.length;n=t.length||t[o+1]>=e)return o;n=o+1|0}else i=o-1|0}return-1},e.prototype._findFirstWhitespaceAfterLineNumber=function(e){e|=0;var t=this._findLastWhitespaceBeforeLineNumber(e)+1;return t1?this._lineHeight*(e-1):0)+this._whitespaces.getAccumulatedHeightBeforeLineNumber(e)},e.prototype.getWhitespaceAccumulatedHeightBeforeLineNumber=function(e){return this._whitespaces.getAccumulatedHeightBeforeLineNumber(e)},e.prototype.hasWhitespace=function(){return this._whitespaces.getCount()>0},e.prototype.isAfterLines=function(e){return e>this.getLinesTotalHeight()},e.prototype.getLineNumberAtOrAfterVerticalOffset=function(e){if((e|=0)<0)return 1;for(var t=0|this._lineCount,n=this._lineHeight,i=1,o=t;i=s+n)i=r+1;else{if(e>=s)return r;o=r}}return i>t?t:i},e.prototype.getLinesViewportData=function(e,t){e|=0,t|=0;var n,i,o=this._lineHeight,r=0|this.getLineNumberAtOrAfterVerticalOffset(e),s=0|this.getVerticalOffsetForLineNumber(r),a=0|this._lineCount,u=0|this._whitespaces.getFirstWhitespaceIndexAfterLineNumber(r),l=0|this._whitespaces.getCount();-1===u?(u=l,i=a+1,n=0):(i=0|this._whitespaces.getAfterLineNumberForWhitespaceIndex(u),n=0|this._whitespaces.getHeightForWhitespaceIndex(u));var c=s,d=c,h=0;s>=5e5&&(h=5e5*Math.floor(s/5e5),d-=h=Math.floor(h/o)*o);for(var p=[],f=e+(t-e)/2,g=-1,m=r;m<=a;m++){if(-1===g){var v=c,_=c+o;(v<=f&&f<_||v>f)&&(g=m)}for(c+=o,p[m-r]=d,d+=o;i===m;)d+=n,c+=n,++u>=l?i=a+1:(i=0|this._whitespaces.getAfterLineNumberForWhitespaceIndex(u),n=0|this._whitespaces.getHeightForWhitespaceIndex(u));if(c>=t){a=m;break}}-1===g&&(g=a);var y=0|this.getVerticalOffsetForLineNumber(a),C=r,b=a;return Ct&&b--,{bigNumbersDelta:h,startLineNumber:r,endLineNumber:a,relativeVerticalOffset:p,centeredLineNumber:g,completelyVisibleStartLineNumber:C,completelyVisibleEndLineNumber:b}},e.prototype.getVerticalOffsetForWhitespaceIndex=function(e){e|=0;var t,n=this._whitespaces.getAfterLineNumberForWhitespaceIndex(e);t=n>=1?this._lineHeight*n:0;var i;return i=e>0?this._whitespaces.getAccumulatedHeight(e-1):0,t+i},e.prototype.getWhitespaceIndexAtOrAfterVerticallOffset=function(e){e|=0;var t,n,i,o=0,r=this._whitespaces.getCount()-1;if(r<0)return-1;if(e>=this.getVerticalOffsetForWhitespaceIndex(r)+this._whitespaces.getHeightForWhitespaceIndex(r))return-1;for(;o=n+i)o=t+1;else{if(e>=n)return t;r=t}return o},e.prototype.getWhitespaceAtVerticalOffset=function(e){e|=0;var t=this.getWhitespaceIndexAtOrAfterVerticallOffset(e);if(t<0)return null;if(t>=this._whitespaces.getCount())return null;var n=this.getVerticalOffsetForWhitespaceIndex(t);if(n>e)return null;var i=this._whitespaces.getHeightForWhitespaceIndex(t);return{id:this._whitespaces.getIdForWhitespaceIndex(t),afterLineNumber:this._whitespaces.getAfterLineNumberForWhitespaceIndex(t),verticalOffset:n,height:i}},e.prototype.getWhitespaceViewportData=function(e,t){e|=0,t|=0;var n=this.getWhitespaceIndexAtOrAfterVerticallOffset(e),i=this._whitespaces.getCount()-1;if(n<0)return[];for(var o=[],r=n;r<=i;r++){var s=this.getVerticalOffsetForWhitespaceIndex(r),a=this._whitespaces.getHeightForWhitespaceIndex(r);if(s>=t)break;o.push({id:this._whitespaces.getIdForWhitespaceIndex(r),afterLineNumber:this._whitespaces.getAfterLineNumberForWhitespaceIndex(r),verticalOffset:s,height:a})}return o},e.prototype.getWhitespaces=function(){return this._whitespaces.getWhitespaces(this._lineHeight)},e}();t.LinesLayout=i}),define(d[104],h([1,0,96]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){return function(e,t){this.index=e,this.remainder=t}}();t.PrefixSumIndexOfResult=i;var o=function(){function e(e){this.values=e,this.prefixSum=new Uint32Array(e.length),this.prefixSumValidIndex=new Int32Array(1),this.prefixSumValidIndex[0]=-1}return e.prototype.getCount=function(){return this.values.length},e.prototype.insertValues=function(e,t){e=n.toUint32(e);var i=this.values,o=this.prefixSum,r=t.length;return 0!==r&&(this.values=new Uint32Array(i.length+r),this.values.set(i.subarray(0,e),0),this.values.set(i.subarray(e),e+r),this.values.set(t,e),e-1=0&&this.prefixSum.set(o.subarray(0,this.prefixSumValidIndex[0]+1)),!0)},e.prototype.changeValue=function(e,t){return e=n.toUint32(e),t=n.toUint32(t),this.values[e]!==t&&(this.values[e]=t,e-1=i.length)return!1;var r=i.length-e;return t>=r&&(t=r),0!==t&&(this.values=new Uint32Array(i.length-t),this.values.set(i.subarray(0,e),0),this.values.set(i.subarray(e+t),e),this.prefixSum=new Uint32Array(this.values.length),e-1=0&&this.prefixSum.set(o.subarray(0,this.prefixSumValidIndex[0]+1)),!0)},e.prototype.getTotalValue=function(){return 0===this.values.length?0:this._getAccumulatedValue(this.values.length-1)},e.prototype.getAccumulatedValue=function(e){return e<0?0:(e=n.toUint32(e),this._getAccumulatedValue(e))},e.prototype._getAccumulatedValue=function(e){if(e<=this.prefixSumValidIndex[0])return this.prefixSum[e];var t=this.prefixSumValidIndex[0]+1;0===t&&(this.prefixSum[0]=this.values[0],t++),e>=this.values.length&&(e=this.values.length-1);for(var n=t;n<=e;n++)this.prefixSum[n]=this.prefixSum[n-1]+this.values[n];return this.prefixSumValidIndex[0]=Math.max(this.prefixSumValidIndex[0],e),this.prefixSum[e]},e.prototype.getIndexOf=function(e){e=Math.floor(e),this.getTotalValue();for(var t,n,o,r=0,s=this.values.length-1;r<=s;)if(t=r+(s-r)/2|0,n=this.prefixSum[t],o=n-this.values[t],e=n))break;r=t+1}return new i(t,e-o)},e}();t.PrefixSumComputer=o;var r=function(){function e(e){this._cacheAccumulatedValueStart=0,this._cache=null,this._actual=new o(e),this._bustCache()}return e.prototype._bustCache=function(){this._cacheAccumulatedValueStart=0,this._cache=null},e.prototype.getCount=function(){return this._actual.getCount()},e.prototype.insertValues=function(e,t){this._actual.insertValues(e,t)&&this._bustCache()},e.prototype.changeValue=function(e,t){this._actual.changeValue(e,t)&&this._bustCache()},e.prototype.removeValues=function(e,t){this._actual.removeValues(e,t)&&this._bustCache()},e.prototype.getTotalValue=function(){return this._actual.getTotalValue()},e.prototype.getAccumulatedValue=function(e){return this._actual.getAccumulatedValue(e)},e.prototype.getIndexOf=function(e){if(e=Math.floor(e),null!==this._cache){var t=e-this._cacheAccumulatedValueStart;if(t>=0&&tthis._lines.length)t=this._lines.length,n=this._lines[t-1].length+1,i=!0;else{var o=this._lines[t-1].length+1;n<1?(n=1,i=!0):n>o&&(n=o,i=!0)}return i?{lineNumber:t,column:n}:e},t}(u.MirrorModel),g=function(){function e(){this._foreignModule=null}return e.prototype.computeDiff=function(e,t,n){var o=this._getModel(e),s=this._getModel(t);if(!o||!s)return null;var a=o.getLinesContent(),u=s.getLinesContent(),l=new r.DiffComputer(a,u,{shouldPostProcessCharChanges:!0,shouldIgnoreTrimWhitespace:n,shouldConsiderTrimWhitespaceInEmptyCase:!0,shouldMakePrettyDiff:!0});return i.TPromise.as(l.computeDiff())},e.prototype.computeDirtyDiff=function(e,t,n){var o=this._getModel(e),s=this._getModel(t);if(!o||!s)return null;var a=o.getLinesContent(),u=s.getLinesContent(),l=new r.DiffComputer(a,u,{shouldPostProcessCharChanges:!1,shouldIgnoreTrimWhitespace:n,shouldConsiderTrimWhitespaceInEmptyCase:!1,shouldMakePrettyDiff:!0});return i.TPromise.as(l.computeDiff())},e.prototype.computeMoreMinimalEdits=function(t,n,r){var a=this._getModel(t);if(!a)return i.TPromise.as(n);for(var u,l=[],c=0,d=n;ce._diffLimit)l.push({range:p,text:f});else for(var v=s.stringDiff(m,f,!1),_=a.offsetAt(o.Range.lift(p).getStartPosition()),y=0,C=v;y=n,l=s,c=i.viewportHeight-s>=n,d=e.left;return d+t>i.scrollLeft+i.viewportWidth&&(d=i.scrollLeft+i.viewportWidth-t),dthis._contentWidth)return null;var s=e.top-i,a=e.top+this._lineHeight,u=r+this._contentLeft,l=n.getDomNodePagePosition(this._viewDomNode.domNode),c=l.top+s-n.StandardWindow.scrollY,d=l.top+a-n.StandardWindow.scrollY,h=l.left+u-n.StandardWindow.scrollX,p=window.innerWidth||document.documentElement.clientWidth||document.body.clientWidth,f=c>=22,g=d+i<=(window.innerHeight||document.documentElement.clientHeight||document.body.clientHeight)-22;if(h+t+20>p&&(h-=m=h-(p-t-20),u-=m),h<0){var m=h;h-=m,u-=m}return this._fixedOverflowWidgets&&(s=c,a=d,u=h),{aboveTop:s,fitsAbove:f,belowTop:a,fitsBelow:g,left:u}},e.prototype._prepareRenderWidgetAtExactPositionOverflowing=function(e){return new s(e.top,e.left+this._contentLeft)},e.prototype._getTopLeft=function(e,t){var n=e.visibleRangeForPosition(t);if(!n)return null;var i=e.getVerticalOffsetForLineNumber(t.lineNumber)-e.scrollTop;return new s(i,n.left)},e.prototype._prepareRenderWidget=function(e){var t=this;if(!this._position||!this._preference)return null;var n=this._context.model.validateModelPosition(this._position);if(!this._context.model.coordinatesConverter.modelPositionIsVisible(n))return null;for(var i=this._context.model.coordinatesConverter.convertModelPositionToViewPosition(n),r=null,a=function(){if(!r){var n=t._getTopLeft(e,i);if(n){var o=t.domNode.domNode,s=o.clientWidth,a=o.clientHeight;r=t.allowEditorOverflow?t._layoutBoxInPage(n,s,a,e):t._layoutBoxInViewport(n,s,a,e)}}},u=1;u<=2;u++)for(var l=0;lo?1:i.Range.compareRangesUsingStarts(e.range,t.range)});for(var u=e.visibleRange.startLineNumber,l=e.visibleRange.endLineNumber,c=[],d=u;d<=l;d++)c[d-u]="";this._renderWholeLineDecorations(e,n,c),this._renderNormalDecorations(e,n,c),this._renderResult=c},t.prototype._renderWholeLineDecorations=function(e,t,n){for(var i=String(this._lineHeight),o=e.visibleRange.startLineNumber,r=e.visibleRange.endLineNumber,s=0,a=t.length;s',c=Math.max(u.range.startLineNumber,o),d=Math.min(u.range.endLineNumber,r),h=c;h<=d;h++)n[h-o]+=l}},t.prototype._renderNormalDecorations=function(e,t,n){for(var r=String(this._lineHeight),s=e.visibleRange.startLineNumber,a=0,u=t.length;a';n[v]+=w}}}}},t.prototype.render=function(e,t){if(!this._renderResult)return"";var n=t-e;if(n<0||n>=this._renderResult.length)throw new Error("Unexpected render request");return this._renderResult[n]},t}(n.DynamicViewOverlay);t.DecorationsOverlay=r}),define(d[113],h([1,0,67,279]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){return function(e,t,n){this.startLineNumber=+e,this.endLineNumber=+t,this.className=String(n)}}();t.DecorationToRender=i;var o=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._render=function(e,t,n){for(var i=[],o=e;o<=t;o++)i[o-e]=[];if(0===n.length)return i;n.sort(function(e,t){return e.className===t.className?e.startLineNumber===t.startLineNumber?e.endLineNumber-t.endLineNumber:e.startLineNumber-t.startLineNumber:e.className',s=[],a=t;a<=n;a++){var u=a-t,l=i[u];0===l.length?s[u]="":s[u]='
    =this._renderResult.length)throw new Error("Unexpected render request");return this._renderResult[n]},t}(o);t.GlyphMarginOverlay=r}),define(d[233],h([1,0,113,288]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(e){function t(t){var n=e.call(this)||this;return n._context=t,n._decorationsLeft=n._context.configuration.editor.layoutInfo.decorationsLeft,n._decorationsWidth=n._context.configuration.editor.layoutInfo.decorationsWidth,n._renderResult=null,n._context.addEventHandler(n),n}return f(t,e),t.prototype.dispose=function(){this._context.removeEventHandler(this),this._context=null,this._renderResult=null,e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return e.layoutInfo&&(this._decorationsLeft=this._context.configuration.editor.layoutInfo.decorationsLeft,this._decorationsWidth=this._context.configuration.editor.layoutInfo.decorationsWidth),!0},t.prototype.onDecorationsChanged=function(e){return!0},t.prototype.onFlushed=function(e){return!0},t.prototype.onLinesChanged=function(e){return!0},t.prototype.onLinesDeleted=function(e){return!0},t.prototype.onLinesInserted=function(e){return!0},t.prototype.onScrollChanged=function(e){return e.scrollTopChanged},t.prototype.onZonesChanged=function(e){return!0},t.prototype._getDecorations=function(e){for(var t=e.getDecorationsInViewport(),i=[],o=0,r=t.length;o
    ',r=[],s=t;s<=n;s++){for(var a=s-t,u=i[a],l="",c=0,d=u.length;c';o[s]=u}this._renderResult=o},t.prototype.render=function(e,t){return this._renderResult?this._renderResult[t-e]:""},t}(n.DedupOverlay);t.MarginViewLineDecorationsOverlay=i}),define(d[236],h([1,0,27,25,35,296]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){function t(t){var i=e.call(this,t)||this;return i._widgets={},i._verticalScrollbarWidth=i._context.configuration.editor.layoutInfo.verticalScrollbarWidth,i._minimapWidth=i._context.configuration.editor.layoutInfo.minimapWidth,i._horizontalScrollbarHeight=i._context.configuration.editor.layoutInfo.horizontalScrollbarHeight,i._editorHeight=i._context.configuration.editor.layoutInfo.height,i._editorWidth=i._context.configuration.editor.layoutInfo.width,i._domNode=n.createFastDomNode(document.createElement("div")),o.PartFingerprints.write(i._domNode,4),i._domNode.setClassName("overlayWidgets"),i}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this),this._widgets=null},t.prototype.getDomNode=function(){return this._domNode},t.prototype.onConfigurationChanged=function(e){return!!e.layoutInfo&&(this._verticalScrollbarWidth=this._context.configuration.editor.layoutInfo.verticalScrollbarWidth,this._minimapWidth=this._context.configuration.editor.layoutInfo.minimapWidth,this._horizontalScrollbarHeight=this._context.configuration.editor.layoutInfo.horizontalScrollbarHeight,this._editorHeight=this._context.configuration.editor.layoutInfo.height,this._editorWidth=this._context.configuration.editor.layoutInfo.width,!0)},t.prototype.addWidget=function(e){var t=n.createFastDomNode(e.getDomNode());this._widgets[e.getId()]={widget:e,preference:null,domNode:t},t.setPosition("absolute"),t.setAttribute("widgetId",e.getId()),this._domNode.appendChild(t),this.setShouldRender()},t.prototype.setWidgetPosition=function(e,t){var n=this._widgets[e.getId()];return n.preference!==t&&(n.preference=t,this.setShouldRender(),!0)},t.prototype.removeWidget=function(e){var t=e.getId();if(this._widgets.hasOwnProperty(t)){var n=this._widgets[t].domNode.domNode;delete this._widgets[t],n.parentNode.removeChild(n),this.setShouldRender()}},t.prototype._renderWidget=function(e){var t=e.domNode;if(null!==e.preference)if(e.preference===i.OverlayWidgetPositionPreference.TOP_RIGHT_CORNER)t.setTop(0),t.setRight(2*this._verticalScrollbarWidth+this._minimapWidth);else if(e.preference===i.OverlayWidgetPositionPreference.BOTTOM_RIGHT_CORNER){var n=t.domNode.clientHeight;t.setTop(this._editorHeight-n-2*this._horizontalScrollbarHeight),t.setRight(2*this._verticalScrollbarWidth+this._minimapWidth)}else e.preference===i.OverlayWidgetPositionPreference.TOP_CENTER&&(t.setTop(0),t.domNode.style.right="50%");else t.unsetTop()},t.prototype.prepareRender=function(e){},t.prototype.render=function(e){this._domNode.setWidth(this._editorWidth);for(var t=Object.keys(this._widgets),n=0,i=t.length;n=e.scrollWidth?0:this._configuration.editor.viewInfo.scrollbar.horizontalScrollbarSize},t.prototype._getTotalHeight=function(){var e=this.scrollable.getState(),t=this._linesLayout.getLinesTotalHeight();return this._configuration.editor.viewInfo.scrollBeyondLastLine?t+=e.height-this._configuration.editor.lineHeight:t+=this._getHorizontalScrollbarHeight(e),Math.max(e.height,t)},t.prototype._updateHeight=function(){this.scrollable.updateState({scrollHeight:this._getTotalHeight()})},t.prototype.getCurrentViewport=function(){var e=this.scrollable.getState();return new r.Viewport(e.scrollTop,e.scrollLeft,e.width,e.height)},t.prototype._computeScrollWidth=function(e,n){return this._configuration.editor.wrappingInfo.isViewportWrapping?Math.max(e,n):Math.max(e+t.LINES_HORIZONTAL_EXTRA_PX,n)},t.prototype.onMaxLineWidthChanged=function(e){var t=this._computeScrollWidth(e,this.getCurrentViewport().width);this.scrollable.updateState({scrollWidth:t}),this._updateHeight()},t.prototype.saveState=function(){var e=this.scrollable.getState(),t=e.scrollTop,n=this._linesLayout.getLineNumberAtOrAfterVerticalOffset(t);return{scrollTop:t,scrollTopWithoutViewZones:t-this._linesLayout.getWhitespaceAccumulatedHeightBeforeLineNumber(n),scrollLeft:e.scrollLeft}},t.prototype.restoreState=function(e){var t=e.scrollTop;"number"!=typeof e.scrollTopWithoutViewZones||this._linesLayout.hasWhitespace()||(t=e.scrollTopWithoutViewZones),this.scrollable.updateState({scrollLeft:e.scrollLeft,scrollTop:t})},t.prototype.addWhitespace=function(e,t,n){return this._linesLayout.insertWhitespace(e,t,n)},t.prototype.changeWhitespace=function(e,t,n){return this._linesLayout.changeWhitespace(e,t,n)},t.prototype.removeWhitespace=function(e){return this._linesLayout.removeWhitespace(e)},t.prototype.getVerticalOffsetForLineNumber=function(e){return this._linesLayout.getVerticalOffsetForLineNumber(e)},t.prototype.isAfterLines=function(e){return this._linesLayout.isAfterLines(e)},t.prototype.getLineNumberAtVerticalOffset=function(e){return this._linesLayout.getLineNumberAtOrAfterVerticalOffset(e)},t.prototype.getWhitespaceAtVerticalOffset=function(e){return this._linesLayout.getWhitespaceAtVerticalOffset(e)},t.prototype.getLinesViewportData=function(){var e=this.getCurrentViewport();return this._linesLayout.getLinesViewportData(e.top,e.top+e.height)},t.prototype.getLinesViewportDataAtScrollTop=function(e){var t=this.scrollable.getState();return e+t.height>t.scrollHeight&&(e=t.scrollHeight-t.height),e<0&&(e=0),this._linesLayout.getLinesViewportData(e,e+t.height)},t.prototype.getWhitespaceViewportData=function(){var e=this.getCurrentViewport();return this._linesLayout.getWhitespaceViewportData(e.top,e.top+e.height)},t.prototype.getWhitespaces=function(){return this._linesLayout.getWhitespaces()},t.prototype.getScrollWidth=function(){return this.scrollable.getState().scrollWidth},t.prototype.getScrollLeft=function(){return this.scrollable.getState().scrollLeft},t.prototype.getScrollHeight=function(){return this.scrollable.getState().scrollHeight},t.prototype.getScrollTop=function(){return this.scrollable.getState().scrollTop},t.prototype.setScrollPosition=function(e){this.scrollable.updateState(e)},t.LINES_HORIZONTAL_EXTRA_PX=30,t}(n.Disposable);t.ViewLayout=s}),define(d[240],h([1,0,2,12,86]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t,n,i){this.editorId=e,this.model=t,this.configuration=n,this._coordinatesConverter=i,this._decorationsCache=Object.create(null),this._clearCachedModelDecorationsResolver()}return e.prototype._clearCachedModelDecorationsResolver=function(){this._cachedModelDecorationsResolver=null,this._cachedModelDecorationsResolverViewRange=null},e.prototype.dispose=function(){this._decorationsCache=null,this._clearCachedModelDecorationsResolver()},e.prototype.reset=function(){this._decorationsCache=Object.create(null),this._clearCachedModelDecorationsResolver()},e.prototype.onModelDecorationsChanged=function(e){for(var t=e.changedDecorations,n=0,i=t.length;n1){v=new o.InlineDecoration(new n.Range(m.endLineNumber,m.endColumn-1,m.endLineNumber,m.endColumn),f.afterContentClassName,!0);l[m.endLineNumber-i].push(v)}}return{decorations:a,inlineDecorations:l}},e}();t.ViewModelDecorations=r}),define(d[241],h([1,0,2]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){this._selection=e,this._isMovingLeft=t}return e.prototype.getEditOperations=function(e,t){var i=this._selection;if(this._selectionId=t.trackSelection(i),i.startLineNumber===i.endLineNumber&&(!this._isMovingLeft||0!==i.startColumn)&&(this._isMovingLeft||i.endColumn!==e.getLineMaxColumn(i.startLineNumber))){var o,r,s,a=i.selectionStartLineNumber,u=e.getLineContent(a);this._isMovingLeft?(o=u.substring(0,i.startColumn-2),r=u.substring(i.startColumn-1,i.endColumn-1),s=u.substring(i.startColumn-2,i.startColumn-1)+u.substring(i.endColumn-1)):(o=u.substring(0,i.startColumn-1)+u.substring(i.endColumn-1,i.endColumn),r=u.substring(i.startColumn-1,i.endColumn-1),s=u.substring(i.endColumn));var l=o+r+s;t.addEditOperation(new n.Range(a,1,a,e.getLineMaxColumn(a)),null),t.addEditOperation(new n.Range(a,1,a,1),l),this._cutStartIndex=i.startColumn+(this._isMovingLeft?-1:1),this._cutEndIndex=this._cutStartIndex+i.endColumn-i.startColumn,this._moved=!0}},e.prototype.computeCursorState=function(e,t){var n=t.getTrackedSelection(this._selectionId);return this._moved&&(n=(n=n.setStartPosition(n.startLineNumber,this._cutStartIndex)).setEndPosition(n.startLineNumber,this._cutEndIndex)),n},e}();t.MoveCaretCommand=i}),define(d[242],h([1,0,32,2]),function(e,t,n,i){"use strict";function o(e){return!!e.transparentFormatter}Object.defineProperty(t,"__esModule",{value:!0}),t.isAdvancedFormatter=o;var r=function(){function e(e,t,n,o,r,s,a){this.editorModel=s,this.colorFormatters=[],this._colorModelIndex=0,this.originalColor=e,this._opaqueFormatter=n,this.colorFormatters=r,this.color=t,this.hue=t.getHue(),this.saturation=t.getSaturation(),this.value=t.getValue(),this._colorRange=new i.Range(a.startLineNumber,a.startColumn,a.endLineNumber,a.endColumn)}return Object.defineProperty(e.prototype,"color",{get:function(){return this._color},set:function(e){this._color=e;var t=e.toRGBA().a;this._opacity||(this._opacity=t/255),this.saturation=e.getSaturation(),this.value=e.getValue(),1===this._opacity?this.selectedColorString=this._opaqueFormatter.toString(this._color):this._transparentFormatter?this.selectedColorString=this._transparentFormatter.toString(this._color):this.nextColorMode()},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"selectedColorString",{get:function(){return this._selectedColor},set:function(e){this._selectedColor!==e&&(this._selectedColor=e,this.widget&&this.widget.header&&this.widget.body&&(this.widget.header.updatePickedColor(),this.widget.body.fillOpacityOverlay(this._color),this.editorModel.pushEditOperations([],[{identifier:null,range:this._colorRange,text:e,forceMoveMarkers:!1}],function(){return[]}),this._colorRange=this._colorRange.setEndPosition(this._colorRange.endLineNumber,this._colorRange.startColumn+e.length)))},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"hue",{get:function(){return this._hue},set:function(e){this._hue=e,this.widget&&this.widget.body&&this.widget.body.saturationBox.fillSaturationBox()},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"opacity",{get:function(){return this._opacity},set:function(e){this._opacity=e;var t=this._color.toRGBA();this.color=n.Color.fromRGBA(new n.RGBA(t.r,t.g,t.b,255*e)),this.widget.header&&this.widget.header.updatePickedColor()},enumerable:!0,configurable:!0}),e.prototype.nextColorMode=function(){this._colorModelIndex++,this._colorModelIndex===this.colorFormatters.length&&(this._colorModelIndex=0);var e=this.colorFormatters[this._colorModelIndex];o(e)?(this._transparentFormatter=e.transparentFormatter,this._opaqueFormatter=e.opaqueFormatter,this.selectedColorString=1===this._opacity?this._opaqueFormatter.toString(this._color):this._transparentFormatter.toString(this._color)):this._transparentFormatter&&1!==this._opacity?this.nextColorMode():(this._transparentFormatter=null,this._opaqueFormatter=e,this.selectedColorString=this._opaqueFormatter.toString(this._color))},e.prototype.getHueColor=function(e){var t=e/60,i=1-Math.abs(t%2-1),o=0,r=0,s=0;return t>=0&&t<1?(o=1,r=i):t>=1&&t<2?(o=i,r=1):t>=2&&t<3?(r=1,s=i):t>=3&&t<4?(r=i,s=1):t>=4&&t<5?(o=i,s=1):(o=1,s=i),o=Math.round(255*o),r=Math.round(255*r),s=Math.round(255*s),n.Color.fromRGBA(new n.RGBA(o,r,s))},e}();t.ColorPickerModel=r;var s=function(){return function(){}}();t.ISaturationState=s}),define(d[243],h([1,0,4,3,83,15,32]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=n.$,u=function(e){function t(t,i){var o=e.call(this)||this;return o.widget=t,o.model=i,o.pixelRatio=o.widget.editor.getConfiguration().pixelRatio,o.domNode=a(".colorpicker-body"),n.append(t.getDomNode(),o.domNode),o.drawSaturationBox(),o.drawOpacityStrip(),o.drawHueStrip(),o.registerListeners(),o}return f(t,e),t.prototype.fillOpacityOverlay=function(e){var t=e.toRGBA(),n=t.r,i=t.g,o=t.b;this.opacityOverlay.style.background="linear-gradient(to bottom, rgba("+n+", "+i+", "+o+", 1) 0%, rgba("+n+", "+i+", "+o+", 0) 100%)"},t.prototype.registerListeners=function(){var e=this,t=this._register(new o.GlobalMouseMoveMonitor);this._register(n.addDisposableListener(this.saturationBox.domNode,n.EventType.MOUSE_DOWN,function(n){e.saturationListener(n,t)})),this._register(n.addDisposableListener(this.hueStrip,n.EventType.MOUSE_DOWN,function(n){e.stripListener(e.hueStrip,n,t)})),this._register(n.addDisposableListener(this.opacityStrip,n.EventType.MOUSE_DOWN,function(n){e.stripListener(e.opacityStrip,n,t)}))},t.prototype.saturationListener=function(e,t){var n=this;if(0===e.button){var i,r,a=function(e,t){var i=n.saturationBox.extractColor(e,t).toRGBA();n.widget.model.color=s.Color.fromRGBA(new s.RGBA(i.r,i.g,i.b,255*n.widget.model.opacity)),n.saturationBox.focusSaturationSelection({x:e,y:t})};e.target!==this.saturationBox.saturationSelection?(i=e.offsetX,r=e.offsetY,a(i,r)):(i=this.widget.model.saturationSelection.x,r=this.widget.model.saturationSelection.y);var u=e.clientY,l=e.clientX;t.startMonitoring(o.standardMouseMoveMerger,function(e){var t=e.posx-l,n=e.posy-u;a(i+t,r+n)},function(){return null})}},t.prototype.stripListener=function(e,t,n){var i=this;if(0===t.button){var s=e===this.hueStrip?this.hueSlider:this.opacitySlider,a=e===this.hueStrip?this.hueStrip:this.opacityStrip;t.target!==this.hueStrip&&t.target!==this.opacityStrip||(s.top=t.offsetY);var u=function(){s===i.hueSlider?i.widget.model.hue=i.calculateSliderHue(s):s===i.opacitySlider&&(i.widget.model.opacity=i.calculateOpacity(s))};u();var l=t.clientY,c=t.clientX,d=s.top;n.startMonitoring(o.standardMouseMoveMerger,function(e){a.style.cursor="-webkit-grabbing";var t=Math.abs(e.posx-c);if(r.isWindows&&t>140)return s.top=0,void(s===i.hueSlider?i.widget.model.hue=0:s===i.opacitySlider&&(i.widget.model.opacity=1));var n=e.posy-l;s.top=d+n,u()},function(){a.style.cursor="-webkit-grab"})}},t.prototype.drawSaturationBox=function(){this.saturationBox=new l(this.model,this.domNode,this.pixelRatio)},t.prototype.drawOpacityStrip=function(){this.opacityStrip=a(".strip.opacity-strip"),n.append(this.domNode,this.opacityStrip),this.opacityOverlay=a(".opacity-overlay"),this.fillOpacityOverlay(this.model.color),n.append(this.opacityStrip,this.opacityOverlay),this.opacitySlider=new c(this.opacityStrip),this.opacitySlider.top=1===this.model.opacity?0:this.opacityStrip.offsetHeight*(1-this.model.opacity),n.append(this.opacityStrip,this.opacitySlider.domNode)},t.prototype.drawHueStrip=function(){this.hueStrip=a(".strip.hue-strip"),n.append(this.domNode,this.hueStrip),this.hueSlider=new c(this.hueStrip),n.append(this.hueStrip,this.hueSlider.domNode),this.hueSlider.top=(this.hueStrip.offsetHeight-this.hueSlider.domNode.offsetHeight)*(this.model.color.getHue()/359)},t.prototype.calculateSliderHue=function(e){var t=this.hueStrip.offsetHeight-e.domNode.offsetHeight;return 359*(1-(t-e.top)/t)},t.prototype.calculateOpacity=function(e){var t=this.opacityStrip.offsetHeight-e.domNode.offsetHeight;return(t-e.top)/t},t}(i.Disposable);t.ColorPickerBody=u;var l=function(){function e(e,t,i){this.model=e,this.pixelRatio=i,this.domNode=a(".saturation-wrap"),n.append(t,this.domNode),this.saturationCanvas=document.createElement("canvas"),this.saturationCanvas.className="saturation-box",n.append(this.domNode,this.saturationCanvas),this.saturationSelection=a(".saturation-selection"),n.append(this.domNode,this.saturationSelection)}return e.prototype.layout=function(){var e=this.domNode.offsetWidth*this.pixelRatio,t=this.domNode.offsetHeight*this.pixelRatio;this.saturationCanvas.width=e,this.saturationCanvas.height=t,this.saturationCtx=this.saturationCanvas.getContext("2d"),this.saturationCtx.rect(0,0,e,t);var n=document.createElement("canvas").getContext("2d");this.whiteGradient=n.createLinearGradient(0,0,e,0),this.whiteGradient.addColorStop(0,"rgba(255, 255, 255, 1)"),this.whiteGradient.addColorStop(1,"rgba(255, 255, 255, 0)"),this.blackGradient=n.createLinearGradient(0,0,0,t),this.blackGradient.addColorStop(0,"rgba(0, 0, 0, 0)"),this.blackGradient.addColorStop(1,"rgba(0, 0, 0, 1)"),this.fillSaturationBox();var i=this.model.saturation*this.saturationCanvas.clientWidth,o=this.model.value*this.saturationCanvas.clientHeight,r=0===o?this.saturationCanvas.clientHeight:this.saturationCanvas.clientHeight-o;this.focusSaturationSelection({x:i,y:r})},e.prototype.fillSaturationBox=function(){if(this.saturationCtx.fillStyle=this.calculateHueColor(this.model.hue).toString(),this.saturationCtx.fill(),this.saturationCtx.fillStyle=this.whiteGradient,this.saturationCtx.fill(),this.saturationCtx.fillStyle=this.blackGradient,this.saturationCtx.fill(),this.model.saturationSelection){var e=s.Color.fromHSV(this.model.hue,this.model.saturation,this.model.value,255*this.model.opacity);this.model.color=e}},e.prototype.focusSaturationSelection=function(e){var t=e.x,n=e.y;t<0?t=0:t>this.domNode.offsetWidth&&(t=this.domNode.offsetWidth),n<0?n=0:n>this.domNode.offsetHeight&&(n=this.domNode.offsetHeight),this.saturationSelection.style.left=t+"px",this.saturationSelection.style.top=n+"px",this.model.saturationSelection={x:t,y:n}},e.prototype.extractColor=function(e,t){var n=1-e/this.domNode.offsetWidth,i=t/this.domNode.offsetHeight,o=s.Color.fromRGBA(new s.RGBA(255,255,255,255*n));return s.Color.fromRGBA(new s.RGBA(0,0,0,255*i)).blend(o).blend(this.calculateHueColor(this.model.hue))},e.prototype.calculateHueColor=function(e){var t=e/60,n=1-Math.abs(t%2-1),i=0,o=0,r=0;return t>=0&&t<1?(i=1,o=n):t>=1&&t<2?(i=n,o=1):t>=2&&t<3?(o=1,r=n):t>=3&&t<4?(o=n,r=1):t>=4&&t<5?(i=n,r=1):(i=1,r=n),i=Math.round(255*i),o=Math.round(255*o),r=Math.round(255*r),s.Color.fromRGBA(new s.RGBA(i,o,r))},e}();t.SaturationBox=l;var c=function(){function e(e){this.strip=e,this.domNode=a(".slider"),this._top=0}return Object.defineProperty(e.prototype,"top",{get:function(){return this._top},set:function(e){e<0?e=0:e>this.strip.offsetHeight-this.domNode.offsetHeight&&(e=this.strip.offsetHeight-this.domNode.offsetHeight),this.domNode.style.top=e+"px",this._top=e},enumerable:!0,configurable:!0}),e}()}),define(d[244],h([1,0,4,3]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=n.$,r=function(e){function t(t,i){var r=e.call(this)||this;return r.widget=t,r.model=i,r.domNode=o(".colorpicker-header"),n.append(t.getDomNode(),r.domNode),r.drawPickedColorBox(),r.drawOriginalColorBox(),n.addDisposableListener(r.pickedColorNode,n.EventType.CLICK,function(){0!==r.model.colorFormatters.length&&r.model.nextColorMode()}),r}return f(t,e),t.prototype.updatePickedColor=function(){this.pickedColorNode.textContent=this.model.selectedColorString,this.pickedColorNode.style.backgroundColor=this.model.color.toString()},t.prototype.drawPickedColorBox=function(){this.pickedColorNode=o(".picked-color"),this.pickedColorNode.style.backgroundColor=this.model.color.toString(),this.pickedColorNode.textContent=this.model.selectedColorString,n.append(this.domNode,this.pickedColorNode)},t.prototype.drawOriginalColorBox=function(){var e=o(".original-color");e.style.backgroundColor=this.model.originalColor,n.append(this.domNode,e)},t}(i.Disposable);t.ColorPickerHeader=r}),define(d[245],h([1,0,41,4,28,244,243,316]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=i.$,u=function(e){function t(t,n){var i=e.call(this)||this;return i.model=t,i.editor=n,i.visible=!1,i._register(o.onDidChangeZoomLevel(function(){return i.layout()})),i.domNode=a(".editor-widget.colorpicker-widget"),i}return f(t,e),t.prototype.layout=function(){this.visible||(this.header=new r.ColorPickerHeader(this,this.model),this.body=new s.ColorPickerBody(this,this.model),this.visible=!0)},t.prototype.layoutSaturationBox=function(){this.body.saturationBox.layout()},t.prototype.dispose=function(){this.visible=!1,this.domNode=null,e.prototype.dispose.call(this)},t.prototype.getId=function(){return t.ID},t.prototype.getDomNode=function(){return this.domNode},t.ID="editor.contrib.colorPickerWidget",t}(n.Widget);t.ColorPickerWidget=u}),define(d[246],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.isColorDecorationOptions=function(e){return!(!e||!e.color)}}),define(d[247],h([1,0]),function(e,t){"use strict";function n(e){return function(){return e}}function i(e,t,n,i){var o=e;if(t||n){if(t>n)throw new Error("Color format range defined is not correct. Range start is bigger than end.");if(t===n)throw new Error("Color format range defined is not correct. Range start is the same as end.");o=o/i*(n-t)+t}return o}function o(e,t,n,o,u){return function(l){var c;switch(e){case"red":c=i(l.toRGBA().r,o,u,r);break;case"green":c=i(l.toRGBA().g,o,u,r);break;case"blue":c=i(l.toRGBA().b,o,u,r);break;case"alpha":c=i(l.toRGBA().a,o,u,r);break;case"hue":c=i(l.toHSLA().h,o,u,s);break;case"saturation":c=i(l.toHSLA().s,o,u,a);break;case"luminosity":c=i(l.toHSLA().l,o,u,a)}if(void 0===c)throw new Error(e+" is not supported as a color format.");var d;return"f"===n?(t=t||2,d=c.toFixed(t)):"x"===n||"X"===n?(2!==(d=i(c,o,u,r).toString(16)).length&&(d="0"+d),"X"===n&&(d=d.toUpperCase())):d=c.toFixed(0),d.toString()}}Object.defineProperty(t,"__esModule",{value:!0});var r=255,s=360,a=1,u=function(){function e(e){this.tree=[],this.parse(e)}return e.prototype.parse=function(t){for(var i=e.PATTERN.exec(t),r=0;null!==i;){var s=i.index;re.length)return!1;for(var o=0;od?u-1:u}},e}();t.LineCommentCommand=l}),define(d[251],h([1,0,22,2]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(e,t,n){this.selection=e,this.targetPosition=t,this.copy=n}return e.prototype.getEditOperations=function(e,t){var o=e.getValueInRange(this.selection);this.copy||t.addEditOperation(this.selection,null),t.addEditOperation(new i.Range(this.targetPosition.lineNumber,this.targetPosition.column,this.targetPosition.lineNumber,this.targetPosition.column),o),!this.selection.containsPosition(this.targetPosition)||this.copy&&(this.selection.getEndPosition().equals(this.targetPosition)||this.selection.getStartPosition().equals(this.targetPosition))?this.copy?this.targetSelection=new n.Selection(this.targetPosition.lineNumber,this.targetPosition.column,this.selection.endLineNumber-this.selection.startLineNumber+this.targetPosition.lineNumber,this.selection.startLineNumber===this.selection.endLineNumber?this.targetPosition.column+this.selection.endColumn-this.selection.startColumn:this.selection.endColumn):this.targetPosition.lineNumber>this.selection.endLineNumber?this.targetSelection=new n.Selection(this.targetPosition.lineNumber-this.selection.endLineNumber+this.selection.startLineNumber,this.targetPosition.column,this.targetPosition.lineNumber,this.selection.startLineNumber===this.selection.endLineNumber?this.targetPosition.column+this.selection.endColumn-this.selection.startColumn:this.selection.endColumn):this.targetPosition.lineNumbern&&(t=n),this._matchesPosition!==t&&(this._matchesPosition=t,r.matchesPosition=!0,s=!0),this._matchesCount!==n&&(this._matchesCount=n,r.matchesCount=!0,s=!0),void 0!==o&&(i.Range.equalsRange(this._currentMatch,o)||(this._currentMatch=o,r.currentMatch=!0,s=!0)),s&&this._eventEmitter.emit(e._CHANGED_EVENT,r)},e.prototype.change=function(t,n,o){void 0===o&&(o=!0);var r={moveCursor:n,updateHistory:o,searchString:!1,replaceString:!1,isRevealed:!1,isReplaceRevealed:!1,isRegex:!1,wholeWord:!1,matchCase:!1,searchScope:!1,matchesPosition:!1,matchesCount:!1,currentMatch:!1},s=!1;void 0!==t.searchString&&this._searchString!==t.searchString&&(this._searchString=t.searchString,r.searchString=!0,s=!0),void 0!==t.replaceString&&this._replaceString!==t.replaceString&&(this._replaceString=t.replaceString,r.replaceString=!0,s=!0),void 0!==t.isRevealed&&this._isRevealed!==t.isRevealed&&(this._isRevealed=t.isRevealed,r.isRevealed=!0,s=!0),void 0!==t.isReplaceRevealed&&this._isReplaceRevealed!==t.isReplaceRevealed&&(this._isReplaceRevealed=t.isReplaceRevealed,r.isReplaceRevealed=!0,s=!0),void 0!==t.isRegex&&this._isRegex!==t.isRegex&&(this._isRegex=t.isRegex,r.isRegex=!0,s=!0),void 0!==t.wholeWord&&this._wholeWord!==t.wholeWord&&(this._wholeWord=t.wholeWord,r.wholeWord=!0,s=!0),void 0!==t.matchCase&&this._matchCase!==t.matchCase&&(this._matchCase=t.matchCase,r.matchCase=!0,s=!0),void 0!==t.searchScope&&(i.Range.equalsRange(this._searchScope,t.searchScope)||(this._searchScope=t.searchScope,r.searchScope=!0,s=!0)),s&&this._eventEmitter.emit(e._CHANGED_EVENT,r)},e._CHANGED_EVENT="changed",e}();t.FindReplaceState=o}),define(d[254],h([1,0,2]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t,n){this._editorSelection=e,this._ranges=t,this._replaceStrings=n}return e.prototype.getEditOperations=function(e,t){if(this._ranges.length>0){for(var i=[],o=0;o0;){if(e=r)break;var a=e.charCodeAt(i);if(36===a){t.emitUnchanged(i-1),t.emitStatic("$",i+1);continue}if(48===a||38===a){t.emitUnchanged(i-1),t.emitMatchIndex(0,i+1);continue}if(49<=a&&a<=57){var u=a-48;if(i+1=r)break;switch(a=e.charCodeAt(i)){case 92:t.emitUnchanged(i-1),t.emitStatic("\\",i+1);break;case 110:t.emitUnchanged(i-1),t.emitStatic("\n",i+1);break;case 116:t.emitUnchanged(i-1),t.emitStatic("\t",i+1)}}}return t.finalize()}}),define(d[256],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ID="editor.contrib.folding"}),define(d[257],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.computeRanges=function(e){return e.getIndentRanges()},t.limitByIndent=function(e,t){if(e.length<=t)return e;var n=[];e.forEach(function(e){e.indent<1e3&&(n[e.indent]=(n[e.indent]||0)+1)});for(var i=n.length,o=0;o0){var u=r.modifyPosition(e.getStartPosition(),a);e=new i.Range(u.lineNumber,u.column,e.endLineNumber,e.endColumn),t=t.substring(a),s=s.substr(a)}var l=n.commonSuffixLength(t,s);if(l>0){var c=r.modifyPosition(e.getEndPosition(),-l);e=new i.Range(e.startLineNumber,e.startColumn,c.lineNumber,c.column),t=t.substring(0,t.length-l),s=s.substring(0,s.length-l)}return{text:t,range:e,forceMoveMarkers:o}},e}();t.EditOperationsCommand=o}),define(d[192],h([1,0,28,3,11,15,171]),function(e,t,n,i,o,r){"use strict";function s(e,t){return!!e[t]}function a(e){return"altKey"===e?r.isMacintosh?new c(57,"metaKey",6,"altKey"):new c(5,"ctrlKey",6,"altKey"):r.isMacintosh?new c(6,"altKey",57,"metaKey"):new c(6,"altKey",5,"ctrlKey")}Object.defineProperty(t,"__esModule",{value:!0});var u=function(){return function(e,t){this.target=e.target,this.hasTriggerModifier=s(e.event,t.triggerModifier),this.hasSideBySideModifier=s(e.event,t.triggerSideBySideModifier),this.isNoneOrSingleMouseDown=n.isIE||e.event.detail<=1}}();t.ClickLinkMouseEvent=u;var l=function(){return function(e,t){this.keyCodeIsTriggerKey=e.keyCode===t.triggerKey,this.keyCodeIsSideBySideKey=e.keyCode===t.triggerSideBySideKey,this.hasTriggerModifier=s(e,t.triggerModifier)}}();t.ClickLinkKeyboardEvent=l;var c=function(){function e(e,t,n,i){this.triggerKey=e,this.triggerModifier=t,this.triggerSideBySideKey=n,this.triggerSideBySideModifier=i}return e.prototype.equals=function(e){return this.triggerKey===e.triggerKey&&this.triggerModifier===e.triggerModifier&&this.triggerSideBySideKey===e.triggerSideBySideKey&&this.triggerSideBySideModifier===e.triggerSideBySideModifier},e}();t.ClickLinkOptions=c;var d=function(e){function t(t){var n=e.call(this)||this;return n._onMouseMoveOrRelevantKeyDown=n._register(new o.Emitter),n.onMouseMoveOrRelevantKeyDown=n._onMouseMoveOrRelevantKeyDown.event,n._onExecute=n._register(new o.Emitter),n.onExecute=n._onExecute.event,n._onCancel=n._register(new o.Emitter),n.onCancel=n._onCancel.event,n._editor=t,n._opts=a(n._editor.getConfiguration().multiCursorModifier),n.lastMouseMoveEvent=null,n.hasTriggerKeyOnMouseDown=!1,n._register(n._editor.onDidChangeConfiguration(function(e){if(e.multiCursorModifier){var t=a(n._editor.getConfiguration().multiCursorModifier);if(n._opts.equals(t))return;n._opts=t,n.lastMouseMoveEvent=null,n.hasTriggerKeyOnMouseDown=!1,n._onCancel.fire()}})),n._register(n._editor.onMouseMove(function(e){return n.onEditorMouseMove(new u(e,n._opts))})),n._register(n._editor.onMouseDown(function(e){return n.onEditorMouseDown(new u(e,n._opts))})),n._register(n._editor.onMouseUp(function(e){return n.onEditorMouseUp(new u(e,n._opts))})),n._register(n._editor.onKeyDown(function(e){return n.onEditorKeyDown(new l(e,n._opts))})),n._register(n._editor.onKeyUp(function(e){return n.onEditorKeyUp(new l(e,n._opts))})),n._register(n._editor.onMouseDrag(function(){return n.resetHandler()})),n._register(n._editor.onDidChangeCursorSelection(function(e){return n.onDidChangeCursorSelection(e)})),n._register(n._editor.onDidChangeModel(function(e){return n.resetHandler()})),n._register(n._editor.onDidChangeModelContent(function(){return n.resetHandler()})),n._register(n._editor.onDidScrollChange(function(e){(e.scrollTopChanged||e.scrollLeftChanged)&&n.resetHandler()})),n}return f(t,e),t.prototype.onDidChangeCursorSelection=function(e){e.selection&&e.selection.startColumn!==e.selection.endColumn&&this.resetHandler()},t.prototype.onEditorMouseMove=function(e){this.lastMouseMoveEvent=e,this._onMouseMoveOrRelevantKeyDown.fire([e,null])},t.prototype.onEditorMouseDown=function(e){this.hasTriggerKeyOnMouseDown=e.hasTriggerModifier},t.prototype.onEditorMouseUp=function(e){this.hasTriggerKeyOnMouseDown&&this._onExecute.fire(e)},t.prototype.onEditorKeyDown=function(e){this.lastMouseMoveEvent&&(e.keyCodeIsTriggerKey||e.keyCodeIsSideBySideKey&&e.hasTriggerModifier)?this._onMouseMoveOrRelevantKeyDown.fire([this.lastMouseMoveEvent,e]):e.hasTriggerModifier&&this._onCancel.fire()},t.prototype.onEditorKeyUp=function(e){e.keyCodeIsTriggerKey&&this._onCancel.fire()},t.prototype.resetHandler=function(){this.lastMouseMoveEvent=null,this.hasTriggerKeyOnMouseDown=!1,this._onCancel.fire()},t}(i.Disposable);t.ClickLinkGesture=d}),define(d[195],h([1,0,18,10]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o;!function(e){e[e.IDLE=0]="IDLE",e[e.FIRST_WAIT=1]="FIRST_WAIT",e[e.SECOND_WAIT=2]="SECOND_WAIT",e[e.WAITING_FOR_ASYNC_COMPUTATION=3]="WAITING_FOR_ASYNC_COMPUTATION"}(o||(o={}));var r=function(){function e(e,t,i,o){var r=this;this._computer=e,this._state=0,this._firstWaitScheduler=new n.RunOnceScheduler(function(){return r._triggerAsyncComputation()},this._getHoverTimeMillis()/2),this._secondWaitScheduler=new n.RunOnceScheduler(function(){return r._triggerSyncComputation()},this._getHoverTimeMillis()/2),this._loadingMessageScheduler=new n.RunOnceScheduler(function(){return r._showLoadingMessage()},3*this._getHoverTimeMillis()),this._asyncComputationPromise=null,this._asyncComputationPromiseDone=!1,this._completeCallback=t,this._errorCallback=i,this._progressCallback=o}return e.prototype.getComputer=function(){return this._computer},e.prototype._getHoverTimeMillis=function(){return this._computer.getHoverTimeMillis?this._computer.getHoverTimeMillis():e.HOVER_TIME},e.prototype._triggerAsyncComputation=function(){var e=this;this._state=2,this._secondWaitScheduler.schedule(),this._computer.computeAsync?(this._asyncComputationPromiseDone=!1,this._asyncComputationPromise=this._computer.computeAsync().then(function(t){e._asyncComputationPromiseDone=!0,e._withAsyncResult(t)},function(){return e._onError})):this._asyncComputationPromiseDone=!0},e.prototype._triggerSyncComputation=function(){this._computer.computeSync&&this._computer.onResult(this._computer.computeSync(),!0),this._asyncComputationPromiseDone?(this._state=0,this._onComplete(this._computer.getResult())):(this._state=3,this._onProgress(this._computer.getResult()))},e.prototype._showLoadingMessage=function(){3===this._state&&this._onProgress(this._computer.getResultWithLoadingMessage())},e.prototype._withAsyncResult=function(e){e&&this._computer.onResult(e,!1),3===this._state&&(this._state=0,this._onComplete(this._computer.getResult()))},e.prototype._onComplete=function(e){this._completeCallback&&this._completeCallback(e)},e.prototype._onError=function(e){this._errorCallback?this._errorCallback(e):i.onUnexpectedError(e)},e.prototype._onProgress=function(e){this._progressCallback&&this._progressCallback(e)},e.prototype.start=function(){0===this._state&&(this._state=1,this._firstWaitScheduler.schedule(),this._loadingMessageScheduler.schedule())},e.prototype.cancel=function(){this._loadingMessageScheduler.cancel(),1===this._state&&this._firstWaitScheduler.cancel(),2===this._state&&(this._secondWaitScheduler.cancel(),this._asyncComputationPromise&&(this._asyncComputationPromise.cancel(),this._asyncComputationPromise=null)),3===this._state&&this._asyncComputationPromise&&(this._asyncComputationPromise.cancel(),this._asyncComputationPromise=null),this._state=0},e.HOVER_TIME=300,e}();t.HoverOperation=r}),define(d[196],h([1,0,4,12,25,41,63,3]),function(e,t,n,i,o,r,s,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var u=function(e){function t(t,n){var i=e.call(this)||this;return i.disposables=[],i.allowEditorOverflow=!0,i._id=t,i._editor=n,i._isVisible=!1,i._containerDomNode=document.createElement("div"),i._containerDomNode.className="monaco-editor-hover hidden",i._containerDomNode.tabIndex=0,i._domNode=document.createElement("div"),i._domNode.className="monaco-editor-hover-content",i.scrollbar=new s.DomScrollableElement(i._domNode,{}),i.disposables.push(i.scrollbar),i._containerDomNode.appendChild(i.scrollbar.getDomNode()),i.onkeydown(i._containerDomNode,function(e){e.equals(9)&&i.hide()}),i._register(i._editor.onDidChangeConfiguration(function(e){e.fontInfo&&i.updateFont()})),i._editor.onDidLayoutChange(function(e){return i.updateMaxHeight()}),i.updateMaxHeight(),i._editor.addContentWidget(i),i._showAtPosition=null,i}return f(t,e),Object.defineProperty(t.prototype,"isVisible",{get:function(){return this._isVisible},set:function(e){this._isVisible=e,n.toggleClass(this._containerDomNode,"hidden",!this._isVisible)},enumerable:!0,configurable:!0}),t.prototype.getId=function(){return this._id},t.prototype.getDomNode=function(){return this._containerDomNode},t.prototype.showAt=function(e,t){this._showAtPosition=new i.Position(e.lineNumber,e.column),this.isVisible=!0,this._editor.layoutContentWidget(this),this._editor.render(),this._stoleFocus=t,t&&this._containerDomNode.focus()},t.prototype.hide=function(){this.isVisible&&(this.isVisible=!1,this._editor.layoutContentWidget(this),this._stoleFocus&&this._editor.focus())},t.prototype.getPosition=function(){return this.isVisible?{position:this._showAtPosition,preference:[o.ContentWidgetPositionPreference.ABOVE,o.ContentWidgetPositionPreference.BELOW]}:null},t.prototype.dispose=function(){this._editor.removeContentWidget(this),this.disposables=a.dispose(this.disposables),e.prototype.dispose.call(this)},t.prototype.updateFont=function(){var e=this,t=Array.prototype.slice.call(this._domNode.getElementsByTagName("code")),n=Array.prototype.slice.call(this._domNode.getElementsByClassName("code"));t.concat(n).forEach(function(t){return e._editor.applyFontInfo(t)})},t.prototype.updateContents=function(e){this._domNode.textContent="",this._domNode.appendChild(e),this.updateFont(),this._editor.layoutContentWidget(this),this.scrollbar.scanDomNode()},t.prototype.updateMaxHeight=function(){var e=Math.max(this._editor.getLayoutInfo().height/4,250),t=this._editor.getConfiguration().fontInfo,n=t.fontSize,i=t.lineHeight;this._domNode.style.fontSize=n+"px",this._domNode.style.lineHeight=i+"px",this._domNode.style.maxHeight=e+"px"},t}(r.Widget);t.ContentHoverWidget=u;var l=function(e){function t(t,n){var i=e.call(this)||this;return i._id=t,i._editor=n,i._isVisible=!1,i._domNode=document.createElement("div"),i._domNode.className="monaco-editor-hover hidden",i._domNode.setAttribute("aria-hidden","true"),i._domNode.setAttribute("role","presentation"),i._showAtLineNumber=-1,i._register(i._editor.onDidChangeConfiguration(function(e){e.fontInfo&&i.updateFont()})),i._editor.addOverlayWidget(i),i}return f(t,e),Object.defineProperty(t.prototype,"isVisible",{get:function(){return this._isVisible},set:function(e){this._isVisible=e,n.toggleClass(this._domNode,"hidden",!this._isVisible)},enumerable:!0,configurable:!0}),t.prototype.getId=function(){return this._id},t.prototype.getDomNode=function(){return this._domNode},t.prototype.showAt=function(e){this._showAtLineNumber=e,this.isVisible||(this.isVisible=!0);var t=this._editor.getLayoutInfo(),n=this._editor.getTopForLineNumber(this._showAtLineNumber),i=this._editor.getScrollTop(),o=this._editor.getConfiguration().lineHeight,r=n-i-(this._domNode.clientHeight-o)/2;this._domNode.style.left=t.glyphMarginLeft+t.glyphMarginWidth+"px",this._domNode.style.top=Math.max(Math.round(r),0)+"px"},t.prototype.hide=function(){this.isVisible&&(this.isVisible=!1)},t.prototype.getPosition=function(){return null},t.prototype.dispose=function(){this._editor.removeOverlayWidget(this),e.prototype.dispose.call(this)},t.prototype.updateFont=function(){var e=this,t=Array.prototype.slice.call(this._domNode.getElementsByTagName("code")),n=Array.prototype.slice.call(this._domNode.getElementsByClassName("code"));t.concat(n).forEach(function(t){return e._editor.applyFontInfo(t)})},t.prototype.updateContents=function(e){this._domNode.textContent="",this._domNode.appendChild(e),this.updateFont()},t}(r.Widget);t.GlyphHoverWidget=l}),define(d[262],h([1,0,22]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t,n){this._editRange=e,this._originalSelection=t,this._text=n}return e.prototype.getEditOperations=function(e,t){t.addTrackedEditOperation(this._editRange,this._text)},e.prototype.computeCursorState=function(e,t){var i=t.getInverseEditOperations()[0].range;return this._originalSelection.isEmpty()?new n.Selection(i.endLineNumber,Math.min(this._originalSelection.positionColumn,i.endColumn),i.endLineNumber,Math.min(this._originalSelection.positionColumn,i.endColumn)):new n.Selection(i.endLineNumber,i.endColumn-this._text.length,i.endLineNumber,i.endColumn)},e}();t.InPlaceReplaceCommand=i}),define(d[263],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.getSpaceCnt=function(e,t){for(var n=0,i=0;i1&&(i-=1,r=e.getLineMaxColumn(i)),t.addTrackedEditOperation(new n.Range(i,r,o,s),null)}},e.prototype.computeCursorState=function(e,t){var n=t.getInverseEditOperations()[0].range;return new i.Selection(n.endLineNumber,this.restoreCursorToColumn,n.endLineNumber,this.restoreCursorToColumn)},e}();t.DeleteLinesCommand=o}),define(d[266],h([1,0,59,2]),function(e,t,n,i){"use strict";function o(e,t,n){var i=t.startLineNumber,o=t.endLineNumber;if(1===t.endColumn&&o--,i>=o)return null;for(var r=[],s=i;s<=o;s++)r.push(e.getLineContent(s));var a=r.slice(0);return a.sort(function(e,t){return e.toLowerCase().localeCompare(t.toLowerCase())}),!0===n&&(a=a.reverse()),{startLineNumber:i,endLineNumber:o,before:r,after:a}}function r(e,t,r){var s=o(e,t,r);return s?n.EditOperation.replace(new i.Range(s.startLineNumber,1,s.endLineNumber,e.getLineMaxColumn(s.endLineNumber)),s.after.join("\n")):null}Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t){this.selection=e,this.descending=t}return e.prototype.getEditOperations=function(e,t){var n=r(e,this.selection,this.descending);n&&t.addEditOperation(n.range,n.text),this.selectionId=t.trackSelection(this.selection)},e.prototype.computeCursorState=function(e,t){return t.getTrackedSelection(this.selectionId)},e.canRun=function(e,t,n){var i=o(e,t,n);if(!i)return!1;for(var r=0,s=i.before.length;r0?t.show(e):t.hide()},function(e){t.hide()})},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"title",{get:function(){return this._options.glyphMarginHoverMessage},set:function(e){},enumerable:!0,configurable:!0}),e.prototype.show=function(e){this._currentLine=e.range.startLineNumber,this._decorationIds=this._editor.deltaDecorations(this._decorationIds,[{options:this._options,range:_({},e.range,{endLineNumber:e.range.startLineNumber})}])},e.prototype.hide=function(){this._decorationIds=this._editor.deltaDecorations(this._decorationIds,[]),this._futureFixes.cancel(),this._currentLine=void 0},e}();t.LightBulbWidget=a}),define(d[268],h([1,0,18,4,12,53,11]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e,t,n){this._onDidExecuteCodeAction=new s.Emitter,this.onDidExecuteCodeAction=this._onDidExecuteCodeAction.event,this._editor=e,this._contextMenuService=t,this._commandService=n}return e.prototype.show=function(e,t){var i=this,s=e.then(function(e){return e.map(function(e){return new r.Action(e.id,e.title,void 0,!0,function(){return n.always((t=i._commandService).executeCommand.apply(t,[e.id].concat(e.arguments)),function(){return i._onDidExecuteCodeAction.fire(void 0)});var t})})});this._contextMenuService.showContextMenu({getAnchor:function(){return o.Position.isIPosition(t)&&(t=i._toCoords(t)),t},getActions:function(){return s},onHide:function(){i._visible=!1}})},Object.defineProperty(e.prototype,"isVisible",{get:function(){return this._visible},enumerable:!0,configurable:!0}),e.prototype._toCoords=function(e){this._editor.revealPosition(e),this._editor.render();var t=this._editor.getScrolledVisiblePosition(this._editor.getPosition()),n=i.getDomNodePagePosition(this._editor.getDomNode());return{x:n.left+t.left,y:n.top+t.top+t.height}},e}();t.QuickFixContextMenu=a}),define(d[269],h([1,0,2,91,98,43]),function(e,t,n,i,o,r){"use strict";function s(e){var t=new u;return t.start=e.range.getStartPosition(),t.end=e.range.getEndPosition(),t}function a(e,t){if(e instanceof l&&e.isEmpty)return null;if(!n.Range.containsPosition(e.range,t))return null;var i;if(e instanceof l){if(e.hasChildren)for(var o=0,r=e.children.length;o0},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"isEmpty",{get:function(){return!this.hasChildren&&!this.parent},enumerable:!0,configurable:!0}),t.prototype.append=function(e){return!!e&&(e.parent=this,this.children||(this.children=[]),e instanceof t?e.children&&this.children.push.apply(this.children,e.children):this.children.push(e),!0)},t}(u);t.NodeList=l;var c=function(e){function t(){var t=e.call(this)||this;return t.elements=new l,t.elements.parent=t,t}return f(t,e),Object.defineProperty(t.prototype,"start",{get:function(){return this.open.start},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"end",{get:function(){return this.close.end},enumerable:!0,configurable:!0}),t}(u);t.Block=c;var d=function(){return function(e,t,n){this.range=e,this.bracket=t,this.bracketType=n}}(),h=function(){return function(e,t,n){this.lineNumber=t,this.lineText=n,this.startOffset=e.startOffset,this.endOffset=e.endOffset,this.type=e.tokenType,this.languageId=e.languageId}}(),p=function(){function e(e){this._model=e,this._lineCount=this._model.getLineCount(),this._versionId=this._model.getVersionId(),this._lineNumber=0,this._lineText=null,this._advance()}return e.prototype._advance=function(){for(this._next=this._next?this._next.next():null;!this._next&&this._lineNumber0)return this._nextBuff.shift();var e=this._rawTokenScanner.next();if(!e)return null;var t=e.lineNumber,s=e.lineText,a=e.type,u=e.startOffset,l=e.endOffset;this._cachedLanguageId!==e.languageId&&(this._cachedLanguageId=e.languageId,this._cachedLanguageBrackets=r.LanguageConfigurationRegistry.getBracketsSupport(this._cachedLanguageId));var c=this._cachedLanguageBrackets;if(!c||i.ignoreBracketsInToken(a))return new d(new n.Range(t,u+1,t,l+1),0,null);var h;do{if(h=o.BracketsUtils.findNextBracketInToken(c.forwardRegex,t,s,u,l)){var p=h.startColumn-1,f=h.endColumn-1;u0;){var i=n.shift();if(!t(i))break;n.unshift.apply(n,i.children)}}Object.defineProperty(t,"__esModule",{value:!0});var i;!function(e){e[e.Dollar=0]="Dollar",e[e.Colon=1]="Colon",e[e.Comma=2]="Comma",e[e.CurlyOpen=3]="CurlyOpen",e[e.CurlyClose=4]="CurlyClose",e[e.Backslash=5]="Backslash",e[e.Forwardslash=6]="Forwardslash",e[e.Pipe=7]="Pipe",e[e.Int=8]="Int",e[e.VariableName=9]="VariableName",e[e.Format=10]="Format",e[e.EOF=11]="EOF"}(i=t.TokenType||(t.TokenType={}));var o=function(){function e(){this.text("")}return e.isDigitCharacter=function(e){return e>=48&&e<=57},e.isVariableCharacter=function(e){return 95===e||e>=97&&e<=122||e>=65&&e<=90},e.prototype.text=function(e){this.value=e,this.pos=0},e.prototype.tokenText=function(e){return this.value.substr(e.pos,e.len)},e.prototype.next=function(){if(this.pos>=this.value.length)return{type:i.EOF,pos:this.pos,len:0};var t,n=this.pos,o=0,r=this.value.charCodeAt(n);if("number"==typeof(t=e._table[r]))return this.pos+=1,{type:t,pos:n,len:1};if(e.isDigitCharacter(r)){t=i.Int;do{o+=1,r=this.value.charCodeAt(n+o)}while(e.isDigitCharacter(r));return this.pos+=o,{type:t,pos:n,len:o}}if(e.isVariableCharacter(r)){t=i.VariableName;do{r=this.value.charCodeAt(n+ ++o)}while(e.isVariableCharacter(r)||e.isDigitCharacter(r));return this.pos+=o,{type:t,pos:n,len:o}}t=i.Format;do{o+=1,r=this.value.charCodeAt(n+o)}while(!isNaN(r)&&void 0===e._table[r]&&!e.isDigitCharacter(r)&&!e.isVariableCharacter(r));return this.pos+=o,{type:t,pos:n,len:o}},e._table=(h={},h[36]=i.Dollar,h[58]=i.Colon,h[44]=i.Comma,h[123]=i.CurlyOpen,h[125]=i.CurlyClose,h[92]=i.Backslash,h[47]=i.Forwardslash,h[124]=i.Pipe,h),e}();t.Scanner=o;var r=function(){function e(){this._children=[]}return e.prototype.appendChild=function(e){return e instanceof s&&this._children[this._children.length-1]instanceof s?this._children[this._children.length-1].value+=e.value:(e.parent=this,this._children.push(e)),this},e.prototype.replace=function(e,t){var n=e.parent,i=n.children.indexOf(e),o=n.children.slice(0);o.splice.apply(o,[i,1].concat(t)),n._children=o,t.forEach(function(e){return e.parent=n})},Object.defineProperty(e.prototype,"children",{get:function(){return this._children},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"snippet",{get:function(){for(var e=this;;){if(!e)return;if(e instanceof c)return e;e=e.parent}},enumerable:!0,configurable:!0}),e.prototype.toString=function(){return this.children.reduce(function(e,t){return e+t.toString()},"")},e.prototype.len=function(){return 0},e}();t.Marker=r;var s=function(e){function t(t){var n=e.call(this)||this;return n.value=t,n}return f(t,e),t.prototype.toString=function(){return this.value},t.prototype.toTextmateString=function(){return this.value.replace(/\$|}|\\/g,"\\$&")},t.prototype.len=function(){return this.value.length},t.prototype.clone=function(){return new t(this.value)},t}(r);t.Text=s;var a=function(e){function t(t){var n=e.call(this)||this;return n.index=t,n}return f(t,e),t.compareByIndex=function(e,t){return e.index===t.index?0:e.isFinalTabstop?1:t.isFinalTabstop?-1:e.indext.index?1:0},Object.defineProperty(t.prototype,"isFinalTabstop",{get:function(){return 0===this.index},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"choice",{get:function(){return 1===this._children.length&&this._children[0]instanceof u?this._children[0]:void 0},enumerable:!0,configurable:!0}),t.prototype.toTextmateString=function(){return 0===this.children.length?"$"+this.index:this.choice?"${"+this.index+"|"+this.choice.toTextmateString()+"|}":"${"+this.index+":"+this.children.map(function(e){return e.toTextmateString()}).join("")+"}"},t.prototype.clone=function(){var e=new t(this.index);return e._children=this.children.map(function(e){return e.clone()}),e},t}(r);t.Placeholder=a;var u=function(e){function t(){var t=null!==e&&e.apply(this,arguments)||this;return t.options=[],t}return f(t,e),t.prototype.appendChild=function(e){return e instanceof s&&(e.parent=this,this.options.push(e)),this},t.prototype.toString=function(){return this.options[0].value},t.prototype.toTextmateString=function(){return this.options.map(function(e){return e.value.replace(/\||,/g,"\\$&")}).join(",")},t.prototype.len=function(){return this.options[0].len()},t.prototype.clone=function(){var e=new t;return this.options.forEach(e.appendChild,e),e},t}(r);t.Choice=u;var l=function(e){function t(t){var n=e.call(this)||this;return n.name=t,n}return f(t,e),t.prototype.resolve=function(e){var t=e.resolve(this);return void 0!==t&&(this._children=[new s(t)],!0)},t.prototype.toTextmateString=function(){return 0===this.children.length?"${"+this.name+"}":"${"+this.name+":"+this.children.map(function(e){return e.toTextmateString()}).join("")+"}"},t.prototype.clone=function(){var e=new t(this.name);return e._children=this.children.map(function(e){return e.clone()}),e},t}(r);t.Variable=l;var c=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),Object.defineProperty(t.prototype,"placeholders",{get:function(){var e=this;return this._placeholders||(this._placeholders=[],this.walk(function(t){return t instanceof a&&e.placeholders.push(t),!0})),this._placeholders},enumerable:!0,configurable:!0}),t.prototype.offset=function(e){var t=0,n=!1;return this.walk(function(i){return i===e?(n=!0,!1):(t+=i.len(),!0)}),n?t:-1},t.prototype.fullLen=function(e){var t=0;return n([e],function(e){return t+=e.len(),!0}),t},t.prototype.enclosingPlaceholders=function(e){for(var t=[],n=e.parent;n;)n instanceof a&&t.push(n),n=n.parent;return t},t.prototype.resolveVariables=function(e){var t=this;return this.walk(function(n){return n instanceof l&&n.resolve(e)&&(t._placeholders=void 0),!0}),this},t.prototype.appendChild=function(t){return this._placeholders=void 0,e.prototype.appendChild.call(this,t)},t.prototype.replace=function(t,n){return this._placeholders=void 0,e.prototype.replace.call(this,t,n)},t.prototype.toTextmateString=function(){return this.children.reduce(function(e,t){return e+t.toTextmateString()},"")},t.prototype.clone=function(){var e=new t;return this._children=this.children.map(function(e){return e.clone()}),e},t.prototype.walk=function(e){n(this.children,e)},t}(r);t.TextmateSnippet=c;var d=function(){function e(){this._scanner=new o}return e.escape=function(e){return e.replace(/\$|}|\\/g,"\\$&")},e.prototype.text=function(e){return this.parse(e).toString()},e.prototype.parse=function(e,t,n){this._scanner.text(e),this._token=this._scanner.next();for(var i=new c;this._parse(i););var o=new Map,r=[];i.walk(function(e){return e instanceof a&&(e.isFinalTabstop?o.set(0):!o.has(e.index)&&e.children.length>0?o.set(e.index,e.children):r.push(e)),!0});for(var s=0,u=r;s0||n)&&i.appendChild(new a(0)),i},e.prototype._accept=function(e,t){if(void 0===e||this._token.type===e){var n=!t||this._scanner.tokenText(this._token);return this._token=this._scanner.next(),n}return!1},e.prototype._backTo=function(e){return this._scanner.pos=e.pos+e.len,this._token=e,!1},e.prototype._parse=function(e){return this._parseEscaped(e)||this._parseTabstopOrVariableName(e)||this._parseComplexPlaceholder(e)||this._parseComplexVariable(e)||this._parseAnything(e)},e.prototype._parseEscaped=function(e){var t;return!!(t=this._accept(i.Backslash,!0))&&(t=this._accept(i.Dollar,!0)||this._accept(i.CurlyClose,!0)||this._accept(i.Backslash,!0)||t,e.appendChild(new s(t)),!0)},e.prototype._parseTabstopOrVariableName=function(e){var t,n=this._token;return this._accept(i.Dollar)&&(t=this._accept(i.VariableName,!0)||this._accept(i.Int,!0))?(e.appendChild(/^\d+$/.test(t)?new a(Number(t)):new l(t)),!0):this._backTo(n)},e.prototype._parseComplexPlaceholder=function(e){var t,n=this._token;if(!(this._accept(i.Dollar)&&this._accept(i.CurlyOpen)&&(t=this._accept(i.Int,!0))))return this._backTo(n);var o=new a(Number(t));if(this._accept(i.Colon))for(;;){if(this._accept(i.CurlyClose))return e.appendChild(o),!0;if(!this._parse(o))return e.appendChild(new s("${"+t+":")),o.children.forEach(e.appendChild,e),!0}else{if(!this._accept(i.Pipe))return this._accept(i.CurlyClose)?(e.appendChild(o),!0):this._backTo(n);for(var r=new u;;){if(this._parseChoiceElement(r)){if(this._accept(i.Comma))continue;if(this._accept(i.Pipe)&&this._accept(i.CurlyClose))return o.appendChild(r),e.appendChild(o),!0}return this._backTo(n),!1}}},e.prototype._parseChoiceElement=function(e){for(var t=this._token,n=[];;){if(this._token.type===i.Comma||this._token.type===i.Pipe)break;var o=void 0;if(!(o=(o=this._accept(i.Backslash,!0))?this._accept(i.Comma,!0)||this._accept(i.Pipe,!0)||o:this._accept(void 0,!0)))return this._backTo(t),!1;n.push(o)}return 0===n.length?(this._backTo(t),!1):(e.appendChild(new s(n.join(""))),!0)},e.prototype._parseComplexVariable=function(e){var t,n=this._token;if(!(this._accept(i.Dollar)&&this._accept(i.CurlyOpen)&&(t=this._accept(i.VariableName,!0))))return this._backTo(n);var o=new l(t);if(!this._accept(i.Colon))return this._accept(i.CurlyClose)?(e.appendChild(o),!0):this._backTo(n);for(;;){if(this._accept(i.CurlyClose))return e.appendChild(o),!0;if(!this._parse(o))return e.appendChild(new s("${"+t+":")),o.children.forEach(e.appendChild,e),!0}},e.prototype._parseAnything=function(e){return this._token.type!==i.EOF&&(e.appendChild(new s(this._scanner.tokenText(this._token))),this._accept(void 0),!0)},e}();t.SnippetParser=d;var h}),define(d[271],h([1,0,45,142,9]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t){this._model=e,this._selection=t}return e.prototype.resolve=function(e){var t=e.name;if("SELECTION"===t||"TM_SELECTED_TEXT"===t){var r=this._model.getValueInRange(this._selection)||void 0;if(r&&this._selection.startLineNumber!==this._selection.endLineNumber){var s=this._model.getLineContent(this._selection.startLineNumber),a=o.getLeadingWhitespace(s,0,this._selection.startColumn-1),u=a;e.snippet.walk(function(t){return t!==e&&(t instanceof i.Text&&(u=o.getLeadingWhitespace(t.value.split(/\r\n|\r|\n/).pop())),!0)});var l=o.commonPrefixLength(u,a);r=r.replace(/(\r\n|\r|\n)(.*)/g,function(e,t,n){return""+t+u.substr(l)+n})}return r}if("TM_CURRENT_LINE"===t)return this._model.getLineContent(this._selection.positionLineNumber);if("TM_CURRENT_WORD"===t){var c=this._model.getWordAtPosition({lineNumber:this._selection.positionLineNumber,column:this._selection.positionColumn});return c&&c.word||void 0}if("TM_LINE_INDEX"===t)return String(this._selection.positionLineNumber-1);if("TM_LINE_NUMBER"===t)return String(this._selection.positionLineNumber);if("TM_FILENAME"===t)return n.basename(this._model.uri.fsPath);if("TM_DIRECTORY"===t){var d=n.dirname(this._model.uri.fsPath);return"."!==d?d:""}return"TM_FILEPATH"===t?this._model.uri.fsPath:void 0},e.VariableNames=Object.freeze({SELECTION:!0,TM_SELECTED_TEXT:!0,TM_CURRENT_LINE:!0,TM_CURRENT_WORD:!0,TM_LINE_INDEX:!0,TM_LINE_NUMBER:!0,TM_FILENAME:!0,TM_DIRECTORY:!0,TM_FILEPATH:!0}),e}();t.EditorSnippetVariableResolver=r}),define(d[272],h([1,0,81]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){return function(){}}();t.LineContext=i;var o=function(){function e(t,n,i,o){this._snippetCompareFn=e._compareCompletionItems,this._items=t,this._column=n,this._lineContext=i,"top"===o?this._snippetCompareFn=e._compareCompletionItemsSnippetsUp:"bottom"===o&&(this._snippetCompareFn=e._compareCompletionItemsSnippetsDown)}return Object.defineProperty(e.prototype,"lineContext",{get:function(){return this._lineContext},set:function(e){this._lineContext.leadingLineContent===e.leadingLineContent&&this._lineContext.characterCountDelta===e.characterCountDelta||(this._lineContext=e,this._filteredItems=void 0)},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"items",{get:function(){return this._ensureCachedState(),this._filteredItems},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"incomplete",{get:function(){return this._ensureCachedState(),this._isIncomplete},enumerable:!0,configurable:!0}),e.prototype.resolveIncompleteInfo=function(){for(var e=[],t=[],n=0,i=this._items;nt.score?-1:e.scoret.idx?1:0},e._compareCompletionItemsSnippetsDown=function(t,n){if(t.suggestion.type!==n.suggestion.type){if("snippet"===t.suggestion.type)return 1;if("snippet"===n.suggestion.type)return-1}return e._compareCompletionItems(t,n)},e._compareCompletionItemsSnippetsUp=function(t,n){if(t.suggestion.type!==n.suggestion.type){if("snippet"===t.suggestion.type)return-1;if("snippet"===n.suggestion.type)return 1}return e._compareCompletionItems(t,n)},e}();t.CompletionModel=o}),define(d[273],h([1,0,7,17,106,90,130,9]),function(e,t,n,i,o,r,s,a){"use strict";function u(e,t,n){return c(e,t,n)}function l(e,t){for(var n=[],i=0,s=e.length;i")}return n.join("")}function c(e,t,n){for(var i=[],r=n.getInitialState(),a=0,u=e.length;a"),r=c.endState}return i.join("")}Object.defineProperty(t,"__esModule",{value:!0});var d=function(){function e(){}return e.colorizeElement=function(e,t,n,i){var o=(i=i||{}).theme||"vs",r=i.mimeType||n.getAttribute("lang")||n.getAttribute("data-lang");if(r){e.setTheme(o);var s=n.firstChild.nodeValue;n.className+="monaco-editor "+o;var a=function(e){n.innerHTML=e};return this.colorize(t,s,r,i).then(a,function(e){return console.error(e)},a)}console.error("Mode not detected")},e._tokenizationSupportChangedPromise=function(e){var t=null,o=function(){t&&(t.dispose(),t=null)};return new n.TPromise(function(n,r,s){t=i.TokenizationRegistry.onDidChange(function(t){t.changedLanguages.indexOf(e)>=0&&(o(),n(void 0))})},o)},e.colorize=function(e,t,o,r){a.startsWithUTF8BOM(t)&&(t=t.substr(1));var s=t.split(/\r\n|\r|\n/),c=e.getModeId(o);void 0===(r=r||{}).tabSize&&(r.tabSize=4),e.getOrCreateMode(c);var d=i.TokenizationRegistry.get(c);return d?n.TPromise.as(u(s,r.tabSize,d)):n.TPromise.any([this._tokenizationSupportChangedPromise(c),n.TPromise.timeout(500)]).then(function(e){var t=i.TokenizationRegistry.get(c);return t?u(s,r.tabSize,t):l(s,r.tabSize)})},e.colorizeLine=function(e,t,n,i){return void 0===i&&(i=4),o.renderViewLine(new o.RenderLineInput(!1,e,t,0,n,[],i,0,-1,"none",!1,!1)).html},e.colorizeModelLine=function(e,t,n){void 0===n&&(n=4);var i=e.getLineContent(t);e.forceTokenization(t);var o=e.getLineTokens(t).inflate();return this.colorizeLine(i,e.mightContainRTL(),o,n)},e}();t.Colorizer=d}),define(d[145],h([1,0]),function(e,t){"use strict";function n(e){return Array.isArray(e)}function i(e){return"string"==typeof e}function o(e){return!e}function r(e,t){return e.ignoreCase&&t?t.toLowerCase():t}Object.defineProperty(t,"__esModule",{value:!0});!function(e){e[e.None=0]="None",e[e.Open=1]="Open",e[e.Close=-1]="Close"}(t.MonarchBracket||(t.MonarchBracket={})),t.isFuzzyActionArr=n,t.isFuzzyAction=function(e){return!n(e)},t.isString=i,t.isIAction=function(e){return!i(e)},t.empty=o,t.fixCase=r,t.sanitize=function(e){return e.replace(/[&<>'"_]/g,"-")},t.log=function(e,t){console.log(e.languageId+": "+t)},t.throwError=function(e,t){throw new Error(e.languageId+": "+t)},t.substituteMatches=function(e,t,n,i,s){var a=/\$((\$)|(#)|(\d\d?)|[sS](\d\d?)|@(\w+))/g,u=null;return t.replace(a,function(t,a,l,c,d,h,p,f,g){return o(l)?o(c)?!o(d)&&d0;){var n=e.tokenizer[t];if(n)return n;var i=t.lastIndexOf(".");t=i<0?null:t.substr(0,i)}return null},t.stateExists=function(e,t){for(;t&&t.length>0;){if(e.stateNames[t])return!0;var n=t.lastIndexOf(".");t=n<0?null:t.substr(0,n)}return!1}}),define(d[275],h([1,0,26,145]),function(e,t,n,i){"use strict";function o(e,t){if(!t)return!1;if(!Array.isArray(t))return!1;var n;for(n in t)if(t.hasOwnProperty(n)&&!e(t[n]))return!1;return!0}function r(e,t,n){return"boolean"==typeof e?e:(n&&(e||void 0===t)&&n(),void 0===t?null:t)}function s(e,t,n){return"string"==typeof e?e:(n&&(e||void 0===t)&&n(),void 0===t?null:t)}function a(e,t){if("string"!=typeof t)return null;for(var n=0;t.indexOf("@")>=0&&n<5;)n++,t=t.replace(/@(\w+)/g,function(n,o){var r="";return"string"==typeof e[o]?r=e[o]:e[o]&&e[o]instanceof RegExp?r=e[o].source:void 0===e[o]?i.throwError(e,"language definition does not contain attribute '"+o+"', used at: "+t):i.throwError(e,"attribute reference '"+o+"' must be a string, used at: "+t),i.empty(r)?"":"(?:"+r+")"});return new RegExp(t,e.ignoreCase?"i":"")}function u(e,t,n,i){if(i<0)return e;if(i=100){i-=100;var o=n.split(".");if(o.unshift(n),i=0&&(o.tokenSubst=!0),"string"==typeof n.bracket&&("@open"===n.bracket?o.bracket=1:"@close"===n.bracket?o.bracket=-1:i.throwError(e,"a 'bracket' attribute must be either '@open' or '@close', in rule: "+t)),n.next)if("string"!=typeof n.next)i.throwError(e,"the next state must be a string value in rule: "+t);else{var r=n.next;/^(@pop|@push|@popall)$/.test(r)||("@"===r[0]&&(r=r.substr(1)),r.indexOf("$")<0&&(i.stateExists(e,i.substituteMatches(e,r,"",[],""))||i.throwError(e,"the next state '"+n.next+"' is not defined in rule: "+t))),o.next=r}return"number"==typeof n.goBack&&(o.goBack=n.goBack),"string"==typeof n.switchTo&&(o.switchTo=n.switchTo),"string"==typeof n.log&&(o.log=n.log),"string"==typeof n.nextEmbedded&&(o.nextEmbedded=n.nextEmbedded,e.usesEmbedded=!0),o}if(Array.isArray(n)){var s,a=[];for(s in n)n.hasOwnProperty(s)&&(a[s]=c(e,t,n[s]));return{group:a}}if(n.cases){var u,d=[];for(u in n.cases)if(n.cases.hasOwnProperty(u)){var h=c(e,t,n.cases[u]);"@default"===u||"@"===u||""===u?d.push({test:null,value:h,name:u}):"@eos"===u?d.push({test:function(e,t,n,i){return i},value:h,name:u}):d.push(l(e,t,u,h))}var p=e.defaultToken;return{test:function(e,t,n,i){var o;for(o in d)if(d.hasOwnProperty(o)&&(!d[o].test||d[o].test(e,t,n,i)))return d[o].value;return p}}}return i.throwError(e,"an action must be a string, an object with a 'token' or 'cases' attribute, or an array of actions; in rule: "+t),""}return{token:""}}Object.defineProperty(t,"__esModule",{value:!0});var d=function(){function e(e){this.regex=new RegExp(""),this.action={token:""},this.matchOnlyAtLineStart=!1,this.name="",this.name=e}return e.prototype.setRegex=function(e,t){var n;"string"==typeof t?n=t:t instanceof RegExp?n=t.source:i.throwError(e,"rules must start with a match string or regular expression: "+this.name),this.matchOnlyAtLineStart=n.length>0&&"^"===n[0],this.name=this.name+": "+n,this.regex=a(e,"^(?:"+(this.matchOnlyAtLineStart?n.substr(1):n)+")")},e.prototype.setAction=function(e,t){this.action=c(e,this.name,t)},e}();t.compile=function(e,t){function n(e,u,l){var c;for(c in l)if(l.hasOwnProperty(c)){var h=l[c],p=h.include;if(p)"string"!=typeof p&&i.throwError(o,"an 'include' attribute must be a string at: "+e),"@"===p[0]&&(p=p.substr(1)),t.tokenizer[p]||i.throwError(o,"include target '"+p+"' is not defined at: "+e),n(e+"."+p,u,t.tokenizer[p]);else{var f=new d(e);if(Array.isArray(h)&&h.length>=1&&h.length<=3)if(f.setRegex(a,h[0]),h.length>=3)if("string"==typeof h[1])f.setAction(a,{token:h[1],next:h[2]});else if("object"==typeof h[1]){var g=h[1];g.next=h[2],f.setAction(a,g)}else i.throwError(o,"a next state as the last element of a rule can only be given if the action is either an object or a string, at: "+e);else f.setAction(a,h[1]);else h.regex||i.throwError(o,"a rule must either be an array, or an object with a 'regex' or 'include' field at: "+e),h.name&&(f.name=s(h.name)),h.matchOnlyAtStart&&(f.matchOnlyAtLineStart=r(h.matchOnlyAtLineStart)),f.setRegex(a,h.regex),f.setAction(a,h.action);u.push(f)}}}if(!t||"object"!=typeof t)throw new Error("Monarch: expecting a language definition object");var o={};o.languageId=e,o.noThrow=!1,o.maxStack=100,o.start=s(t.start),o.ignoreCase=r(t.ignoreCase,!1),o.tokenPostfix=s(t.tokenPostfix,"."+o.languageId),o.defaultToken=s(t.defaultToken,"source",function(){i.throwError(o,"the 'defaultToken' must be a string")}),o.usesEmbedded=!1;var a=t;a.languageId=e,a.ignoreCase=o.ignoreCase,a.noThrow=o.noThrow,a.usesEmbedded=o.usesEmbedded,a.stateNames=t.tokenizer,a.defaultToken=o.defaultToken,t.tokenizer&&"object"==typeof t.tokenizer||i.throwError(o,"a language definition must define the 'tokenizer' attribute as an object"),o.tokenizer=[];var u;for(u in t.tokenizer)if(t.tokenizer.hasOwnProperty(u)){o.start||(o.start=u);var l=t.tokenizer[u];o.tokenizer[u]=new Array,n("tokenizer."+u,o.tokenizer[u],l)}o.usesEmbedded=a.usesEmbedded,t.brackets?Array.isArray(t.brackets)||i.throwError(o,"the 'brackets' attribute must be defined as an array"):t.brackets=[{open:"{",close:"}",token:"delimiter.curly"},{open:"[",close:"]",token:"delimiter.square"},{open:"(",close:")",token:"delimiter.parenthesis"},{open:"<",close:">",token:"delimiter.angle"}];var c=[];for(var h in t.brackets)if(t.brackets.hasOwnProperty(h)){var p=t.brackets[h];p&&Array.isArray(p)&&3===p.length&&(p={token:p[2],open:p[0],close:p[1]}),p.open===p.close&&i.throwError(o,"open and close brackets in a 'brackets' attribute must be different: "+p.open+"\n hint: use the 'bracket' attribute if matching on equal brackets is required."),"string"==typeof p.open&&"string"==typeof p.token?c.push({token:s(p.token)+o.tokenPostfix,open:i.fixCase(o,s(p.open)),close:i.fixCase(o,s(p.close))}):i.throwError(o,"every element in the 'brackets' array must be a '{open,close,token}' object or array")}return o.brackets=c,o.noThrow=!0,o}}),define(d[276],h([1,0,17,145,97,68]),function(e,t,n,i,o,r){"use strict";function s(e,t){if(!t)return null;t=i.fixCase(e,t);for(var n=e.brackets,o=0;o=this._maxCacheDepth)return new u(e,t);var n=u.getStackElementId(e);n.length>0&&(n+="|"),n+=t;var i=this._entries[n];return i||(i=new u(e,t),this._entries[n]=i,i)},e._INSTANCE=new e(5),e}(),u=function(){function e(e,t){this.parent=e,this.state=t,this.depth=(this.parent?this.parent.depth:0)+1}return e.getStackElementId=function(e){for(var t="";null!==e;)t.length>0&&(t+="|"),t+=e.state,e=e.parent;return t},e._equals=function(e,t){for(;null!==e&&null!==t;){if(e===t)return!0;if(e.state!==t.state)return!1;e=e.parent,t=t.parent}return null===e&&null===t},e.prototype.equals=function(t){return e._equals(this,t)},e.prototype.push=function(e){return a.create(this,e)},e.prototype.pop=function(){return this.parent},e.prototype.popall=function(){for(var e=this;e.parent;)e=e.parent;return e},e.prototype.switchTo=function(e){return a.create(this.parent,e)},e}(),l=function(){function e(e,t){this.modeId=e,this.state=t}return e.prototype.equals=function(e){return this.modeId===e.modeId&&this.state.equals(e.state)},e.prototype.clone=function(){return this.state.clone()===this.state?this:new e(this.modeId,this.state)},e}(),c=function(){function e(e){this._maxCacheDepth=e,this._entries=Object.create(null)}return e.create=function(e,t){return this._INSTANCE.create(e,t)},e.prototype.create=function(e,t){if(null!==t)return new d(e,t);if(null!==e&&e.depth>=this._maxCacheDepth)return new d(e,t);var n=u.getStackElementId(e),i=this._entries[n];return i||(i=new d(e,null),this._entries[n]=i,i)},e._INSTANCE=new e(5),e}(),d=function(){function e(e,t){this.stack=e,this.embeddedModeData=t}return e.prototype.clone=function(){return(this.embeddedModeData?this.embeddedModeData.clone():null)===this.embeddedModeData?this:c.create(this.stack,this.embeddedModeData)},e.prototype.equals=function(t){return t instanceof e&&(!!this.stack.equals(t.stack)&&(null===this.embeddedModeData&&null===t.embeddedModeData||null!==this.embeddedModeData&&null!==t.embeddedModeData&&this.embeddedModeData.equals(t.embeddedModeData)))},e}(),h=Object.hasOwnProperty,p=function(){function e(){this._tokens=[],this._language=null,this._lastTokenType=null,this._lastTokenLanguage=null}return e.prototype.enterMode=function(e,t){this._language=t},e.prototype.emit=function(e,t){this._lastTokenType===t&&this._lastTokenLanguage===this._language||(this._lastTokenType=t,this._lastTokenLanguage=this._language,this._tokens.push(new o.Token(e,t,this._language)))},e.prototype.nestedModeTokenize=function(e,t,i){var o=t.modeId,r=t.state,s=n.TokenizationRegistry.get(o);if(!s)return this.enterMode(i,o),this.emit(i,""),r;var a=s.tokenize(e,r,i);return this._tokens=this._tokens.concat(a.tokens),this._lastTokenType=null,this._lastTokenLanguage=null,this._language=null,a.endState},e.prototype.finalize=function(e){return new o.TokenizationResult(this._tokens,e)},e}(),f=function(){function e(e,t){this._modeService=e,this._theme=t,this._prependTokens=null,this._tokens=[],this._currentLanguageId=0,this._lastTokenMetadata=0}return e.prototype.enterMode=function(e,t){this._currentLanguageId=this._modeService.getLanguageIdentifier(t).id},e.prototype.emit=function(e,t){var n=this._theme.match(this._currentLanguageId,t);this._lastTokenMetadata!==n&&(this._lastTokenMetadata=n,this._tokens.push(e),this._tokens.push(n))},e._merge=function(e,t,n){var i=null!==e?e.length:0,o=t.length,r=null!==n?n.length:0;if(0===i&&0===o&&0===r)return new Uint32Array(0);if(0===i&&0===o)return n;if(0===o&&0===r)return e;var s=new Uint32Array(i+o+r);null!==e&&s.set(e);for(var a=0;a0&&i.nestedModeTokenize(s,t.embeddedModeData,n);var a=e.substring(o);return this._myTokenize(a,t,n+o,i)},e.prototype._myTokenize=function(e,t,n,o){o.enterMode(n,this._modeId);for(var r=e.length,a=t.embeddedModeData,u=t.stack,l=0,d=null,p=null,f=null,g=null;l=r)break;var L=this._lexer.tokenizer[y];L||(L=i.findRules(this._lexer,y))||i.throwError(this._lexer,"tokenizer state is not defined: "+y);W=e.substr(l);for(var x in L)if(h.call(L,x)){var N=L[x];if((0===l||!N.matchOnlyAtLineStart)&&(C=W.match(N.regex))){b=C[0],w=N.action;break}}}for(C||(C=[""],b=""),w||(l=this._lexer.maxStack?i.throwError(this._lexer,"maximum tokenizer stack size reached: ["+u.state+","+u.parent.state+",...]"):u=u.push(y);else if("@pop"===w.next)u.depth<=1?i.throwError(this._lexer,"trying to pop an empty stack in rule: "+S.name):u=u.pop();else if("@popall"===w.next)u=u.popall();else{var T=i.substituteMatches(this._lexer,w.next,b,C,y);"@"===T[0]&&(T=T.substr(1)),i.findRules(this._lexer,T)?u=u.push(T):i.throwError(this._lexer,"trying to set a next state '"+T+"' that is undefined in rule: "+S.name)}w.log&&"string"==typeof w.log&&i.log(this._lexer,this._lexer.languageId+": "+i.substituteMatches(this._lexer,w.log,b,C,y))}if(null===M&&i.throwError(this._lexer,"lexer rule has no well-defined action in rule: "+S.name),Array.isArray(M)){d&&d.length>0&&i.throwError(this._lexer,"groups cannot be nested: "+S.name),C.length!==M.length+1&&i.throwError(this._lexer,"matched number of groups does not match the number of actions in rule: "+S.name);for(var k=0,I=1;I=n.actionsList.children.length?n.actionsList.appendChild(i):n.actionsList.insertBefore(i,n.actionsList.children[r++]),n.items.push(s)})},t.prototype.pull=function(e){e>=0&&e=0){var n=void 0;e.equals(17)?n=(t+1)%s.length:e.equals(15)&&(n=0===t?s.length-1:t-1),e.equals(9)?s[t].blur():n>=0&&s[n].focus(),i.EventHelper.stop(e,!0)}}}),this.setInputWidth();var u=document.createElement("div");u.className="controls",u.appendChild(this.caseSensitive.domNode),u.appendChild(this.wholeWords.domNode),u.appendChild(this.regex.domNode),this.domNode.appendChild(u)},t.prototype.validate=function(){this.inputBox.validate()},t.prototype.showMessage=function(e){this.inputBox.showMessage(e)},t.prototype.clearMessage=function(){this.inputBox.hideMessage()},t.prototype.clearValidation=function(){this.inputBox.hideMessage()},t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.OPTION_CHANGE="optionChange",t}(r.Widget);t.FindInput=l}),define(d[290],h([6,8]),function(e,t){return e.create("vs/base/common/keybindingLabels",t)}),define(d[155],h([1,0,290]),function(e,t,n){"use strict";function i(e,t,n){if(null===t)return"";var i=[];return e.ctrlKey&&i.push(n.ctrlKey),e.shiftKey&&i.push(n.shiftKey),e.altKey&&i.push(n.altKey),e.metaKey&&i.push(n.metaKey),i.push(t),i.join(n.separator)}function o(e,t,n,o,r){var s=i(e,t,r);return null!==o&&(s+=" ",s+=i(n,o,r)),s}Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t,n){void 0===n&&(n=t),this.modifierLabels=[null],this.modifierLabels[2]=e,this.modifierLabels[1]=t,this.modifierLabels[3]=n}return e.prototype.toLabel=function(e,t,n,i,r){return null===t&&null===i?null:o(e,t,n,i,this.modifierLabels[r])},e}();t.ModifierLabelProvider=r,t.UILabelProvider=new r({ctrlKey:"⌃",shiftKey:"⇧",altKey:"⌥",metaKey:"⌘",separator:""},{ctrlKey:n.localize(0,null),shiftKey:n.localize(1,null),altKey:n.localize(2,null),metaKey:n.localize(3,null),separator:"+"}),t.AriaLabelProvider=new r({ctrlKey:n.localize(4,null),shiftKey:n.localize(5,null),altKey:n.localize(6,null),metaKey:n.localize(7,null),separator:"+"},{ctrlKey:n.localize(8,null),shiftKey:n.localize(9,null),altKey:n.localize(10,null),metaKey:n.localize(11,null),separator:"+"}),t.ElectronAcceleratorLabelProvider=new r({ctrlKey:"Ctrl",shiftKey:"Shift",altKey:"Alt",metaKey:"Cmd",separator:"+"},{ctrlKey:"Ctrl",shiftKey:"Shift",altKey:"Alt",metaKey:"Super",separator:"+"}),t.UserSettingsLabelProvider=new r({ctrlKey:"ctrl",shiftKey:"shift",altKey:"alt",metaKey:"cmd",separator:"+"},{ctrlKey:"ctrl",shiftKey:"shift",altKey:"alt",metaKey:"win",separator:"+"},{ctrlKey:"ctrl",shiftKey:"shift",altKey:"alt",metaKey:"meta",separator:"+"})}),define(d[292],h([1,0,26,155,4,212]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=o.$,s=function(){function e(e,t){this.os=t,this.domNode=o.append(e,r(".monaco-keybinding")),this.didEverRender=!1,e.appendChild(this.domNode)}return Object.defineProperty(e.prototype,"element",{get:function(){return this.domNode},enumerable:!0,configurable:!0}),e.prototype.set=function(t,n){this.didEverRender&&this.keybinding===t&&e.areSame(this.matches,n)||(this.keybinding=t,this.matches=n,this.render())},e.prototype.render=function(){if(o.clearNode(this.domNode),this.keybinding){var e=this.keybinding.getParts(),t=e[0],n=e[1];t&&this.renderPart(this.domNode,t,this.matches?this.matches.firstPart:null),n&&(o.append(this.domNode,r("span.monaco-keybinding-key-chord-separator",null," ")),this.renderPart(this.domNode,n,this.matches?this.matches.chordPart:null)),this.domNode.title=this.keybinding.getAriaLabel()}this.didEverRender=!0},e.prototype.renderPart=function(e,t,n){var o=i.UILabelProvider.modifierLabels[this.os];t.ctrlKey&&this.renderKey(e,o.ctrlKey,n&&n.ctrlKey,o.separator),t.shiftKey&&this.renderKey(e,o.shiftKey,n&&n.shiftKey,o.separator),t.altKey&&this.renderKey(e,o.altKey,n&&n.altKey,o.separator),t.metaKey&&this.renderKey(e,o.metaKey,n&&n.metaKey,o.separator);var r=t.keyLabel;r&&this.renderKey(e,r,n&&n.keyCode,"")},e.prototype.renderKey=function(e,t,n,i){o.append(e,r("span.monaco-keybinding-key"+(n?".highlight":""),null,t)),i&&o.append(e,r("span.monaco-keybinding-key-separator",null,i))},e.prototype.dispose=function(){this.keybinding=null},e.areSame=function(e,t){return e===t||!e&&!t||!!e&&!!t&&n.equals(e.firstPart,t.firstPart)&&n.equals(e.chordPart,t.chordPart)},e}();t.KeybindingLabel=s}),define(d[293],h([6,8]),function(e,t){return e.create("vs/base/common/severity",t)}),define(d[36],h([1,0,293,9]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o;!function(e){e[e.Ignore=0]="Ignore",e[e.Info=1]="Info",e[e.Warning=2]="Warning",e[e.Error=3]="Error"}(o||(o={})),function(e){var t="error",o="warning",r="warn",s="info",a=Object.create(null);a[e.Error]=n.localize(0,null),a[e.Warning]=n.localize(1,null),a[e.Info]=n.localize(2,null),e.fromValue=function(n){return n?i.equalsIgnoreCase(t,n)?e.Error:i.equalsIgnoreCase(o,n)||i.equalsIgnoreCase(r,n)?e.Warning:i.equalsIgnoreCase(s,n)?e.Info:e.Ignore:e.Ignore},e.toString=function(e){return a[e]||i.empty},e.compare=function(e,t){return t-e}}(o||(o={})),t.default=o}),define(d[295],h([6,8]),function(e,t){return e.create("vs/base/parts/quickopen/browser/quickOpenModel",t)}),define(d[120],h([1,0,295,7,29,81,9,45,162,229,75,110,4,292,15]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var m=0,v=function(){function e(){}return e.getLabel=function(e){return e.getLabel()},e.getResourcePath=function(e){var t=e.getResource();return t&&t.fsPath},e}(),_=function(){function e(e){void 0===e&&(e=[]),this.id=(m++).toString(),this.labelHighlights=e,this.descriptionHighlights=[]}return e.prototype.getId=function(){return this.id},e.prototype.getLabel=function(){return null},e.prototype.getLabelOptions=function(){return null},e.prototype.getAriaLabel=function(){return this.getLabel()},e.prototype.getDetail=function(){return null},e.prototype.getIcon=function(){return null},e.prototype.getDescription=function(){return null},e.prototype.getKeybinding=function(){return null},e.prototype.getResource=function(){return null},e.prototype.isHidden=function(){return this.hidden},e.prototype.setHidden=function(e){this.hidden=e},e.prototype.setHighlights=function(e,t,n){this.labelHighlights=e,this.descriptionHighlights=t,this.detailHighlights=n},e.prototype.getHighlights=function(){return[this.labelHighlights,this.descriptionHighlights,this.detailHighlights]},e.prototype.run=function(e,t){return!1},e.compare=function(e,t,n){var i=e.getHighlights()[0]||[],o=t.getHighlights()[0]||[];if(i.length&&!o.length)return-1;if(!i.length&&o.length)return 1;var r=e.getLabel(),s=t.getLabel();if(r===s){var a=e.getResource(),u=t.getResource();a&&u&&(r=a.fsPath,s=u.fsPath)}return l.compareAnything(r,s,n)},e.compareByScore=function(e,t,n,i,o){return l.compareByScore(e,t,v,n,i,o)},e.highlight=function(e,t,n){void 0===n&&(n=!1);var i=[],o=[],u=s.stripWildcards(t),l=e.getLabel(),c=e.getDescription();if(e.getResource()){var d=e.getResource().fsPath;if(t.length===d.length&&t.toLowerCase()===d.toLowerCase())i.push({start:0,end:l.length}),o.push({start:0,end:c.length});else if(n||t.indexOf(a.nativeSep)>=0){var h=r.matchesFuzzy(t,l,n);if(h)i=h;else{var p=c?c+a.nativeSep:"",f=p.length,g=r.matchesFuzzy(t,p+l,n);g||t===u||(g=r.matchesFuzzy(u,p+l,n)),g&&g.forEach(function(e){e.startf?(i.push({start:0,end:e.end-f}),o.push({start:e.start,end:f})):e.start>=f?i.push({start:e.start-f,end:e.end-f}):o.push(e)})}}else i=r.matchesFuzzy(t,l)}else i=r.matchesFuzzy(t,l);return{labelHighlights:i,descriptionHighlights:o}},e.prototype.isFile=function(){return!1},e}();t.QuickOpenEntry=_;var y=function(e){function t(t,n,i){var o=e.call(this)||this;return o.entry=t,o.groupLabel=n,o.withBorder=i,o}return f(t,e),t.prototype.getGroupLabel=function(){return this.groupLabel},t.prototype.setGroupLabel=function(e){this.groupLabel=e},t.prototype.showBorder=function(){return this.withBorder},t.prototype.setShowBorder=function(e){this.withBorder=e},t.prototype.getLabel=function(){return this.entry?this.entry.getLabel():e.prototype.getLabel.call(this)},t.prototype.getLabelOptions=function(){return this.entry?this.entry.getLabelOptions():e.prototype.getLabelOptions.call(this)},t.prototype.getAriaLabel=function(){return this.entry?this.entry.getAriaLabel():e.prototype.getAriaLabel.call(this)},t.prototype.getDetail=function(){return this.entry?this.entry.getDetail():e.prototype.getDetail.call(this)},t.prototype.getResource=function(){return this.entry?this.entry.getResource():e.prototype.getResource.call(this)},t.prototype.getIcon=function(){return this.entry?this.entry.getIcon():e.prototype.getIcon.call(this)},t.prototype.getDescription=function(){return this.entry?this.entry.getDescription():e.prototype.getDescription.call(this)},t.prototype.getEntry=function(){return this.entry},t.prototype.getHighlights=function(){return this.entry?this.entry.getHighlights():e.prototype.getHighlights.call(this)},t.prototype.isHidden=function(){return this.entry?this.entry.isHidden():e.prototype.isHidden.call(this)},t.prototype.setHighlights=function(t,n,i){this.entry?this.entry.setHighlights(t,n,i):e.prototype.setHighlights.call(this,t,n,i)},t.prototype.setHidden=function(t){this.entry?this.entry.setHidden(t):e.prototype.setHidden.call(this,t)},t.prototype.run=function(t,n){return this.entry?this.entry.run(t,n):e.prototype.run.call(this,t,n)},t}(_);t.QuickOpenEntryGroup=y;var C=function(){function e(){}return e.prototype.hasActions=function(e,t){return!1},e.prototype.getActions=function(e,t){return i.TPromise.as(null)},e.prototype.hasSecondaryActions=function(e,t){return!1},e.prototype.getSecondaryActions=function(e,t){return i.TPromise.as(null)},e.prototype.getActionItem=function(e,t,n){return null},e}(),b=function(){function e(e,t){void 0===e&&(e=new C),void 0===t&&(t=null),this.actionProvider=e,this.actionRunner=t}return e.prototype.getHeight=function(e){return e.getDetail()?44:22},e.prototype.getTemplateId=function(e){return e instanceof y?"quickOpenEntryGroup":"quickOpenEntry"},e.prototype.renderTemplate=function(e,t,n){var i=document.createElement("div");h.addClass(i,"sub-content"),t.appendChild(i);var o=h.$(".quick-open-row"),r=h.$(".quick-open-row"),s=h.$(".quick-open-entry",null,o,r);i.appendChild(s);var a=document.createElement("span");o.appendChild(a);var l=new u.IconLabel(o,{supportHighlights:!0}),f=document.createElement("span");o.appendChild(f),h.addClass(f,"quick-open-entry-description");var m=new d.HighlightedLabel(f),v=document.createElement("span");o.appendChild(v),h.addClass(v,"quick-open-entry-keybinding");var _=new p.KeybindingLabel(v,g.OS),y=document.createElement("div");r.appendChild(y),h.addClass(y,"quick-open-entry-meta");var C,b=new d.HighlightedLabel(y);"quickOpenEntryGroup"===e&&(C=document.createElement("div"),h.addClass(C,"results-group"),t.appendChild(C)),h.addClass(t,"actions");var w=document.createElement("div");return h.addClass(w,"primary-action-bar"),t.appendChild(w),{container:t,entry:s,icon:a,label:l,detail:b,description:m,keybinding:_,group:C,actionBar:new c.ActionBar(w,{actionRunner:this.actionRunner})}},e.prototype.renderElement=function(e,t,n,i){var o=n;if(this.actionProvider.hasActions(null,e)?h.addClass(o.container,"has-actions"):h.removeClass(o.container,"has-actions"),o.actionBar.context=e,this.actionProvider.getActions(null,e).then(function(e){o.actionBar.isEmpty()&&e&&e.length>0?o.actionBar.push(e,{icon:!0,label:!1}):o.actionBar.isEmpty()||e&&0!==e.length||o.actionBar.clear()}),e instanceof y&&e.getGroupLabel()?h.addClass(o.container,"has-group-label"):h.removeClass(o.container,"has-group-label"),e instanceof y){var r=e,s=n;r.showBorder()?(h.addClass(s.container,"results-group-separator"),s.container.style.borderTopColor=i.pickerGroupBorder.toString()):(h.removeClass(s.container,"results-group-separator"),s.container.style.borderTopColor=null);var a=r.getGroupLabel()||"";s.group.textContent=a,s.group.style.color=i.pickerGroupForeground.toString()}if(e instanceof _){var u=e.getHighlights(),l=u[0],c=u[1],d=u[2],p=e.getIcon()?"quick-open-entry-icon "+e.getIcon():"";o.icon.className=p;var f=e.getLabelOptions()||Object.create(null);f.matches=l||[],o.label.setValue(e.getLabel(),null,f),o.detail.set(e.getDetail(),d),o.description.set(e.getDescription(),c||[]),o.description.element.title=e.getDescription(),o.keybinding.set(e.getKeybinding(),null)}},e.prototype.disposeTemplate=function(e,t){var n=t;n.actionBar.dispose(),n.actionBar=null,n.container=null,n.entry=null,n.description.dispose(),n.description=null,n.keybinding.dispose(),n.keybinding=null,n.detail.dispose(),n.detail=null,n.group=null,n.icon=null,n.label.dispose(),n.label=null},e}(),w=function(){function e(e,t){void 0===e&&(e=[]),void 0===t&&(t=new C),this._entries=e,this._dataSource=this,this._renderer=new b(t),this._filter=this,this._runner=this,this._accessibilityProvider=this}return Object.defineProperty(e.prototype,"entries",{get:function(){return this._entries},set:function(e){this._entries=e},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"dataSource",{get:function(){return this._dataSource},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"renderer",{get:function(){return this._renderer},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"filter",{get:function(){return this._filter},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"runner",{get:function(){return this._runner},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"accessibilityProvider",{get:function(){return this._accessibilityProvider},enumerable:!0,configurable:!0}),e.prototype.addEntries=function(e){o.isArray(e)&&(this._entries=this._entries.concat(e))},e.prototype.setEntries=function(e){o.isArray(e)&&(this._entries=e)},e.prototype.getEntries=function(e){return e?this._entries.filter(function(e){return!e.isHidden()}):this._entries},e.prototype.getId=function(e){return e.getId()},e.prototype.getLabel=function(e){return e.getLabel()},e.prototype.getAriaLabel=function(e){return e.getAriaLabel()?n.localize(0,null,e.getAriaLabel()):n.localize(1,null)},e.prototype.isVisible=function(e){return!e.isHidden()},e.prototype.run=function(e,t,n){return e.run(t,n)},e}();t.QuickOpenModel=w}),define(d[297],h([6,8]),function(e,t){return e.create("vs/base/parts/quickopen/browser/quickOpenWidget",t)}),define(d[298],h([6,8]),function(e,t){return e.create("vs/base/parts/tree/browser/treeDefaults",t)}),define(d[128],h([1,0,298,7,53,15,10,4,40]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l;!function(e){e[e.ON_MOUSE_DOWN=0]="ON_MOUSE_DOWN",e[e.ON_MOUSE_UP=1]="ON_MOUSE_UP"}(l=t.ClickBehavior||(t.ClickBehavior={}));var c=function(){function e(){this._arr=[]}return e.prototype.set=function(e,t){this._arr.push({keybinding:u.createKeybinding(e,r.OS),callback:t})},e.prototype.dispatch=function(e){for(var t=this._arr.length-1;t>=0;t--){var n=this._arr[t];if(e.equals(n.keybinding))return n.callback}return null},e}();t.KeybindingDispatcher=c;var d=function(){function e(e){void 0===e&&(e={clickBehavior:l.ON_MOUSE_UP,keyboardSupport:!0});var t=this;this.options=e,this.downKeyBindingDispatcher=new c,this.upKeyBindingDispatcher=new c,("boolean"!=typeof e.keyboardSupport||e.keyboardSupport)&&(this.downKeyBindingDispatcher.set(16,function(e,n){return t.onUp(e,n)}),this.downKeyBindingDispatcher.set(18,function(e,n){return t.onDown(e,n)}),this.downKeyBindingDispatcher.set(15,function(e,n){return t.onLeft(e,n)}),this.downKeyBindingDispatcher.set(17,function(e,n){return t.onRight(e,n)}),r.isMacintosh&&(this.downKeyBindingDispatcher.set(2064,function(e,n){return t.onLeft(e,n)}),this.downKeyBindingDispatcher.set(300,function(e,n){return t.onDown(e,n)}),this.downKeyBindingDispatcher.set(302,function(e,n){return t.onUp(e,n)})),this.downKeyBindingDispatcher.set(11,function(e,n){return t.onPageUp(e,n)}),this.downKeyBindingDispatcher.set(12,function(e,n){return t.onPageDown(e,n)}),this.downKeyBindingDispatcher.set(14,function(e,n){return t.onHome(e,n)}),this.downKeyBindingDispatcher.set(13,function(e,n){return t.onEnd(e,n)}),this.downKeyBindingDispatcher.set(10,function(e,n){return t.onSpace(e,n)}),this.downKeyBindingDispatcher.set(9,function(e,n){return t.onEscape(e,n)}),this.upKeyBindingDispatcher.set(3,this.onEnter.bind(this)),this.upKeyBindingDispatcher.set(2051,this.onEnter.bind(this)))}return e.prototype.onMouseDown=function(e,t,n,i){if(void 0===i&&(i="mouse"),this.options.clickBehavior===l.ON_MOUSE_DOWN&&(n.leftButton||n.middleButton)){if(n.target){if(n.target.tagName&&"input"===n.target.tagName.toLowerCase())return!1;if(a.findParentWithClass(n.target,"monaco-action-bar","row"))return!1}return this.onLeftClick(e,t,n,i)}return!1},e.prototype.onClick=function(e,t,n){return r.isMacintosh&&n.ctrlKey?(n.preventDefault(),n.stopPropagation(),!1):(!n.target||!n.target.tagName||"input"!==n.target.tagName.toLowerCase())&&((this.options.clickBehavior!==l.ON_MOUSE_DOWN||!n.leftButton&&!n.middleButton)&&this.onLeftClick(e,t,n))},e.prototype.onLeftClick=function(e,t,n,i){void 0===i&&(i="mouse");var o={origin:i,originalEvent:n};return e.getInput()===t?(e.clearFocus(o),e.clearSelection(o)):(n&&n.browserEvent&&"mousedown"===n.browserEvent.type||n.preventDefault(),n.stopPropagation(),e.DOMFocus(),e.setSelection([t],o),e.setFocus(t,o),e.isExpanded(t)?e.collapse(t).done(null,s.onUnexpectedError):e.expand(t).done(null,s.onUnexpectedError)),!0},e.prototype.onContextMenu=function(e,t,n){return(!n.target||!n.target.tagName||"input"!==n.target.tagName.toLowerCase())&&(n&&(n.preventDefault(),n.stopPropagation()),!1)},e.prototype.onTap=function(e,t,n){var i=n.initialTarget;return(!i||!i.tagName||"input"!==i.tagName.toLowerCase())&&this.onLeftClick(e,t,n,"touch")},e.prototype.onKeyDown=function(e,t){return this.onKey(this.downKeyBindingDispatcher,e,t)},e.prototype.onKeyUp=function(e,t){return this.onKey(this.upKeyBindingDispatcher,e,t)},e.prototype.onKey=function(e,t,n){var i=e.dispatch(n.toKeybinding());return!(!i||!i(t,n))&&(n.preventDefault(),n.stopPropagation(),!0)},e.prototype.onUp=function(e,t){var n={origin:"keyboard",originalEvent:t};return e.getHighlight()?e.clearHighlight(n):(e.focusPrevious(1,n),e.reveal(e.getFocus()).done(null,s.onUnexpectedError)),!0},e.prototype.onPageUp=function(e,t){var n={origin:"keyboard",originalEvent:t};return e.getHighlight()?e.clearHighlight(n):(e.focusPreviousPage(n),e.reveal(e.getFocus()).done(null,s.onUnexpectedError)),!0},e.prototype.onDown=function(e,t){var n={origin:"keyboard",originalEvent:t};return e.getHighlight()?e.clearHighlight(n):(e.focusNext(1,n),e.reveal(e.getFocus()).done(null,s.onUnexpectedError)),!0},e.prototype.onPageDown=function(e,t){var n={origin:"keyboard",originalEvent:t};return e.getHighlight()?e.clearHighlight(n):(e.focusNextPage(n),e.reveal(e.getFocus()).done(null,s.onUnexpectedError)),!0},e.prototype.onHome=function(e,t){var n={origin:"keyboard",originalEvent:t};return e.getHighlight()?e.clearHighlight(n):(e.focusFirst(n),e.reveal(e.getFocus()).done(null,s.onUnexpectedError)),!0},e.prototype.onEnd=function(e,t){var n={origin:"keyboard",originalEvent:t};return e.getHighlight()?e.clearHighlight(n):(e.focusLast(n),e.reveal(e.getFocus()).done(null,s.onUnexpectedError)),!0},e.prototype.onLeft=function(e,t){var n={origin:"keyboard",originalEvent:t};if(e.getHighlight())e.clearHighlight(n);else{var i=e.getFocus();e.collapse(i).then(function(t){if(i&&!t)return e.focusParent(n),e.reveal(e.getFocus())}).done(null,s.onUnexpectedError)}return!0},e.prototype.onRight=function(e,t){var n={origin:"keyboard",originalEvent:t};if(e.getHighlight())e.clearHighlight(n);else{var i=e.getFocus();e.expand(i).then(function(t){if(i&&!t)return e.focusFirstChild(n),e.reveal(e.getFocus())}).done(null,s.onUnexpectedError)}return!0},e.prototype.onEnter=function(e,t){var n={origin:"keyboard",originalEvent:t};if(e.getHighlight())return!1;var i=e.getFocus();return i&&e.setSelection([i],n),!0},e.prototype.onSpace=function(e,t){if(e.getHighlight())return!1;var n=e.getFocus();return n&&e.toggleExpansion(n),!0},e.prototype.onEscape=function(e,t){var n={origin:"keyboard",originalEvent:t};return e.getHighlight()?(e.clearHighlight(n),!0):e.getSelection().length?(e.clearSelection(n),!0):!!e.getFocus()&&(e.clearFocus(n),!0)},e}();t.DefaultController=d;var h=function(){function e(){}return e.prototype.getDragURI=function(e,t){return null},e.prototype.onDragStart=function(e,t,n){},e.prototype.onDragOver=function(e,t,n,i){return null},e.prototype.drop=function(e,t,n,i){},e}();t.DefaultDragAndDrop=h;var p=function(){function e(){}return e.prototype.isVisible=function(e,t){return!0},e}();t.DefaultFilter=p;var g=function(){function e(){}return e.prototype.compare=function(e,t,n){return 0},e}();t.DefaultSorter=g;var m=function(){function e(){}return e.prototype.getAriaLabel=function(e,t){return null},e}();t.DefaultAccessibilityProvider=m;var v=function(e){function t(t,i){var o=e.call(this,"vs.tree.collapse",n.localize(0,null),"monaco-tree-action collapse-all",i)||this;return o.viewer=t,o}return f(t,e),t.prototype.run=function(e){return this.viewer.getHighlight()?i.TPromise.as(null):(this.viewer.collapseAll(),this.viewer.clearSelection(),this.viewer.clearFocus(),this.viewer.DOMFocus(),this.viewer.focusFirst(),i.TPromise.as(null))},t}(o.Action);t.CollapseAllAction=v}),define(d[161],h([1,0,128,38,466,238,107,11,3,32,26,261]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var d=function(){return function(e,t,i){if(void 0===i&&(i={}),this.tree=e,this.configuration=t,this.options=i,!t.dataSource)throw new Error("You must provide a Data Source to the tree.");this.dataSource=t.dataSource,this.renderer=t.renderer,this.controller=t.controller||new n.DefaultController({clickBehavior:n.ClickBehavior.ON_MOUSE_UP,keyboardSupport:"boolean"!=typeof i.keyboardSupport||i.keyboardSupport}),this.dnd=t.dnd||new n.DefaultDragAndDrop,this.filter=t.filter||new n.DefaultFilter,this.sorter=t.sorter||null,this.accessibilityProvider=t.accessibilityProvider||new n.DefaultAccessibilityProvider}}();t.TreeContext=d;var h={listFocusBackground:l.Color.fromHex("#073655"),listActiveSelectionBackground:l.Color.fromHex("#0E639C"),listActiveSelectionForeground:l.Color.fromHex("#FFFFFF"),listFocusAndSelectionBackground:l.Color.fromHex("#094771"),listFocusAndSelectionForeground:l.Color.fromHex("#FFFFFF"),listInactiveSelectionBackground:l.Color.fromHex("#3F3F46"),listHoverBackground:l.Color.fromHex("#2A2D2E"),listDropBackground:l.Color.fromHex("#383B3D")},p=function(e){function t(t,n,i){void 0===i&&(i={});var s=e.call(this)||this;return s.toDispose=[],s._onDispose=new a.Emitter,s._onHighlightChange=new a.Emitter,s.toDispose.push(s._onDispose,s._onHighlightChange),s.container=t,s.configuration=n,s.options=i,c.mixin(s.options,h,!1),s.options.twistiePixels="number"==typeof s.options.twistiePixels?s.options.twistiePixels:32,s.options.showTwistie=!1!==s.options.showTwistie,s.options.indentPixels="number"==typeof s.options.indentPixels?s.options.indentPixels:12,s.options.alwaysFocused=!0===s.options.alwaysFocused,s.options.useShadows=!1!==s.options.useShadows,s.options.paddingOnRow=!1!==s.options.paddingOnRow,s.context=new d(s,n,i),s.model=new o.TreeModel(s.context),s.view=new r.TreeView(s.context,s.container),s.view.setModel(s.model),s.addEmitter(s.model),s.addEmitter(s.view),s.toDispose.push(s.model.addListener("highlight",function(){return s._onHighlightChange.fire()})),s}return f(t,e),t.prototype.style=function(e){this.view.applyStyles(e)},Object.defineProperty(t.prototype,"onDOMFocus",{get:function(){return this.view&&this.view.onDOMFocus},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onDOMBlur",{get:function(){return this.view&&this.view.onDOMBlur},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onHighlightChange",{get:function(){return this._onHighlightChange&&this._onHighlightChange.event},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"onDispose",{get:function(){return this._onDispose&&this._onDispose.event},enumerable:!0,configurable:!0}),t.prototype.getHTMLElement=function(){return this.view.getHTMLElement()},t.prototype.layout=function(e){this.view.layout(e)},t.prototype.DOMFocus=function(){this.view.focus()},t.prototype.isDOMFocused=function(){return this.view.isFocused()},t.prototype.DOMBlur=function(){this.view.blur()},t.prototype.onVisible=function(){this.view.onVisible()},t.prototype.onHidden=function(){this.view.onHidden()},t.prototype.setInput=function(e){return this.model.setInput(e)},t.prototype.getInput=function(){return this.model.getInput()},t.prototype.refresh=function(e,t){return void 0===e&&(e=null),void 0===t&&(t=!0),this.model.refresh(e,t)},t.prototype.expand=function(e){return this.model.expand(e)},t.prototype.expandAll=function(e){return this.model.expandAll(e)},t.prototype.collapse=function(e,t){return void 0===t&&(t=!1),this.model.collapse(e,t)},t.prototype.collapseAll=function(e,t){return void 0===e&&(e=null),void 0===t&&(t=!1),this.model.collapseAll(e,t)},t.prototype.toggleExpansion=function(e){return this.model.toggleExpansion(e)},t.prototype.toggleExpansionAll=function(e){return this.model.toggleExpansionAll(e)},t.prototype.isExpanded=function(e){return this.model.isExpanded(e)},t.prototype.getExpandedElements=function(){return this.model.getExpandedElements()},t.prototype.reveal=function(e,t){return void 0===t&&(t=null),this.model.reveal(e,t)},t.prototype.getRelativeTop=function(e){var t=this.model.getItem(e);return this.view.getRelativeTop(t)},t.prototype.getScrollPosition=function(){return this.view.getScrollPosition()},t.prototype.setScrollPosition=function(e){this.view.setScrollPosition(e)},t.prototype.getContentHeight=function(){return this.view.getTotalHeight()},t.prototype.setHighlight=function(e,t){this.model.setHighlight(e,t)},t.prototype.getHighlight=function(){return this.model.getHighlight()},t.prototype.isHighlighted=function(e){return this.model.isFocused(e)},t.prototype.clearHighlight=function(e){this.model.setHighlight(null,e)},t.prototype.select=function(e,t){this.model.select(e,t)},t.prototype.selectRange=function(e,t,n){this.model.selectRange(e,t,n)},t.prototype.deselectRange=function(e,t,n){this.model.deselectRange(e,t,n)},t.prototype.selectAll=function(e,t){this.model.selectAll(e,t)},t.prototype.deselect=function(e,t){this.model.deselect(e,t)},t.prototype.deselectAll=function(e,t){this.model.deselectAll(e,t)},t.prototype.setSelection=function(e,t){this.model.setSelection(e,t)},t.prototype.toggleSelection=function(e,t){this.model.toggleSelection(e,t)},t.prototype.isSelected=function(e){return this.model.isSelected(e)},t.prototype.getSelection=function(){return this.model.getSelection()},t.prototype.clearSelection=function(e){this.model.setSelection([],e)},t.prototype.selectNext=function(e,t,n){this.model.selectNext(e,t,n)},t.prototype.selectPrevious=function(e,t,n){this.model.selectPrevious(e,t,n)},t.prototype.selectParent=function(e,t){this.model.selectParent(e,t)},t.prototype.setFocus=function(e,t){this.model.setFocus(e,t)},t.prototype.isFocused=function(e){return this.model.isFocused(e)},t.prototype.getFocus=function(){return this.model.getFocus()},t.prototype.focusNext=function(e,t){this.model.focusNext(e,t)},t.prototype.focusPrevious=function(e,t){this.model.focusPrevious(e,t)},t.prototype.focusParent=function(e){this.model.focusParent(e)},t.prototype.focusFirstChild=function(e){this.model.focusFirstChild(e)},t.prototype.focusFirst=function(e,t){this.model.focusFirst(e,t)},t.prototype.focusNth=function(e,t){this.model.focusNth(e,t)},t.prototype.focusLast=function(e,t){this.model.focusLast(e,t)},t.prototype.focusNextPage=function(e){this.view.focusNextPage(e)},t.prototype.focusPreviousPage=function(e){this.view.focusPreviousPage(e)},t.prototype.clearFocus=function(e){this.model.setFocus(null,e)},t.prototype.addTraits=function(e,t){this.model.addTraits(e,t)},t.prototype.removeTraits=function(e,t){this.model.removeTraits(e,t)},t.prototype.toggleTrait=function(e,t){this.model.hasTrait(e,t)?this.model.removeTraits(e,[t]):this.model.addTraits(e,[t])},t.prototype.hasTrait=function(e,t){return this.model.hasTrait(e,t)},t.prototype.getNavigator=function(e,t){return new s.MappedNavigator(this.model.getNavigator(e,t),function(e){return e&&e.getElement()})},t.prototype.dispose=function(){this._onDispose.fire(),null!==this.model&&(this.model.dispose(),this.model=null),null!==this.view&&(this.view.dispose(),this.view=null),this.toDispose=u.dispose(this.toDispose),e.prototype.dispose.call(this)},t}(i.EventEmitter);t.Tree=p}),define(d[301],h([1,0,297,7,15,79,29,10,100,445,52,115,36,161,220,65,128,4,3,48,32,26,260]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_,y,C,b,w){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var S=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.onContextMenu=function(t,n,i){return o.isMacintosh?this.onLeftClick(t,n,i):e.prototype.onContextMenu.call(this,t,n,i)},t}(v.DefaultController);t.QuickOpenController=S;var E;!function(e){e[e.ELEMENT_SELECTED=0]="ELEMENT_SELECTED",e[e.FOCUS_LOST=1]="FOCUS_LOST",e[e.CANCELED=2]="CANCELED"}(E=t.HideReason||(t.HideReason={}));var L={background:b.Color.fromHex("#1E1E1E"),foreground:b.Color.fromHex("#CCCCCC"),pickerGroupForeground:b.Color.fromHex("#0097FB"),pickerGroupBorder:b.Color.fromHex("#3F3F46"),widgetShadow:b.Color.fromHex("#000000"),progressBarBackground:b.Color.fromHex("#0E70C0")},x=n.localize(0,null),N=function(){function e(e,t,n,i){this.isDisposed=!1,this.toUnbind=[],this.container=e,this.callbacks=t,this.options=n,this.styles=n||Object.create(null),w.mixin(this.styles,L,!1),this.usageLogger=i,this.model=null}return e.prototype.getElement=function(){return c.$(this.builder)},e.prototype.getModel=function(){return this.model},e.prototype.setCallbacks=function(e){this.callbacks=e},e.prototype.create=function(){var e=this;return this.builder=c.$().div(function(t){t.on(_.EventType.KEY_DOWN,function(t){9===new m.StandardKeyboardEvent(t).keyCode&&(_.EventHelper.stop(t,!0),e.hide(E.CANCELED))}).on(_.EventType.CONTEXT_MENU,function(e){return _.EventHelper.stop(e,!0)}).on(_.EventType.FOCUS,function(t){return e.gainingFocus()},null,!0).on(_.EventType.BLUR,function(t){return e.loosingFocus(t)},null,!0),e.progressBar=new g.ProgressBar(t.clone(),{progressBarBackground:e.styles.progressBarBackground}),e.progressBar.getContainer().hide(),t.div({class:"quick-open-input"},function(t){e.inputContainer=t,e.inputBox=new d.InputBox(t.getHTMLElement(),null,{placeholder:e.options.inputPlaceHolder||"",ariaLabel:x,inputBackground:e.styles.inputBackground,inputForeground:e.styles.inputForeground,inputBorder:e.styles.inputBorder,inputValidationInfoBackground:e.styles.inputValidationInfoBackground,inputValidationInfoBorder:e.styles.inputValidationInfoBorder,inputValidationWarningBackground:e.styles.inputValidationWarningBackground,inputValidationWarningBorder:e.styles.inputValidationWarningBorder,inputValidationErrorBackground:e.styles.inputValidationErrorBackground,inputValidationErrorBorder:e.styles.inputValidationErrorBorder}),e.inputElement=e.inputBox.inputElement,e.inputElement.setAttribute("role","combobox"),e.inputElement.setAttribute("aria-haspopup","false"),e.inputElement.setAttribute("aria-autocomplete","list"),_.addDisposableListener(e.inputBox.inputElement,_.EventType.KEY_DOWN,function(t){var n=new m.StandardKeyboardEvent(t),i=e.shouldOpenInBackground(n);if(2!==n.keyCode)if(18===n.keyCode||16===n.keyCode||12===n.keyCode||11===n.keyCode)_.EventHelper.stop(t,!0),e.navigateInTree(n.keyCode,n.shiftKey),e.inputBox.inputElement.selectionStart===e.inputBox.inputElement.selectionEnd&&(e.inputBox.inputElement.selectionStart=e.inputBox.value.length);else if(3===n.keyCode||i){_.EventHelper.stop(t,!0);var o=e.tree.getFocus();o&&e.elementSelected(o,t,i?u.Mode.OPEN_IN_BACKGROUND:u.Mode.OPEN)}}),_.addDisposableListener(e.inputBox.inputElement,_.EventType.INPUT,function(t){e.onType()})}),e.treeContainer=t.div({class:"quick-open-tree"},function(t){e.tree=new p.Tree(t.getHTMLElement(),{dataSource:new l.DataSource(e),controller:new S({clickBehavior:v.ClickBehavior.ON_MOUSE_UP,keyboardSupport:e.options.keyboardSupport}),renderer:e.renderer=new l.Renderer(e,e.styles),filter:new l.Filter(e),accessibilityProvider:new l.AccessibilityProvider(e)},{twistiePixels:11,indentPixels:0,alwaysFocused:!0,verticalScrollMode:C.ScrollbarVisibility.Visible,ariaLabel:n.localize(1,null),keyboardSupport:e.options.keyboardSupport}),e.treeElement=e.tree.getHTMLElement(),e.toUnbind.push(e.tree.addListener(r.EventType.FOCUS,function(t){e.elementFocused(t.focus,t)})),e.toUnbind.push(e.tree.addListener(r.EventType.SELECTION,function(t){t.selection&&t.selection.length>0&&e.elementSelected(t.selection[0],t)}))}).on(_.EventType.KEY_DOWN,function(t){var n=new m.StandardKeyboardEvent(t);e.quickNavigateConfiguration&&(18!==n.keyCode&&16!==n.keyCode&&12!==n.keyCode&&11!==n.keyCode||(_.EventHelper.stop(t,!0),e.navigateInTree(n.keyCode)))}).on(_.EventType.KEY_UP,function(t){var n=new m.StandardKeyboardEvent(t),i=n.keyCode;if(e.quickNavigateConfiguration){var o=e.quickNavigateConfiguration.keybindings;if(3===i||o.some(function(e){var t=e.getParts(),o=t[0];return!t[1]&&(o.shiftKey&&4===i?!(n.ctrlKey||n.altKey||n.metaKey):!(!o.altKey||6!==i)||(!(!o.ctrlKey||5!==i)||!(!o.metaKey||57!==i)))})){var r=e.tree.getFocus();r&&e.elementSelected(r,t)}}}).clone()}).addClass("quick-open-widget").build(this.container),this.layoutDimensions&&this.layout(this.layoutDimensions),this.applyStyles(),this.builder.getHTMLElement()},e.prototype.style=function(e){this.styles=e,this.applyStyles()},e.prototype.applyStyles=function(){if(this.builder){var e=this.styles.foreground?this.styles.foreground.toString():null,t=this.styles.background?this.styles.background.toString():null,n=this.styles.borderColor?this.styles.borderColor.toString():null,i=this.styles.widgetShadow?this.styles.widgetShadow.toString():null;this.builder.style("color",e),this.builder.style("background-color",t),this.builder.style("border-color",n),this.builder.style("border-width",n?"1px":null),this.builder.style("border-style",n?"solid":null),this.builder.style("box-shadow",i?"0 5px 8px "+i:null)}this.progressBar&&this.progressBar.style({progressBarBackground:this.styles.progressBarBackground}),this.inputBox&&this.inputBox.style({inputBackground:this.styles.inputBackground,inputForeground:this.styles.inputForeground,inputBorder:this.styles.inputBorder,inputValidationInfoBackground:this.styles.inputValidationInfoBackground,inputValidationInfoBorder:this.styles.inputValidationInfoBorder,inputValidationWarningBackground:this.styles.inputValidationWarningBackground,inputValidationWarningBorder:this.styles.inputValidationWarningBorder,inputValidationErrorBackground:this.styles.inputValidationErrorBackground,inputValidationErrorBorder:this.styles.inputValidationErrorBorder}),this.tree&&this.tree.style(this.styles),this.renderer&&this.renderer.updateStyles(this.styles)},e.prototype.shouldOpenInBackground=function(e){return 17===e.keyCode&&(!(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey)&&this.inputBox.inputElement.selectionEnd===this.inputBox.value.length)},e.prototype.onType=function(){var e=this.inputBox.value;this.helpText&&(e?this.helpText.hide():this.helpText.show()),this.callbacks.onType(e)},e.prototype.navigate=function(e,t){this.isVisible()&&(!this.quickNavigateConfiguration&&t&&(this.quickNavigateConfiguration=t,this.tree.DOMFocus()),this.navigateInTree(e?18:16))},e.prototype.navigateInTree=function(e,t){var n=this.tree.getInput(),i=n?n.entries:[],o=this.tree.getFocus();switch(e){case 18:this.tree.focusNext();break;case 16:this.tree.focusPrevious();break;case 12:this.tree.focusNextPage();break;case 11:this.tree.focusPreviousPage();break;case 2:t?this.tree.focusPrevious():this.tree.focusNext()}var r=this.tree.getFocus();i.length>1&&o===r&&(16===e||2===e&&t?this.tree.focusLast():(18===e||2===e&&!t)&&this.tree.focusFirst()),(r=this.tree.getFocus())&&this.tree.reveal(r).done(null,a.onUnexpectedError)},e.prototype.elementFocused=function(e,t){if(e&&this.isVisible()){this.inputElement.setAttribute("aria-activedescendant",this.treeElement.getAttribute("aria-activedescendant"));var n={event:t,keymods:this.extractKeyMods(t),quickNavigateConfiguration:this.quickNavigateConfiguration};this.model.runner.run(e,u.Mode.PREVIEW,n)}},e.prototype.elementSelected=function(e,t,n){var i=!0;if(this.isVisible()){var o=n||u.Mode.OPEN,r={event:t,keymods:this.extractKeyMods(t),quickNavigateConfiguration:this.quickNavigateConfiguration};i=this.model.runner.run(e,o,r)}if(this.usageLogger){var s=this.model.entries.indexOf(e),a=this.model.entries.length;this.usageLogger.publicLog("quickOpenWidgetItemAccepted",{index:s,count:a,isQuickNavigate:!!this.quickNavigateConfiguration})}i&&this.hide(E.ELEMENT_SELECTED)},e.prototype.extractKeyMods=function(e){return e&&(e.ctrlKey||e.metaKey||e.payload&&e.payload.originalEvent&&(e.payload.originalEvent.ctrlKey||e.payload.originalEvent.metaKey))?[2048]:[]},e.prototype.show=function(e,t){this.visible=!0,this.isLoosingFocus=!1,this.quickNavigateConfiguration=t?t.quickNavigateConfiguration:void 0,this.quickNavigateConfiguration?(this.inputContainer.hide(),this.builder.show(),this.tree.DOMFocus()):(this.inputContainer.show(),this.builder.show(),this.inputBox.focus()),this.helpText&&(this.quickNavigateConfiguration||s.isString(e)?this.helpText.hide():this.helpText.show()),s.isString(e)?this.doShowWithPrefix(e):this.doShowWithInput(e,t&&t.autoFocus?t.autoFocus:{}),t&&t.inputSelection&&!this.quickNavigateConfiguration&&this.inputBox.select(t.inputSelection),this.callbacks.onShow&&this.callbacks.onShow()},e.prototype.doShowWithPrefix=function(e){this.inputBox.value=e,this.callbacks.onType(e)},e.prototype.doShowWithInput=function(e,t){this.setInput(e,t)},e.prototype.setInputAndLayout=function(e,t){var n=this;this.treeContainer.style({height:this.getHeight(e)+"px"}),this.tree.setInput(null).then(function(){return n.model=e,n.inputElement.setAttribute("aria-haspopup",String(e&&e.entries&&e.entries.length>0)),n.tree.setInput(e)}).done(function(){n.tree.layout(),e&&e.entries.some(function(t){return n.isElementVisible(e,t)})&&n.autoFocus(e,t)},a.onUnexpectedError)},e.prototype.isElementVisible=function(e,t){return!e.filter||e.filter.isVisible(t)},e.prototype.autoFocus=function(e,t){var n=this;void 0===t&&(t={});var i=e.entries.filter(function(t){return n.isElementVisible(e,t)});if(t.autoFocusPrefixMatch){for(var o=void 0,r=void 0,s=t.autoFocusPrefixMatch,u=s.toLowerCase(),l=0;lt.autoFocusIndex&&(this.tree.focusNth(t.autoFocusIndex),this.tree.reveal(this.tree.getFocus()).done(null,a.onUnexpectedError)):t.autoFocusSecondEntry?i.length>1&&this.tree.focusNth(1):t.autoFocusLastEntry&&i.length>1&&this.tree.focusLast()},e.prototype.refresh=function(e,t){var n=this;this.isVisible()&&(e||(e=this.tree.getInput()),e&&(this.treeContainer.style({height:this.getHeight(e)+"px"}),this.tree.refresh().done(function(){n.tree.layout(),t&&t&&e&&e.entries.some(function(t){return n.isElementVisible(e,t)})&&n.autoFocus(e,t)},a.onUnexpectedError)))},e.prototype.getHeight=function(t){var n=this,i=t.renderer;if(!t){var o=i.getHeight(null);return this.options.minItemsToShow?this.options.minItemsToShow*o:0}var r,s=0;this.layoutDimensions&&this.layoutDimensions.height&&(r=.4*(this.layoutDimensions.height-50)),(!r||r>e.MAX_ITEMS_HEIGHT)&&(r=e.MAX_ITEMS_HEIGHT);for(var a=t.entries.filter(function(e){return n.isElementVisible(t,e)}),u=this.options.maxItemsToShow||a.length,l=0;l=2?(S=v?g.Large:g.LargeBlocks,k=2/y):(S=v?g.Small:g.SmallBlocks,k=1/y),(E=Math.max(0,Math.floor((T-d)*k/(l+k))))/k>_&&(E=Math.floor(_*k)),L=T-E}else E=0,S=g.None,L=T;var I=Math.max(1,Math.floor((L-d)/l)),D=h?p:0;return{width:t,height:n,glyphMarginLeft:0,glyphMarginWidth:w,glyphMarginHeight:n,lineNumbersLeft:x,lineNumbersWidth:C,lineNumbersHeight:n,decorationsLeft:N,decorationsWidth:u,decorationsHeight:n,contentLeft:M,contentWidth:L,contentHeight:n,renderMinimap:S,minimapWidth:E,viewportColumn:I,verticalScrollbarWidth:d,horizontalScrollbarHeight:f,overviewRuler:{top:D,width:d,height:n-2*D,right:0}}},e}();t.EditorLayoutProvider=S;t.EDITOR_FONT_DEFAULTS={fontFamily:i.isMacintosh?"Menlo, Monaco, 'Courier New', monospace":i.isLinux?"'Droid Sans Mono', 'Courier New', monospace, 'Droid Sans Fallback'":"Consolas, 'Courier New', monospace",fontWeight:"normal",fontSize:i.isMacintosh?12:14,lineHeight:0,letterSpacing:0},t.EDITOR_MODEL_DEFAULTS={tabSize:4,insertSpaces:!0,detectIndentation:!0,trimAutoWhitespace:!0},t.EDITOR_DEFAULTS={inDiffEditor:!1,wordSeparators:r.USUAL_WORD_SEPARATORS,lineNumbersMinChars:5,lineDecorationsWidth:10,readOnly:!1,mouseStyle:"text",disableLayerHinting:!1,automaticLayout:!1,wordWrap:"off",wordWrapColumn:80,wordWrapMinified:!0,wrappingIndent:m.Same,wordWrapBreakBeforeCharacters:"([{‘“〈《「『【〔([{「£¥$£¥++",wordWrapBreakAfterCharacters:" \t})]?|&,;¢°′″‰℃、。。、¢,.:;?!%・・ゝゞヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻ァィゥェォャュョッー”〉》」』】〕)]}」",wordWrapBreakObtrusiveCharacters:".",autoClosingBrackets:!0,autoIndent:!0,dragAndDrop:!0,emptySelectionClipboard:!0,useTabStops:!0,multiCursorModifier:"altKey",accessibilitySupport:"auto",viewInfo:{extraEditorClassName:"",disableMonospaceOptimizations:!1,rulers:[],ariaLabel:n.localize(1,null),renderLineNumbers:!0,renderCustomLineNumbers:null,renderRelativeLineNumbers:!1,selectOnLineNumbers:!0,glyphMargin:!0,revealHorizontalRightPadding:30,roundedSelection:!0,overviewRulerLanes:2,overviewRulerBorder:!0,cursorBlinking:v.Blink,mouseWheelZoom:!1,cursorStyle:y.Line,hideCursorInOverviewRuler:!1,scrollBeyondLastLine:!0,stopRenderingLineAfter:1e4,renderWhitespace:"none",renderControlCharacters:!1,fontLigatures:!1,renderIndentGuides:!0,renderLineHighlight:"line",scrollbar:{vertical:o.ScrollbarVisibility.Auto,horizontal:o.ScrollbarVisibility.Auto,arrowSize:11,useShadows:!0,verticalHasArrows:!1,horizontalHasArrows:!1,horizontalScrollbarSize:10,horizontalSliderSize:10,verticalScrollbarSize:14,verticalSliderSize:14,handleMouseWheel:!0,mouseWheelScrollSensitivity:1},minimap:{enabled:!0,showSlider:"mouseover",renderCharacters:!0,maxColumn:120},fixedOverflowWidgets:!1},contribInfo:{selectionClipboard:!0,hover:!0,links:!0,contextmenu:!0,quickSuggestions:{other:!0,comments:!1,strings:!1},quickSuggestionsDelay:10,parameterHints:!0,iconsInSuggestions:!0,formatOnType:!1,formatOnPaste:!1,suggestOnTriggerCharacters:!0,acceptSuggestionOnEnter:"on",acceptSuggestionOnCommitCharacter:!0,snippetSuggestions:"inline",wordBasedSuggestions:!0,suggestFontSize:0,suggestLineHeight:0,selectionHighlight:!0,occurrencesHighlight:!0,codeLens:!0,folding:!0,showFoldingControls:"mouseover",matchBrackets:!0,find:{seedSearchStringFromSelection:!0,autoFindInSelection:!1}}}}),define(d[132],h([1,0,15,135,49]),function(e,t,n,i,o){"use strict";function r(e,t){if("number"==typeof e)return e;var n=parseFloat(e);return isNaN(n)?t:n}function s(e,t){if("number"==typeof e)return Math.round(e);var n=parseInt(e);return isNaN(n)?t:n}function a(e,t,n){return en?n:e}function u(e,t){return"string"!=typeof e?t:e}Object.defineProperty(t,"__esModule",{value:!0});var l=n.isMacintosh?1.5:1.35,c=function(){function e(e){this.zoomLevel=e.zoomLevel,this.fontFamily=String(e.fontFamily),this.fontWeight=String(e.fontWeight),this.fontSize=e.fontSize,this.lineHeight=0|e.lineHeight,this.letterSpacing=e.letterSpacing}return e.createFromRawSettings=function(t,n){var c=u(t.fontFamily,o.EDITOR_FONT_DEFAULTS.fontFamily),d=u(t.fontWeight,o.EDITOR_FONT_DEFAULTS.fontWeight),h=r(t.fontSize,o.EDITOR_FONT_DEFAULTS.fontSize);0===(h=a(h,0,100))?h=o.EDITOR_FONT_DEFAULTS.fontSize:h<8&&(h=8);var p=s(t.lineHeight,0);0===(p=a(p,0,150))?p=Math.round(l*h):p<8&&(p=8);var f=r(t.letterSpacing,0);f=a(f,-20,20);var g=1+.1*i.EditorZoom.getZoomLevel();return h*=g,p*=g,new e({zoomLevel:n,fontFamily:c,fontWeight:d,fontSize:h,lineHeight:p,letterSpacing:f})},e.prototype.getId=function(){return this.zoomLevel+"-"+this.fontFamily+"-"+this.fontWeight+"-"+this.fontSize+"-"+this.lineHeight+"-"+this.letterSpacing},e}();t.BareFontInfo=c;var d=function(e){function t(t,n){var i=e.call(this,t)||this;return i.isTrusted=n,i.isMonospace=t.isMonospace,i.typicalHalfwidthCharacterWidth=t.typicalHalfwidthCharacterWidth,i.typicalFullwidthCharacterWidth=t.typicalFullwidthCharacterWidth,i.spaceWidth=t.spaceWidth,i.maxDigitWidth=t.maxDigitWidth,i}return f(t,e),t.prototype.equals=function(e){return this.fontFamily===e.fontFamily&&this.fontWeight===e.fontWeight&&this.fontSize===e.fontSize&&this.lineHeight===e.lineHeight&&this.letterSpacing===e.letterSpacing&&this.typicalHalfwidthCharacterWidth===e.typicalHalfwidthCharacterWidth&&this.typicalFullwidthCharacterWidth===e.typicalFullwidthCharacterWidth&&this.spaceWidth===e.spaceWidth&&this.maxDigitWidth===e.maxDigitWidth},t}(c);t.FontInfo=d}),define(d[93],h([1,0,38,9,12,2,20,118,482,49,104,481,158,101,55]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,f){"use strict";Object.defineProperty(t,"__esModule",{value:!0});t.LONG_LINE_BOUNDARY=1e4;var g=function(){function e(t,i){this._eventEmitter=new n.OrderGuaranteeEventEmitter;var o=e.resolveCreationData(t,i);this._isTooLargeForTokenization=o.text.length>e.MODEL_TOKENIZATION_LIMIT||o.text.lines.length>e.MANY_MANY_LINES,this._shouldSimplifyMode=this._isTooLargeForTokenization||o.text.length>e.MODEL_SYNC_LIMIT,this._options=new s.TextModelResolvedOptions(o.options),this._constructLines(o.text),this._setVersionId(1),this._isDisposed=!1,this._isDisposing=!1}return e.createFromString=function(t,n){return void 0===n&&(n=e.DEFAULT_CREATION_OPTIONS),new e(p.RawTextSource.fromString(t),n)},e.resolveCreationData=function(e,t){var n,i=p.TextSource.fromRawTextSource(e,t.defaultEOL);if(t.detectIndentation){var o=u.guessIndentation(i.lines,t.tabSize,t.insertSpaces);n=new s.TextModelResolvedOptions({tabSize:o.tabSize,insertSpaces:o.insertSpaces,trimAutoWhitespace:t.trimAutoWhitespace,defaultEOL:t.defaultEOL})}else n=new s.TextModelResolvedOptions({tabSize:t.tabSize,insertSpaces:t.insertSpaces,trimAutoWhitespace:t.trimAutoWhitespace,defaultEOL:t.defaultEOL});return{text:i,options:n}},e.prototype.addBulkListener=function(e){return this._eventEmitter.addBulkListener(e)},e.prototype._createModelLine=function(e,t){return this._isTooLargeForTokenization?new a.MinimalModelLine(e,t):new a.ModelLine(e,t)},e.prototype._assertNotDisposed=function(){if(this._isDisposed)throw new Error("Model is disposed!")},e.prototype.isTooLargeForHavingARichMode=function(){return this._shouldSimplifyMode},e.prototype.isTooLargeForTokenization=function(){return this._isTooLargeForTokenization},e.prototype.getOptions=function(){return this._assertNotDisposed(),this._options},e.prototype.updateOptions=function(e){this._assertNotDisposed();var t=void 0!==e.tabSize?e.tabSize:this._options.tabSize,n=void 0!==e.insertSpaces?e.insertSpaces:this._options.insertSpaces,i=void 0!==e.trimAutoWhitespace?e.trimAutoWhitespace:this._options.trimAutoWhitespace,o=new s.TextModelResolvedOptions({tabSize:t,insertSpaces:n,defaultEOL:this._options.defaultEOL,trimAutoWhitespace:i});if(!this._options.equals(o)){var r=this._options.createChangeEvent(o);if(this._options=o,r.tabSize)for(var a=this._options.tabSize,u=0,l=this._lines.length;u=t.LONG_LINE_BOUNDARY?r+=i:o+=i;return r>o},e.prototype.getLineCount=function(){return this._assertNotDisposed(),this._lines.length},e.prototype.getLineContent=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");return this._lines[e-1].text},e.prototype.getIndentLevel=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");return this._lines[e-1].getIndentLevel()},e.prototype._resetIndentRanges=function(){this._indentRanges=null},e.prototype._getIndentRanges=function(){return this._indentRanges||(this._indentRanges=d.computeRanges(this)),this._indentRanges},e.prototype.getIndentRanges=function(){this._assertNotDisposed();var e=this._getIndentRanges();return d.IndentRange.deepCloneArr(e)},e.prototype._toValidLineIndentGuide=function(e,t){var n=this._lines[e-1].getIndentLevel();if(-1===n)return t;var i=Math.ceil(n/this._options.tabSize);return Math.min(i,t)},e.prototype.getLineIndentGuide=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");for(var t=this._getIndentRanges(),n=t.length-1;n>=0;n--){var i=t[n];if(i.startLineNumber===e)return this._toValidLineIndentGuide(e,Math.ceil(i.indent/this._options.tabSize));if(i.startLineNumber0;)(i=t[--n]).endLineNumber+1===e&&(o=i.indent);return this._toValidLineIndentGuide(e,Math.ceil(o/this._options.tabSize))}}return 0},e.prototype.getLinesContent=function(){this._assertNotDisposed();for(var e=[],t=0,n=this._lines.length;tthis.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");return this._lines[e-1].text.length+1},e.prototype.getLineFirstNonWhitespaceColumn=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");var t=i.firstNonWhitespaceIndex(this._lines[e-1].text);return-1===t?0:t+1},e.prototype.getLineLastNonWhitespaceColumn=function(e){if(this._assertNotDisposed(),e<1||e>this.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");var t=i.lastNonWhitespaceIndex(this._lines[e-1].text);return-1===t?0:t+2},e.prototype.validateLineNumber=function(e){return this._assertNotDisposed(),e<1&&(e=1),e>this._lines.length&&(e=this._lines.length),e},e.prototype._validatePosition=function(e,t,n){var r=Math.floor("number"==typeof e?e:1),s=Math.floor("number"==typeof t?t:1);if(r<1)return new o.Position(1,1);if(r>this._lines.length)return new o.Position(this._lines.length,this.getLineMaxColumn(this._lines.length));if(s<=1)return new o.Position(r,1);var a=this.getLineMaxColumn(r);if(s>=a)return new o.Position(r,a);if(n){var u=this._lines[r-1].text.charCodeAt(s-2);if(i.isHighSurrogate(u))return new o.Position(r,s-1)}return new o.Position(r,s)},e.prototype.validatePosition=function(e){return this._assertNotDisposed(),this._validatePosition(e.lineNumber,e.column,!0)},e.prototype.validateRange=function(e){this._assertNotDisposed();var t=this._validatePosition(e.startLineNumber,e.startColumn,!1),n=this._validatePosition(e.endLineNumber,e.endColumn,!1),o=t.lineNumber,s=t.column,a=n.lineNumber,u=n.column,l=this._lines[o-1].text,c=this._lines[a-1].text,d=s>1?l.charCodeAt(s-2):0,h=u>1&&u<=c.length?c.charCodeAt(u-2):0,p=i.isHighSurrogate(d),f=i.isHighSurrogate(h);return p||f?o===a&&s===u?new r.Range(o,s-1,a,u-1):p&&f?new r.Range(o,s-1,a,u+1):p?new r.Range(o,s-1,a,u):new r.Range(o,s,a,u+1):new r.Range(o,s,a,u)},e.prototype.modifyPosition=function(e,t){return this._assertNotDisposed(),this.getPositionAt(this.getOffsetAt(e)+t)},e.prototype.getFullModelRange=function(){this._assertNotDisposed();var e=this.getLineCount();return new r.Range(1,1,e,this.getLineMaxColumn(e))},e.prototype._emitModelRawContentChangedEvent=function(e){this._isDisposing||this._eventEmitter.emit(f.TextModelEventType.ModelRawContentChanged2,e)},e.prototype._constructLines=function(e){for(var t=this._options.tabSize,n=e.lines,i=new Array(n.length),o=0,r=n.length;ot.endLineNumber||i.lineNumber===t.endLineNumber&&i.column>t.endColumn?new c(e.selectionStart,e.selectionStartLeftoverVisibleColumns,new n.Position(t.endLineNumber,t.endColumn),0):null},e.prototype.equals=function(e){return this.viewState.equals(e.viewState)&&this.modelState.equals(e.viewState)},e}();t.CursorState=h;var p=function(){return function(e,t){this.commands=e,this.shouldPushStackElementBefore=t.shouldPushStackElementBefore,this.shouldPushStackElementAfter=t.shouldPushStackElementAfter}}();t.EditOperationResult=p;var f=function(){function e(){}return e.isLowSurrogate=function(e,t,n){var o=e.getLineContent(t);return!(n<0||n>=o.length)&&i.isLowSurrogate(o.charCodeAt(n))},e.isHighSurrogate=function(e,t,n){var o=e.getLineContent(t);return!(n<0||n>=o.length)&&i.isHighSurrogate(o.charCodeAt(n))},e.isInsideSurrogatePair=function(e,t,n){return this.isHighSurrogate(e,t,n-2)},e.visibleColumnFromColumn=function(e,t,n){var i=e.length;i>t-1&&(i=t-1);for(var o=0,r=0;r=t)return s-ts?s:o},e.nextTabStop=function(e,t){return e+t-e%t},e.prevTabStop=function(e,t){return e-1-(e-1)%t},e}();t.CursorColumns=f}),define(d[167],h([1,0,9,39,2,22,43]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(e,t){this._opts=t,this._selection=e,this._useLastEditRangeForCursorEndPosition=!1,this._selectionStartColumnStaysPut=!1}return e.unshiftIndentCount=function(e,t,n){var o=i.CursorColumns.visibleColumnFromColumn(e,t,n);return i.CursorColumns.prevTabStop(o,n)/n},e.shiftIndentCount=function(e,t,n){var o=i.CursorColumns.visibleColumnFromColumn(e,t,n);return i.CursorColumns.nextTabStop(o,n)/n},e.prototype._addEditOperation=function(e,t,n){this._useLastEditRangeForCursorEndPosition?e.addTrackedEditOperation(t,n):e.addEditOperation(t,n)},e.prototype.getEditOperations=function(t,r){var a=this._selection.startLineNumber,u=this._selection.endLineNumber;1===this._selection.endColumn&&a!==u&&(u-=1);var l=this._opts.tabSize,c=this._opts.oneIndent,d=a===u;if(this._selection.isEmpty()&&/^\s*$/.test(t.getLineContent(a))&&(this._useLastEditRangeForCursorEndPosition=!0),this._opts.useTabStops)for(var h=["",c],p=0,f=0,g=a;g<=u;g++,p=f){f=0;var m=t.getLineContent(g),v=n.firstNonWhitespaceIndex(m);if((!this._opts.isUnshift||0!==m.length&&0!==v)&&(d||this._opts.isUnshift||0!==m.length)){if(-1===v&&(v=m.length),g>1&&i.CursorColumns.visibleColumnFromColumn(m,v+1,l)%l!=0){var _=s.LanguageConfigurationRegistry.getRawEnterActionAtPosition(t,g-1,t.getLineMaxColumn(g-1));if(_){if(f=p,_.appendText)for(var y=0,C=_.appendText.length;ya,d=s>u,h=su)continue;if(ys)continue;if(_1&&o--,this.columnSelect(e,t,n.selection,i,o)},e.columnSelectRight=function(e,t,i,r,s){for(var a=0,u=Math.min(i.position.lineNumber,r),l=Math.max(i.position.lineNumber,r),c=u;c<=l;c++){var d=t.getLineMaxColumn(c),h=o.CursorColumns.visibleColumnFromColumn2(e,t,new n.Position(c,d));a=Math.max(a,h)}return st.getLineCount()&&(o=t.getLineCount()),this.columnSelect(e,t,n.selection,o,r)},e}();t.ColumnSelection=r}),define(d[169],h([1,0,39,12,2]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){return function(e,t,n){this.lineNumber=e,this.column=t,this.leftoverVisibleColumns=n}}();t.CursorPosition=r;var s=function(){function e(){}return e.left=function(e,t,i,o){return o>t.getLineMinColumn(i)?n.CursorColumns.isLowSurrogate(t,i,o-2)?o-=2:o-=1:i>1&&(i-=1,o=t.getLineMaxColumn(i)),new r(i,o,0)},e.moveLeft=function(t,n,i,o,r){var s,a;if(i.hasSelection()&&!o)s=i.selection.startLineNumber,a=i.selection.startColumn;else{var u=e.left(t,n,i.position.lineNumber,i.position.column-(r-1));s=u.lineNumber,a=u.column}return i.move(o,s,a,0)},e.right=function(e,t,i,o){return oc?(i=c,u?o=t.getLineMaxColumn(i):(o=Math.min(t.getLineMaxColumn(i),o),n.CursorColumns.isInsideSurrogatePair(t,i,o)&&(o-=1))):(o=n.CursorColumns.columnFromVisibleColumn2(e,t,i,l),n.CursorColumns.isInsideSurrogatePair(t,i,o)&&(o-=1)),s=l-n.CursorColumns.visibleColumnFromColumn(t.getLineContent(i),o,e.tabSize),new r(i,o,s)},e.moveDown=function(t,n,i,o,r){var s,a;i.hasSelection()&&!o?(s=i.selection.endLineNumber,a=i.selection.endColumn):(s=i.position.lineNumber,a=i.position.column);var u=e.down(t,n,s,a,i.leftoverVisibleColumns,r,!0);return i.move(o,u.lineNumber,u.column,u.leftoverVisibleColumns)},e.translateDown=function(t,r,s){var a=s.selection,u=e.down(t,r,a.selectionStartLineNumber,a.selectionStartColumn,s.selectionStartLeftoverVisibleColumns,1,!1),l=e.down(t,r,a.positionLineNumber,a.positionColumn,s.leftoverVisibleColumns,1,!1);return new n.SingleCursorState(new o.Range(u.lineNumber,u.column,u.lineNumber,u.column),u.leftoverVisibleColumns,new i.Position(l.lineNumber,l.column),l.leftoverVisibleColumns)},e.up=function(e,t,i,o,s,a,u){var l=n.CursorColumns.visibleColumnFromColumn(t.getLineContent(i),o,e.tabSize)+s;return(i-=a)<1?(i=1,u?o=t.getLineMinColumn(i):(o=Math.min(t.getLineMaxColumn(i),o),n.CursorColumns.isInsideSurrogatePair(t,i,o)&&(o-=1))):(o=n.CursorColumns.columnFromVisibleColumn2(e,t,i,l),n.CursorColumns.isInsideSurrogatePair(t,i,o)&&(o-=1)),s=l-n.CursorColumns.visibleColumnFromColumn(t.getLineContent(i),o,e.tabSize),new r(i,o,s)},e.moveUp=function(t,n,i,o,r){var s,a;i.hasSelection()&&!o?(s=i.selection.startLineNumber,a=i.selection.startColumn):(s=i.position.lineNumber,a=i.position.column);var u=e.up(t,n,s,a,i.leftoverVisibleColumns,r,!0);return i.move(o,u.lineNumber,u.column,u.leftoverVisibleColumns)},e.translateUp=function(t,r,s){var a=s.selection,u=e.up(t,r,a.selectionStartLineNumber,a.selectionStartColumn,s.selectionStartLeftoverVisibleColumns,1,!1),l=e.up(t,r,a.positionLineNumber,a.positionColumn,s.leftoverVisibleColumns,1,!1);return new n.SingleCursorState(new o.Range(u.lineNumber,u.column,u.lineNumber,u.column),u.leftoverVisibleColumns,new i.Position(l.lineNumber,l.column),l.leftoverVisibleColumns)},e.moveToBeginningOfLine=function(e,t,n,i){var o,r=n.position.lineNumber,s=t.getLineMinColumn(r),a=t.getLineFirstNonWhitespaceColumn(r)||s;return o=n.position.column===a?s:a,n.move(i,r,o,0)},e.moveToEndOfLine=function(e,t,n,i){var o=n.position.lineNumber,r=t.getLineMaxColumn(o);return n.move(i,o,r,0)},e.moveToBeginningOfBuffer=function(e,t,n,i){return n.move(i,1,1,0)},e.moveToEndOfBuffer=function(e,t,n,i){var o=t.getLineCount(),r=t.getLineMaxColumn(o);return n.move(i,o,r,0)},e}();t.MoveOperations=s}),define(d[170],h([1,0,73,39,2,169,9]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(){}return e.deleteRight=function(e,t,i){for(var s=[],a=!1,u=0,l=i.length;u1){var g=t.getLineContent(f.lineNumber),m=s.firstNonWhitespaceIndex(g),v=-1===m?g.length+1:m+1;if(f.column<=v){var _=i.CursorColumns.visibleColumnFromColumn2(e,t,f),y=i.CursorColumns.prevTabStop(_,e.tabSize),C=i.CursorColumns.columnFromVisibleColumn2(e,t,f.lineNumber,y);p=new o.Range(f.lineNumber,C,f.lineNumber,f.column)}else p=new o.Range(f.lineNumber,f.column-1,f.lineNumber,f.column)}else{var b=r.MoveOperations.left(e,t,f.lineNumber,f.column);p=new o.Range(b.lineNumber,b.column,f.lineNumber,f.column)}}p.isEmpty()?u[c]=null:(p.startLineNumber!==p.endLineNumber&&(l=!0),u[c]=new n.ReplaceCommand(p,""))}return[l,u]},e.cut=function(e,t,r){for(var s=[],a=0,u=r.length;a1?(d=c.lineNumber-1,h=t.getLineMaxColumn(c.lineNumber-1),p=c.lineNumber,f=t.getLineMaxColumn(c.lineNumber)):(d=c.lineNumber,h=1,p=c.lineNumber,f=t.getLineMaxColumn(c.lineNumber));var g=new o.Range(d,h,p,f);g.isEmpty()?s[a]=null:s[a]=new n.ReplaceCommand(g,"")}else s[a]=null;else s[a]=new n.ReplaceCommand(l,"")}return new i.EditOperationResult(s,{shouldPushStackElementBefore:!0,shouldPushStackElementAfter:!0})},e}();t.DeleteOperations=a}),define(d[119],h([1,0,10,73,39,2,9,167,43,60,441,94]),function(e,t,n,i,o,r,s,a,u,l,c,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var h=function(){function e(){}return e.indent=function(e,t,n){for(var i=[],o=0,r=n.length;o1){var d=i-1;for(d=i-1;d>=1;d--){var h=n.getLineContent(d);if(s.lastNonWhitespaceIndex(h)>=0)break}if(d<1)return null;var p=n.getLineMaxColumn(d),f=u.LanguageConfigurationRegistry.getEnterAction(n,new r.Range(d,p,d,p));f&&(a=f.indentation,(o=f.enterAction)&&(a+=o.appendText))}return o&&(o===l.IndentAction.Indent&&(a=e.shiftIndent(t,a)),o===l.IndentAction.Outdent&&(a=e.unshiftIndent(t,a)),a=t.normalizeIndentation(a)),a||null},e._replaceJumpToNextIndent=function(e,t,n,r){var s="",a=n.getStartPosition();if(e.insertSpaces)for(var u=o.CursorColumns.visibleColumnFromColumn2(e,t,a),l=e.tabSize,c=l-u%l,d=0;d=0?c.setEndPosition(c.endLineNumber,Math.max(c.endColumn,L+1)):c.setEndPosition(c.endLineNumber,n.getLineMaxColumn(c.endLineNumber)),a)return new i.ReplaceCommandWithoutChangingPosition(c,S+t.normalizeIndentation(_.afterEnter),!0);var x=0;return w<=L+1&&(t.insertSpaces||(b=Math.ceil(b/t.tabSize)),x=Math.min(b+1-t.normalizeIndentation(_.afterEnter).length-1,0)),new i.ReplaceCommandWithOffsetCursorState(c,S+t.normalizeIndentation(_.afterEnter),0,x,!0)}return e._typeCommand(c,"\n"+t.normalizeIndentation(C),a)},e._runAutoIndentType=function(t,n,i,o){var s=u.LanguageConfigurationRegistry.getIndentationAtPosition(n,i.startLineNumber,i.startColumn),a=u.LanguageConfigurationRegistry.getIndentActionForType(n,i,o,{shiftIndent:function(n){return e.shiftIndent(t,n)},unshiftIndent:function(n){return e.unshiftIndent(t,n)}});if(null===a)return null;if(a!==t.normalizeIndentation(s)){var l=n.getLineFirstNonWhitespaceColumn(i.startLineNumber);return 0===l?e._typeCommand(new r.Range(i.startLineNumber,0,i.endLineNumber,i.endColumn),t.normalizeIndentation(a)+o,!1):e._typeCommand(new r.Range(i.startLineNumber,0,i.endLineNumber,i.endColumn),t.normalizeIndentation(a)+n.getLineContent(i.startLineNumber).substring(l-1,i.startColumn-1)+o,!1)}return null},e._isAutoClosingCloseCharType=function(e,t,n,i){if(!e.autoClosingBrackets||!e.autoClosingPairsClose.hasOwnProperty(i))return!1;for(var o=0,r=n.length;o1){var h=d.getMapForWordSeparators(e.wordSeparators),p=c.charCodeAt(l.column-2);if(0===h.get(p))return!1}var f=c.charAt(l.column-1);if(f){var g=e.autoClosingPairsOpen[o]===o,m=!1;for(var v in e.autoClosingPairsClose){var _=e.autoClosingPairsOpen[v]===v;if((g||!_)&&f===v){m=!0;break}}if(!m&&!/\s/.test(f))return!1}t.forceTokenization(l.lineNumber);var y=t.getLineTokens(l.lineNumber),C=!1;try{C=u.LanguageConfigurationRegistry.shouldAutoClosePair(o,y,l.column)}catch(e){n.onUnexpectedError(e)}if(!C)return!1}return!0},e._runAutoClosingOpenCharType=function(e,t,n,r){for(var s=[],a=0,u=n.length;a=0;o--){var r=e.charCodeAt(o),s=t.get(r);if(0===s){if(2===i)return this._createWord(e,i,o+1,this._findEndOfWord(e,t,i,o+1));i=1}else if(2===s){if(1===i)return this._createWord(e,i,o+1,this._findEndOfWord(e,t,i,o+1));i=2}else if(1===s&&0!==i)return this._createWord(e,i,o+1,this._findEndOfWord(e,t,i,o+1))}return 0!==i?this._createWord(e,i,0,this._findEndOfWord(e,t,i,0)):null},e._findEndOfWord=function(e,t,n,i){for(var o=e.length,r=i;r=0;o--){var r=e.charCodeAt(o),s=t.get(r);if(1===s)return o+1;if(1===n&&2===s)return o+1;if(2===n&&0===s)return o+1}return 0},e.moveWordLeft=function(t,n,o,r){var s=o.lineNumber,a=o.column;1===a&&s>1&&(s-=1,a=n.getLineMaxColumn(s));var u=e._findPreviousWordOnLine(t,n,new i.Position(s,a));return 0===r?a=u?u.start+1:1:(u&&a<=u.end+1&&(u=e._findPreviousWordOnLine(t,n,new i.Position(s,u.start+1))),a=u?u.end+1:1),new i.Position(s,a)},e.moveWordRight=function(t,n,o,r){var s=o.lineNumber,a=o.column;a===n.getLineMaxColumn(s)&&s=u.start+1&&(u=e._findNextWordOnLine(t,n,new i.Position(s,u.end+1))),a=u?u.start+1:n.getLineMaxColumn(s)),new i.Position(s,a)},e._deleteWordLeftWhitespace=function(e,t){var n=e.getLineContent(t.lineNumber),i=t.column-2,o=r.lastNonWhitespaceIndex(n,i);return o+11?c=1:(l--,c=n.getLineMaxColumn(l)):(h&&c<=h.end+1&&(h=e._findPreviousWordOnLine(t,n,new i.Position(l,h.start+1))),h?c=h.end+1:c>1?c=1:(l--,c=n.getLineMaxColumn(l))),new s.Range(l,c,u.lineNumber,u.column)},e._findFirstNonWhitespaceChar=function(e,t){for(var n=e.length,i=t;i=f.start+1&&(f=e._findNextWordOnLine(t,n,new i.Position(l,f.end+1))),f?c=f.start+1:cc&&(d=c,h=e.model.getLineMaxColumn(d)),n.CursorState.fromModelState(new n.SingleCursorState(new o.Range(u.lineNumber,1,d,h),0,new i.Position(d,h),0))}var p=t.modelState.selectionStart.getStartPosition().lineNumber;if(u.lineNumberp){var c=e.viewModel.getLineCount(),f=l.lineNumber+1,g=1;return f>c&&(f=c,g=e.viewModel.getLineMaxColumn(f)),n.CursorState.fromViewState(t.viewState.move(t.modelState.hasSelection(),f,g,0))}var m=t.modelState.selectionStart.getEndPosition();return n.CursorState.fromModelState(t.modelState.move(t.modelState.hasSelection(),m.lineNumber,m.column,0))},e.word=function(e,t,i,o){var r=e.model.validatePosition(o);return n.CursorState.fromModelState(s.WordOperations.word(e.config,e.model,t.modelState,i,r))},e.cancelSelection=function(e,t){if(!t.modelState.hasSelection())return new n.CursorState(t.modelState,t.viewState);var r=t.viewState.position.lineNumber,s=t.viewState.position.column;return n.CursorState.fromViewState(new n.SingleCursorState(new o.Range(r,s,r,s),0,new i.Position(r,s),0))},e.moveTo=function(e,t,o,r,s){var a=e.model.validatePosition(r),u=s?e.validateViewPosition(new i.Position(s.lineNumber,s.column),a):e.convertModelPositionToViewPosition(a);return n.CursorState.fromViewState(t.viewState.move(o,u.lineNumber,u.column,0))},e.move=function(e,t,n){var i=n.select,o=n.value;switch(n.direction){case 0:return 4===n.unit?this._moveHalfLineLeft(e,t,i):this._moveLeft(e,t,i,o);case 1:return 4===n.unit?this._moveHalfLineRight(e,t,i):this._moveRight(e,t,i,o);case 2:return 2===n.unit?this._moveUpByViewLines(e,t,i,o):this._moveUpByModelLines(e,t,i,o);case 3:return 2===n.unit?this._moveDownByViewLines(e,t,i,o):this._moveDownByModelLines(e,t,i,o);case 4:return this._moveToViewMinColumn(e,t,i);case 5:return this._moveToViewFirstNonWhitespaceColumn(e,t,i);case 6:return this._moveToViewCenterColumn(e,t,i);case 7:return this._moveToViewMaxColumn(e,t,i);case 8:return this._moveToViewLastNonWhitespaceColumn(e,t,i);case 9:var r=t[0],s=e.getCompletelyVisibleModelRange(),a=this._firstLineNumberInRange(e.model,s,o),u=e.model.getLineFirstNonWhitespaceColumn(a);return[this._moveToModelPosition(e,r,i,a,u)];case 11:var r=t[0],s=e.getCompletelyVisibleModelRange(),a=this._lastLineNumberInRange(e.model,s,o),u=e.model.getLineFirstNonWhitespaceColumn(a);return[this._moveToModelPosition(e,r,i,a,u)];case 10:var r=t[0],s=e.getCompletelyVisibleModelRange(),a=Math.round((s.startLineNumber+s.endLineNumber)/2),u=e.model.getLineFirstNonWhitespaceColumn(a);return[this._moveToModelPosition(e,r,i,a,u)];case 12:for(var l=e.getCompletelyVisibleViewRange(),c=[],d=0,h=t.length;di.endLineNumber-1&&(r=i.endLineNumber-1),rn)for(var r=t-n,o=0;o=e+1&&this.lastAddedCursorIndex--,this.secondaryCursors[e].dispose(this.context),this.secondaryCursors.splice(e,1)},e.prototype._getAll=function(){var e=[];e[0]=this.primaryCursor;for(var t=0,n=this.secondaryCursors.length;tp&&t[S].index--;e.splice(p,1),t.splice(h,1),this._removeSecondaryCursor(p-1),s--}}}},e}();t.CursorCollection=r}),define(d[319],h([1,0,9,2,22,43,167,263,60]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l=function(){function e(e,t,n){this._selection=e,this._isMovingDown=t,this._autoIndent=n,this._moveEndLineSelectionShrink=!1}return e.prototype.getEditOperations=function(e,t){var s=e.getLineCount();if((!this._isMovingDown||this._selection.endLineNumber!==s)&&(this._isMovingDown||1!==this._selection.startLineNumber)){this._moveEndPositionDown=!1;var u=this._selection;u.startLineNumber=u.startLineNumber+1&&t<=u.endLineNumber+1?e.getLineContent(t-1):e.getLineContent(t)};var S=r.LanguageConfigurationRegistry.getGoodIndentForLine(h,e.getLanguageIdAtPosition(g,1),u.startLineNumber+1,d);if(null!==S){y=n.getLeadingWhitespace(e.getLineContent(u.startLineNumber));if((C=a.getSpaceCnt(S,l))!==(N=a.getSpaceCnt(y,l))){M=C-N;this.getIndentEditsOfMovingBlock(e,t,u,l,c,M)}}}}else t.addEditOperation(new i.Range(u.startLineNumber,1,u.startLineNumber,1),v+"\n")}else if(g=u.startLineNumber-1,m=e.getLineContent(g),t.addEditOperation(new i.Range(g,1,g+1,1),null),t.addEditOperation(new i.Range(u.endLineNumber,e.getLineMaxColumn(u.endLineNumber),u.endLineNumber,e.getLineMaxColumn(u.endLineNumber)),"\n"+m),this.isAutoIndent(e,u)){h.getLineContent=function(t){return t===g?e.getLineContent(u.startLineNumber):e.getLineContent(t)};var E=this.matchEnterRule(e,d,l,u.startLineNumber,u.startLineNumber-2);if(null!==E)0!==E&&this.getIndentEditsOfMovingBlock(e,t,u,l,c,E);else{var L=r.LanguageConfigurationRegistry.getGoodIndentForLine(h,e.getLanguageIdAtPosition(u.startLineNumber,1),g,d);if(null!==L){var x=n.getLeadingWhitespace(e.getLineContent(u.startLineNumber)),C=a.getSpaceCnt(L,l),N=a.getSpaceCnt(x,l);if(C!==N){var M=C-N;this.getIndentEditsOfMovingBlock(e,t,u,l,c,M)}}}}}this._selectionId=t.trackSelection(u)}},e.prototype.buildIndentConverter=function(e){return{shiftIndent:function(t){for(var n=s.ShiftCommand.shiftIndentCount(t,t.length+1,e),i="",o=0;o=1;){var h=void 0;if(h=d===l&&void 0!==c?c:e.getLineContent(d),n.lastNonWhitespaceIndex(h)>=0)break;d--}if(d<1||s>e.getLineCount())return null;var p=e.getLineMaxColumn(d),f=r.LanguageConfigurationRegistry.getEnterAction(e,new i.Range(d,p,d,p));if(f){var g=f.indentation,m=f.enterAction;m.indentAction===u.IndentAction.None?g=f.indentation+m.appendText:m.indentAction===u.IndentAction.Indent?g=f.indentation+m.appendText:m.indentAction===u.IndentAction.IndentOutdent?g=f.indentation:m.indentAction===u.IndentAction.Outdent&&(g=t.unshiftIndent(f.indentation)+m.appendText);var v=e.getLineContent(s);if(this.trimLeft(v).indexOf(this.trimLeft(g))>=0){var _=n.getLeadingWhitespace(e.getLineContent(s)),y=n.getLeadingWhitespace(g);return 2&r.LanguageConfigurationRegistry.getIndentMetadata(e,s)&&(y=t.unshiftIndent(y)),a.getSpaceCnt(y,o)-a.getSpaceCnt(_,o)}}return null},e.prototype.trimLeft=function(e){return e.replace(/^\s+/,"")},e.prototype.isAutoIndent=function(e,t){if(!this._autoIndent)return!1;var n=e.getLanguageIdAtPosition(t.startLineNumber,1);return n===e.getLanguageIdAtPosition(t.endLineNumber,1)&&null!==r.LanguageConfigurationRegistry.getIndentRulesSupport(n)},e.prototype.getIndentEditsOfMovingBlock=function(e,t,o,r,s,u){for(var l=o.startLineNumber;l<=o.endLineNumber;l++){var c=e.getLineContent(l),d=n.getLeadingWhitespace(c),h=a.getSpaceCnt(d,r)+u,p=a.generateIndent(h,r,s);p!==d&&(t.addEditOperation(new i.Range(l,1,l,d.length+1),p),l===o.endLineNumber&&o.endColumn<=d.length+1&&""===p&&(this._moveEndLineSelectionShrink=!0))}},e.prototype.computeCursorState=function(e,t){var n=t.getTrackedSelection(this._selectionId);return this._moveEndPositionDown&&(n=n.setEndPosition(n.endLineNumber+1,1)),this._moveEndLineSelectionShrink&&n.startLineNumber1)return;var a=new s.Range(o.lineNumber,o.column,o.lineNumber,o.column);this.emitCursorRevealRange(a,t,n)},t.prototype.emitCursorRevealRange=function(e,t,n){this._emit([new m.ViewRevealRangeRequestEvent(e,t,n)])},t.prototype.trigger=function(e,t,n){var i=u.Handler;if(t!==i.CompositionStart)if(t!==i.CompositionEnd){var r=new C(this._model,this),s=g.CursorChangeReason.NotSet;this._cursors.ensureValidState(),this._isHandling=!0;try{switch(t){case i.Type:this._type(e,n.text);break;case i.ReplacePreviousChar:this._replacePreviousChar(n.text,n.replaceCharCnt);break;case i.Paste:s=g.CursorChangeReason.Paste,this._paste(n.text,n.pasteOnNewLine);break;case i.Cut:this._cut();break;case i.Undo:s=g.CursorChangeReason.Undo,this._interpretCommandResult(this._model.undo());break;case i.Redo:s=g.CursorChangeReason.Redo,this._interpretCommandResult(this._model.redo());break;case i.ExecuteCommand:this._externalExecuteCommand(n);break;case i.ExecuteCommands:this._externalExecuteCommands(n)}}catch(e){o.onUnexpectedError(e)}this._isHandling=!1,this._emitStateChangedIfNecessary(e,s,r)&&this._revealRange(0,0,!0)}else this._isDoingComposition=!1;else this._isDoingComposition=!0},t.prototype._type=function(e,t){if(this._isDoingComposition||"keyboard"!==e)this._executeEditOperation(h.TypeOperations.typeWithoutInterceptors(this.context.config,this.context.model,this.getSelections(),t));else for(var n=0,o=t.length;n0&&(h[0]._isTracked=!0);var p=e.model.pushEditOperations(e.selectionsBefore,h,function(n){for(var i=[],o=0;o0?(i[n].sort(s),u[n]=t[n].computeCursorState(e.model,{getInverseEditOperations:function(){return i[n]},getTrackedSelection:function(t){var n=parseInt(t,10),i=e.model._getMarker(e.selectionStartMarkers[n]),o=e.model._getMarker(e.positionMarkers[n]);return new a.Selection(i.lineNumber,i.column,o.lineNumber,o.column)}})):u[n]=e.selectionsBefore[n]}(o);return u}),f=[];for(var g in d)d.hasOwnProperty(g)&&f.push(parseInt(g,10));f.sort(function(e,t){return t-e});for(u=0;uo.identifier.major?i.identifier.major:o.identifier.major).toString()]=!0;for(var a=0;a0&&n--}}return t},e}()}),define(d[322],h([6,8]),function(e,t){return e.create("vs/editor/common/model/textModelWithTokens",t)}),define(d[323],h([1,0,322,10,426,93,17,68,91,98,43,95,55]),function(e,t,n,i,o,r,s,a,u,l,c,d,h){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var p=function(){function e(){this._ranges=[]}return e.prototype.registerChangedTokens=function(e){var t=this._ranges,n=t.length,i=n>0?t[n-1]:null;i&&i.toLineNumber===e-1?i.toLineNumber++:t[n]={fromLineNumber:e,toLineNumber:e}},e.prototype.build=function(){return 0===this._ranges.length?null:{ranges:this._ranges}},e}(),g=function(e){function t(t,n,i){var o=e.call(this,t,n)||this;return o._languageIdentifier=i||a.NULL_LANGUAGE_IDENTIFIER,o._tokenizationListener=s.TokenizationRegistry.onDidChange(function(e){-1!==e.changedLanguages.indexOf(o._languageIdentifier.language)&&(o._resetTokenizationState(),o.emitModelTokensChangedEvent({ranges:[{fromLineNumber:1,toLineNumber:o.getLineCount()}]}))}),o._revalidateTokensTimeout=-1,o._resetTokenizationState(),o}return f(t,e),t.prototype.dispose=function(){this._tokenizationListener.dispose(),this._clearTimers(),this._lastState=null,e.prototype.dispose.call(this)},t.prototype._shouldAutoTokenize=function(){return!1},t.prototype._resetValue=function(t){e.prototype._resetValue.call(this,t),this._resetTokenizationState()},t.prototype._resetTokenizationState=function(){this._clearTimers();for(var e=0;ethis.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");this._withModelTokensChangedEventBuilder(function(n){t._updateTokensUntilLine(n,e)})},t.prototype.getLineTokens=function(e){if(e<1||e>this.getLineCount())throw new Error("Illegal value "+e+" for `lineNumber`");return this._getLineTokens(e)},t.prototype._getLineTokens=function(e){return this._lines[e-1].getTokens(this._languageIdentifier.id)},t.prototype.getLanguageIdentifier=function(){return this._languageIdentifier},t.prototype.getModeId=function(){return this._languageIdentifier.language},t.prototype.setMode=function(e){if(this._languageIdentifier.id!==e.id){var t={oldLanguage:this._languageIdentifier.language,newLanguage:e.language};this._languageIdentifier=e,this._resetTokenizationState(),this.emitModelTokensChangedEvent({ranges:[{fromLineNumber:1,toLineNumber:this.getLineCount()}]}),this._emitModelModeChangedEvent(t)}},t.prototype.getLanguageIdAtPosition=function(e,t){if(!this._tokenizationSupport)return this._languageIdentifier.id;var n=this.validatePosition({lineNumber:e,column:t}),i=n.lineNumber,o=n.column;return this._getLineTokens(i).findTokenAtOffset(o-1).languageId},t.prototype._invalidateLine=function(e){this._lines[e].setIsInvalid(!0),e=200){t=n-1;break}this._revalidateTokensNow(t),this._invalidLineStartIndex20){e=c-1;break}if(a=t._lines[c-1].text.length,s>0&&(u=i/s*a,i+u>20)){e=c-1;break}t._updateTokensUntilLine(n,c),s+=a,c=Math.max(c,t._invalidLineStartIndex+1)}i=l.elapsed(),t._invalidLineStartIndex=1;r--){var s=this._getLineTokens(r),a=this._lines[r-1].text,c=void 0,d=void 0;for(r===t.lineNumber?(c=s.findTokenAtOffset(t.column-1),d=t.column-1):(c=s.lastToken())&&(d=c.endOffset);c;){if(c.languageId===n&&!u.ignoreBracketsInToken(c.tokenType))for(;;){var h=l.BracketsUtils.findPrevBracketInToken(i,r,a,c.startOffset,d);if(!h)break;var p=a.substring(h.startColumn-1,h.endColumn-1);if((p=p.toLowerCase())===e.open?o++:p===e.close&&o--,0===o)return h;d=h.startColumn-1}(c=c.prev())&&(d=c.endOffset)}}return null},t.prototype._findMatchingBracketDown=function(e,t){for(var n=e.languageIdentifier.id,i=e.forwardRegex,o=1,r=t.lineNumber,s=this.getLineCount();r<=s;r++){var a=this._getLineTokens(r),c=this._lines[r-1].text,d=void 0,h=void 0;for(r===t.lineNumber?(d=a.findTokenAtOffset(t.column-1),h=t.column-1):(d=a.firstToken())&&(h=d.startOffset);d;){if(d.languageId===n&&!u.ignoreBracketsInToken(d.tokenType))for(;;){var p=l.BracketsUtils.findNextBracketInToken(i,r,c,h,d.endOffset);if(!p)break;var f=c.substring(p.startColumn-1,p.endColumn-1);if((f=f.toLowerCase())===e.open?o++:f===e.close&&o--,0===o)return p;h=p.endColumn-1}(d=d.next())&&(h=d.startOffset)}}return null},t.prototype.findPrevBracket=function(e){for(var t=this.validatePosition(e),n=-1,i=null,o=t.lineNumber;o>=1;o--){var r=this._getLineTokens(o),s=this._lines[o-1].text,a=void 0,d=void 0;for(o===t.lineNumber?(a=r.findTokenAtOffset(t.column-1),d=t.column-1):(a=r.lastToken())&&(d=a.endOffset);a;){if(n!==a.languageId&&(n=a.languageId,i=c.LanguageConfigurationRegistry.getBracketsSupport(n)),i&&!u.ignoreBracketsInToken(a.tokenType)){var h=l.BracketsUtils.findPrevBracketInToken(i.reversedRegex,o,s,a.startOffset,d);if(h)return this._toFoundBracket(i,h)}(a=a.prev())&&(d=a.endOffset)}}return null},t.prototype.findNextBracket=function(e){for(var t=this.validatePosition(e),n=-1,i=null,o=t.lineNumber,r=this.getLineCount();o<=r;o++){var s=this._getLineTokens(o),a=this._lines[o-1].text,d=void 0,h=void 0;for(o===t.lineNumber?(d=s.findTokenAtOffset(t.column-1),h=t.column-1):(d=s.firstToken())&&(h=d.startOffset);d;){if(n!==d.languageId&&(n=d.languageId,i=c.LanguageConfigurationRegistry.getBracketsSupport(n)),i&&!u.ignoreBracketsInToken(d.tokenType)){var p=l.BracketsUtils.findNextBracketInToken(i.forwardRegex,o,a,h,d.endOffset);if(p)return this._toFoundBracket(i,p)}(d=d.next())&&(h=d.startOffset)}}return null},t.prototype._toFoundBracket=function(e,t){if(!t)return null;var n=this.getValueInRange(t);n=n.toLowerCase();var i=e.textIsBracket[n];return i?{range:t,open:i.open,close:i.close,isOpen:e.textIsOpenBracket[n]}:null},t.MODE_TOKENIZATION_FAILED_MSG=n.localize(0,null),t}(r.TextModel);t.TextModelWithTokens=g}),define(d[324],h([1,0,108,12,118,323]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=0,a=function(e){function t(t,i,o){var r=e.call(this,t,i,o)||this;return r._markerIdGenerator=new n.IdGenerator(++s+";"),r._markerIdToMarker=Object.create(null),r}return f(t,e),t.prototype.dispose=function(){this._markerIdToMarker=null,e.prototype.dispose.call(this)},t.prototype._resetValue=function(t){e.prototype._resetValue.call(this,t),this._markerIdToMarker=Object.create(null)},t.prototype._addMarker=function(e,t,n,r){var s=this.validatePosition(new i.Position(t,n)),a=new o.LineMarker(this._markerIdGenerator.nextId(),e,s,r);return this._markerIdToMarker[a.id]=a,this._lines[s.lineNumber-1].addMarker(a),a.id},t.prototype._addMarkers=function(e){if(0===e.length)return[];for(var t=[],n=0,i=e.length;nthis.getLineCount()?[]:this.getLinesDecorations(e,e,t,n)},t.prototype._getMultiLineDecorations=function(e,t,n){var i=e.startLineNumber,o=e.startColumn,r=e.endLineNumber,s=e.endColumn,a=[];for(var u in this._multiLineDecorationsMap){var l=this._multiLineDecorationsMap[u];if((!t||!l.ownerId||l.ownerId===t)&&(!n||!l.isForValidation)){var c=l.range;c.startLineNumber>r||c.startLineNumber===r&&c.startColumn>s||c.endLineNumberr||g.startLineNumber===r&&g.startColumn>s||g.endLineNumber0){var p={addedDecorations:[],changedDecorations:i,removedDecorations:[]};this.emitModelDecorationsChangedEvent(p)}}},t._createRangeFromMarkers=function(e,t){return t.isBefore(e)?new r.Range(e.lineNumber,e.column,e.lineNumber,e.column):new r.Range(e.lineNumber,e.column,t.lineNumber,t.column)},t.prototype._acquireDecorationsTracker=function(){return 0===this._currentDecorationsTrackerCnt&&(this._currentDecorationsTracker=new g),this._currentDecorationsTrackerCnt++,this._currentDecorationsTracker},t.prototype._releaseDecorationsTracker=function(){if(this._currentDecorationsTrackerCnt--,0===this._currentDecorationsTrackerCnt){var e=this._currentDecorationsTracker;this._currentDecorationsTracker=null,this._handleTrackedDecorations(e)}},t.prototype._handleTrackedDecorations=function(e){if(0!==e.addedDecorationsLen||0!==e.changedDecorationsLen||0!==e.removedDecorationsLen){var t={addedDecorations:e.addedDecorations,changedDecorations:e.changedDecorations,removedDecorations:e.removedDecorations};this.emitModelDecorationsChangedEvent(t)}},t.prototype.emitModelDecorationsChangedEvent=function(e){this._isDisposing||this._eventEmitter.emit(c.TextModelEventType.ModelDecorationsChanged,e)},t.prototype._normalizeDeltaDecorations=function(e){for(var t=[],n=0,i=e.length;n0&&this._removeMarkers(n)},t.prototype._resolveOldDecorations=function(e){for(var t=[],n=0,i=e.length;n0?(d.push(f),l++):p.options.equals(f.options)?(s[f.index]=p.id,a++,l++):(h.push(p.id),a++)}for(;a0&&this._removeDecorationsImpl(e,h),d.length>0)for(var m=this._addDecorationsImpl(e,t,d),v=0,_=d.length;v<_;v++)s[d[v].index]=m[v];return s},t}(l.TextModelWithMarkers);t.TextModelWithDecorations=_;var y=function(){function e(e){this.color=o.empty,this.darkColor=o.empty,this.hcColor=o.empty,this.position=s.OverviewRulerLane.Center,e&&e.color&&(this.color=e.color),e&&e.darkColor&&(this.darkColor=e.darkColor,this.hcColor=e.darkColor),e&&e.hcColor&&(this.hcColor=e.hcColor),e&&e.hasOwnProperty("position")&&(this.position=e.position)}return e.prototype.equals=function(e){return this.color===e.color&&this.darkColor===e.darkColor&&this.hcColor===e.hcColor&&this.position===e.position},e}();t.ModelDecorationOverviewRulerOptions=y;var C=0,b=function(){function e(e,t){this.staticId=e,this.stickiness=t.stickiness||s.TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges,this.className=t.className?h(t.className):o.empty,this.hoverMessage=t.hoverMessage||[],this.glyphMarginHoverMessage=t.glyphMarginHoverMessage||o.empty,this.isWholeLine=t.isWholeLine||!1,this.showIfCollapsed=t.showIfCollapsed||!1,this.overviewRuler=new y(t.overviewRuler),this.glyphMarginClassName=t.glyphMarginClassName?h(t.glyphMarginClassName):o.empty,this.linesDecorationsClassName=t.linesDecorationsClassName?h(t.linesDecorationsClassName):o.empty,this.marginClassName=t.marginClassName?h(t.marginClassName):o.empty,this.inlineClassName=t.inlineClassName?h(t.inlineClassName):o.empty,this.beforeContentClassName=t.beforeContentClassName?h(t.beforeContentClassName):o.empty,this.afterContentClassName=t.afterContentClassName?h(t.afterContentClassName):o.empty,t.__extraOptions&&(this.extraOptions=t.__extraOptions)}return e.register=function(t){return new e(++C,t)},e.createDynamic=function(t){return new e(0,t)},e.prototype.equals=function(e){return this.staticId>0||e.staticId>0?this.staticId===e.staticId:this.stickiness===e.stickiness&&this.className===e.className&&this.isWholeLine===e.isWholeLine&&this.showIfCollapsed===e.showIfCollapsed&&this.glyphMarginClassName===e.glyphMarginClassName&&this.linesDecorationsClassName===e.linesDecorationsClassName&&this.marginClassName===e.marginClassName&&this.inlineClassName===e.inlineClassName&&this.beforeContentClassName===e.beforeContentClassName&&this.afterContentClassName===e.afterContentClassName&&i.markedStringsEquals(this.hoverMessage,e.hoverMessage)&&i.markedStringsEquals(this.glyphMarginHoverMessage,e.glyphMarginHoverMessage)&&this.overviewRuler.equals(e.overviewRuler)&&this.extraOptions===e.extraOptions},e}();t.ModelDecorationOptions=b,b.EMPTY=b.register({});var w=function(){return function(e,t,n){this.index=e,this.range=t,this.options=n}}()}),define(d[326],h([1,0,2,20,475,34,9,33,12,101,93,55]),function(e,t,n,i,o,r,s,a,u,l,c,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var h=function(e){function t(t,n,i){var r=e.call(this,t,n,i)||this;return r._commandManager=new o.EditStack(r),r._isUndoing=!1,r._isRedoing=!1,r._hasEditableRange=!1,r._editableRangeId=null,r._trimAutoWhitespaceLines=null,r}return f(t,e),t.createFromString=function(e,n,i){return void 0===n&&(n=c.TextModel.DEFAULT_CREATION_OPTIONS),void 0===i&&(i=null),new t(l.RawTextSource.fromString(e),n,i)},t.prototype.onDidChangeRawContent=function(e){return this._eventEmitter.addListener(d.TextModelEventType.ModelRawContentChanged2,e)},t.prototype.onDidChangeContent=function(e){return this._eventEmitter.addListener(d.TextModelEventType.ModelContentChanged,e)},t.prototype.dispose=function(){this._commandManager=null,e.prototype.dispose.call(this)},t.prototype._resetValue=function(t){e.prototype._resetValue.call(this,t),this._commandManager=new o.EditStack(this),this._hasEditableRange=!1,this._editableRangeId=null,this._trimAutoWhitespaceLines=null},t.prototype.pushStackElement=function(){this._commandManager.pushStackElement()},t.prototype.pushEditOperations=function(e,t,n){try{return this._eventEmitter.beginDeferredEmit(),this._pushEditOperations(e,t,n)}finally{this._eventEmitter.endDeferredEmit()}},t.prototype._pushEditOperations=function(e,t,i){var o=this;if(this._options.trimAutoWhitespace&&this._trimAutoWhitespaceLines){for(var r=t.map(function(e){return{range:o.validateRange(e.range),text:e.text}}),s=!0,a=0,u=e.length;al.endLineNumber,f=l.startLineNumber>_.endLineNumber;if(!p&&!f){c=!0;break}}if(!c){s=!1;break}}if(s)for(var a=0,u=this._trimAutoWhitespaceLines.length;a_.endLineNumber)&&!(g===_.startLineNumber&&_.startColumn===m&&_.isEmpty()&&y&&y.length>0&&"\n"===y.charAt(0))){v=!1;break}}v&&t.push({identifier:null,range:new n.Range(g,1,g,m),text:null,forceMoveMarkers:!1,isAutoWhitespaceEdit:!1})}this._trimAutoWhitespaceLines=null}return this._commandManager.pushEditOperation(e,t,i)},t.prototype._reduceOperations=function(e){return e.length<1e3?e:[this._toSingleEditOperation(e)]},t.prototype._toSingleEditOperation=function(e){for(var t=!1,i=e[0].range,o=e[e.length-1].range,r=new n.Range(i.startLineNumber,i.startColumn,o.endLineNumber,o.endColumn),s=i.startLineNumber,a=i.startColumn,u=[],l=0,c=e.length;l0){_.sort(function(e,t){return t.lineNumber-e.lineNumber}),this._trimAutoWhitespaceLines=[];for(var u=0,w=_.length;u0&&_[u-1].lineNumber===S)){var E=_[u].oldContent,L=this.getLineContent(S);0!==L.length&&L!==E&&-1===s.firstNonWhitespaceIndex(L)&&this._trimAutoWhitespaceLines.push(S)}}}return v},t._getInverseEditRanges=function(e){for(var t,i,o=[],r=null,s=0,a=e.length;s0){var h=u.lines.length,p=u.lines[0],f=u.lines[h-1];d=1===h?new n.Range(l,c,l,c+p.length):new n.Range(l,c,l+h-1,f.length+1)}else d=new n.Range(l,c,l,c);t=d.endLineNumber,i=d.endColumn,o.push(d),r=u}return o},t.prototype._doApplyEdits=function(e,i){var o=this,r=this._options.tabSize;i.sort(t._sortOpsDescending);for(var s=[],l=[],c=[],h=function(){if(0!==c.length){c.reverse();for(var t=c[0].lineNumber,n=0,i=1,a=c.length;i=0;D--){var x=y+D;!function(e){e.startColumn===e.endColumn&&0===e.text.length||c.push(e)}({lineNumber:x,startColumn:x===y?C:1,endColumn:x===b?w:this.getLineMaxColumn(x),text:_.lines?_.lines[D]:"",forceMoveMarkers:_.forceMoveMarkers})}if(L0},Object.defineProperty(t.prototype,"uri",{get:function(){return this._associatedResource},enumerable:!0,configurable:!0}),t}(i.EditableTextModel);t.Model=u}),define(d[179],h([1,0,12,22,2,104,86,57,34]),function(e,t,n,i,o,r,s,a,u){"use strict";function l(e,t,n,i,o,r,s){var a=e.createLineMapping(t,n,i,o,r);return null===a?s?p.INSTANCE:f.INSTANCE:new g(a,s)}Object.defineProperty(t,"__esModule",{value:!0});var c=function(){return function(e,t){this.outputLineIndex=e,this.outputOffset=t}}();t.OutputPosition=c;var d=function(){function e(e){this._lines=e}return e.prototype.convertViewPositionToModelPosition=function(e){return this._lines.convertViewPositionToModelPosition(e.lineNumber,e.column)},e.prototype.convertViewRangeToModelRange=function(e){var t=this._lines.convertViewPositionToModelPosition(e.startLineNumber,e.startColumn),n=this._lines.convertViewPositionToModelPosition(e.endLineNumber,e.endColumn);return new o.Range(t.lineNumber,t.column,n.lineNumber,n.column)},e.prototype.convertViewSelectionToModelSelection=function(e){var t=this._lines.convertViewPositionToModelPosition(e.selectionStartLineNumber,e.selectionStartColumn),n=this._lines.convertViewPositionToModelPosition(e.positionLineNumber,e.positionColumn);return new i.Selection(t.lineNumber,t.column,n.lineNumber,n.column)},e.prototype.validateViewPosition=function(e,t){return this._lines.validateViewPosition(e.lineNumber,e.column,t)},e.prototype.validateViewRange=function(e,t){var n=this._lines.validateViewPosition(e.startLineNumber,e.startColumn,t.getStartPosition()),i=this._lines.validateViewPosition(e.endLineNumber,e.endColumn,t.getEndPosition());return new o.Range(n.lineNumber,n.column,i.lineNumber,i.column)},e.prototype.convertModelPositionToViewPosition=function(e){return this._lines.convertModelPositionToViewPosition(e.lineNumber,e.column)},e.prototype.convertModelRangeToViewRange=function(e){var t=this._lines.convertModelPositionToViewPosition(e.startLineNumber,e.startColumn),n=this._lines.convertModelPositionToViewPosition(e.endLineNumber,e.endColumn);return new o.Range(t.lineNumber,t.column,n.lineNumber,n.column)},e.prototype.convertModelSelectionToViewSelection=function(e){var t=this._lines.convertModelPositionToViewPosition(e.selectionStartLineNumber,e.selectionStartColumn),n=this._lines.convertModelPositionToViewPosition(e.positionLineNumber,e.positionColumn);return new i.Selection(t.lineNumber,t.column,n.lineNumber,n.column)},e.prototype.modelPositionIsVisible=function(e){return this._lines.modelPositionIsVisible(e.lineNumber,e.column)},e}();t.CoordinatesConverter=d;var h=function(){function e(e,t,n,i,o,r){this.model=e,this._validModelVersionId=-1,this.tabSize=n,this.wrappingColumn=i,this.columnsForFullWidthChar=o,this.wrappingIndent=r,this.linePositionMapperFactory=t,this._constructLines(!0)}return e.prototype.dispose=function(){this.hiddenAreasIds=this.model.deltaDecorations(this.hiddenAreasIds,[])},e.prototype.createCoordinatesConverter=function(){return new d(this)},e.prototype._ensureValidState=function(){if(this.model.getVersionId()!==this._validModelVersionId)throw new Error("SplitLinesCollection: attempt to access a 'newer' model")},e.prototype._constructLines=function(e){var t=this;this.lines=[],e&&(this.hiddenAreasIds=[]);for(var n=this.model.getLinesContent(),i=n.length,s=new Uint32Array(i),a=this.hiddenAreasIds.map(function(e){return t.model.getDecorationRange(e)}).sort(o.Range.compareRangesUsingStarts),u=1,c=0,d=-1,h=d+1=u&&f<=c,m=l(this.linePositionMapperFactory,n[p],this.tabSize,this.wrappingColumn,this.columnsForFullWidthChar,this.wrappingIndent,!g);s[p]=m.getViewLineCount(),this.lines[p]=m}this._validModelVersionId=this.model.getVersionId(),this.prefixSumComputer=new r.PrefixSumComputerWithCache(s)},e.prototype.getHiddenAreas=function(){var e=this;return this.hiddenAreasIds.map(function(t){return e.model.getDecorationRange(t)}).sort(o.Range.compareRangesUsingStarts)},e.prototype._reduceRanges=function(e){var t=this;if(0===e.length)return[];for(var n=e.map(function(e){return t.model.validateRange(e)}).sort(o.Range.compareRangesUsingStarts),i=[],r=n[0].startLineNumber,s=n[0].endLineNumber,a=1,u=n.length;as+1?(i.push(new o.Range(r,1,s,1)),r=l.startLineNumber,s=l.endLineNumber):l.endLineNumber>s&&(s=l.endLineNumber)}return i.push(new o.Range(r,1,s,1)),i},e.prototype.setHiddenAreas=function(e){var t=this,n=this._reduceRanges(e),i=this.hiddenAreasIds.map(function(e){return t.model.getDecorationRange(e)}).sort(o.Range.compareRangesUsingStarts);if(n.length===i.length){for(var r=!1,s=0;s=c&&f<=d?this.lines[s].isVisible()&&(this.lines[s]=this.lines[s].setVisible(!1),g=!0):this.lines[s].isVisible()||(this.lines[s]=this.lines[s].setVisible(!0),g=!0),g){var m=this.lines[s].getViewLineCount();this.prefixSumComputer.changeValue(s,m)}}return!0},e.prototype.modelPositionIsVisible=function(e,t){return!(e<1||e>this.lines.length)&&this.lines[e-1].isVisible()},e.prototype.setTabSize=function(e){return this.tabSize!==e&&(this.tabSize=e,this._constructLines(!1),!0)},e.prototype.setWrappingSettings=function(e,t,n){return(this.wrappingIndent!==e||this.wrappingColumn!==t||this.columnsForFullWidthChar!==n)&&(this.wrappingIndent=e,this.wrappingColumn=t,this.columnsForFullWidthChar=n,this._constructLines(!1),!0)},e.prototype.onModelFlushed=function(){this._constructLines(!0)},e.prototype.onModelLinesDeleted=function(e,t,n){if(e<=this._validModelVersionId)return null;var i=1===t?1:this.prefixSumComputer.getAccumulatedValue(t-2)+1,o=this.prefixSumComputer.getAccumulatedValue(n-1);return this.lines.splice(t-1,n-t+1),this.prefixSumComputer.removeValues(t-1,n-t+1),new a.ViewLinesDeletedEvent(i,o)},e.prototype.onModelLinesInserted=function(e,t,i,o){if(e<=this._validModelVersionId)return null;for(var r=this.getHiddenAreas(),s=!1,u=new n.Position(t,1),c=0;cu?(m=(g=(h=(d=1===t?1:this.prefixSumComputer.getAccumulatedValue(t-2)+1)+u-1)+1)+(o-u)-1,c=!0):ot?t:e},e.prototype.warmUpLookupCache=function(e,t){this.prefixSumComputer.warmUpCache(e-1,t-1)},e.prototype.getViewLineIndentGuide=function(e){this._ensureValidState(),e=this._toValidViewLineNumber(e);var t=this.prefixSumComputer.getIndexOf(e-1);return this.model.getLineIndentGuide(t.index+1)},e.prototype.getViewLineContent=function(e){this._ensureValidState(),e=this._toValidViewLineNumber(e);var t=this.prefixSumComputer.getIndexOf(e-1),n=t.index,i=t.remainder;return this.lines[n].getViewLineContent(this.model,n+1,i)},e.prototype.getViewLineMinColumn=function(e){this._ensureValidState(),e=this._toValidViewLineNumber(e);var t=this.prefixSumComputer.getIndexOf(e-1),n=t.index,i=t.remainder;return this.lines[n].getViewLineMinColumn(this.model,n+1,i)},e.prototype.getViewLineMaxColumn=function(e){this._ensureValidState(),e=this._toValidViewLineNumber(e);var t=this.prefixSumComputer.getIndexOf(e-1),n=t.index,i=t.remainder;return this.lines[n].getViewLineMaxColumn(this.model,n+1,i)},e.prototype.getViewLineData=function(e){this._ensureValidState(),e=this._toValidViewLineNumber(e);var t=this.prefixSumComputer.getIndexOf(e-1),n=t.index,i=t.remainder;return this.lines[n].getViewLineData(this.model,n+1,i)},e.prototype.getViewLinesData=function(e,t,n){this._ensureValidState(),e=this._toValidViewLineNumber(e),t=this._toValidViewLineNumber(t);for(var i=this.prefixSumComputer.getIndexOf(e-1),o=e,r=i.index,s=i.remainder,a=[],u=r,l=this.model.getLineCount();ut&&(p=!0,h=t-o+1);var f=d+h;if(c.getViewLinesData(this.model,u+1,d,f,o-e,n,a),o+=h,p)break}}return a},e.prototype.validateViewPosition=function(e,t,i){this._ensureValidState(),e=this._toValidViewLineNumber(e);var o=this.prefixSumComputer.getIndexOf(e-1),r=o.index,s=o.remainder,a=this.lines[r],u=a.getViewLineMinColumn(this.model,r+1,s),l=a.getViewLineMaxColumn(this.model,r+1,s);tl&&(t=l);var c=a.getModelColumnOfViewPosition(s,t);return this.model.validatePosition(new n.Position(r+1,c)).equals(i)?new n.Position(e,t):this.convertModelPositionToViewPosition(i.lineNumber,i.column)},e.prototype.convertViewPositionToModelPosition=function(e,t){this._ensureValidState(),e=this._toValidViewLineNumber(e);var i=this.prefixSumComputer.getIndexOf(e-1),o=i.index,r=i.remainder,s=this.lines[o].getModelColumnOfViewPosition(r,t);return this.model.validatePosition(new n.Position(o+1,s))},e.prototype.convertModelPositionToViewPosition=function(e,t){this._ensureValidState();for(var i=this.model.validatePosition(new n.Position(e,t)),o=i.lineNumber,r=i.column,s=o-1,a=!1;s>0&&!this.lines[s].isVisible();)s--,a=!0;if(0===s&&!this.lines[s].isVisible())return new n.Position(1,1);var u=1+(0===s?0:this.prefixSumComputer.getAccumulatedValue(s-1));return a?this.lines[s].getViewPositionOfModelPosition(u,this.model.getLineMaxColumn(s+1)):this.lines[o-1].getViewPositionOfModelPosition(u,r)},e}();t.SplitLinesCollection=h;var p=function(){function e(){}return e.prototype.isVisible=function(){return!0},e.prototype.setVisible=function(e){return e?this:f.INSTANCE},e.prototype.getViewLineCount=function(){return 1},e.prototype.getViewLineContent=function(e,t,n){return e.getLineContent(t)},e.prototype.getViewLineMinColumn=function(e,t,n){return e.getLineMinColumn(t)},e.prototype.getViewLineMaxColumn=function(e,t,n){return e.getLineMaxColumn(t)},e.prototype.getViewLineData=function(e,t,n){var i=e.getLineTokens(t),o=i.getLineContent();return new s.ViewLineData(o,1,o.length+1,i.inflate())},e.prototype.getViewLinesData=function(e,t,n,i,o,r,s){r[o]?s[o]=this.getViewLineData(e,t,0):s[o]=null},e.prototype.getModelColumnOfViewPosition=function(e,t){return t},e.prototype.getViewPositionOfModelPosition=function(e,t){return new n.Position(e,t)},e.INSTANCE=new e,e}(),f=function(){function e(){}return e.prototype.isVisible=function(){return!1},e.prototype.setVisible=function(e){return e?p.INSTANCE:this},e.prototype.getViewLineCount=function(){return 0},e.prototype.getViewLineContent=function(e,t,n){throw new Error("Not supported")},e.prototype.getViewLineMinColumn=function(e,t,n){throw new Error("Not supported")},e.prototype.getViewLineMaxColumn=function(e,t,n){throw new Error("Not supported")},e.prototype.getViewLineData=function(e,t,n){throw new Error("Not supported")},e.prototype.getViewLinesData=function(e,t,n,i,o,r,s){throw new Error("Not supported")},e.prototype.getModelColumnOfViewPosition=function(e,t){throw new Error("Not supported")},e.prototype.getViewPositionOfModelPosition=function(e,t){throw new Error("Not supported")},e.INSTANCE=new e,e}(),g=function(){function e(e,t){this.positionMapper=e,this.wrappedIndent=this.positionMapper.getWrappedLinesIndent(),this.wrappedIndentLength=this.wrappedIndent.length,this.outputLineCount=this.positionMapper.getOutputLineCount(),this._isVisible=t}return e.prototype.isVisible=function(){return this._isVisible},e.prototype.setVisible=function(e){return this._isVisible=e,this},e.prototype.getViewLineCount=function(){return this._isVisible?this.outputLineCount:0},e.prototype.getInputStartOffsetOfOutputLineIndex=function(e){return this.positionMapper.getInputOffsetOfOutputPosition(e,0)},e.prototype.getInputEndOffsetOfOutputLineIndex=function(e,t,n){return n+1===this.outputLineCount?e.getLineMaxColumn(t)-1:this.positionMapper.getInputOffsetOfOutputPosition(n+1,0)},e.prototype.getViewLineContent=function(e,t,n){if(!this._isVisible)throw new Error("Not supported");var i=this.getInputStartOffsetOfOutputLineIndex(n),o=this.getInputEndOffsetOfOutputLineIndex(e,t,n),r=e.getLineContent(t).substring(i,o);return n>0&&(r=this.wrappedIndent+r),r},e.prototype.getViewLineMinColumn=function(e,t,n){if(!this._isVisible)throw new Error("Not supported");return n>0?this.wrappedIndentLength+1:1},e.prototype.getViewLineMaxColumn=function(e,t,n){if(!this._isVisible)throw new Error("Not supported");return this.getViewLineContent(e,t,n).length+1},e.prototype.getViewLineData=function(e,t,n){if(!this._isVisible)throw new Error("Not supported");var i=this.getInputStartOffsetOfOutputLineIndex(n),o=this.getInputEndOffsetOfOutputLineIndex(e,t,n),r=e.getLineContent(t).substring(i,o);n>0&&(r=this.wrappedIndent+r);var a=n>0?this.wrappedIndentLength+1:1,u=r.length+1,l=0;n>0&&(l=this.wrappedIndentLength);var c=e.getLineTokens(t);return new s.ViewLineData(r,a,u,c.sliceAndInflate(i,o,l))},e.prototype.getViewLinesData=function(e,t,n,i,o,r,s){if(!this._isVisible)throw new Error("Not supported");for(var a=n;a0&&(n0&&(r+=this.wrappedIndentLength),new n.Position(e+o,r)},e}();t.SplitLine=g;var m=function(){function e(e){this._lines=e}return e.prototype._validPosition=function(e){return this._lines.model.validatePosition(e)},e.prototype._validRange=function(e){return this._lines.model.validateRange(e)},e.prototype._validSelection=function(e){var t=this._validPosition(new n.Position(e.selectionStartLineNumber,e.selectionStartColumn)),o=this._validPosition(new n.Position(e.positionLineNumber,e.positionColumn));return new i.Selection(t.lineNumber,t.column,o.lineNumber,o.column)},e.prototype.convertViewPositionToModelPosition=function(e){return this._validPosition(e)},e.prototype.convertViewRangeToModelRange=function(e){return this._validRange(e)},e.prototype.convertViewSelectionToModelSelection=function(e){return this._validSelection(e)},e.prototype.validateViewPosition=function(e,t){return this._validPosition(t)},e.prototype.validateViewRange=function(e,t){return this._validRange(t)},e.prototype.convertModelPositionToViewPosition=function(e){return this._validPosition(e)},e.prototype.convertModelRangeToViewRange=function(e){return this._validRange(e)},e.prototype.convertModelSelectionToViewSelection=function(e){return this._validSelection(e)},e.prototype.modelPositionIsVisible=function(e){var t=this._lines.model.getLineCount();return!(e.lineNumber<1||e.lineNumber>t)},e}();t.IdentityCoordinatesConverter=m;var v=function(){function e(e){this.model=e}return e.prototype.dispose=function(){},e.prototype.createCoordinatesConverter=function(){return new m(this)},e.prototype.setHiddenAreas=function(e){return!1},e.prototype.setTabSize=function(e){return!1},e.prototype.setWrappingSettings=function(e,t,n){return!1},e.prototype.onModelFlushed=function(){},e.prototype.onModelLinesDeleted=function(e,t,n){return new a.ViewLinesDeletedEvent(t,n)},e.prototype.onModelLinesInserted=function(e,t,n,i){return new a.ViewLinesInsertedEvent(t,n)},e.prototype.onModelLineChanged=function(e,t,n){return[!1,new a.ViewLinesChangedEvent(t,t),null,null]},e.prototype.acceptVersionId=function(e){},e.prototype.getViewLineCount=function(){return this.model.getLineCount()},e.prototype.warmUpLookupCache=function(e,t){},e.prototype.getViewLineIndentGuide=function(e){return 0},e.prototype.getViewLineContent=function(e){return this.model.getLineContent(e)},e.prototype.getViewLineMinColumn=function(e){return this.model.getLineMinColumn(e)},e.prototype.getViewLineMaxColumn=function(e){return this.model.getLineMaxColumn(e)},e.prototype.getViewLineData=function(e){var t=this.model.getLineTokens(e),n=t.getLineContent();return new s.ViewLineData(n,1,n.length+1,t.inflate())},e.prototype.getViewLinesData=function(e,t,n){var i=this.model.getLineCount();e=Math.min(Math.max(1,e),i),t=Math.min(Math.max(1,t),i);for(var o=[],r=e;r<=t;r++){var s=r-e;n[s]||(o[s]=null),o[s]=this.getViewLineData(r)}return o},e}();t.IdentityLinesCollection=v}),define(d[329],h([1,0,9,104,179,89,96,49]),function(e,t,n,i,o,r,s,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var u;!function(e){e[e.NONE=0]="NONE",e[e.BREAK_BEFORE=1]="BREAK_BEFORE",e[e.BREAK_AFTER=2]="BREAK_AFTER",e[e.BREAK_OBTRUSIVE=3]="BREAK_OBTRUSIVE",e[e.BREAK_IDEOGRAPHIC=4]="BREAK_IDEOGRAPHIC"}(u||(u={}));var l=function(e){function t(t,n,i){for(var o=e.call(this,0)||this,r=0;r=12352&&t<=12543||t>=13312&&t<=19903||t>=19968&&t<=40959?4:e.prototype.get.call(this,t)},t}(r.CharacterClassifier),c=function(){function e(e,t,n){this.classifier=new l(e,t,n)}return e.nextVisibleColumn=function(e,t,n,i){return e=+e,t=+t,i=+i,n?e+(t-e%t):e+i},e.prototype.createLineMapping=function(t,o,r,u,l){if(-1===r)return null;o=+o,r=+r,u=+u;var c=0,h="",p=-1;if((l=+l)!==a.WrappingIndent.None&&-1!==(p=n.firstNonWhitespaceIndex(t))){h=t.substring(0,p);for(E=0;E.5*r&&(h="",c=0)}for(var f=this.classifier,g=0,m=[],v=0,_=0,y=-1,C=0,b=-1,w=0,S=t.length,E=0;E0){var M=t.charCodeAt(E-1);1!==f.get(M)&&(y=E,C=c)}var T=1;if(n.isFullWidthCharacter(L)&&(T=u),(_=e.nextVisibleColumn(_,o,x,T))>r&&0!==E){var k=void 0,I=void 0;-1!==y&&C<=r?(k=y,I=C):-1!==b&&w<=r?(k=b,I=w):(k=E,I=c),m[v++]=k-g,g=k,_=e.nextVisibleColumn(I,o,x,T),y=-1,C=0,b=-1,w=0}if(-1!==y&&(C=e.nextVisibleColumn(C,o,x,T)),-1!==b&&(w=e.nextVisibleColumn(w,o,x,T)),2===N&&(l===a.WrappingIndent.None||E>=p)&&(y=E+1,C=c),4===N&&E'+this._getHTMLToCopy(n,s)+""},t.prototype._getHTMLToCopy=function(e,t){for(var n=e.startLineNumber,i=e.startColumn,o=e.endLineNumber,r=e.endColumn,s=this.getTabSize(),u="",l=n;l<=o;l++){var c=this.model.getLineTokens(l),d=c.getLineContent(),h=l===n?i-1:0,p=l===o?r-1:d.length;u+=""===d?"
    ":a.tokenizeLineToHTML(d,c.inflate(),t,h,p,s)}return u},t.prototype._getColorMap=function(){for(var e=s.TokenizationRegistry.getColorMap(),t=[null],n=1,i=e.length;n0},e.prototype._cannotFind=function(){if(!this._hasMatches()){var e=this._decorations.getFindScope();return e&&this._editor.revealRangeInCenterIfOutsideViewport(e),!0}return!1},e.prototype._setCurrentFindMatch=function(e){var t=this._decorations.setCurrentFindMatch(e);this._state.changeMatchInfo(t,this._decorations.getCount(),e),this._editor.setSelection(e),this._editor.revealRangeInCenterIfOutsideViewport(e)},e.prototype._moveToPrevMatch=function(t,n){if(void 0===n&&(n=!1),!this._cannotFind()){var i=this._decorations.getFindScope(),o=e._getSearchRange(this._editor.getModel(),this._state.isReplaceRevealed,i);o.getEndPosition().isBefore(t)&&(t=o.getEndPosition()),t.isBefore(o.getStartPosition())&&(t=o.getEndPosition());var r=t.lineNumber,a=t.column,u=this._editor.getModel(),l=new s.Position(r,a),c=u.findPreviousMatch(this._state.searchString,l,this._state.isRegex,this._state.matchCase,this._state.wholeWord?this._editor.getConfiguration().wordSeparators:null,!1);return c&&c.range.isEmpty()&&c.range.getStartPosition().equals(l)&&(this._state.isRegex&&(this._state.searchString.indexOf("^")>=0||this._state.searchString.indexOf("$")>=0)||1===a?(1===r?r=u.getLineCount():r--,a=u.getLineMaxColumn(r)):a--,l=new s.Position(r,a),c=u.findPreviousMatch(this._state.searchString,l,this._state.isRegex,this._state.matchCase,this._state.wholeWord?this._editor.getConfiguration().wordSeparators:null,!1)),c?n||o.containsRange(c.range)?void this._setCurrentFindMatch(c.range):this._moveToPrevMatch(c.range.getStartPosition(),!0):null}},e.prototype.moveToPrevMatch=function(){this._moveToPrevMatch(this._editor.getSelection().getStartPosition())},e.prototype._moveToNextMatch=function(e){var t=this._getNextMatch(e,!1,!0);t&&this._setCurrentFindMatch(t.range)},e.prototype._getNextMatch=function(t,n,i,o){if(void 0===o&&(o=!1),this._cannotFind())return null;var r=this._decorations.getFindScope(),a=e._getSearchRange(this._editor.getModel(),this._state.isReplaceRevealed,r);a.getEndPosition().isBefore(t)&&(t=a.getStartPosition()),t.isBefore(a.getStartPosition())&&(t=a.getStartPosition());var u=t.lineNumber,l=t.column,c=this._editor.getModel(),d=new s.Position(u,l),h=c.findNextMatch(this._state.searchString,d,this._state.isRegex,this._state.matchCase,this._state.wholeWord?this._editor.getConfiguration().wordSeparators:null,n);return i&&h&&h.range.isEmpty()&&h.range.getStartPosition().equals(d)&&(this._state.isRegex&&(this._state.searchString.indexOf("^")>=0||this._state.searchString.indexOf("$")>=0)||l===c.getLineMaxColumn(u)?(u===c.getLineCount()?u=1:u++,l=1):l++,d=new s.Position(u,l),h=c.findNextMatch(this._state.searchString,d,this._state.isRegex,this._state.matchCase,this._state.wholeWord?this._editor.getConfiguration().wordSeparators:null,n)),h?o||a.containsRange(h.range)?h:this._getNextMatch(h.range.getEndPosition(),n,i,!0):null},e.prototype.moveToNextMatch=function(){this._moveToNextMatch(this._editor.getSelection().getEndPosition())},e.prototype._getReplacePattern=function(){return this._state.isRegex?o.parseReplaceString(this._state.replaceString):o.ReplacePattern.fromStaticValue(this._state.replaceString)},e.prototype.replace=function(){if(this._hasMatches()){var e=this._getReplacePattern(),t=this._editor.getSelection(),n=this._getNextMatch(t.getStartPosition(),e.hasReplacementPatterns,!1);if(n)if(t.equalsRange(n.range)){var i=e.buildReplaceString(n.matches),o=new r.ReplaceCommand(t,i);this._executeEditorCommand("replace",o),this._decorations.setStartPosition(new s.Position(t.startLineNumber,t.startColumn+i.length)),this.research(!0)}else this._decorations.setStartPosition(this._editor.getPosition()),this._setCurrentFindMatch(n.range)}},e.prototype._findMatches=function(t,n,i){var o=e._getSearchRange(this._editor.getModel(),this._state.isReplaceRevealed,t);return this._editor.getModel().findMatches(this._state.searchString,o,this._state.isRegex,this._state.matchCase,this._state.wholeWord?this._editor.getConfiguration().wordSeparators:null,n,i)},e.prototype.replaceAll=function(){if(this._hasMatches()){var e=this._decorations.getFindScope();null===e&&this._state.matchesCount>=t.MATCHES_LIMIT?this._largeReplaceAll():this._regularReplaceAll(e),this.research(!1)}},e.prototype._largeReplaceAll=function(){var e=new h.SearchParams(this._state.searchString,this._state.isRegex,this._state.matchCase,this._state.wholeWord?this._editor.getConfiguration().wordSeparators:null).parseSearchRequest();if(e){var t,n=this._editor.getModel(),i=n.getValue(u.EndOfLinePreference.LF),o=n.getFullModelRange(),s=this._getReplacePattern();t=s.hasReplacementPatterns?i.replace(e.regex,function(){return s.buildReplaceString(arguments)}):i.replace(e.regex,s.buildReplaceString(null));var a=new r.ReplaceCommandThatPreservesSelection(o,t,this._editor.getSelection());this._executeEditorCommand("replaceAll",a)}},e.prototype._regularReplaceAll=function(e){for(var t=this._getReplacePattern(),n=this._findMatches(e,t.hasReplacementPatterns,1073741824),i=[],o=0,r=n.length;o=e.startLineNumber&&t<=e.endLineNumber}function u(e,t){return te.endLineNumber}function c(e,t){return e instanceof i.Range&&t instanceof i.Range?e.containsRange(t):e.startLineNumber<=t.startLineNumber&&e.endLineNumber>=t.endLineNumber}function d(e,t,n,i,o){return(o?new f(e,t,n):new p(e,t,n)).getRegionsTill(i)}Object.defineProperty(t,"__esModule",{value:!0}),t.toString=function(e){return(e?e.startLineNumber+"/"+e.endLineNumber:"null")+(e.isCollapsed?" (collapsed)":"")+" - "+e.indent};var h=function(){function e(e,t,n){this.decorationIds=[],this.update(e,t,n)}return Object.defineProperty(e.prototype,"isCollapsed",{get:function(){return this._isCollapsed},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"isExpanded",{get:function(){return!this._isCollapsed},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"indent",{get:function(){return this._indent},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"foldingRange",{get:function(){return this._lastRange},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"startLineNumber",{get:function(){return this._lastRange?this._lastRange.startLineNumber:void 0},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"endLineNumber",{get:function(){return this._lastRange?this._lastRange.endLineNumber:void 0},enumerable:!0,configurable:!0}),e.prototype.setCollapsed=function(e,t){this._isCollapsed=e,this.decorationIds.length>0&&t.changeDecorationOptions(this.decorationIds[0],this.getVisualDecorationOptions())},e.prototype.getDecorationRange=function(e){return this.decorationIds.length>0?e.getDecorationRange(this.decorationIds[1]):null},e.prototype.getVisualDecorationOptions=function(){return this._isCollapsed?e._COLLAPSED_VISUAL_DECORATION:e._EXPANDED_VISUAL_DECORATION},e.prototype.getRangeDecorationOptions=function(){return e._RANGE_DECORATION},e.prototype.update=function(e,t,n){this._lastRange=e,this._isCollapsed=!!e.isCollapsed,this._indent=e.indent;var i=[],o=t.getLineMaxColumn(e.startLineNumber),r={startLineNumber:e.startLineNumber,startColumn:o-1,endLineNumber:e.startLineNumber,endColumn:o};i.push({range:r,options:this.getVisualDecorationOptions()});var s={startLineNumber:e.startLineNumber,startColumn:1,endLineNumber:e.endLineNumber,endColumn:t.getLineMaxColumn(e.endLineNumber)};i.push({range:s,options:this.getRangeDecorationOptions()}),this.decorationIds=n.deltaDecorations(this.decorationIds,i)},e.prototype.dispose=function(e){this._lastRange=null,this.decorationIds=e.deltaDecorations(this.decorationIds,[])},e.prototype.toString=function(){var e=this.isCollapsed?"collapsed ":"expanded ";return this._lastRange?e+=this._lastRange.startLineNumber+"/"+this._lastRange.endLineNumber:e+="no range",e},e._COLLAPSED_VISUAL_DECORATION=o.ModelDecorationOptions.register({stickiness:n.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,afterContentClassName:"inline-folded",linesDecorationsClassName:"folding collapsed"}),e._EXPANDED_VISUAL_DECORATION=o.ModelDecorationOptions.register({stickiness:n.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,linesDecorationsClassName:"folding"}),e._RANGE_DECORATION=o.ModelDecorationOptions.register({stickiness:n.TrackedRangeStickiness.GrowsOnlyWhenTypingBefore}),e}();t.CollapsibleRegion=h,t.getCollapsibleRegionsToFoldAtLine=function(e,t,n,i,o){var s=r(e,t,n);return s?1===i?[s]:d(s,e,t,i,o).filter(function(e){return!e.isCollapsed}):[]},t.getCollapsibleRegionsToUnfoldAtLine=function(e,t,n,i){var o=r(e,t,n);if(!o)return[];if(1===i){var a=o.isCollapsed?o:s(e,t,o,n);return a?[a]:[]}return d(o,e,t,i,!1).filter(function(e){return e.isCollapsed})},t.doesLineBelongsToCollapsibleRegion=a;var p=function(){function e(e,t,n){this.region=e,this.children=[];for(var i=t.indexOf(e)+1;i0?r.lastChildIndex:o},e.prototype.getRegionsTill=function(e){var t=[this.region];return e>1&&this.children.forEach(function(n){return t=t.concat(n.getRegionsTill(e-1))}),t},e}(),f=function(){function e(t,n,i){this.region=t;for(var o=n.indexOf(t)-1;o>=0;o--){var r=n[o],s=r.getDecorationRange(i);if(s){if(c(s,t.foldingRange)){this.parent=new e(r,n,i);break}if(l(s,t.foldingRange.endLineNumber))break}}}return e.prototype.getRegionsTill=function(e){var t=[this.region];return this.parent&&e>1&&(t=t.concat(this.parent.getRegionsTill(e-1))),t},e}()}),define(d[334],h([1,0,9,20,59,142,22,2,33,3,271,34,394]),function(e,t,n,i,o,r,s,a,u,l,c,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var h=function(){function e(e,t,n){this._nestingLevel=1,this._editor=e,this._snippet=t,this._offset=n,this._placeholderGroups=u.groupBy(t.placeholders,r.Placeholder.compareByIndex),this._placeholderGroupsIdx=-1}return e.prototype.dispose=function(){var e=this;this._placeholderDecorations&&this._editor.changeDecorations(function(t){return e._placeholderDecorations.forEach(function(e){return t.removeDecoration(e)})}),this._placeholderGroups.length=0},e.prototype._initDecorations=function(){var t=this;if(!this._placeholderDecorations){this._placeholderDecorations=new Map;var n=this._editor.getModel();this._editor.changeDecorations(function(i){for(var o=0,r=t._snippet.placeholders;o0&&(this._placeholderGroupsIdx-=1),this._editor.getModel().changeDecorations(function(t){for(var i=new Set,o=[],r=0,a=n._placeholderGroups[n._placeholderGroupsIdx];r0},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"placeholderRanges",{get:function(){var e=this,t=[];return this._placeholderDecorations.forEach(function(n,i){if(!i.isFinalTabstop){var o=e._editor.getModel().getDecorationRange(n);o&&t.push(o)}}),t},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"choice",{get:function(){return this._placeholderGroups[this._placeholderGroupsIdx][0].choice},enumerable:!0,configurable:!0}),e.prototype.merge=function(t){var n=this,i=this._editor.getModel();this._nestingLevel*=10,this._editor.changeDecorations(function(o){for(var s=0,l=n._placeholderGroups[n._placeholderGroupsIdx];st.length)return!1;e.sort(a.Range.compareRangesUsingStarts),t.sort(a.Range.compareRangesUsingStarts);e:for(var r=0,s=e;r=a&&(t=a);var u=0,l=0;if(this.options.showArrow&&(u=Math.round(s/3),this._arrow.height=u,this._arrow.show(i)),this.options.showFrame&&(l=Math.round(s/9)),this.editor.changeViewZones(function(e){n._viewZone&&e.removeZone(n._viewZone.id),n._overlayWidget&&(n.editor.removeOverlayWidget(n._overlayWidget),n._overlayWidget=null),n.domNode.style.top="-1000px",n._viewZone=new p(r,i.lineNumber,i.column,t,function(e){return n._onViewZoneTop(e)},function(e){return n._onViewZoneHeight(e)}),n._viewZone.id=e.addZone(n._viewZone),n._overlayWidget=new g("vs.editor.contrib.zoneWidget"+n._viewZone.id,n.domNode),n.editor.addOverlayWidget(n._overlayWidget)}),this.options.showFrame){var c=this.options.frameWidth?this.options.frameWidth:l;this.container.style.borderTopWidth=c+"px",this.container.style.borderBottomWidth=c+"px"}var d=t*s-this._decoratingElementsHeight();this.container.style.top=u+"px",this.container.style.height=d+"px",this.container.style.overflow="hidden",this._doLayout(d,o),this.editor.setSelection(e);var h=Math.min(this.editor.getModel().getLineCount(),Math.max(1,e.endLineNumber+1));this.editor.revealLine(h)},t.prototype.setCssClass=function(e,t){t&&this.container.classList.remove(t),r.addClass(this.container,e)},t.prototype._onWidth=function(e){},t.prototype._doLayout=function(e,t){},t.prototype._relayout=function(e){var t=this;this._viewZone.heightInLines!==e&&this.editor.changeViewZones(function(n){t._viewZone.heightInLines=e,n.layoutZone(t._viewZone.id)})},t.prototype._initSash=function(){var e=this;this._resizeSash=new s.Sash(this.domNode,this,{orientation:s.Orientation.HORIZONTAL}),this.options.isResizeable||(this._resizeSash.hide(),this._resizeSash.disable());var t;this._disposables.push(this._resizeSash.addListener("start",function(n){e._viewZone&&(t={startY:n.startY,heightInLines:e._viewZone.heightInLines})})),this._disposables.push(this._resizeSash.addListener("end",function(){t=void 0})),this._disposables.push(this._resizeSash.addListener("change",function(n){if(t){var i=(n.currentY-t.startY)/e.editor.getConfiguration().lineHeight,o=i<0?Math.ceil(i):Math.floor(i),r=t.heightInLines+o;r>5&&r<35&&e._relayout(r)}}))},t.prototype.getHorizontalSashLeft=function(){return 0},t.prototype.getHorizontalSashTop=function(){return parseInt(this.domNode.style.height)-this._decoratingElementsHeight()/2},t.prototype.getHorizontalSashWidth=function(){var e=this.editor.getLayoutInfo();return e.width-e.minimapWidth},t}(i.Widget);t.ZoneWidget=v}),define(d[336],h([6,8]),function(e,t){return e.create("vs/editor/common/modes/modesRegistry",t)}),define(d[337],h([6,8]),function(e,t){return e.create("vs/editor/common/services/bulkEdit",t)}),define(d[338],h([1,0,337,33,157,3,24,7,59,2,22]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";function d(e,t,i){function r(){for(var e,t=0,i=u;t1&&t>1?n.localize(2,null,e,t):n.localize(3,null,e,t)}}}Object.defineProperty(t,"__esModule",{value:!0});var h=function(){function e(e){this._fileService=e}return e.prototype.start=function(){var e,t=Object.create(null);return this._fileService&&(e=this._fileService.onFileChanges(function(e){e.changes.forEach(function(e){var n=String(e.resource),i=t[n];i||(t[n]=i=[]),i.push(e)})})),{stop:function(){return e&&e.dispose()},hasChanged:function(e){return!!t[e.toString()]},allChanges:function(){return i.flatten(o.values(t))}}},e}(),p=function(){function e(e){this._endCursorSelection=null,this._modelReference=e,this._edits=[]}return Object.defineProperty(e.prototype,"_model",{get:function(){return this._modelReference.object.textEditorModel},enumerable:!0,configurable:!0}),e.prototype.addEdit=function(e){if("number"==typeof e.newEol&&(this._newEol=e.newEol),e.range||e.newText){var t=void 0;t=e.range?l.Range.lift(e.range):this._model.getFullModelRange(),this._edits.push(u.EditOperation.replaceMove(t,e.newText))}},e.prototype.apply=function(){var e=this;this._edits.length>0&&(this._edits=this._edits.map(function(e,t){return{value:e,index:t}}).sort(function(e,t){var n=l.Range.compareRangesUsingStarts(e.value.range,t.value.range);return 0===n&&(n=e.index-t.index),n}).map(function(e){return e.value}),this._initialSelections=this._getInitialSelections(),this._model.pushEditOperations(this._initialSelections,this._edits,function(t){return e._getEndCursorSelections(t)})),void 0!==this._newEol&&this._model.setEOL(this._newEol)},e.prototype._getInitialSelections=function(){var e=this._edits[0].range;return[new c.Selection(e.startLineNumber,e.startColumn,e.endLineNumber,e.endColumn)]},e.prototype._getEndCursorSelections=function(e){for(var t=0,n=0;nt.prefixLen?-1:e.prefixLent.offsetDist?1:0})[0];if(n)return this._references[n.idx]},e.prototype.dispose=function(){this._groups=s.dispose(this._groups)},e._compareReferences=function(e,t){var n=e.uri.toString(),i=t.uri.toString();return ni?1:c.Range.compareRangesUsingStarts(e.range,t.range)},e}();t.ReferencesModel=f}),define(d[367],h([6,8]),function(e,t){return e.create("vs/editor/contrib/referenceSearch/browser/referencesWidget",t)}),define(d[368],h([6,8]),function(e,t){return e.create("vs/editor/contrib/rename/browser/rename",t)}),define(d[369],h([6,8]),function(e,t){return e.create("vs/editor/contrib/rename/browser/renameInputField",t)}),define(d[370],h([6,8]),function(e,t){return e.create("vs/editor/contrib/smartSelect/common/smartSelect",t)}),define(d[371],h([6,8]),function(e,t){return e.create("vs/editor/contrib/suggest/browser/suggestController",t)}),define(d[372],h([6,8]),function(e,t){return e.create("vs/editor/contrib/suggest/browser/suggestWidget",t)}),define(d[373],h([6,8]),function(e,t){return e.create("vs/editor/contrib/toggleTabFocusMode/common/toggleTabFocusMode",t)}),define(d[374],h([6,8]),function(e,t){return e.create("vs/editor/contrib/wordHighlighter/common/wordHighlighter",t)}),define(d[375],h([6,8]),function(e,t){return e.create("vs/editor/contrib/zoneWidget/browser/peekViewWidget",t)}),define(d[376],h([6,8]),function(e,t){return e.create("vs/editor/standalone/browser/inspectTokens/inspectTokens",t)}),define(d[377],h([6,8]),function(e,t){return e.create("vs/editor/standalone/browser/quickOpen/gotoLine",t)}),define(d[378],h([6,8]),function(e,t){return e.create("vs/editor/standalone/browser/quickOpen/quickCommand",t)}),define(d[379],h([6,8]),function(e,t){return e.create("vs/editor/standalone/browser/quickOpen/quickOutline",t)}),define(d[380],h([6,8]),function(e,t){return e.create("vs/editor/standalone/browser/standaloneCodeEditor",t)}),define(d[381],h([6,8]),function(e,t){return e.create("vs/editor/standalone/browser/toggleHighContrast/toggleHighContrast",t)}),define(d[382],h([6,8]),function(e,t){return e.create("vs/platform/configuration/common/configurationRegistry",t)}),define(d[383],h([6,8]),function(e,t){return e.create("vs/platform/keybinding/common/abstractKeybindingService",t)}),define(d[384],h([6,8]),function(e,t){return e.create("vs/platform/message/common/message",t)}),define(d[385],h([6,8]),function(e,t){return e.create("vs/platform/theme/common/colorRegistry",t)}),define(d[386],h([1,0,52,3,47,53,281,79,36,409]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l=function(){function e(e,t,n,i){var o=this;this.setContainer(e),this.contextViewService=t,this.telemetryService=n,this.messageService=i,this.actionRunner=new r.ActionRunner,this.menuContainerElement=null,this.toDispose=[];var s=!1;this.toDispose.push(this.actionRunner.addListener(a.EventType.BEFORE_RUN,function(e){o.telemetryService&&o.telemetryService.publicLog("workbenchActionExecuted",{id:e.action.id,from:"contextMenu"}),(s=!!e.retainActionItem)||o.contextViewService.hideContextView(!1)})),this.toDispose.push(this.actionRunner.addListener(a.EventType.RUN,function(e){s&&o.contextViewService.hideContextView(!1),s=!1,e.error&&o.messageService&&o.messageService.show(u.default.Error,e.error)}))}return e.prototype.setContainer=function(e){var t=this;this.$el&&(this.$el.off(["click","mousedown"]),this.$el=null),e&&(this.$el=n.$(e),this.$el.on("mousedown",function(e){return t.onMouseDown(e)}))},e.prototype.showContextMenu=function(e){var t=this;e.getActions().done(function(n){t.contextViewService.showContextView({getAnchor:function(){return e.getAnchor()},canRelayout:!1,render:function(o){t.menuContainerElement=o;var r=e.getMenuClassName?e.getMenuClassName():"";r&&(o.className+=" "+r);var u=new s.Menu(o,n,{actionItemProvider:e.getActionItem,context:e.getActionsContext?e.getActionsContext():null,actionRunner:t.actionRunner}),l=u.addListener(a.EventType.CANCEL,function(e){t.contextViewService.hideContextView(!0)}),c=u.addListener(a.EventType.BLUR,function(e){t.contextViewService.hideContextView(!0)});return u.focus(),i.combinedDisposable([l,c,u])},onHide:function(n){e.onHide&&e.onHide(n),t.menuContainerElement=null}})})},e.prototype.onMouseDown=function(e){if(this.menuContainerElement){for(var t=new o.StandardMouseEvent(e).target;t;){if(t===this.menuContainerElement)return;t=t.parentElement}this.contextViewService.hideContextView()}},e.prototype.dispose=function(){this.setContainer(null)},e}();t.ContextMenuHandler=l}),define(d[387],h([1,0,386]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t,i,o){this.contextMenuHandler=new n.ContextMenuHandler(e,o,t,i)}return e.prototype.dispose=function(){this.contextMenuHandler.dispose()},e.prototype.setContainer=function(e){this.contextMenuHandler.setContainer(e)},e.prototype.showContextMenu=function(e){this.contextMenuHandler.showContextMenu(e)},e}();t.ContextMenuService=i}),define(d[187],h([1,0,10]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e){this._staticArguments=e}return e.prototype.appendStaticArguments=function(e){this._staticArguments.push.apply(this._staticArguments,e)},e.prototype.staticArguments=function(e){return isNaN(e)?this._staticArguments.slice(0):this._staticArguments[e]},e.prototype._validate=function(e){if(!e)throw n.illegalArgument("can not be falsy")},e}();t.AbstractDescriptor=i;var o=function(e){function t(t){for(var n=[],i=1;im&&(this._editorWorkerClient.dispose(),this._editorWorkerClient=null)},t.prototype.withWorker=function(){return this._lastWorkerUsedTime=(new Date).getTime(),this._editorWorkerClient||(this._editorWorkerClient=new E(this._modelService,"editorWorkerService")),o.TPromise.as(this._editorWorkerClient)},t}(i.Disposable),w=function(e){function t(t,i,o){var r=e.call(this)||this;if(r._syncedModels=Object.create(null),r._syncedModelsLastUsedTime=Object.create(null),r._proxy=t,r._modelService=i,!o){var s=new n.IntervalTimer;s.cancelAndSet(function(){return r._checkStopModelSync()},Math.round(g/2)),r._register(s)}return r}return f(t,e),t.prototype.dispose=function(){for(var t in this._syncedModels)i.dispose(this._syncedModels[t]);this._syncedModels=Object.create(null),this._syncedModelsLastUsedTime=Object.create(null),e.prototype.dispose.call(this)},t.prototype.esureSyncedResources=function(e){for(var t=0;tg&&t.push(n);for(var i=0;i=0&&(t.splice(i,1),0===t.length&&n._commands.delete(e))}else r(t)&&n._commands.delete(e)}}},e.prototype.getCommand=function(e){var t=this._commands.get(e);return Array.isArray(t)?t[0]:t},e.prototype.getCommands=function(){var e=this,t=Object.create(null);return this._commands.forEach(function(n,i){t[i]=e.getCommand(i)}),t},e}()),t.NullCommandService={_serviceBrand:void 0,onWillExecuteCommand:function(){return{dispose:function(){}}},executeCommand:function(){return n.TPromise.as(void 0)}}}),define(d[401],h([1,0,10,24,7,2,17,18,31,56]),function(e,t,n,i,o,r,s,a,u,l){"use strict";function c(e){var t=[],i=s.LinkProviderRegistry.ordered(e).reverse().map(function(i){return a.asWinJsPromise(function(t){return i.provideLinks(e,t)}).then(function(e){if(Array.isArray(e)){var n=e.map(function(e){return new h(e,i)});t=d(t,n)}},n.onUnexpectedExternalError)});return o.TPromise.join(i).then(function(){return t})}function d(e,t){var n,i,o,s,a,u,l=[];for(n=0,o=0,i=e.length,s=t.length;n=0&&i.splice(e,1)}}},e.prototype.getMenuItems=function(e){var t=e.id,n=this._menuItems[t]||[];return t===s.CommandPalette.id&&this._appendImplicitItems(n),n},e.prototype._appendImplicitItems=function(e){for(var t=new Set,n=0,i=e;n=0){t=e.split("!=");return new u(t[0].trim(),this._deserializeValue(t[1]))}if(e.indexOf("==")>=0){var t=e.split("==");return new a(t[0].trim(),this._deserializeValue(t[1]))}return/^\!\s*/.test(e)?new l(e.substr(1).trim()):new s(e)},e._deserializeValue=function(e){if("true"===(e=e.trim()))return!0;if("false"===e)return!1;var t=/^'([^']*)'$/.exec(e);return t?t[1].trim():e},e}();t.ContextKeyExpr=r;var s=function(){function e(e){this.key=e}return e.prototype.getType=function(){return o.Defined},e.prototype.cmp=function(e){return this.keye.key?1:0},e.prototype.equals=function(t){return t instanceof e&&this.key===t.key},e.prototype.evaluate=function(e){return!!e.getValue(this.key)},e.prototype.normalize=function(){return this},e.prototype.serialize=function(){return this.key},e.prototype.keys=function(){return[this.key]},e}();t.ContextKeyDefinedExpr=s;var a=function(){function e(e,t){this.key=e,this.value=t}return e.prototype.getType=function(){return o.Equals},e.prototype.cmp=function(e){return this.keye.key?1:this.valuee.value?1:0},e.prototype.equals=function(t){return t instanceof e&&(this.key===t.key&&this.value===t.value)},e.prototype.evaluate=function(e){return e.getValue(this.key)==this.value},e.prototype.normalize=function(){return"boolean"==typeof this.value?this.value?new s(this.key):new l(this.key):this},e.prototype.serialize=function(){return"boolean"==typeof this.value?this.normalize().serialize():this.key+" == '"+this.value+"'"},e.prototype.keys=function(){return[this.key]},e}();t.ContextKeyEqualsExpr=a;var u=function(){function e(e,t){this.key=e,this.value=t}return e.prototype.getType=function(){return o.NotEquals},e.prototype.cmp=function(e){return this.keye.key?1:this.valuee.value?1:0},e.prototype.equals=function(t){return t instanceof e&&(this.key===t.key&&this.value===t.value)},e.prototype.evaluate=function(e){return e.getValue(this.key)!=this.value},e.prototype.normalize=function(){return"boolean"==typeof this.value?this.value?new l(this.key):new s(this.key):this},e.prototype.serialize=function(){return"boolean"==typeof this.value?this.normalize().serialize():this.key+" != '"+this.value+"'"},e.prototype.keys=function(){return[this.key]},e}();t.ContextKeyNotEqualsExpr=u;var l=function(){function e(e){this.key=e}return e.prototype.getType=function(){return o.Not},e.prototype.cmp=function(e){return this.keye.key?1:0},e.prototype.equals=function(t){return t instanceof e&&this.key===t.key},e.prototype.evaluate=function(e){return!e.getValue(this.key)},e.prototype.normalize=function(){return this},e.prototype.serialize=function(){return"!"+this.key},e.prototype.keys=function(){return[this.key]},e}();t.ContextKeyNotExpr=l;var c=function(){function e(t){this.expr=e._normalizeArr(t)}return e.prototype.getType=function(){return o.And},e.prototype.equals=function(t){if(t instanceof e){if(this.expr.length!==t.expr.length)return!1;for(var n=0,i=this.expr.length;n0&&t.push([s,a])}return t},e._fillInKbExprKeys=function(e,t){if(e)for(var n=0,i=e.keys();ns)return 1;var a="string"==typeof e.command.title?e.command.title:e.command.title.value,u="string"==typeof t.command.title?t.command.title:t.command.title.value;return a.localeCompare(u)},e=v([y(2,s.ICommandService),y(3,o.IContextKeyService)],e)}();t.Menu=a}),define(d[69],h([1,0,53,16]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IContextViewService=i.createDecorator("contextViewService"),t.IContextMenuService=i.createDecorator("contextMenuService");var o=function(e){function t(t,n){var i=e.call(this,"contextsubmenu",t,"",!0)||this;return i.entries=n,i}return f(t,e),t}(n.Action);t.ContextSubMenu=o}),define(d[64],h([1,0,16]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IEditorService=n.createDecorator("editorService");var i;!function(e){e[e.ONE=0]="ONE",e[e.TWO=1]="TWO",e[e.THREE=2]="THREE"}(i=t.Position||(t.Position={})),t.POSITIONS=[i.ONE,i.TWO,i.THREE];!function(e){e[e.LEFT=0]="LEFT",e[e.RIGHT=1]="RIGHT"}(t.Direction||(t.Direction={}));!function(e){e[e.SHORT=0]="SHORT",e[e.MEDIUM=1]="MEDIUM",e[e.LONG=2]="LONG"}(t.Verbosity||(t.Verbosity={}))}),define(d[156],h([1,0,16]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IEnvironmentService=n.createDecorator("environmentService")}),define(d[411],h([1,0,45,79,15,16,9]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IFileService=r.createDecorator("fileService");!function(e){e[e.CREATE=0]="CREATE",e[e.DELETE=1]="DELETE",e[e.MOVE=2]="MOVE",e[e.COPY=3]="COPY",e[e.IMPORT=4]="IMPORT"}(t.FileOperation||(t.FileOperation={}));var a=function(){function e(e,t,n){this._resource=e,this._operation=t,this._target=n}return Object.defineProperty(e.prototype,"resource",{get:function(){return this._resource},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"target",{get:function(){return this._target},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"operation",{get:function(){return this._operation},enumerable:!0,configurable:!0}),e}();t.FileOperationEvent=a;var u;!function(e){e[e.UPDATED=0]="UPDATED",e[e.ADDED=1]="ADDED",e[e.DELETED=2]="DELETED"}(u=t.FileChangeType||(t.FileChangeType={}));var l=function(e){function t(t){var n=e.call(this)||this;return n._changes=t,n}return f(t,e),Object.defineProperty(t.prototype,"changes",{get:function(){return this._changes},enumerable:!0,configurable:!0}),t.prototype.contains=function(e,t){return!!e&&this._changes.some(function(i){return i.type===t&&(t===u.DELETED?n.isEqualOrParent(e.fsPath,i.resource.fsPath,!o.isLinux):n.isEqual(e.fsPath,i.resource.fsPath,!o.isLinux))})},t.prototype.getAdded=function(){return this.getOfType(u.ADDED)},t.prototype.gotAdded=function(){return this.hasType(u.ADDED)},t.prototype.getDeleted=function(){return this.getOfType(u.DELETED)},t.prototype.gotDeleted=function(){return this.hasType(u.DELETED)},t.prototype.getUpdated=function(){return this.getOfType(u.UPDATED)},t.prototype.gotUpdated=function(){return this.hasType(u.UPDATED)},t.prototype.getOfType=function(e){return this._changes.filter(function(t){return t.type===e})},t.prototype.hasType=function(e){return this._changes.some(function(t){return t.type===e})},t}(i.Event);t.FileChangesEvent=l,t.isParent=function(e,t,i){return!(!e||!t||e===t)&&!(t.length>e.length)&&(t.charAt(t.length-1)!==n.nativeSep&&(t+=n.nativeSep),i?s.beginsWithIgnoreCase(e,t):0===e.indexOf(t))},t.indexOf=function(e,t,n){return t.length>e.length?-1:e===t?0:(n&&(e=e.toLowerCase(),t=t.toLowerCase()),e.indexOf(t))};var c=function(e){function t(t,n){var i=e.call(this,t)||this;return i.fileOperationResult=n,i}return f(t,e),t}(Error);t.FileOperationError=c;!function(e){e[e.FILE_IS_BINARY=0]="FILE_IS_BINARY",e[e.FILE_IS_DIRECTORY=1]="FILE_IS_DIRECTORY",e[e.FILE_NOT_FOUND=2]="FILE_NOT_FOUND",e[e.FILE_NOT_MODIFIED_SINCE=3]="FILE_NOT_MODIFIED_SINCE",e[e.FILE_MODIFIED_SINCE=4]="FILE_MODIFIED_SINCE",e[e.FILE_MOVE_CONFLICT=5]="FILE_MOVE_CONFLICT",e[e.FILE_READ_ONLY=6]="FILE_READ_ONLY",e[e.FILE_TOO_LARGE=7]="FILE_TOO_LARGE",e[e.FILE_INVALID_PATH=8]="FILE_INVALID_PATH"}(t.FileOperationResult||(t.FileOperationResult={}));t.MAX_FILE_SIZE="object"==typeof process?"ia32"===process.arch?314572800:17179869184:314572800,t.AutoSaveConfiguration={OFF:"off",AFTER_DELAY:"afterDelay",ON_FOCUS_CHANGE:"onFocusChange",ON_WINDOW_CHANGE:"onWindowChange"},t.HotExitConfiguration={OFF:"off",ON_EXIT:"onExit",ON_EXIT_AND_WINDOW_CLOSE:"onExitAndWindowClose"},t.CONTENT_CHANGE_EVENT_BUFFER_DELAY=1e3,t.SUPPORTED_ENCODINGS={utf8:{labelLong:"UTF-8",labelShort:"UTF-8",order:1,alias:"utf8bom"},utf8bom:{labelLong:"UTF-8 with BOM",labelShort:"UTF-8 with BOM",encodeOnly:!0,order:2,alias:"utf8"},utf16le:{labelLong:"UTF-16 LE",labelShort:"UTF-16 LE",order:3},utf16be:{labelLong:"UTF-16 BE",labelShort:"UTF-16 BE",order:4},windows1252:{labelLong:"Western (Windows 1252)",labelShort:"Windows 1252",order:5},iso88591:{labelLong:"Western (ISO 8859-1)",labelShort:"ISO 8859-1",order:6},iso88593:{labelLong:"Western (ISO 8859-3)",labelShort:"ISO 8859-3",order:7},iso885915:{labelLong:"Western (ISO 8859-15)",labelShort:"ISO 8859-15",order:8},macroman:{labelLong:"Western (Mac Roman)",labelShort:"Mac Roman",order:9},cp437:{labelLong:"DOS (CP 437)",labelShort:"CP437",order:10},windows1256:{labelLong:"Arabic (Windows 1256)",labelShort:"Windows 1256",order:11},iso88596:{labelLong:"Arabic (ISO 8859-6)",labelShort:"ISO 8859-6",order:12},windows1257:{labelLong:"Baltic (Windows 1257)",labelShort:"Windows 1257",order:13},iso88594:{labelLong:"Baltic (ISO 8859-4)",labelShort:"ISO 8859-4",order:14},iso885914:{labelLong:"Celtic (ISO 8859-14)",labelShort:"ISO 8859-14",order:15},windows1250:{labelLong:"Central European (Windows 1250)",labelShort:"Windows 1250",order:16},iso88592:{labelLong:"Central European (ISO 8859-2)",labelShort:"ISO 8859-2",order:17},cp852:{labelLong:"Central European (CP 852)",labelShort:"CP 852",order:18},windows1251:{labelLong:"Cyrillic (Windows 1251)",labelShort:"Windows 1251",order:19},cp866:{labelLong:"Cyrillic (CP 866)",labelShort:"CP 866",order:20},iso88595:{labelLong:"Cyrillic (ISO 8859-5)",labelShort:"ISO 8859-5",order:21},koi8r:{labelLong:"Cyrillic (KOI8-R)",labelShort:"KOI8-R",order:22},koi8u:{labelLong:"Cyrillic (KOI8-U)",labelShort:"KOI8-U",order:23},iso885913:{labelLong:"Estonian (ISO 8859-13)",labelShort:"ISO 8859-13",order:24},windows1253:{labelLong:"Greek (Windows 1253)",labelShort:"Windows 1253",order:25},iso88597:{labelLong:"Greek (ISO 8859-7)",labelShort:"ISO 8859-7",order:26},windows1255:{labelLong:"Hebrew (Windows 1255)",labelShort:"Windows 1255",order:27},iso88598:{labelLong:"Hebrew (ISO 8859-8)",labelShort:"ISO 8859-8",order:28},iso885910:{labelLong:"Nordic (ISO 8859-10)",labelShort:"ISO 8859-10",order:29},iso885916:{labelLong:"Romanian (ISO 8859-16)",labelShort:"ISO 8859-16",order:30},windows1254:{labelLong:"Turkish (Windows 1254)",labelShort:"Windows 1254",order:31},iso88599:{labelLong:"Turkish (ISO 8859-9)",labelShort:"ISO 8859-9",order:32},windows1258:{labelLong:"Vietnamese (Windows 1258)",labelShort:"Windows 1258",order:33},gbk:{labelLong:"Chinese (GBK)",labelShort:"GBK",order:34},gb18030:{labelLong:"Chinese (GB18030)",labelShort:"GB18030",order:35},cp950:{labelLong:"Traditional Chinese (Big5)",labelShort:"Big5",order:36},big5hkscs:{labelLong:"Traditional Chinese (Big5-HKSCS)",labelShort:"Big5-HKSCS",order:37},shiftjis:{labelLong:"Japanese (Shift JIS)",labelShort:"Shift JIS",order:38},eucjp:{labelLong:"Japanese (EUC-JP)",labelShort:"EUC-JP",order:39},euckr:{labelLong:"Korean (EUC-KR)",labelShort:"EUC-KR",order:40},windows874:{labelLong:"Thai (Windows 874)",labelShort:"Windows 874",order:41},iso885911:{labelLong:"Latin/Thai (ISO 8859-11)",labelShort:"ISO 8859-11",order:42},koi8ru:{labelLong:"Cyrillic (KOI8-RU)",labelShort:"KOI8-RU",order:43},koi8t:{labelLong:"Tajik (KOI8-T)",labelShort:"KOI8-T",order:44},gb2312:{labelLong:"Simplified Chinese (GB 2312)",labelShort:"GB 2312",order:45}};!function(e){e[e.FILE=0]="FILE",e[e.FOLDER=1]="FOLDER",e[e.ROOT_FOLDER=2]="ROOT_FOLDER"}(t.FileKind||(t.FileKind={}))}),define(d[84],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){function e(){for(var e=[],t=0;t0?i[0].index:n.length;if(n.length!==d){console.warn("[createInstance] First service dependency of "+e.ctor.name+" at position "+(d+1)+" conflicts with "+n.length+" static arguments");var h=d-n.length;n=h>0?n.concat(new Array(h)):n.slice(0,d)}var p=[e.ctor];p.push.apply(p,n),p.push.apply(p,r);var f=o.create.apply(null,p);return e._validate(f),f},t.prototype._getOrCreateServiceInstance=function(e){var t=this._services.get(e);return t instanceof a.SyncDescriptor?this._createAndCacheServiceInstance(e,t):t},t.prototype._createAndCacheServiceInstance=function(e,t){function n(){var e=new Error("[createInstance] cyclic dependency between services");throw e.message=i.toString(),e}r.ok(this._services.get(e)instanceof a.SyncDescriptor);for(var i=new s.Graph(function(e){return e.id.toString()}),o=0,l=[{id:e,desc:t}];l.length;){var c=l.pop();i.lookupOrInsertNode(c),o++>100&&n();for(var d=0,h=u._util.getServiceDependencies(c.desc.ctor);d=0;c--)this._isTargetedForRemoval(e[c],a,u,s,l)&&e.splice(c,1);else n.push(r)}return e.concat(n)},e.prototype._addKeyPress=function(t,n){var i=this._map.get(t);if(void 0===i)return this._map.set(t,[n]),void this._addToLookupMap(n);for(var o=i.length-1;o>=0;o--){var r=i[o];if(r.command!==n.command){var s=null!==r.keypressChordPart,a=null!==n.keypressChordPart;s&&a&&r.keypressChordPart!==n.keypressChordPart||e.whenIsEntirelyIncluded(!0,r.when,n.when)&&this._removeFromLookupMap(r)}}i.push(n),this._addToLookupMap(n)},e.prototype._addToLookupMap=function(e){if(e.command){var t=this._lookupMap.get(e.command);void 0===t?(t=[e],this._lookupMap.set(e.command,t)):t.push(e)}},e.prototype._removeFromLookupMap=function(e){var t=this._lookupMap.get(e.command);if(void 0!==t)for(var n=0,i=t.length;n=0;o--)n[i++]=t[o];return n},e.prototype.lookupPrimaryKeybinding=function(e){var t=this._lookupMap.get(e);return void 0===t||0===t.length?null:t[t.length-1]},e.prototype.resolve=function(e,t,n){var i=null;if(null!==t){if(void 0===(a=this._map.get(t)))return null;i=[];for(var o=0,r=a.length;o=0;i--){var o=n[i];if(e.contextMatchesRules(t,o.when))return o}return null},e.contextMatchesRules=function(e,t){return!t||t.evaluate(e)},e.getAllUnboundCommands=function(e){var t=i.CommandsRegistry.getCommands(),o=[];for(var r in t)"_"!==r[0]&&0!==r.indexOf("vscode.")&&("object"!=typeof t[r].description||n.isFalsyOrEmpty(t[r].description.args))&&!0!==e.get(r)&&o.push(r);return o},e}();t.KeybindingResolver=o}),define(d[417],h([1,0,3,31,159,19,62,11]),function(e,t,n,i,o,r,s,a){"use strict";function u(e){for(;e;){if(e.hasAttribute(l))return parseInt(e.getAttribute(l),10);e=e.parentElement}return 0}Object.defineProperty(t,"__esModule",{value:!0});var l="data-keybinding-context",c=function(){function e(e,t){this._id=e,this._parent=t,this._value=Object.create(null),this._value._contextId=e}return e.prototype.setValue=function(e,t){return this._value[e]!==t&&(this._value[e]=t,!0)},e.prototype.removeValue=function(e){return delete this._value[e]},e.prototype.getValue=function(e){var t=this._value[e];return void 0===t&&this._parent?this._parent.getValue(e):t},e}();t.Context=c;var d=function(e){function t(t,n,i){var o=e.call(this,t,null)||this;return o._emitter=i,o._subscription=n.onDidUpdateConfiguration(function(e){return o._updateConfigurationContext(n.getConfiguration())}),o._updateConfigurationContext(n.getConfiguration()),o}return f(t,e),t.prototype.dispose=function(){this._subscription.dispose()},t.prototype._updateConfigurationContext=function(e){var t=this;for(var n in this._value)0===n.indexOf("config.")&&delete this._value[n];var i=function(e,n){for(var o in e)if(Object.prototype.hasOwnProperty.call(e,o)){n.push(o);var r=e[o];if("boolean"==typeof r){var s=n.join(".");t._value[s]=r,t._emitter.fire(s)}else"object"==typeof r&&i(r,n);n.pop()}};i(e,["config"])},t}(c),h=function(){function e(e,t,n){this._parent=e,this._key=t,this._defaultValue=n,this.reset()}return e.prototype.set=function(e){this._parent.setContext(this._key,e)},e.prototype.reset=function(){void 0===this._defaultValue?this._parent.removeContext(this._key):this._parent.setContext(this._key,this._defaultValue)},e.prototype.get=function(){return this._parent.getContextKeyValue(this._key)},e}(),p=function(){function e(e){this._myContextId=e,this._onDidChangeContextKey=new a.Emitter}return e.prototype.createKey=function(e,t){return new h(this,e,t)},Object.defineProperty(e.prototype,"onDidChangeContext",{get:function(){return this._onDidChangeContext||(this._onDidChangeContext=a.debounceEvent(this._onDidChangeContextKey.event,function(e,t){return e?e.indexOf(t)<0&&e.push(t):e=[t],e},25)),this._onDidChangeContext},enumerable:!0,configurable:!0}),e.prototype.createScoped=function(e){return new m(this,this._onDidChangeContextKey,e)},e.prototype.contextMatchesRules=function(e){var t=this.getContextValuesContainer(this._myContextId);return o.KeybindingResolver.contextMatchesRules(t,e)},e.prototype.getContextKeyValue=function(e){return this.getContextValuesContainer(this._myContextId).getValue(e)},e.prototype.setContext=function(e,t){this.getContextValuesContainer(this._myContextId).setValue(e,t)&&this._onDidChangeContextKey.fire(e)},e.prototype.removeContext=function(e){this.getContextValuesContainer(this._myContextId).removeValue(e)&&this._onDidChangeContextKey.fire(e)},e.prototype.getContext=function(e){return this.getContextValuesContainer(u(e))},e}();t.AbstractContextKeyService=p;var g=function(e){function t(t){var n=e.call(this,0)||this;n._toDispose=[],n._lastContextId=0,n._contexts=Object.create(null);var i=new d(n._myContextId,t,n._onDidChangeContextKey);return n._contexts[String(n._myContextId)]=i,n._toDispose.push(i),n}return f(t,e),t.prototype.dispose=function(){this._toDispose=n.dispose(this._toDispose)},t.prototype.getContextValuesContainer=function(e){return this._contexts[String(e)]},t.prototype.createChildContext=function(e){void 0===e&&(e=this._myContextId);var t=++this._lastContextId;return this._contexts[String(t)]=new c(t,this.getContextValuesContainer(e)),t},t.prototype.disposeContext=function(e){delete this._contexts[String(e)]},t=v([y(0,s.IConfigurationService)],t)}(p);t.ContextKeyService=g;var m=function(e){function t(t,n,i){var o=e.call(this,t.createChildContext())||this;return o._parent=t,o._onDidChangeContextKey=n,i&&(o._domNode=i,o._domNode.setAttribute(l,String(o._myContextId))),o}return f(t,e),t.prototype.dispose=function(){this._parent.disposeContext(this._myContextId),this._domNode&&this._domNode.removeAttribute(l)},Object.defineProperty(t.prototype,"onDidChangeContext",{get:function(){return this._parent.onDidChangeContext},enumerable:!0,configurable:!0}),t.prototype.getContextValuesContainer=function(e){return this._parent.getContextValuesContainer(e)},t.prototype.createChildContext=function(e){return void 0===e&&(e=this._myContextId),this._parent.createChildContext(e)},t.prototype.disposeContext=function(e){this._parent.disposeContext(e)},t}(p);i.CommandsRegistry.registerCommand(r.SET_CONTEXT_COMMAND_ID,function(e,t,n){e.get(r.IContextKeyService).createKey(String(t),n)})}),define(d[418],h([1,0]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(){return function(e,t,n,i,o){if(this.resolvedKeybinding=e,e){var r=e.getDispatchParts(),s=r[0],a=r[1];this.keypressFirstPart=s,this.keypressChordPart=a}else this.keypressFirstPart=null,this.keypressChordPart=null;this.bubble=!!t&&94===t.charCodeAt(0),this.command=this.bubble?t.substr(1):t,this.commandArgs=n,this.when=i,this.isDefault=o}}();t.ResolvedKeybindingItem=n}),define(d[419],h([1,0,40,155]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t,n){var i=e.call(this)||this;return i._os=n,null===t?(i._firstPart=null,i._chordPart=null):2===t.type?(i._firstPart=t.firstPart,i._chordPart=t.chordPart):(i._firstPart=t,i._chordPart=null),i}return f(t,e),t.prototype._keyCodeToUILabel=function(e){if(2===this._os)switch(e){case 15:return"←";case 16:return"↑";case 17:return"→";case 18:return"↓"}return n.KeyCodeUtils.toString(e)},t.prototype._getUILabelForKeybinding=function(e){return e?e.isDuplicateModifierCase()?"":this._keyCodeToUILabel(e.keyCode):null},t.prototype.getLabel=function(){var e=this._getUILabelForKeybinding(this._firstPart),t=this._getUILabelForKeybinding(this._chordPart);return i.UILabelProvider.toLabel(this._firstPart,e,this._chordPart,t,this._os)},t.prototype._getAriaLabelForKeybinding=function(e){return e?e.isDuplicateModifierCase()?"":n.KeyCodeUtils.toString(e.keyCode):null},t.prototype.getAriaLabel=function(){var e=this._getAriaLabelForKeybinding(this._firstPart),t=this._getAriaLabelForKeybinding(this._chordPart);return i.AriaLabelProvider.toLabel(this._firstPart,e,this._chordPart,t,this._os)},t.prototype._keyCodeToElectronAccelerator=function(e){if(e>=93&&e<=108)return null;switch(e){case 16:return"Up";case 18:return"Down";case 15:return"Left";case 17:return"Right"}return n.KeyCodeUtils.toString(e)},t.prototype._getElectronAcceleratorLabelForKeybinding=function(e){return e?e.isDuplicateModifierCase()?null:this._keyCodeToElectronAccelerator(e.keyCode):null},t.prototype.getElectronAccelerator=function(){if(null!==this._chordPart)return null;var e=this._getElectronAcceleratorLabelForKeybinding(this._firstPart);return i.ElectronAcceleratorLabelProvider.toLabel(this._firstPart,e,null,null,this._os)},t.prototype._getUserSettingsLabelForKeybinding=function(e){return e?e.isDuplicateModifierCase()?"":n.KeyCodeUtils.toUserSettingsUS(e.keyCode):null},t.prototype.getUserSettingsLabel=function(){var e=this._getUserSettingsLabelForKeybinding(this._firstPart),t=this._getUserSettingsLabelForKeybinding(this._chordPart),n=i.UserSettingsLabelProvider.toLabel(this._firstPart,e,this._chordPart,t,this._os);return n?n.toLowerCase():n},t.prototype.isWYSIWYG=function(){return!0},t.prototype.isChord=function(){return!!this._chordPart},t.prototype.getParts=function(){return[this._toResolvedKeybindingPart(this._firstPart),this._toResolvedKeybindingPart(this._chordPart)]},t.prototype._toResolvedKeybindingPart=function(e){return e?new n.ResolvedKeybindingPart(e.ctrlKey,e.shiftKey,e.altKey,e.metaKey,this._getUILabelForKeybinding(e),this._getAriaLabelForKeybinding(e)):null},t.prototype.getDispatchParts=function(){return[this._firstPart?t.getDispatchStr(this._firstPart):null,this._chordPart?t.getDispatchStr(this._chordPart):null]},t.getDispatchStr=function(e){if(e.isModifierKey())return null;var t="";return e.ctrlKey&&(t+="ctrl+"),e.shiftKey&&(t+="shift+"),e.altKey&&(t+="alt+"),e.metaKey&&(t+="meta+"),t+=n.KeyCodeUtils.toString(e.keyCode)},t}(n.ResolvedKeybinding);t.USLayoutResolvedKeybinding=o}),define(d[420],h([1,0,7,11,16]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ILifecycleService=o.createDecorator("lifecycleService");!function(e){e[e.CLOSE=1]="CLOSE",e[e.QUIT=2]="QUIT",e[e.RELOAD=3]="RELOAD",e[e.LOAD=4]="LOAD"}(t.ShutdownReason||(t.ShutdownReason={}));var r;!function(e){e[e.NewWindow=1]="NewWindow",e[e.ReloadedWindow=3]="ReloadedWindow",e[e.ReopenedWindow=4]="ReopenedWindow"}(r=t.StartupKind||(t.StartupKind={}));var s;!function(e){e[e.Starting=1]="Starting",e[e.Running=2]="Running",e[e.ShuttingDown=3]="ShuttingDown"}(s=t.LifecyclePhase||(t.LifecyclePhase={})),t.NullLifecycleService={_serviceBrand:null,phase:s.Running,startupKind:r.NewWindow,onDidChangePhase:i.default.None,onWillShutdown:i.default.None,onShutdown:i.default.None},t.handleVetos=function(e,t){if(0===e.length)return n.TPromise.as(!1);for(var i=[],o=!1,r=0,s=e;r0?a:1,u=u>0?u:1,l=l>=a?l:a,c=c>0?c:u,{resource:t,owner:e,code:i,severity:o,message:r,source:s,startLineNumber:a,startColumn:u,endLineNumber:l,endColumn:c}},e.prototype.changeAll=function(t,i){var o=[],r=this._byOwner[t];if(r){delete this._byOwner[t];for(var s in r){var u=a.get(this._byResource,s,t)[0];u&&o.push(u.resource),a.remove(this._byResource,s,t)}}if(!n.isFalsyOrEmpty(i)){for(var l=Object.create(null),c=0,d=i;c0&&this._onMarkerChanged.fire(o)},e.prototype.read=function(e){void 0===e&&(e=Object.create(null));var t=e.owner,n=e.resource,i=e.take;if((!i||i<0)&&(i=-1),t&&n)return(d=a.get(this._byResource,n.toString(),t))?d.slice(0,i>0?i:void 0):[];if(t||n){var o=t?this._byOwner[t]:this._byResource[n.toString()];if(!o)return[];d=[];for(var r in o)for(var s=0,u=o[r];s0&&c===i)return d}return d}var d=[];for(var h in this._byResource)for(var p in this._byResource[h])for(var f=0,g=this._byResource[h][p];f0&&c===i)return d}return d},e._debouncer=function(t,n){t||(e._dedupeMap=Object.create(null),t=[]);for(var i=0,o=n;i0)})(s)&&(Array.isArray(s)?n=n.concat(s.map(e)):n.push(e(s)))}}return n},e.prototype.onResult=function(e,t){this._result=this._result.concat(e)},e.prototype.getResult=function(){return this._result},e.prototype.getResultWithLoadingMessage=function(){return this.getResult()},e}(),d=function(e){function t(i,o,r){var a=e.call(this,t.ID,i)||this;return a.openerService=o,a.modeService=r,a.openerService=o||s.NullOpenerService,a._lastLineNumber=-1,a._computer=new c(a._editor),a._hoverOperation=new n.HoverOperation(a._computer,function(e){return a._withResult(e)},null,function(e){return a._withResult(e)}),a}return f(t,e),t.prototype.dispose=function(){this._hoverOperation.cancel(),e.prototype.dispose.call(this)},t.prototype.onModelDecorationsChanged=function(){this.isVisible&&(this._hoverOperation.cancel(),this._computer.clearResult(),this._hoverOperation.start())},t.prototype.startShowingAt=function(e){this._lastLineNumber!==e&&(this._hoverOperation.cancel(),this.hide(),this._lastLineNumber=e,this._computer.setLineNumber(e),this._hoverOperation.start())},t.prototype.hide=function(){this._lastLineNumber=-1,this._hoverOperation.cancel(),e.prototype.hide.call(this)},t.prototype._withResult=function(e){this._messages=e,this._messages.length>0?this._renderMessages(this._lastLineNumber,this._messages):this.hide()},t.prototype._renderMessages=function(e,t){var n=this,i=document.createDocumentFragment();t.forEach(function(e){var t=r.renderMarkedString(e.value,{actionCallback:function(e){return n.openerService.open(a.default.parse(e)).then(void 0,u.onUnexpectedError)},codeBlockRenderer:function(e,t){var i=n.modeService.getModeIdForLanguageName(e);return n.modeService.getOrCreateMode(i).then(function(e){return l.tokenizeToString(t,i)})}});i.appendChild(o.$("div.hover-row",null,t))}),this.updateContents(i),this.showAt(e)},t.ID="editor.contrib.modesGlyphHoverWidget",t}(i.GlyphHoverWidget);t.ModesGlyphHoverWidget=d}),define(d[163],h([1,0,16]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IProgressService=n.createDecorator("progressService"),t.emptyProgress=Object.freeze({report:function(){}});var i=function(){function e(e){this._callback=e}return Object.defineProperty(e.prototype,"value",{get:function(){return this._value},enumerable:!0,configurable:!0}),e.prototype.report=function(e){this._value=e,this._callback()},e}();t.Progress=i;!function(e){e[e.Scm=1]="Scm",e[e.Window=10]="Window"}(t.ProgressLocation||(t.ProgressLocation={})),t.IProgressService2=n.createDecorator("progressService2")}),define(d[44],h([1,0,29,72]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(){function e(){this.data={}}return e.prototype.add=function(e,t){i.ok(n.isString(e)),i.ok(n.isObject(t)),i.ok(!this.data.hasOwnProperty(e),"There is already an extension with this id"),this.data[e]=t},e.prototype.knows=function(e){return this.data.hasOwnProperty(e)},e.prototype.as=function(e){return this.data[e]||null},e}();t.Registry=new o;var r=function(){function e(){this.toBeInstantiated=[],this.instances=[]}return e.prototype.setInstantiationService=function(e){for(this.instantiationService=e;this.toBeInstantiated.length>0;){var t=this.toBeInstantiated.shift();this.instantiate(t)}},e.prototype.instantiate=function(e){var t=this.instantiationService.createInstance(e);this.instances.push(t)},e.prototype._register=function(e){this.instantiationService?this.instantiate(e):this.toBeInstantiated.push(e)},e.prototype._getInstances=function(){return this.instances.slice(0)},e.prototype._setInstances=function(e){this.instances=e},e}();t.BaseRegistry=r}),define(d[30],h([1,0,44]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.editorContribution=function(e){o.INSTANCE.registerEditorBrowserContribution(e)};!function(e){e.getEditorContributions=function(){return o.INSTANCE.getEditorBrowserContributions()}}(t.EditorBrowserRegistry||(t.EditorBrowserRegistry={}));var i={EditorContributions:"editor.contributions"},o=function(){function e(){this.editorContributions=[]}return e.prototype.registerEditorBrowserContribution=function(e){this.editorContributions.push(e)},e.prototype.getEditorBrowserContributions=function(){return this.editorContributions.slice(0)},e.INSTANCE=new e,e}();n.Registry.add(i.EditorContributions,o.INSTANCE)}),define(d[126],h([1,0,336,11,44,43,17]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.Extensions={ModesRegistry:"editor.modesRegistry"};var a=function(){function e(){this._onDidAddLanguages=new i.Emitter,this.onDidAddLanguages=this._onDidAddLanguages.event,this._languages=[]}return e.prototype.registerLanguage=function(e){this._languages.push(e),this._onDidAddLanguages.fire([e])},e.prototype.registerLanguages=function(e){this._languages=this._languages.concat(e),this._onDidAddLanguages.fire(e)},e.prototype.getLanguages=function(){return this._languages.slice(0)},e}();t.EditorModesRegistry=a,t.ModesRegistry=new a,o.Registry.add(t.Extensions.ModesRegistry,t.ModesRegistry),t.PLAINTEXT_MODE_ID="plaintext",t.PLAINTEXT_LANGUAGE_IDENTIFIER=new s.LanguageIdentifier(t.PLAINTEXT_MODE_ID,1),t.ModesRegistry.registerLanguage({id:t.PLAINTEXT_MODE_ID,extensions:[".txt",".gitignore"],aliases:[n.localize(0,null),"text"],mimetypes:["text/plain"]}),r.LanguageConfigurationRegistry.register(t.PLAINTEXT_LANGUAGE_IDENTIFIER,{brackets:[["(",")"],["[","]"],["{","}"]]})}),define(d[430],h([1,0,339,70,11,36,7,103,2,20,175,15,62,49,126,101,55,34]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,f,g,m,_){"use strict";function C(e){return e.toString()}Object.defineProperty(t,"__esModule",{value:!0});var b=function(){function e(e,t){var n=this;this.model=e,this._markerDecorations=[],this._modelEventsListener=e.addBulkListener(function(e){return t(n,e)})}return e.prototype.dispose=function(){this._markerDecorations=this.model.deltaDecorations(this._markerDecorations,[]),this._modelEventsListener.dispose(),this._modelEventsListener=null,this.model=null},e.prototype.getModelId=function(){return C(this.model.uri)},e.prototype.acceptMarkerDecorations=function(e){this._markerDecorations=this.model.deltaDecorations(this._markerDecorations,e)},e}(),w=function(){function e(){}return e.setMarkers=function(e,t){var n=this,i=t.read({resource:e.model.uri,take:500}).map(function(t){return{range:n._createDecorationRange(e.model,t),options:n._createDecorationOption(t)}});e.acceptMarkerDecorations(i)},e._createDecorationRange=function(e,t){var n=e.validateRange(new u.Range(t.startLineNumber,t.startColumn,t.endLineNumber,t.endColumn)),i=new u.Range(n.startLineNumber,n.startColumn,n.endLineNumber,n.endColumn);if(i.isEmpty()){var o=e.getWordAtPosition(i.getStartPosition());if(o)i=new u.Range(i.startLineNumber,o.startColumn,i.endLineNumber,o.endColumn);else{var r=e.getLineLastNonWhitespaceColumn(n.startLineNumber)||e.getLineMaxColumn(n.startLineNumber);1===r||(i=i.endColumn>=r?new u.Range(i.startLineNumber,r-1,i.endLineNumber,r):new u.Range(i.startLineNumber,i.startColumn,i.endLineNumber,i.endColumn+1))}}else if(t.endColumn===Number.MAX_VALUE&&1===t.startColumn&&i.startLineNumber===i.endLineNumber){var s=e.getLineFirstNonWhitespaceColumn(t.startLineNumber);s0&&"#"===e.charAt(e.length-1)?e.substring(0,e.length-1):e}Object.defineProperty(t,"__esModule",{value:!0}),t.Extensions={JSONContribution:"base.contributions.json"};var r=new(function(){function e(){this.schemasById={},this.eventEmitter=new i.EventEmitter}return e.prototype.addRegistryChangedListener=function(e){return this.eventEmitter.addListener("registryChanged",e)},e.prototype.registerSchema=function(e,t){this.schemasById[o(e)]=t,this.eventEmitter.emit("registryChanged",{})},e.prototype.getSchemaContributions=function(){return{schemas:this.schemasById}},e}());n.Registry.add(t.Extensions.JSONContribution,r)}),define(d[127],h([1,0,382,11,44,29,9,433]),function(e,t,n,i,o,r,s,a){"use strict";function u(e){switch(Array.isArray(e)?e[0]:e){case"boolean":return!1;case"integer":case"number":return 0;case"string":return"";case"array":return[];case"object":return{};default:return null}}function l(e){return t.OVERRIDE_PROPERTY_PATTERN.test(e)?n.localize(3,null,e):void 0!==m.getConfigurationProperties()[e]?n.localize(4,null,e):null}Object.defineProperty(t,"__esModule",{value:!0}),t.Extensions={Configuration:"base.contributions.configuration"};var c;!function(e){e[e.WINDOW=1]="WINDOW",e[e.RESOURCE=2]="RESOURCE"}(c=t.ConfigurationScope||(t.ConfigurationScope={})),t.schemaId="vscode://schemas/settings",t.editorConfigurationSchemaId="vscode://schemas/settings/editor",t.resourceConfigurationSchemaId="vscode://schemas/settings/resource";var d=o.Registry.as(a.Extensions.JSONContribution),h=function(){function e(){this.overrideIdentifiers=[],this.configurationContributors=[],this.configurationSchema={properties:{},patternProperties:{},additionalProperties:!1,errorMessage:"Unknown configuration setting"},this.editorConfigurationSchema={properties:{},patternProperties:{},additionalProperties:!1,errorMessage:"Unknown editor configuration setting"},this.resourceConfigurationSchema={properties:{},patternProperties:{},additionalProperties:!1,errorMessage:"Not a resource configuration setting"},this._onDidRegisterConfiguration=new i.Emitter,this.configurationProperties={},this.computeOverridePropertyPattern(),d.registerSchema(t.schemaId,this.configurationSchema),d.registerSchema(t.editorConfigurationSchemaId,this.editorConfigurationSchema),d.registerSchema(t.resourceConfigurationSchemaId,this.resourceConfigurationSchema)}return Object.defineProperty(e.prototype,"onDidRegisterConfiguration",{get:function(){return this._onDidRegisterConfiguration.event},enumerable:!0,configurable:!0}),e.prototype.registerConfiguration=function(e,t){void 0===t&&(t=!0),this.registerConfigurations([e],t)},e.prototype.registerConfigurations=function(e,t){var n=this;void 0===t&&(t=!0),e.forEach(function(e){n.validateAndRegisterProperties(e,t),n.configurationContributors.push(e),n.registerJSONConfiguration(e),n.updateSchemaForOverrideSettingsConfiguration(e)}),this._onDidRegisterConfiguration.fire(this)},e.prototype.registerOverrideIdentifiers=function(e){(t=this.overrideIdentifiers).push.apply(t,e),this.updateOverridePropertyPatternKey();var t},e.prototype.registerDefaultConfigurations=function(e){for(var i={id:"defaultOverrides",title:n.localize(0,null),properties:{}},o=0,r=e;o0){var m=t.firstLine;"^"!==m.charAt(0)&&(m="^"+m);try{var v=new RegExp(m);o.regExpLeadsToEndlessLoop(v)||i.registerTextMime({id:r,mime:s,firstline:v})}catch(e){n.onUnexpectedError(e)}}e.aliases.push(r);var _=null;if(void 0!==t.aliases&&Array.isArray(t.aliases)&&(_=0===t.aliases.length?[null]:t.aliases),null!==_)for(var y=0;y<_.length;y++)_[y]&&0!==_[y].length&&e.aliases.push(_[y]);var C=null!==_&&_.length>0;if(C&&null===_[0]);else{var b=(C?_[0]:null)||r;!C&&e.name||(e.name=b)}"string"==typeof t.configuration&&e.configurationFiles.push(t.configuration)},e.prototype.isRegisteredMode=function(e){return!!c.call(this._mimeTypesMap,e)||c.call(this._languages,e)},e.prototype.getRegisteredModes=function(){return Object.keys(this._languages)},e.prototype.getRegisteredLanguageNames=function(){return Object.keys(this._nameMap)},e.prototype.getLanguageName=function(e){return c.call(this._languages,e)?this._languages[e].name:null},e.prototype.getModeIdForLanguageNameLowercase=function(e){return c.call(this._lowercaseNameMap,e)?this._lowercaseNameMap[e].language:null},e.prototype.getConfigurationFiles=function(e){return c.call(this._languages,e)?this._languages[e].configurationFiles||[]:[]},e.prototype.getMimeForMode=function(e){return c.call(this._languages,e)?this._languages[e].mimetypes[0]||null:null},e.prototype.extractModeIds=function(e){var t=this;return e?e.split(",").map(function(e){return e.trim()}).map(function(e){return c.call(t._mimeTypesMap,e)?t._mimeTypesMap[e].language:e}).filter(function(e){return c.call(t._languages,e)}):[]},e.prototype.getLanguageIdentifier=function(e){if(e===u.NULL_MODE_ID||0===e)return u.NULL_LANGUAGE_IDENTIFIER;var t;if("string"==typeof e)t=e;else if(!(t=this._languageIds[e]))return null;return c.call(this._languages,t)?this._languages[t].identifier:null},e.prototype.getModeIdsFromLanguageName=function(e){return e&&c.call(this._nameMap,e)?[this._nameMap[e].language]:[]},e.prototype.getModeIdsFromFilenameOrFirstLine=function(e,t){if(!e&&!t)return[];var n=i.guessMimeTypes(e,t);return this.extractModeIds(n.join(","))},e.prototype.getExtensions=function(e){if(!c.call(this._nameMap,e))return[];var t=this._nameMap[e];return this._languages[t.language].extensions},e.prototype.getFilenames=function(e){if(!c.call(this._nameMap,e))return[];var t=this._nameMap[e];return this._languages[t.language].filenames},e}();t.LanguagesRegistry=d}),define(d[437],h([1,0,10,11,7,511,436]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(){this._onDidCreateMode=new i.Emitter,this.onDidCreateMode=this._onDidCreateMode.event,this._instantiatedModes={},this._registry=new s.LanguagesRegistry}return e.prototype._onReady=function(){return o.TPromise.as(!0)},e.prototype.isRegisteredMode=function(e){return this._registry.isRegisteredMode(e)},e.prototype.getRegisteredModes=function(){return this._registry.getRegisteredModes()},e.prototype.getRegisteredLanguageNames=function(){return this._registry.getRegisteredLanguageNames()},e.prototype.getExtensions=function(e){return this._registry.getExtensions(e)},e.prototype.getFilenames=function(e){return this._registry.getFilenames(e)},e.prototype.getMimeForMode=function(e){return this._registry.getMimeForMode(e)},e.prototype.getLanguageName=function(e){return this._registry.getLanguageName(e)},e.prototype.getModeIdForLanguageName=function(e){return this._registry.getModeIdForLanguageNameLowercase(e)},e.prototype.getModeIdByFilenameOrFirstLine=function(e,t){var n=this._registry.getModeIdsFromFilenameOrFirstLine(e,t);return n.length>0?n[0]:null},e.prototype.getModeId=function(e){var t=this._registry.extractModeIds(e);return t.length>0?t[0]:null},e.prototype.getLanguageIdentifier=function(e){return this._registry.getLanguageIdentifier(e)},e.prototype.getConfigurationFiles=function(e){return this._registry.getConfigurationFiles(e)},e.prototype.lookup=function(e){for(var t=[],n=this._registry.extractModeIds(e),i=0;i0?t[0]:null},e.prototype.getOrCreateModeByFilenameOrFirstLine=function(e,t){var n=this;return this._onReady().then(function(){var i=n.getModeIdByFilenameOrFirstLine(e,t);return n._getOrCreateMode(i||"plaintext")})},e.prototype._getOrCreateMode=function(e){if(!this._instantiatedModes.hasOwnProperty(e)){var t=this.getLanguageIdentifier(e);this._instantiatedModes[e]=new r.FrankensteinMode(t),this._onDidCreateMode.fire(this._instantiatedModes[e])}return this._instantiatedModes[e]},e}();t.ModeServiceImpl=a}),define(d[438],h([1,0,44,442,127,62]),function(e,t,n,i,o,r){"use strict";function s(){var e=Object.create(null),t=n.Registry.as(o.Extensions.Configuration).getConfigurationProperties();for(var i in t)u(e,i,t[i].default,function(e){return console.error("Conflict in default settings: "+e)});return e}function a(e,t){var n=Object.create(null);for(var i in e)u(n,i,e[i],t);return n}function u(e,t,n,i){for(var o=t.split("."),r=o.pop(),s=e,a=0;at.command?1:e.weight2-t.weight2}Object.defineProperty(t,"__esModule",{value:!0});var a=function(){function e(){this.WEIGHT={editorCore:function(e){return void 0===e&&(e=0),0+e},editorContrib:function(e){return void 0===e&&(e=0),100+e},workbenchContrib:function(e){return void 0===e&&(e=0),200+e},builtinExtension:function(e){return void 0===e&&(e=0),300+e},externalExtension:function(e){return void 0===e&&(e=0),400+e}},this._keybindings=[]}return e.bindToCurrentPlatform=function(e){if(1===i.OS){if(e&&e.win)return e.win}else if(2===i.OS){if(e&&e.mac)return e.mac}else if(e&&e.linux)return e.linux;return e},e.bindToCurrentPlatform2=function(e){if(1===i.OS){if(e&&e.win)return e.win}else if(2===i.OS){if(e&&e.mac)return e.mac}else if(e&&e.linux)return e.linux;return e},e.prototype.registerKeybindingRule=function(t){var o=this,r=e.bindToCurrentPlatform(t);r&&r.primary&&this.registerDefaultKeybinding(n.createKeybinding(r.primary,i.OS),t.id,t.weight,0,t.when),r&&Array.isArray(r.secondary)&&r.secondary.forEach(function(e,r){return o.registerDefaultKeybinding(n.createKeybinding(e,i.OS),t.id,t.weight,-r-1,t.when)})},e.prototype.registerKeybindingRule2=function(t){var n=e.bindToCurrentPlatform2(t);n&&n.primary&&this.registerDefaultKeybinding(n.primary,t.id,t.weight,0,t.when)},e.prototype.registerCommandAndKeybindingRule=function(e){this.registerKeybindingRule(e),o.CommandsRegistry.registerCommand(e.id,e)},e._mightProduceChar=function(e){return e>=21&&e<=30||(e>=31&&e<=56||(80===e||81===e||82===e||83===e||84===e||85===e||86===e||110===e||111===e||87===e||88===e||89===e||90===e||91===e||92===e))},e.prototype._assertNoCtrlAlt=function(t,n){t.ctrlKey&&t.altKey&&!t.metaKey&&e._mightProduceChar(t.keyCode)&&console.warn("Ctrl+Alt+ keybindings should not be used by default under Windows. Offender: ",t," for ",n)},e.prototype.registerDefaultKeybinding=function(e,t,n,o,r){1===i.OS&&(2===e.type?this._assertNoCtrlAlt(e.firstPart,t):this._assertNoCtrlAlt(e,t)),this._keybindings.push({keybinding:e,command:t,commandArgs:null,when:r,weight1:n,weight2:o})},e.prototype.getDefaultKeybindings=function(){var e=this._keybindings.slice(0);return e.sort(s),e},e}();t.KeybindingsRegistry=new a,t.Extensions={EditorModes:"platform.keybindingsRegistry"},r.Registry.add(t.Extensions.EditorModes,t.KeybindingsRegistry)}),define(d[129],h([1,0,70,36,24,7,62,31,414,419,159,46,20,11,438,3,4,65,105,407,40,418,15]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_,y,C,b,w,S){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var E=function(){function e(e){this._widget=e}return e.prototype.getId=function(){return"editor"},e.prototype.getControl=function(){return this._widget},e.prototype.getSelection=function(){return this._widget.getSelection()},e.prototype.focus=function(){this._widget.focus()},e.prototype.isVisible=function(){return!0},e.prototype.withTypedEditor=function(e,t){return h.isCommonCodeEditor(this._widget)?e(this._widget):t(this._widget)},e}();t.SimpleEditor=E;var L=function(){function e(e){this.model=e,this._onDispose=new p.Emitter}return Object.defineProperty(e.prototype,"onDispose",{get:function(){return this._onDispose.event},enumerable:!0,configurable:!0}),e.prototype.load=function(){return r.TPromise.as(this)},Object.defineProperty(e.prototype,"textEditorModel",{get:function(){return this.model},enumerable:!0,configurable:!0}),e.prototype.dispose=function(){this._onDispose.fire()},e}();t.SimpleModel=L;var x=function(){function e(){this.openEditorDelegate=null}return e.prototype.setEditor=function(e){this.editor=new E(e)},e.prototype.setOpenEditorDelegate=function(e){this.openEditorDelegate=e},e.prototype.openEditor=function(e,t){var n=this;return r.TPromise.as(this.editor.withTypedEditor(function(t){return n.doOpenEditor(t,e)},function(t){return n.doOpenEditor(t.getOriginalEditor(),e)||n.doOpenEditor(t.getModifiedEditor(),e)}))},e.prototype.doOpenEditor=function(e,t){if(!this.findModel(e,t)){if(t.resource){if(this.openEditorDelegate)return this.openEditorDelegate(t.resource.toString()),null;var i=t.resource.scheme;if(i===n.Schemas.http||i===n.Schemas.https)return v.windowOpenNoOpener(t.resource.toString()),this.editor}return null}var o=t.options.selection;if(o)if("number"==typeof o.endLineNumber&&"number"==typeof o.endColumn)e.setSelection(o),e.revealRangeInCenter(o);else{var r={lineNumber:o.startLineNumber,column:o.startColumn};e.setPosition(r),e.revealPositionInCenter(r)}return this.editor},e.prototype.findModel=function(e,t){var n=e.getModel();return n.uri.toString()!==t.resource.toString()?null:n},e}();t.SimpleEditorService=x;var N=function(){function e(){}return e.prototype.setEditor=function(e){this.editor=new E(e)},e.prototype.createModelReference=function(e){var t,n=this;return(t=this.editor.withTypedEditor(function(t){return n.findModel(t,e)},function(t){return n.findModel(t.getOriginalEditor(),e)||n.findModel(t.getModifiedEditor(),e)}))?r.TPromise.as(new m.ImmortalReference(new L(t))):r.TPromise.as(new m.ImmortalReference(null))},e.prototype.registerTextModelContentProvider=function(e,t){return{dispose:function(){}}},e.prototype.findModel=function(e,t){var n=e.getModel();return n.uri.toString()!==t.toString()?null:n},e}();t.SimpleEditorModelResolverService=N;var M=function(){function e(){}return e.prototype.show=function(){return e.NULL_PROGRESS_RUNNER},e.prototype.showWhile=function(e,t){return null},e.NULL_PROGRESS_RUNNER={done:function(){},total:function(){},worked:function(){}},e}();t.SimpleProgressService=M;var T=function(){function e(){}return e.prototype.show=function(t,n){switch(t){case i.default.Error:console.error(n);break;case i.default.Warning:console.warn(n);break;default:console.log(n)}return e.Empty},e.prototype.hideAll=function(){},e.prototype.confirm=function(e){var t=e.message;return e.detail&&(t=t+"\n\n"+e.detail),window.confirm(t)},e.Empty=function(){},e}();t.SimpleMessageService=T;var k=function(){function e(e){this._onWillExecuteCommand=new p.Emitter,this.onWillExecuteCommand=this._onWillExecuteCommand.event,this._instantiationService=e,this._dynamicCommands=Object.create(null)}return e.prototype.addCommand=function(e,t){var n=this;return this._dynamicCommands[e]=t,{dispose:function(){delete n._dynamicCommands[e]}}},e.prototype.executeCommand=function(e){for(var t=[],n=1;n.001){C=!1;break}}var L=r.getTimeSinceLastZoomLevelChanged()>2e3;return new a.FontInfo({zoomLevel:r.getZoomLevel(),fontFamily:e.fontFamily,fontWeight:e.fontWeight,fontSize:e.fontSize,lineHeight:e.lineHeight,letterSpacing:e.letterSpacing,isMonospace:C,typicalHalfwidthCharacterWidth:i.width,typicalFullwidthCharacterWidth:o.width,spaceWidth:s.width,maxDigitWidth:y},L)},t.INSTANCE=new t,t}(i.Disposable),p=function(e){function t(t,n){void 0===n&&(n=null);var i=e.call(this,t)||this;return i._elementSizeObserver=i._register(new u.ElementSizeObserver(n,function(){return i._onReferenceDomElementSizeChanged()})),i._register(h.INSTANCE.onDidChange(function(){return i._onCSSBasedConfigurationChanged()})),i._validatedOptions.automaticLayout&&i._elementSizeObserver.startObserving(),i._register(r.onDidChangeZoomLevel(function(e){return i._recomputeOptions()})),i._register(r.onDidChangeAccessibilitySupport(function(){return i._recomputeOptions()})),i._recomputeOptions(),i}return f(t,e),t.applyFontInfoSlow=function(e,t){e.style.fontFamily=t.fontFamily,e.style.fontWeight=t.fontWeight,e.style.fontSize=t.fontSize+"px",e.style.lineHeight=t.lineHeight+"px",e.style.letterSpacing=t.letterSpacing+"px"},t.applyFontInfo=function(e,t){e.setFontFamily(t.fontFamily),e.setFontWeight(t.fontWeight),e.setFontSize(t.fontSize),e.setLineHeight(t.lineHeight),e.setLetterSpacing(t.letterSpacing)},t.prototype._onReferenceDomElementSizeChanged=function(){this._recomputeOptions()},t.prototype._onCSSBasedConfigurationChanged=function(){this._recomputeOptions()},t.prototype.observeReferenceElement=function(e){this._elementSizeObserver.observe(e)},t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype._getExtraEditorClassName=function(){var e="";return r.isIE?e+="ie ":r.isFirefox?e+="ff ":r.isEdge&&(e+="edge "),o.isMacintosh&&(e+="mac "),e},t.prototype._getEnvConfiguration=function(){return{extraEditorClassName:this._getExtraEditorClassName(),outerWidth:this._elementSizeObserver.getWidth(),outerHeight:this._elementSizeObserver.getHeight(),emptySelectionClipboard:r.isWebKit,pixelRatio:r.getPixelRatio(),zoomLevel:r.getZoomLevel(),accessibilitySupport:r.getAccessibilitySupport()}},t.prototype.readConfiguration=function(e){return h.INSTANCE.readConfiguration(e)},t}(s.CommonEditorConfiguration);t.Configuration=p}),define(d[443],h([1,0,27,112,66,35]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(t){var n=e.call(this,t)||this;return n._visibleLines=new i.VisibleLinesCollection(n),n.domNode=n._visibleLines.domNode,n._dynamicOverlays=[],n._isFocused=!1,n.domNode.setClassName("view-overlays"),n}return f(t,e),t.prototype.shouldRender=function(){if(e.prototype.shouldRender.call(this))return!0;for(var t=0,n=this._dynamicOverlays.length;t'+i+"")},e.prototype.layoutLine=function(e,t){this._domNode&&(this._domNode.setTop(t),this._domNode.setHeight(this._lineHeight))},e}();t.ViewOverlayLine=a;var u=function(e){function t(t){var n=e.call(this,t)||this;return n._contentWidth=n._context.configuration.editor.layoutInfo.contentWidth,n.domNode.setHeight(0),n}return f(t,e),t.prototype.onConfigurationChanged=function(t){return t.layoutInfo&&(this._contentWidth=this._context.configuration.editor.layoutInfo.contentWidth),e.prototype.onConfigurationChanged.call(this,t)},t.prototype.onScrollChanged=function(t){return e.prototype.onScrollChanged.call(this,t)||t.scrollWidthChanged},t.prototype._viewOverlaysRender=function(t){e.prototype._viewOverlaysRender.call(this,t),this.domNode.setWidth(Math.max(t.scrollWidth,this._contentWidth))},t}(s);t.ContentViewOverlays=u;var l=function(e){function t(t){var n=e.call(this,t)||this;return n._contentLeft=n._context.configuration.editor.layoutInfo.contentLeft,n.domNode.setClassName("margin-view-overlays"),n.domNode.setWidth(1),o.Configuration.applyFontInfo(n.domNode,n._context.configuration.editor.fontInfo),n}return f(t,e),t.prototype.onConfigurationChanged=function(t){var n=!1;return t.fontInfo&&(o.Configuration.applyFontInfo(this.domNode,this._context.configuration.editor.fontInfo),n=!0),t.layoutInfo&&(this._contentLeft=this._context.configuration.editor.layoutInfo.contentLeft,n=!0),e.prototype.onConfigurationChanged.call(this,t)||n},t.prototype.onScrollChanged=function(t){return e.prototype.onScrollChanged.call(this,t)||t.scrollHeightChanged},t.prototype._viewOverlaysRender=function(t){e.prototype._viewOverlaysRender.call(this,t);var n=Math.min(t.scrollHeight,1e6);this.domNode.setHeight(n),this.domNode.setWidth(this._contentLeft)},t}(s);t.MarginViewOverlays=l}),define(d[444],h([1,0,27,12,2,49,66,4]),function(e,t,n,i,o,r,s,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var u=function(){return function(e,t,n,i){this.top=e,this.left=t,this.width=n,this.textContent=i}}(),l=function(){function e(e,t){this._context=e,this._isSecondary=t,this._cursorStyle=this._context.configuration.editor.viewInfo.cursorStyle,this._lineHeight=this._context.configuration.editor.lineHeight,this._typicalHalfwidthCharacterWidth=this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth,this._isVisible=!0,this._domNode=n.createFastDomNode(document.createElement("div")),this._isSecondary?this._domNode.setClassName("cursor secondary"):this._domNode.setClassName("cursor"),this._domNode.setHeight(this._lineHeight),this._domNode.setTop(0),this._domNode.setLeft(0),s.Configuration.applyFontInfo(this._domNode,this._context.configuration.editor.fontInfo),this._domNode.setDisplay("none"),this.updatePosition(new i.Position(1,1)),this._isInEditableRange=!0,this._lastRenderedContent="",this._renderData=null}return e.prototype.getDomNode=function(){return this._domNode},e.prototype.getIsInEditableRange=function(){return this._isInEditableRange},e.prototype.getPosition=function(){return this._position},e.prototype.show=function(){this._isVisible||(this._domNode.setVisibility("inherit"),this._isVisible=!0)},e.prototype.hide=function(){this._isVisible&&(this._domNode.setVisibility("hidden"),this._isVisible=!1)},e.prototype.onConfigurationChanged=function(e){return e.lineHeight&&(this._lineHeight=this._context.configuration.editor.lineHeight),e.viewInfo&&(this._cursorStyle=this._context.configuration.editor.viewInfo.cursorStyle),e.fontInfo&&(s.Configuration.applyFontInfo(this._domNode,this._context.configuration.editor.fontInfo),this._typicalHalfwidthCharacterWidth=this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth),!0},e.prototype.onCursorPositionChanged=function(e,t){return this.updatePosition(e),this._isInEditableRange=t,!0},e.prototype._prepareRender=function(e){if(this._cursorStyle===r.TextEditorCursorStyle.Line||this._cursorStyle===r.TextEditorCursorStyle.LineThin){var t=e.visibleRangeForPosition(this._position);if(!t)return null;var n;n=this._cursorStyle===r.TextEditorCursorStyle.Line?a.computeScreenAwareSize(2):a.computeScreenAwareSize(1);var i=e.getVerticalOffsetForLineNumber(this._position.lineNumber)-e.bigNumbersDelta;return new u(i,t.left,n,"")}var s=e.linesVisibleRangesForRange(new o.Range(this._position.lineNumber,this._position.column,this._position.lineNumber,this._position.column+1),!1);if(!s||0===s.length||0===s[0].ranges.length)return null;var l=s[0].ranges[0],c=l.width<1?this._typicalHalfwidthCharacterWidth:l.width,d="";this._cursorStyle===r.TextEditorCursorStyle.Block&&(d=this._context.model.getLineContent(this._position.lineNumber).charAt(this._position.column-1));var h=e.getVerticalOffsetForLineNumber(this._position.lineNumber)-e.bigNumbersDelta;return new u(h,l.left,c,d)},e.prototype.prepareRender=function(e){this._renderData=this._prepareRender(e)},e.prototype.render=function(e){return this._renderData?(this._lastRenderedContent!==this._renderData.textContent&&(this._lastRenderedContent=this._renderData.textContent,this._domNode.domNode.textContent=this._lastRenderedContent),this._domNode.setDisplay("block"),this._domNode.setTop(this._renderData.top),this._domNode.setLeft(this._renderData.left),this._domNode.setWidth(this._renderData.width),this._domNode.setLineHeight(this._lineHeight),this._domNode.setHeight(this._lineHeight),{domNode:this._domNode.domNode,position:this._position,contentLeft:this._renderData.left,height:this._lineHeight,width:2}):(this._domNode.setDisplay("none"),null)},e.prototype.updatePosition=function(e){this._position=e},e}();t.ViewCursor=l}),define(d[58],h([1,0,16]),function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ITelemetryService=n.createDecorator("telemetryService")}),define(d[13],h([1,0,10,24,31,105,44,58,12,56,85,64,19,42]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p){"use strict";function g(e){return e.get(p.ICodeEditorService).getFocusedCodeEditor()}function m(e){var t=e.get(d.IEditorService),n=t.getActiveEditor&&t.getActiveEditor();return p.getCodeEditor(n)}function v(e){return w.registerEditorCommand(e),e}Object.defineProperty(t,"__esModule",{value:!0});var y=function(){function e(e){this.id=e.id,this.precondition=e.precondition,this._kbOpts=e.kbOpts,this._description=e.description}return e.prototype.toCommandAndKeybindingRule=function(e){var t=this,n=this._kbOpts||{primary:0},i=n.kbExpr;this.precondition&&(i=i?h.ContextKeyExpr.and(i,this.precondition):this.precondition);var o="number"==typeof n.weight?n.weight:e;return{id:this.id,handler:function(e,n){return t.runCommand(e,n)},weight:o,when:i,primary:n.primary,secondary:n.secondary,win:n.win,linux:n.linux,mac:n.mac,description:this._description}},e}();t.Command=y;var C=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.bindToContribution=function(e){return function(t){function n(e){var n=t.call(this,e)||this;return n._callback=e.handler,n}return f(n,t),n.prototype.runEditorCommand=function(t,n,i){e(n)&&this._callback(e(n))},n}(t)},t.prototype.runCommand=function(e,t){var n=this,i=g(e);if(i||(i=m(e)),i)return i.invokeWithinContext(function(e){if(e.get(h.IContextKeyService).contextMatchesRules(n.precondition))return n.runEditorCommand(e,i,t)})},t}(y);t.EditorCommand=C;var b=function(e){function t(t){var n=e.call(this,t)||this;return n.label=t.label,n.alias=t.alias,n.menuOpts=t.menuOpts,n}return f(t,e),t.prototype.toMenuItem=function(){return this.menuOpts?{command:{id:this.id,title:this.label},when:this.precondition,group:this.menuOpts.group,order:this.menuOpts.order}:null},t.prototype.runEditorCommand=function(e,t,n){return this.reportTelemetry(e,t),this.run(e,t,n||{})},t.prototype.reportTelemetry=function(e,t){e.get(a.ITelemetryService).publicLog("editorActionInvoked",_({name:this.label,id:this.id},t.getTelemetryData()))},t}(C);t.EditorAction=b,t.editorAction=function(e){w.registerEditorAction(new e)},t.editorCommand=function(e){v(new e)},t.registerEditorCommand=v,t.commonEditorContribution=function(e){E.INSTANCE.registerEditorContribution(e)};var w;!function(e){function t(e,t){o.CommandsRegistry.registerCommand(e,function(e,n){return t(e,n||{})})}e.registerEditorAction=function(e){E.INSTANCE.registerEditorAction(e)},e.getEditorActions=function(){return E.INSTANCE.getEditorActions()},e.getEditorCommand=function(e){return E.INSTANCE.getEditorCommand(e)},e.getEditorContributions=function(){return E.INSTANCE.getEditorContributions()},e.commandWeight=function(e){return void 0===e&&(e=0),r.KeybindingsRegistry.WEIGHT.editorContrib(e)},e.registerEditorCommand=function(e){E.INSTANCE.registerEditorCommand(e)},e.registerLanguageCommand=t,e.registerDefaultLanguageCommand=function(e,o){t(e,function(e,t){var r=t.resource,s=t.position;if(!(r instanceof i.default))throw n.illegalArgument("resource");if(!u.Position.isIPosition(s))throw n.illegalArgument("position");var a=e.get(l.IModelService).getModel(r);if(!a)throw n.illegalArgument("Can not find open model for "+r);var c=u.Position.lift(s);return o(a,c,t)})}}(w=t.CommonEditorRegistry||(t.CommonEditorRegistry={}));var S={EditorCommonContributions:"editor.commonContributions"},E=function(){function e(){this.editorContributions=[],this.editorActions=[],this.editorCommands=Object.create(null)}return e.prototype.registerEditorContribution=function(e){this.editorContributions.push(e)},e.prototype.registerEditorAction=function(e){var t=e.toMenuItem();t&&c.MenuRegistry.appendMenuItem(c.MenuId.EditorContext,t),r.KeybindingsRegistry.registerCommandAndKeybindingRule(e.toCommandAndKeybindingRule(r.KeybindingsRegistry.WEIGHT.editorContrib())),this.editorActions.push(e)},e.prototype.getEditorContributions=function(){return this.editorContributions.slice(0)},e.prototype.getEditorActions=function(){return this.editorActions.slice(0)},e.prototype.registerEditorCommand=function(e){r.KeybindingsRegistry.registerCommandAndKeybindingRule(e.toCommandAndKeybindingRule(r.KeybindingsRegistry.WEIGHT.editorContrib())),this.editorCommands[e.id]=e},e.prototype.getEditorCommand=function(e){return this.editorCommands[e]||null},e.INSTANCE=new e,e}();s.Registry.add(S.EditorCommonContributions,E.INSTANCE)}),define(d[447],h([1,0,10,11,3,7,84,19,321,39,12,2,22,20,330,488,406,55,21,13]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_,y,C){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var b=0,w=function(e){function t(t,n,o,r){var u=e.call(this)||this;return u._onDidDispose=u._register(new i.Emitter),u.onDidDispose=u._onDidDispose.event,u._onDidChangeModelContent=u._register(new i.Emitter),u.onDidChangeModelContent=u._onDidChangeModelContent.event,u._onDidChangeModelLanguage=u._register(new i.Emitter),u.onDidChangeModelLanguage=u._onDidChangeModelLanguage.event,u._onDidChangeModelOptions=u._register(new i.Emitter),u.onDidChangeModelOptions=u._onDidChangeModelOptions.event,u._onDidChangeModelDecorations=u._register(new i.Emitter),u.onDidChangeModelDecorations=u._onDidChangeModelDecorations.event,u._onDidChangeConfiguration=u._register(new i.Emitter),u.onDidChangeConfiguration=u._onDidChangeConfiguration.event,u._onDidChangeModel=u._register(new i.Emitter),u.onDidChangeModel=u._onDidChangeModel.event,u._onDidChangeCursorPosition=u._register(new i.Emitter),u.onDidChangeCursorPosition=u._onDidChangeCursorPosition.event,u._onDidChangeCursorSelection=u._register(new i.Emitter),u.onDidChangeCursorSelection=u._onDidChangeCursorSelection.event,u._onDidLayoutChange=u._register(new i.Emitter),u.onDidLayoutChange=u._onDidLayoutChange.event,u._onDidFocusEditorText=u._register(new i.Emitter),u.onDidFocusEditorText=u._onDidFocusEditorText.event,u._onDidBlurEditorText=u._register(new i.Emitter),u.onDidBlurEditorText=u._onDidBlurEditorText.event,u._onDidFocusEditor=u._register(new i.Emitter),u.onDidFocusEditor=u._onDidFocusEditor.event,u._onDidBlurEditor=u._register(new i.Emitter),u.onDidBlurEditor=u._onDidBlurEditor.event,u._onWillType=u._register(new i.Emitter),u.onWillType=u._onWillType.event,u._onDidType=u._register(new i.Emitter),u.onDidType=u._onDidType.event,u._onDidPaste=u._register(new i.Emitter),u.onDidPaste=u._onDidPaste.event,u.domElement=t,u.id=++b,u._decorationTypeKeysToIds={},u._decorationTypeSubtypes={},n=n||{},u._configuration=u._register(u._createConfiguration(n)),u._register(u._configuration.onDidChange(function(e){u._onDidChangeConfiguration.fire(e),e.layoutInfo&&u._onDidLayoutChange.fire(u._configuration.editor.layoutInfo)})),u._contextKeyService=u._register(r.createScoped(u.domElement)),u._register(new S(u,u._contextKeyService)),u._register(new v.EditorModeContext(u,u._contextKeyService)),u._instantiationService=o.createChild(new s.ServiceCollection([a.IContextKeyService,u._contextKeyService])),u._attachModel(null),u._contributions={},u._actions={},u}return f(t,e),t.prototype.getId=function(){return this.getEditorType()+":"+this.id},t.prototype.getEditorType=function(){return p.EditorType.ICodeEditor},t.prototype.destroy=function(){this.dispose()},t.prototype.dispose=function(){for(var t=Object.keys(this._contributions),n=0,i=t.length;n1),this._hasNonEmptySelection.set(e.some(function(e){return!e.isEmpty()}))):(this._hasMultipleSelections.reset(),this._hasNonEmptySelection.reset())},t.prototype._updateFromFocus=function(){this._editorFocus.set(this._editor.hasWidgetFocus()),this._editorTextFocus.set(this._editor.isFocused())},t}(o.Disposable)}),define(d[134],h([1,0,12,2,20,39,54,173,13,311,21,105,42,19,29,64,119,170]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var y=o.Handler,C=d.KeybindingsRegistry.WEIGHT.editorCore(),b=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){this.runCoreEditorCommand(t._getCursors(),n||{})},t}(u.EditorCommand);t.CoreEditorCommand=b;var w;!function(e){e.description={description:"Scroll editor in the given direction",args:[{name:"Editor scroll argument object",description:"Property-value pairs that can be passed through this argument:\n\t\t\t\t\t* 'to': A mandatory direction value.\n\t\t\t\t\t\t```\n\t\t\t\t\t\t'up', 'down'\n\t\t\t\t\t\t```\n\t\t\t\t\t* 'by': Unit to move. Default is computed based on 'to' value.\n\t\t\t\t\t\t```\n\t\t\t\t\t\t'line', 'wrappedLine', 'page', 'halfPage'\n\t\t\t\t\t\t```\n\t\t\t\t\t* 'value': Number of units to move. Default is '1'.\n\t\t\t\t\t* 'revealCursor': If 'true' reveals the cursor if it is outside view port.\n\t\t\t\t",constraint:function(e){if(!g.isObject(e))return!1;var t=e;return!(!g.isString(t.to)||!g.isUndefined(t.by)&&!g.isString(t.by)||!g.isUndefined(t.value)&&!g.isNumber(t.value)||!g.isUndefined(t.revealCursor)&&!g.isBoolean(t.revealCursor))}}]},e.RawDirection={Up:"up",Down:"down"},e.RawUnit={Line:"line",WrappedLine:"wrappedLine",Page:"page",HalfPage:"halfPage"},e.parse=function(t){var n;switch(t.to){case e.RawDirection.Up:n=1;break;case e.RawDirection.Down:n=2;break;default:return null}var i;switch(t.by){case e.RawUnit.Line:i=1;break;case e.RawUnit.WrappedLine:i=2;break;case e.RawUnit.Page:i=3;break;case e.RawUnit.HalfPage:i=4;break;default:i=2}return{direction:n,unit:i,value:Math.floor(t.value||1),revealCursor:!!t.revealCursor,select:!!t.select}};!function(e){e[e.Up=1]="Up",e[e.Down=2]="Down"}(e.Direction||(e.Direction={}));!function(e){e[e.Line=1]="Line",e[e.WrappedLine=2]="WrappedLine",e[e.Page=3]="Page",e[e.HalfPage=4]="HalfPage"}(e.Unit||(e.Unit={}))}(w=t.EditorScroll_||(t.EditorScroll_={}));var S;!function(e){e.description={description:"Reveal the given line at the given logical position",args:[{name:"Reveal line argument object",description:"Property-value pairs that can be passed through this argument:\n\t\t\t\t\t* 'lineNumber': A mandatory line number value.\n\t\t\t\t\t* 'at': Logical position at which line has to be revealed .\n\t\t\t\t\t\t```\n\t\t\t\t\t\t'top', 'center', 'bottom'\n\t\t\t\t\t\t```\n\t\t\t\t",constraint:function(e){if(!g.isObject(e))return!1;var t=e;return!(!g.isNumber(t.lineNumber)||!g.isUndefined(t.at)&&!g.isString(t.at))}}]},e.RawAtArgument={Top:"top",Center:"center",Bottom:"bottom"}}(S=t.RevealLine_||(t.RevealLine_={}));var E;!function(e){var t=function(e){function t(t){var n=e.call(this,t)||this;return n._inSelectionMode=t.inSelectionMode,n}return f(t,e),t.prototype.runCoreEditorCommand=function(e,t){e.context.model.pushStackElement(),e.setStates(t.source,s.CursorChangeReason.Explicit,[a.CursorMoveCommands.moveTo(e.context,e.getPrimaryCursor(),this._inSelectionMode,t.position,t.viewPosition)]),e.reveal(!0,0)},t}(b);e.MoveTo=u.registerEditorCommand(new t({id:"_moveTo",inSelectionMode:!1,precondition:null})),e.MoveToSelect=u.registerEditorCommand(new t({id:"_moveToSelect",inSelectionMode:!0,precondition:null}));var o=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.runCoreEditorCommand=function(e,t){e.context.model.pushStackElement();var n=this._getColumnSelectResult(e.context,e.getPrimaryCursor(),e.getColumnSelectData(),t);e.setStates(t.source,s.CursorChangeReason.Explicit,n.viewStates.map(function(e){return r.CursorState.fromViewState(e)})),e.setColumnSelectData({toViewLineNumber:n.toLineNumber,toViewVisualColumn:n.toVisualColumn}),e.reveal(!0,n.reversed?1:2)},t}(b);e.ColumnSelect=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"columnSelect",precondition:null})||this}return f(t,e),t.prototype._getColumnSelectResult=function(e,t,i,o){var r,s=e.model.validatePosition(o.position);return r=o.viewPosition?e.validateViewPosition(new n.Position(o.viewPosition.lineNumber,o.viewPosition.column),s):e.convertModelPositionToViewPosition(s),l.ColumnSelection.columnSelect(e.config,e.viewModel,t.viewState.selection,r.lineNumber,o.mouseColumn-1)},t}(o))),e.CursorColumnSelectLeft=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"cursorColumnSelectLeft",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:3599,linux:{primary:0}}})||this}return f(t,e),t.prototype._getColumnSelectResult=function(e,t,n,i){return l.ColumnSelection.columnSelectLeft(e.config,e.viewModel,t.viewState,n.toViewLineNumber,n.toViewVisualColumn)},t}(o))),e.CursorColumnSelectRight=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"cursorColumnSelectRight",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:3601,linux:{primary:0}}})||this}return f(t,e),t.prototype._getColumnSelectResult=function(e,t,n,i){return l.ColumnSelection.columnSelectRight(e.config,e.viewModel,t.viewState,n.toViewLineNumber,n.toViewVisualColumn)},t}(o)));var d=function(e){function t(t){var n=e.call(this,t)||this;return n._isPaged=t.isPaged,n}return f(t,e),t.prototype._getColumnSelectResult=function(e,t,n,i){return l.ColumnSelection.columnSelectUp(e.config,e.viewModel,t.viewState,this._isPaged,n.toViewLineNumber,n.toViewVisualColumn)},t}(o);e.CursorColumnSelectUp=u.registerEditorCommand(new d({isPaged:!1,id:"cursorColumnSelectUp",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:3600,linux:{primary:0}}})),e.CursorColumnSelectPageUp=u.registerEditorCommand(new d({isPaged:!0,id:"cursorColumnSelectPageUp",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:3595,linux:{primary:0}}}));var h=function(e){function t(t){var n=e.call(this,t)||this;return n._isPaged=t.isPaged,n}return f(t,e),t.prototype._getColumnSelectResult=function(e,t,n,i){return l.ColumnSelection.columnSelectDown(e.config,e.viewModel,t.viewState,this._isPaged,n.toViewLineNumber,n.toViewVisualColumn)},t}(o);e.CursorColumnSelectDown=u.registerEditorCommand(new h({isPaged:!1,id:"cursorColumnSelectDown",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:3602,linux:{primary:0}}})),e.CursorColumnSelectPageDown=u.registerEditorCommand(new h({isPaged:!0,id:"cursorColumnSelectPageDown",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:3596,linux:{primary:0}}}));var p=function(e){function t(){return e.call(this,{id:"cursorMove",precondition:null,description:a.CursorMove.description})||this}return f(t,e),t.prototype.runCoreEditorCommand=function(e,t){var n=a.CursorMove.parse(t);n&&this._runCursorMove(e,t.source,n)},t.prototype._runCursorMove=function(e,t,n){e.context.model.pushStackElement(),e.setStates(t,s.CursorChangeReason.Explicit,r.CursorState.ensureInEditableRange(e.context,a.CursorMoveCommands.move(e.context,e.getAll(),n))),e.reveal(!0,0)},t}(b);e.CursorMoveImpl=p,e.CursorMove=u.registerEditorCommand(new p);var g;!function(e){e[e.PAGE_SIZE_MARKER=-1]="PAGE_SIZE_MARKER"}(g||(g={}));var m=function(t){function n(e){var n=t.call(this,e)||this;return n._staticArgs=e.args,n}return f(n,t),n.prototype.runCoreEditorCommand=function(t,n){var i=this._staticArgs;-1===this._staticArgs.value&&(i={direction:this._staticArgs.direction,unit:this._staticArgs.unit,select:this._staticArgs.select,value:t.context.config.pageSize}),e.CursorMove._runCursorMove(t,n.source,i)},n}(b);e.CursorLeft=u.registerEditorCommand(new m({args:{direction:0,unit:0,select:!1,value:1},id:"cursorLeft",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:15,mac:{primary:15,secondary:[288]}}})),e.CursorLeftSelect=u.registerEditorCommand(new m({args:{direction:0,unit:0,select:!0,value:1},id:"cursorLeftSelect",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:1039}})),e.CursorRight=u.registerEditorCommand(new m({args:{direction:1,unit:0,select:!1,value:1},id:"cursorRight",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:17,mac:{primary:17,secondary:[292]}}})),e.CursorRightSelect=u.registerEditorCommand(new m({args:{direction:1,unit:0,select:!0,value:1},id:"cursorRightSelect",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:1041}})),e.CursorUp=u.registerEditorCommand(new m({args:{direction:2,unit:2,select:!1,value:1},id:"cursorUp",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:16,mac:{primary:16,secondary:[302]}}})),e.CursorUpSelect=u.registerEditorCommand(new m({args:{direction:2,unit:2,select:!0,value:1},id:"cursorUpSelect",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:1040,secondary:[3088],mac:{primary:1040},linux:{primary:1040}}})),e.CursorPageUp=u.registerEditorCommand(new m({args:{direction:2,unit:2,select:!1,value:-1},id:"cursorPageUp",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:11}})),e.CursorPageUpSelect=u.registerEditorCommand(new m({args:{direction:2,unit:2,select:!0,value:-1},id:"cursorPageUpSelect",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:1035}})),e.CursorDown=u.registerEditorCommand(new m({args:{direction:3,unit:2,select:!1,value:1},id:"cursorDown",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:18,mac:{primary:18,secondary:[300]}}})),e.CursorDownSelect=u.registerEditorCommand(new m({args:{direction:3,unit:2,select:!0,value:1},id:"cursorDownSelect",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:1042,secondary:[3090],mac:{primary:1042},linux:{primary:1042}}})),e.CursorPageDown=u.registerEditorCommand(new m({args:{direction:3,unit:2,select:!1,value:-1},id:"cursorPageDown",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:12}})),e.CursorPageDownSelect=u.registerEditorCommand(new m({args:{direction:3,unit:2,select:!0,value:-1},id:"cursorPageDownSelect",precondition:null,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:1036}})),e.CreateCursor=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"createCursor",precondition:null})||this}return f(t,e),t.prototype.runCoreEditorCommand=function(e,t){var n=e.context;if(!n.config.readOnly&&!n.model.hasEditableRange()){var i;i=t.wholeLine?a.CursorMoveCommands.line(n,e.getPrimaryCursor(),!1,t.position,t.viewPosition):a.CursorMoveCommands.moveTo(n,e.getPrimaryCursor(),!1,t.position,t.viewPosition);var o=e.getAll();if(o.length>1)for(var r=i.modelState?i.modelState.position:null,u=i.viewState?i.viewState.position:null,l=0,c=o.length;lr&&(o=r);var s=new i.Range(o,1,o,e.context.model.getLineMaxColumn(o)),a=0;if(n.at)switch(n.at){case S.RawAtArgument.Top:a=3;break;case S.RawAtArgument.Center:a=1;break;case S.RawAtArgument.Bottom:a=4}var u=e.context.convertModelRangeToViewRange(s);e.revealRange(!1,u,a)},t}(b))),e.SelectAll=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"selectAll",precondition:null})||this}return f(t,e),t.prototype.runCoreEditorCommand=function(e,t){e.context.model.pushStackElement(),e.setStates(t.source,s.CursorChangeReason.Explicit,[a.CursorMoveCommands.selectAll(e.context,e.getPrimaryCursor())])},t}(b)))}(E=t.CoreNavigationCommands||(t.CoreNavigationCommands={}));!function(e){e.LineBreakInsert=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"lineBreakInsert",precondition:c.EditorContextKeys.writable,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:null,mac:{primary:301}}})||this}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){t.pushUndoStop(),t.executeCommands(this.id,v.TypeOperations.lineBreakInsert(t._getCursorConfiguration(),t.getModel(),t.getSelections()))},t}(u.EditorCommand))),e.Outdent=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"outdent",precondition:c.EditorContextKeys.writable,kbOpts:{weight:C,kbExpr:p.ContextKeyExpr.and(c.EditorContextKeys.textFocus,c.EditorContextKeys.tabDoesNotMoveFocus),primary:1026}})||this}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){t.pushUndoStop(),t.executeCommands(this.id,v.TypeOperations.outdent(t._getCursorConfiguration(),t.getModel(),t.getSelections())),t.pushUndoStop()},t}(u.EditorCommand))),e.Tab=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"tab",precondition:c.EditorContextKeys.writable,kbOpts:{weight:C,kbExpr:p.ContextKeyExpr.and(c.EditorContextKeys.textFocus,c.EditorContextKeys.tabDoesNotMoveFocus),primary:2}})||this}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){t.pushUndoStop(),t.executeCommands(this.id,v.TypeOperations.tab(t._getCursorConfiguration(),t.getModel(),t.getSelections())),t.pushUndoStop()},t}(u.EditorCommand))),e.DeleteLeft=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"deleteLeft",precondition:c.EditorContextKeys.writable,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:1,secondary:[1025],mac:{primary:1,secondary:[1025,294,257]}}})||this}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){var i=_.DeleteOperations.deleteLeft(t._getCursorConfiguration(),t.getModel(),t.getSelections()),o=i[0],r=i[1];o&&t.pushUndoStop(),t.executeCommands(this.id,r)},t}(u.EditorCommand))),e.DeleteRight=u.registerEditorCommand(new(function(e){function t(){return e.call(this,{id:"deleteRight",precondition:c.EditorContextKeys.writable,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:20,mac:{primary:20,secondary:[290,276]}}})||this}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){var i=_.DeleteOperations.deleteRight(t._getCursorConfiguration(),t.getModel(),t.getSelections()),o=i[0],r=i[1];o&&t.pushUndoStop(),t.executeCommands(this.id,r)},t}(u.EditorCommand)))}(t.CoreEditingCommands||(t.CoreEditingCommands={}));var L;!function(e){function t(e){return e.get(h.ICodeEditorService).getFocusedCodeEditor()}function n(e){var t=e.get(m.IEditorService),n=t.getActiveEditor&&t.getActiveEditor();return h.getCodeEditor(n)}function i(e){d.KeybindingsRegistry.registerCommandAndKeybindingRule(e.toCommandAndKeybindingRule(C))}function o(e){i(new s("default:"+e,e)),i(new s(e,e))}var r=function(e){function i(t){var n=e.call(this,t)||this;return n._editorHandler=t.editorHandler,n._inputHandler=t.inputHandler,n}return f(i,e),i.prototype.runCommand=function(e,i){var o=t(e);if(o&&o.isFocused())return this._runEditorHandler(o,i);var r=document.activeElement;if(!(r&&["input","textarea"].indexOf(r.tagName.toLowerCase())>=0)){var s=n(e);return s?(s.focus(),this._runEditorHandler(s,i)):void 0}document.execCommand(this._inputHandler)},i.prototype._runEditorHandler=function(e,t){var n=this._editorHandler;"string"==typeof n?e.trigger("keyboard",n,t):((t=t||{}).source="keyboard",n.runEditorCommand(null,e,t))},i}(u.Command);i(new r({editorHandler:E.SelectAll,inputHandler:"selectAll",id:"editor.action.selectAll",precondition:null,kbOpts:{weight:C,kbExpr:null,primary:2079}})),i(new r({editorHandler:y.Undo,inputHandler:"undo",id:y.Undo,precondition:c.EditorContextKeys.writable,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:2104}})),i(new r({editorHandler:y.Redo,inputHandler:"redo",id:y.Redo,precondition:c.EditorContextKeys.writable,kbOpts:{weight:C,kbExpr:c.EditorContextKeys.textFocus,primary:2103,secondary:[3128],mac:{primary:3128}}}));var s=function(e){function n(t,n){var i=e.call(this,{id:t,precondition:null})||this;return i._handlerId=n,i}return f(n,e),n.prototype.runCommand=function(e,n){var i=t(e);i&&i.trigger("keyboard",this._handlerId,n)},n}(u.Command);o(y.Type),o(y.ReplacePreviousChar),o(y.CompositionStart),o(y.CompositionEnd),o(y.Paste),o(y.Cut)}(L||(L={}))}),define(d[449],h([1,0,12,20,134]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t,n,i,o){this.configuration=e,this.viewModel=t,this._execCoreEditorCommandFunc=n,this.outgoingEvents=i,this.commandService=o}return e.prototype._execMouseCommand=function(e,t){t.source="mouse",this._execCoreEditorCommandFunc(e,t)},e.prototype.paste=function(e,t,n){this.commandService.executeCommand(i.Handler.Paste,{text:t,pasteOnNewLine:n})},e.prototype.type=function(e,t){this.commandService.executeCommand(i.Handler.Type,{text:t})},e.prototype.replacePreviousChar=function(e,t,n){this.commandService.executeCommand(i.Handler.ReplacePreviousChar,{text:t,replaceCharCnt:n})},e.prototype.compositionStart=function(e){this.commandService.executeCommand(i.Handler.CompositionStart,{})},e.prototype.compositionEnd=function(e){this.commandService.executeCommand(i.Handler.CompositionEnd,{})},e.prototype.cut=function(e){this.commandService.executeCommand(i.Handler.Cut,{})},e.prototype._validateViewColumn=function(e){var t=this.viewModel.getLineMinColumn(e.lineNumber);return e.column=4?this.selectAll():3===e.mouseDownCount?this._hasMulticursorModifier(e)?e.inSelectionMode?this.lastCursorLineSelectDrag(e.position):this.lastCursorLineSelect(e.position):e.inSelectionMode?this.lineSelectDrag(e.position):this.lineSelect(e.position):2===e.mouseDownCount?this._hasMulticursorModifier(e)?this.lastCursorWordSelect(e.position):e.inSelectionMode?this.wordSelectDrag(e.position):this.wordSelect(e.position):this._hasMulticursorModifier(e)?this._hasNonMulticursorModifier(e)||(e.shiftKey?this.columnSelect(e.position,e.mouseColumn):e.inSelectionMode?this.lastCursorMoveToSelect(e.position):this.createCursor(e.position,!1)):e.inSelectionMode?this.moveToSelect(e.position):this.moveTo(e.position)},e.prototype._usualArgs=function(e){return e=this._validateViewColumn(e),{position:this.convertViewToModelPosition(e),viewPosition:e}},e.prototype.moveTo=function(e){this._execMouseCommand(o.CoreNavigationCommands.MoveTo,this._usualArgs(e))},e.prototype.moveToSelect=function(e){this._execMouseCommand(o.CoreNavigationCommands.MoveToSelect,this._usualArgs(e))},e.prototype.columnSelect=function(e,t){e=this._validateViewColumn(e),this._execMouseCommand(o.CoreNavigationCommands.ColumnSelect,{position:this.convertViewToModelPosition(e),viewPosition:e,mouseColumn:t})},e.prototype.createCursor=function(e,t){e=this._validateViewColumn(e),this._execMouseCommand(o.CoreNavigationCommands.CreateCursor,{position:this.convertViewToModelPosition(e),viewPosition:e,wholeLine:t})},e.prototype.lastCursorMoveToSelect=function(e){this._execMouseCommand(o.CoreNavigationCommands.LastCursorMoveToSelect,this._usualArgs(e))},e.prototype.wordSelect=function(e){this._execMouseCommand(o.CoreNavigationCommands.WordSelect,this._usualArgs(e))},e.prototype.wordSelectDrag=function(e){this._execMouseCommand(o.CoreNavigationCommands.WordSelectDrag,this._usualArgs(e))},e.prototype.lastCursorWordSelect=function(e){this._execMouseCommand(o.CoreNavigationCommands.LastCursorWordSelect,this._usualArgs(e))},e.prototype.lineSelect=function(e){this._execMouseCommand(o.CoreNavigationCommands.LineSelect,this._usualArgs(e))},e.prototype.lineSelectDrag=function(e){this._execMouseCommand(o.CoreNavigationCommands.LineSelectDrag,this._usualArgs(e))},e.prototype.lastCursorLineSelect=function(e){this._execMouseCommand(o.CoreNavigationCommands.LastCursorLineSelect,this._usualArgs(e))},e.prototype.lastCursorLineSelectDrag=function(e){this._execMouseCommand(o.CoreNavigationCommands.LastCursorLineSelectDrag,this._usualArgs(e))},e.prototype.selectAll=function(){this._execMouseCommand(o.CoreNavigationCommands.SelectAll,{})},e.prototype.convertViewToModelPosition=function(e){return this.viewModel.coordinatesConverter.convertViewPositionToModelPosition(e)},e.prototype.emitKeyDown=function(e){this.outgoingEvents.emitKeyDown(e)},e.prototype.emitKeyUp=function(e){this.outgoingEvents.emitKeyUp(e)},e.prototype.emitContextMenu=function(e){this.outgoingEvents.emitContextMenu(e)},e.prototype.emitMouseMove=function(e){this.outgoingEvents.emitMouseMove(e)},e.prototype.emitMouseLeave=function(e){this.outgoingEvents.emitMouseLeave(e)},e.prototype.emitMouseUp=function(e){this.outgoingEvents.emitMouseUp(e)},e.prototype.emitMouseDown=function(e){this.outgoingEvents.emitMouseDown(e)},e.prototype.emitMouseDrag=function(e){this.outgoingEvents.emitMouseDrag(e)},e.prototype.emitMouseDrop=function(e){this.outgoingEvents.emitMouseDrop(e)},e}();t.ViewController=r}),define(d[450],h([1,0,342,21,13,241]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(t,n){var i=e.call(this,n)||this;return i.left=t,i}return f(t,e),t.prototype.run=function(e,t){for(var n=[],i=t.getSelections(),o=0;o0&&(t.pushUndoStop(),t.executeCommands(this.id,o),t.pushUndoStop())},t=v([r.editorAction],t)}(r.EditorAction)}),define(d[452],h([1,0,344,28,15,42,13,154,21,314]),function(e,t,n,i,o,r,s,a,u){"use strict";function l(e){return e?s.editorAction:function(){}}Object.defineProperty(t,"__esModule",{value:!0});var c="9_cutcopypaste",d=o.isNative||document.queryCommandSupported("cut"),h=o.isNative||document.queryCommandSupported("copy"),p=h&&!i.isEdgeOrIE,g=o.isNative||!i.isChrome&&document.queryCommandSupported("paste"),m=function(e){function t(t,n){var i=e.call(this,n)||this;return i.browserCommand=t,i}return f(t,e),t.prototype.runCommand=function(e,t){var n=e.get(r.ICodeEditorService).getFocusedCodeEditor();n&&n.isFocused()?n.trigger("keyboard",this.id,t):document.execCommand(this.browserCommand)},t.prototype.run=function(e,t){t.focus(),document.execCommand(this.browserCommand)},t}(s.EditorAction);(function(e){function t(){var t={kbExpr:u.EditorContextKeys.textFocus,primary:2102,win:{primary:2102,secondary:[1044]}};return o.isNative||(t=null),e.call(this,"cut",{id:"editor.action.clipboardCutAction",label:n.localize(0,null),alias:"Cut",precondition:u.EditorContextKeys.writable,kbOpts:t,menuOpts:{group:c,order:1}})||this}f(t,e),t.prototype.run=function(t,n){!n.getConfiguration().emptySelectionClipboard&&n.getSelection().isEmpty()||e.prototype.run.call(this,t,n)},t=v([l(d)],t)})(m),function(e){function t(){var t={kbExpr:u.EditorContextKeys.textFocus,primary:2081,win:{primary:2081,secondary:[2067]}};return o.isNative||(t=null),e.call(this,"copy",{id:"editor.action.clipboardCopyAction",label:n.localize(1,null),alias:"Copy",precondition:null,kbOpts:t,menuOpts:{group:c,order:2}})||this}f(t,e),t.prototype.run=function(t,n){!n.getConfiguration().emptySelectionClipboard&&n.getSelection().isEmpty()||e.prototype.run.call(this,t,n)},t=v([l(h)],t)}(m),function(e){function t(){var t={kbExpr:u.EditorContextKeys.textFocus,primary:2100,win:{primary:2100,secondary:[1043]}};return o.isNative||(t=null),e.call(this,"paste",{id:"editor.action.clipboardPasteAction",label:n.localize(2,null),alias:"Paste",precondition:u.EditorContextKeys.writable,kbOpts:t,menuOpts:{group:c,order:3}})||this}f(t,e),t=v([l(g)],t)}(m),function(e){function t(){return e.call(this,"copy",{id:"editor.action.clipboardCopyWithSyntaxHighlightingAction",label:n.localize(3,null),alias:"Copy With Syntax Highlighting",precondition:null,kbOpts:{kbExpr:u.EditorContextKeys.textFocus,primary:null}})||this}f(t,e),t.prototype.run=function(t,n){!n.getConfiguration().emptySelectionClipboard&&n.getSelection().isEmpty()||(a.CopyOptions.forceCopyWithSyntaxHighlighting=!0,e.prototype.run.call(this,t,n),a.CopyOptions.forceCopyWithSyntaxHighlighting=!1)},t=v([l(p)],t)}(m)}),define(d[453],h([1,0,10,33,24,7,13,17,56,18]),function(e,t,n,i,o,r,s,a,u,l){"use strict";function c(e){var t=[],o=a.CodeLensProviderRegistry.ordered(e),s=o.map(function(i){return l.asWinJsPromise(function(t){return i.provideCodeLenses(e,t)}).then(function(e){if(Array.isArray(e))for(var n=0,o=e;nt.symbol.range.startLineNumber?1:o.indexOf(e.provider)o.indexOf(t.provider)?1:e.symbol.range.startColumnt.symbol.range.startColumn?1:0})})}Object.defineProperty(t,"__esModule",{value:!0}),t.getCodeLensData=c,s.CommonEditorRegistry.registerLanguageCommand("_executeCodeLensProvider",function(e,t){var i=t.resource;if(!(i instanceof o.default))throw n.illegalArgument();var r=e.get(u.IModelService).getModel(i);if(!r)throw n.illegalArgument();return c(r).then(function(e){return e.map(function(e){return e.symbol})})})}),define(d[454],h([1,0,345,40,21,13,189,250]),function(e,t,n,i,o,r,s,a){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var u=function(e){function t(t,n){var i=e.call(this,n)||this;return i._type=t,i}return f(t,e),t.prototype.run=function(e,t){var n=t.getModel();if(n){for(var i=[],o=t.getSelections(),r=n.getOptions(),s=0;s0&&s._contextViewService.hideContextView()})),this._toDispose.push(this._editor.onKeyDown(function(e){58===e.keyCode&&(e.preventDefault(),e.stopPropagation(),s.showContextMenu())}))}return t=e,e.get=function(e){return e.getContribution(t.ID)},e.prototype._onContextMenu=function(e){if(!this._editor.getConfiguration().contribInfo.contextmenu)return this._editor.focus(),void(e.target.position&&!this._editor.getSelection().containsPosition(e.target.position)&&this._editor.setPosition(e.target.position));if(e.target.type!==p.MouseTargetType.OVERLAY_WIDGET&&(e.event.preventDefault(),e.target.type===p.MouseTargetType.CONTENT_TEXT||e.target.type===p.MouseTargetType.CONTENT_EMPTY||e.target.type===p.MouseTargetType.TEXTAREA)){this._editor.focus(),e.target.position&&!this._editor.getSelection().containsPosition(e.target.position)&&this._editor.setPosition(e.target.position);var t;e.target.type!==p.MouseTargetType.TEXTAREA&&(t={x:e.event.posx,y:e.event.posy+1}),this.showContextMenu(t)}},e.prototype.showContextMenu=function(e){if(this._editor.getConfiguration().contribInfo.contextmenu)if(this._contextMenuService){var t=this._getMenuActions();t.length>0&&this._doShowContextMenu(t,e)}else this._editor.focus()},e.prototype._getMenuActions=function(){var e=[],t=this._menuService.createMenu(c.MenuId.EditorContext,this._contextKeyService),n=t.getActions({arg:this._editor.getModel().uri});t.dispose();for(var i=0,o=n;i0&&this._contextViewService.hideContextView(),this._toDispose=i.dispose(this._toDispose)},e.ID="editor.contrib.contextmenu",e=t=v([g.editorContribution,y(1,a.IContextMenuService),y(2,a.IContextViewService),y(3,l.IContextKeyService),y(4,u.IKeybindingService),y(5,c.IMenuService)],e);var t}();t.ContextMenuController=m;!function(e){function t(){return e.call(this,{id:"editor.action.showContextMenu",label:n.localize(0,null),alias:"Show Editor Context Menu",precondition:null,kbOpts:{kbExpr:d.EditorContextKeys.textFocus,primary:1092}})||this}f(t,e),t.prototype.run=function(e,t){m.get(t).showContextMenu()},t=v([h.editorAction],t)}(h.EditorAction)}),define(d[456],h([1,0,13,3,21,30]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e){this.selections=e}return e.prototype.equals=function(e){var t=this.selections.length;if(t!==e.selections.length)return!1;for(var n=0;n50&&(n._undoStack=n._undoStack.splice(0,n._undoStack.length-50))),n._prevState=n._readState()})),n}return f(t,e),n=t,t.get=function(e){return e.getContribution(n.ID)},t.prototype._readState=function(){return this._editor.getModel()?new s(this._editor.getSelections()):null},t.prototype.getId=function(){return n.ID},t.prototype.cursorUndo=function(){for(var e=new s(this._editor.getSelections());this._undoStack.length>0;){var t=this._undoStack.pop();if(!t.equals(e))return this._isCursorUndo=!0,this._editor.setSelections(t.selections),void(this._isCursorUndo=!1)}},t.ID="editor.contrib.cursorUndoController",t=n=v([r.editorContribution],t);var n}(i.Disposable);t.CursorUndoController=a;var u=function(e){function t(){return e.call(this,{id:"cursorUndo",precondition:null,kbOpts:{kbExpr:o.EditorContextKeys.textFocus,primary:2099}})||this}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){a.get(t).cursorUndo()},t=v([n.editorCommand],t)}(n.EditorCommand);t.CursorUndo=u}),define(d[176],h([1,0,348,434,40,3,19,2,22,9,20,13,121,253,252,17,18,54,21,76,34]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_,C,b,w,S){"use strict";function E(e,t){var n=L.get(e);if(!n)return null;var i,o,r=n.getState(),s=r.wholeWord,a=r.matchCase;if(!e.isFocused()&&r.isRevealed&&r.searchString.length>0)i=r.searchString;else{var l=e.getSelection();if(l.startLineNumber!==l.endLineNumber&&!t.allowMultiline)return null;if(l.isEmpty()){var c=e.getModel().getWordAtPosition(l.getStartPosition());if(!c)return null;i=c.word,o=new u.Selection(l.startLineNumber,c.startColumn,l.startLineNumber,c.endColumn)}else i=e.getModel().getValueInRange(l).replace(/\r\n/g,"\n");t.changeFindSearchString&&n.setSearchString(i)}return t.highlightFindOptions&&n.highlightFindOptions(),{searchText:i,matchCase:a,wholeWord:s,currentMatch:o}}Object.defineProperty(t,"__esModule",{value:!0});!function(e){e[e.NoFocusChange=0]="NoFocusChange",e[e.FocusFindInput=1]="FocusFindInput",e[e.FocusReplaceInput=2]="FocusReplaceInput"}(t.FindStartFocusAction||(t.FindStartFocusAction={})),t.CONTEXT_FIND_WIDGET_VISIBLE=new s.RawContextKey("findWidgetVisible",!1),t.CONTEXT_FIND_WIDGET_NOT_VISIBLE=t.CONTEXT_FIND_WIDGET_VISIBLE.toNegated(),t.CONTEXT_FIND_INPUT_FOCUSSED=new s.RawContextKey("findInputFocussed",!1);var L=function(e){function n(n,o,r){var s=e.call(this)||this;return s._editor=n,s._findWidgetVisible=t.CONTEXT_FIND_WIDGET_VISIBLE.bindTo(o),s._storageService=r,s._updateHistoryDelayer=new _.Delayer(500),s._currentHistoryNavigator=new i.HistoryNavigator,s._state=s._register(new p.FindReplaceState),s.loadQueryState(),s._register(s._state.addChangeListener(function(e){return s._onStateChanged(e)})),s._model=null,s._register(s._editor.onDidChangeModel(function(){var e=s._editor.getModel()&&s._state.isRevealed;s.disposeModel(),s._state.change({searchScope:null,matchCase:s._storageService.getBoolean("editor.matchCase",w.StorageScope.WORKSPACE,!1),wholeWord:s._storageService.getBoolean("editor.wholeWord",w.StorageScope.WORKSPACE,!1),isRegex:s._storageService.getBoolean("editor.isRegex",w.StorageScope.WORKSPACE,!1)},!1),e&&s._start({forceRevealReplace:!1,seedSearchStringFromSelection:!1,shouldFocus:0,shouldAnimate:!1})})),s}return f(n,e),n.get=function(e){return e.getContribution(n.ID)},n.prototype.dispose=function(){this.disposeModel(),e.prototype.dispose.call(this)},n.prototype.disposeModel=function(){this._model&&(this._model.dispose(),this._model=null)},n.prototype.getId=function(){return n.ID},n.prototype._onStateChanged=function(e){this.saveQueryState(e),e.updateHistory&&e.searchString&&this._delayedUpdateHistory(),e.isRevealed&&(this._state.isRevealed?this._findWidgetVisible.set(!0):(this._findWidgetVisible.reset(),this.disposeModel()))},n.prototype.saveQueryState=function(e){e.isRegex&&void 0!==this._state.isRegex&&this._storageService.store("editor.isRegex",this._state.isRegex,w.StorageScope.WORKSPACE),e.wholeWord&&void 0!==this._state.wholeWord&&this._storageService.store("editor.wholeWord",this._state.wholeWord,w.StorageScope.WORKSPACE),e.matchCase&&void 0!==this._state.matchCase&&this._storageService.store("editor.matchCase",this._state.matchCase,w.StorageScope.WORKSPACE)},n.prototype.loadQueryState=function(){this._state.change({matchCase:this._storageService.getBoolean("editor.matchCase",w.StorageScope.WORKSPACE,this._state.matchCase),wholeWord:this._storageService.getBoolean("editor.wholeWord",w.StorageScope.WORKSPACE,this._state.wholeWord),isRegex:this._storageService.getBoolean("editor.isRegex",w.StorageScope.WORKSPACE,this._state.isRegex)},!1)},n.prototype._delayedUpdateHistory=function(){this._updateHistoryDelayer.trigger(this._updateHistory.bind(this))},n.prototype._updateHistory=function(){this._state.searchString&&this._currentHistoryNavigator.add(this._state.searchString)},n.prototype.getState=function(){return this._state},n.prototype.getHistory=function(){return this._currentHistoryNavigator},n.prototype.closeFindWidget=function(){this._state.change({isRevealed:!1,searchScope:null},!1),this._editor.focus()},n.prototype.toggleCaseSensitive=function(){this._state.change({matchCase:!this._state.matchCase},!1)},n.prototype.toggleWholeWords=function(){this._state.change({wholeWord:!this._state.wholeWord},!1)},n.prototype.toggleRegex=function(){this._state.change({isRegex:!this._state.isRegex},!1)},n.prototype.toggleSearchScope=function(){if(this._state.searchScope)this._state.change({searchScope:null},!0);else{var e=this._editor.getSelection();1===e.endColumn&&e.endLineNumber>e.startLineNumber&&(e=e.setEndPosition(e.endLineNumber-1,1)),e.isEmpty()||this._state.change({searchScope:e},!0)}},n.prototype.setSearchString=function(e){this._state.change({searchString:e},!1)},n.prototype.highlightFindOptions=function(){},n.prototype._start=function(e){if(this.disposeModel(),this._editor.getModel()){var t={isRevealed:!0};if(e.seedSearchStringFromSelection&&this._editor.getConfiguration().contribInfo.find.seedSearchStringFromSelection){var n=g.getSelectionSearchString(this._editor);n&&(this._state.isRegex?t.searchString=l.escapeRegExpCharacters(n):t.searchString=n)}e.forceRevealReplace?t.isReplaceRevealed=!0:this._findWidgetVisible.get()||(t.isReplaceRevealed=!1),this._state.change(t,!1),this._model||(this._model=new h.FindModelBoundToEditorModel(this._editor,this._state))}},n.prototype.start=function(e){this._start(e)},n.prototype.moveToNextMatch=function(){return!!this._model&&(this._model.moveToNextMatch(),!0)},n.prototype.moveToPrevMatch=function(){return!!this._model&&(this._model.moveToPrevMatch(),!0)},n.prototype.replace=function(){return!!this._model&&(this._model.replace(),!0)},n.prototype.replaceAll=function(){return!!this._model&&(this._model.replaceAll(),!0)},n.prototype.selectAllMatches=function(){return!!this._model&&(this._model.selectAllMatches(),this._editor.focus(),!0)},n.prototype.showPreviousFindTerm=function(){var e=this._currentHistoryNavigator.previous();return e&&this._state.change({searchString:e},!1,!1),!0},n.prototype.showNextFindTerm=function(){var e=this._currentHistoryNavigator.next();return e&&this._state.change({searchString:e},!1,!1),!0},n.ID="editor.contrib.findController",n=v([y(1,s.IContextKeyService),y(2,w.IStorageService)],n)}(r.Disposable);t.CommonFindController=L;var x=function(e){function t(){return e.call(this,{id:h.FIND_IDS.StartFindAction,label:n.localize(0,null),alias:"Find",precondition:null,kbOpts:{kbExpr:null,primary:2084,mac:{primary:2084,secondary:[2083]}}})||this}return f(t,e),t.prototype.run=function(e,t){var n=L.get(t);n&&n.start({forceRevealReplace:!1,seedSearchStringFromSelection:!0,shouldFocus:1,shouldAnimate:!0})},t=v([d.editorAction],t)}(d.EditorAction);t.StartFindAction=x;var N=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.run=function(e,t){var n=L.get(t);n&&!this._run(n)&&(n.start({forceRevealReplace:!1,seedSearchStringFromSelection:0===n.getState().searchString.length,shouldFocus:0,shouldAnimate:!0}),this._run(n))},t}(d.EditorAction);t.MatchFindAction=N;var M=function(e){function t(){return e.call(this,{id:h.FIND_IDS.NextMatchFindAction,label:n.localize(1,null),alias:"Find Next",precondition:null,kbOpts:{kbExpr:b.EditorContextKeys.focus,primary:61,mac:{primary:2085,secondary:[61]}}})||this}return f(t,e),t.prototype._run=function(e){return e.moveToNextMatch()},t=v([d.editorAction],t)}(N);t.NextMatchFindAction=M;var T=function(e){function t(){return e.call(this,{id:h.FIND_IDS.PreviousMatchFindAction,label:n.localize(2,null),alias:"Find Previous",precondition:null,kbOpts:{kbExpr:b.EditorContextKeys.focus,primary:1085,mac:{primary:3109,secondary:[1085]}}})||this}return f(t,e),t.prototype._run=function(e){return e.moveToPrevMatch()},t=v([d.editorAction],t)}(N);t.PreviousMatchFindAction=T;var k=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.run=function(e,t){var n=L.get(t);if(n){var i=g.getSelectionSearchString(t);i&&n.setSearchString(i),this._run(n)||(n.start({forceRevealReplace:!1,seedSearchStringFromSelection:!1,shouldFocus:0,shouldAnimate:!0}),this._run(n))}},t}(d.EditorAction);t.SelectionMatchFindAction=k;var I=function(e){function t(){return e.call(this,{id:h.FIND_IDS.NextSelectionMatchFindAction,label:n.localize(3,null),alias:"Find Next Selection",precondition:null,kbOpts:{kbExpr:b.EditorContextKeys.focus,primary:2109}})||this}return f(t,e),t.prototype._run=function(e){return e.moveToNextMatch()},t=v([d.editorAction],t)}(k);t.NextSelectionMatchFindAction=I;var D=function(e){function t(){return e.call(this,{id:h.FIND_IDS.PreviousSelectionMatchFindAction,label:n.localize(4,null),alias:"Find Previous Selection",precondition:null,kbOpts:{kbExpr:b.EditorContextKeys.focus,primary:3133}})||this}return f(t,e),t.prototype._run=function(e){return e.moveToPrevMatch()},t=v([d.editorAction],t)}(k);t.PreviousSelectionMatchFindAction=D;var O=function(e){function t(){return e.call(this,{id:h.FIND_IDS.StartFindReplaceAction,label:n.localize(5,null),alias:"Replace",precondition:null,kbOpts:{kbExpr:null,primary:2086,mac:{primary:2596}}})||this}return f(t,e),t.prototype.run=function(e,t){if(!t.getConfiguration().readOnly){var n=L.get(t),i=t.getSelection(),o=!i.isEmpty()&&i.startLineNumber===i.endLineNumber,r=n.getState().searchString||o?2:1;n&&n.start({forceRevealReplace:!0,seedSearchStringFromSelection:o,shouldFocus:r,shouldAnimate:!0})}},t=v([d.editorAction],t)}(d.EditorAction);t.StartFindReplaceAction=O;var R=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._getNextMatch=function(e){var t=E(e,{changeFindSearchString:!0,allowMultiline:!0,highlightFindOptions:!0});if(!t)return null;if(t.currentMatch)return t.currentMatch;var n=e.getSelections(),i=n[n.length-1],o=e.getModel().findNextMatch(t.searchText,i.getEndPosition(),!1,t.matchCase,t.wholeWord?e.getConfiguration().wordSeparators:null,!1);return o?new u.Selection(o.range.startLineNumber,o.range.startColumn,o.range.endLineNumber,o.range.endColumn):null},t}(d.EditorAction);t.SelectNextFindMatchAction=R;var P=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._getPreviousMatch=function(e){var t=E(e,{changeFindSearchString:!0,allowMultiline:!0,highlightFindOptions:!0});if(!t)return null;if(t.currentMatch)return t.currentMatch;var n=e.getSelections(),i=n[n.length-1],o=e.getModel().findPreviousMatch(t.searchText,i.getStartPosition(),!1,t.matchCase,t.wholeWord?e.getConfiguration().wordSeparators:null,!1);return o?new u.Selection(o.range.startLineNumber,o.range.startColumn,o.range.endLineNumber,o.range.endColumn):null},t}(d.EditorAction);t.SelectPreviousFindMatchAction=P;var A=function(e){function t(){return e.call(this,{id:h.FIND_IDS.AddSelectionToNextFindMatchAction,label:n.localize(6,null),alias:"Add Selection To Next Find Match",precondition:null,kbOpts:{kbExpr:b.EditorContextKeys.focus,primary:2082}})||this}return f(t,e),t.prototype.run=function(e,t){var n=t.getSelections();if(n.length>1){var i=t.getModel(),o=L.get(t);if(!o)return;var r=o.getState().matchCase,s=!0,a=i.getValueInRange(n[0]);r||(a=a.toLowerCase());for(var l=1,c=n.length;l0)i=t.getModel().findMatches(o.searchString,!0,o.isRegex,o.matchCase,o.wholeWord?t.getConfiguration().wordSeparators:null,!1).map(function(e){return e.range});else{var r=E(t,{changeFindSearchString:!0,allowMultiline:!0,highlightFindOptions:!0});if(!r)return;i=t.getModel().findMatches(r.searchText,!0,!1,r.matchCase,r.wholeWord?t.getConfiguration().wordSeparators:null,!1).map(function(e){return e.range})}if(i.length>0){for(var s=t.getSelection(),a=0,l=i.length;a200)return null;var a=L.get(t);if(!a)return null;var u=a.getState().matchCase,l=t.getSelections(),c=n.getValueInRange(l[0]);u||(c=c.toLowerCase());for(var d=1;d=d)s.push(h),u++;else{var p=a.Range.compareRangesUsingStarts(h,r[l]);p<0?(s.push(h),u++):p>0?l++:(u++,l++)}}var f=s.map(function(e){return{range:e,options:i?n._SELECTION_HIGHLIGHT:n._SELECTION_HIGHLIGHT_OVERVIEW}});this.decorations=this.editor.deltaDecorations(this.decorations,f)}else this.decorations.length>0&&(this.decorations=this.editor.deltaDecorations(this.decorations,[]))},t.prototype.dispose=function(){this._setState(null),e.prototype.dispose.call(this)},t.ID="editor.contrib.selectionHighlighter",t._SELECTION_HIGHLIGHT_OVERVIEW=S.ModelDecorationOptions.register({stickiness:c.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,className:"selectionHighlight",overviewRuler:{color:"#A0A0A0",darkColor:"#A0A0A0",position:c.OverviewRulerLane.Center}}),t._SELECTION_HIGHLIGHT=S.ModelDecorationOptions.register({stickiness:c.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,className:"selectionHighlight"}),t=n=v([d.commonEditorContribution],t);var n}(r.Disposable);t.SelectionHighlighter=U;var j=function(e){function i(){return e.call(this,{id:h.FIND_IDS.ShowNextFindTermAction,label:n.localize(12,null),alias:"Show Next Find Term",precondition:t.CONTEXT_FIND_WIDGET_VISIBLE,kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:s.ContextKeyExpr.and(t.CONTEXT_FIND_INPUT_FOCUSSED,b.EditorContextKeys.focus),primary:h.ShowNextFindTermKeybinding.primary,mac:h.ShowNextFindTermKeybinding.mac,win:h.ShowNextFindTermKeybinding.win,linux:h.ShowNextFindTermKeybinding.linux}})||this}return f(i,e),i.prototype._run=function(e){return e.showNextFindTerm()},i=v([d.editorAction],i)}(N);t.ShowNextFindTermAction=j;var q=function(e){function i(){return e.call(this,{id:h.FIND_IDS.ShowPreviousFindTermAction,label:n.localize(13,null),alias:"Find Show Previous Find Term",precondition:t.CONTEXT_FIND_WIDGET_VISIBLE,kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:s.ContextKeyExpr.and(t.CONTEXT_FIND_INPUT_FOCUSSED,b.EditorContextKeys.focus),primary:h.ShowPreviousFindTermKeybinding.primary,mac:h.ShowPreviousFindTermKeybinding.mac,win:h.ShowPreviousFindTermKeybinding.win,linux:h.ShowPreviousFindTermKeybinding.linux}})||this}return f(i,e),i.prototype._run=function(e){return e.showPreviousFindTerm()},i=v([d.editorAction],i)}(N);t.ShpwPreviousFindTermAction=q;var G=d.EditorCommand.bindToContribution(L.get);d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.CloseFindWidgetCommand,precondition:t.CONTEXT_FIND_WIDGET_VISIBLE,handler:function(e){return e.closeFindWidget()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:9,secondary:[1033]}})),d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.ToggleCaseSensitiveCommand,precondition:null,handler:function(e){return e.toggleCaseSensitive()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:h.ToggleCaseSensitiveKeybinding.primary,mac:h.ToggleCaseSensitiveKeybinding.mac,win:h.ToggleCaseSensitiveKeybinding.win,linux:h.ToggleCaseSensitiveKeybinding.linux}})),d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.ToggleWholeWordCommand,precondition:null,handler:function(e){return e.toggleWholeWords()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:h.ToggleWholeWordKeybinding.primary,mac:h.ToggleWholeWordKeybinding.mac,win:h.ToggleWholeWordKeybinding.win,linux:h.ToggleWholeWordKeybinding.linux}})),d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.ToggleRegexCommand,precondition:null,handler:function(e){return e.toggleRegex()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:h.ToggleRegexKeybinding.primary,mac:h.ToggleRegexKeybinding.mac,win:h.ToggleRegexKeybinding.win,linux:h.ToggleRegexKeybinding.linux}})),d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.ToggleSearchScopeCommand,precondition:null,handler:function(e){return e.toggleSearchScope()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:h.ToggleSearchScopeKeybinding.primary,mac:h.ToggleSearchScopeKeybinding.mac,win:h.ToggleSearchScopeKeybinding.win,linux:h.ToggleSearchScopeKeybinding.linux}})),d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.ReplaceOneAction,precondition:t.CONTEXT_FIND_WIDGET_VISIBLE,handler:function(e){return e.replace()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:3094}})),d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.ReplaceAllAction,precondition:t.CONTEXT_FIND_WIDGET_VISIBLE,handler:function(e){return e.replaceAll()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:2563}})),d.CommonEditorRegistry.registerEditorCommand(new G({id:h.FIND_IDS.SelectAllMatchesAction,precondition:t.CONTEXT_FIND_WIDGET_VISIBLE,handler:function(e){return e.selectAllMatches()},kbOpts:{weight:d.CommonEditorRegistry.commandWeight(5),kbExpr:b.EditorContextKeys.focus,primary:515}}))}),define(d[458],h([1,0,349,29,4,18,40,3,2,13,25,30,333,257,256,21,328]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m){"use strict";function _(e){if(!i.isUndefined(e)){if(!i.isObject(e))return!1;var t=e;if(!i.isUndefined(t.levels)&&!i.isNumber(t.levels))return!1;if(!i.isUndefined(t.direction)&&!i.isString(t.direction))return!1}return!0}Object.defineProperty(t,"__esModule",{value:!0});var y=function(){function e(e){var t=this;this.editor=e,this._isEnabled=this.editor.getConfiguration().contribInfo.folding,this._showFoldingControls=this.editor.getConfiguration().contribInfo.showFoldingControls,this.globalToDispose=[],this.localToDispose=[],this.decorations=[],this.computeToken=0,this.globalToDispose.push(this.editor.onDidChangeModel(function(){return t.onModelChanged()})),this.globalToDispose.push(this.editor.onDidChangeConfiguration(function(e){var n=t._isEnabled;t._isEnabled=t.editor.getConfiguration().contribInfo.folding,n!==t._isEnabled&&t.onModelChanged();var i=t._showFoldingControls;t._showFoldingControls=t.editor.getConfiguration().contribInfo.showFoldingControls,i!==t._showFoldingControls&&t.updateHideFoldIconClass()})),this.onModelChanged()}return t=e,e.get=function(e){return e.getContribution(g.ID)},e.prototype.getId=function(){return g.ID},e.prototype.dispose=function(){this.cleanState(),this.globalToDispose=a.dispose(this.globalToDispose)},e.prototype.updateHideFoldIconClass=function(){var e=this.editor.getDomNode();e&&o.toggleClass(e,"alwaysShowFoldIcons","always"===this._showFoldingControls)},e.prototype.saveViewState=function(){var e=this.editor.getModel();if(!e)return{};var t=[];return this.decorations.forEach(function(n){if(n.isCollapsed){var i=n.getDecorationRange(e);i&&t.push({startLineNumber:i.startLineNumber,endLineNumber:i.endLineNumber,indent:n.indent,isCollapsed:!0})}}),{collapsedRegions:t,lineCount:e.getLineCount()}},e.prototype.restoreViewState=function(e){var t=this.editor.getModel();t&&this._isEnabled&&e&&Array.isArray(e.collapsedRegions)&&0!==e.collapsedRegions.length&&e.lineCount===t.getLineCount()&&(this.cleanState(),this.applyRegions(e.collapsedRegions),this.onModelChanged())},e.prototype.cleanState=function(){this.localToDispose=a.dispose(this.localToDispose)},e.prototype.applyRegions=function(e){var n=this,i=this.editor.getModel();if(i){var o=!1;e=p.limitByIndent(e,t.MAX_FOLDING_REGIONS).sort(function(e,t){return e.startLineNumber-t.startLineNumber}),this.editor.changeDecorations(function(t){for(var r=[],s=0,a=0;ae[s].startLineNumber;){d=e[s];o=o||d.isCollapsed,r.push(new h.CollapsibleRegion(d,i,t)),s++}if(s0&&(u.forEach(function(e,n){t.editor.changeDecorations(function(t){e.setCollapsed(!1,t),i=!0})}),!h.doesLineBelongsToCollapsibleRegion(u[0].foldingRange,s.startLineNumber))){var l=u[0].startLineNumber,c=n.getLineMaxColumn(u[0].startLineNumber);o[a]=s.setEndPosition(l,c).setStartPosition(l,c),r=!0}}),r&&this.editor.setSelections(o),i&&this.updateHiddenAreas(o[0].startLineNumber)},e.prototype.fold=function(e,t){var n=this,i=!1,o=this.editor.getSelections();o.forEach(function(o){var r=o.startLineNumber;h.getCollapsibleRegionsToFoldAtLine(n.decorations,n.editor.getModel(),r,e,t).forEach(function(e){return n.editor.changeDecorations(function(t){e.setCollapsed(!0,t),i=!0})})}),i&&this.updateHiddenAreas(o[0].startLineNumber)},e.prototype.foldUnfoldRecursively=function(e){var t=this,n=!1,i=this.editor.getModel(),o=this.editor.getSelections();o.forEach(function(o){for(var r,s=o.startLineNumber,a=[],u=0,l=t.decorations.length;u=s&&(d.endLineNumber<=r||void 0===r))){if(d.startLineNumber!==s&&void 0===r)return;r=r||d.endLineNumber,a.push(c)}}a.length>0&&a.forEach(function(i){t.editor.changeDecorations(function(t){i.setCollapsed(e,t),n=!0})})}),n&&this.updateHiddenAreas(o[0].startLineNumber)},e.prototype.foldAll=function(){this.changeAll(!0)},e.prototype.unfoldAll=function(){this.changeAll(!1)},e.prototype.changeAll=function(e){var t=this;if(this.decorations.length>0){var n=!0;this.editor.changeDecorations(function(i){t.decorations.forEach(function(t){e!==t.isCollapsed&&(t.setCollapsed(e,i),n=!0)})}),n&&this.updateHiddenAreas(this.editor.getPosition().lineNumber)}},e.prototype.foldLevel=function(e,t){var n=this,i=this.editor.getModel(),o=[i.getFullModelRange()],r=!1;this.editor.changeDecorations(function(s){n.decorations.forEach(function(n){var a=n.getDecorationRange(i);if(a){for(;!u.Range.containsRange(o[o.length-1],a);)o.pop();o.push(a),o.length!==e+1||n.isCollapsed||t.some(function(e){return a.startLineNumber1)){var n=this.editor.getModel(),o=this.editor.getPosition(),r=!1,s=this.editor.onDidChangeModelContent(function(e){if(e.isFlush)return r=!0,void s.dispose();for(var t=0,n=e.changes.length;t1)){var n=this.editor.getModel(),o=n.getOptions(),r=o.tabSize,s=o.insertSpaces,a=new b.EditorState(this.editor,5);c.getDocumentRangeFormattingEdits(n,e,{tabSize:r,insertSpaces:s}).then(function(e){return t.workerService.computeMoreMinimalEdits(n.uri,e,[])}).then(function(e){a.validate(t.editor)&&!i.isFalsyOrEmpty(e)&&(d.EditOperationsCommand.execute(t.editor,e),S(e))})}},e.prototype.getId=function(){return t.ID},e.prototype.dispose=function(){this.callOnDispose=r.dispose(this.callOnDispose),this.callOnModel=r.dispose(this.callOnModel)},e.ID="editor.contrib.formatOnPaste",e=t=v([u.commonEditorContribution,y(1,g.IEditorWorkerService)],e);var t}();var E=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.run=function(e,t){var n=e.get(g.IEditorWorkerService),o=this._getFormattingEdits(t);if(!o)return s.TPromise.as(void 0);var r=new b.EditorState(t,5);return o.then(function(e){return n.computeMoreMinimalEdits(t.getModel().uri,e,t.getSelections())}).then(function(e){r.validate(t)&&!i.isFalsyOrEmpty(e)&&(d.EditOperationsCommand.execute(t,e),S(e),t.focus())})},t}(u.EditorAction);t.AbstractFormatAction=E;var L=function(e){function t(){return e.call(this,{id:"editor.action.formatDocument",label:n.localize(4,null),alias:"Format Document",precondition:a.ContextKeyExpr.and(w.EditorContextKeys.writable,w.EditorContextKeys.hasDocumentFormattingProvider),kbOpts:{kbExpr:w.EditorContextKeys.textFocus,primary:1572,linux:{primary:3111}},menuOpts:{group:"1_modification",order:1.3}})||this}return f(t,e),t.prototype._getFormattingEdits=function(e){var t=e.getModel(),n=t.getOptions(),i=n.tabSize,o=n.insertSpaces;return c.getDocumentFormattingEdits(t,{tabSize:i,insertSpaces:o})},t=v([u.editorAction],t)}(E);t.FormatDocumentAction=L;var x=function(e){function t(){return e.call(this,{id:"editor.action.formatSelection",label:n.localize(5,null),alias:"Format Code",precondition:a.ContextKeyExpr.and(w.EditorContextKeys.writable,w.EditorContextKeys.hasDocumentSelectionFormattingProvider,w.EditorContextKeys.hasNonEmptySelection),kbOpts:{kbExpr:w.EditorContextKeys.textFocus,primary:o.KeyChord(2089,2084)},menuOpts:{group:"1_modification",order:1.31}})||this}return f(t,e),t.prototype._getFormattingEdits=function(e){var t=e.getModel(),n=t.getOptions(),i=n.tabSize,o=n.insertSpaces;return c.getDocumentRangeFormattingEdits(t,e.getSelection(),{tabSize:i,insertSpaces:o})},t=v([u.editorAction],t)}(E);t.FormatSelectionAction=x,h.CommandsRegistry.registerCommand("editor.action.format",function(e){var t=e.get(p.ICodeEditorService).getFocusedCodeEditor();if(t)return(new(function(e){function t(){return e.call(this,{})||this}return f(t,e),t.prototype._getFormattingEdits=function(e){var t=e.getModel(),n=e.getSelection(),i=t.getOptions(),o=i.tabSize,r=i.insertSpaces;return n.isEmpty()?c.getDocumentFormattingEdits(t,{tabSize:o,insertSpaces:r}):c.getDocumentRangeFormattingEdits(t,n,{tabSize:o,insertSpaces:r})},t}(E))).run(e,t)})}),define(d[177],h([1,0,10,7,13,17,18]),function(e,t,n,i,o,r,s){"use strict";function a(e){return i.TPromise.join(e).then(function(e){for(var t=[],n=0,i=e;n0;t&&n&&(r[o]=e)}},function(e){i.onUnexpectedExternalError(e)})});return o.TPromise.join(u).then(function(){return n.coalesce(r)})}Object.defineProperty(t,"__esModule",{value:!0}),t.getHover=u,r.CommonEditorRegistry.registerDefaultLanguageCommand("_executeHoverProvider",u)}),define(d[463],h([1,0,355,24,10,4,7,102,71,2,12,17,122,462,195,196,133,34,242,245,246,247,32,3]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_,y,C,b,w,S,E){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var L=r.$,x=function(){return function(e,t,n,i){this.range=e,this.color=t,this.format=n,this.availableFormats=i}}(),N=function(){function e(e){this._editor=e,this._range=null}return e.prototype.setRange=function(e){this._range=e,this._result=[]},e.prototype.clearResult=function(){this._result=[]},e.prototype.computeAsync=function(){var e=this._editor.getModel();return d.HoverProviderRegistry.has(e)?p.getHover(e,new c.Position(this._range.startLineNumber,this._range.startColumn)):s.TPromise.as(null)},e.prototype.computeSync=function(){var e=this,t=this._range.startLineNumber;if(t>this._editor.getModel().getLineCount())return[];var n=function(e){return e&&(!Array.isArray(e)||e.length>0)},i=this._editor.getModel().getLineMaxColumn(t);return this._editor.getLineDecorations(t).map(function(o){var r=o.range.startLineNumber===t?o.range.startColumn:1,s=o.range.endLineNumber===t?o.range.endColumn:i;if(r>e._range.startColumn||e._range.endColumn>s)return null;var a=new l.Range(e._range.startLineNumber,r,e._range.startLineNumber,s),u=o.options,c=u&&u.extraOptions;if(b.isColorDecorationOptions(c)){var d=c.color,h=c.format,p=c.availableFormats;return new x(a,d,h,p)}if(!n(o.options.hoverMessage))return null;var f=void 0;return o.options.hoverMessage&&(f=Array.isArray(o.options.hoverMessage)?o.options.hoverMessage.slice():[o.options.hoverMessage]),{contents:f,range:a}}).filter(function(e){return!!e})},e.prototype.onResult=function(e,t){this._result=t?e.concat(this._result.sort(function(e,t){return e instanceof x?-1:t instanceof x?1:0})):this._result.concat(e)},e.prototype.getResult=function(){return this._result.slice(0)},e.prototype.getResultWithLoadingMessage=function(){return this._result.slice(0).concat([this._getLoadingMessage()])},e.prototype._getLoadingMessage=function(){return{range:this._range,contents:[v.textToMarkedString(n.localize(0,null))]}},e}(),M=function(e){function t(n,i,o){var s=e.call(this,t.ID,n)||this;return s._computer=new N(s._editor),s._highlightDecorations=[],s._isChangingDecorations=!1,s._openerService=i||u.NullOpenerService,s._modeService=o,s._hoverOperation=new g.HoverOperation(s._computer,function(e){return s._withResult(e,!0)},null,function(e){return s._withResult(e,!1)}),s.toDispose=[],s.toDispose.push(r.addStandardDisposableListener(s.getDomNode(),r.EventType.FOCUS,function(){s._colorPicker&&r.addClass(s.getDomNode(),"colorpicker-hover")})),s.toDispose.push(r.addStandardDisposableListener(s.getDomNode(),r.EventType.BLUR,function(){r.removeClass(s.getDomNode(),"colorpicker-hover")})),s}return f(t,e),t.prototype.dispose=function(){this._hoverOperation.cancel(),this._colorPicker&&this._colorPicker.dispose(),this.toDispose=E.dispose(this.toDispose),e.prototype.dispose.call(this)},t.prototype.onModelDecorationsChanged=function(){this._isChangingDecorations||this.isVisible&&(this._hoverOperation.cancel(),this._computer.clearResult(),this._colorPicker||this._hoverOperation.start())},t.prototype.startShowingAt=function(e,t){if(!this._lastRange||!this._lastRange.equalsRange(e)){if(this._hoverOperation.cancel(),this.isVisible)if(this._showAtPosition.lineNumber!==e.startLineNumber)this.hide();else{for(var n=[],i=0,o=this._messages.length;i=e.endColumn&&n.push(r)}n.length>0?this._renderMessages(e,n):this.hide()}this._lastRange=e,this._computer.setRange(e),this._shouldFocus=t,this._hoverOperation.start()}},t.prototype.hide=function(){this._lastRange=null,this._hoverOperation.cancel(),e.prototype.hide.call(this),this._isChangingDecorations=!0,this._highlightDecorations=this._editor.deltaDecorations(this._highlightDecorations,[]),this._isChangingDecorations=!1,this._colorPicker=null,this._colorPicker&&this._colorPicker.dispose()},t.prototype.isColorPickerVisible=function(){return!!this._colorPicker},t.prototype._withResult=function(e,t){this._messages=e,this._lastRange&&this._messages.length>0?this._renderMessages(this._lastRange,this._messages):t&&this.hide()},t.prototype._renderMessages=function(e,n){var r=this,s=Number.MAX_VALUE,u=n[0].range,d=document.createDocumentFragment();n.forEach(function(e){if(e.range)if(s=Math.min(s,e.range.startColumn),u=l.Range.plusRange(u,e.range),e instanceof x){var t=void 0,n=void 0;"string"==typeof e.format?t=new w.ColorFormatter(e.format):(t=new w.ColorFormatter(e.format.opaque),n=new w.ColorFormatter(e.format.transparent));var c=[];e.availableFormats&&e.availableFormats.forEach(function(e){var t;t="string"==typeof e?new w.ColorFormatter(e):{opaqueFormatter:new w.ColorFormatter(e.opaque),transparentFormatter:new w.ColorFormatter(e.transparent)},c.push(t)});var p=e.color,f=p.red,g=p.green,m=p.blue,v=p.alpha,_=new S.RGBA(255*f,255*g,255*m,255*v),b=S.Color.fromRGBA(_),E=new y.ColorPickerModel(_.toString(),b,t,n,c,r._editor.getModel(),e.range),N=r._register(new C.ColorPickerWidget(E,r._editor));E.widget=N,r._colorPicker=N,d.appendChild(N.getDomNode())}else e.contents.filter(function(e){return!!e}).forEach(function(e){var t=a.renderMarkedString(e,{actionCallback:function(e){r._openerService.open(i.default.parse(e)).then(void 0,o.onUnexpectedError)},codeBlockRenderer:function(e,t){var n=e?r._modeService.getModeIdForLanguageName(e):r._editor.getModel().getLanguageIdentifier().language;return r._modeService.getOrCreateMode(n).then(function(e){return h.tokenizeToString(t,n)})}});d.appendChild(L("div.hover-row",null,t))})}),this.showAt(new c.Position(e.startLineNumber,s),this._shouldFocus),this.updateContents(d),this._colorPicker&&(this._colorPicker.layout(),this._colorPicker.layoutSaturationBox()),this._isChangingDecorations=!0,this._highlightDecorations=this._editor.deltaDecorations(this._highlightDecorations,[{range:u,options:t._DECORATION_OPTIONS}]),this._isChangingDecorations=!1},t.ID="editor.contrib.modesContentHoverWidget",t._DECORATION_OPTIONS=_.ModelDecorationOptions.register({className:"hoverHighlight"}),t}(m.ContentHoverWidget);t.ModesContentHoverWidget=M}),define(d[464],h([1,0,357,40,266,59,435,21,73,2,22,13,264,265,319,119,134]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var y=function(e){function t(t,n){var i=e.call(this,n)||this;return i.down=t,i}return f(t,e),t.prototype.run=function(e,t){for(var n=[],i=t.getSelections(),o=0;oe.endLineNumber+1?(o.push(e),t):new c.Selection(e.startLineNumber,e.startColumn,t.endLineNumber,t.endColumn):t.startLineNumber>e.endLineNumber?(o.push(e),t):new c.Selection(e.startLineNumber,e.startColumn,t.endLineNumber,t.endColumn)});o.push(s);for(var a=t.getModel(),u=[],d=[],h=i,p=0,f=0,g=o.length;f=1){var N=!0;""===S&&(N=!1),!N||" "!==S.charAt(S.length-1)&&"\t"!==S.charAt(S.length-1)||(N=!1,S=S.replace(/[\s\uFEFF\xA0]+$/g," "));var M=L.substr(x-1);S+=(N?" ":"")+M,C=N?M.length+1:M.length}else C=0}var T=new l.Range(v,1,_,y);if(!T.isEmpty()){var k=void 0;m.isEmpty()?(u.push(r.EditOperation.replace(T,S)),k=new c.Selection(T.startLineNumber-p,S.length-C+1,v-p,S.length-C+1)):m.startLineNumber===m.endLineNumber?(u.push(r.EditOperation.replace(T,S)),k=new c.Selection(m.startLineNumber-p,m.startColumn,m.endLineNumber-p,m.endColumn)):(u.push(r.EditOperation.replace(T,S)),k=new c.Selection(m.startLineNumber-p,m.startColumn,m.startLineNumber-p,S.length-b)),null!==l.Range.intersectRanges(T,i)?h=k:d.push(k)}p+=T.endLineNumber-T.startLineNumber}d.unshift(h),t.executeEdits(this.id,u,d)},t=v([d.editorAction],t)}(d.EditorAction);t.JoinLinesAction=T;var k=function(e){function t(){return e.call(this,{id:"editor.action.transpose",label:n.localize(15,null),alias:"Transpose characters around the cursor",precondition:a.EditorContextKeys.writable})||this}return f(t,e),t.prototype.run=function(e,t){for(var n=t.getSelections(),i=t.getModel(),o=[],r=0,s=n.length;r=h){if(d.lineNumber===i.getLineCount())continue;var p=new l.Range(d.lineNumber,Math.max(1,d.column-1),d.lineNumber+1,1),f=i.getValueInRange(p).split("").reverse().join("");o.push(new u.ReplaceCommand(new c.Selection(d.lineNumber,Math.max(1,d.column-1),d.lineNumber+1,1),f))}else{var p=new l.Range(d.lineNumber,Math.max(1,d.column-1),d.lineNumber,d.column+1),f=i.getValueInRange(p).split("").reverse().join("");o.push(new u.ReplaceCommandThatPreservesSelection(p,f,new c.Selection(d.lineNumber,d.column+1,d.lineNumber,d.column+1)))}}}t.pushUndoStop(),t.executeCommands(this.id,o),t.pushUndoStop()},t=v([d.editorAction],t)}(d.EditorAction);t.TransposeAction=k;var I=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype.run=function(e,t){for(var n=t.getSelections(),i=t.getModel(),o=[],r=0,s=n.length;r1&&i.push(new r.Selection(e.endLineNumber,e.endColumn,e.endLineNumber,e.endColumn)),i},t.prototype.run=function(e,t){var n=this,i=t.getSelections().map(function(e){return n.getCursorsForSelection(e,t)}).reduce(function(e,t){return e.concat(t)});i.length>0&&t.setSelections(i)},t=v([o.editorAction],t)}(o.EditorAction)}),define(d[178],h([1,0,10,13,17,18,19]),function(e,t,n,i,o,r,s){"use strict";function a(e,t){var i,s=o.SignatureHelpProviderRegistry.ordered(e);return r.sequence(s.map(function(o){return function(){if(!i)return r.asWinJsPromise(function(n){return o.provideSignatureHelp(e,t,n)}).then(function(e){i=e},n.onUnexpectedExternalError)}})).then(function(){return i})}Object.defineProperty(t,"__esModule",{value:!0}),t.Context={Visible:new s.RawContextKey("parameterHintsVisible",!1),MultipleSignatures:new s.RawContextKey("parameterHintsMultipleSignatures",!1)},t.provideSignatureHelp=a,i.CommonEditorRegistry.registerDefaultLanguageCommand("_executeSignatureHelpProvider",a)}),define(d[467],h([1,0,24,2,17,18,7,10,56,13]),function(e,t,n,i,o,r,s,a,u,l){"use strict";function c(e,t){var n=[],i=o.CodeActionProviderRegistry.all(e).map(function(i){return r.asWinJsPromise(function(n){return i.provideCodeActions(e,t,n)}).then(function(e){if(Array.isArray(e))for(var t=0,i=e;tthis.context.column&&this.completionModel.incomplete&&0!==e.leadingWord.word.length){var t=this.completionModel.resolveIncompleteInfo(),n=t.complete,i=t.incomplete;this.trigger(2===this._state,!0,i,n)}else{var o=this.completionModel.lineContext,r=!1;if(this.completionModel.lineContext={leadingLineContent:e.leadingLineContent,characterCountDelta:e.column-this.context.column},0===this.completionModel.items.length){if(p.shouldAutoTrigger(this.editor)&&this.context.leadingWord.endColumn0)&&0===e.leadingWord.word.length)return void this.cancel()}this._onDidSuggest.fire({completionModel:this.completionModel,auto:this.context.auto,isFrozen:r})}}else this.cancel()},e}();t.SuggestModel=f}),define(d[181],h([1,0,373,13,168]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){function t(){return e.call(this,{id:r.ID,label:n.localize(0,null),alias:"Toggle Tab Key Moves Focus",precondition:null,kbOpts:{kbExpr:null,primary:2091,mac:{primary:1323}}})||this}return f(t,e),r=t,t.prototype.run=function(e,t){var n=o.TabFocus.getTabFocusMode();o.TabFocus.setTabFocusMode(!n)},t.ID="editor.action.toggleTabFocusMode",t=r=v([i.editorAction],t);var r}(i.EditorAction);t.ToggleTabFocusModeAction=r}),define(d[476],h([1,0,21,22,13,12,2,172,73,94,39,54]),function(e,t,n,i,o,r,s,a,u,l,c,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var h=function(e){function t(t){var n=e.call(this,t)||this;return n._inSelectionMode=t.inSelectionMode,n._wordNavigationType=t.wordNavigationType,n}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){var i=this,o=t.getConfiguration(),s=l.getMapForWordSeparators(o.wordSeparators),a=t.getModel(),u=t.getSelections().map(function(e){var t=new r.Position(e.positionLineNumber,e.positionColumn),n=i._move(s,a,t,i._wordNavigationType);return i._moveTo(e,n,i._inSelectionMode)});if(t._getCursors().setStates("moveWordCommand",d.CursorChangeReason.NotSet,u.map(function(e){return c.CursorState.fromModelSelection(e)})),1===u.length){var h=new r.Position(u[0].positionLineNumber,u[0].positionColumn);t.revealPosition(h,!1,!0)}},t.prototype._moveTo=function(e,t,n){return n?new i.Selection(e.selectionStartLineNumber,e.selectionStartColumn,t.lineNumber,t.column):new i.Selection(t.lineNumber,t.column,t.lineNumber,t.column)},t}(o.EditorCommand);t.MoveWordCommand=h;var p=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._move=function(e,t,n,i){return a.WordOperations.moveWordLeft(e,t,n,i)},t}(h);t.WordLeftCommand=p;var g=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._move=function(e,t,n,i){return a.WordOperations.moveWordRight(e,t,n,i)},t}(h);t.WordRightCommand=g;var m=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:0,id:"cursorWordStartLeft",precondition:null,kbOpts:{kbExpr:n.EditorContextKeys.textFocus,primary:2063,mac:{primary:527}}})||this}return f(t,e),t=v([o.editorCommand],t)}(p);t.CursorWordStartLeft=m;var _=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:1,id:"cursorWordEndLeft",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(p);t.CursorWordEndLeft=_;var y=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:0,id:"cursorWordLeft",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(p);t.CursorWordLeft=y;var C=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:0,id:"cursorWordStartLeftSelect",precondition:null,kbOpts:{kbExpr:n.EditorContextKeys.textFocus,primary:3087,mac:{primary:1551}}})||this}return f(t,e),t=v([o.editorCommand],t)}(p);t.CursorWordStartLeftSelect=C;var b=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:1,id:"cursorWordEndLeftSelect",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(p);t.CursorWordEndLeftSelect=b;var w=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:0,id:"cursorWordLeftSelect",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(p);t.CursorWordLeftSelect=w;var S=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:0,id:"cursorWordStartRight",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(g);t.CursorWordStartRight=S;var E=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:1,id:"cursorWordEndRight",precondition:null,kbOpts:{kbExpr:n.EditorContextKeys.textFocus,primary:2065,mac:{primary:529}}})||this}return f(t,e),t=v([o.editorCommand],t)}(g);t.CursorWordEndRight=E;var L=function(e){function t(){return e.call(this,{inSelectionMode:!1,wordNavigationType:1,id:"cursorWordRight",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(g);t.CursorWordRight=L;var x=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:0,id:"cursorWordStartRightSelect",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(g);t.CursorWordStartRightSelect=x;var N=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:1,id:"cursorWordEndRightSelect",precondition:null,kbOpts:{kbExpr:n.EditorContextKeys.textFocus,primary:3089,mac:{primary:1553}}})||this}return f(t,e),t=v([o.editorCommand],t)}(g);t.CursorWordEndRightSelect=N;var M=function(e){function t(){return e.call(this,{inSelectionMode:!0,wordNavigationType:1,id:"cursorWordRightSelect",precondition:null})||this}return f(t,e),t=v([o.editorCommand],t)}(g);t.CursorWordRightSelect=M;var T=function(e){function t(t){var n=e.call(this,t)||this;return n._whitespaceHeuristics=t.whitespaceHeuristics,n._wordNavigationType=t.wordNavigationType,n}return f(t,e),t.prototype.runEditorCommand=function(e,t,n){var i=this,o=t.getConfiguration(),r=l.getMapForWordSeparators(o.wordSeparators),s=t.getModel(),a=t.getSelections().map(function(e){var t=i._delete(r,s,e,i._whitespaceHeuristics,i._wordNavigationType);return new u.ReplaceCommand(t,"")});t.pushUndoStop(),t.executeCommands(this.id,a),t.pushUndoStop()},t}(o.EditorCommand);t.DeleteWordCommand=T;var k=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._delete=function(e,t,n,i,o){var r=a.WordOperations.deleteWordLeft(e,t,n,i,o);return r||new s.Range(1,1,1,1)},t}(T);t.DeleteWordLeftCommand=k;var I=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._delete=function(e,t,n,i,o){var r=a.WordOperations.deleteWordRight(e,t,n,i,o);if(r)return r;var u=t.getLineCount(),l=t.getLineMaxColumn(u);return new s.Range(u,l,u,l)},t}(T);t.DeleteWordRightCommand=I;var D=function(e){function t(){return e.call(this,{whitespaceHeuristics:!1,wordNavigationType:0,id:"deleteWordStartLeft",precondition:n.EditorContextKeys.writable})||this}return f(t,e),t=v([o.editorCommand],t)}(k);t.DeleteWordStartLeft=D;var O=function(e){function t(){return e.call(this,{whitespaceHeuristics:!1,wordNavigationType:1,id:"deleteWordEndLeft",precondition:n.EditorContextKeys.writable})||this}return f(t,e),t=v([o.editorCommand],t)}(k);t.DeleteWordEndLeft=O;var R=function(e){function t(){return e.call(this,{whitespaceHeuristics:!0,wordNavigationType:0,id:"deleteWordLeft",precondition:n.EditorContextKeys.writable,kbOpts:{kbExpr:n.EditorContextKeys.textFocus,primary:2049,mac:{primary:513}}})||this}return f(t,e),t=v([o.editorCommand],t)}(k);t.DeleteWordLeft=R;var P=function(e){function t(){return e.call(this,{whitespaceHeuristics:!1,wordNavigationType:0,id:"deleteWordStartRight",precondition:n.EditorContextKeys.writable})||this}return f(t,e),t=v([o.editorCommand],t)}(I);t.DeleteWordStartRight=P;var A=function(e){function t(){return e.call(this,{whitespaceHeuristics:!1,wordNavigationType:1,id:"deleteWordEndRight",precondition:n.EditorContextKeys.writable})||this}return f(t,e),t=v([o.editorCommand],t)}(I);t.DeleteWordEndRight=A;var F=function(e){function t(){return e.call(this,{whitespaceHeuristics:!0,wordNavigationType:1,id:"deleteWordRight",precondition:n.EditorContextKeys.writable,kbOpts:{kbExpr:n.EditorContextKeys.textFocus,primary:2068,mac:{primary:532}}})||this}return f(t,e),t=v([o.editorCommand],t)}(I);t.DeleteWordRight=F}),define(d[477],h([1,0,381,13,77]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});!function(e){function t(){var t=e.call(this,{id:"editor.action.toggleHighContrast",label:n.localize(0,null),alias:"Toggle High Contrast Theme",precondition:null})||this;return t._originalThemeName=null,t}f(t,e),t.prototype.run=function(e,t){var n=e.get(o.IStandaloneThemeService);this._originalThemeName?(n.setTheme(this._originalThemeName),this._originalThemeName=null):(this._originalThemeName=n.getTheme().themeName,n.setTheme("hc-black"))},t=v([i.editorAction],t)}(i.EditorAction)}),define(d[478],h([1,0,160,58,50]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(){function e(e,t,i){this.contextView=new n.ContextView(e)}return e.prototype.dispose=function(){this.contextView.dispose()},e.prototype.setContainer=function(e){this.contextView.setContainer(e)},e.prototype.showContextView=function(e){this.contextView.show(e)},e.prototype.layout=function(){this.contextView.layout()},e.prototype.hideContextView=function(e){this.contextView.hide(e)},e=v([y(1,i.ITelemetryService),y(2,o.IMessageService)],e)}();t.ContextViewService=r}),define(d[479],h([1,0,7,166,45,62,46,420]),function(e,t,n,i,o,r,s,a){"use strict";function u(e){if(!e)return e;for(var t="",n=0;n="0"&&i<="9"?t+="0":t+=i>="a"&&i<="z"?"a":i>="A"&&i<="Z"?"A":i}return t}function l(e){if(!e)return[];var t=[];return c(t,"",e),t}function c(e,t,n){n&&"object"==typeof n&&!Array.isArray(n)?Object.keys(n).forEach(function(i){return c(e,t?t+"."+i:i,n[i])}):e.push(t)}function d(e,t){return e?t.reduce(function(t,n){var i=n.split(".").reduce(function(e,t){return e&&"object"==typeof e?e[t]:void 0},e);return void 0!==i&&t.push((o={},o[n]=i,o)),t;var o},[]):[]}Object.defineProperty(t,"__esModule",{value:!0}),t.NullTelemetryService={_serviceBrand:void 0,publicLog:function(e,t){return n.TPromise.as(null)},isOptedIn:!0,getTelemetryInfo:function(){return n.TPromise.as({instanceId:"someValue.instanceId",sessionId:"someValue.sessionId",machineId:"someValue.machineId"})}},t.combinedAppender=function(){for(var e=[],t=0;t0&&(o.insertRule(this._unThemedSelector+" {"+e+"}",0),r=!0),t.length>0&&(o.insertRule(".vs"+this._unThemedSelector+" {"+t+"}",0),r=!0),n.length>0&&(o.insertRule(".vs-dark"+this._unThemedSelector+", .hc-black"+this._unThemedSelector+" {"+n+"}",0),r=!0),this._hasContent=r},e.prototype._removeCSS=function(){o.removeCSSRulesContainingSelector(this._unThemedSelector,this._providerArgs.styleSheet)},e.prototype.getCSSTextForModelDecorationClassName=function(e){if(!e)return"";var t=[];return this.collectCSSText(e,["backgroundColor"],t),this.collectCSSText(e,["outline","outlineColor","outlineStyle","outlineWidth"],t),this.collectBorderSettingsCSSText(e,t),t.join("")},e.prototype.getCSSTextForModelDecorationInlineClassName=function(e){if(!e)return"";var t=[];return this.collectCSSText(e,["textDecoration","cursor","color","letterSpacing"],t),t.join("")},e.prototype.getCSSTextForModelDecorationContentClassName=function(e){if(!e)return"";var t=[];if(void 0!==e){if(this.collectBorderSettingsCSSText(e,t),"string"==typeof e.contentIconPath?t.push(n.format(p.contentIconPath,i.default.file(e.contentIconPath).toString().replace(/'/g,"%27"))):e.contentIconPath instanceof i.default&&t.push(n.format(p.contentIconPath,e.contentIconPath.toString(!0).replace(/'/g,"%27"))),"string"==typeof e.contentText){var o=e.contentText.match(/^.*$/m)[0].replace(/['\\]/g,"\\$&");t.push(n.format(p.contentText,o))}this.collectCSSText(e,["textDecoration","color","backgroundColor","margin"],t),this.collectCSSText(e,["width","height"],t)&&t.push("display:inline-block;")}return t.join("")},e.prototype.getCSSTextForModelDecorationGlyphMarginClassName=function(e){if(!e)return"";var t=[];return void 0!==e.gutterIconPath&&("string"==typeof e.gutterIconPath?t.push(n.format(p.gutterIconPath,i.default.file(e.gutterIconPath).toString())):t.push(n.format(p.gutterIconPath,e.gutterIconPath.toString(!0).replace(/'/g,"%27"))),void 0!==e.gutterIconSize&&t.push(n.format(p.gutterIconSize,e.gutterIconSize))),t.join("")},e.prototype.collectBorderSettingsCSSText=function(e,t){return!!this.collectCSSText(e,["border","borderColor","borderRadius","borderSpacing","borderStyle","borderWidth"],t)&&(t.push(n.format("box-sizing: border-box;")),!0)},e.prototype.collectCSSText=function(e,t,i){for(var o=i.length,r=0,s=t;rt)){var v=m.startLineNumber===t?m.startColumn:r.minColumn,_=m.endLineNumber===t?m.endColumn:r.maxColumn;v<_&&l.push(new s.LineDecoration(v,_,"inline-selected-text",!1))}}var C=new a.RenderLineInput(u.useMonospaceOptimizations,r.content,r.mightContainRTL,r.minColumn-1,r.tokens,l,r.tabSize,u.spaceWidth,u.stopRenderingLineAfter,u.renderWhitespace,u.renderControlCharacters,u.fontLigatures);if(this._renderedViewLine&&this._renderedViewLine.input.equals(C))return null;var b=a.renderViewLine(C),S=null;if(p&&u.useMonospaceOptimizations&&!b.containsForeignElements){var E=!0;r.mightContainNonBasicASCII&&(E=o.isBasicASCII(r.content)),E&&r.content.length<1e3&&(S=new y(this._renderedViewLine?this._renderedViewLine.domNode:null,C,b.characterMapping))}return S||(S=w(this._renderedViewLine?this._renderedViewLine.domNode:null,C,b.characterMapping,b.containsRTL,b.containsForeignElements)),this._renderedViewLine=S,'
    '+b.html+"
    "},e.prototype.layoutLine=function(e,t){this._renderedViewLine&&this._renderedViewLine.domNode&&(this._renderedViewLine.domNode.setTop(t),this._renderedViewLine.domNode.setHeight(this._options.lineHeight))},e.prototype.getWidth=function(){return this._renderedViewLine?this._renderedViewLine.getWidth():0},e.prototype.getVisibleRangesForRange=function(e,t,n){return e=Math.min(this._renderedViewLine.input.lineContent.length+1,Math.max(1,e)),t=Math.min(this._renderedViewLine.input.lineContent.length+1,Math.max(1,t)),this._renderedViewLine.getVisibleRangesForRange(e,t,n)},e.prototype.getColumnOfNodeOffset=function(e,t,n){return this._renderedViewLine.getColumnOfNodeOffset(e,t,n)},e.CLASS_NAME="view-line",e}();t.ViewLine=_;var y=function(){function e(e,t,n){this.domNode=e,this.input=t,this._characterMapping=n,this._charWidth=t.spaceWidth,this._charOffset=null}return e._createCharOffset=function(e){for(var t=e.getPartLengths(),n=e.length,i=new Uint32Array(n),o=0,r=0,s=0;si&&t>i)return null;-1!==i&&e>i&&(e=i),-1!==i&&t>i&&(t=i);var o=this._getCharPosition(e),r=this._getCharPosition(t);return[new l.HorizontalRange(o,r-o)]},e.prototype._getCharPosition=function(e){var t=this._getOrCreateCharOffset();return 0===t.length?0:Math.round(this._charWidth*t[e-1])},e.prototype.getColumnOfNodeOffset=function(e,t,n){for(var i=t.textContent.length,o=-1;t;)t=t.previousSibling,o++;return this._characterMapping.partDataToCharOffset(o,i,n)+1},e}(),C=function(){function e(e,t,n,i,o){if(this.domNode=e,this.input=t,this._characterMapping=n,this._isWhitespaceOnly=/^\s*$/.test(t.lineContent),this._containsForeignElements=o,this._cachedWidth=-1,this._pixelOffsetCache=null,!i||0===this._characterMapping.length){this._pixelOffsetCache=new Int32Array(this._characterMapping.length+1);for(var r=0,s=this._characterMapping.length;r<=s;r++)this._pixelOffsetCache[r]=-1}}return e.prototype._getReadingTarget=function(){return this.domNode.domNode.firstChild},e.prototype.getWidth=function(){return-1===this._cachedWidth&&(this._cachedWidth=this._getReadingTarget().offsetWidth),this._cachedWidth},e.prototype.getVisibleRangesForRange=function(e,t,n){e|=0,t|=0;var i=0|this.input.stopRenderingLineAfter;if(-1!==i&&e>i&&t>i)return null;if(-1!==i&&e>i&&(e=i),-1!==i&&t>i&&(t=i),null!==this._pixelOffsetCache){var o=this._readPixelOffset(e,n);if(-1===o)return null;var r=this._readPixelOffset(t,n);return-1===r?null:[new l.HorizontalRange(o,r-o)]}return this._readVisibleRangesForRange(e,t,n)},e.prototype._readVisibleRangesForRange=function(e,t,n){if(e===t){var i=this._readPixelOffset(e,n);return-1===i?null:[new l.HorizontalRange(i,0)]}return this._readRawVisibleRangesForRange(e,t,n)},e.prototype._readPixelOffset=function(e,t){if(0===this._characterMapping.length&&!this._containsForeignElements)return 0;if(null!==this._pixelOffsetCache){var n=this._pixelOffsetCache[e];if(-1!==n)return n;var i=this._actualReadPixelOffset(e,t);return this._pixelOffsetCache[e]=i,i}return this._actualReadPixelOffset(e,t)},e.prototype._actualReadPixelOffset=function(e,t){if(0===this._characterMapping.length){var n=u.RangeUtil.readHorizontalRanges(this._getReadingTarget(),0,0,0,0,t.clientRectDeltaLeft,t.endNode);return n&&0!==n.length?n[0].left:-1}if(e===this._characterMapping.length&&this._isWhitespaceOnly&&!this._containsForeignElements)return this.getWidth();var i=this._characterMapping.charOffsetToPartData(e-1),o=a.CharacterMapping.getPartIndex(i),r=a.CharacterMapping.getCharIndex(i),s=u.RangeUtil.readHorizontalRanges(this._getReadingTarget(),o,r,o,r,t.clientRectDeltaLeft,t.endNode);return s&&0!==s.length?s[0].left:-1},e.prototype._readRawVisibleRangesForRange=function(e,t,n){if(1===e&&t===this._characterMapping.length)return[new l.HorizontalRange(0,this.getWidth())];var i=this._characterMapping.charOffsetToPartData(e-1),o=a.CharacterMapping.getPartIndex(i),r=a.CharacterMapping.getCharIndex(i),s=this._characterMapping.charOffsetToPartData(t-1),c=a.CharacterMapping.getPartIndex(s),d=a.CharacterMapping.getCharIndex(s);return u.RangeUtil.readHorizontalRanges(this._getReadingTarget(),o,r,c,d,n.clientRectDeltaLeft,n.endNode)},e.prototype.getColumnOfNodeOffset=function(e,t,n){for(var i=t.textContent.length,o=-1;t;)t=t.previousSibling,o++;return this._characterMapping.partDataToCharOffset(o,i,n)+1},e}(),b=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._readVisibleRangesForRange=function(t,n,i){var o=e.prototype._readVisibleRangesForRange.call(this,t,n,i);if(!o||0===o.length||t===n||1===t&&n===this._characterMapping.length)return o;var r=this._readPixelOffset(n-1,i),s=this._readPixelOffset(n,i);if(-1!==r&&-1!==s){var a=r<=s,u=o[o.length-1];a&&u.left=4&&3===e[0]&&7===e[3]},e.isChildOfScrollableElement=function(e){return e.length>=2&&3===e[0]&&5===e[1]},e.isChildOfMinimap=function(e){return e.length>=2&&3===e[0]&&8===e[1]},e.isChildOfContentWidgets=function(e){return e.length>=4&&3===e[0]&&1===e[3]},e.isChildOfOverflowingContentWidgets=function(e){return e.length>=1&&2===e[0]},e.isChildOfOverlayWidgets=function(e){return e.length>=2&&3===e[0]&&4===e[1]},e}(),d=function(){function e(e,t,n){this.model=e.model,this.layoutInfo=e.configuration.editor.layoutInfo,this.viewDomNode=t.viewDomNode,this.lineHeight=e.configuration.editor.lineHeight,this.typicalHalfwidthCharacterWidth=e.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth,this.lastViewCursorsRenderData=n,this._context=e,this._viewHelper=t}return e.prototype.getZoneAtCoord=function(e){var t=this._context.viewLayout.getWhitespaceAtVerticalOffset(e);if(t){var i=t.verticalOffset+t.height/2,o=this._context.model.getLineCount(),r=null,s=void 0,a=null;return t.afterLineNumber!==o&&(a=new n.Position(t.afterLineNumber+1,1)),t.afterLineNumber>0&&(r=new n.Position(t.afterLineNumber,this._context.model.getLineMaxColumn(t.afterLineNumber))),s=null===a?r:null===r?a:er.contentLeft+r.width)){var l=e.getVerticalOffsetForLineNumber(r.position.lineNumber);if(l<=u&&u<=l+r.height)return t.fulfill(o.MouseTargetType.CONTENT_TEXT,r.position)}return null},e._hitTestViewZone=function(e,t){var n=e.getZoneAtCoord(t.mouseVerticalOffset);if(n){var i=t.isInContentArea?o.MouseTargetType.CONTENT_VIEW_ZONE:o.MouseTargetType.GUTTER_VIEW_ZONE;return t.fulfill(i,n.position,null,n)}return null},e._hitTestTextArea=function(e,t){return c.isTextArea(t.targetPath)?t.fulfill(o.MouseTargetType.TEXTAREA):null},e._hitTestMargin=function(e,t){if(t.isInMarginArea){var n=e.getFullLineRangeAtCoord(t.mouseVerticalOffset),i=n.range.getStartPosition(),r=Math.abs(t.pos.x-t.editorPos.x);return r<=e.layoutInfo.glyphMarginWidth?t.fulfill(o.MouseTargetType.GUTTER_GLYPH_MARGIN,i,n.range,n.isAfterLines):(r-=e.layoutInfo.glyphMarginWidth)<=e.layoutInfo.lineNumbersWidth?t.fulfill(o.MouseTargetType.GUTTER_LINE_NUMBERS,i,n.range,n.isAfterLines):(r-=e.layoutInfo.lineNumbersWidth,t.fulfill(o.MouseTargetType.GUTTER_LINE_DECORATIONS,i,n.range,n.isAfterLines))}return null},e._hitTestViewLines=function(t,i,r){if(!c.isChildOfViewLines(i.targetPath))return null;if(t.isAfterLines(i.mouseVerticalOffset)){var s=t.model.getLineCount(),a=t.model.getLineMaxColumn(s);return i.fulfill(o.MouseTargetType.CONTENT_EMPTY,new n.Position(s,a))}if(r)return i.fulfill(o.MouseTargetType.UNKNOWN);var u=e._doHitTest(t,i);return u.position?e.createMouseTargetFromHitTestPosition(t,i,u.position.lineNumber,u.position.column):this._createMouseTarget(t,i.withTarget(u.hitTarget),!0)},e._hitTestMinimap=function(e,t){if(c.isChildOfMinimap(t.targetPath)){var i=e.getLineNumberAtVerticalOffset(t.mouseVerticalOffset),r=e.model.getLineMaxColumn(i);return t.fulfill(o.MouseTargetType.SCROLLBAR,new n.Position(i,r))}return null},e._hitTestScrollbarSlider=function(e,t){if(c.isChildOfScrollableElement(t.targetPath)&&t.target&&1===t.target.nodeType){var i=t.target.className;if(i&&/\b(slider|scrollbar)\b/.test(i)){var r=e.getLineNumberAtVerticalOffset(t.mouseVerticalOffset),s=e.model.getLineMaxColumn(r);return t.fulfill(o.MouseTargetType.SCROLLBAR,new n.Position(r,s))}}return null},e._hitTestScrollbar=function(e,t){if(c.isChildOfScrollableElement(t.targetPath)){var i=e.getLineNumberAtVerticalOffset(t.mouseVerticalOffset),r=e.model.getLineMaxColumn(i);return t.fulfill(o.MouseTargetType.SCROLLBAR,new n.Position(i,r))}return null},e.prototype.getMouseColumn=function(t,n){var i=this._context.configuration.editor.layoutInfo,o=this._context.viewLayout.getScrollLeft()+n.x-t.x-i.contentLeft;return e._getMouseColumn(o,this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth)},e._getMouseColumn=function(e,t){return e<0?1:Math.round(e/t)+1},e.createMouseTargetFromHitTestPosition=function(e,t,r,a){var u=new n.Position(r,a),l=e.getLineWidth(r);if(t.mouseContentHorizontalOffset>l)return s.isEdge&&1===u.column?t.fulfill(o.MouseTargetType.CONTENT_EMPTY,new n.Position(r,e.model.getLineMaxColumn(r))):t.fulfill(o.MouseTargetType.CONTENT_EMPTY,u);var c=e.visibleRangeForPosition2(r,a);if(!c)return t.fulfill(o.MouseTargetType.UNKNOWN,u);var d=c.left;if(t.mouseContentHorizontalOffset===d)return t.fulfill(o.MouseTargetType.CONTENT_TEXT,u);var h;if(a>1){var p=c.left;if(h=!1,h=h||p=t.editorPos.y+e.layoutInfo.height&&(o=t.editorPos.y+e.layoutInfo.height-1);var s=new r.PageCoordinates(t.pos.x,o),a=this._actualDoHitTestWithCaretRangeFromPoint(e,s.toClientCoordinates());return a.position?a:this._actualDoHitTestWithCaretRangeFromPoint(e,t.pos.toClientCoordinates())},e._actualDoHitTestWithCaretRangeFromPoint=function(e,t){var n=document.caretRangeFromPoint(t.clientX,t.clientY);if(!n||!n.startContainer)return{position:null,hitTarget:null};var i,o=n.startContainer;if(o.nodeType===o.TEXT_NODE){var r=(a=(s=o.parentNode)?s.parentNode:null)?a.parentNode:null;if((r&&r.nodeType===r.ELEMENT_NODE?r.className:null)===u.ViewLine.CLASS_NAME)return{position:l=e.getPositionFromDOMInfo(s,n.startOffset),hitTarget:null};i=o.parentNode}else if(o.nodeType===o.ELEMENT_NODE){var s=o.parentNode,a=s?s.parentNode:null;if((a&&a.nodeType===a.ELEMENT_NODE?a.className:null)===u.ViewLine.CLASS_NAME){var l=e.getPositionFromDOMInfo(o,o.textContent.length);return{position:l,hitTarget:null}}i=o}return{position:null,hitTarget:i}},e._doHitTestWithCaretPositionFromPoint=function(e,t){var n=document.caretPositionFromPoint(t.clientX,t.clientY);if(n.offsetNode.nodeType===n.offsetNode.TEXT_NODE){var i=n.offsetNode.parentNode,o=i?i.parentNode:null,r=o?o.parentNode:null;return(r&&r.nodeType===r.ELEMENT_NODE?r.className:null)===u.ViewLine.CLASS_NAME?{position:e.getPositionFromDOMInfo(n.offsetNode.parentNode,n.offset),hitTarget:null}:{position:null,hitTarget:n.offsetNode.parentNode}}return{position:null,hitTarget:n.offsetNode}},e._doHitTestWithMoveToPoint=function(e,t){var n=null,i=null,o=document.body.createTextRange();try{o.moveToPoint(t.clientX,t.clientY)}catch(e){return{position:null,hitTarget:null}}o.collapse(!0);var r=o?o.parentElement():null,s=r?r.parentNode:null,a=s?s.parentNode:null;if((a&&a.nodeType===a.ELEMENT_NODE?a.className:"")===u.ViewLine.CLASS_NAME){var l=o.duplicate();l.moveToElementText(r),l.setEndPoint("EndToStart",o),n=e.getPositionFromDOMInfo(r,l.text.length),l.moveToElementText(e.viewDomNode)}else i=r;return o.moveToElementText(e.viewDomNode),{position:n,hitTarget:i}},e._doHitTest=function(e,t){return document.caretRangeFromPoint?this._doHitTestWithCaretRangeFromPoint(e,t):document.caretPositionFromPoint?this._doHitTestWithCaretPositionFromPoint(e,t.pos.toClientCoordinates()):document.body.createTextRange?this._doHitTestWithMoveToPoint(e,t.pos.toClientCoordinates()):{position:null,hitTarget:null}},e}();t.MouseTargetFactory=p}),define(d[489],h([1,0,3,15,28,4,12,22,82,186,25,18,114,47,135]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g){"use strict";function m(e){return function(t,n){var i=!1;return e&&(i=e.mouseTargetIsWidget(n)),i||n.preventDefault(),n}}Object.defineProperty(t,"__esModule",{value:!0});var v=function(e){function t(n,i,o){var s=e.call(this)||this;s._isFocused=!1,s._context=n,s.viewController=i,s.viewHelper=o,s.mouseTargetFactory=new l.MouseTargetFactory(s._context,o),s._mouseDownOperation=s._register(new _(s._context,s.viewController,s.viewHelper,function(e,t){return s._createMouseTarget(e,t)},function(e){return s._getMouseColumn(e)})),s._asyncFocus=s._register(new d.RunOnceScheduler(function(){return s.viewHelper.focusTextArea()},0)),s.lastMouseLeaveTime=-1;var a=new h.EditorMouseEventFactory(s.viewHelper.viewDomNode);s._register(a.onContextMenu(s.viewHelper.viewDomNode,function(e){return s._onContextMenu(e,!0)})),s._register(a.onMouseMoveThrottled(s.viewHelper.viewDomNode,function(e){return s._onMouseMove(e)},m(s.mouseTargetFactory),t.MOUSE_MOVE_MINIMUM_TIME)),s._register(a.onMouseUp(s.viewHelper.viewDomNode,function(e){return s._onMouseUp(e)})),s._register(a.onMouseLeave(s.viewHelper.viewDomNode,function(e){return s._onMouseLeave(e)})),s._register(a.onMouseDown(s.viewHelper.viewDomNode,function(e){return s._onMouseDown(e)}));var u=function(e){if(s._context.configuration.editor.viewInfo.mouseWheelZoom){var t=new p.StandardMouseWheelEvent(e);if(t.browserEvent.ctrlKey||t.browserEvent.metaKey){var n=g.EditorZoom.getZoomLevel(),i=t.deltaY>0?1:-1;g.EditorZoom.setZoomLevel(n+i),t.preventDefault(),t.stopPropagation()}}};return s._register(r.addDisposableListener(s.viewHelper.viewDomNode,"mousewheel",u,!0)),s._register(r.addDisposableListener(s.viewHelper.viewDomNode,"DOMMouseScroll",u,!0)),s._context.addEventHandler(s),s}return f(t,e),t.prototype.dispose=function(){this._context.removeEventHandler(this),e.prototype.dispose.call(this)},t.prototype.onCursorStateChanged=function(e){return this._mouseDownOperation.onCursorStateChanged(e),!1},t.prototype.onFocusChanged=function(e){return this._isFocused=e.isFocused,!1},t.prototype.onScrollChanged=function(e){return this._mouseDownOperation.onScrollChanged(),!1},t.prototype.getTargetAtClientPoint=function(e,t){var n=new h.ClientCoordinates(e,t).toPageCoordinates(),i=h.createEditorPagePosition(this.viewHelper.viewDomNode);if(n.yi.y+i.height||n.xi.x+i.width)return null;var o=this.viewHelper.getLastViewCursorsRenderData();return this.mouseTargetFactory.createMouseTarget(o,i,n,null)},t.prototype._createMouseTarget=function(e,t){var n=this.viewHelper.getLastViewCursorsRenderData();return this.mouseTargetFactory.createMouseTarget(n,e.editorPos,e.pos,t?e.target:null)},t.prototype._getMouseColumn=function(e){return this.mouseTargetFactory.getMouseColumn(e.editorPos,e.pos)},t.prototype._onContextMenu=function(e,t){this.viewController.emitContextMenu({event:e,target:this._createMouseTarget(e,t)})},t.prototype._onMouseMove=function(e){this._mouseDownOperation.isActive()||e.timestampt.y+t.height){var a=i.getLineNumberAtVerticalOffset(i.getScrollTop()+(e.posy-t.y));return new l.MouseTarget(null,c.MouseTargetType.OUTSIDE_EDITOR,o,new s.Position(a,n.getLineMaxColumn(a)))}var u=i.getLineNumberAtVerticalOffset(i.getScrollTop()+(e.posy-t.y));return e.posxt.x+t.width?new l.MouseTarget(null,c.MouseTargetType.OUTSIDE_EDITOR,o,new s.Position(u,n.getLineMaxColumn(u))):null},t.prototype._findMousePosition=function(e,t){var n=this._getPositionOutsideEditor(e);if(n)return n;var i=this._createMouseTarget(e,t);if(!i.position)return null;if(i.type===c.MouseTargetType.CONTENT_VIEW_ZONE||i.type===c.MouseTargetType.GUTTER_VIEW_ZONE){var o=new s.Position(this._currentSelection.selectionStartLineNumber,this._currentSelection.selectionStartColumn),r=i.detail,a=r.positionBefore,u=r.positionAfter;if(a&&u)return a.isBefore(o)?new l.MouseTarget(i.element,i.type,i.mouseColumn,a,null,i.detail):new l.MouseTarget(i.element,i.type,i.mouseColumn,u,null,i.detail)}return i},t.prototype._dispatchMouse=function(e,t){this._viewController.dispatchMouse({position:e.position,mouseColumn:e.mouseColumn,startedOnLineNumbers:this._mouseState.startedOnLineNumbers,inSelectionMode:t,mouseDownCount:this._mouseState.count,altKey:this._mouseState.altKey,ctrlKey:this._mouseState.ctrlKey,metaKey:this._mouseState.metaKey,shiftKey:this._mouseState.shiftKey})},t}(n.Disposable),y=function(){function e(){this._altKey=!1,this._ctrlKey=!1,this._metaKey=!1,this._shiftKey=!1,this._startedOnLineNumbers=!1,this._lastMouseDownPosition=null,this._lastMouseDownPositionEqualCount=0,this._lastMouseDownCount=0,this._lastSetMouseDownCountTime=0,this.isDragAndDrop=!1}return Object.defineProperty(e.prototype,"altKey",{get:function(){return this._altKey},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"ctrlKey",{get:function(){return this._ctrlKey},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"metaKey",{get:function(){return this._metaKey},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"shiftKey",{get:function(){return this._shiftKey},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"startedOnLineNumbers",{get:function(){return this._startedOnLineNumbers},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"count",{get:function(){return this._lastMouseDownCount},enumerable:!0,configurable:!0}),e.prototype.setModifiers=function(e){this._altKey=e.altKey,this._ctrlKey=e.ctrlKey,this._metaKey=e.metaKey,this._shiftKey=e.shiftKey},e.prototype.setStartedOnLineNumbers=function(e){this._startedOnLineNumbers=e},e.prototype.trySetCount=function(t,n){var i=(new Date).getTime();i-this._lastSetMouseDownCountTime>e.CLEAR_MOUSE_DOWN_COUNT_TIME&&(t=1),this._lastSetMouseDownCountTime=i,t>this._lastMouseDownCount+1&&(t=this._lastMouseDownCount+1),this._lastMouseDownPosition&&this._lastMouseDownPosition.equals(n)?this._lastMouseDownPositionEqualCount++:this._lastMouseDownPositionEqualCount=1,this._lastMouseDownPosition=n,this._lastMouseDownCount=Math.min(t,this._lastMouseDownPositionEqualCount)},e.CLEAR_MOUSE_DOWN_COUNT_TIME=400,e}()}),define(d[490],h([1,0,4,74,489,114]),function(e,t,n,i,o,r){"use strict";function s(e,t){var n={translationY:t.translationY,translationX:t.translationX};return e&&(n.translationY+=e.translationY,n.translationX+=e.translationX),n}Object.defineProperty(t,"__esModule",{value:!0});var a=function(e){function t(t,i,o){var r=e.call(this,t,i,o)||this;return r.viewHelper.linesContentDomNode.style.msTouchAction="none",r.viewHelper.linesContentDomNode.style.msContentZooming="none",r._installGestureHandlerTimeout=window.setTimeout(function(){if(r._installGestureHandlerTimeout=-1,window.MSGesture){var e=new MSGesture,t=new MSGesture;e.target=r.viewHelper.linesContentDomNode,t.target=r.viewHelper.linesContentDomNode,r.viewHelper.linesContentDomNode.addEventListener("MSPointerDown",function(n){var i=n.pointerType;i!==(n.MSPOINTER_TYPE_MOUSE||"mouse")?i===(n.MSPOINTER_TYPE_TOUCH||"touch")?(r._lastPointerType="touch",e.addPointer(n.pointerId)):(r._lastPointerType="pen",t.addPointer(n.pointerId)):r._lastPointerType="mouse"}),r._register(n.addDisposableThrottledListener(r.viewHelper.linesContentDomNode,"MSGestureChange",function(e){return r._onGestureChange(e)},s)),r._register(n.addDisposableListener(r.viewHelper.linesContentDomNode,"MSGestureTap",function(e){return r._onCaptureGestureTap(e)},!0))}},100),r._lastPointerType="mouse",r}return f(t,e),t.prototype._onMouseDown=function(t){"mouse"===this._lastPointerType&&e.prototype._onMouseDown.call(this,t)},t.prototype._onCaptureGestureTap=function(e){var t=this,n=new r.EditorMouseEvent(e,this.viewHelper.viewDomNode),i=this._createMouseTarget(n,!1);i.position&&this.viewController.moveTo(i.position),n.browserEvent.fromElement?(n.preventDefault(),this.viewHelper.focusTextArea()):setTimeout(function(){t.viewHelper.focusTextArea()})},t.prototype._onGestureChange=function(e){var t=this._context.viewLayout;t.setScrollPosition({scrollLeft:t.getScrollLeft()-e.translationX,scrollTop:t.getScrollTop()-e.translationY})},t.prototype.dispose=function(){window.clearTimeout(this._installGestureHandlerTimeout),e.prototype.dispose.call(this)},t}(o.MouseHandler),u=function(e){function t(t,i,o){var r=e.call(this,t,i,o)||this;return r.viewHelper.linesContentDomNode.style.touchAction="none",r._installGestureHandlerTimeout=window.setTimeout(function(){if(r._installGestureHandlerTimeout=-1,window.MSGesture){var e=new MSGesture,t=new MSGesture;e.target=r.viewHelper.linesContentDomNode,t.target=r.viewHelper.linesContentDomNode,r.viewHelper.linesContentDomNode.addEventListener("pointerdown",function(n){var i=n.pointerType;"mouse"!==i?"touch"===i?(r._lastPointerType="touch",e.addPointer(n.pointerId)):(r._lastPointerType="pen",t.addPointer(n.pointerId)):r._lastPointerType="mouse"}),r._register(n.addDisposableThrottledListener(r.viewHelper.linesContentDomNode,"MSGestureChange",function(e){return r._onGestureChange(e)},s)),r._register(n.addDisposableListener(r.viewHelper.linesContentDomNode,"MSGestureTap",function(e){return r._onCaptureGestureTap(e)},!0))}},100),r._lastPointerType="mouse",r}return f(t,e),t.prototype._onMouseDown=function(t){"mouse"===this._lastPointerType&&e.prototype._onMouseDown.call(this,t)},t.prototype._onCaptureGestureTap=function(e){var t=this,n=new r.EditorMouseEvent(e,this.viewHelper.viewDomNode),i=this._createMouseTarget(n,!1);i.position&&this.viewController.moveTo(i.position),n.browserEvent.fromElement?(n.preventDefault(),this.viewHelper.focusTextArea()):setTimeout(function(){t.viewHelper.focusTextArea()})},t.prototype._onGestureChange=function(e){var t=this._context.viewLayout;t.setScrollPosition({scrollLeft:t.getScrollLeft()-e.translationX,scrollTop:t.getScrollTop()-e.translationY})},t.prototype.dispose=function(){window.clearTimeout(this._installGestureHandlerTimeout),e.prototype.dispose.call(this)},t}(o.MouseHandler),l=function(e){function t(t,o,s){var a=e.call(this,t,o,s)||this;return a.gesture=new i.Gesture(a.viewHelper.linesContentDomNode),a._register(n.addDisposableListener(a.viewHelper.linesContentDomNode,i.EventType.Tap,function(e){return a.onTap(e)})),a._register(n.addDisposableListener(a.viewHelper.linesContentDomNode,i.EventType.Change,function(e){return a.onChange(e)})),a._register(n.addDisposableListener(a.viewHelper.linesContentDomNode,i.EventType.Contextmenu,function(e){return a._onContextMenu(new r.EditorMouseEvent(e,a.viewHelper.viewDomNode),!1)})),a}return f(t,e),t.prototype.dispose=function(){this.gesture.dispose(),e.prototype.dispose.call(this)},t.prototype.onTap=function(e){e.preventDefault(),this.viewHelper.focusTextArea();var t=this._createMouseTarget(new r.EditorMouseEvent(e,this.viewHelper.viewDomNode),!1);t.position&&this.viewController.moveTo(t.position)},t.prototype.onChange=function(e){var t=this._context.viewLayout;t.setScrollPosition({scrollLeft:t.getScrollLeft()-e.translationX,scrollTop:t.getScrollTop()-e.translationY})},t}(o.MouseHandler),c=function(){function e(e,t,n){window.navigator.msPointerEnabled?this.handler=new a(e,t,n):window.TouchEvent?this.handler=new l(e,t,n):window.navigator.pointerEnabled?this.handler=new u(e,t,n):this.handler=new o.MouseHandler(e,t,n)}return e.prototype.getTargetAtClientPoint=function(e,t){return this.handler.getTargetAtClientPoint(e,t)},e.prototype.dispose=function(){this.handler.dispose()},e}();t.PointerHandler=c}),define(d[491],h([1,0,3,186,11]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){function t(t){var n=e.call(this)||this;return n._onDidScroll=n._register(new o.Emitter),n.onDidScroll=n._onDidScroll.event,n._onDidGainFocus=n._register(new o.Emitter),n.onDidGainFocus=n._onDidGainFocus.event,n._onDidLoseFocus=n._register(new o.Emitter),n.onDidLoseFocus=n._onDidLoseFocus.event,n._onKeyDown=n._register(new o.Emitter),n.onKeyDown=n._onKeyDown.event,n._onKeyUp=n._register(new o.Emitter),n.onKeyUp=n._onKeyUp.event,n._onContextMenu=n._register(new o.Emitter),n.onContextMenu=n._onContextMenu.event,n._onMouseMove=n._register(new o.Emitter),n.onMouseMove=n._onMouseMove.event,n._onMouseLeave=n._register(new o.Emitter),n.onMouseLeave=n._onMouseLeave.event,n._onMouseUp=n._register(new o.Emitter),n.onMouseUp=n._onMouseUp.event,n._onMouseDown=n._register(new o.Emitter),n.onMouseDown=n._onMouseDown.event,n._onMouseDrag=n._register(new o.Emitter),n.onMouseDrag=n._onMouseDrag.event,n._onMouseDrop=n._register(new o.Emitter),n.onMouseDrop=n._onMouseDrop.event,n._viewModel=t,n}return f(t,e),t.prototype.emitScrollChanged=function(e){this._onDidScroll.fire(e)},t.prototype.emitViewFocusGained=function(){this._onDidGainFocus.fire()},t.prototype.emitViewFocusLost=function(){this._onDidLoseFocus.fire()},t.prototype.emitKeyDown=function(e){this._onKeyDown.fire(e)},t.prototype.emitKeyUp=function(e){this._onKeyUp.fire(e)},t.prototype.emitContextMenu=function(e){this._onContextMenu.fire(this._convertViewToModelMouseEvent(e))},t.prototype.emitMouseMove=function(e){this._onMouseMove.fire(this._convertViewToModelMouseEvent(e))},t.prototype.emitMouseLeave=function(e){this._onMouseLeave.fire(this._convertViewToModelMouseEvent(e))},t.prototype.emitMouseUp=function(e){this._onMouseUp.fire(this._convertViewToModelMouseEvent(e))},t.prototype.emitMouseDown=function(e){this._onMouseDown.fire(this._convertViewToModelMouseEvent(e))},t.prototype.emitMouseDrag=function(e){this._onMouseDrag.fire(this._convertViewToModelMouseEvent(e))},t.prototype.emitMouseDrop=function(e){this._onMouseDrop.fire(this._convertViewToModelMouseEvent(e))},t.prototype._convertViewToModelMouseEvent=function(e){return e.target?{event:e.event,target:this._convertViewToModelMouseTarget(e.target)}:e},t.prototype._convertViewToModelMouseTarget=function(e){return new s(e.element,e.type,e.mouseColumn,e.position?this._convertViewToModelPosition(e.position):null,e.range?this._convertViewToModelRange(e.range):null,e.detail)},t.prototype._convertViewToModelPosition=function(e){return this._viewModel.coordinatesConverter.convertViewPositionToModelPosition(e)},t.prototype._convertViewToModelRange=function(e){return this._viewModel.coordinatesConverter.convertViewRangeToModelRange(e)},t}(n.Disposable);t.ViewOutgoingEvents=r;var s=function(){function e(e,t,n,i,o,r){this.element=e,this.type=t,this.mouseColumn=n,this.position=i,this.range=o,this.detail=r}return e.prototype.toString=function(){return i.MouseTarget.toString(this)},e}()}),define(d[492],h([1,0,18,2,12,112,185,66,78,35,57,286]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var d=function(){function e(){this._currentVisibleRange=new i.Range(1,1,1,1)}return e.prototype.getCurrentVisibleRange=function(){return this._currentVisibleRange},e.prototype.setCurrentVisibleRange=function(e){this._currentVisibleRange=e},e}(),h=function(e){function t(t,i){var o=e.call(this,t)||this;o._linesContent=i,o._textRangeRestingSpot=document.createElement("div"),o._visibleLines=new r.VisibleLinesCollection(o),o.domNode=o._visibleLines.domNode;var u=o._context.configuration;return o._lineHeight=u.editor.lineHeight,o._typicalHalfwidthCharacterWidth=u.editor.fontInfo.typicalHalfwidthCharacterWidth,o._isViewportWrapping=u.editor.wrappingInfo.isViewportWrapping,o._revealHorizontalRightPadding=u.editor.viewInfo.revealHorizontalRightPadding,o._canUseLayerHinting=u.editor.canUseLayerHinting,o._viewLineOptions=new s.ViewLineOptions(u,o._context.theme.type),l.PartFingerprints.write(o.domNode,7),o.domNode.setClassName("view-lines"),a.Configuration.applyFontInfo(o.domNode,u.editor.fontInfo),o._maxLineWidth=0,o._asyncUpdateLineWidths=new n.RunOnceScheduler(function(){o._updateLineWidths()},200),o._lastRenderedData=new d,o._lastCursorRevealRangeHorizontallyEvent=null,o}return f(t,e),t.prototype.dispose=function(){this._asyncUpdateLineWidths.dispose(),e.prototype.dispose.call(this)},t.prototype.getDomNode=function(){return this.domNode},t.prototype.createVisibleLine=function(){return new s.ViewLine(this._viewLineOptions)},t.prototype.onConfigurationChanged=function(e){this._visibleLines.onConfigurationChanged(e),e.wrappingInfo&&(this._maxLineWidth=0);var t=this._context.configuration;return e.lineHeight&&(this._lineHeight=t.editor.lineHeight),e.fontInfo&&(this._typicalHalfwidthCharacterWidth=t.editor.fontInfo.typicalHalfwidthCharacterWidth),e.wrappingInfo&&(this._isViewportWrapping=t.editor.wrappingInfo.isViewportWrapping),e.viewInfo&&(this._revealHorizontalRightPadding=t.editor.viewInfo.revealHorizontalRightPadding),e.canUseLayerHinting&&(this._canUseLayerHinting=t.editor.canUseLayerHinting),e.fontInfo&&a.Configuration.applyFontInfo(this.domNode,t.editor.fontInfo),this._onOptionsMaybeChanged(),e.layoutInfo&&(this._maxLineWidth=0),!0},t.prototype._onOptionsMaybeChanged=function(){var e=this._context.configuration,t=new s.ViewLineOptions(e,this._context.theme.type);if(!this._viewLineOptions.equals(t)){this._viewLineOptions=t;for(var n=this._visibleLines.getStartLineNumber(),i=this._visibleLines.getEndLineNumber(),o=n;o<=i;o++)this._visibleLines.getVisibleLine(o).onOptionsChanged(this._viewLineOptions);return!0}return!1},t.prototype.onCursorStateChanged=function(e){for(var t=this._visibleLines.getStartLineNumber(),n=this._visibleLines.getEndLineNumber(),i=!1,o=t;o<=n;o++)i=this._visibleLines.getVisibleLine(o).onSelectionChanged()||i;return i},t.prototype.onDecorationsChanged=function(e){for(var t=this._visibleLines.getStartLineNumber(),n=this._visibleLines.getEndLineNumber(),i=t;i<=n;i++)this._visibleLines.getVisibleLine(i).onDecorationsChanged();return!0},t.prototype.onFlushed=function(e){var t=this._visibleLines.onFlushed(e);return this._maxLineWidth=0,t},t.prototype.onLinesChanged=function(e){return this._visibleLines.onLinesChanged(e)},t.prototype.onLinesDeleted=function(e){return this._visibleLines.onLinesDeleted(e)},t.prototype.onLinesInserted=function(e){return this._visibleLines.onLinesInserted(e)},t.prototype.onRevealRangeRequest=function(e){var t=this._computeScrollTopToRevealRange(this._context.viewLayout.getCurrentViewport(),e.range,e.verticalType);return e.revealHorizontal&&(this._lastCursorRevealRangeHorizontallyEvent=e),this._context.viewLayout.setScrollPosition({scrollTop:t}),!0},t.prototype.onScrollChanged=function(e){return this.domNode.setWidth(e.scrollWidth),this._visibleLines.onScrollChanged(e)||!0},t.prototype.onTokensChanged=function(e){return this._visibleLines.onTokensChanged(e)},t.prototype.onZonesChanged=function(e){return this._visibleLines.onZonesChanged(e)},t.prototype.onThemeChanged=function(e){return this._onOptionsMaybeChanged()},t.prototype.getPositionFromDOMInfo=function(e,t){var n=this._getViewLineDomNode(e);if(null===n)return null;var i=this._getLineNumberFor(n);if(-1===i)return null;if(i<1||i>this._context.model.getLineCount())return null;if(1===this._context.model.getLineMaxColumn(i))return new o.Position(i,1);var r=this._visibleLines.getStartLineNumber(),s=this._visibleLines.getEndLineNumber();if(is)return null;var a=this._visibleLines.getVisibleLine(i).getColumnOfNodeOffset(i,e,t),u=this._context.model.getLineMinColumn(i);return an?-1:this._visibleLines.getVisibleLine(e).getWidth()},t.prototype.linesVisibleRangesForRange=function(e,t){if(this.shouldRender())return null;var n=e.endLineNumber;if(!(e=i.Range.intersectRanges(e,this._lastRenderedData.getCurrentVisibleRange())))return null;var r,a=[],l=new s.DomReadingContext(this.domNode.domNode,this._textRangeRestingSpot);t&&(r=this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new o.Position(e.startLineNumber,1)).lineNumber);for(var c=this._visibleLines.getStartLineNumber(),d=this._visibleLines.getEndLineNumber(),h=e.startLineNumber;h<=e.endLineNumber;h++)if(!(hd)){var p=h===e.startLineNumber?e.startColumn:1,f=h===e.endLineNumber?e.endColumn:this._context.model.getLineMaxColumn(h),g=this._visibleLines.getVisibleLine(h).getVisibleRangesForRange(p,f,l);g&&0!==g.length&&(t&&hr)){var u=a===e.startLineNumber?e.startColumn:1,l=a===e.endLineNumber?e.endColumn:this._context.model.getLineMaxColumn(a),c=this._visibleLines.getVisibleLine(a).getVisibleRangesForRange(u,l,n);c&&0!==c.length&&(t=t.concat(c))}return 0===t.length?null:t},t.prototype._updateLineWidths=function(){for(var e=this._visibleLines.getStartLineNumber(),t=this._visibleLines.getEndLineNumber(),n=1,i=e;i<=t;i++){var o=this._visibleLines.getVisibleLine(i).getWidth();n=Math.max(n,o)}1===e&&t===this._context.model.getLineCount()&&(this._maxLineWidth=0),this._ensureMaxLineWidth(n)},t.prototype.prepareRender=function(){throw new Error("Not supported")},t.prototype.render=function(){throw new Error("Not supported")},t.prototype.renderText=function(e){if(this._visibleLines.renderLines(e),this._lastRenderedData.setCurrentVisibleRange(e.visibleRange),this.domNode.setWidth(this._context.viewLayout.getScrollWidth()),this.domNode.setHeight(Math.min(this._context.viewLayout.getScrollHeight(),1e6)),this._lastCursorRevealRangeHorizontallyEvent){var t=this._lastCursorRevealRangeHorizontallyEvent.range;this._lastCursorRevealRangeHorizontallyEvent=null,this.onDidRender();var n=this._computeScrollLeftToRevealRange(t);this._isViewportWrapping||this._ensureMaxLineWidth(n.maxHorizontalOffset),this._context.viewLayout.setScrollPosition({scrollLeft:n.scrollLeft})}this._linesContent.setLayerHinting(this._canUseLayerHinting);var i=this._context.viewLayout.getScrollTop()-e.bigNumbersDelta;this._linesContent.setTop(-i),this._linesContent.setLeft(-this._context.viewLayout.getScrollLeft()),this._asyncUpdateLineWidths.schedule()},t.prototype._ensureMaxLineWidth=function(e){var t=Math.ceil(e);this._maxLineWidthu&&(u=c.left+c.width)}return n=u,a=Math.max(0,a-t.HORIZONTAL_EXTRA_PX),u+=this._revealHorizontalRightPadding,{scrollLeft:this._computeMinimumScrolling(o,r,a,u),maxHorizontalOffset:n}},t.prototype._computeMinimumScrolling=function(e,t,n,i,o,r){o=!!o,r=!!r;var s=(t|=0)-(e|=0);return(i|=0)-(n|=0)t?Math.max(0,i-s):e:n},t.HORIZONTAL_EXTRA_PX=30,t}(l.ViewPart);t.ViewLines=h}),define(d[493],h([1,0,35,215,4,111,27,112,2,57,83,15,14,23,294]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p){"use strict";function g(e){return 2===e?4:4===e?6:1===e?2:3}function m(e){return 2===e?2:4===e?2:1}Object.defineProperty(t,"__esModule",{value:!0});var v;!function(e){e[e.None=0]="None",e[e.Small=1]="Small",e[e.Large=2]="Large",e[e.SmallBlocks=3]="SmallBlocks",e[e.LargeBlocks=4]="LargeBlocks"}(v||(v={}));var _=140,y=function(){function e(e){var t=e.editor.pixelRatio,n=e.editor.layoutInfo,i=e.editor.viewInfo,o=e.editor.fontInfo;this.renderMinimap=0|n.renderMinimap,this.scrollBeyondLastLine=i.scrollBeyondLastLine,this.showSlider=i.minimap.showSlider,this.pixelRatio=t,this.typicalHalfwidthCharacterWidth=o.typicalHalfwidthCharacterWidth,this.lineHeight=e.editor.lineHeight,this.minimapWidth=n.minimapWidth,this.minimapHeight=n.height,this.canvasInnerWidth=Math.max(1,Math.floor(t*this.minimapWidth)),this.canvasInnerHeight=Math.max(1,Math.floor(t*this.minimapHeight)),this.canvasOuterWidth=this.canvasInnerWidth/t,this.canvasOuterHeight=this.canvasInnerHeight/t}return e.prototype.equals=function(e){return this.renderMinimap===e.renderMinimap&&this.scrollBeyondLastLine===e.scrollBeyondLastLine&&this.showSlider===e.showSlider&&this.pixelRatio===e.pixelRatio&&this.typicalHalfwidthCharacterWidth===e.typicalHalfwidthCharacterWidth&&this.lineHeight===e.lineHeight&&this.minimapWidth===e.minimapWidth&&this.minimapHeight===e.minimapHeight&&this.canvasInnerWidth===e.canvasInnerWidth&&this.canvasInnerHeight===e.canvasInnerHeight&&this.canvasOuterWidth===e.canvasOuterWidth&&this.canvasOuterHeight===e.canvasOuterHeight},e}(),C=function(){function e(e,t,n,i,o,r,s){this.scrollTop=e,this.scrollHeight=t,this._computedSliderRatio=n,this.sliderTop=i,this.sliderHeight=o,this.startLineNumber=r,this.endLineNumber=s}return e.prototype.getDesiredScrollTopFromDelta=function(e){var t=this.sliderTop+e;return Math.round(t/this._computedSliderRatio)},e.create=function(t,n,i,o,r,s,a,u,l){var c,d=t.pixelRatio,h=g(t.renderMinimap),p=Math.floor(t.canvasInnerHeight/h),f=t.lineHeight;if(r&&i!==s){var m=i-n+1;c=Math.floor(m*h/d)}else{var v=o/f;c=Math.floor(v*h/d)}var _;_=t.scrollBeyondLastLine?(s-1)*h/d:Math.max(0,s*h/d-c);var y=(_=Math.min(t.minimapHeight-c,_))/(u-o),C=a*y;if(p>=s)return new e(a,u,y,C,c,b=1,w=s);var b=Math.max(1,Math.floor(n-C*d/h));l&&l.scrollHeight===u&&(l.scrollTop>a&&(b=Math.min(b,l.startLineNumber)),l.scrollTop_)a._context.viewLayout.setScrollPosition({scrollTop:i.scrollTop});else{var r=e.posy-t;a._context.viewLayout.setScrollPosition({scrollTop:i.getDesiredScrollTopFromDelta(r)})}},function(){a._slider.toggleClassName("active",!1)})}}),a}return f(t,e),t.prototype.dispose=function(){this._mouseDownListener.dispose(),this._sliderMouseMoveMonitor.dispose(),this._sliderMouseDownListener.dispose(),e.prototype.dispose.call(this)},t.prototype._getMinimapDomNodeClassName=function(){return"always"===this._options.showSlider?"minimap slider-always":"minimap slider-mouseover"},t.prototype.getDomNode=function(){return this._domNode},t.prototype._applyLayout=function(){this._domNode.setWidth(this._options.minimapWidth),this._domNode.setHeight(this._options.minimapHeight),this._shadow.setHeight(this._options.minimapHeight),this._canvas.setWidth(this._options.canvasOuterWidth),this._canvas.setHeight(this._options.canvasOuterHeight),this._canvas.domNode.width=this._options.canvasInnerWidth,this._canvas.domNode.height=this._options.canvasInnerHeight,this._slider.setWidth(this._options.minimapWidth)},t.prototype._getBuffer=function(){return this._buffers||(this._buffers=new S(this._canvas.domNode.getContext("2d"),this._options.canvasInnerWidth,this._options.canvasInnerHeight,this._tokensColorTracker.getColor(2))),this._buffers.getBuffer()},t.prototype._onOptionsMaybeChanged=function(){var e=new y(this._context.configuration);return!this._options.equals(e)&&(this._options=e,this._lastRenderData=null,this._buffers=null,this._applyLayout(),this._domNode.setClassName(this._getMinimapDomNodeClassName()),!0)},t.prototype.onConfigurationChanged=function(e){return this._onOptionsMaybeChanged()},t.prototype.onFlushed=function(e){return this._lastRenderData=null,!0},t.prototype.onLinesChanged=function(e){return!!this._lastRenderData&&this._lastRenderData.onLinesChanged(e)},t.prototype.onLinesDeleted=function(e){return this._lastRenderData&&this._lastRenderData.onLinesDeleted(e),!0},t.prototype.onLinesInserted=function(e){return this._lastRenderData&&this._lastRenderData.onLinesInserted(e),!0},t.prototype.onScrollChanged=function(e){return!0},t.prototype.onTokensChanged=function(e){return!!this._lastRenderData&&this._lastRenderData.onTokensChanged(e)},t.prototype.onTokensColorsChanged=function(e){return this._lastRenderData=null,this._buffers=null,!0},t.prototype.onZonesChanged=function(e){return this._lastRenderData=null,!0},t.prototype.prepareRender=function(e){},t.prototype.render=function(e){if(0!==this._options.renderMinimap){e.scrollLeft+e.viewportWidth>=e.scrollWidth?this._shadow.setClassName("minimap-shadow-hidden"):this._shadow.setClassName("minimap-shadow-visible");var t=C.create(this._options,e.visibleRange.startLineNumber,e.visibleRange.endLineNumber,e.viewportHeight,e.viewportData.whitespaceViewportData.length>0,this._context.model.getLineCount(),e.scrollTop,e.scrollHeight,this._lastRenderData?this._lastRenderData.renderedLayout:null);this._slider.setTop(t.sliderTop),this._slider.setHeight(t.sliderHeight);var n=e.scrollLeft/this._options.typicalHalfwidthCharacterWidth,i=Math.min(this._options.minimapWidth,Math.round(n*m(this._options.renderMinimap)/this._options.pixelRatio));this._sliderHorizontal.setLeft(i),this._sliderHorizontal.setWidth(this._options.minimapWidth-i),this._sliderHorizontal.setTop(0),this._sliderHorizontal.setHeight(t.sliderHeight),this._lastRenderData=this.renderLines(t)}else this._shadow.setClassName("minimap-shadow-hidden")},t.prototype.renderLines=function(e){var n=this._options.renderMinimap,i=e.startLineNumber,o=e.endLineNumber,r=g(n);if(this._lastRenderData&&this._lastRenderData.linesEquals(e)){var s=this._lastRenderData._get();return new w(e,s.imageData,s.lines)}for(var a=this._getBuffer(),u=t._renderUntouchedLines(a,i,o,r,this._lastRenderData),l=this._context.model.getMinimapLinesRenderingData(i,o,u),c=l.tabSize,d=this._tokensColorTracker.getColor(2),h=this._tokensColorTracker.backgroundIsLight(),p=0,f=[],m=0,v=o-i+1;m=0&&wh)return;var S=l.charCodeAt(f);if(9===S){var E=a-(f+g)%a;g+=E-1,p+=E*d}else 32===S?p+=d:(2===i?r.x2RenderChar(e,p,s,S,w,t,n):1===i?r.x1RenderChar(e,p,s,S,w,t,n):4===i?r.x2BlockRenderChar(e,p,s,w,t,n):r.x1BlockRenderChar(e,p,s,w,t,n),p+=d)}},t}(n.ViewPart);t.Minimap=E,h.registerThemingParticipant(function(e,t){var n=e.getColor(p.scrollbarSliderBackground);if(n){var i=n.transparent(.5);t.addRule(".monaco-editor .minimap-slider, .monaco-editor .minimap-slider .minimap-slider-horizontal { background: "+i+"; }")}var o=e.getColor(p.scrollbarSliderHoverBackground);if(o){var r=o.transparent(.5);t.addRule(".monaco-editor .minimap-slider:hover, .monaco-editor .minimap-slider:hover .minimap-slider-horizontal { background: "+r+"; }")}var s=e.getColor(p.scrollbarSliderActiveBackground);if(s){var a=s.transparent(.5);t.addRule(".monaco-editor .minimap-slider.active, .monaco-editor .minimap-slider.active .minimap-slider-horizontal { background: "+a+"; }")}var u=e.getColor(p.scrollbarShadow);u&&t.addRule(".monaco-editor .minimap-shadow-visible { box-shadow: "+u+" -6px 0 6px -6px inset; }")})}),define(d[494],h([1,0,27,35,14,23,300]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(e){function t(t){var i=e.call(this,t)||this;return i._scrollTop=0,i._width=0,i._updateWidth(),i._shouldShow=!1,i._useShadows=i._context.configuration.editor.viewInfo.scrollbar.useShadows,i._domNode=n.createFastDomNode(document.createElement("div")),i._domNode.setAttribute("role","presentation"),i._domNode.setAttribute("aria-hidden","true"),i}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype._updateShouldShow=function(){var e=this._useShadows&&this._scrollTop>0;return this._shouldShow!==e&&(this._shouldShow=e,!0)},t.prototype.getDomNode=function(){return this._domNode},t.prototype._updateWidth=function(){var e=this._context.configuration.editor.layoutInfo,t=e.width-e.minimapWidth;return this._width!==t&&(this._width=t,!0)},t.prototype.onConfigurationChanged=function(e){var t=!1;return e.viewInfo&&(this._useShadows=this._context.configuration.editor.viewInfo.scrollbar.useShadows),e.layoutInfo&&(t=this._updateWidth()),this._updateShouldShow()||t},t.prototype.onScrollChanged=function(e){return this._scrollTop=e.scrollTop,this._updateShouldShow()},t.prototype.prepareRender=function(e){},t.prototype.render=function(e){this._domNode.setWidth(this._width),this._domNode.setClassName(this._shouldShow?"scroll-decoration":"")},t}(i.ViewPart);t.ScrollDecorationViewPart=s,o.registerThemingParticipant(function(e,t){var n=e.getColor(r.scrollbarShadow);n&&t.addRule(".monaco-editor .scroll-decoration { box-shadow: "+n+" 0 6px 6px -6px inset; }")})}),define(d[495],h([1,0,14,23,67,28,306]),function(e,t,n,i,o,r){"use strict";function s(e){return new l(e)}function a(e){return new c(e.lineNumber,e.ranges.map(s))}Object.defineProperty(t,"__esModule",{value:!0});var u;!function(e){e[e.EXTERN=0]="EXTERN",e[e.INTERN=1]="INTERN",e[e.FLAT=2]="FLAT"}(u||(u={}));var l=function(){return function(e){this.left=e.left,this.width=e.width,this.startStyle=null,this.endStyle=null}}(),c=function(){return function(e,t){this.lineNumber=e,this.ranges=t}}(),d=r.isEdgeOrIE,h=function(e){function t(t){var n=e.call(this)||this;return n._previousFrameVisibleRangesWithStyle=[],n._context=t,n._lineHeight=n._context.configuration.editor.lineHeight,n._roundedSelection=n._context.configuration.editor.viewInfo.roundedSelection,n._selections=[],n._renderResult=null,n._context.addEventHandler(n),n}return f(t,e),t.prototype.dispose=function(){this._context.removeEventHandler(this),this._context=null,this._selections=null,this._renderResult=null,e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return e.lineHeight&&(this._lineHeight=this._context.configuration.editor.lineHeight),e.viewInfo&&(this._roundedSelection=this._context.configuration.editor.viewInfo.roundedSelection),!0},t.prototype.onCursorStateChanged=function(e){return this._selections=e.selections.slice(0),!0},t.prototype.onDecorationsChanged=function(e){return!0},t.prototype.onFlushed=function(e){return!0},t.prototype.onLinesChanged=function(e){return!0},t.prototype.onLinesDeleted=function(e){return!0},t.prototype.onLinesInserted=function(e){return!0},t.prototype.onScrollChanged=function(e){return e.scrollTopChanged},t.prototype.onZonesChanged=function(e){return!0},t.prototype._visibleRangesHaveGaps=function(e){for(var t=0,n=e.length;t1)return!0;return!1},t.prototype._enrichVisibleRangesWithStyle=function(e,t){var n=null,i=null;if(t&&t.length>0&&e.length>0){for(var o=e[0].lineNumber,r=0;!n&&r=0;r--)t[r].lineNumber===s&&(i=t[r].ranges[0]);n&&!n.startStyle&&(n=null),i&&!i.startStyle&&(i=null)}for(var r=0,a=e.length;r0){var p=e[r-1].ranges[0].left,f=e[r-1].ranges[0].left+e[r-1].ranges[0].width;l===p?d.top=2:l>p&&(d.top=1),c===f?h.top=2:p'},t.prototype._actualRenderOneSelection=function(e,n,i,o){for(var r=o.length>0&&o[0].ranges[0].startStyle,s=this._lineHeight.toString(),a=(this._lineHeight-1).toString(),u=o.length>0?o[0].lineNumber:0,l=o.length>0?o[o.length-1].lineNumber:0,c=0,d=o.length;c1,l)}}this._previousFrameVisibleRangesWithStyle=r,this._renderResult=t},t.prototype.render=function(e,t){if(!this._renderResult)return"";var n=t-e;if(n<0||n>=this._renderResult.length)throw new Error("Unexpected render request");return this._renderResult[n]},t.SELECTION_CLASS_NAME="selected-text",t.SELECTION_TOP_LEFT="top-left-radius",t.SELECTION_BOTTOM_LEFT="bottom-left-radius",t.SELECTION_TOP_RIGHT="top-right-radius",t.SELECTION_BOTTOM_RIGHT="bottom-right-radius",t.EDITOR_BACKGROUND_CLASS_NAME="monaco-editor-background",t.ROUNDED_PIECE_WIDTH=10,t}(o.DynamicViewOverlay);t.SelectionsOverlay=h,n.registerThemingParticipant(function(e,t){var n=e.getColor(i.editorSelectionBackground);n&&t.addRule(".monaco-editor .focused .selected-text { background-color: "+n+"; }");var o=e.getColor(i.editorInactiveSelection);o&&t.addRule(".monaco-editor .selected-text { background-color: "+o+"; }");var r=e.getColor(i.editorSelectionForeground);r&&t.addRule(".monaco-editor .view-line span.inline-selected-text { color: "+r+"; }")})}),define(d[37],h([1,0,340,23,14,32]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.editorLineHighlight=i.registerColor("editor.lineHighlightBackground",{dark:null,light:null,hc:null},n.localize(0,null)),t.editorLineHighlightBorder=i.registerColor("editor.lineHighlightBorder",{dark:"#282828",light:"#eeeeee",hc:"#f38518"},n.localize(1,null)),t.editorRangeHighlight=i.registerColor("editor.rangeHighlightBackground",{dark:"#ffffff0b",light:"#fdff0033",hc:null},n.localize(2,null)),t.editorCursorForeground=i.registerColor("editorCursor.foreground",{dark:"#AEAFAD",light:r.Color.black,hc:r.Color.white},n.localize(3,null)),t.editorCursorBackground=i.registerColor("editorCursor.background",null,n.localize(4,null)),t.editorWhitespaces=i.registerColor("editorWhitespace.foreground",{dark:"#e3e4e229",light:"#33333333",hc:"#e3e4e229"},n.localize(5,null)),t.editorIndentGuides=i.registerColor("editorIndentGuide.background",{dark:t.editorWhitespaces,light:t.editorWhitespaces,hc:t.editorWhitespaces},n.localize(6,null)),t.editorLineNumbers=i.registerColor("editorLineNumber.foreground",{dark:"#5A5A5A",light:"#2B91AF",hc:r.Color.white},n.localize(7,null)),t.editorRuler=i.registerColor("editorRuler.foreground",{dark:"#5A5A5A",light:r.Color.lightgrey,hc:r.Color.white},n.localize(8,null)),t.editorCodeLensForeground=i.registerColor("editorCodeLens.foreground",{dark:"#999999",light:"#999999",hc:"#999999"},n.localize(9,null)),t.editorBracketMatchBackground=i.registerColor("editorBracketMatch.background",{dark:"#0064001a",light:"#0064001a",hc:"#0064001a"},n.localize(10,null)),t.editorBracketMatchBorder=i.registerColor("editorBracketMatch.border",{dark:"#888",light:"#B9B9B9",hc:"#fff"},n.localize(11,null)),t.editorOverviewRulerBorder=i.registerColor("editorOverviewRuler.border",{dark:"#7f7f7f4d",light:"#7f7f7f4d",hc:"#7f7f7f4d"},n.localize(12,null)),t.editorGutter=i.registerColor("editorGutter.background",{dark:i.editorBackground,light:i.editorBackground,hc:i.editorBackground},n.localize(13,null)),t.editorErrorForeground=i.registerColor("editorError.foreground",{dark:"#FF0000",light:"#FF0000",hc:null},n.localize(14,null)),t.editorErrorBorder=i.registerColor("editorError.border",{dark:null,light:null,hc:r.Color.fromHex("#E47777").transparent(.8)},n.localize(15,null)),t.editorWarningForeground=i.registerColor("editorWarning.foreground",{dark:"#008000",light:"#008000",hc:null},n.localize(16,null)),t.editorWarningBorder=i.registerColor("editorWarning.border",{dark:null,light:null,hc:r.Color.fromHex("#71B771").transparent(.8)},n.localize(17,null)),o.registerThemingParticipant(function(e,n){var o=e.getColor(i.editorBackground);o&&n.addRule(".monaco-editor, .monaco-editor-background, .monaco-editor .inputarea.ime-input { background-color: "+o+"; }");var r=e.getColor(i.editorForeground);r&&n.addRule(".monaco-editor, .monaco-editor .inputarea.ime-input { color: "+r+"; }");var s=e.getColor(t.editorGutter);s&&n.addRule(".monaco-editor .margin { background-color: "+s+"; }");var a=e.getColor(t.editorRangeHighlight);a&&n.addRule(".monaco-editor .rangeHighlight { background-color: "+a+"; }");var u=e.getColor(i.activeContrastBorder);u&&n.addRule(".monaco-editor .rangeHighlight { border: 1px dotted "+u+"; }; }");var l=e.getColor(t.editorWhitespaces);l&&n.addRule(".vs-whitespace { color: "+l+" !important; }")})}),define(d[497],h([1,0,67,14,37,274]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){function t(t){var n=e.call(this)||this;return n._context=t,n._lineHeight=n._context.configuration.editor.lineHeight,n._readOnly=n._context.configuration.editor.readOnly,n._renderLineHighlight=n._context.configuration.editor.viewInfo.renderLineHighlight,n._selectionIsEmpty=!0,n._primaryCursorIsInEditableRange=!0,n._primaryCursorLineNumber=1,n._scrollWidth=0,n._contentWidth=n._context.configuration.editor.layoutInfo.contentWidth,n._context.addEventHandler(n),n}return f(t,e),t.prototype.dispose=function(){this._context.removeEventHandler(this),this._context=null,e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return e.lineHeight&&(this._lineHeight=this._context.configuration.editor.lineHeight),e.readOnly&&(this._readOnly=this._context.configuration.editor.readOnly),e.viewInfo&&(this._renderLineHighlight=this._context.configuration.editor.viewInfo.renderLineHighlight),e.layoutInfo&&(this._contentWidth=this._context.configuration.editor.layoutInfo.contentWidth),!0},t.prototype.onCursorStateChanged=function(e){var t=!1;this._primaryCursorIsInEditableRange!==e.isInEditableRange&&(this._primaryCursorIsInEditableRange=e.isInEditableRange,t=!0);var n=e.selections[0].positionLineNumber;this._primaryCursorLineNumber!==n&&(this._primaryCursorLineNumber=n,t=!0);var i=e.selections[0].isEmpty();return this._selectionIsEmpty!==i?(this._selectionIsEmpty=i,t=!0,!0):t},t.prototype.onFlushed=function(e){return!0},t.prototype.onLinesDeleted=function(e){return!0},t.prototype.onLinesInserted=function(e){return!0},t.prototype.onScrollChanged=function(e){return e.scrollWidthChanged},t.prototype.onZonesChanged=function(e){return!0},t.prototype.prepareRender=function(e){this._scrollWidth=e.scrollWidth},t.prototype.render=function(e,t){return t===this._primaryCursorLineNumber&&this._shouldShowCurrentLine()?'
    ':""},t.prototype._shouldShowCurrentLine=function(){return("line"===this._renderLineHighlight||"all"===this._renderLineHighlight)&&this._selectionIsEmpty&&this._primaryCursorIsInEditableRange},t}(n.DynamicViewOverlay);t.CurrentLineHighlightOverlay=r,i.registerThemingParticipant(function(e,t){var n=e.getColor(o.editorLineHighlight);if(n&&t.addRule(".monaco-editor .view-overlays .current-line { background-color: "+n+"; }"),!n||n.isTransparent()||e.defines(o.editorLineHighlightBorder)){var i=e.getColor(o.editorLineHighlightBorder);i&&(t.addRule(".monaco-editor .view-overlays .current-line { border: 2px solid "+i+"; }"),"hc"===e.type&&t.addRule(".monaco-editor .view-overlays .current-line { border-width: 1px; }"))}})}),define(d[498],h([1,0,67,14,37,277]),function(e,t,n,i,o){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){function t(t){var n=e.call(this)||this;return n._context=t,n._lineHeight=n._context.configuration.editor.lineHeight,n._renderLineHighlight=n._context.configuration.editor.viewInfo.renderLineHighlight,n._primaryCursorIsInEditableRange=!0,n._primaryCursorLineNumber=1,n._contentLeft=n._context.configuration.editor.layoutInfo.contentLeft,n._context.addEventHandler(n),n}return f(t,e),t.prototype.dispose=function(){this._context.removeEventHandler(this),this._context=null,e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return e.lineHeight&&(this._lineHeight=this._context.configuration.editor.lineHeight),e.viewInfo&&(this._renderLineHighlight=this._context.configuration.editor.viewInfo.renderLineHighlight),e.layoutInfo&&(this._contentLeft=this._context.configuration.editor.layoutInfo.contentLeft),!0},t.prototype.onCursorStateChanged=function(e){var t=!1;this._primaryCursorIsInEditableRange!==e.isInEditableRange&&(this._primaryCursorIsInEditableRange=e.isInEditableRange,t=!0);var n=e.selections[0].positionLineNumber;return this._primaryCursorLineNumber!==n&&(this._primaryCursorLineNumber=n,t=!0),t},t.prototype.onFlushed=function(e){return!0},t.prototype.onLinesDeleted=function(e){return!0},t.prototype.onLinesInserted=function(e){return!0},t.prototype.onZonesChanged=function(e){return!0},t.prototype.prepareRender=function(e){},t.prototype.render=function(e,t){return t===this._primaryCursorLineNumber&&this._shouldShowCurrentLine()?'
    ':""},t.prototype._shouldShowCurrentLine=function(){return("gutter"===this._renderLineHighlight||"all"===this._renderLineHighlight)&&this._primaryCursorIsInEditableRange},t}(n.DynamicViewOverlay);t.CurrentLineMarginHighlightOverlay=r,i.registerThemingParticipant(function(e,t){var n=e.getColor(o.editorLineHighlight);if(n)t.addRule(".monaco-editor .margin-view-overlays .current-line-margin { background-color: "+n+"; border: none; }");else{var i=e.getColor(o.editorLineHighlightBorder);i&&t.addRule(".monaco-editor .margin-view-overlays .current-line-margin { border: 2px solid "+i+"; }"),"hc"===e.type&&t.addRule(".monaco-editor .margin-view-overlays .current-line-margin { border-width: 1px; }")}})}),define(d[499],h([1,0,67,14,37,4,12,280]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(e){function t(t){var n=e.call(this)||this;return n._context=t,n._lineHeight=n._context.configuration.editor.lineHeight,n._spaceWidth=n._context.configuration.editor.fontInfo.spaceWidth,n._enabled=n._context.configuration.editor.viewInfo.renderIndentGuides,n._renderResult=null,n._context.addEventHandler(n),n}return f(t,e),t.prototype.dispose=function(){this._context.removeEventHandler(this),this._context=null,this._renderResult=null,e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return e.lineHeight&&(this._lineHeight=this._context.configuration.editor.lineHeight),e.fontInfo&&(this._spaceWidth=this._context.configuration.editor.fontInfo.spaceWidth),e.viewInfo&&(this._enabled=this._context.configuration.editor.viewInfo.renderIndentGuides),!0},t.prototype.onDecorationsChanged=function(e){return!0},t.prototype.onFlushed=function(e){return!0},t.prototype.onLinesChanged=function(e){return!0},t.prototype.onLinesDeleted=function(e){return!0},t.prototype.onLinesInserted=function(e){return!0},t.prototype.onScrollChanged=function(e){return e.scrollTopChanged},t.prototype.onZonesChanged=function(e){return!0},t.prototype.prepareRender=function(e){if(this._enabled){for(var t=e.visibleRange.startLineNumber,n=e.visibleRange.endLineNumber,i=this._context.model.getTabSize()*this._spaceWidth,o=this._lineHeight,a=r.computeScreenAwareSize(1),u=[],l=t;l<=n;l++){for(var c=l-t,d=this._context.model.getLineIndentGuide(l),h="",p=e.visibleRangeForPosition(new s.Position(l,1)),f=p?p.left:0,g=0;g',f+=i;u[c]=h}this._renderResult=u}else this._renderResult=null},t.prototype.render=function(e,t){if(!this._renderResult)return"";var n=t-e;if(n<0||n>=this._renderResult.length)throw new Error("Unexpected render request");return this._renderResult[n]},t}(n.DynamicViewOverlay);t.IndentGuidesOverlay=a,i.registerThemingParticipant(function(e,t){var n=e.getColor(o.editorIndentGuides);n&&t.addRule(".monaco-editor .lines-content .cigr { background-color: "+n+"; }")})}),define(d[188],h([1,0,37,14,15,67,12,283]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(e){function t(t){var n=e.call(this)||this;return n._context=t,n._readConfig(),n._lastCursorModelPosition=new s.Position(1,1),n._renderResult=null,n._context.addEventHandler(n),n}return f(t,e),t.prototype._readConfig=function(){var e=this._context.configuration.editor;this._lineHeight=e.lineHeight,this._renderLineNumbers=e.viewInfo.renderLineNumbers,this._renderCustomLineNumbers=e.viewInfo.renderCustomLineNumbers,this._renderRelativeLineNumbers=e.viewInfo.renderRelativeLineNumbers,this._lineNumbersLeft=e.layoutInfo.lineNumbersLeft,this._lineNumbersWidth=e.layoutInfo.lineNumbersWidth},t.prototype.dispose=function(){this._context.removeEventHandler(this),this._context=null,this._renderResult=null,e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return this._readConfig(),!0},t.prototype.onCursorStateChanged=function(e){var t=e.selections[0].getPosition();return this._lastCursorModelPosition=this._context.model.coordinatesConverter.convertViewPositionToModelPosition(t),!!this._renderRelativeLineNumbers},t.prototype.onFlushed=function(e){return!0},t.prototype.onLinesChanged=function(e){return!0},t.prototype.onLinesDeleted=function(e){return!0},t.prototype.onLinesInserted=function(e){return!0},t.prototype.onScrollChanged=function(e){return e.scrollTopChanged},t.prototype.onZonesChanged=function(e){return!0},t.prototype._getLineRenderLineNumber=function(e){var t=this._context.model.coordinatesConverter.convertViewPositionToModelPosition(new s.Position(e,1));if(1!==t.column)return"";var n=t.lineNumber;if(this._renderCustomLineNumbers)return this._renderCustomLineNumbers(n);if(this._renderRelativeLineNumbers){var i=Math.abs(this._lastCursorModelPosition.lineNumber-n);return 0===i?''+n+"":String(i)}return String(n)},t.prototype.prepareRender=function(e){if(this._renderLineNumbers){for(var n=o.isLinux?this._lineHeight%2==0?" lh-even":" lh-odd":"",i=e.visibleRange.startLineNumber,r=e.visibleRange.endLineNumber,s='
    ',a=[],u=i;u<=r;u++){var l=u-i,c=this._getLineRenderLineNumber(u);a[l]=c?s+c+"
    ":""}this._renderResult=a}else this._renderResult=null},t.prototype.render=function(e,t){if(!this._renderResult)return"";var n=t-e;if(n<0||n>=this._renderResult.length)throw new Error("Unexpected render request");return this._renderResult[n]},t.CLASS_NAME="line-numbers",t}(r.DynamicViewOverlay);t.LineNumbersOverlay=a,i.registerThemingParticipant(function(e,t){var i=e.getColor(n.editorLineNumbers);i&&t.addRule(".monaco-editor .line-numbers { color: "+i+"; }")})}),define(d[501],h([1,0,15,28,154,151,2,22,12,66,57,27,35,182,188,270]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g){"use strict";function m(e,t){var n=document.createElement("canvas").getContext("2d");n.font=v(t);var o=n.measureText(e);return i.isFirefox?o.width+2:o.width}function v(e){return _("normal",e.fontWeight,e.fontSize,e.lineHeight,e.fontFamily)}function _(e,t,n,i,o){return e+" normal "+t+" "+n+"px / "+i+"px "+o}Object.defineProperty(t,"__esModule",{value:!0});var y=function(){function e(e,t,n){this.top=e,this.left=t,this.width=n}return e.prototype.setWidth=function(t){return new e(this.top,this.left,t)},e}(),C=i.isEdgeOrIE||i.isFirefox,b=function(e){function t(t,n,u){var l=e.call(this,t)||this;l._primaryCursorVisibleRange=null,l._viewController=n,l._viewHelper=u;var p=l._context.configuration.editor;l._pixelRatio=p.pixelRatio,l._accessibilitySupport=p.accessibilitySupport,l._contentLeft=p.layoutInfo.contentLeft,l._contentWidth=p.layoutInfo.contentWidth,l._contentHeight=p.layoutInfo.contentHeight,l._scrollLeft=0,l._scrollTop=0,l._fontInfo=p.fontInfo,l._lineHeight=p.lineHeight,l._emptySelectionClipboard=p.emptySelectionClipboard,l._visibleTextArea=null,l._selections=[new a.Selection(1,1,1,1)],l._lastCopiedValue=null,l._lastCopiedValueIsFromEmptySelection=!1,l.textArea=d.createFastDomNode(document.createElement("textarea")),h.PartFingerprints.write(l.textArea,6),l.textArea.setClassName("inputarea"),l.textArea.setAttribute("wrap","off"),l.textArea.setAttribute("autocorrect","off"),l.textArea.setAttribute("autocapitalize","off"),l.textArea.setAttribute("autocomplete","off"),l.textArea.setAttribute("spellcheck","false"),l.textArea.setAttribute("aria-label",p.viewInfo.ariaLabel),l.textArea.setAttribute("role","textbox"),l.textArea.setAttribute("aria-multiline","true"),l.textArea.setAttribute("aria-haspopup","false"),l.textArea.setAttribute("aria-autocomplete","both"),l.textAreaCover=d.createFastDomNode(document.createElement("div")),l.textAreaCover.setPosition("absolute");var f={getLineCount:function(){return l._context.model.getLineCount()},getLineMaxColumn:function(e){return l._context.model.getLineMaxColumn(e)},getValueInRange:function(e,t){return l._context.model.getValueInRange(e,t)}},g={getPlainTextToCopy:function(){var e=l._context.model.getPlainTextToCopy(l._selections,l._emptySelectionClipboard);if(l._emptySelectionClipboard){i.isFirefox?l._lastCopiedValue=e.replace(/\r\n/g,"\n"):l._lastCopiedValue=e;var t=l._selections;l._lastCopiedValueIsFromEmptySelection=1===t.length&&t[0].isEmpty()}return e},getHTMLToCopy:function(){return l._context.model.getHTMLToCopy(l._selections,l._emptySelectionClipboard)},getScreenReaderContent:function(e){return i.isIPad?r.TextAreaState.EMPTY:1===l._accessibilitySupport?r.TextAreaState.EMPTY:r.PagedScreenReaderStrategy.fromEditorSelection(e,f,l._selections[0])}};return l._textAreaInput=l._register(new o.TextAreaInput(g,l.textArea)),l._register(l._textAreaInput.onKeyDown(function(e){l._viewController.emitKeyDown(e)})),l._register(l._textAreaInput.onKeyUp(function(e){l._viewController.emitKeyUp(e)})),l._register(l._textAreaInput.onPaste(function(e){var t=!1;l._emptySelectionClipboard&&(t=e.text===l._lastCopiedValue&&l._lastCopiedValueIsFromEmptySelection),l._viewController.paste("keyboard",e.text,t)})),l._register(l._textAreaInput.onCut(function(){l._viewController.cut("keyboard")})),l._register(l._textAreaInput.onType(function(e){e.replaceCharCnt?l._viewController.replacePreviousChar("keyboard",e.text,e.replaceCharCnt):l._viewController.type("keyboard",e.text)})),l._register(l._textAreaInput.onCompositionStart(function(){var e=l._selections[0].startLineNumber,t=l._selections[0].startColumn;l._context.privateViewEventBus.emit(new c.ViewRevealRangeRequestEvent(new s.Range(e,t,e,t),0,!0));var n=l._viewHelper.visibleRangeForPositionRelativeToEditor(e,t);n&&(l._visibleTextArea=new y(l._context.viewLayout.getVerticalOffsetForLineNumber(e),n.left,C?0:1),l._render()),l.textArea.setClassName("inputarea ime-input"),l._viewController.compositionStart("keyboard")})),l._register(l._textAreaInput.onCompositionUpdate(function(e){i.isEdgeOrIE?l._visibleTextArea=l._visibleTextArea.setWidth(0):l._visibleTextArea=l._visibleTextArea.setWidth(m(e.data,l._fontInfo)),l._render()})),l._register(l._textAreaInput.onCompositionEnd(function(){l._visibleTextArea=null,l._render(),l.textArea.setClassName("inputarea"),l._viewController.compositionEnd("keyboard")})),l._register(l._textAreaInput.onFocus(function(){l._context.privateViewEventBus.emit(new c.ViewFocusChangedEvent(!0))})),l._register(l._textAreaInput.onBlur(function(){l._context.privateViewEventBus.emit(new c.ViewFocusChangedEvent(!1))})),l}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){var t=this._context.configuration.editor;return e.fontInfo&&(this._fontInfo=t.fontInfo),e.viewInfo&&this.textArea.setAttribute("aria-label",t.viewInfo.ariaLabel),e.layoutInfo&&(this._contentLeft=t.layoutInfo.contentLeft,this._contentWidth=t.layoutInfo.contentWidth,this._contentHeight=t.layoutInfo.contentHeight),e.lineHeight&&(this._lineHeight=t.lineHeight),e.pixelRatio&&(this._pixelRatio=t.pixelRatio),e.accessibilitySupport&&(this._accessibilitySupport=t.accessibilitySupport,this._textAreaInput.writeScreenReaderContent("strategy changed")),e.emptySelectionClipboard&&(this._emptySelectionClipboard=t.emptySelectionClipboard),!0},t.prototype.onCursorStateChanged=function(e){return this._selections=e.selections.slice(0),this._textAreaInput.writeScreenReaderContent("selection changed"),!0},t.prototype.onDecorationsChanged=function(e){return!0},t.prototype.onFlushed=function(e){return!0},t.prototype.onLinesChanged=function(e){return!0},t.prototype.onLinesDeleted=function(e){return!0},t.prototype.onLinesInserted=function(e){return!0},t.prototype.onScrollChanged=function(e){return this._scrollLeft=e.scrollLeft,this._scrollTop=e.scrollTop,!0},t.prototype.onZonesChanged=function(e){return!0},t.prototype.isFocused=function(){return this._textAreaInput.isFocused()},t.prototype.focusTextArea=function(){this._textAreaInput.focusTextArea()},t.prototype.setAriaActiveDescendant=function(e){e?(this.textArea.setAttribute("role","combobox"),this.textArea.getAttribute("aria-activedescendant")!==e&&(this.textArea.setAttribute("aria-haspopup","true"),this.textArea.setAttribute("aria-activedescendant",e))):(this.textArea.setAttribute("role","textbox"),this.textArea.removeAttribute("aria-activedescendant"),this.textArea.removeAttribute("aria-haspopup"))},t.prototype.prepareRender=function(e){if(2===this._accessibilitySupport)this._primaryCursorVisibleRange=null;else{var t=new u.Position(this._selections[0].positionLineNumber,this._selections[0].positionColumn);this._primaryCursorVisibleRange=e.visibleRangeForPosition(t)}},t.prototype.render=function(e){this._textAreaInput.writeScreenReaderContent("render"),this._render()},t.prototype._render=function(){if(this._visibleTextArea)this._renderInsideEditor(this._visibleTextArea.top-this._scrollTop,this._contentLeft+this._visibleTextArea.left-this._scrollLeft,this._visibleTextArea.width,this._lineHeight,!0);else if(this._primaryCursorVisibleRange){var e=this._contentLeft+this._primaryCursorVisibleRange.left-this._scrollLeft;if(ethis._contentLeft+this._contentWidth)this._renderAtTopLeft();else{var t=this._context.viewLayout.getVerticalOffsetForLineNumber(this._selections[0].positionLineNumber)-this._scrollTop;t<0||t>this._contentHeight?this._renderAtTopLeft():this._renderInsideEditor(t,e,C?0:1,C?0:1,!1)}}else this._renderAtTopLeft()},t.prototype._renderInsideEditor=function(e,t,n,i,o){var r=this.textArea,s=this.textAreaCover;o?l.Configuration.applyFontInfo(r,this._fontInfo):(r.setFontSize(1),r.setLineHeight(this._fontInfo.lineHeight)),r.setTop(e),r.setLeft(t),r.setWidth(n),r.setHeight(i),s.setTop(0),s.setLeft(0),s.setWidth(0),s.setHeight(0)},t.prototype._renderAtTopLeft=function(){var e=this.textArea,t=this.textAreaCover;if(l.Configuration.applyFontInfo(e,this._fontInfo),e.setTop(0),e.setLeft(0),t.setTop(0),t.setLeft(0),C)return e.setWidth(0),e.setHeight(0),t.setWidth(0),void t.setHeight(0);e.setWidth(1),e.setHeight(1),t.setWidth(1),t.setHeight(1),this._context.configuration.editor.viewInfo.glyphMargin?t.setClassName("monaco-editor-background textAreaCover "+p.Margin.CLASS_NAME):this._context.configuration.editor.viewInfo.renderLineNumbers?t.setClassName("monaco-editor-background textAreaCover "+g.LineNumbersOverlay.CLASS_NAME):t.setClassName("monaco-editor-background textAreaCover")},t}(h.ViewPart);t.TextAreaHandler=b}),define(d[502],h([1,0,27,35,14,37,4,299]),function(e,t,n,i,o,r,s){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var a=function(e){function t(t){var i=e.call(this,t)||this;return i.domNode=n.createFastDomNode(document.createElement("div")),i.domNode.setAttribute("role","presentation"),i.domNode.setAttribute("aria-hidden","true"),i.domNode.setClassName("view-rulers"),i._renderedRulers=[],i._rulers=i._context.configuration.editor.viewInfo.rulers,i._height=i._context.configuration.editor.layoutInfo.contentHeight,i._typicalHalfwidthCharacterWidth=i._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth,i}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return!!(e.viewInfo||e.layoutInfo||e.fontInfo)&&(this._rulers=this._context.configuration.editor.viewInfo.rulers,this._height=this._context.configuration.editor.layoutInfo.contentHeight,this._typicalHalfwidthCharacterWidth=this._context.configuration.editor.fontInfo.typicalHalfwidthCharacterWidth,!0)},t.prototype.onScrollChanged=function(e){return e.scrollHeightChanged},t.prototype.prepareRender=function(e){},t.prototype._ensureRulersCount=function(){var e=this._renderedRulers.length,t=this._rulers.length;if(e!==t)if(e0;)(a=n.createFastDomNode(document.createElement("div"))).setClassName("view-ruler"),a.setWidth(i),this.domNode.appendChild(a),this._renderedRulers.push(a),o--;else for(var r=e-t;r>0;){var a=this._renderedRulers.pop();this.domNode.removeChild(a),r--}},t.prototype.render=function(e){this._ensureRulersCount();for(var t=0,n=this._rulers.length;tt.length)for(var a=this._secondaryCursors.length-t.length,r=0;r0){var _=e[r-1];m=0===_.originalEndLineNumber?_.originalStartLineNumber+1:_.originalEndLineNumber+1,v=0===_.modifiedEndLineNumber?_.modifiedStartLineNumber+1:_.modifiedEndLineNumber+1}var y=f-3+1,C=g-3+1;yL&&(M+=k=L-M,T+=k),T>x){var k=x-T;M+=k,T+=k}h[p++]=new S(b,M,w,T),i[o++]=new E(h)}for(var I=i[0].entries,D=[],O=0,r=1,s=i.length;rg)&&(g=b),0!==w&&(0===m||wv)&&(v=S)}var E=document.createElement("div");E.className="diff-review-row";var L=document.createElement("div");L.className="diff-review-cell diff-review-summary",L.appendChild(document.createTextNode(d+1+"/"+this._diffs.length+": @@ -"+f+","+(g-f+1)+" +"+m+","+(v-m+1)+" @@")),E.setAttribute("data-line",String(m)),E.setAttribute("aria-label",n.localize(1,null,d+1,this._diffs.length,f,g-f+1,m,v-m+1)),E.appendChild(L),E.setAttribute("role","listitem"),p.appendChild(E);for(var x=m,_=0,y=h.length;_=i)s.push(a),o++;else{var u=t[n],l=u.compareTo(a);l<0?n++:l>0?(s.push(a),o++):(s.push(u),n++,o++)}}this._zones=s},e.prototype.setLineHeight=function(e){return this._lineHeight!==e&&(this._lineHeight=e,this._colorZonesInvalid=!0,!0)},e.prototype.setPixelRatio=function(e){this._pixelRatio=e,this._colorZonesInvalid=!0},e.prototype.getDOMWidth=function(){return this._domWidth},e.prototype.getCanvasWidth=function(){return this._domWidth*this._pixelRatio},e.prototype.setDOMWidth=function(e){return this._domWidth!==e&&(this._domWidth=e,this._colorZonesInvalid=!0,!0)},e.prototype.getDOMHeight=function(){return this._domHeight},e.prototype.getCanvasHeight=function(){return this._domHeight*this._pixelRatio},e.prototype.setDOMHeight=function(e){return this._domHeight!==e&&(this._domHeight=e,this._colorZonesInvalid=!0,!0)},e.prototype.getOuterHeight=function(){return this._outerHeight},e.prototype.setOuterHeight=function(e){return this._outerHeight!==e&&(this._outerHeight=e,this._colorZonesInvalid=!0,!0)},e.prototype.setMaximumHeight=function(e){return this._maximumHeight!==e&&(this._maximumHeight=e,this._colorZonesInvalid=!0,!0)},e.prototype.setMinimumHeight=function(e){return this._minimumHeight!==e&&(this._minimumHeight=e,this._colorZonesInvalid=!0,!0)},e.prototype.setThemeType=function(e){return this._themeType!==e&&(this._themeType=e,this._colorZonesInvalid=!0,!0)},e.prototype.resolveColorZones=function(){for(var e=this._colorZonesInvalid,t=Math.floor(this._lineHeight),n=Math.floor(this.getCanvasHeight()),i=Math.floor(this._maximumHeight*this._pixelRatio),o=Math.floor(this._minimumHeight*this._pixelRatio),r=this._themeType,s=n/Math.floor(this._outerHeight),a=[],u=0,l=this._zones.length;u_)for(var y=c.startLineNumber;y<=c.endLineNumber;y++)v=(m=Math.floor(this._getVerticalOffsetForLine(y)))+t,m=Math.floor(m*s),v=Math.floor(v*s),f.push(this.createZone(n,m,v,o,i,c.getColor(r),c.position));else f.push(this.createZone(n,m,v,o,_,c.getColor(r),c.position))}c.setColorZones(f);for(var h=0,p=f.length;hr/2&&(l=r/2),le&&(u=e-l);var c=this._color2Id[s];return c||(c=++this._lastAssignedId,this._color2Id[s]=c,this._id2Color[c]=s),new i(u-l,u+l,c,a)},e}();t.OverviewZoneManager=r}),define(d[190],h([1,0,27,20,140,14]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var s=function(){function e(e,t,i,s,a,u,l,c){this._canvasLeftOffset=e,this._domNode=n.createFastDomNode(document.createElement("canvas")),this._domNode.setClassName(t),this._domNode.setPosition("absolute"),this._domNode.setLayerHinting(!0),this._lanesCount=3,this._background=null,this._zoneManager=new o.OverviewZoneManager(c),this._zoneManager.setMinimumHeight(u),this._zoneManager.setMaximumHeight(l),this._zoneManager.setThemeType(r.LIGHT),this._zoneManager.setDOMWidth(0),this._zoneManager.setDOMHeight(0),this._zoneManager.setOuterHeight(i),this._zoneManager.setLineHeight(s),this._zoneManager.setPixelRatio(a)}return e.prototype.dispose=function(){this._zoneManager=null},e.prototype.setLayout=function(e,t){this._domNode.setTop(e.top),this._domNode.setRight(e.right);var n=!1;n=this._zoneManager.setDOMWidth(e.width)||n,(n=this._zoneManager.setDOMHeight(e.height)||n)&&(this._domNode.setWidth(this._zoneManager.getDOMWidth()),this._domNode.setHeight(this._zoneManager.getDOMHeight()),this._domNode.domNode.width=this._zoneManager.getCanvasWidth(),this._domNode.domNode.height=this._zoneManager.getCanvasHeight(),t&&this.render(!0))},e.prototype.getLanesCount=function(){return this._lanesCount},e.prototype.setLanesCount=function(e,t){this._lanesCount=e,t&&this.render(!0)},e.prototype.setThemeType=function(e,t){this._zoneManager.setThemeType(e),t&&this.render(!0)},e.prototype.setUseBackground=function(e,t){this._background=e,t&&this.render(!0)},e.prototype.getDomNode=function(){return this._domNode.domNode},e.prototype.getPixelWidth=function(){return this._zoneManager.getCanvasWidth()},e.prototype.getPixelHeight=function(){return this._zoneManager.getCanvasHeight()},e.prototype.setScrollHeight=function(e,t){this._zoneManager.setOuterHeight(e),t&&this.render(!0)},e.prototype.setLineHeight=function(e,t){this._zoneManager.setLineHeight(e),t&&this.render(!0)},e.prototype.setPixelRatio=function(e,t){this._zoneManager.setPixelRatio(e),this._domNode.setWidth(this._zoneManager.getDOMWidth()),this._domNode.setHeight(this._zoneManager.getDOMHeight()),this._domNode.domNode.width=this._zoneManager.getCanvasWidth(),this._domNode.domNode.height=this._zoneManager.getCanvasHeight(),t&&this.render(!0)},e.prototype.setZones=function(e,t){this._zoneManager.setZones(e),t&&this.render(!1)},e.prototype.render=function(e){if(0===this._zoneManager.getOuterHeight())return!1;var t=this._zoneManager.getCanvasWidth(),n=this._zoneManager.getCanvasHeight(),i=this._zoneManager.resolveColorZones(),o=this._zoneManager.getId2Color(),r=this._domNode.domNode.getContext("2d");if(null===this._background?r.clearRect(0,0,t,n):(r.fillStyle=this._background.toRGBHex(),r.fillRect(0,0,t,n)),i.length>0){var s=t-this._canvasLeftOffset;this._lanesCount>=3?this._renderThreeLanes(r,i,o,s):2===this._lanesCount?this._renderTwoLanes(r,i,o,s):1===this._lanesCount&&this._renderOneLane(r,i,o,s)}return!0},e.prototype._renderOneLane=function(e,t,n,o){this._renderVerticalPatch(e,t,n,i.OverviewRulerLane.Left|i.OverviewRulerLane.Center|i.OverviewRulerLane.Right,this._canvasLeftOffset,o)},e.prototype._renderTwoLanes=function(e,t,n,o){var r=Math.floor(o/2),s=o-r,a=this._canvasLeftOffset,u=this._canvasLeftOffset+r;this._renderVerticalPatch(e,t,n,i.OverviewRulerLane.Left|i.OverviewRulerLane.Center,a,r),this._renderVerticalPatch(e,t,n,i.OverviewRulerLane.Right,u,s)},e.prototype._renderThreeLanes=function(e,t,n,o){var r=Math.floor(o/3),s=Math.floor(o/3),a=o-r-s,u=this._canvasLeftOffset,l=this._canvasLeftOffset+r,c=this._canvasLeftOffset+r+a;this._renderVerticalPatch(e,t,n,i.OverviewRulerLane.Left,u,r),this._renderVerticalPatch(e,t,n,i.OverviewRulerLane.Center,l,a),this._renderVerticalPatch(e,t,n,i.OverviewRulerLane.Right,c,s)},e.prototype._renderVerticalPatch=function(e,t,n,i,o,r){for(var s=0,a=0,u=0,l=0,c=t.length;l=p?u=Math.max(u,f):(e.fillRect(o,a,r,u-a),a=p,u=f)}}e.fillRect(o,a,r,u-a)},e}();t.OverviewRulerImpl=s}),define(d[507],h([1,0,20,35,190,17,140,37,32]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l=function(e){function t(n){var i=e.call(this,n)||this;return i._overviewRuler=new o.OverviewRulerImpl(1,"decorationsOverviewRuler",i._context.viewLayout.getScrollHeight(),i._context.configuration.editor.lineHeight,i._context.configuration.editor.pixelRatio,t.MIN_DECORATION_HEIGHT,t.MAX_DECORATION_HEIGHT,function(e){return i._context.viewLayout.getVerticalOffsetForLineNumber(e)}),i._overviewRuler.setLanesCount(i._context.configuration.editor.viewInfo.overviewRulerLanes,!1),i._overviewRuler.setLayout(i._context.configuration.editor.layoutInfo.overviewRuler,!1),i._renderBorder=i._context.configuration.editor.viewInfo.overviewRulerBorder,i._updateColors(),i._updateBackground(!1),i._tokensColorTrackerListener=r.TokenizationRegistry.onDidChange(function(e){e.changedColorMap&&i._updateBackground(!0)}),i._shouldUpdateDecorations=!0,i._zonesFromDecorations=[],i._shouldUpdateCursorPosition=!0,i._hideCursor=i._context.configuration.editor.viewInfo.hideCursorInOverviewRuler,i._zonesFromCursors=[],i._cursorPositions=[],i}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this),this._overviewRuler.dispose(),this._tokensColorTrackerListener.dispose()},t.prototype._updateBackground=function(e){var t=this._context.configuration.editor.viewInfo.minimap.enabled;this._overviewRuler.setUseBackground(t?r.TokenizationRegistry.getDefaultBackground():null,e)},t.prototype.onConfigurationChanged=function(e){var t=this._overviewRuler.getLanesCount(),n=this._context.configuration.editor.viewInfo.overviewRulerLanes;return t!==n&&this._overviewRuler.setLanesCount(n,!1),e.lineHeight&&this._overviewRuler.setLineHeight(this._context.configuration.editor.lineHeight,!1),e.pixelRatio&&this._overviewRuler.setPixelRatio(this._context.configuration.editor.pixelRatio,!1),e.viewInfo&&(this._renderBorder=this._context.configuration.editor.viewInfo.overviewRulerBorder,this._hideCursor=this._context.configuration.editor.viewInfo.hideCursorInOverviewRuler,this._shouldUpdateCursorPosition=!0,this._updateBackground(!1)),e.layoutInfo&&this._overviewRuler.setLayout(this._context.configuration.editor.layoutInfo.overviewRuler,!1),!0},t.prototype.onCursorStateChanged=function(e){this._shouldUpdateCursorPosition=!0,this._cursorPositions=[];for(var t=0,n=e.selections.length;t0&&(this._zonesFromDecorations.length>0||this._zonesFromCursors.length>0)){var n=this._overviewRuler.getDomNode().getContext("2d");n.beginPath(),n.lineWidth=1,n.strokeStyle=this._borderColor,n.moveTo(0,0),n.lineTo(0,this._overviewRuler.getPixelHeight()),n.stroke(),n.moveTo(0,0),n.lineTo(this._overviewRuler.getPixelWidth(),0),n.stroke()}},t.MIN_DECORATION_HEIGHT=6,t.MAX_DECORATION_HEIGHT=60,t}(i.ViewPart);t.DecorationsOverviewRuler=l}),define(d[508],h([1,0,82,190]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var o=function(e){function t(t,n,o,r){var s=e.call(this)||this;return s._context=t,s._overviewRuler=new i.OverviewRulerImpl(0,n,s._context.viewLayout.getScrollHeight(),s._context.configuration.editor.lineHeight,s._context.configuration.editor.pixelRatio,o,r,function(e){return s._context.viewLayout.getVerticalOffsetForLineNumber(e)}),s._context.addEventHandler(s),s}return f(t,e),t.prototype.dispose=function(){this._context.removeEventHandler(this),this._overviewRuler.dispose(),e.prototype.dispose.call(this)},t.prototype.onConfigurationChanged=function(e){return e.lineHeight&&this._overviewRuler.setLineHeight(this._context.configuration.editor.lineHeight,!0),e.pixelRatio&&this._overviewRuler.setPixelRatio(this._context.configuration.editor.pixelRatio,!0),!0},t.prototype.onFlushed=function(e){return!0},t.prototype.onScrollChanged=function(t){return this._overviewRuler.setScrollHeight(t.scrollHeight,!0),e.prototype.onScrollChanged.call(this,t)||t.scrollHeightChanged},t.prototype.onZonesChanged=function(e){return!0},t.prototype.getDomNode=function(){return this._overviewRuler.getDomNode()},t.prototype.setLayout=function(e){this._overviewRuler.setLayout(e,!0)},t.prototype.setZones=function(e){this._overviewRuler.setZones(e,!0)},t}(n.ViewEventHandler);t.OverviewRuler=o}),define(d[509],h([1,0,10,4,27,2,82,501,490,449,217,443,230,497,498,231,113,188,499,492,182,233,235,236,507,508,502,494,495,503,237,35,216,78,491,221,486,493,57,14]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_,y,C,b,w,S,E,L,x,N,M,T,k,I,D,O,R,P,A,F,W,B,V){"use strict";function H(e){try{return e()}catch(e){n.onUnexpectedError(e)}}function z(e,t){try{return e(t)}catch(e){n.onUnexpectedError(e)}}Object.defineProperty(t,"__esModule",{value:!0});var K=function(e){function t(t,n,i,o,r,s){var d=e.call(this)||this;d._isDisposed=!1,d._cursor=r,d._renderAnimationFrame=null,d.outgoingEvents=new P.ViewOutgoingEvents(o);var h=new l.ViewController(n,o,s,d.outgoingEvents,t);return d.eventDispatcher=new c.ViewEventDispatcher(function(e){return d._renderOnce(e)}),d.eventDispatcher.addEventHandler(d),d._context=new O.ViewContext(n,i.getTheme(),o,d.eventDispatcher),d._register(i.onThemeChange(function(e){d._context.theme=e,d.eventDispatcher.emit(new B.ViewThemeChangedEvent),d.render(!0,!1)})),d.viewParts=[],d._textAreaHandler=new a.TextAreaHandler(d._context,h,d.createTextAreaHandlerHelper()),d.viewParts.push(d._textAreaHandler),d.createViewParts(),d._setLayout(),d.pointerHandler=new u.PointerHandler(d._context,h,d.createPointerHandlerHelper()),d._register(o.addEventListener(function(e){d.eventDispatcher.emitMany(e)})),d._register(d._cursor.addEventListener(function(e){d.eventDispatcher.emitMany(e)})),d}return f(t,e),t.prototype.createViewParts=function(){this.linesContent=o.createFastDomNode(document.createElement("div")),this.linesContent.setClassName("lines-content monaco-editor-background"),this.linesContent.setPosition("absolute"),this.domNode=o.createFastDomNode(document.createElement("div")),this.domNode.setClassName(this.getEditorClassName()),this.overflowGuardContainer=o.createFastDomNode(document.createElement("div")),D.PartFingerprints.write(this.overflowGuardContainer,3),this.overflowGuardContainer.setClassName("overflow-guard"),this._scrollbar=new F.EditorScrollbar(this._context,this.linesContent,this.domNode,this.overflowGuardContainer),this.viewParts.push(this._scrollbar),this.viewLines=new C.ViewLines(this._context,this.linesContent),this.viewZones=new I.ViewZones(this._context),this.viewParts.push(this.viewZones);var e=new L.DecorationsOverviewRuler(this._context);this.viewParts.push(e);var t=new M.ScrollDecorationViewPart(this._context);this.viewParts.push(t);var n=new d.ContentViewOverlays(this._context);this.viewParts.push(n),n.addDynamicOverlay(new p.CurrentLineHighlightOverlay(this._context)),n.addDynamicOverlay(new T.SelectionsOverlay(this._context)),n.addDynamicOverlay(new m.DecorationsOverlay(this._context)),n.addDynamicOverlay(new y.IndentGuidesOverlay(this._context));var i=new d.MarginViewOverlays(this._context);this.viewParts.push(i),i.addDynamicOverlay(new g.CurrentLineMarginHighlightOverlay(this._context)),i.addDynamicOverlay(new v.GlyphMarginOverlay(this._context)),i.addDynamicOverlay(new S.MarginViewLineDecorationsOverlay(this._context)),i.addDynamicOverlay(new w.LinesDecorationsOverlay(this._context)),i.addDynamicOverlay(new _.LineNumbersOverlay(this._context));var r=new b.Margin(this._context);r.getDomNode().appendChild(this.viewZones.marginDomNode),r.getDomNode().appendChild(i.getDomNode()),this.viewParts.push(r),this.contentWidgets=new h.ViewContentWidgets(this._context,this.domNode),this.viewParts.push(this.contentWidgets),this.viewCursors=new k.ViewCursors(this._context),this.viewParts.push(this.viewCursors),this.overlayWidgets=new E.ViewOverlayWidgets(this._context),this.viewParts.push(this.overlayWidgets);var s=new N.Rulers(this._context);this.viewParts.push(s);var a=new W.Minimap(this._context);if(this.viewParts.push(a),e){var u=this._scrollbar.getOverviewRulerLayoutInfo();u.parent.insertBefore(e.getDomNode(),u.insertBefore)}this.linesContent.appendChild(n.getDomNode()),this.linesContent.appendChild(s.domNode),this.linesContent.appendChild(this.viewZones.domNode),this.linesContent.appendChild(this.viewLines.getDomNode()),this.linesContent.appendChild(this.contentWidgets.domNode),this.linesContent.appendChild(this.viewCursors.getDomNode()),this.overflowGuardContainer.appendChild(r.getDomNode()),this.overflowGuardContainer.appendChild(this._scrollbar.getDomNode()),this.overflowGuardContainer.appendChild(t.getDomNode()),this.overflowGuardContainer.appendChild(this._textAreaHandler.textArea),this.overflowGuardContainer.appendChild(this._textAreaHandler.textAreaCover),this.overflowGuardContainer.appendChild(this.overlayWidgets.getDomNode()),this.overflowGuardContainer.appendChild(a.getDomNode()),this.domNode.appendChild(this.overflowGuardContainer),this.domNode.appendChild(this.contentWidgets.overflowingContentWidgetsDomNode)},t.prototype._flushAccumulatedAndRenderNow=function(){this._renderNow()},t.prototype.createPointerHandlerHelper=function(){var e=this;return{viewDomNode:this.domNode.domNode,linesContentDomNode:this.linesContent.domNode,focusTextArea:function(){e.focus()},getLastViewCursorsRenderData:function(){return e.viewCursors.getLastRenderData()||[]},shouldSuppressMouseDownOnViewZone:function(t){return e.viewZones.shouldSuppressMouseDownOnViewZone(t)},shouldSuppressMouseDownOnWidget:function(t){return e.contentWidgets.shouldSuppressMouseDownOnWidget(t)},getPositionFromDOMInfo:function(t,n){return e._flushAccumulatedAndRenderNow(),e.viewLines.getPositionFromDOMInfo(t,n)},visibleRangeForPosition2:function(t,n){e._flushAccumulatedAndRenderNow();var i=e.viewLines.visibleRangesForRange2(new r.Range(t,n,t,n));return i?i[0]:null},getLineWidth:function(t){return e._flushAccumulatedAndRenderNow(),e.viewLines.getLineWidth(t)}}},t.prototype.createTextAreaHandlerHelper=function(){var e=this;return{visibleRangeForPositionRelativeToEditor:function(t,n){e._flushAccumulatedAndRenderNow();var i=e.viewLines.visibleRangesForRange2(new r.Range(t,n,t,n));return i?i[0]:null}}},t.prototype._setLayout=function(){var e=this._context.configuration.editor.layoutInfo;this.domNode.setWidth(e.width),this.domNode.setHeight(e.height),this.overflowGuardContainer.setWidth(e.width),this.overflowGuardContainer.setHeight(e.height),this.linesContent.setWidth(1e6),this.linesContent.setHeight(1e6)},t.prototype.getEditorClassName=function(){return this._context.configuration.editor.editorClassName+" "+V.getThemeTypeSelector(this._context.theme.type)},t.prototype.onConfigurationChanged=function(e){return e.editorClassName&&this.domNode.setClassName(this.getEditorClassName()),e.layoutInfo&&this._setLayout(),!1},t.prototype.onFocusChanged=function(e){return this.domNode.toggleClassName("focused",e.isFocused),e.isFocused?this.outgoingEvents.emitViewFocusGained():this.outgoingEvents.emitViewFocusLost(),!1},t.prototype.onScrollChanged=function(e){return this.outgoingEvents.emitScrollChanged(e),!1},t.prototype.onThemeChanged=function(e){return this.domNode.setClassName(this.getEditorClassName()),!1},t.prototype.dispose=function(){this._isDisposed=!0,null!==this._renderAnimationFrame&&(this._renderAnimationFrame.dispose(),this._renderAnimationFrame=null),this.eventDispatcher.removeEventHandler(this),this.outgoingEvents.dispose(),this.pointerHandler.dispose(),this.viewLines.dispose();for(var t=0,n=this.viewParts.length;t");m.registerThemingParticipant(function(e,t){var n=e.getColor(_.editorErrorBorder);n&&t.addRule(".monaco-editor .redsquiggly { border-bottom: 4px double "+n+"; }");var i=e.getColor(_.editorErrorForeground);i&&t.addRule('.monaco-editor .redsquiggly { background: url("data:image/svg+xml,'+C(i)+'") repeat-x bottom left; }');var o=e.getColor(_.editorWarningBorder);o&&t.addRule(".monaco-editor .greensquiggly { border-bottom: 4px double "+o+"; }");var r=e.getColor(_.editorWarningForeground);r&&t.addRule('.monaco-editor .greensquiggly { background: url("data:image/svg+xml;utf8,'+C(r)+'") repeat-x bottom left; }')})}),define(d[141],h([1,0,16,31,19,42,191,13,30,14]),function(e,t,n,i,o,r,s,a,u,l){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var c=function(e){function t(t,n,i,o,r,s,a){return e.call(this,t,n,i,o,r,s,a)||this}return f(t,e),t.prototype._getContributions=function(){return[].concat(u.EditorBrowserRegistry.getEditorContributions()).concat(a.CommonEditorRegistry.getEditorContributions())},t.prototype._getActions=function(){return a.CommonEditorRegistry.getEditorActions()},t=v([y(2,n.IInstantiationService),y(3,r.ICodeEditorService),y(4,i.ICommandService),y(5,o.IContextKeyService),y(6,l.IThemeService)],t)}(s.CodeEditorWidget);t.CodeEditor=c}),define(d[193],h([1,0,302,18,3,26,4,36,27,99,16,19,42,2,20,61,138,106,141,90,66,86,84,11,49,14,23,140,34,504,50,308]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_,C,b,w,S,E,L,x,N,M,T,k,I,D,O){"use strict";function R(e,t,n,i,o){return{range:new p.Range(e,t,n,i),options:o}}function P(e){return e.modifiedEndLineNumber>0}function A(e){return e.originalEndLineNumber>0}function F(){var e=document.createElement("div");return e.className="diagonal-fill",e}Object.defineProperty(t,"__esModule",{value:!0});var W=function(){function e(){this._zones=[],this._zonesMap={},this._decorations=[]}return e.prototype.getForeignViewZones=function(e){var t=this;return e.filter(function(e){return!t._zonesMap[String(e.id)]})},e.prototype.clean=function(e){var t=this;this._zones.length>0&&e.changeViewZones(function(e){for(var n=0,i=t._zones.length;n0&&e.changeDecorations(function(e){e.deltaDecorations(t._decorations,[])}),this._decorations=[]},e.prototype.apply=function(e,t,n){var i=this;e.changeViewZones(function(e){for(var t=0,o=i._zones.length;t0?o/n:0;return{height:Math.max(0,Math.floor(e.contentHeight*r)),top:Math.floor(t*r)}},t.prototype._createDataSource=function(){var e=this;return{getWidth:function(){return e._width},getHeight:function(){return e._height-e._reviewHeight},getContainerDomNode:function(){return e._containerDomElement},relayoutEditors:function(){e._doLayout()},getOriginalEditor:function(){return e.originalEditor},getModifiedEditor:function(){return e.modifiedEditor}}},t.prototype._setStrategy=function(e){this._strategy&&this._strategy.dispose(),this._strategy=e,e.applyColors(this._themeService.getTheme()),this._lineChanges&&this._updateDecorations(),this._measureDomElement(!0)},t.prototype._getLineChangeAtOrBeforeLineNumber=function(e,t){if(0===this._lineChanges.length||e=s?n=o+1:(n=o,i=o)}return this._lineChanges[n]},t.prototype._getEquivalentLineForOriginalLineNumber=function(e){var t=this._getLineChangeAtOrBeforeLineNumber(e,function(e){return e.originalStartLineNumber});if(!t)return e;var n=t.originalStartLineNumber+(t.originalEndLineNumber>0?-1:0),i=t.modifiedStartLineNumber+(t.modifiedEndLineNumber>0?-1:0),o=t.originalEndLineNumber>0?t.originalEndLineNumber-t.originalStartLineNumber+1:0,r=t.modifiedEndLineNumber>0?t.modifiedEndLineNumber-t.modifiedStartLineNumber+1:0,s=e-n;return s<=o?i+Math.min(s,r):i+r-o+s},t.prototype._getEquivalentLineForModifiedLineNumber=function(e){var t=this._getLineChangeAtOrBeforeLineNumber(e,function(e){return e.modifiedStartLineNumber});if(!t)return e;var n=t.originalStartLineNumber+(t.originalEndLineNumber>0?-1:0),i=t.modifiedStartLineNumber+(t.modifiedEndLineNumber>0?-1:0),o=t.originalEndLineNumber>0?t.originalEndLineNumber-t.originalStartLineNumber+1:0,r=t.modifiedEndLineNumber>0?t.modifiedEndLineNumber-t.modifiedStartLineNumber+1:0,s=e-i;return s<=r?n+Math.min(s,o):n+o-r+s},t.prototype.getDiffLineInformationForOriginal=function(e){return this._lineChanges?{equivalentLineNumber:this._getEquivalentLineForOriginalLineNumber(e)}:null},t.prototype.getDiffLineInformationForModified=function(e){return this._lineChanges?{equivalentLineNumber:this._getEquivalentLineForModifiedLineNumber(e)}:null},t.ONE_OVERVIEW_WIDTH=15,t.ENTIRE_DIFF_OVERVIEW_WIDTH=30,t.UPDATE_DIFF_DECORATIONS_DELAY=200,t=v([y(2,m.IEditorWorkerService),y(3,d.IContextKeyService),y(4,c.IInstantiationService),y(5,h.ICodeEditorService),y(6,M.IThemeService),y(7,O.IMessageService)],t)}(o.Disposable);t.DiffEditorWidget=V;var H=function(e){function t(t){var n=e.call(this)||this;return n._dataSource=t,n}return f(t,e),t.prototype.applyColors=function(e){var t=(e.getColor(T.diffInserted)||T.defaultInsertColor).transparent(2),n=(e.getColor(T.diffRemoved)||T.defaultRemoveColor).transparent(2),i=!t.equals(this._insertColor)||!n.equals(this._removeColor);return this._insertColor=t,this._removeColor=n,i},t.prototype.getEditorsDiffDecorations=function(e,t,n,i,o,r,s){o=o.sort(function(e,t){return e.afterLineNumber-t.afterLineNumber}),i=i.sort(function(e,t){return e.afterLineNumber-t.afterLineNumber});var a=this._getViewZones(e,i,o,r,s,n),u=this._getOriginalEditorDecorations(e,t,n,r,s),l=this._getModifiedEditorDecorations(e,t,n,r,s);return{original:{decorations:u.decorations,overviewZones:u.overviewZones,zones:a.original},modified:{decorations:l.decorations,overviewZones:l.overviewZones,zones:a.modified}}},t.prototype._getViewZones=function(e,t,n,i,o,r){return null},t.prototype._getOriginalEditorDecorations=function(e,t,n,i,o){return null},t.prototype._getModifiedEditorDecorations=function(e,t,n,i,o){return null},t}(o.Disposable),z=function(){function e(e){this._source=e,this._index=-1,this.advance()}return e.prototype.advance=function(){this._index++,this._index0){var n=e[e.length-1];if(n.afterLineNumber===t.afterLineNumber&&null===n.domNode)return void(n.heightInLines+=t.heightInLines)}e.push(t)},l=new z(this.modifiedForeignVZ),c=new z(this.originalForeignVZ),d=0,h=this.lineChanges.length;d<=h;d++){var p=d0?-1:0),o=p.modifiedStartLineNumber+(p.modifiedEndLineNumber>0?-1:0),n=p.originalEndLineNumber>0?p.originalEndLineNumber-p.originalStartLineNumber+1:0,t=p.modifiedEndLineNumber>0?p.modifiedEndLineNumber-p.modifiedStartLineNumber+1:0,r=Math.max(p.originalStartLineNumber,p.originalEndLineNumber),s=Math.max(p.modifiedStartLineNumber,p.modifiedEndLineNumber)):(r=i+=1e7+n,s=o+=1e7+t);for(var f=[],g=[];l.current&&l.current.afterLineNumber<=s;){m=void 0;m=l.current.afterLineNumber<=o?i-o+l.current.afterLineNumber:r,f.push({afterLineNumber:m,heightInLines:l.current.heightInLines,domNode:null}),l.advance()}for(;c.current&&c.current.afterLineNumber<=r;){var m=void 0;m=c.current.afterLineNumber<=i?o-i+c.current.afterLineNumber:s,g.push({afterLineNumber:m,heightInLines:c.current.heightInLines,domNode:null}),c.advance()}if(null!==p&&P(p)&&(v=this._produceOriginalFromDiff(p,n,t))&&f.push(v),null!==p&&A(p)){var v=this._produceModifiedFromDiff(p,n,t);v&&g.push(v)}var _=0,y=0;for(f=f.sort(a),g=g.sort(a);_=b.heightInLines?(C.heightInLines-=b.heightInLines,y++):(b.heightInLines-=C.heightInLines,_++)}for(;_2*t.MINIMUM_EDITOR_WIDTH?(in-t.MINIMUM_EDITOR_WIDTH&&(i=n-t.MINIMUM_EDITOR_WIDTH)):i=o,this._sashPosition!==i&&(this._sashPosition=i,this._sash.layout()),this._sashPosition},t.prototype.onSashDragStart=function(){this._startSashPosition=this._sashPosition},t.prototype.onSashDrag=function(e){var t=this._dataSource.getWidth()-V.ENTIRE_DIFF_OVERVIEW_WIDTH,n=this.layout((this._startSashPosition+(e.currentX-e.startX))/t);this._sashRatio=n/t,this._dataSource.relayoutEditors()},t.prototype.onSashDragEnd=function(){this._sash.layout()},t.prototype.onSashReset=function(){this._sashRatio=.5,this._dataSource.relayoutEditors(),this._sash.layout()},t.prototype.getVerticalSashTop=function(e){return 0},t.prototype.getVerticalSashLeft=function(e){return this._sashPosition},t.prototype.getVerticalSashHeight=function(e){return this._dataSource.getHeight()},t.prototype._getViewZones=function(e,t,n,i,o){return new q(e,t,n).getViewZones()},t.prototype._getOriginalEditorDecorations=function(e,t,n,i,o){for(var r={decorations:[],overviewZones:[]},s=i.getModel(),a=0,u=e.length;at?{afterLineNumber:Math.max(e.originalStartLineNumber,e.originalEndLineNumber),heightInLines:n-t,domNode:null}:null},t.prototype._produceModifiedFromDiff=function(e,t,n){return t>n?{afterLineNumber:Math.max(e.modifiedStartLineNumber,e.modifiedEndLineNumber),heightInLines:t-n,domNode:null}:null},t}(K),G=function(e){function t(t,n){var i=e.call(this,t)||this;return i.decorationsLeft=t.getOriginalEditor().getLayoutInfo().decorationsLeft,i._register(t.getOriginalEditor().onDidLayoutChange(function(e){i.decorationsLeft!==e.decorationsLeft&&(i.decorationsLeft=e.decorationsLeft,t.relayoutEditors())})),i}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype.setEnableSplitViewResizing=function(e){},t.prototype._getViewZones=function(e,t,n,i,o,r){return new Y(e,t,n,i,o,r).getViewZones()},t.prototype._getOriginalEditorDecorations=function(e,t,n,i,o){for(var r={decorations:[],overviewZones:[]},s=0,a=e.length;s'])}var f=document.createElement("div");f.className="view-lines line-delete",f.innerHTML=a.join(""),S.Configuration.applyFontInfoSlow(f,this.modifiedEditorConfiguration.fontInfo);var g=document.createElement("div");return g.className="inline-deleted-margin-view-zone",g.innerHTML=u.join(""),S.Configuration.applyFontInfoSlow(g,this.modifiedEditorConfiguration.fontInfo),{shouldNotShrink:!0,afterLineNumber:0===e.modifiedEndLineNumber?e.modifiedStartLineNumber:e.modifiedStartLineNumber-1,heightInLines:t,domNode:f,marginDomNode:g}},t.prototype.renderOriginalLine=function(e,t,n,i,o,r){var s=t.getLineContent(o),a=_.LineDecoration.filter(r,o,1,s.length+1),u=C.renderViewLine(new C.RenderLineInput(n.fontInfo.isMonospace&&!n.viewInfo.disableMonospaceOptimizations,s,t.mightContainRTL(),0,[new w.ViewLineToken(s.length,16793600)],a,i,n.fontInfo.spaceWidth,n.viewInfo.stopRenderingLineAfter,n.viewInfo.renderWhitespace,n.viewInfo.renderControlCharacters,n.viewInfo.fontLigatures)),l=[];return l.push('
    '),(l=l.concat(u.html)).push("
    "),l},t}(K);M.registerThemingParticipant(function(e,t){var n=e.getColor(T.diffInserted);n&&(t.addRule(".monaco-editor .line-insert, .monaco-editor .char-insert { background-color: "+n+"; }"),t.addRule(".monaco-diff-editor .line-insert, .monaco-diff-editor .char-insert { background-color: "+n+"; }"),t.addRule(".monaco-editor .inline-added-margin-view-zone { background-color: "+n+"; }"));var i=e.getColor(T.diffRemoved);i&&(t.addRule(".monaco-editor .line-delete, .monaco-editor .char-delete { background-color: "+i+"; }"),t.addRule(".monaco-diff-editor .line-delete, .monaco-diff-editor .char-delete { background-color: "+i+"; }"),t.addRule(".monaco-editor .inline-deleted-margin-view-zone { background-color: "+i+"; }"));var o=e.getColor(T.diffInsertedOutline);o&&t.addRule(".monaco-editor .line-insert, .monaco-editor .char-insert { border: 1px dashed "+o+"; }");var r=e.getColor(T.diffRemovedOutline);r&&t.addRule(".monaco-editor .line-delete, .monaco-editor .char-delete { border: 1px dashed "+r+"; }");var s=e.getColor(T.scrollbarShadow);s&&t.addRule(".monaco-diff-editor.side-by-side .editor.modified { box-shadow: -6px 0 5px -5px "+s+"; }")})}),define(d[194],h([1,0,26,16,31,19,42,141,14]),function(e,t,n,i,o,r,s,a,u){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var l=function(e){function t(t,n,i,o,r,s,a,u){var l=e.call(this,t,i.getRawConfiguration(),o,r,s,a,u)||this;return l._parentEditor=i,l._overwriteOptions=n,e.prototype.updateOptions.call(l,l._overwriteOptions),l._register(i.onDidChangeConfiguration(function(e){return l._onParentConfigurationChanged(e)})),l}return f(t,e),t.prototype.getParentEditor=function(){return this._parentEditor},t.prototype._onParentConfigurationChanged=function(t){e.prototype.updateOptions.call(this,this._parentEditor.getRawConfiguration()),e.prototype.updateOptions.call(this,this._overwriteOptions)},t.prototype.updateOptions=function(t){n.mixin(this._overwriteOptions,t,!0),e.prototype.updateOptions.call(this,this._overwriteOptions)},t=v([y(3,i.IInstantiationService),y(4,s.ICodeEditorService),y(5,o.ICommandService),y(6,r.IContextKeyService),y(7,u.IThemeService)],t)}(a.CodeEditor);t.EmbeddedCodeEditorWidget=l}),define(d[514],h([1,0,341,3,12,18,20,13,21,14,37,34]),function(e,t,n,i,o,r,s,a,u,l,c,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0});!function(e){function t(){return e.call(this,{id:"editor.action.jumpToBracket",label:n.localize(0,null),alias:"Go to Bracket",precondition:null,kbOpts:{kbExpr:u.EditorContextKeys.textFocus,primary:3160}})||this}f(t,e),t.prototype.run=function(e,t){var n=p.get(t);n&&n.jumpToBracket()},t=v([a.editorAction],t)}(a.EditorAction);var h=function(){return function(e,t){this.position=e,this.brackets=t}}(),p=function(e){function t(t){var n=e.call(this)||this;return n._editor=t,n._lastBracketsData=[],n._lastVersionId=0,n._decorations=[],n._updateBracketsSoon=n._register(new r.RunOnceScheduler(function(){return n._updateBrackets()},50)),n._matchBrackets=n._editor.getConfiguration().contribInfo.matchBrackets,n._updateBracketsSoon.schedule(),n._register(t.onDidChangeCursorPosition(function(e){n._matchBrackets&&n._updateBracketsSoon.schedule()})),n._register(t.onDidChangeModel(function(e){n._decorations=[],n._updateBracketsSoon.schedule()})),n._register(t.onDidChangeConfiguration(function(e){n._matchBrackets=n._editor.getConfiguration().contribInfo.matchBrackets,!n._matchBrackets&&n._decorations.length>0&&(n._decorations=n._editor.deltaDecorations(n._decorations,[])),n._updateBracketsSoon.schedule()})),n}return f(t,e),n=t,t.get=function(e){return e.getContribution(n.ID)},t.prototype.getId=function(){return n.ID},t.prototype.jumpToBracket=function(){var e=this._editor.getModel();if(e){var t=this._editor.getSelection();if(t.isEmpty()){var n=t.getStartPosition(),i=e.matchBracket(n);if(i){var o=null;i[0].containsPosition(n)?o=i[1].getStartPosition():i[1].containsPosition(n)&&(o=i[0].getStartPosition()),o&&(this._editor.setPosition(o),this._editor.revealPosition(o))}}}},t.prototype._updateBrackets=function(){if(this._matchBrackets){this._recomputeBrackets();for(var e=[],t=0,i=0,o=this._lastBracketsData.length;i1&&r.sort(o.Position.compare);for(var c=[],d=0,p=0,f=n.length,a=0,u=r.length;a{1}",n,r),this._commands[n]=i):s=o.format("{0}",r),t.push(s)}this._domNode.innerHTML=t.join(" | "),this._editor.layoutContentWidget(this)}else this._domNode.innerHTML="no commands"},e.prototype.getId=function(){return this._id},e.prototype.getDomNode=function(){return this._domNode},e.prototype.setSymbolRange=function(e){this._symbolRange=e;var t=e.startLineNumber,n=this._editor.getModel().getLineFirstNonWhitespaceColumn(t);this._widgetPosition={position:{lineNumber:t,column:n},preference:[a.ContentWidgetPositionPreference.ABOVE]}},e.prototype.getPosition=function(){return this._widgetPosition},e.prototype.isVisible=function(){return this._domNode.hasAttribute("monaco-visible-content-widget")},e._idPool=0,e}(),f=function(){function e(){this._removeDecorations=[],this._addDecorations=[],this._addDecorationsCallbacks=[]}return e.prototype.addDecoration=function(e,t){this._addDecorations.push(e),this._addDecorationsCallbacks.push(t)},e.prototype.removeDecoration=function(e){this._removeDecorations.push(e)},e.prototype.commit=function(e){for(var t=e.deltaDecorations(this._removeDecorations,this._addDecorations),n=0,i=t.length;n a:hover { color: "+i+" !important; }")})}),define(d[516],h([1,0,18,10,3,7,31,50,17,30,453,515]),function(e,t,n,i,o,r,s,a,u,l,c,d){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var h=function(){function e(e,t,n){var i=this;this._editor=e,this._commandService=t,this._messageService=n,this._isEnabled=this._editor.getConfiguration().contribInfo.codeLens,this._globalToDispose=[],this._localToDispose=[],this._lenses=[],this._currentFindCodeLensSymbolsPromise=null,this._modelChangeCounter=0,this._globalToDispose.push(this._editor.onDidChangeModel(function(){return i._onModelChange()})),this._globalToDispose.push(this._editor.onDidChangeModelLanguage(function(){return i._onModelChange()})),this._globalToDispose.push(this._editor.onDidChangeConfiguration(function(e){var t=i._isEnabled;i._isEnabled=i._editor.getConfiguration().contribInfo.codeLens,t!==i._isEnabled&&i._onModelChange()})),this._globalToDispose.push(u.CodeLensProviderRegistry.onDidChange(this._onModelChange,this)),this._onModelChange()}return t=e,e.prototype.dispose=function(){this._localDispose(),this._globalToDispose=o.dispose(this._globalToDispose)},e.prototype._localDispose=function(){this._currentFindCodeLensSymbolsPromise&&(this._currentFindCodeLensSymbolsPromise.cancel(),this._currentFindCodeLensSymbolsPromise=null,this._modelChangeCounter++),this._currentFindOccPromise&&(this._currentFindOccPromise.cancel(),this._currentFindOccPromise=null),this._localToDispose=o.dispose(this._localToDispose)},e.prototype.getId=function(){return t.ID},e.prototype._onModelChange=function(){var e=this;this._localDispose();var t=this._editor.getModel();if(t&&this._isEnabled&&u.CodeLensProviderRegistry.has(t)){for(var o=0,r=u.CodeLensProviderRegistry.all(t);oi||(n&&n[n.length-1].symbol.range.startLineNumber===u?n.push(a):(n=[a],o.push(n)))}var l=this._editor.getCenteredRangeInViewport(),c=l&&o.length!==this._lenses.length&&0!==this._editor.getScrollTop();this._editor.changeDecorations(function(e){t._editor.changeViewZones(function(n){for(var i=0,r=0,s=new d.CodeLensHelper;rD)return l._domNode.style.maxWidth=e-28-t-15+"px",void(l._replaceInputBox.inputElement.style.width=s.getTotalWidth(l._findInput.inputBox.inputElement)+"px");D+28+t>=e&&(i=!0),D+28+t-O>=e&&(o=!0),D+28+t-O>=e+50&&(n=!0),s.toggleClass(l._domNode,"collapsed-find-widget",n),s.toggleClass(l._domNode,"narrow-find-widget",o),s.toggleClass(l._domNode,"reduced-find-widget",i),o||n||(l._domNode.style.maxWidth=e-28-t-15+"px");var r=s.getTotalWidth(l._findInput.inputBox.inputElement);r>0&&(l._replaceInputBox.inputElement.style.width=r+"px")};return c(),l._register(l._codeEditor.onDidChangeConfiguration(function(e){e.readOnly&&(l._codeEditor.getConfiguration().readOnly&&l._state.change({isReplaceRevealed:!1},!1),l._updateButtons()),e.layoutInfo&&c()})),l._register(l._codeEditor.onDidChangeCursorSelection(function(){l._isVisible&&l._updateToggleSelectionFindButton()})),l._findInputFocussed=g.CONTEXT_FIND_INPUT_FOCUSSED.bindTo(a),l._focusTracker=l._register(s.trackFocus(l._findInput.inputBox.inputElement)),l._focusTracker.addFocusListener(function(){if(l._findInputFocussed.set(!0),l._toggleSelectionFind.checked){var e=l._codeEditor.getSelection();1===e.endColumn&&e.endLineNumber>e.startLineNumber&&(e=e.setEndPosition(e.endLineNumber-1,1));var t=l._state.currentMatch;e.startLineNumber!==e.endLineNumber&&(p.Range.equalsRange(e,t)||l._state.change({searchScope:e},!0))}}),l._focusTracker.addBlurListener(function(){l._findInputFocussed.set(!1)}),l._codeEditor.addOverlayWidget(l),l._viewZone=new A(0),l._applyTheme(u.getTheme()),l._register(u.onThemeChange(l._applyTheme.bind(l))),l._register(l._codeEditor.onDidChangeModel(function(e){l._isVisible&&void 0!==l._viewZoneId&&l._codeEditor.changeViewZones(function(e){e.removeZone(l._viewZoneId),l._viewZoneId=void 0})})),l._register(l._codeEditor.onDidScrollChange(function(e){e.scrollTopChanged?l._layoutViewZone():setTimeout(function(){l._layoutViewZone()},0)})),l}return f(t,e),t.prototype.getId=function(){return t.ID},t.prototype.getDomNode=function(){return this._domNode},t.prototype.getPosition=function(){return this._isVisible?{preference:d.OverlayWidgetPositionPreference.TOP_RIGHT_CORNER}:null},t.prototype._onStateChanged=function(e){if(e.searchString&&(this._findInput.setValue(this._state.searchString),this._updateButtons()),e.replaceString&&(this._replaceInputBox.value=this._state.replaceString),e.isRevealed&&(this._state.isRevealed?this._reveal(!0):this._hide(!0)),e.isReplaceRevealed&&(this._state.isReplaceRevealed?this._codeEditor.getConfiguration().readOnly||this._isReplaceVisible||(this._isReplaceVisible=!0,this._updateButtons()):this._isReplaceVisible&&(this._isReplaceVisible=!1,this._updateButtons())),e.isRegex&&this._findInput.setRegex(this._state.isRegex),e.wholeWord&&this._findInput.setWholeWords(this._state.wholeWord),e.matchCase&&this._findInput.setCaseSensitive(this._state.matchCase),e.searchScope&&(this._state.searchScope?this._toggleSelectionFind.checked=!0:this._toggleSelectionFind.checked=!1,this._updateToggleSelectionFindButton()),e.searchString||e.matchesCount||e.matchesPosition){var t=this._state.searchString.length>0&&0===this._state.matchesCount;s.toggleClass(this._domNode,"no-results",t),this._updateMatchesCount()}(e.searchString||e.currentMatch)&&this._layoutViewZone()},t.prototype._updateMatchesCount=function(){this._matchesCount.style.minWidth=O+"px",this._state.matchesCount>=h.MATCHES_LIMIT?this._matchesCount.title=T:this._matchesCount.title="",this._matchesCount.firstChild&&this._matchesCount.removeChild(this._matchesCount.firstChild);var e;if(this._state.matchesCount>0){var t=String(this._state.matchesCount);this._state.matchesCount>=h.MATCHES_LIMIT&&(t+="+");var n=String(this._state.matchesPosition);"0"===n&&(n="?"),e=r.format(k,n,t)}else e=I;this._matchesCount.appendChild(document.createTextNode(e)),O=Math.max(O,this._matchesCount.clientWidth)},t.prototype._updateToggleSelectionFindButton=function(){var e=this._codeEditor.getSelection(),t=!!e&&(e.startLineNumber!==e.endLineNumber||e.startColumn!==e.endColumn),n=this._toggleSelectionFind.checked;this._toggleSelectionFind.setEnabled(this._isVisible&&(n||t))},t.prototype._updateButtons=function(){this._findInput.setEnabled(this._isVisible),this._replaceInputBox.setEnabled(this._isVisible&&this._isReplaceVisible),this._updateToggleSelectionFindButton(),this._closeBtn.setEnabled(this._isVisible);var e=this._state.searchString.length>0;this._prevBtn.setEnabled(this._isVisible&&e),this._nextBtn.setEnabled(this._isVisible&&e),this._replaceBtn.setEnabled(this._isVisible&&this._isReplaceVisible&&e),this._replaceAllBtn.setEnabled(this._isVisible&&this._isReplaceVisible&&e),s.toggleClass(this._domNode,"replaceToggled",this._isReplaceVisible),this._toggleReplaceBtn.toggleClass("collapse",!this._isReplaceVisible),this._toggleReplaceBtn.toggleClass("expand",this._isReplaceVisible),this._toggleReplaceBtn.setExpanded(this._isReplaceVisible);var t=!this._codeEditor.getConfiguration().readOnly;this._toggleReplaceBtn.setEnabled(this._isVisible&&t)},t.prototype._reveal=function(e){var t=this;if(!this._isVisible){this._isVisible=!0;var n=this._codeEditor.getSelection();!!n&&(n.startLineNumber!==n.endLineNumber||n.startColumn!==n.endColumn)&&this._codeEditor.getConfiguration().contribInfo.find.autoFindInSelection?this._toggleSelectionFind.checked=!0:this._toggleSelectionFind.checked=!1,this._updateButtons(),setTimeout(function(){s.addClass(t._domNode,"visible"),t._domNode.setAttribute("aria-hidden","false"),e||(s.addClass(t._domNode,"noanimation"),setTimeout(function(){s.removeClass(t._domNode,"noanimation")},200))},0),this._codeEditor.layoutOverlayWidget(this);var i=!0;if(this._codeEditor.getConfiguration().contribInfo.find.seedSearchStringFromSelection&&n){var o=s.getDomNodePagePosition(this._codeEditor.getDomNode()),r=this._codeEditor.getScrolledVisiblePosition(n.getStartPosition()),a=o.left+r.left;if(r.topn.startLineNumber&&(i=!1);var u=s.getTopLeftOffset(this._domNode).left;a>u&&(i=!1);var l=this._codeEditor.getScrolledVisiblePosition(n.getEndPosition());o.left+l.left>u&&(i=!1)}}this._showViewZone(i)}},t.prototype._hide=function(e){var t=this;this._isVisible&&(this._isVisible=!1,this._updateButtons(),s.removeClass(this._domNode,"visible"),this._domNode.setAttribute("aria-hidden","true"),e&&this._codeEditor.focus(),this._codeEditor.layoutOverlayWidget(this),this._codeEditor.changeViewZones(function(e){void 0!==t._viewZoneId&&(e.removeZone(t._viewZoneId),t._viewZoneId=void 0,t._codeEditor.setScrollTop(t._codeEditor.getScrollTop()-t._viewZone.heightInPx))}))},t.prototype._layoutViewZone=function(){var e=this;this._isVisible&&void 0===this._viewZoneId&&this._codeEditor.changeViewZones(function(t){e._state.isReplaceRevealed?e._viewZone.heightInPx=64:e._viewZone.heightInPx=P,e._viewZoneId=t.addZone(e._viewZone),e._codeEditor.setScrollTop(e._codeEditor.getScrollTop()+e._viewZone.heightInPx)})},t.prototype._showViewZone=function(e){var t=this;void 0===e&&(e=!0),this._isVisible&&this._codeEditor.changeViewZones(function(n){var i=P;void 0!==t._viewZoneId?(t._state.isReplaceRevealed?(t._viewZone.heightInPx=64,i=64-P):(t._viewZone.heightInPx=P,i=P-64),n.removeZone(t._viewZoneId)):t._viewZone.heightInPx=P,t._viewZoneId=n.addZone(t._viewZone),e&&t._codeEditor.setScrollTop(t._codeEditor.getScrollTop()+i)})},t.prototype._applyTheme=function(e){var t={inputActiveOptionBorder:e.getColor(v.inputActiveOptionBorder),inputBackground:e.getColor(v.inputBackground),inputForeground:e.getColor(v.inputForeground),inputBorder:e.getColor(v.inputBorder),inputValidationInfoBackground:e.getColor(v.inputValidationInfoBackground),inputValidationInfoBorder:e.getColor(v.inputValidationInfoBorder),inputValidationWarningBackground:e.getColor(v.inputValidationWarningBackground),inputValidationWarningBorder:e.getColor(v.inputValidationWarningBorder),inputValidationErrorBackground:e.getColor(v.inputValidationErrorBackground),inputValidationErrorBorder:e.getColor(v.inputValidationErrorBorder)};this._findInput.style(t),this._replaceInputBox.style(t)},t.prototype.focusFindInput=function(){this._findInput.select(),this._findInput.focus()},t.prototype.focusReplaceInput=function(){this._replaceInputBox.select(),this._replaceInputBox.focus()},t.prototype.highlightFindOptions=function(){this._findInput.highlightFindOptions()},t.prototype._onFindInputMouseDown=function(e){e.middleButton&&e.stopPropagation()},t.prototype._onFindInputKeyDown=function(e){return e.equals(3)?(this._codeEditor.getAction(h.FIND_IDS.NextMatchFindAction).run().done(null,i.onUnexpectedError),void e.preventDefault()):e.equals(1027)?(this._codeEditor.getAction(h.FIND_IDS.PreviousMatchFindAction).run().done(null,i.onUnexpectedError),void e.preventDefault()):e.equals(2)?(this._isReplaceVisible?this._replaceInputBox.focus():this._findInput.focusOnCaseSensitive(),void e.preventDefault()):e.equals(2066)?(this._codeEditor.focus(),void e.preventDefault()):void 0},t.prototype._onReplaceInputKeyDown=function(e){return e.equals(3)?(this._controller.replace(),void e.preventDefault()):e.equals(2051)?(this._controller.replaceAll(),void e.preventDefault()):e.equals(2)?(this._findInput.focusOnCaseSensitive(),void e.preventDefault()):e.equals(1026)?(this._findInput.focus(),void e.preventDefault()):e.equals(2066)?(this._codeEditor.focus(),void e.preventDefault()):void 0},t.prototype.getHorizontalSashTop=function(e){return 0},t.prototype.getHorizontalSashLeft=function(e){return 0},t.prototype.getHorizontalSashWidth=function(e){return 500},t.prototype._keybindingLabelFor=function(e){var t=this._keybindingService.lookupKeybinding(e);return t?" ("+t.getLabel()+")":""},t.prototype._buildFindPart=function(){var e=this;this._findInput=this._register(new a.FindInput(null,this._contextViewProvider,{width:221,label:_,placeholder:y,appendCaseSensitiveLabel:this._keybindingLabelFor(h.FIND_IDS.ToggleCaseSensitiveCommand),appendWholeWordsLabel:this._keybindingLabelFor(h.FIND_IDS.ToggleWholeWordCommand),appendRegexLabel:this._keybindingLabelFor(h.FIND_IDS.ToggleRegexCommand),validation:function(t){if(0===t.length)return null;if(!e._findInput.getRegex())return null;try{return new RegExp(t),null}catch(e){return{content:e.message}}}})),this._findInput.setRegex(!!this._state.isRegex),this._findInput.setCaseSensitive(!!this._state.matchCase),this._findInput.setWholeWords(!!this._state.wholeWord),this._register(this._findInput.onKeyDown(function(t){return e._onFindInputKeyDown(t)})),this._register(this._findInput.onInput(function(){e._state.change({searchString:e._findInput.getValue()},!0)})),this._register(this._findInput.onDidOptionChange(function(){e._state.change({isRegex:e._findInput.getRegex(),wholeWord:e._findInput.getWholeWords(),matchCase:e._findInput.getCaseSensitive()},!0)})),this._register(this._findInput.onCaseSensitiveKeyDown(function(t){t.equals(1026)&&e._isReplaceVisible&&(e._replaceInputBox.focus(),t.preventDefault())})),o.isLinux&&this._register(this._findInput.onMouseDown(function(t){return e._onFindInputMouseDown(t)})),this._matchesCount=document.createElement("div"),this._matchesCount.className="matchesCount",this._updateMatchesCount(),this._prevBtn=this._register(new B({label:C+this._keybindingLabelFor(h.FIND_IDS.PreviousMatchFindAction),className:"previous",onTrigger:function(){e._codeEditor.getAction(h.FIND_IDS.PreviousMatchFindAction).run().done(null,i.onUnexpectedError)},onKeyDown:function(e){}})),this._nextBtn=this._register(new B({label:b+this._keybindingLabelFor(h.FIND_IDS.NextMatchFindAction),className:"next",onTrigger:function(){e._codeEditor.getAction(h.FIND_IDS.NextMatchFindAction).run().done(null,i.onUnexpectedError)},onKeyDown:function(e){}}));var t=document.createElement("div");return t.className="find-part",t.appendChild(this._findInput.domNode),t.appendChild(this._matchesCount),t.appendChild(this._prevBtn.domNode),t.appendChild(this._nextBtn.domNode),this._toggleSelectionFind=this._register(new W({parent:t,title:w+this._keybindingLabelFor(h.FIND_IDS.ToggleSearchScopeCommand),onChange:function(){if(e._toggleSelectionFind.checked){var t=e._codeEditor.getSelection();1===t.endColumn&&t.endLineNumber>t.startLineNumber&&(t=t.setEndPosition(t.endLineNumber-1,1)),t.isEmpty()||e._state.change({searchScope:t},!0)}else e._state.change({searchScope:null},!0)}})),this._closeBtn=this._register(new B({label:S+this._keybindingLabelFor(h.FIND_IDS.CloseFindWidgetCommand),className:"close-fw",onTrigger:function(){e._state.change({isRevealed:!1,searchScope:null},!1)},onKeyDown:function(t){t.equals(2)&&e._isReplaceVisible&&(e._replaceBtn.isEnabled()?e._replaceBtn.focus():e._codeEditor.focus(),t.preventDefault())}})),t.appendChild(this._closeBtn.domNode),t},t.prototype._buildReplacePart=function(){var e=this,t=document.createElement("div");t.className="replace-input",t.style.width="221px",this._replaceInputBox=this._register(new u.InputBox(t,null,{ariaLabel:E,placeholder:L})),this._register(s.addStandardDisposableListener(this._replaceInputBox.inputElement,"keydown",function(t){return e._onReplaceInputKeyDown(t)})),this._register(s.addStandardDisposableListener(this._replaceInputBox.inputElement,"input",function(t){e._state.change({replaceString:e._replaceInputBox.value},!1)})),this._replaceBtn=this._register(new B({label:x+this._keybindingLabelFor(h.FIND_IDS.ReplaceOneAction),className:"replace",onTrigger:function(){e._controller.replace()},onKeyDown:function(t){t.equals(1026)&&(e._closeBtn.focus(),t.preventDefault())}})),this._replaceAllBtn=this._register(new B({label:N+this._keybindingLabelFor(h.FIND_IDS.ReplaceAllAction),className:"replace-all",onTrigger:function(){e._controller.replaceAll()},onKeyDown:function(e){}}));var n=document.createElement("div");return n.className="replace-part",n.appendChild(t),n.appendChild(this._replaceBtn.domNode),n.appendChild(this._replaceAllBtn.domNode),n},t.prototype._buildDomNode=function(){var e=this,t=this._buildFindPart(),n=this._buildReplacePart();this._toggleReplaceBtn=this._register(new B({label:M,className:"toggle left",onTrigger:function(){e._state.change({isReplaceRevealed:!e._isReplaceVisible},!1),e._isReplaceVisible&&(e._replaceInputBox.width=e._findInput.inputBox.width),e._showViewZone()},onKeyDown:function(e){}})),this._toggleReplaceBtn.toggleClass("expand",this._isReplaceVisible),this._toggleReplaceBtn.toggleClass("collapse",!this._isReplaceVisible),this._toggleReplaceBtn.setExpanded(this._isReplaceVisible),this._domNode=document.createElement("div"),this._domNode.className="editor-widget find-widget",this._domNode.setAttribute("aria-hidden","true"),this._domNode.style.width=D+"px",this._domNode.appendChild(this._toggleReplaceBtn.domNode),this._domNode.appendChild(t),this._domNode.appendChild(n),this._buildSash()},t.prototype._buildSash=function(){var e=this;this._resizeSash=new c.Sash(this._domNode,this,{orientation:c.Orientation.VERTICAL});var t=D;this._register(this._resizeSash.addListener("start",function(n){t=s.getTotalWidth(e._domNode)})),this._register(this._resizeSash.addListener("change",function(n){var i=t+n.startX-n.currentX;if(!(i(parseFloat(s.getComputedStyle(e._domNode).maxWidth)||0)||(e._domNode.style.width=i+"px",e._isReplaceVisible&&(e._replaceInputBox.width=o))}}))},t.ID="editor.contrib.findWidget",t}(l.Widget);t.FindWidget=F;var W=function(e){function t(n){var i=e.call(this)||this;return i._opts=n,i._domNode=document.createElement("div"),i._domNode.className="monaco-checkbox",i._domNode.title=i._opts.title,i._domNode.tabIndex=0,i._checkbox=document.createElement("input"),i._checkbox.type="checkbox",i._checkbox.className="checkbox",i._checkbox.id="checkbox-"+t._COUNTER++,i._checkbox.tabIndex=-1,i._label=document.createElement("label"),i._label.className="label",i._label.htmlFor=i._checkbox.id,i._label.tabIndex=-1,i._domNode.appendChild(i._checkbox),i._domNode.appendChild(i._label),i._opts.parent.appendChild(i._domNode),i.onchange(i._checkbox,function(e){i._opts.onChange()}),i}return f(t,e),Object.defineProperty(t.prototype,"domNode",{get:function(){return this._domNode},enumerable:!0,configurable:!0}),Object.defineProperty(t.prototype,"checked",{get:function(){return this._checkbox.checked},set:function(e){this._checkbox.checked=e},enumerable:!0,configurable:!0}),t.prototype.focus=function(){this._checkbox.focus()},t.prototype.enable=function(){this._checkbox.removeAttribute("disabled")},t.prototype.disable=function(){this._checkbox.disabled=!0},t.prototype.setEnabled=function(e){e?(this.enable(),this.domNode.tabIndex=0):(this.disable(),this.domNode.tabIndex=-1)},t._COUNTER=0,t}(l.Widget),B=function(e){function t(t){var n=e.call(this)||this;return n._opts=t,n._domNode=document.createElement("div"),n._domNode.title=n._opts.label,n._domNode.tabIndex=0,n._domNode.className="button "+n._opts.className,n._domNode.setAttribute("role","button"),n._domNode.setAttribute("aria-label",n._opts.label),n.onclick(n._domNode,function(e){n._opts.onTrigger(),e.preventDefault()}),n.onkeydown(n._domNode,function(e){if(e.equals(10)||e.equals(3))return n._opts.onTrigger(),void e.preventDefault();n._opts.onKeyDown(e)}),n}return f(t,e),Object.defineProperty(t.prototype,"domNode",{get:function(){return this._domNode},enumerable:!0,configurable:!0}),t.prototype.isEnabled=function(){return this._domNode.tabIndex>=0},t.prototype.focus=function(){this._domNode.focus()},t.prototype.setEnabled=function(e){s.toggleClass(this._domNode,"disabled",!e),this._domNode.setAttribute("aria-disabled",String(!e)),this._domNode.tabIndex=e?0:-1},t.prototype.setExpanded=function(e){this._domNode.setAttribute("aria-expanded",String(!!e))},t.prototype.toggleClass=function(e,t){s.toggleClass(this._domNode,e,t)},t}(l.Widget);t.SimpleButton=B,m.registerThemingParticipant(function(e,t){function n(e,n){n&&t.addRule(".monaco-editor "+e+" { background-color: "+n+"; }")}n(".findMatch",e.getColor(v.editorFindMatchHighlight)),n(".currentFindMatch",e.getColor(v.editorFindMatch)),n(".findScope",e.getColor(v.editorFindRangeHighlight)),n(".find-widget",e.getColor(v.editorWidgetBackground));var i=e.getColor(v.widgetShadow);i&&t.addRule(".monaco-editor .find-widget { box-shadow: 0 2px 8px "+i+"; }");var o=e.getColor(v.activeContrastBorder);o&&(t.addRule(".monaco-editor .findScope { border: 1px dashed "+o.transparent(.4)+"; }"),t.addRule(".monaco-editor .currentFindMatch { border: 2px solid "+o+"; padding: 1px; -moz-box-sizing: border-box; box-sizing: border-box; }"),t.addRule(".monaco-editor .findMatch { border: 1px dotted "+o+"; -moz-box-sizing: border-box; box-sizing: border-box; }"));var r=e.getColor(v.contrastBorder);r&&t.addRule(".monaco-editor .find-widget { border: 2px solid "+r+"; }");var s=e.getColor(v.errorForeground);s&&t.addRule(".monaco-editor .find-widget.no-results .matchesCount { color: "+s+"; }");var a=e.getColor("panel.border");a&&t.addRule(".monaco-editor .find-widget .monaco-sash { background-color: "+a+"; width: 3px !important; margin-left: -4px;}")})}),define(d[520],h([1,0,69,46,19,30,519,518,176,14,76]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var d=function(e){function t(t,n,i,o,r,u){var l=e.call(this,t,i,u)||this;return l._widget=l._register(new s.FindWidget(t,l,l._state,n,o,i,r)),l._findOptionsWidget=l._register(new a.FindOptionsWidget(t,l._state,o,r)),l}return f(t,e),t.prototype._start=function(t){e.prototype._start.call(this,t),2===t.shouldFocus?this._widget.focusReplaceInput():1===t.shouldFocus&&this._widget.focusFindInput()},t.prototype.highlightFindOptions=function(){this._state.isRevealed?this._widget.highlightFindOptions():this._findOptionsWidget.highlightFindOptions()},t=v([r.editorContribution,y(1,n.IContextViewService),y(2,o.IContextKeyService),y(3,i.IKeybindingService),y(4,l.IThemeService),y(5,c.IStorageService)],t)}(u.CommonFindController);t.FindController=d}),define(d[521],h([1,0,18,3,51,2,13,25,19,14,23,335]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var d=function(){function e(e,n){this._messageListeners=[],this._editor=e,this._visible=t.CONTEXT_SNIPPET_MODE.bindTo(n)}return t=e,e.get=function(e){return e.getContribution(t._id)},e.prototype.getId=function(){return t._id},e.prototype.dispose=function(){this._visible.reset()},e.prototype.showMessage=function(e,t){var s=this;o.alert(e),this._visible.set(!0),i.dispose(this._messageWidget),this._messageListeners=i.dispose(this._messageListeners),this._messageWidget=new p(this._editor,t,e),this._messageListeners.push(this._editor.onDidBlurEditorText(function(){return s.closeMessage()})),this._messageListeners.push(this._editor.onDidChangeCursorPosition(function(){return s.closeMessage()})),this._messageListeners.push(this._editor.onDidDispose(function(){return s.closeMessage()})),this._messageListeners.push(this._editor.onDidChangeModel(function(){return s.closeMessage()})),this._messageListeners.push(n.setDisposableTimeout(function(){return s.closeMessage()},3e3));var a;this._messageListeners.push(this._editor.onMouseMove(function(e){e.target.position&&(a?a.containsPosition(e.target.position)||s.closeMessage():a=new r.Range(t.lineNumber-3,1,e.target.position.lineNumber+3,1))}))},e.prototype.closeMessage=function(){this._visible.reset(),this._messageListeners=i.dispose(this._messageListeners),this._messageListeners.push(p.fadeOut(this._messageWidget))},e._id="editor.contrib.messageController",e.CONTEXT_SNIPPET_MODE=new u.RawContextKey("messageVisible",!1),e=t=v([s.commonEditorContribution,y(1,u.IContextKeyService)],e);var t}();t.MessageController=d;var h=s.EditorCommand.bindToContribution(d.get);s.CommonEditorRegistry.registerEditorCommand(new h({id:"leaveEditorMessage",precondition:d.CONTEXT_SNIPPET_MODE,handler:function(e){return e.closeMessage()},kbOpts:{weight:s.CommonEditorRegistry.commandWeight(30),primary:9}}));var p=function(){function e(e,t,n){var i=t.lineNumber,o=t.column;this.allowEditorOverflow=!0,this.suppressMouseDown=!1,this._editor=e,this._editor.revealLinesInCenterIfOutsideViewport(i,i),this._position={lineNumber:i,column:1},this._domNode=document.createElement("div"),this._domNode.style.paddingLeft=e.getOffsetForColumn(i,o)-6+"px",this._domNode.classList.add("monaco-editor-overlaymessage");var r=document.createElement("div");r.classList.add("message"),r.textContent=n,this._domNode.appendChild(r);var s=document.createElement("div");s.classList.add("anchor"),this._domNode.appendChild(s),this._editor.addContentWidget(this),this._domNode.classList.add("fadeIn")}return e.fadeOut=function(e){var t,n=function(){e.dispose(),clearTimeout(t),e.getDomNode().removeEventListener("animationend",n)};return t=setTimeout(n,110),e.getDomNode().addEventListener("animationend",n),e.getDomNode().classList.add("fadeOut"),{dispose:n}},e.prototype.dispose=function(){this._editor.removeContentWidget(this)},e.prototype.getId=function(){return"messageoverlay"},e.prototype.getDomNode=function(){return this._domNode},e.prototype.getPosition=function(){return{position:this._position,preference:[a.ContentWidgetPositionPreference.ABOVE]}},e}();l.registerThemingParticipant(function(e,t){var n=e.getColor(c.inputValidationInfoBorder);if(n){var i=e.type===l.HIGH_CONTRAST?2:1;t.addRule(".monaco-editor .monaco-editor-overlaymessage .anchor { border-top-color: "+n+"; }"),t.addRule(".monaco-editor .monaco-editor-overlaymessage .message { border: "+i+"px solid "+n+"; }")}var o=e.getColor(c.inputValidationInfoBackground);o&&t.addRule(".monaco-editor .monaco-editor-overlaymessage .message { background-color: "+o+"; }")})}),define(d[522],h([1,0,353,11,3,36,4,19,103,58,12,2,13,30,183,23,14,32,21,37,366]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,C,b,w,S){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var E=function(){function e(e,t){var n=this;this._editor=e,this._markers=null,this._nextIdx=-1,this._toUnbind=[],this._ignoreSelectionChange=!1,this._onCurrentMarkerChanged=new i.Emitter,this._onMarkerSetChanged=new i.Emitter,this.setMarkers(t),this._toUnbind.push(this._editor.onDidDispose(function(){return n.dispose()})),this._toUnbind.push(this._editor.onDidChangeCursorPosition(function(){n._ignoreSelectionChange||(n._nextIdx=-1)}))}return Object.defineProperty(e.prototype,"onCurrentMarkerChanged",{get:function(){return this._onCurrentMarkerChanged.event},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"onMarkerSetChanged",{get:function(){return this._onMarkerSetChanged.event},enumerable:!0,configurable:!0}),e.prototype.setMarkers=function(e){this._markers=e||[],this._markers.sort(function(e,t){return r.default.compare(e.severity,t.severity)||d.Range.compareRangesUsingStarts(e,t)}),this._nextIdx=-1,this._onMarkerSetChanged.fire(this)},e.prototype.withoutWatchingEditorPosition=function(e){this._ignoreSelectionChange=!0;try{e()}finally{this._ignoreSelectionChange=!1}},e.prototype._initIdx=function(e){for(var t=!1,n=this._editor.getPosition(),i=0;i=this._markers.length&&(this._nextIdx=0)):(this._nextIdx-=1,this._nextIdx<0&&(this._nextIdx=this._markers.length-1));var t=this._markers[this._nextIdx];this._onCurrentMarkerChanged.fire(t)}else this._onCurrentMarkerChanged.fire(void 0)},e.prototype.canNavigate=function(){return this._markers.length>0},e.prototype.next=function(){this.move(!0)},e.prototype.previous=function(){this.move(!1)},e.prototype.findMarkerAtPosition=function(e){for(var t=0,n=this._markers;t1&&(a=new r.Selection(a.startLineNumber,a.startColumn,a.endLineNumber,a.endColumn+h-1));var p=new l.InPlaceReplaceCommand(u,a,n.value);s.editor.pushUndoStop(),s.editor.executeCommand(e,p),s.editor.pushUndoStop(),s.decorationIds=s.editor.deltaDecorations(s.decorationIds,[{range:c,options:t.DECORATION}]),s.decorationRemover.cancel(),s.decorationRemover=i.TPromise.timeout(350),s.decorationRemover.then(function(){s.editor.changeDecorations(function(e){s.decorationIds=e.deltaDecorations(s.decorationIds,[])})})}})},e.ID="editor.contrib.inPlaceReplaceController",e.DECORATION=p.ModelDecorationOptions.register({className:"valueSetReplacement"}),e=t=v([a.commonEditorContribution,y(1,u.IEditorWorkerService)],e);var t}();(function(e){function t(){return e.call(this,{id:"editor.action.inPlaceReplace.up",label:n.localize(0,null),alias:"Replace with Previous Value",precondition:s.EditorContextKeys.writable,kbOpts:{kbExpr:s.EditorContextKeys.textFocus,primary:3154}})||this}f(t,e),t.prototype.run=function(e,t){var n=g.get(t);if(n)return n.run(this.id,!0)},t=v([a.editorAction],t)})(a.EditorAction),function(e){function t(){return e.call(this,{id:"editor.action.inPlaceReplace.down",label:n.localize(1,null),alias:"Replace with Next Value",precondition:s.EditorContextKeys.writable,kbOpts:{kbExpr:s.EditorContextKeys.textFocus,primary:3156}})||this}f(t,e),t.prototype.run=function(e,t){var n=g.get(t);if(n)return n.run(this.id,!1)},t=v([a.editorAction],t)}(a.EditorAction);d.registerThemingParticipant(function(e,t){var n=e.getColor(h.editorBracketMatchBorder);n&&t.addRule(".monaco-editor.vs .valueSetReplacement { outline: solid 2px "+n+"; }")})}),define(d[525],h([1,0,358,10,15,36,7,50,71,20,13,17,61,25,401,3,30,14,23,34,192,389]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_,C,b,w,S){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var E=o.isMacintosh?n.localize(0,null):n.localize(1,null),L=o.isMacintosh?n.localize(2,null):n.localize(3,null),x=n.localize(4,null),N=n.localize(5,null),M={meta:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link",hoverMessage:E}),metaActive:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link-active",hoverMessage:E}),alt:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link",hoverMessage:x}),altActive:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link-active",hoverMessage:x}),altCommand:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link",hoverMessage:N}),altCommandActive:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link-active",hoverMessage:N}),metaCommand:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link",hoverMessage:L}),metaCommandActive:w.ModelDecorationOptions.register({stickiness:l.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,inlineClassName:"detected-link-active",hoverMessage:L})},T=function(){function e(e,t){this.link=e,this.decorationId=t}return e.decoration=function(t,n){return{range:t.range,options:e._getOptions(t,n,!1)}},e._getOptions=function(e,t,n){return/^command:/i.test(e.url)?t?n?M.metaCommandActive:M.metaCommand:n?M.altCommandActive:M.altCommand:t?n?M.metaActive:M.meta:n?M.altActive:M.alt},e.prototype.activate=function(t,n){t.changeDecorationOptions(this.decorationId,e._getOptions(this.link,n,!0))},e.prototype.deactivate=function(t,n){t.changeDecorationOptions(this.decorationId,e._getOptions(this.link,n,!1))},e}(),k=function(){function e(e,t,n,i){var o=this;this.editor=e,this.openerService=t,this.messageService=n,this.editorWorkerService=i,this.listenersToRemove=[];var r=new S.ClickLinkGesture(e);this.listenersToRemove.push(r),this.listenersToRemove.push(r.onMouseMoveOrRelevantKeyDown(function(e){var t=e[0],n=e[1];o._onEditorMouseMove(t,n)})),this.listenersToRemove.push(r.onExecute(function(e){o.onEditorMouseUp(e)})),this.listenersToRemove.push(r.onCancel(function(e){o.cleanUpActiveLinkDecoration()})),this.enabled=e.getConfiguration().contribInfo.links,this.listenersToRemove.push(e.onDidChangeConfiguration(function(t){var n=e.getConfiguration().contribInfo.links;o.enabled!==n&&(o.enabled=n,o.updateDecorations([]),o.stop(),o.beginCompute())})),this.listenersToRemove.push(e.onDidChangeModelContent(function(e){return o.onChange()})),this.listenersToRemove.push(e.onDidChangeModel(function(e){return o.onModelChanged()})),this.listenersToRemove.push(e.onDidChangeModelLanguage(function(e){return o.onModelModeChanged()})),this.listenersToRemove.push(d.LinkProviderRegistry.onDidChange(function(e){return o.onModelModeChanged()})),this.timeoutPromise=null,this.computePromise=null,this.currentOccurrences={},this.activeLinkDecorationId=null,this.beginCompute()}return t=e,e.get=function(e){return e.getContribution(t.ID)},e.prototype.getId=function(){return t.ID},e.prototype.isComputing=function(){return s.TPromise.is(this.computePromise)},e.prototype.onModelChanged=function(){this.currentOccurrences={},this.activeLinkDecorationId=null,this.stop(),this.beginCompute()},e.prototype.onModelModeChanged=function(){this.stop(),this.beginCompute()},e.prototype.onChange=function(){var e=this;this.timeoutPromise||(this.timeoutPromise=s.TPromise.timeout(t.RECOMPUTE_TIME),this.timeoutPromise.then(function(){e.timeoutPromise=null,e.beginCompute()}))},e.prototype.beginCompute=function(){var e=this;this.editor.getModel()&&this.enabled&&d.LinkProviderRegistry.has(this.editor.getModel())&&(this.computePromise=g.getLinks(this.editor.getModel()).then(function(t){e.updateDecorations(t),e.computePromise=null}))},e.prototype.updateDecorations=function(e){var t=this,n="altKey"===this.editor.getConfiguration().multiCursorModifier;this.editor.changeDecorations(function(i){for(var o=[],r=Object.keys(t.currentOccurrences),s=0,a=r.length;s1;r.toggleClass(this.element,"multiple",e),this.keyMultipleSignatures.set(e),this.signature.innerHTML="",this.docs.innerHTML="";var t=this.hints.signatures[this.currentSignature];if(t){var i=r.append(this.signature,w(".code")),o=t.parameters.length>0,a=this.editor.getConfiguration().fontInfo;i.style.fontSize=a.fontSize+"px",i.style.fontFamily=a.fontFamily,o?this.renderParameters(i,t,this.hints.activeParameter):r.append(i,w("span")).textContent=t.label;var u=t.parameters[this.hints.activeParameter];if(u&&u.documentation){var l=w("span.documentation");l.textContent=u.documentation,r.append(this.docs,w("p",null,l))}r.toggleClass(this.signature,"has-docs",!!t.documentation),t.documentation&&r.append(this.docs,w("p",null,t.documentation));var c=String(this.currentSignature+1);if(this.hints.signatures.length<10&&(c+="/"+this.hints.signatures.length),this.overloads.textContent=c,u){var d=u.label;this.announcedLabel!==d&&(s.alert(n.localize(0,null,d)),this.announcedLabel=d)}this.editor.layoutContentWidget(this),this.scrollbar.scanDomNode()}},e.prototype.renderParameters=function(e,t,n){for(var i,o=t.label.length,s=0,a=t.parameters.length-1;a>=0;a--){var u=t.parameters[a],l=0,c=0;(s=t.label.lastIndexOf(u.label,o-1))>=0&&(l=s,c=s+u.label.length),(i=document.createElement("span")).textContent=t.label.substring(c,o),r.prepend(e,i),(i=document.createElement("span")).className="parameter "+(a===n?"active":""),i.textContent=t.label.substring(l,c),r.prepend(e,i),o=l}(i=document.createElement("span")).textContent=t.label.substring(0,o),r.prepend(e,i)},e.prototype.next=function(){var e=this.hints.signatures.length;return e<2?(this.cancel(),!1):(this.currentSignature=(this.currentSignature+1)%e,this.render(),!0)},e.prototype.previous=function(){var e=this.hints.signatures.length;return e<2?(this.cancel(),!1):(this.currentSignature=(this.currentSignature-1+e)%e,this.render(),!0)},e.prototype.cancel=function(){this.model.cancel()},e.prototype.getDomNode=function(){return this.element},e.prototype.getId=function(){return e.ID},e.prototype.trigger=function(){this.model.trigger(0)},e.prototype.updateMaxHeight=function(){var e=Math.max(this.editor.getLayoutInfo().height/4,250);this.element.style.maxHeight=e+"px"},e.prototype.dispose=function(){this.disposables=i.dispose(this.disposables),this.model=null},e.ID="editor.widget.parameterHintsWidget",e=v([y(1,p.IContextKeyService)],e)}();t.ParameterHintsWidget=E,C.registerThemingParticipant(function(e,t){var n=e.getColor(b.editorHoverBorder);if(n){var i=e.type===C.HIGH_CONTRAST?2:1;t.addRule(".monaco-editor .parameter-hints-widget { border: "+i+"px solid "+n+"; }"),t.addRule(".monaco-editor .parameter-hints-widget.multiple .body { border-left: 1px solid "+n.transparent(.5)+"; }"),t.addRule(".monaco-editor .parameter-hints-widget .signature.has-docs { border-bottom: 1px solid "+n.transparent(.5)+"; }")}var o=e.getColor(b.editorHoverBackground);o&&t.addRule(".monaco-editor .parameter-hints-widget { background-color: "+o+"; }")})}),define(d[527],h([1,0,360,3,16,21,19,13,30,526,178]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var d=function(){function e(e,t){this.editor=e,this.widget=t.createInstance(l.ParameterHintsWidget,this.editor)}return t=e,e.get=function(e){return e.getContribution(t.ID)},e.prototype.getId=function(){return t.ID},e.prototype.cancel=function(){this.widget.cancel()},e.prototype.previous=function(){this.widget.previous()},e.prototype.next=function(){this.widget.next()},e.prototype.trigger=function(){this.widget.trigger()},e.prototype.dispose=function(){this.widget=i.dispose(this.widget)},e.ID="editor.controller.parameterHints",e=t=v([u.editorContribution,y(1,o.IInstantiationService)],e);var t}(),h=function(e){function t(){return e.call(this,{id:"editor.action.triggerParameterHints",label:n.localize(0,null),alias:"Trigger Parameter Hints",precondition:r.EditorContextKeys.hasSignatureHelpProvider,kbOpts:{kbExpr:r.EditorContextKeys.textFocus,primary:3082}})||this}return f(t,e),t.prototype.run=function(e,t){var n=d.get(t);n&&n.trigger()},t=v([a.editorAction],t)}(a.EditorAction);t.TriggerParameterHintsAction=h;var p=a.CommonEditorRegistry.commandWeight(75),g=a.EditorCommand.bindToContribution(d.get);a.CommonEditorRegistry.registerEditorCommand(new g({id:"closeParameterHints",precondition:c.Context.Visible,handler:function(e){return e.cancel()},kbOpts:{weight:p,kbExpr:r.EditorContextKeys.textFocus,primary:9,secondary:[1033]}})),a.CommonEditorRegistry.registerEditorCommand(new g({id:"showPrevParameterHint",precondition:s.ContextKeyExpr.and(c.Context.Visible,c.Context.MultipleSignatures),handler:function(e){return e.previous()},kbOpts:{weight:p,kbExpr:r.EditorContextKeys.textFocus,primary:16,secondary:[528],mac:{primary:16,secondary:[528,302]}}})),a.CommonEditorRegistry.registerEditorCommand(new g({id:"showNextParameterHint",precondition:s.ContextKeyExpr.and(c.Context.Visible,c.Context.MultipleSignatures),handler:function(e){return e.next()},kbOpts:{weight:p,kbExpr:r.EditorContextKeys.textFocus,primary:18,secondary:[530],mac:{primary:18,secondary:[530,300]}}}))}),define(d[528],h([1,0,369,10,3,7,2,25,14,23,12,393]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var d=function(){function e(e,t){var n=this;this.themeService=t,this._disposables=[],this.allowEditorOverflow=!0,this._currentAcceptInput=null,this._currentCancelInput=null,this._editor=e,this._editor.addContentWidget(this),this._disposables.push(e.onDidChangeConfiguration(function(e){e.fontInfo&&n.updateFont()})),this._disposables.push(t.onThemeChange(function(e){return n.onThemeChange(e)}))}return e.prototype.onThemeChange=function(e){this.updateStyles(e)},e.prototype.dispose=function(){this._disposables=o.dispose(this._disposables),this._editor.removeContentWidget(this)},e.prototype.getId=function(){return"__renameInputWidget"},e.prototype.getDomNode=function(){return this._domNode||(this._inputField=document.createElement("input"),this._inputField.className="rename-input",this._inputField.type="text",this._inputField.setAttribute("aria-label",n.localize(0,null)),this._domNode=document.createElement("div"),this._domNode.style.height=this._editor.getConfiguration().lineHeight+"px",this._domNode.className="monaco-editor rename-box",this._domNode.appendChild(this._inputField),this.updateFont(),this.updateStyles(this.themeService.getTheme())),this._domNode},e.prototype.updateStyles=function(e){if(this._inputField){var t=e.getColor(l.inputBackground),n=e.getColor(l.inputForeground),i=e.getColor(l.widgetShadow),o=e.getColor(l.inputBorder);this._inputField.style.backgroundColor=t?t.toString():null,this._inputField.style.color=n?n.toString():null,this._inputField.style.borderWidth=o?"1px":"0px",this._inputField.style.borderStyle=o?"solid":"none",this._inputField.style.borderColor=o?o.toString():"none",this._domNode.style.boxShadow=i?" 0 2px 8px "+i:null}},e.prototype.updateFont=function(){if(this._inputField){var e=this._editor.getConfiguration().fontInfo;this._inputField.style.fontFamily=e.fontFamily,this._inputField.style.fontWeight=e.fontWeight,this._inputField.style.fontSize=e.fontSize+"px"}},e.prototype.getPosition=function(){return this._visible?{position:this._position,preference:[a.ContentWidgetPositionPreference.BELOW,a.ContentWidgetPositionPreference.ABOVE]}:null},e.prototype.acceptInput=function(){this._currentAcceptInput&&this._currentAcceptInput()},e.prototype.cancelInput=function(){this._currentCancelInput&&this._currentCancelInput()},e.prototype.getInput=function(e,t,n,a){var u=this;this._position=new c.Position(e.startLineNumber,e.startColumn),this._inputField.value=t,this._inputField.setAttribute("selectionStart",n.toString()),this._inputField.setAttribute("selectionEnd",a.toString()),this._inputField.size=Math.max(1.1*(e.endColumn-e.startColumn),20);var l,d=[];return l=function(){o.dispose(d),u._hide()},new r.TPromise(function(n,o){u._currentCancelInput=function(){return u._currentAcceptInput=null,u._currentCancelInput=null,o(i.canceled()),!0},u._currentAcceptInput=function(){0!==u._inputField.value.trim().length&&u._inputField.value!==t?(u._currentAcceptInput=null,u._currentCancelInput=null,n(u._inputField.value)):u.cancelInput()};d.push(u._editor.onDidChangeCursorSelection(function(){s.Range.containsPosition(e,u._editor.getPosition())||u.cancelInput()})),d.push(u._editor.onDidBlurEditor(function(){return u.cancelInput()})),u._show()},this._currentCancelInput).then(function(e){return l(),e},function(e){return l(),r.TPromise.wrapError(e)})},e.prototype._show=function(){var e=this;this._editor.revealLineInCenterIfOutsideViewport(this._position.lineNumber),this._visible=!0,this._editor.layoutContentWidget(this),setTimeout(function(){e._inputField.focus(),e._inputField.setSelectionRange(parseInt(e._inputField.getAttribute("selectionStart")),parseInt(e._inputField.getAttribute("selectionEnd")))},25)},e.prototype._hide=function(){this._visible=!1,this._editor.layoutContentWidget(this)},e=v([y(1,u.IThemeService)],e)}();t.default=d}),define(d[529],h([1,0,368,10,36,7,411,19,50,163,13,30,21,338,528,87,16,14,18,17,51,2]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_,C,b,w,S,E){"use strict";function L(e,t,o){var s=[],a=!1,u=w.RenameProviderRegistry.ordered(e).map(function(n){return function(){if(!a)return b.asWinJsPromise(function(i){return n.provideRenameEdits(e,t,o,i)}).then(function(e){if(e){if(!e.rejectReason)return a=!0,e;s.push(e.rejectReason)}else;},function(e){return i.onUnexpectedExternalError(e),r.TPromise.wrapError(new Error("provider failed"))})}});return b.sequence(u).then(function(e){var t=e[0];return s.length>0?{edits:void 0,rejectReason:s.join("\n")}:t||{edits:void 0,rejectReason:n.localize(0,null)}})}Object.defineProperty(t,"__esModule",{value:!0}),t.rename=L;var x=new a.RawContextKey("renameInputVisible",!1),N=function(){function e(e,t,n,i,o,r,s){this.editor=e,this._messageService=t,this._textModelResolverService=n,this._progressService=i,this._fileService=s,this._renameInputField=new g.default(e,r),this._renameInputVisible=x.bindTo(o)}return t=e,e.get=function(e){return e.getContribution(t.ID)},e.prototype.dispose=function(){this._renameInputField.dispose()},e.prototype.getId=function(){return t.ID},e.prototype.run=function(){var e=this,t=this.editor.getSelection(),s=this.editor.getModel().getWordAtPosition(t.getStartPosition());if(s){var a,u=t.startLineNumber,l=0,c=s.word.length;return a=new E.Range(u,s.startColumn,u,s.endColumn),t.isEmpty()||t.startLineNumber!==t.endLineNumber||(l=Math.max(0,t.startColumn-s.startColumn),c=Math.min(s.endColumn,t.endColumn)-s.startColumn),this._renameInputVisible.set(!0),this._renameInputField.getInput(a,s.word,l,c).then(function(t){e._renameInputVisible.reset(),e.editor.focus();var i=e._prepareRename(t).then(function(i){return i.finish().then(function(o){o&&e.editor.setSelection(o),S.alert(n.localize(1,null,s.word,t,i.ariaMessage()))})},function(t){return"string"==typeof t?void e._messageService.show(o.default.Info,t):(e._messageService.show(o.default.Error,n.localize(2,null)),r.TPromise.wrapError(t))});return e._progressService.showWhile(i,250),i},function(t){if(e._renameInputVisible.reset(),e.editor.focus(),!i.isPromiseCanceledError(t))return r.TPromise.wrapError(t)})}},e.prototype.acceptRenameInput=function(){this._renameInputField.acceptInput()},e.prototype.cancelRenameInput=function(){this._renameInputField.cancelInput()},e.prototype._prepareRename=function(e){var t=p.createBulkEdit(this._textModelResolverService,this.editor,this._fileService);return L(this.editor.getModel(),this.editor.getPosition(),e).then(function(e){return e.rejectReason?r.TPromise.wrapError(new Error(e.rejectReason)):(t.add(e.edits),t)})},e.ID="editor.contrib.renameController",e=t=v([d.editorContribution,y(1,u.IMessageService),y(2,m.ITextModelService),y(3,l.IProgressService),y(4,a.IContextKeyService),y(5,C.IThemeService),y(6,_.optional(s.IFileService))],e);var t}(),M=function(e){function t(){return e.call(this,{id:"editor.action.rename",label:n.localize(3,null),alias:"Rename Symbol",precondition:a.ContextKeyExpr.and(h.EditorContextKeys.writable,h.EditorContextKeys.hasRenameProvider),kbOpts:{kbExpr:h.EditorContextKeys.textFocus,primary:60},menuOpts:{group:"1_modification",order:1.1}})||this}return f(t,e),t.prototype.run=function(e,t){var n=N.get(t);if(n)return n.run()},t=v([c.editorAction],t)}(c.EditorAction);t.RenameAction=M;var T=c.EditorCommand.bindToContribution(N.get);c.CommonEditorRegistry.registerEditorCommand(new T({id:"acceptRenameInput",precondition:x,handler:function(e){return e.acceptRenameInput()},kbOpts:{weight:c.CommonEditorRegistry.commandWeight(99),kbExpr:h.EditorContextKeys.focus,primary:3}})),c.CommonEditorRegistry.registerEditorCommand(new T({id:"cancelRenameInput",precondition:x,handler:function(e){return e.cancelRenameInput()},kbOpts:{weight:c.CommonEditorRegistry.commandWeight(99),kbExpr:h.EditorContextKeys.focus,primary:9,secondary:[1033]}})),c.CommonEditorRegistry.registerDefaultLanguageCommand("_executeDocumentRenameProvider",function(e,t,n){var o=n.newName;if("string"!=typeof o)throw i.illegalArgument("newName");return L(e,t,o)})}),define(d[530],h([1,0,372,81,9,11,7,10,3,4,110,234,63,46,19,25,109,51,58,137,14,23,76,395]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,f,g,m,C,b,w,S,E,L){"use strict";function x(e){return e&&e.match(k)?e:null}function N(e){if(!e)return!1;var t=e.suggestion;return!!t.documentation||t.detail&&t.detail!==t.label}Object.defineProperty(t,"__esModule",{value:!0});var M=!1;t.editorSuggestWidgetBackground=E.registerColor("editorSuggestWidget.background",{dark:E.editorWidgetBackground,light:E.editorWidgetBackground,hc:E.editorWidgetBackground},n.localize(0,null)),t.editorSuggestWidgetBorder=E.registerColor("editorSuggestWidget.border",{dark:E.editorWidgetBorder,light:E.editorWidgetBorder,hc:E.editorWidgetBorder},n.localize(1,null)),t.editorSuggestWidgetForeground=E.registerColor("editorSuggestWidget.foreground",{dark:E.editorForeground,light:E.editorForeground,hc:E.editorForeground},n.localize(2,null)),t.editorSuggestWidgetSelectedBackground=E.registerColor("editorSuggestWidget.selectedBackground",{dark:E.listFocusBackground,light:E.listFocusBackground,hc:E.listFocusBackground},n.localize(3,null)),t.editorSuggestWidgetHighlightForeground=E.registerColor("editorSuggestWidget.highlightForeground",{dark:E.listHighlightForeground,light:E.listHighlightForeground,hc:E.listHighlightForeground},n.localize(4,null));var T,k=/^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i,I=function(){function e(e,t,n){this.widget=e,this.editor=t,this.triggerKeybindingLabel=n}return Object.defineProperty(e.prototype,"templateId",{get:function(){return"suggestion"},enumerable:!0,configurable:!0}),e.prototype.renderTemplate=function(e){var t=this,i=Object.create(null);i.disposables=[],i.root=e,i.icon=l.append(e,l.$(".icon")),i.colorspan=l.append(i.icon,l.$("span.colorspan"));var o=l.append(e,l.$(".contents")),s=l.append(o,l.$(".main"));i.highlightedLabel=new c.HighlightedLabel(s),i.disposables.push(i.highlightedLabel),i.typeLabel=l.append(s,l.$("span.type-label")),i.readMore=l.append(s,l.$("span.readMore")),i.readMore.title=n.localize(5,null,this.triggerKeybindingLabel);var a=function(){var e=t.editor.getConfiguration(),n=e.fontInfo.fontFamily,o=(e.contribInfo.suggestFontSize||e.fontInfo.fontSize)+"px",r=(e.contribInfo.suggestLineHeight||e.fontInfo.lineHeight)+"px";i.root.style.fontSize=o,s.style.fontFamily=n,s.style.lineHeight=r,i.icon.style.height=r,i.icon.style.width=r,i.readMore.style.height=r,i.readMore.style.width=r};return a(),r.chain(this.editor.onDidChangeConfiguration.bind(this.editor)).filter(function(e){return e.fontInfo||e.contribInfo}).on(a,null,i.disposables),i},e.prototype.renderElement=function(e,t,o){var r=this,s=o,a=e.suggestion;if(N(e)?s.root.setAttribute("aria-label",n.localize(6,null,a.label)):s.root.setAttribute("aria-label",n.localize(7,null,a.label)),s.icon.className="icon "+a.type,s.colorspan.style.backgroundColor="","color"===a.type){var u=x(a.label)||x(a.documentation);u&&(s.icon.className="icon customcolor",s.colorspan.style.backgroundColor=u)}s.highlightedLabel.set(a.label,i.createMatches(e.matches)),s.typeLabel.textContent=(a.detail||"").replace(/\n.*$/m,""),N(e)?(l.show(s.readMore),s.readMore.onmousedown=function(e){e.stopPropagation(),e.preventDefault()},s.readMore.onclick=function(e){e.stopPropagation(),e.preventDefault(),r.widget.toggleDetails()}):(l.hide(s.readMore),s.readMore.onmousedown=null,s.readMore.onclick=null)},e.prototype.disposeTemplate=function(e){e.highlightedLabel.dispose(),e.disposables=u.dispose(e.disposables)},e}();!function(e){e[e.Hidden=0]="Hidden",e[e.Loading=1]="Loading",e[e.Empty=2]="Empty",e[e.Open=3]="Open",e[e.Frozen=4]="Frozen",e[e.Details=5]="Details"}(T||(T={}));var D=function(){function e(e,t,i,o){var s=this;this.widget=t,this.editor=i,this.triggerKeybindingLabel=o,this.borderWidth=1,this.disposables=[],this.el=l.append(e,l.$(".details")),this.disposables.push(u.toDisposable(function(){return e.removeChild(s.el)})),this.body=l.$(".body"),this.scrollbar=new h.DomScrollableElement(this.body,{}),l.append(this.el,this.scrollbar.getDomNode()),this.disposables.push(this.scrollbar),this.header=l.append(this.body,l.$(".header")),this.close=l.append(this.header,l.$("span.close")),this.close.title=n.localize(8,null,o),this.type=l.append(this.header,l.$("p.type")),this.docs=l.append(this.body,l.$("p.docs")),this.ariaLabel=null,this.configureFont(),r.chain(this.editor.onDidChangeConfiguration.bind(this.editor)).filter(function(e){return e.fontInfo}).on(this.configureFont,this,this.disposables)}return Object.defineProperty(e.prototype,"element",{get:function(){return this.el},enumerable:!0,configurable:!0}),e.prototype.render=function(e){var t=this;if(!e||!N(e))return this.type.textContent="",this.docs.textContent="",l.addClass(this.el,"no-docs"),void(this.ariaLabel=null);l.removeClass(this.el,"no-docs"),this.docs.textContent=e.suggestion.documentation,e.suggestion.detail?(this.type.innerText=e.suggestion.detail,l.show(this.type)):(this.type.innerText="",l.hide(this.type)),this.el.style.height=this.header.offsetHeight+this.docs.offsetHeight+2*this.borderWidth+"px",this.close.onmousedown=function(e){e.preventDefault(),e.stopPropagation()},this.close.onclick=function(e){e.preventDefault(),e.stopPropagation(),t.widget.toggleDetails()},this.body.scrollTop=0,this.scrollbar.scanDomNode(),this.ariaLabel=o.format("{0}\n{1}\n{2}",e.suggestion.label||"",e.suggestion.detail||"",e.suggestion.documentation||"")},e.prototype.getAriaLabel=function(){return this.ariaLabel},e.prototype.scrollDown=function(e){void 0===e&&(e=8),this.body.scrollTop+=e},e.prototype.scrollUp=function(e){void 0===e&&(e=8),this.body.scrollTop-=e},e.prototype.scrollTop=function(){this.body.scrollTop=0},e.prototype.scrollBottom=function(){this.body.scrollTop=this.body.scrollHeight},e.prototype.pageDown=function(){this.scrollDown(80)},e.prototype.pageUp=function(){this.scrollUp(80)},e.prototype.setBorderWidth=function(e){this.borderWidth=e},e.prototype.configureFont=function(){var e=this.editor.getConfiguration(),t=e.fontInfo.fontFamily,n=(e.contribInfo.suggestFontSize||e.fontInfo.fontSize)+"px",i=(e.contribInfo.suggestLineHeight||e.fontInfo.lineHeight)+"px";this.el.style.fontSize=n,this.type.style.fontFamily=t,this.close.style.height=i,this.close.style.width=i},e.prototype.dispose=function(){this.disposables=u.dispose(this.disposables)},e}(),O=function(){function e(e,n,i,o,s,a){var u=this;this.editor=e,this.telemetryService=n,this.allowEditorOverflow=!0,this.ignoreFocusEvents=!1,this.onDidSelectEmitter=new r.Emitter,this.onDidFocusEmitter=new r.Emitter,this.onDidHideEmitter=new r.Emitter,this.onDidShowEmitter=new r.Emitter,this.onDidSelect=this.onDidSelectEmitter.event,this.onDidFocus=this.onDidFocusEmitter.event,this.onDidHide=this.onDidHideEmitter.event,this.onDidShow=this.onDidShowEmitter.event,this.maxWidgetWidth=660,this.listWidth=330,this.storageServiceAvailable=!0,this.expandSuggestionDocs=!1;var c=a.lookupKeybinding("editor.action.triggerSuggest"),h=c?" ("+c.getLabel()+")":"";this.isAuto=!1,this.focusedItem=null,this.storageService=s,void 0===this.expandDocsSettingFromStorage()&&(this.storageService.store("expandSuggestionDocs",M,L.StorageScope.GLOBAL),void 0===this.expandDocsSettingFromStorage()&&(this.storageServiceAvailable=!1)),this.element=l.$(".editor-widget.suggest-widget"),this.editor.getConfiguration().contribInfo.iconsInSuggestions||l.addClass(this.element,"no-icons"),this.messageElement=l.append(this.element,l.$(".message")),this.listElement=l.append(this.element,l.$(".tree")),this.details=new D(this.element,this,this.editor,h);var p=new I(this,this.editor,h);this.list=new d.List(this.listElement,this,[p],{useShadows:!1,selectOnMouseDown:!0}),this.toDispose=[w.attachListStyler(this.list,o,{listInactiveFocusBackground:t.editorSuggestWidgetSelectedBackground,listInactiveFocusOutline:E.activeContrastBorder}),o.onThemeChange(function(e){return u.onThemeChange(e)}),e.onDidBlurEditorText(function(){return u.onEditorBlur()}),e.onDidLayoutChange(function(){return u.onEditorLayoutChange()}),this.list.onSelectionChange(function(e){return u.onListSelection(e)}),this.list.onFocusChange(function(e){return u.onListFocus(e)}),this.editor.onDidChangeCursorSelection(function(){return u.onCursorSelectionChanged()})],this.suggestWidgetVisible=m.Context.Visible.bindTo(i),this.suggestWidgetMultipleSuggestions=m.Context.MultipleSuggestions.bindTo(i),this.suggestionSupportsAutoAccept=m.Context.AcceptOnKey.bindTo(i),this.editor.addContentWidget(this),this.setState(0),this.onThemeChange(o.getTheme())}return e.prototype.onCursorSelectionChanged=function(){0!==this.state&&this.editor.layoutContentWidget(this)},e.prototype.onEditorBlur=function(){var e=this;this.editorBlurTimeout=s.TPromise.timeout(150).then(function(){e.editor.isFocused()||e.setState(0)})},e.prototype.onEditorLayoutChange=function(){3!==this.state&&5!==this.state||!this.expandDocsSettingFromStorage()||this.expandSideOrBelow()},e.prototype.onListSelection=function(e){if(e.elements.length){var t=e.elements[0];this.onDidSelectEmitter.fire(t),C.alert(n.localize(11,null,t.suggestion.label)),this.editor.focus()}},e.prototype._getSuggestionAriaAlertLabel=function(e){return N(e)?n.localize(12,null,e.suggestion.label):n.localize(13,null,e.suggestion.label)},e.prototype._ariaAlert=function(e){this._lastAriaAlertLabel!==e&&(this._lastAriaAlertLabel=e,this._lastAriaAlertLabel&&C.alert(this._lastAriaAlertLabel))},e.prototype.onThemeChange=function(e){var n=e.getColor(t.editorSuggestWidgetBackground);n&&(this.listElement.style.backgroundColor=n.toString(),this.details.element.style.backgroundColor=n.toString(),this.messageElement.style.backgroundColor=n.toString());var i=e.getColor(t.editorSuggestWidgetBorder);i&&(this.listElement.style.borderColor=i.toString(),this.details.element.style.borderColor=i.toString(),this.messageElement.style.borderColor=i.toString(),this.detailsBorderColor=i.toString());var o=e.getColor(E.focusBorder);o&&(this.detailsFocusBorderColor=o.toString()),this.details.setBorderWidth("hc"===e.type?2:1)},e.prototype.onListFocus=function(e){var t=this;if(!this.ignoreFocusEvents){if(!e.elements.length)return this.currentSuggestionDetails&&(this.currentSuggestionDetails.cancel(),this.currentSuggestionDetails=null,this.focusedItem=null),void this._ariaAlert(null);var n=e.elements[0];if(this._ariaAlert(this._getSuggestionAriaAlertLabel(n)),n!==this.focusedItem){this.currentSuggestionDetails&&(this.currentSuggestionDetails.cancel(),this.currentSuggestionDetails=null);var i=e.indexes[0];this.suggestionSupportsAutoAccept.set(!n.suggestion.noAutoAccept);var o=this.focusedItem,r=this.focusedItemIndex;this.focusedItemIndex=i,this.focusedItem=n,o&&(this.ignoreFocusEvents=!0,this.list.splice(r,1,[o]),this.ignoreFocusEvents=!1),this.updateListHeight(),this.list.reveal(i),this.currentSuggestionDetails=n.resolve().then(function(){t.ignoreFocusEvents=!0,t.list.splice(i,1,[n]),t.ignoreFocusEvents=!1,t.list.setFocus([i]),t.list.reveal(i),t.expandDocsSettingFromStorage()?t.showDetails():l.removeClass(t.element,"docs-side")}).then(null,function(e){return!a.isPromiseCanceledError(e)&&a.onUnexpectedError(e)}).then(function(){return t.currentSuggestionDetails=null}),this.onDidFocusEmitter.fire(n)}}},e.prototype.setState=function(t){if(this.element){var n=this.state!==t;switch(this.state=t,l.toggleClass(this.element,"frozen",4===t),t){case 0:l.hide(this.messageElement,this.details.element),l.show(this.listElement),this.hide(),n&&this.list.splice(0,this.list.length);break;case 1:this.messageElement.textContent=e.LOADING_MESSAGE,l.hide(this.listElement,this.details.element),l.show(this.messageElement),l.removeClass(this.element,"docs-side"),this.show();break;case 2:this.messageElement.textContent=e.NO_SUGGESTIONS_MESSAGE,l.hide(this.listElement,this.details.element),l.show(this.messageElement),l.removeClass(this.element,"docs-side"),this.show();break;case 3:l.hide(this.messageElement),l.show(this.listElement),this.expandDocsSettingFromStorage()&&N(this.list.getFocusedElements()[0])?(l.show(this.details.element),this.expandSideOrBelow()):l.hide(this.details.element),this.show();break;case 4:l.hide(this.messageElement,this.details.element),l.show(this.listElement),this.show();break;case 5:l.hide(this.messageElement),l.show(this.details.element,this.listElement),this.show(),this._ariaAlert(this.details.getAriaLabel())}n&&this.editor.layoutContentWidget(this)}},e.prototype.showTriggered=function(e){var t=this;0===this.state&&(this.isAuto=!!e,this.isAuto||(this.loadingTimeout=setTimeout(function(){t.loadingTimeout=null,t.setState(1)},50)))},e.prototype.showSuggestions=function(e,t,n){if(this.loadingTimeout&&(clearTimeout(this.loadingTimeout),this.loadingTimeout=null),this.completionModel=e,t&&2!==this.state&&0!==this.state)this.setState(4);else{var i=this.completionModel.items.length,o=0===i;if(this.suggestWidgetMultipleSuggestions.set(i>1),o)n?this.setState(0):this.setState(2),this.completionModel=null;else{var r=this.completionModel.stats;r.wasAutomaticallyTriggered=!!n,this.telemetryService.publicLog("suggestWidget",_({},r,this.editor.getTelemetryData())),this.focusedItem=null,this.focusedItemIndex=null,this.list.splice(0,this.list.length,this.completionModel.items),this.list.setFocus([0]),this.list.reveal(0,0),t?this.setState(4):this.setState(3),this.detailsBorderColor&&(this.details.element.style.borderColor=this.detailsBorderColor)}}},e.prototype.selectNextPage=function(){switch(this.state){case 0:return!1;case 5:return this.details.pageDown(),!0;case 1:return!this.isAuto;default:return this.list.focusNextPage(),!0}},e.prototype.selectNext=function(){switch(this.state){case 0:return!1;case 1:return!this.isAuto;default:return this.list.focusNext(1,!0),!0}},e.prototype.selectLast=function(){switch(this.state){case 0:return!1;case 5:return this.details.scrollBottom(),!0;case 1:return!this.isAuto;default:return this.list.focusLast(),!0}},e.prototype.selectPreviousPage=function(){switch(this.state){case 0:return!1;case 5:return this.details.pageUp(),!0;case 1:return!this.isAuto;default:return this.list.focusPreviousPage(),!0}},e.prototype.selectPrevious=function(){switch(this.state){case 0:return!1;case 1:return!this.isAuto;default:return this.list.focusPrevious(1,!0),!1}},e.prototype.selectFirst=function(){switch(this.state){case 0:return!1;case 5:return this.details.scrollTop(),!0;case 1:return!this.isAuto;default:return this.list.focusFirst(),!0}},e.prototype.getFocusedItem=function(){if(0!==this.state&&2!==this.state&&1!==this.state)return this.list.getFocusedElements()[0]},e.prototype.toggleDetailsFocus=function(){5===this.state?(this.setState(3),this.detailsBorderColor&&(this.details.element.style.borderColor=this.detailsBorderColor)):3===this.state&&this.expandDocsSettingFromStorage()&&(this.setState(5),this.detailsFocusBorderColor&&(this.details.element.style.borderColor=this.detailsFocusBorderColor)),this.telemetryService.publicLog("suggestWidget:toggleDetailsFocus",this.editor.getTelemetryData())},e.prototype.toggleDetails=function(){if(N(this.list.getFocusedElements()[0]))if(this.expandDocsSettingFromStorage())this.updateExpandDocsSetting(!1),l.hide(this.details.element),l.removeClass(this.element,"docs-side"),l.removeClass(this.element,"docs-below"),this.editor.layoutContentWidget(this),this.telemetryService.publicLog("suggestWidget:collapseDetails",this.editor.getTelemetryData());else{if(3!==this.state&&5!==this.state)return;this.updateExpandDocsSetting(!0),this.showDetails(),this.telemetryService.publicLog("suggestWidget:expandDetails",this.editor.getTelemetryData())}},e.prototype.showDetails=function(){this.expandSideOrBelow(),l.show(this.details.element),this.details.render(this.list.getFocusedElements()[0]),this.details.element.style.maxHeight=this.maxWidgetHeight+"px",this.listElement.style.marginTop="0px",this.editor.layoutContentWidget(this),this.adjustDocsPosition(),this.editor.focus(),this._ariaAlert(this.details.getAriaLabel())},e.prototype.show=function(){var e=this;this.updateListHeight(),this.suggestWidgetVisible.set(!0),this.showTimeout=s.TPromise.timeout(100).then(function(){l.addClass(e.element,"visible"),e.onDidShowEmitter.fire(e)})},e.prototype.hide=function(){this.suggestWidgetVisible.reset(),this.suggestWidgetMultipleSuggestions.reset(),l.removeClass(this.element,"visible")},e.prototype.hideWidget=function(){clearTimeout(this.loadingTimeout),this.setState(0),this.onDidHideEmitter.fire(this)},e.prototype.getPosition=function(){return 0===this.state?null:{position:this.editor.getPosition(),preference:[g.ContentWidgetPositionPreference.BELOW,g.ContentWidgetPositionPreference.ABOVE]}},e.prototype.getDomNode=function(){return this.element},e.prototype.getId=function(){return e.ID},e.prototype.updateListHeight=function(){var e=0;if(2===this.state||1===this.state)e=this.unfocusedHeight;else{var t=this.list.contentHeight/this.unfocusedHeight;e=Math.min(t,12)*this.unfocusedHeight}return this.element.style.lineHeight=this.unfocusedHeight+"px",this.listElement.style.height=e+"px",this.list.layout(e),this.editor.layoutContentWidget(this),e},e.prototype.adjustDocsPosition=function(){var e=this.editor.getConfiguration().fontInfo.lineHeight,t=this.editor.getScrolledVisiblePosition(this.editor.getPosition()),n=l.getDomNodePagePosition(this.editor.getDomNode()),i=n.left+t.left,o=n.top+t.top+t.height,r=l.getDomNodePagePosition(this.element),s=r.left,a=r.top;sa&&this.details.element.offsetHeight>this.listElement.offsetHeight&&(this.listElement.style.marginTop=this.details.element.offsetHeight-this.listElement.offsetHeight+"px")},e.prototype.expandSideOrBelow=function(){var e=this.element.style.maxWidth.match(/(\d+)px/);!e||Number(e[1])0&&this._activeAcceptCharacters.add(i[0])}}else this.reset()},e.prototype.reset=function(){this._activeItem=void 0},e.prototype.dispose=function(){r.dispose(this._disposables)},e}(),x=function(){function e(e,t,n,i,o){var r=this;this._editor=e,this._commandService=t,this._telemetryService=n,this._toDispose=[],this._model=new S.SuggestModel(this._editor),this._toDispose.push(this._model.onDidTrigger(function(e){return r._widget.showTriggered(e.auto)})),this._toDispose.push(this._model.onDidSuggest(function(e){return r._widget.showSuggestions(e.completionModel,e.isFrozen,e.auto)})),this._toDispose.push(this._model.onDidCancel(function(e){return!e.retrigger&&r._widget.hideWidget()}));var s=w.Context.AcceptSuggestionsOnEnter.bindTo(i),a=function(){var e=r._editor.getConfiguration().contribInfo.acceptSuggestionOnEnter;s.set("on"===e||"smart"===e||!0===e)};this._toDispose.push(this._editor.onDidChangeConfiguration(function(e){return a()})),a(),this._widget=o.createInstance(E.SuggestWidget,this._editor),this._toDispose.push(this._widget.onDidSelect(this._onDidSelectItem,this));var u=new L(e,this._widget,function(e){return r._onDidSelectItem(e)});this._toDispose.push(u,this._model.onDidSuggest(function(e){0===e.completionModel.items.length&&u.reset()}));var l=w.Context.MakesTextEdit.bindTo(i);this._toDispose.push(this._widget.onDidFocus(function(e){var t=r._editor.getPosition(),n=e.position.column-e.suggestion.overwriteBefore,i=t.column,o=!0;"smart"!==r._editor.getConfiguration().contribInfo.acceptSuggestionOnEnter||2!==r._model.state||e.suggestion.command||e.suggestion.additionalTextEdits||"textmate"===e.suggestion.snippetType||i-n!==e.suggestion.insertText.length||(o=r._editor.getModel().getValueInRange({startLineNumber:t.lineNumber,startColumn:n,endLineNumber:t.lineNumber,endColumn:i})!==e.suggestion.insertText),l.set(o)})),this._toDispose.push({dispose:function(){l.reset()}})}return t=e,e.get=function(e){return e.getContribution(t.ID)},e.prototype.getId=function(){return t.ID},e.prototype.dispose=function(){this._toDispose=r.dispose(this._toDispose),this._widget&&(this._widget.dispose(),this._widget=null),this._model&&(this._model.dispose(),this._model=null)},e.prototype._onDidSelectItem=function(e){if(e){var t=e.suggestion,n=e.position,o=this._editor.getPosition().column-n.column;Array.isArray(t.additionalTextEdits)&&(this._editor.pushUndoStop(),this._editor.executeEdits("suggestController.additionalTextEdits",t.additionalTextEdits.map(function(e){return g.EditOperation.replace(m.Range.lift(e.range),e.text)})),this._editor.pushUndoStop());var r=t.insertText;"textmate"!==t.snippetType&&(r=C.SnippetParser.escape(r)),b.SnippetController2.get(this._editor).insert(r,t.overwriteBefore+o,t.overwriteAfter),t.command&&(s=this._commandService).executeCommand.apply(s,[t.command.id].concat(t.command.arguments)).done(void 0,i.onUnexpectedError),this._alertCompletionItem(e),this._telemetryService.publicLog("suggestSnippetInsert",_({},this._editor.getTelemetryData(),{suggestionType:t.type}))}this._model.cancel();var s},e.prototype._alertCompletionItem=function(e){var t=e.suggestion,i=n.localize(0,null,t.label,t.insertText);h.alert(i)},e.prototype.triggerSuggest=function(e){this._model.trigger(!1,!1,e),this._editor.revealLine(this._editor.getPosition().lineNumber),this._editor.focus()},e.prototype.acceptSelectedSuggestion=function(){if(this._widget){var e=this._widget.getFocusedItem();this._onDidSelectItem(e)}},e.prototype.cancelSuggestWidget=function(){this._widget&&(this._model.cancel(),this._widget.hideWidget())},e.prototype.selectNextSuggestion=function(){this._widget&&this._widget.selectNext()},e.prototype.selectNextPageSuggestion=function(){this._widget&&this._widget.selectNextPage()},e.prototype.selectLastSuggestion=function(){this._widget&&this._widget.selectLast()},e.prototype.selectPrevSuggestion=function(){this._widget&&this._widget.selectPrevious()},e.prototype.selectPrevPageSuggestion=function(){this._widget&&this._widget.selectPreviousPage()},e.prototype.selectFirstSuggestion=function(){this._widget&&this._widget.selectFirst()},e.prototype.toggleSuggestionDetails=function(){this._widget&&this._widget.toggleDetails()},e.prototype.toggleSuggestionFocus=function(){this._widget&&this._widget.toggleDetailsFocus()},e.ID="editor.contrib.suggestController",e=t=v([p.editorContribution,y(1,l.ICommandService),y(2,a.ITelemetryService),y(3,u.IContextKeyService),y(4,s.IInstantiationService)],e);var t}();t.SuggestController=x;var N=function(e){function t(){return e.call(this,{id:"editor.action.triggerSuggest",label:n.localize(1,null),alias:"Trigger Suggest",precondition:u.ContextKeyExpr.and(c.EditorContextKeys.writable,c.EditorContextKeys.hasCompletionItemProvider),kbOpts:{kbExpr:c.EditorContextKeys.textFocus,primary:2058,mac:{primary:266}}})||this}return f(t,e),t.prototype.run=function(e,t){var n=x.get(t);n&&n.triggerSuggest()},t=v([d.editorAction],t)}(d.EditorAction);t.TriggerSuggestAction=N;var M=d.CommonEditorRegistry.commandWeight(90),T=d.EditorCommand.bindToContribution(x.get);d.CommonEditorRegistry.registerEditorCommand(new T({id:"acceptSelectedSuggestion",precondition:w.Context.Visible,handler:function(e){return e.acceptSelectedSuggestion()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:2}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"acceptSelectedSuggestionOnEnter",precondition:w.Context.Visible,handler:function(e){return e.acceptSelectedSuggestion()},kbOpts:{weight:M,kbExpr:u.ContextKeyExpr.and(c.EditorContextKeys.textFocus,w.Context.AcceptSuggestionsOnEnter,w.Context.MakesTextEdit),primary:3}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"hideSuggestWidget",precondition:w.Context.Visible,handler:function(e){return e.cancelSuggestWidget()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:9,secondary:[1033]}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"selectNextSuggestion",precondition:u.ContextKeyExpr.and(w.Context.Visible,w.Context.MultipleSuggestions),handler:function(e){return e.selectNextSuggestion()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:18,secondary:[2066],mac:{primary:18,secondary:[2066,300]}}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"selectNextPageSuggestion",precondition:u.ContextKeyExpr.and(w.Context.Visible,w.Context.MultipleSuggestions),handler:function(e){return e.selectNextPageSuggestion()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:12,secondary:[2060]}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"selectLastSuggestion",precondition:u.ContextKeyExpr.and(w.Context.Visible,w.Context.MultipleSuggestions),handler:function(e){return e.selectLastSuggestion()}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"selectPrevSuggestion",precondition:u.ContextKeyExpr.and(w.Context.Visible,w.Context.MultipleSuggestions),handler:function(e){return e.selectPrevSuggestion()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:16,secondary:[2064],mac:{primary:16,secondary:[2064,302]}}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"selectPrevPageSuggestion",precondition:u.ContextKeyExpr.and(w.Context.Visible,w.Context.MultipleSuggestions),handler:function(e){return e.selectPrevPageSuggestion()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:11,secondary:[2059]}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"selectFirstSuggestion",precondition:u.ContextKeyExpr.and(w.Context.Visible,w.Context.MultipleSuggestions),handler:function(e){return e.selectFirstSuggestion()}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"toggleSuggestionDetails",precondition:w.Context.Visible,handler:function(e){return e.toggleSuggestionDetails()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:2058,mac:{primary:266}}})),d.CommonEditorRegistry.registerEditorCommand(new T({id:"toggleSuggestionFocus",precondition:w.Context.Visible,handler:function(e){return e.toggleSuggestionFocus()},kbOpts:{weight:M,kbExpr:c.EditorContextKeys.textFocus,primary:2570,mac:{primary:778}}}))}),define(d[532],h([1,0,374,18,10,2,20,13,17,3,23,14,54,34]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p){"use strict";function f(e,t){var n=u.DocumentHighlightProviderRegistry.ordered(e),r=!1;return i.sequence(n.map(function(n){return function(){if(!r)return i.asWinJsPromise(function(i){return n.provideDocumentHighlights(e,t,i)}).then(function(e){if(Array.isArray(e)&&e.length>0)return r=!0,e},function(e){o.onUnexpectedExternalError(e)})}})).then(function(e){return e[0]})}Object.defineProperty(t,"__esModule",{value:!0}),t.editorWordHighlight=c.registerColor("editor.wordHighlightBackground",{dark:"#575757B8",light:"#57575740",hc:null},n.localize(0,null)),t.editorWordHighlightStrong=c.registerColor("editor.wordHighlightStrongBackground",{dark:"#004972B8",light:"#0e639c40",hc:null},n.localize(1,null)),t.getOccurrencesAtPosition=f,a.CommonEditorRegistry.registerDefaultLanguageCommand("_executeDocumentHighlights",f);var g=function(){function e(e){var t=this;this.workerRequestTokenId=0,this.workerRequest=null,this.workerRequestCompleted=!1,this.workerRequestValue=[],this.lastCursorPositionChangeTime=0,this.renderDecorationsTimer=-1,this.editor=e,this.occurrencesHighlight=this.editor.getConfiguration().contribInfo.occurrencesHighlight,this.model=this.editor.getModel(),this.toUnhook=[],this.toUnhook.push(e.onDidChangeCursorPosition(function(e){t.occurrencesHighlight&&t._onPositionChanged(e)})),this.toUnhook.push(e.onDidChangeModel(function(e){t._stopAll(),t.model=t.editor.getModel()})),this.toUnhook.push(e.onDidChangeModelContent(function(e){t._stopAll()})),this.toUnhook.push(e.onDidChangeConfiguration(function(e){var n=t.editor.getConfiguration().contribInfo.occurrencesHighlight;t.occurrencesHighlight!==n&&(t.occurrencesHighlight=n,t._stopAll())})),this._lastWordRange=null,this._decorationIds=[],this.workerRequestTokenId=0,this.workerRequest=null,this.workerRequestCompleted=!1,this.lastCursorPositionChangeTime=0,this.renderDecorationsTimer=-1}return e.prototype._removeDecorations=function(){this._decorationIds.length>0&&(this._decorationIds=this.editor.deltaDecorations(this._decorationIds,[]))},e.prototype._stopAll=function(){this._lastWordRange=null,this._removeDecorations(),-1!==this.renderDecorationsTimer&&(clearTimeout(this.renderDecorationsTimer),this.renderDecorationsTimer=-1),null!==this.workerRequest&&(this.workerRequest.cancel(),this.workerRequest=null),this.workerRequestCompleted||(this.workerRequestTokenId++,this.workerRequestCompleted=!0)},e.prototype._onPositionChanged=function(e){var t=this;if(this.occurrencesHighlight)if(e.reason===h.CursorChangeReason.Explicit)if(u.DocumentHighlightProviderRegistry.has(this.model)){var n=this.editor.getSelection();if(n.startLineNumber===n.endLineNumber){var i=n.startLineNumber,o=n.startColumn,s=n.endColumn,a=this.model.getWordAtPosition({lineNumber:i,column:o});if(!a||a.startColumn>o||a.endColumn=s&&(c=!0)}if(this.lastCursorPositionChangeTime=(new Date).getTime(),c)this.workerRequestCompleted&&-1!==this.renderDecorationsTimer&&(clearTimeout(this.renderDecorationsTimer),this.renderDecorationsTimer=-1,this._beginRenderDecorations());else{this._stopAll();var m=++this.workerRequestTokenId;this.workerRequestCompleted=!1,this.workerRequest=f(this.model,this.editor.getPosition()),this.workerRequest.then(function(e){m===t.workerRequestTokenId&&(t.workerRequestCompleted=!0,t.workerRequestValue=e||[],t._beginRenderDecorations())}).done()}this._lastWordRange=l}}else this._stopAll()}else this._stopAll();else this._stopAll();else this._stopAll()},e.prototype._beginRenderDecorations=function(){var e=this,t=(new Date).getTime(),n=this.lastCursorPositionChangeTime+250;t>=n?(this.renderDecorationsTimer=-1,this.renderDecorations()):this.renderDecorationsTimer=setTimeout(function(){e.renderDecorations()},n-t)},e.prototype.renderDecorations=function(){this.renderDecorationsTimer=-1;for(var t=[],n=0,i=this.workerRequestValue.length;n0?o.format(O,e.length):null:T}Object.defineProperty(t,"__esModule",{value:!0});var N=new d.RawContextKey("accessibilityHelpWidgetVisible",!1),M=function(e){function t(t,n){var i=e.call(this)||this;return i._editor=t,i._widget=i._register(n.createInstance(R,i._editor)),i}return f(t,e),n=t,t.get=function(e){return e.getContribution(n.ID)},t.prototype.getId=function(){return n.ID},t.prototype.show=function(){this._widget.show()},t.prototype.hide=function(){this._widget.hide()},t.ID="editor.contrib.accessibilityHelpController",t=n=v([g.editorContribution,y(1,l.IInstantiationService)],t);var n}(i.Disposable),T=n.localize("noSelection","No selection"),k=n.localize("singleSelectionRange","Line {0}, Column {1} ({2} selected)"),I=n.localize("singleSelection","Line {0}, Column {1}"),D=n.localize("multiSelectionRange","{0} selections ({1} characters selected)"),O=n.localize("multiSelection","{0} selections"),R=function(e){function t(t,i,o,s){var u=e.call(this)||this;return u._contextKeyService=i,u._keybindingService=o,u._openerService=s,u._editor=t,u._isVisibleKey=N.bindTo(u._contextKeyService),u._domNode=a.createFastDomNode(document.createElement("div")),u._domNode.setClassName("accessibilityHelpWidget"),u._domNode.setDisplay("none"),u._domNode.setAttribute("role","dialog"),u._domNode.setAttribute("aria-hidden","true"),u._contentDomNode=a.createFastDomNode(document.createElement("div")),u._contentDomNode.setAttribute("role","document"),u._domNode.appendChild(u._contentDomNode),u._isVisible=!1,u._register(u._editor.onDidLayoutChange(function(){u._isVisible&&u._layout()})),u._register(r.addStandardDisposableListener(u._contentDomNode.domNode,"keydown",function(e){if(u._isVisible&&(e.equals(2083)&&(w.alert(n.localize("emergencyConfOn","Now changing the setting `accessibilitySupport` to 'on'.")),u._editor.updateOptions({accessibilitySupport:"on"}),r.clearNode(u._contentDomNode.domNode),u._buildContent(),u._contentDomNode.domNode.focus(),e.preventDefault(),e.stopPropagation()),e.equals(2086))){w.alert(n.localize("openingDocs","Now opening the Editor Accessibility documentation page."));var t=u._editor.getRawConfiguration().accessibilityHelpUrl;void 0===t&&(t="https://go.microsoft.com/fwlink/?linkid=852450"),u._openerService.open(E.default.parse(t)),e.preventDefault(),e.stopPropagation()}})),u.onblur(u._contentDomNode.domNode,function(){u.hide()}),u._editor.addOverlayWidget(u),u}return f(t,e),t.prototype.dispose=function(){this._editor.removeOverlayWidget(this),e.prototype.dispose.call(this)},t.prototype.getId=function(){return t.ID},t.prototype.getDomNode=function(){return this._domNode.domNode},t.prototype.getPosition=function(){return{preference:null}},t.prototype.show=function(){this._isVisible||(this._isVisible=!0,this._isVisibleKey.set(!0),this._layout(),this._domNode.setDisplay("block"),this._domNode.setAttribute("aria-hidden","false"),this._contentDomNode.domNode.tabIndex=0,this._buildContent(),this._contentDomNode.domNode.focus())},t.prototype._descriptionForCommand=function(e,t,n){var i=this._keybindingService.lookupKeybinding(e);return i?o.format(t,i.getAriaLabel()):o.format(n,e)},t.prototype._buildContent=function(){var e=this._editor.getConfiguration(),t=this._editor.getSelections(),i=0;if(t){var o=this._editor.getModel();o&&t.forEach(function(e){i+=o.getValueLengthInRange(e)})}var r=x(t,i);switch(e.wrappingInfo.inDiffEditor?e.readOnly?r+=n.localize("readonlyDiffEditor"," in a read-only pane of a diff editor."):r+=n.localize("editableDiffEditor"," in a pane of a diff editor."):e.readOnly?r+=n.localize("readonlyEditor"," in a read-only code editor"):r+=n.localize("editableEditor"," in a code editor"),e.accessibilitySupport){case 0:var a=b.isMacintosh?n.localize("changeConfigToOnMac","To configure the editor to be optimized for usage with a Screen Reader press Command+E now."):n.localize("changeConfigToOnWinLinux","To configure the editor to be optimized for usage with a Screen Reader press Control+E now.");r+="\n\n - "+a;break;case 2:r+="\n\n - "+n.localize("auto_on","The editor is configured to be optimized for usage with a Screen Reader.");break;case 1:r+="\n\n - "+n.localize("auto_off","The editor is configured to never be optimized for usage with a Screen Reader, which is not the case at this time."),r+=" "+a}var u=n.localize("tabFocusModeOnMsg","Pressing Tab in the current editor will move focus to the next focusable element. Toggle this behavior by pressing {0}."),l=n.localize("tabFocusModeOnMsgNoKb","Pressing Tab in the current editor will move focus to the next focusable element. The command {0} is currently not triggerable by a keybinding."),c=n.localize("tabFocusModeOffMsg","Pressing Tab in the current editor will insert the tab character. Toggle this behavior by pressing {0}."),d=n.localize("tabFocusModeOffMsgNoKb","Pressing Tab in the current editor will insert the tab character. The command {0} is currently not triggerable by a keybinding.");e.tabFocusMode?r+="\n\n - "+this._descriptionForCommand(m.ToggleTabFocusModeAction.ID,u,l):r+="\n\n - "+this._descriptionForCommand(m.ToggleTabFocusModeAction.ID,c,d),r+="\n\n - "+(b.isMacintosh?n.localize("openDocMac","Press Command+H now to open a browser window with more information related to editor accessibility."):n.localize("openDocWinLinux","Press Control+H now to open a browser window with more information related to editor accessibility.")),r+="\n\n"+n.localize("outroMsg","You can dismiss this tooltip and return to the editor by pressing Escape or Shift+Escape."),this._contentDomNode.domNode.appendChild(s.renderFormattedText(r)),this._contentDomNode.domNode.setAttribute("aria-label",r)},t.prototype.hide=function(){this._isVisible&&(this._isVisible=!1,this._isVisibleKey.reset(),this._domNode.setDisplay("none"),this._domNode.setAttribute("aria-hidden","true"),this._contentDomNode.domNode.tabIndex=-1,r.clearNode(this._contentDomNode.domNode),this._editor.focus())},t.prototype._layout=function(){var e=this._editor.getLayoutInfo(),n=Math.max(5,Math.min(t.WIDTH,e.width-40)),i=Math.max(5,Math.min(t.HEIGHT,e.height-40));this._domNode.setWidth(n),this._domNode.setHeight(i);var o=Math.round((e.height-i)/2);this._domNode.setTop(o);var r=Math.round((e.width-n)/2);this._domNode.setLeft(r)},t.ID="editor.contrib.accessibilityHelpWidget",t.WIDTH=500,t.HEIGHT=300,t=v([y(1,d.IContextKeyService),y(2,c.IKeybindingService),y(3,S.IOpenerService)],t)}(u.Widget),P=(function(e){function t(){return e.call(this,{id:"editor.action.showAccessibilityHelp",label:n.localize("ShowAccessibilityHelpAction","Show Accessibility Help"),alias:"Show Accessibility Help",precondition:null,kbOpts:{kbExpr:h.EditorContextKeys.focus,primary:L.isIE?2107:571}})||this}f(t,e),t.prototype.run=function(e,t){var n=M.get(t);n&&n.show()},t=v([p.editorAction],t)}(p.EditorAction),p.EditorCommand.bindToContribution(M.get));p.CommonEditorRegistry.registerEditorCommand(new P({id:"closeAccessibilityHelp",precondition:N,handler:function(e){return e.hide()},kbOpts:{weight:p.CommonEditorRegistry.commandWeight(100),kbExpr:h.EditorContextKeys.focus,primary:9,secondary:[1033]}})),_.registerThemingParticipant(function(e,t){var n=e.getColor(C.editorWidgetBackground);n&&t.addRule(".monaco-editor .accessibilityHelpWidget { background-color: "+n+"; }");var i=e.getColor(C.widgetShadow);i&&t.addRule(".monaco-editor .accessibilityHelpWidget { box-shadow: 0 2px 8px "+i+"; }");var o=e.getColor(C.contrastBorder);o&&t.addRule(".monaco-editor .accessibilityHelpWidget { border: 2px solid "+o+"; }")})}),define(d[535],h([1,0,376,3,9,13,25,30,88,116,17,77,68,14,23,403]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g){"use strict";function m(e){for(var t="",n=0,i=e.length;n=0;i--){var r=t.tokens1[i];if(e.column-1>=r.offset){n=i;break}}for(var s=0,i=t.tokens2.length>>>1;i>=0;i--)if(e.column-1>=t.tokens2[i<<1]){s=i;break}var a="",u=this._model.getLineContent(e.lineNumber),l="";if(n'+m(l)+'('+l.length+" "+(1===l.length?"char":"chars")+")",a+='
    ';var h=this._decodeMetadata(t.tokens2[1+(s<<1)]);a+='',a+='",a+='",a+='",a+='",a+='",a+="",a+='
    ',n'+o.escape(t.tokens1[n].type)+""),this._domNode.innerHTML=a,this._editor.layoutContentWidget(this)},t.prototype._decodeMetadata=function(e){var t=c.TokenizationRegistry.getColorMap(),n=l.TokenMetadata.getLanguageId(e),i=l.TokenMetadata.getTokenType(e),o=l.TokenMetadata.getFontStyle(e),r=l.TokenMetadata.getForeground(e),s=l.TokenMetadata.getBackground(e);return{languageIdentifier:this._modeService.getLanguageIdentifier(n),tokenType:i,fontStyle:o,foreground:t[r],background:t[s]}},t.prototype._tokenTypeToString=function(e){switch(e){case 0:return"Other";case 1:return"Comment";case 2:return"String";case 4:return"RegEx"}return"??"},t.prototype._fontStyleToString=function(e){var t="";return 1&e&&(t+="italic "),2&e&&(t+="bold "),4&e&&(t+="underline "),0===t.length&&(t="---"),t},t.prototype._getTokensAtLine=function(e){var t=this._getStateBeforeLine(e),n=this._tokenizationSupport.tokenize(this._model.getLineContent(e),t,0),i=this._tokenizationSupport.tokenize2(this._model.getLineContent(e),t,0);return{startState:t,tokens1:n.tokens,tokens2:i.tokens,endState:n.endState}},t.prototype._getStateBeforeLine=function(e){for(var t=this._tokenizationSupport.getInitialState(),n=1;n1?n.localize(0,null,t.lineNumber,t.column):n.localize(1,null,t.lineNumber,t.column):t.lineNumber<1||t.lineNumber>o.getLineCount()?n.localize(2,null,o.getLineCount()):n.localize(3,null,o.getLineMaxColumn(t.lineNumber)),{position:t,isValid:a,label:s}},t.prototype.getLabel=function(){return this._parseResult.label},t.prototype.getAriaLabel=function(){return n.localize(4,null,this._parseResult.label)},t.prototype.run=function(e,t){return e===o.Mode.OPEN?this.runOpen():this.runPreview()},t.prototype.runOpen=function(){if(!this._parseResult.isValid)return!1;var e=this.toSelection();return this.editor.setSelection(e),this.editor.revealRangeInCenter(e),this.editor.focus(),!0},t.prototype.runPreview=function(){if(!this._parseResult.isValid)return this.decorator.clearDecorations(),!1;var e=this.toSelection();return this.editor.revealRangeInCenter(e),this.decorator.decorateLine(e,this.editor),!1},t.prototype.toSelection=function(){return new c.Range(this._parseResult.position.lineNumber,this._parseResult.position.column,this._parseResult.position.lineNumber,this._parseResult.position.column)},t}(i.QuickOpenEntry);t.GotoLineEntry=d;var h=function(e){function t(){return e.call(this,n.localize(5,null),{id:"editor.action.gotoLine",label:n.localize(6,null),alias:"Go to Line...",precondition:null,kbOpts:{kbExpr:s.EditorContextKeys.focus,primary:2085,mac:{primary:293}}})||this}return f(t,e),t.prototype.run=function(e,t){var n=this;this._show(this.getController(t),{getModel:function(e){return new i.QuickOpenModel([new d(e,t,n.getController(t))])},getAutoFocus:function(e){return{autoFocusFirstEntry:e.length>0}}})},t=v([u.editorAction],t)}(a.BaseEditorQuickOpenAction);t.GotoLineAction=h}),define(d[538],h([1,0,378,10,81,7,120,100,46,21,143,13,28]),function(e,t,n,i,o,r,s,a,u,l,c,d,h){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var p=function(e){function t(t,n,i,o){var r=e.call(this)||this;return r.key=t,r.setHighlights(n),r.action=i,r.editor=o,r}return f(t,e),t.prototype.getLabel=function(){return this.action.label},t.prototype.getAriaLabel=function(){return n.localize(0,null,this.getLabel())},t.prototype.getGroupLabel=function(){return this.key},t.prototype.run=function(e,t){var n=this;return e===a.Mode.OPEN&&(r.TPromise.timeout(50).done(function(){n.editor.focus();try{(n.action.run()||r.TPromise.as(null)).done(null,i.onUnexpectedError)}catch(e){i.onUnexpectedError(e)}},i.onUnexpectedError),!0)},t}(s.QuickOpenEntryGroup);t.EditorActionCommandEntry=p;var g=function(e){function t(){return e.call(this,n.localize(1,null),{id:"editor.action.quickCommand",label:n.localize(2,null),alias:"Command Palette",precondition:null,kbOpts:{kbExpr:l.EditorContextKeys.focus,primary:h.isIE?571:59},menuOpts:{}})||this}return f(t,e),t.prototype.run=function(e,t){var n=this,i=e.get(u.IKeybindingService);this._show(this.getController(t),{getModel:function(e){return new s.QuickOpenModel(n._editorActionsToEntries(i,t,e))},getAutoFocus:function(e){return{autoFocusFirstEntry:!0,autoFocusPrefixMatch:e}}})},t.prototype._sort=function(e,t){var n=e.getLabel().toLowerCase(),i=t.getLabel().toLowerCase();return n.localeCompare(i)},t.prototype._editorActionsToEntries=function(e,t,n){for(var i=t.getSupportedActions(),r=[],s=0;s0&&0===r.indexOf(":")){for(var m=null,v=null,_=0,y=0;y0)):_++}v&&v.setGroupLabel(this.typeToLabel(m,_))}else a.length>0&&a[0].setGroupLabel(n.localize(3,null,a.length));return a},t.prototype.typeToLabel=function(e,t){switch(e){case"module":return n.localize(4,null,t);case"class":return n.localize(5,null,t);case"interface":return n.localize(6,null,t);case"method":return n.localize(7,null,t);case"function":return n.localize(8,null,t);case"property":return n.localize(9,null,t);case"variable":return n.localize(10,null,t);case"var":return n.localize(11,null,t);case"constructor":return n.localize(12,null,t);case"call":return n.localize(13,null,t)}return e},t.prototype.sortNormal=function(e,t,n){var i=t.getLabel().toLowerCase(),o=n.getLabel().toLowerCase(),r=i.localeCompare(o);if(0!==r)return r;var s=t.getRange(),a=n.getRange();return s.startLineNumber-a.startLineNumber},t.prototype.sortScoped=function(e,t,n){e=e.substr(":".length);var i=t.getType(),o=n.getType(),r=i.localeCompare(o);if(0!==r)return r;if(e){var s=t.getLabel().toLowerCase(),a=n.getLabel().toLowerCase(),u=s.localeCompare(a);if(0!==u)return u}var l=t.getRange(),c=n.getRange();return l.startLineNumber-c.startLineNumber},t=v([d.editorAction],t)}(l.BaseEditorQuickOpenAction);t.QuickOutlineAction=g}),define(d[540],h([1,0,3,7,69,16,31,46,19,42,61,129,141,193,77,149,85,14,51,50,380,28]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_,C,b,w,S,E){"use strict";function L(){N||(N=!0,b.setARIAContainer(document.body))}Object.defineProperty(t,"__esModule",{value:!0});var x=0,N=!1,M=function(e){function t(t,n,i,o,r,s,a,u){var l=this;return n=n||{},n.ariaLabel=n.ariaLabel||S.localize(0,null),n.ariaLabel=n.ariaLabel+";"+(E.isIE?S.localize(1,null):S.localize(2,null)),l=e.call(this,t,n,i,o,r,s,u)||this,a instanceof d.StandaloneKeybindingService&&(l._standaloneKeybindingService=a),L(),l}return f(t,e),t.prototype.addCommand=function(e,t,n){if(!this._standaloneKeybindingService)return console.warn("Cannot add command because the editor is configured with an unrecognized KeybindingService"),null;var i="DYNAMIC_"+ ++x,o=u.ContextKeyExpr.deserialize(n);return this._standaloneKeybindingService.addDynamicKeybinding(i,e,t,o),i},t.prototype.createContextKey=function(e,t){return this._contextKeyService.createKey(e,t)},t.prototype.addAction=function(e){var t=this;if("string"!=typeof e.id||"string"!=typeof e.label||"function"!=typeof e.run)throw new Error("Invalid action descriptor, `id`, `label` and `run` are required properties!");if(!this._standaloneKeybindingService)return console.warn("Cannot add keybinding because the editor is configured with an unrecognized KeybindingService"),n.empty;var o=e.id,r=e.label,a=u.ContextKeyExpr.and(u.ContextKeyExpr.equals("editorId",this.getId()),u.ContextKeyExpr.deserialize(e.precondition)),l=e.keybindings,c=u.ContextKeyExpr.and(a,u.ContextKeyExpr.deserialize(e.keybindingContext)),d=e.contextMenuGroupId||null,h=e.contextMenuOrder||0,p=function(){var n=e.run(t);return n||i.TPromise.as(void 0)},f=[],g=this.getId()+":"+o;if(f.push(s.CommandsRegistry.registerCommand(g,p)),d){var v={command:{id:g,title:r},when:a,group:d,order:h};f.push(_.MenuRegistry.appendMenuItem(_.MenuId.EditorContext,v))}Array.isArray(l)&&(f=f.concat(l.map(function(e){return t._standaloneKeybindingService.addDynamicKeybinding(g,e,p,c)})));var y=new m.InternalEditorAction(g,r,r,a,p,this._contextKeyService);return this._actions[o]=y,f.push({dispose:function(){delete t._actions[o]}}),n.combinedDisposable(f)},t=v([y(2,r.IInstantiationService),y(3,l.ICodeEditorService),y(4,s.ICommandService),y(5,u.IContextKeyService),y(6,a.IKeybindingService),y(7,C.IThemeService)],t)}(h.CodeEditor);t.StandaloneCodeEditor=M;var T=function(e){function t(t,n,i,o,r,s,a,u,l,c){var d=this;"string"==typeof(n=n||{}).theme&&c.setTheme(n.theme);var h=n.model;if(delete n.model,d=e.call(this,t,n,o,r,s,a,u,c)||this,d._contextViewService=l,d._register(i),void 0===h?(h=self.monaco.editor.createModel(n.value||"",n.language||"text/plain"),d._ownsModel=!0):d._ownsModel=!1,d._attachModel(h),h){var p={oldModelUrl:null,newModelUrl:h.uri};d._onDidChangeModel.fire(p)}return d}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype.destroy=function(){this.dispose()},t.prototype._attachModel=function(t){e.prototype._attachModel.call(this,t),this._view&&this._contextViewService.setContainer(this._view.domNode.domNode)},t.prototype._postDetachModelCleanup=function(t){e.prototype._postDetachModelCleanup.call(this,t),t&&this._ownsModel&&(t.dispose(),this._ownsModel=!1)},t=v([y(3,r.IInstantiationService),y(4,l.ICodeEditorService),y(5,s.ICommandService),y(6,u.IContextKeyService),y(7,a.IKeybindingService),y(8,o.IContextViewService),y(9,g.IStandaloneThemeService)],t)}(M);t.StandaloneEditor=T;var k=function(e){function t(t,n,i,o,r,s,a,u,l,c,h){var p=this;return"string"==typeof(n=n||{}).theme&&(n.theme=c.setTheme(n.theme)),p=e.call(this,t,n,u,r,o,l,c,h)||this,s instanceof d.StandaloneKeybindingService&&(p._standaloneKeybindingService=s),p._contextViewService=a,p._register(i),p._contextViewService.setContainer(p._containerDomElement),p}return f(t,e),t.prototype.dispose=function(){e.prototype.dispose.call(this)},t.prototype.destroy=function(){this.dispose()},t.prototype._createInnerEditor=function(e,t,n){return e.createInstance(M,t,n)},t.prototype.getOriginalEditor=function(){return e.prototype.getOriginalEditor.call(this)},t.prototype.getModifiedEditor=function(){return e.prototype.getModifiedEditor.call(this)},t.prototype.addCommand=function(e,t,n){return this.getModifiedEditor().addCommand(e,t,n)},t.prototype.createContextKey=function(e,t){return this.getModifiedEditor().createContextKey(e,t)},t.prototype.addAction=function(e){return this.getModifiedEditor().addAction(e)},t=v([y(3,r.IInstantiationService),y(4,u.IContextKeyService),y(5,a.IKeybindingService),y(6,o.IContextViewService),y(7,c.IEditorWorkerService),y(8,l.ICodeEditorService),y(9,g.IStandaloneThemeService),y(10,w.IMessageService)],t)}(p.DiffEditorWidget);t.StandaloneDiffEditor=k}),define(d[541],h([1,0,23,37]),function(e,t,n,i){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.vs={base:"vs",inherit:!1,rules:[{token:"",foreground:"000000",background:"fffffe"},{token:"invalid",foreground:"cd3131"},{token:"emphasis",fontStyle:"italic"},{token:"strong",fontStyle:"bold"},{token:"variable",foreground:"001188"},{token:"variable.predefined",foreground:"4864AA"},{token:"constant",foreground:"dd0000"},{token:"comment",foreground:"008000"},{token:"number",foreground:"09885A"},{token:"number.hex",foreground:"3030c0"},{token:"regexp",foreground:"800000"},{token:"annotation",foreground:"808080"},{token:"type",foreground:"008080"},{token:"delimiter",foreground:"000000"},{token:"delimiter.html",foreground:"383838"},{token:"delimiter.xml",foreground:"0000FF"},{token:"tag",foreground:"800000"},{token:"tag.id.jade",foreground:"4F76AC"},{token:"tag.class.jade",foreground:"4F76AC"},{token:"meta.scss",foreground:"800000"},{token:"metatag",foreground:"e00000"},{token:"metatag.content.html",foreground:"FF0000"},{token:"metatag.html",foreground:"808080"},{token:"metatag.xml",foreground:"808080"},{token:"metatag.php",fontStyle:"bold"},{token:"key",foreground:"863B00"},{token:"string.key.json",foreground:"A31515"},{token:"string.value.json",foreground:"0451A5"},{token:"attribute.name",foreground:"FF0000"},{token:"attribute.value",foreground:"0451A5"},{token:"attribute.value.number",foreground:"09885A"},{token:"attribute.value.unit",foreground:"09885A"},{token:"attribute.value.html",foreground:"0000FF"},{token:"attribute.value.xml",foreground:"0000FF"},{token:"string",foreground:"A31515"},{token:"string.html",foreground:"0000FF"},{token:"string.sql",foreground:"FF0000"},{token:"string.yaml",foreground:"0451A5"},{token:"keyword",foreground:"0000FF"},{token:"keyword.json",foreground:"0451A5"},{token:"keyword.flow",foreground:"AF00DB"},{token:"keyword.flow.scss",foreground:"0000FF"},{token:"operator.scss",foreground:"666666"},{token:"operator.sql",foreground:"778899"},{token:"operator.swift",foreground:"666666"},{token:"predefined.sql",foreground:"FF00FF"}],colors:(o={},o[n.editorBackground]="#FFFFFE",o[n.editorForeground]="#000000",o[n.editorInactiveSelection]="#E5EBF1",o[i.editorIndentGuides]="#D3D3D3",o[n.editorSelectionHighlight]="#ADD6FF4D",o)},t.vs_dark={base:"vs-dark",inherit:!1,rules:[{token:"",foreground:"D4D4D4",background:"1E1E1E"},{token:"invalid",foreground:"f44747"},{token:"emphasis",fontStyle:"italic"},{token:"strong",fontStyle:"bold"},{token:"variable",foreground:"74B0DF"},{token:"variable.predefined",foreground:"4864AA"},{token:"variable.parameter",foreground:"9CDCFE"},{token:"constant",foreground:"569CD6"},{token:"comment",foreground:"608B4E"},{token:"number",foreground:"B5CEA8"},{token:"number.hex",foreground:"5BB498"},{token:"regexp",foreground:"B46695"},{token:"annotation",foreground:"cc6666"},{token:"type",foreground:"3DC9B0"},{token:"delimiter",foreground:"DCDCDC"},{token:"delimiter.html",foreground:"808080"},{token:"delimiter.xml",foreground:"808080"},{token:"tag",foreground:"569CD6"},{token:"tag.id.jade",foreground:"4F76AC"},{token:"tag.class.jade",foreground:"4F76AC"},{token:"meta.scss",foreground:"A79873"},{token:"meta.tag",foreground:"CE9178"},{token:"metatag",foreground:"DD6A6F"},{token:"metatag.content.html",foreground:"9CDCFE"},{token:"metatag.html",foreground:"569CD6"},{token:"metatag.xml",foreground:"569CD6"},{token:"metatag.php",fontStyle:"bold"},{token:"key",foreground:"9CDCFE"},{token:"string.key.json",foreground:"9CDCFE"},{token:"string.value.json",foreground:"CE9178"},{token:"attribute.name",foreground:"9CDCFE"},{token:"attribute.value",foreground:"CE9178"},{token:"attribute.value.number.css",foreground:"B5CEA8"},{token:"attribute.value.unit.css",foreground:"B5CEA8"},{token:"attribute.value.hex.css",foreground:"D4D4D4"},{token:"string",foreground:"CE9178"},{token:"string.sql",foreground:"FF0000"},{token:"keyword",foreground:"569CD6"},{token:"keyword.flow",foreground:"C586C0"},{token:"keyword.json",foreground:"CE9178"},{token:"keyword.flow.scss",foreground:"569CD6"},{token:"operator.scss",foreground:"909090"},{token:"operator.sql",foreground:"778899"},{token:"operator.swift",foreground:"909090"},{token:"predefined.sql",foreground:"FF00FF"}],colors:(r={},r[n.editorBackground]="#1E1E1E",r[n.editorForeground]="#D4D4D4",r[n.editorInactiveSelection]="#3A3D41",r[i.editorIndentGuides]="#404040",r[n.editorSelectionHighlight]="#ADD6FF26",r)},t.hc_black={base:"hc-black",inherit:!1,rules:[{token:"",foreground:"FFFFFF",background:"000000"},{token:"invalid",foreground:"f44747"},{token:"emphasis",fontStyle:"italic"},{token:"strong",fontStyle:"bold"},{token:"variable",foreground:"1AEBFF"},{token:"variable.parameter",foreground:"9CDCFE"},{token:"constant",foreground:"569CD6"},{token:"comment",foreground:"608B4E"},{token:"number",foreground:"FFFFFF"},{token:"regexp",foreground:"C0C0C0"},{token:"annotation",foreground:"569CD6"},{token:"type",foreground:"3DC9B0"},{token:"delimiter",foreground:"FFFF00"},{token:"delimiter.html",foreground:"FFFF00"},{token:"tag",foreground:"569CD6"},{token:"tag.id.jade",foreground:"4F76AC"},{token:"tag.class.jade",foreground:"4F76AC"},{token:"meta",foreground:"D4D4D4"},{token:"meta.tag",foreground:"CE9178"},{token:"metatag",foreground:"569CD6"},{token:"metatag.content.html",foreground:"1AEBFF"},{token:"metatag.html",foreground:"569CD6"},{token:"metatag.xml",foreground:"569CD6"},{token:"metatag.php",fontStyle:"bold"},{token:"key",foreground:"9CDCFE"},{token:"string.key",foreground:"9CDCFE"},{token:"string.value",foreground:"CE9178"},{token:"attribute.name",foreground:"569CD6"},{token:"attribute.value",foreground:"3FF23F"},{token:"string",foreground:"CE9178"},{token:"string.sql",foreground:"FF0000"},{token:"keyword",foreground:"569CD6"},{token:"keyword.flow",foreground:"C586C0"},{token:"operator.sql",foreground:"778899"},{token:"operator.swift",foreground:"909090"},{token:"predefined.sql",foreground:"FF00FF"}],colors:(s={},s[n.editorBackground]="#000000",s[n.editorForeground]="#FFFFFF",s[i.editorIndentGuides]="#FFFFFF",s)};var o,r,s}),define(d[542],h([1,0,202,541,4,17,32,23,14,44,11]),function(e,t,n,i,o,r,s,a,u,l,c){"use strict";function d(e){return e===f||e===g||e===m}function h(e){switch(e){case f:return i.vs;case g:return i.vs_dark;case m:return i.hc_black}}function p(e){var t=h(e);return new y(e,"",t.colors,t.rules)}Object.defineProperty(t,"__esModule",{value:!0});var f="vs",g="vs-dark",m="hc-black",v=l.Registry.as(a.Extensions.ColorContribution),_=l.Registry.as(u.Extensions.ThemingContribution),y=function(){function e(e,t,n,i){t.length>0?(this.id=e+" "+t,this.themeName=t):(this.id=e,this.themeName=e),this.base=e,this.rules=i,this.colors={};for(var o in n)this.colors[o]=s.Color.fromHex(n[o]);this.defaultColors={}}return e.prototype.getColor=function(e,t){return this.colors.hasOwnProperty(e)?this.colors[e]:!1!==t?this.getDefault(e):null},e.prototype.getDefault=function(e){if(this.defaultColors.hasOwnProperty(e))return this.defaultColors[e];var t=v.resolveDefaultColor(e,this);return this.defaultColors[e]=t,t},e.prototype.defines=function(e){return this.colors.hasOwnProperty(e)},Object.defineProperty(e.prototype,"type",{get:function(){switch(this.base){case f:return"light";case m:return"hc";default:return"dark"}},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"tokenTheme",{get:function(){return this._tokenTheme||(this._tokenTheme=n.TokenTheme.createFromRawTokenTheme(this.rules)),this._tokenTheme},enumerable:!0,configurable:!0}),e}(),C=function(){function e(){this._onThemeChange=new c.Emitter,this._knownThemes=new Map,this._knownThemes.set(f,p(f)),this._knownThemes.set(g,p(g)),this._knownThemes.set(m,p(m)),this._styleElement=o.createStyleSheet(),this._styleElement.className="monaco-colors",this.setTheme(f)}return Object.defineProperty(e.prototype,"onThemeChange",{get:function(){return this._onThemeChange.event},enumerable:!0,configurable:!0}),e.prototype.defineTheme=function(e,t){if(!/^[a-z0-9\-]+$/i.test(e)||d(e))throw new Error("Illegal theme name!");if(!d(t.base))throw new Error("Illegal theme base!");var n=[],i={};if(t.inherit){var o=h(t.base);n=n.concat(o.rules);for(var r in o.colors)i[r]=o.colors[r]}n=n.concat(t.rules);for(var r in t.colors)i[r]=t.colors[r];this._knownThemes.set(e,new y(t.base,e,i,n))},e.prototype.getTheme=function(){return this._theme},e.prototype.setTheme=function(e){var t;t=this._knownThemes.has(e)?this._knownThemes.get(e):this._knownThemes.get(f),this._theme=t;var i=[],o={},s={addRule:function(e){o[e]||(i.push(e),o[e]=!0)}};_.getThemingParticipants().forEach(function(e){return e(t,s)});var a=t.tokenTheme.getColorMap();return s.addRule(n.generateTokensCSSForColorMap(a)),this._styleElement.innerHTML=i.join("\n"),r.TokenizationRegistry.setColorMap(a),this._onThemeChange.fire(t),t.id},e}();t.StandaloneThemeServiceImpl=C}),define(d[144],h([1,0,24,16,45,80]),function(e,t,n,i,o,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.IWorkspaceContextService=i.createDecorator("contextService");var s=function(){function e(e,t){this._resource=e,this._ctime=t,this._name=o.basename(this._resource.fsPath)||this._resource.fsPath}return Object.defineProperty(e.prototype,"resource",{get:function(){return this._resource},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"name",{get:function(){return this._name},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"ctime",{get:function(){return this._ctime},enumerable:!0,configurable:!0}),e.prototype.toResource=function(e,t){return"string"==typeof e?n.default.file(o.join(t?t.fsPath:this._resource.fsPath,e)):null},e}();t.LegacyWorkspace=s;var a=function(){function e(e,t,n,i){void 0===i&&(i=null),this.id=e,this._name=t,this._roots=n,this._configuration=i,this._rootsMap=new r.TrieMap(r.TrieMap.PathSplitter),this.updateRootsMap()}return Object.defineProperty(e.prototype,"roots",{get:function(){return this._roots},set:function(e){this._roots=e,this.updateRootsMap()},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"name",{get:function(){return this._name},set:function(e){this._name=e},enumerable:!0,configurable:!0}),Object.defineProperty(e.prototype,"configuration",{get:function(){return this._configuration},set:function(e){this._configuration=e},enumerable:!0,configurable:!0}),e.prototype.getRoot=function(e){return e?this._rootsMap.findSubstr(e.fsPath):null},e.prototype.updateRootsMap=function(){this._rootsMap=new r.TrieMap(r.TrieMap.PathSplitter);for(var e=0,t=this.roots;e1?this.badge.setTitleFormat(n.localize(1,null,t)):this.badge.setTitleFormat(n.localize(2,null,t))},e=v([y(1,S.IWorkspaceContextService),y(2,b.optional(R.IEnvironmentService)),y(3,D.IThemeService)],e)}(),H=function(){function e(e){var t=document.createElement("div");this.before=document.createElement("span"),this.inside=document.createElement("span"),this.after=document.createElement("span"),h.addClass(this.inside,"referenceMatch"),h.addClass(t,"reference"),t.appendChild(this.before),t.appendChild(this.inside),t.appendChild(this.after),e.appendChild(t)}return e.prototype.set=function(e){var t=e.parent.preview.preview(e.range),n=t.before,i=t.inside,o=t.after;this.before.innerHTML=u.escape(n),this.inside.innerHTML=u.escape(i),this.after.innerHTML=u.escape(o)},e}(),z=function(){function e(e,t,n){this._contextService=e,this._themeService=t,this._environmentService=n}return e.prototype.getHeight=function(e,t){return 22},e.prototype.getTemplateId=function(t,n){if(n instanceof T.FileReferences)return e._ids.FileReferences;if(n instanceof T.OneReference)return e._ids.OneReference;throw n},e.prototype.renderTemplate=function(t,n,i){if(n===e._ids.FileReferences)return new V(i,this._contextService,this._environmentService,this._themeService);if(n===e._ids.OneReference)return new H(i);throw n},e.prototype.renderElement=function(e,t,n,i){if(t instanceof T.FileReferences)i.set(t);else{if(!(t instanceof T.OneReference))throw n;i.set(t)}},e.prototype.disposeTemplate=function(e,t,n){n instanceof V&&n.dispose()},e._ids={FileReferences:"FileReferences",OneReference:"OneReference"},e=v([y(0,S.IWorkspaceContextService),y(1,D.IThemeService),y(2,b.optional(R.IEnvironmentService))],e)}(),K=function(){function e(){}return e.prototype.getAriaLabel=function(e,t){return t instanceof T.FileReferences?t.getAriaMessage():t instanceof T.OneReference?t.getAriaMessage():void 0},e}(),U=function(){function e(e,t){var n=this;this._disposables=[],this._onDidChangePercentages=new r.Emitter,this._ratio=t,this._sash=new p.Sash(e,{getVerticalSashLeft:function(){return n._width*n._ratio},getVerticalSashHeight:function(){return n._height}});var i;this._disposables.push(this._sash.addListener("start",function(e){i=e.startX-n._width*n.ratio})),this._disposables.push(this._sash.addListener("change",function(e){var t=e.currentX-i;t>20&&t+200?e.children[0]:void 0},u.prototype._revealReference=function(e){var t=this;e.uri.scheme!==a.Schemas.inMemory?this.setTitle(e.name,o.getPathLabel(e.directory,this._contextService,this._environmentService)):this.setTitle(n.localize(6,null));var r=this._textModelResolverService.createModelReference(e.uri);return l.TPromise.join([r,this._tree.reveal(e)]).then(function(n){var i=n[0];if(t._model){s.dispose(t._previewModelReference);var o=i.object;if(o){t._previewModelReference=i,t._preview.setModel(o.textEditorModel);var r=E.Range.lift(e.range).collapseToStart();t._preview.setSelection(r),t._preview.revealRangeInCenter(r)}else t._preview.setModel(t._previewNotAvailableMessage),i.dispose();t._tree.setSelection([e]),t._tree.setFocus(e)}else i.dispose()},i.onUnexpectedError)},u}(M.PeekViewWidget);t.ReferenceWidget=j,t.peekViewTitleBackground=I.registerColor("peekViewTitle.background",{dark:"#1E1E1E",light:"#FFFFFF",hc:"#0C141F"},n.localize(7,null)),t.peekViewTitleForeground=I.registerColor("peekViewTitleLabel.foreground",{dark:"#FFFFFF",light:"#333333",hc:"#FFFFFF"},n.localize(8,null)),t.peekViewTitleInfoForeground=I.registerColor("peekViewTitleDescription.foreground",{dark:"#ccccccb3",light:"#6c6c6cb3",hc:"#FFFFFF99"},n.localize(9,null)),t.peekViewBorder=I.registerColor("peekView.border",{dark:"#007acc",light:"#007acc",hc:I.contrastBorder},n.localize(10,null)),t.peekViewResultsBackground=I.registerColor("peekViewResult.background",{dark:"#252526",light:"#F3F3F3",hc:c.Color.black},n.localize(11,null)),t.peekViewResultsMatchForeground=I.registerColor("peekViewResult.lineForeground",{dark:"#bbbbbb",light:"#646465",hc:c.Color.white},n.localize(12,null)),t.peekViewResultsFileForeground=I.registerColor("peekViewResult.fileForeground",{dark:c.Color.white,light:"#1E1E1E",hc:c.Color.white},n.localize(13,null)),t.peekViewResultsSelectionBackground=I.registerColor("peekViewResult.selectionBackground",{dark:"#3399ff33",light:"#3399ff33",hc:null},n.localize(14,null)),t.peekViewResultsSelectionForeground=I.registerColor("peekViewResult.selectionForeground",{dark:c.Color.white,light:"#6C6C6C",hc:c.Color.white},n.localize(15,null)),t.peekViewEditorBackground=I.registerColor("peekViewEditor.background",{dark:"#001F33",light:"#F2F8FC",hc:c.Color.black},n.localize(16,null)),t.peekViewEditorGutterBackground=I.registerColor("peekViewEditorGutter.background",{dark:t.peekViewEditorBackground,light:t.peekViewEditorBackground,hc:t.peekViewEditorBackground},n.localize(17,null)),t.peekViewResultsMatchHighlight=I.registerColor("peekViewResult.matchHighlightBackground",{dark:"#ea5c004d",light:"#ea5c004d",hc:null},n.localize(18,null)),t.peekViewEditorMatchHighlight=I.registerColor("peekViewEditor.matchHighlightBackground",{dark:"#ff8f0099",light:"#f5d802de",hc:null},n.localize(19,null)),D.registerThemingParticipant(function(e,n){var i=e.getColor(t.peekViewResultsMatchHighlight);i&&n.addRule(".monaco-editor .reference-zone-widget .ref-tree .referenceMatch { background-color: "+i+"; }");var o=e.getColor(t.peekViewEditorMatchHighlight);o&&n.addRule(".monaco-editor .reference-zone-widget .preview .reference-decoration { background-color: "+o+"; }");var r=e.getColor(I.activeContrastBorder);r&&(n.addRule(".monaco-editor .reference-zone-widget .ref-tree .referenceMatch { border: 1px dotted "+r+"; box-sizing: border-box; }"),n.addRule(".monaco-editor .reference-zone-widget .preview .reference-decoration { border: 2px solid "+r+"; box-sizing: border-box; }"));var s=e.getColor(t.peekViewResultsBackground);s&&n.addRule(".monaco-editor .reference-zone-widget .ref-tree { background-color: "+s+"; }");var a=e.getColor(t.peekViewResultsMatchForeground);a&&n.addRule(".monaco-editor .reference-zone-widget .ref-tree { color: "+a+"; }");var u=e.getColor(t.peekViewResultsFileForeground);u&&n.addRule(".monaco-editor .reference-zone-widget .ref-tree .reference-file { color: "+u+"; }");var l=e.getColor(t.peekViewResultsSelectionBackground);l&&n.addRule(".monaco-editor .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { background-color: "+l+"; }");var c=e.getColor(t.peekViewResultsSelectionForeground);c&&n.addRule(".monaco-editor .reference-zone-widget .ref-tree .monaco-tree.focused .monaco-tree-rows > .monaco-tree-row.selected:not(.highlighted) { color: "+c+" !important; }");var d=e.getColor(t.peekViewEditorBackground);d&&n.addRule(".monaco-editor .reference-zone-widget .preview .monaco-editor .monaco-editor-background,.monaco-editor .reference-zone-widget .preview .monaco-editor .inputarea.ime-input {\tbackground-color: "+d+";}");var h=e.getColor(t.peekViewEditorGutterBackground);h&&n.addRule(".monaco-editor .reference-zone-widget .preview .monaco-editor .margin {\tbackground-color: "+h+";}")})}),define(d[198],h([1,0,364,10,3,36,64,11,16,19,50,58,62,144,76,30,92,544,87,14,12,156]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,f,g,m,_,C,b,w,S){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.ctxReferenceSearchVisible=new l.RawContextKey("referenceSearchVisible",!1);var E=function(){function e(e,n,i,o,r,s,a,u,l,c,d,h,p){this._editorService=i,this._textModelResolverService=o,this._telemetryService=r,this._messageService=s,this._instantiationService=a,this._contextService=u,this._storageService=l,this._themeService=c,this._configurationService=d,this._peekViewService=h,this._environmentService=p,this._requestIdPool=0,this._disposables=[],this._ignoreModelChangeEvent=!1,this._editor=e,this._referenceSearchVisible=t.ctxReferenceSearchVisible.bindTo(n)}return E=e,e.get=function(e){return e.getContribution(E.ID)},e.prototype.getId=function(){return E.ID},e.prototype.dispose=function(){this._widget&&(this._widget.dispose(),this._widget=null),this._editor=null},e.prototype.toggleWidget=function(e,t,i){var o,s=this;if(this._widget&&(o=this._widget.position),this.closeWidget(),o&&e.containsPosition(o))return null;this._referenceSearchVisible.set(!0),this._disposables.push(this._editor.onDidChangeModelLanguage(function(){s.closeWidget()})),this._disposables.push(this._editor.onDidChangeModel(function(){s._ignoreModelChangeEvent||s.closeWidget()}));var u=JSON.parse(this._storageService.get("peekViewLayout",void 0,"{}"));this._widget=new _.ReferenceWidget(this._editor,u,this._textModelResolverService,this._contextService,this._themeService,this._instantiationService,this._environmentService),this._widget.setTitle(n.localize(0,null)),this._widget.show(e),this._disposables.push(this._widget.onDidClose(function(){t.cancel(),s._storageService.store("peekViewLayout",JSON.stringify(s._widget.layoutData)),s._widget=null,s.closeWidget()})),this._disposables.push(this._widget.onDidSelectReference(function(e){var t=e.element,n=e.kind;switch(n){case"open":if("editor"===e.source&&s._configurationService.lookup("editor.stablePeek").value)break;case"side":s._openReference(t,"side"===n);break;case"goto":i.onGoto?i.onGoto(t):s._gotoReference(t)}}));var l=++this._requestIdPool,c=t.then(function(t){if(l===s._requestIdPool&&s._widget){s._model&&s._model.dispose(),s._model=t;var n=Date.now();return s._disposables.push({dispose:function(){s._telemetryService.publicLog("zoneWidgetShown",{mode:"reference search",elapsedTime:Date.now()-n})}}),s._widget.setModel(s._model).then(function(){s._widget.setMetaTitle(i.getMetaTitle(s._model));var t=s._editor.getModel().uri,n=new w.Position(e.startLineNumber,e.startColumn),o=s._model.nearestReference(t,n);if(o)return s._widget.setSelection(o)})}},function(e){s._messageService.show(r.default.Error,e)}),d=a.stopwatch(a.fromPromise(c)),h=this._editor.getModel().getLanguageIdentifier().language;d(function(e){return s._telemetryService.publicLog("findReferences",{duration:e,mode:h})})},e.prototype.closeWidget=function(){this._widget&&(this._widget.dispose(),this._widget=null),this._referenceSearchVisible.reset(),this._disposables=o.dispose(this._disposables),this._model&&(this._model.dispose(),this._model=null),this._editor.focus(),this._requestIdPool+=1},e.prototype._gotoReference=function(e){var t=this;this._widget.hide(),this._ignoreModelChangeEvent=!0;var n=e.uri,o=e.range;this._editorService.openEditor({resource:n,options:{selection:o}}).done(function(e){t._ignoreModelChangeEvent=!1,e&&e.getControl()===t._editor?(t._widget.show(o),t._widget.focus()):t.closeWidget()},function(e){t._ignoreModelChangeEvent=!1,i.onUnexpectedError(e)})},e.prototype._openReference=function(e,t){var n=e.uri,i=e.range;this._editorService.openEditor({resource:n,options:{selection:i}},t),t||this.closeWidget()},e.ID="editor.contrib.referencesController",e=E=v([g.editorContribution,y(1,l.IContextKeyService),y(2,s.IEditorService),y(3,C.ITextModelService),y(4,d.ITelemetryService),y(5,c.IMessageService),y(6,u.IInstantiationService),y(7,p.IWorkspaceContextService),y(8,f.IStorageService),y(9,b.IThemeService),y(10,h.IConfigurationService),y(11,u.optional(m.IPeekViewService)),y(12,u.optional(S.IEnvironmentService))],e);var E}();t.ReferencesController=E}),define(d[199],h([1,0,351,51,40,15,36,7,64,50,2,13,177,198,125,92,19,521,21]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_,y,C){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var b=function(){return function(e,t,n,i){void 0===e&&(e=!1),void 0===t&&(t=!1),void 0===n&&(n=!0),void 0===i&&(i=!0),this.openToSide=e,this.openInPeek=t,this.filterCurrent=n,this.showMessage=i}}();t.DefinitionActionConfig=b;var w=function(e){function t(t,n){var i=e.call(this,n)||this;return i._configuration=t,i}return f(t,e),t.prototype.run=function(e,t){var n=this,i=e.get(l.IMessageService),o=e.get(u.IEditorService),r=t.getModel(),a=t.getPosition();return this._getDeclarationsAtPosition(r,a).then(function(e){if(!r.isDisposed()&&t.getModel()===r){for(var i=-1,s=[],u=0;u1&&n.localize(2,null,e.references.length)},t.prototype._onResult=function(e,t,n){var o=this,r=n.getAriaMessage();if(i.alert(r),this._configuration.openInPeek)this._openInPeek(e,t,n);else{var s=n.nearestReference(t.getModel().uri,t.getPosition());this._openReference(e,s,this._configuration.openToSide).then(function(t){t&&n.references.length>1?o._openInPeek(e,t,n):n.dispose()})}},t.prototype._openReference=function(e,t,n){var i=t.uri,o=t.range;return e.openEditor({resource:i,options:{selection:c.Range.collapseToStart(o),revealIfVisible:!n}},n).then(function(e){return e&&e.getControl()})},t.prototype._openInPeek=function(e,t,n){var i=this,o=p.ReferencesController.get(t);o?o.toggleWidget(t.getSelection(),a.TPromise.as(n),{getMetaTitle:function(e){return i._getMetaTitle(e)},onGoto:function(t){return o.closeWidget(),i._openReference(e,t,!1)}}):n.dispose()},t}(d.EditorAction);t.DefinitionAction=w;var S=r.isWeb?2118:70,E=function(e){function t(){return e.call(this,new b,{id:i.ID,label:n.localize(3,null),alias:"Go to Definition",precondition:_.ContextKeyExpr.and(C.EditorContextKeys.hasDefinitionProvider,C.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:C.EditorContextKeys.textFocus,primary:S},menuOpts:{group:"navigation",order:1.1}})||this}return f(t,e),i=t,t.ID="editor.action.goToDeclaration",t=i=v([d.editorAction],t);var i}(w);t.GoToDefinitionAction=E;var L=function(e){function t(){return e.call(this,new b(!0),{id:i.ID,label:n.localize(4,null),alias:"Open Definition to the Side",precondition:_.ContextKeyExpr.and(C.EditorContextKeys.hasDefinitionProvider,C.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:C.EditorContextKeys.textFocus,primary:o.KeyChord(2089,S)}})||this}return f(t,e),i=t,t.ID="editor.action.openDeclarationToTheSide",t=i=v([d.editorAction],t);var i}(w);t.OpenDefinitionToSideAction=L;var x=function(e){function t(){return e.call(this,new b(void 0,!0,!1),{id:"editor.action.previewDeclaration",label:n.localize(5,null),alias:"Peek Definition",precondition:_.ContextKeyExpr.and(C.EditorContextKeys.hasDefinitionProvider,m.PeekContext.notInPeekEditor,C.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:C.EditorContextKeys.textFocus,primary:582,linux:{primary:3140}},menuOpts:{group:"navigation",order:1.2}})||this}return f(t,e),t=v([d.editorAction],t)}(w);t.PeekDefinitionAction=x;var N=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._getDeclarationsAtPosition=function(e,t){return h.getImplementationsAtPosition(e,t)},t.prototype._getNoResultFoundMessage=function(e){return e&&e.word?n.localize(6,null,e.word):n.localize(7,null)},t.prototype._getMetaTitle=function(e){return e.references.length>1&&n.localize(8,null,e.references.length)},t}(w);t.ImplementationAction=N;var M=function(e){function t(){return e.call(this,new b,{id:i.ID,label:n.localize(9,null),alias:"Go to Implementation",precondition:_.ContextKeyExpr.and(C.EditorContextKeys.hasImplementationProvider,C.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:C.EditorContextKeys.textFocus,primary:2118}})||this}return f(t,e),i=t,t.ID="editor.action.goToImplementation",t=i=v([d.editorAction],t);var i}(N);t.GoToImplementationAction=M;var T=function(e){function t(){return e.call(this,new b(!1,!0,!1),{id:i.ID,label:n.localize(10,null),alias:"Peek Implementation",precondition:_.ContextKeyExpr.and(C.EditorContextKeys.hasImplementationProvider,C.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:C.EditorContextKeys.textFocus,primary:3142}})||this}return f(t,e),i=t,t.ID="editor.action.peekImplementation",t=i=v([d.editorAction],t);var i}(N);t.PeekImplementationAction=T;var k=function(e){function t(){return null!==e&&e.apply(this,arguments)||this}return f(t,e),t.prototype._getDeclarationsAtPosition=function(e,t){return h.getTypeDefinitionsAtPosition(e,t)},t.prototype._getNoResultFoundMessage=function(e){return e&&e.word?n.localize(11,null,e.word):n.localize(12,null)},t.prototype._getMetaTitle=function(e){return e.references.length>1&&n.localize(13,null,e.references.length)},t}(w);t.TypeDefinitionAction=k;var I=function(e){function t(){return e.call(this,new b,{id:i.ID,label:n.localize(14,null),alias:"Go to Type Definition",precondition:_.ContextKeyExpr.and(C.EditorContextKeys.hasTypeDefinitionProvider,C.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:C.EditorContextKeys.textFocus,primary:0},menuOpts:{group:"navigation",order:1.4}})||this}return f(t,e),i=t,t.ID="editor.action.goToTypeDefinition",t=i=v([d.editorAction],t);var i}(k);t.GoToTypeDefintionAction=I;var D=function(e){function t(){return e.call(this,new b(!1,!0,!1),{id:i.ID,label:n.localize(15,null),alias:"Peek Type Definition",precondition:_.ContextKeyExpr.and(C.EditorContextKeys.hasTypeDefinitionProvider,C.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:C.EditorContextKeys.textFocus,primary:0}})||this}return f(t,e),i=t,t.ID="editor.action.peekTypeDefinition",t=i=v([d.editorAction],t);var i}(k);t.PeekTypeDefinitionAction=D}),define(d[547],h([1,0,352,18,10,7,88,2,17,25,30,177,3,87,14,23,131,199,192,171]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,f,g,m,_,C){"use strict";Object.defineProperty(t,"__esModule",{value:!0});!function(){function e(e,t,n){var r=this;this.textModelResolverService=t,this.modeService=n,this.toUnhook=[],this.decorations=[],this.editor=e,this.throttler=new i.Throttler;var s=new C.ClickLinkGesture(e);this.toUnhook.push(s),this.toUnhook.push(s.onMouseMoveOrRelevantKeyDown(function(e){var t=e[0],n=e[1];r.startFindDefinition(t,n)})),this.toUnhook.push(s.onExecute(function(e){r.isEnabled(e)&&r.gotoDefinition(e.target,e.hasSideBySideModifier).done(function(){r.removeDecorations()},function(e){r.removeDecorations(),o.onUnexpectedError(e)})})),this.toUnhook.push(s.onCancel(function(){r.removeDecorations(),r.currentWordUnderMouse=null}))}return t=e,e.prototype.startFindDefinition=function(e,i){var s=this;if(!this.isEnabled(e,i))return this.currentWordUnderMouse=null,void this.removeDecorations();var u=e.target.position,l=u?this.editor.getModel().getWordAtPosition(u):null;if(!l)return this.currentWordUnderMouse=null,void this.removeDecorations();if(!this.currentWordUnderMouse||this.currentWordUnderMouse.startColumn!==l.startColumn||this.currentWordUnderMouse.endColumn!==l.endColumn||this.currentWordUnderMouse.word!==l.word){this.currentWordUnderMouse=l;var c=new m.EditorState(this.editor,15);this.throttler.queue(function(){return c.validate(s.editor)?s.findDefinition(e.target):r.TPromise.as(null)}).then(function(e){if(e&&e.length&&c.validate(s.editor))if(e.length>1)s.addDecoration(new a.Range(u.lineNumber,l.startColumn,u.lineNumber,l.endColumn),n.localize(0,null,e.length));else{var i=e[0];if(!i.uri)return;s.textModelResolverService.createModelReference(i.uri).then(function(e){if(e.object&&e.object.textEditorModel){var n=e.object.textEditorModel,o=i.range.startLineNumber;if(0!==n.getLineMaxColumn(o)){for(var r=n.getLineFirstNonWhitespaceColumn(o),c=Math.min(n.getLineCount(),o+t.MAX_SOURCE_PREVIEW_LINES),d=o+1,h=r;d0&&(this.decorations=this.editor.deltaDecorations(this.decorations,[]))},e.prototype.isEnabled=function(e,t){return this.editor.getModel()&&e.isNoneOrSingleMouseDown&&e.target.type===l.MouseTargetType.CONTENT_TEXT&&(e.hasTriggerModifier||t&&t.keyCodeIsTriggerKey)&&u.DefinitionProviderRegistry.has(this.editor.getModel())},e.prototype.findDefinition=function(e){return this.editor.getModel()?d.getDefinitionsAtPosition(this.editor.getModel(),e.position):r.TPromise.as(null)},e.prototype.gotoDefinition=function(e,t){var n=this;this.editor.setPosition(e.position);var i=new _.DefinitionAction(new _.DefinitionActionConfig(t,!1,!0,!1),{alias:void 0,label:void 0,id:void 0,precondition:void 0});return this.editor.invokeWithinContext(function(e){return i.run(e,n.editor)})},e.prototype.getId=function(){return t.ID},e.prototype.dispose=function(){this.toUnhook=h.dispose(this.toUnhook)},e.ID="editor.contrib.gotodefinitionwithmouse",e.MAX_SOURCE_PREVIEW_LINES=8,e=t=v([c.editorContribution,y(1,p.ITextModelService),y(2,s.IModeService)],e);var t}();f.registerThemingParticipant(function(e,t){var n=e.getColor(g.editorActiveLinkForeground);n&&t.addRule(".monaco-editor .goto-definition-link { color: "+n+" !important; }")})}),define(d[548],h([1,0,363,24,7,64,16,31,19,105,12,2,20,13,17,92,198,125,18,10,21]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,_,C,b,w,S){"use strict";function E(e,t){var n=m.getOuterEditor(e);if(n){var i=_.ReferencesController.get(n);i&&i.closeWidget()}}function L(e,t){var n=g.ReferenceProviderRegistry.ordered(e).map(function(n){return b.asWinJsPromise(function(i){return n.provideReferences(e,t,{includeDeclaration:!0},i)}).then(function(e){if(Array.isArray(e))return e},function(e){w.onUnexpectedExternalError(e)})});return o.TPromise.join(n).then(function(e){for(var t=[],n=0,i=e;n1&&n.localize(0,null,e.references.length)}},N=function(){function e(e,t,n){n&&m.PeekContext.inPeekEditor.bindTo(t)}return t=e,e.prototype.dispose=function(){},e.prototype.getId=function(){return t.ID},e.ID="editor.contrib.referenceController",e=t=v([p.commonEditorContribution,y(1,u.IContextKeyService),y(2,s.optional(m.IPeekViewService))],e);var t}();t.ReferenceController=N;var M=function(e){function t(){return e.call(this,{id:"editor.action.referenceSearch.trigger",label:n.localize(1,null),alias:"Find All References",precondition:u.ContextKeyExpr.and(S.EditorContextKeys.hasReferenceProvider,m.PeekContext.notInPeekEditor,S.EditorContextKeys.isInEmbeddedEditor.toNegated()),kbOpts:{kbExpr:S.EditorContextKeys.textFocus,primary:1094},menuOpts:{group:"navigation",order:1.5}})||this}return f(t,e),t.prototype.run=function(e,t){var n=_.ReferencesController.get(t);if(n){var i=t.getSelection(),o=L(t.getModel(),i.getStartPosition()).then(function(e){return new C.ReferencesModel(e)});n.toggleWidget(i,o,x)}},t=v([p.editorAction],t)}(p.EditorAction);t.ReferenceAction=M;a.CommandsRegistry.registerCommand("editor.action.findReferences",function(e,t,n){if(!(t instanceof i.default))throw new Error("illegal argument, uri");if(!n)throw new Error("illegal argument, position");return e.get(r.IEditorService).openEditor({resource:t}).then(function(e){var t=e.getControl();if(h.isCommonCodeEditor(t)){var i=_.ReferencesController.get(t);if(i){var r=L(t.getModel(),c.Position.lift(n)).then(function(e){return new C.ReferencesModel(e)}),s=new d.Range(n.lineNumber,n.column,n.lineNumber,n.column);return o.TPromise.as(i.toggleWidget(s,r,x))}}})}),a.CommandsRegistry.registerCommand("editor.action.showReferences",{handler:function(e,t,n,s){if(!(t instanceof i.default))throw new Error("illegal argument, uri expected");return e.get(r.IEditorService).openEditor({resource:t}).then(function(e){var t=e.getControl();if(h.isCommonCodeEditor(t)){var i=_.ReferencesController.get(t);if(i)return o.TPromise.as(i.toggleWidget(new d.Range(n.lineNumber,n.column,n.lineNumber,n.column),o.TPromise.as(new C.ReferencesModel(s)),x)).then(function(){return!0})}})},description:{description:"Show references at a position in a file",args:[{name:"uri",description:"The text document in which to show references",constraint:i.default},{name:"position",description:"The position at which to show",constraint:c.Position.isIPosition},{name:"locations",description:"An array of locations.",constraint:Array}]}}),l.KeybindingsRegistry.registerCommandAndKeybindingRule({id:"closeReferenceSearch",weight:p.CommonEditorRegistry.commandWeight(50),primary:9,secondary:[1033],when:u.ContextKeyExpr.and(_.ctxReferenceSearchVisible,u.ContextKeyExpr.not("config.editor.stablePeek")),handler:E}),l.KeybindingsRegistry.registerCommandAndKeybindingRule({id:"closeReferenceSearchEditor",weight:p.CommonEditorRegistry.commandWeight(-101),primary:9,secondary:[1033],when:u.ContextKeyExpr.and(m.PeekContext.inPeekEditor,u.ContextKeyExpr.not("config.editor.stablePeek")),handler:E}),t.provideReferences=L,p.CommonEditorRegistry.registerDefaultLanguageCommand("_executeReferenceProvider",L)}),define(d[549],h([1,0,134,191,193,164,514,313,450,451,452,516,454,455,456,431,520,458,460,199,547,522,523,524,464,525,465,527,469,548,529,471,180,531,181,532,476,517]),function(e,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0})}),define(d[200],h([1,0,3,62,387,69,478,16,413,84,31,46,19,421,103,50,163,76,58,144,42,61,147,146,88,437,56,430,485,129,417,85,77,542]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,g,m,v,_,y,C,b,w,S,E,L,x,N,M,T,k,I,D,O,R){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var P;!function(e){function t(e,t){var n=new o(e,t);return r.push(n),n}var n=new l.ServiceCollection,o=function(){function e(e,t){this._serviceId=e,this._factory=t,this._value=null}return Object.defineProperty(e.prototype,"id",{get:function(){return this._serviceId},enumerable:!0,configurable:!0}),e.prototype.get=function(e){if(!this._value){if(e&&(this._value=e[this._serviceId.toString()]),this._value||(this._value=this._factory(e)),!this._value)throw new Error("Service "+this._serviceId+" is missing!");n.set(this._serviceId,this._value)}return this._value},e}();e.LazyStaticService=o;var r=[];e.init=function(e){var t=new l.ServiceCollection;for(var n in e)e.hasOwnProperty(n)&&t.set(a.createDecorator(n),e[n]);r.forEach(function(n){return t.set(n.id,n.get(e))});var i=new u.InstantiationService(t,!0);return t.set(a.IInstantiationService,i),[t,i]},e.instantiationService=t(a.IInstantiationService,function(){return new u.InstantiationService(n,!0)});var s=new k.SimpleConfigurationService;e.configurationService=t(i.IConfigurationService,function(){return s}),e.resourceConfigurationService=t(E.ITextResourceConfigurationService,function(){return new k.SimpleResourceConfigurationService(s)}),e.contextService=t(C.IWorkspaceContextService,function(){return new k.SimpleWorkspaceContextService}),e.telemetryService=t(y.ITelemetryService,function(){return new k.StandaloneTelemetryService}),e.messageService=t(m.IMessageService,function(){return new k.SimpleMessageService}),e.markerService=t(g.IMarkerService,function(){return new p.MarkerService}),e.modeService=t(L.IModeService,function(e){return new x.ModeServiceImpl}),e.modelService=t(N.IModelService,function(t){return new M.ModelServiceImpl(e.markerService.get(t),e.configurationService.get(t))}),e.editorWorkerService=t(w.IEditorWorkerService,function(t){return new S.EditorWorkerServiceImpl(e.modelService.get(t),e.resourceConfigurationService.get(t),e.modeService.get(t))}),e.standaloneThemeService=t(O.IStandaloneThemeService,function(){return new R.StandaloneThemeServiceImpl}),e.codeEditorService=t(b.ICodeEditorService,function(t){return new T.CodeEditorServiceImpl(e.standaloneThemeService.get(t))}),e.progressService=t(v.IProgressService,function(){return new k.SimpleProgressService}),e.storageService=t(_.IStorageService,function(){return _.NullStorageService})}(P=t.StaticServices||(t.StaticServices={}));var A=function(e){function t(t,n){var a=e.call(this)||this,u=P.init(n),l=u[0],p=u[1];a._serviceCollection=l,a._instantiationService=p;var f=a.get(i.IConfigurationService),g=a.get(m.IMessageService),v=a.get(y.ITelemetryService),_=function(e,t){var i=null;return n&&(i=n[e.toString()]),i||(i=t()),a._serviceCollection.set(e,i),i},C=_(h.IContextKeyService,function(){return a._register(new I.ContextKeyService(f))}),b=_(c.ICommandService,function(){return new k.StandaloneCommandService(a._instantiationService)});_(d.IKeybindingService,function(){return a._register(new k.StandaloneKeybindingService(C,b,g,t))});var w=_(r.IContextViewService,function(){return a._register(new s.ContextViewService(t,v,g))});return _(r.IContextMenuService,function(){return a._register(new o.ContextMenuService(t,v,g,w))}),_(D.IMenuService,function(){return new k.SimpleMenuService(b)}),a}return f(t,e),t.prototype.get=function(e){var t=this._serviceCollection.get(e);if(!t)throw new Error("Missing service "+e);return t},t.prototype.set=function(e,t){this._serviceCollection.set(e,t)},t.prototype.has=function(e){return this._serviceCollection.has(e)},t}(n.Disposable);t.DynamicStandaloneServices=A}),define(d[422],h([1,0,20,25,540,48,200,480,71,273,129,17,397,164,64,31,69,16,46,19,42,61,87,68,77,132,49,54,50,408]),function(e,t,n,i,o,r,s,a,u,l,c,d,h,p,f,g,m,v,_,y,C,b,w,S,E,L,x,N,M){"use strict";function T(e,t,n){var i=new s.DynamicStandaloneServices(e,t),o=null;i.has(f.IEditorService)||(o=new c.SimpleEditorService,i.set(f.IEditorService,o));var r=null;i.has(w.ITextModelService)||(r=new c.SimpleEditorModelResolverService,i.set(w.ITextModelService,r)),i.has(u.IOpenerService)||i.set(u.IOpenerService,new a.OpenerService(i.get(f.IEditorService),i.get(g.ICommandService)));var l=n(i);return o&&o.setEditor(l),r&&r.setEditor(l),l}function k(e,t,n){return T(e,n,function(n){return new o.StandaloneEditor(e,t,n,n.get(v.IInstantiationService),n.get(C.ICodeEditorService),n.get(g.ICommandService),n.get(y.IContextKeyService),n.get(_.IKeybindingService),n.get(m.IContextViewService),n.get(E.IStandaloneThemeService))})}function I(e){return s.StaticServices.codeEditorService.get().onCodeEditorAdd(function(t){e(t)})}function D(e,t,n){return T(e,n,function(n){return new o.StandaloneDiffEditor(e,t,n,n.get(v.IInstantiationService),n.get(y.IContextKeyService),n.get(_.IKeybindingService),n.get(m.IContextViewService),n.get(b.IEditorWorkerService),n.get(C.ICodeEditorService),n.get(E.IStandaloneThemeService),n.get(M.IMessageService))})}function O(e,t){return new p.DiffNavigator(e,t)}function R(e,t,n){return s.StaticServices.modelService.get().createModel(e,t,n)}function P(e,t,n){if(e=e||"",!t){var i=n?n.path:null,o=e.indexOf("\n"),r=e;return-1!==o&&(r=e.substring(0,o)),R(e,s.StaticServices.modeService.get().getOrCreateModeByFilenameOrFirstLine(i,r),n)}return R(e,s.StaticServices.modeService.get().getOrCreateMode(t),n)}function A(e,t){s.StaticServices.modelService.get().setMode(e,s.StaticServices.modeService.get().getOrCreateMode(t))}function F(e,t,n){e&&s.StaticServices.markerService.get().changeOne(t,e.uri,n)}function W(e){return s.StaticServices.markerService.get().read(e)}function B(e){return s.StaticServices.modelService.get().getModel(e)}function V(){return s.StaticServices.modelService.get().getModels()}function H(e){return s.StaticServices.modelService.get().onModelAdded(e)}function z(e){return s.StaticServices.modelService.get().onModelRemoved(e)}function K(e){return s.StaticServices.modelService.get().onModelModeChanged(function(t){e({model:t.model,oldLanguage:t.oldModeId})})}function U(e){return h.createWebWorker(s.StaticServices.modelService.get(),e)}function j(e,t){return l.Colorizer.colorizeElement(s.StaticServices.standaloneThemeService.get(),s.StaticServices.modeService.get(),e,t)}function q(e,t,n){return l.Colorizer.colorize(s.StaticServices.modeService.get(),e,t,n)}function G(e,t,n){return void 0===n&&(n=4),l.Colorizer.colorizeModelLine(e,t,n)}function Y(e){var t=d.TokenizationRegistry.get(e);return t||{getInitialState:function(){return S.NULL_STATE},tokenize:function(t,n,i){return S.nullTokenize(e,t,n,i)},tokenize2:void 0}}function Z(e,t){s.StaticServices.modeService.get().getOrCreateMode(t);for(var n=Y(t),i=e.split(/\r\n|\r|\n/),o=[],r=n.getInitialState(),a=0,u=i.length;a0&&o[r-1]===c)){var d=l.startIndex;0===a?d=0:d